项目实战【vue,react,微信小程序】(1706C)

网友投稿 654 2022-08-30

项目实战【vue,react,微信小程序】(1706C)

目录

​​一、接口封装​​

​​二、vue图片懒加载​​

​​三、书城后端接口​​

​​四、登录、注册、修改密码后端接口​​

​​五、时间戳转日期​​

​​六、文件上传单个文件​​

​​ 七、上传多个文件​​

​​八、设置淘宝镜像​​

​​九、yarn和npm清缓存​​

​​十、react图片懒加载​​

​​十一、yarn​​

​​十二、react路由懒加载​​

​​十三、让所有接口延时返回​​

​​十四、前端使用代理解决跨域问题​​

​​十五、mock很多数据、分页查找​​

​​十六、如何上线​​

​​十八、父子组件通讯​​

​​十九、路由​​

​​二十、tabBar​​

​​二十一、组件生命周期​​

​​二十二、计算属性​​

​​二十四、使用 Component 构造器构造页面​​

​​二十五、使用组件observers做数据-​​

​​github源码:​​

一、接口封装

index.js:

import axios from 'axios'import urls from './urls'console.log(process)if (process.env.NODE_ENV === 'development') { console.log('env', process.env.NODE_ENV) axios.defaults.baseURL = '=> { return config})axios.interceptors.response.use((res) => { if (res.data.code === 400) { alert(res.data.message) } return res})const common = async (config) => { let response = await axios(config) return response.data}export default { login: (data) => common({ url: urls.login, data, method: 'post' }), getNav: () => common({ url: urls.getNav }), getList: (url) => common({ url: urls.getList + url }), updateMyBook: (data) => common({ url: urls.updateMyBook, data, method: 'post' }), add:(data) => common({ url: urls.add, data, method: 'post' }), getMyBook: () => common({ url: urls.getMyBook }), getDetail: (url) => common({ url: urls.getDetail + url })}

urls.js:

const urls = { login: '/api/login', getNav: '/api/nav', getList: '/api/list', updateMyBook: '/api/update_my_book', add: '/api/add', getMyBook: '/api/get_my_book', getDetail: '/api/detail'}export default urls

-参考链接:​​Vue from 'vue'import VueLazyload from 'vue-lazyload'import loadingImg from './images/loading.gif'Vue.use(VueLazyload, { preLoad: 1.3, error: loadingImg, loading: loadingImg, attempt: 1})new Vue({ render: h => h(App)}).$mount('#app')

v-lazy:

样式:

.m-list-item-img-wrap{display: flex; width: 112px;height: 150px;background: #dddddd;}.m-list-item-img{width: 100%;}.m-list-item-img[lazy=loading]{margin: auto; width: 40px;height: 40px;}

三、书城后端接口

const express = require('express')const { bookNavData, bookMallData, bookMallDetailData } = require('./data')const bodyParser = require('body-parser')const cors = require('cors')const history = require('connect-history-api-fallback')const app = express()//用户列表let userList = [{ id: '001', username: 'admin', password: '123456'}, { id: '002', username: 'xu', password: '123'}, { id: '003', username: 'a', password: '123456'}]//书包let myBook = []//跨域app.use(cors())//解析post请求// parse application/jsonapp.use(bodyParser.json())app.use(history())// parse application/x-extended: false }))//静态web服务器app.use(express.static( __dirname + '/public')) //登录app.post('/api/login', (req, res) => { let { username, password } = req.body console.log(JSON.stringify(req.body)) console.log(username, password) let user = userList.find(item => item.username === username) if (user) { if (user.password === password) { res.send({ code: 200, data: { username }, message: '登录成功' }) } else { res.send({ code: 400, message: '密码错误' }) } } else { res.send({ code: 400, message: '用户名不存在' }) }})//导航app.get('/api/nav', (req, res) => { res.send({ code: 200, data: bookNavData, message: '导航' })})//列表app.get('/api/list', (req, res) => { let { id } = req.query let list = bookMallData.find(item => item.id == id).list list.forEach(item => { item.is_in_my_book = myBook.findIndex(book => book.id === item.id) >= 0 }) res.send({ code: 200, data: list, message: '列表' })})//详情app.get('/api/detail', (req, res) => { let { id } = req.query let detail bookMallDetailData.forEach(item => { item.list.forEach(book => { if (book.id == id) { book.is_in_my_book = myBook.findIndex(item => item.id === book.id) >= 0 detail = book } }) }) res.send({ code: 200, data: detail, message: '详情' })})//更新书包app.post('/api/update_my_book', (req, res) => { let { myBookNew } = req.body myBook = myBookNew res.send({ code: 200, data: myBook, message: '更新成功' })})//添加app.post('/api/add', (req, res) => { let { book } = req.body myBook.push(book) res.send({ code: 200, data: myBook, message: '添加成功' }) })//获取书包app.get('/api/get_my_book', (req, res) => { res.send({ code: 200, data: myBook, message: '书包' })})app.listen(80)console.log(80)

四、登录、注册、修改密码后端接口

const express = require('express')const bodyParser = require('body-parser')const cors = require('cors')const history = require('connect-history-api-fallback')const uuidv1 = require('uuid/v1')const jwt = require('jwt-simple')const redis = require('redis')//用户列表let userList = [{ id: '001', username: 'admin', password: '123456'}, { id: '002', username: 'xu', password: '123'}, { id: '003', username: 'a', password: '123456'}]//token加密密码let secret = 'xxx'const app = express()const client = redis.createClient()client.on('error', err => { console.log('redis错误:' + err)})//跨域app.use(cors())//解析post请求// parse application/jsonapp.use(bodyParser.json())//let jsonParser = bodyParser.json()//处理react前端路由(BrowserRoute),vue前端路由(mode:history),注意:开启后无法用postman和浏览器地址栏调试get接口//app.use(history())//静态web服务器app.use(express.static(__dirname + '/public'))//通过中间件检查登录是否过期,并自动续期const checkTokenByMiddleware = (req, res, next) => { let token = req.headers.token client.get(token, (err, response) => { if (response) { client.set(token, token, 'EX', 60) next() } else { res.send({ code: 403, message: '登录过期' }) } }) console.log(2)}//登录app.post('/api/login', (req, res) => { let { username, password } = req.body let user = userList.find(item => item.username === username) if (user) { if (user.password === password) { let token = jwt.encode(user.id, secret) client.set(token, token, 'EX', 60) res.send({ code: 200, data: { username, token }, message: '登录成功' }) } else { res.send({ code: 400, message: '密码错误' }) } } else { res.send({ code: 400, message: '用户名不存在' }) }})//注册app.post('/api/register', (req, res) => { let { username, password } = req.body console.log(username, password) let user = userList.find(item => item.username === username) if (user) { res.send({ code: 400, message: '用户名已存在' }) } else { let id = uuidv1() userList.push({ id, username, password, }) let token = jwt.encode(id, secret) client.set(token, token, 'EX', 60) res.send({ code: 200, data: { userList, token }, message: '注册成功' }) }})//修改密码app.post('/api/modify_password', checkTokenByMiddleware, async (req, res) => { let token = req.headers.token let { password } = req.body let id = jwt.decode(token, secret) console.log(id) let index = userList.findIndex(item => item.id === id) userList[index].password = password res.send({ code: 200, data: userList, message: '修改成功' })})//动态路由app.get('/api/test/:id', (req, res) => { let { id } = req.params res.send({ code: 200, data: id, message: '动态路由测试' })})app.listen(82)console.log(82)

五、时间戳转日期

参考链接:​​moment from "moment"moment.locale('zh-cn') console.log(moment(1573042782076).format('YYYY年MM月DD日 hh:mm:ss'))console.log(moment(1573042782076).fromNow())

六、文件上传单个文件

controller/upload.js:

const multer = require('multer')const storage = multer.diskStorage({ destination: (req ,file, cb) => { cb(null, 'upload') }, filename: (req, file, cb) => { cb(null, `${Date.now()} - ${file.originalname}` ) }})const upload = multer({ storage })const uploadImg = (req, res) => { res.send({ code: 200, data: req.file, message: '上传成功' })}module.exports = { upload, uploadImg}

router/upload.js:

const express = require('express')const router = express.Router()const { upload, uploadImg } = require('../controller/upload')router.post('/upload', upload.single('img'), uploadImg)module.exports = router

app.js:

const express = require('express')const bodyParser = require('body-parser')const cors = require('cors')const history = require('connect-history-api-fallback')const upload = require('./router/upload')const app = express()//跨域app.use(cors())//解析post请求// parse application/jsonapp.use(bodyParser.json())//let jsonParser = bodyParser.json()//处理react前端路由(BrowserRoute),vue前端路由(mode:history),注意:开启后无法用postman和浏览器地址栏调试get接口app.use(history())//静态web服务器app.use(express.static(__dirname + '/public'))app.use(express.static(__dirname + '/upload'))//上传文件app.use('/api/', upload)app.listen(82)console.log(82)

使用postman测试上传接口:

前端:

handleUpload(e) { const data = new FormData() data.append('img', e.target.files[0]) axios({ url: '/api/upload', data, method: 'post', timeout: 1000 * 60 }).then(res => { if (res.code === 200) { } }) }

七、上传多个文件

controller/upload.js:

const multer = require('multer')const storage = multer.diskStorage({ destination: (req ,file, cb) => { cb(null, 'upload') }, filename: (req, file, cb) => { cb(null, `${Date.now()}-${file.originalname}` ) }})const upload = multer({ storage })const uploadImg = (req, res) => { res.send({ code: 200, data: req.files, //单个文件:req.file 多个文件:req.files message: '上传成功' })}module.exports = { upload, uploadImg}

router/upload.js:

const express = require('express')const router = express.Router()const { upload, uploadImg } = require('../controller/upload')//上传单个文件//router.post('/upload', upload.single('img'), uploadImg)router.post('/upload', upload.array('img', 9), uploadImg)module.exports = router

app.js和上传单个文件相同

使用postman测试上传多个文件:

前端代码:

handleUpload(e) { const data = new FormData() let files = e.target.files files.forEach(item => { data.append('img', item) }) axios({ url: '/api/upload', data, method: 'post', timeout: 1000 * 60 }).then(res => { if (res.code === 200) { } }) } },

八、设置淘宝镜像

yarn config set registry config set sass-binary-site cache clean --forceyarn cache clean

十、react图片懒加载

参考链接:

​​LazyLoad from 'react-lazy-load'

{item.title}

.m-list-item{display: flex;margin: 5px;}.m-list-item-img-wrap{display: flex; width: 112px;height: 150px;background: #dddddd;}.m-list-item-img-wrap::before{content: '';margin: auto;width: 38px;height: 38px;background-image: url(./images/loading.png);animation: loading 0.5s linear infinite;}.m-list-item-img{position: absolute; width: 112px;height: 150px;}.m-list-item-info{flex:1}@keyframes loading { from {transform: rotate(0deg);} to{transform: rotate(360deg);}}

十一、yarn

yarn是什么? 能干什么? yarn是facebook发布的一种包管理工具,作用同npm 一样,是一个包管理用具

优点:

快速: 1.会缓存它-的每个包, 无需重复-;能并行化操作以最大资源利用率

可靠: 使用格式详尽而又简洁的 lockfile文件 和确定性算法来安装依赖,能够保证在一个系统上的运行的安装过程也会以同样的方式运行在其他系统上。

安全: 安装包被执行前校验其完整性

十二、react路由懒加载

参考链接:

​​React, { Component, Suspense, lazy } from 'react'import { NavLink, Switch, Route } from 'react-router-dom'import Home from './Home'//import MyBook from './MyBook'import Loading from '../components/Loading'const MyBook = lazy(async () => { return await new Promise((resolve, reject) => { setTimeout(() => { resolve(import('./MyBook')) }, 2000) })})export default class Index extends Component { render() { return (

首页 书包
}>
) }}

Loading.js:

import React, { Component } from 'react'import { connect } from 'react-redux'class Loading extends Component { render() { let { loading, lazyLoading } = this.props return (

) }}const mapStateToProps = (state) => { state = state.toJS() return { loading: state.loading }}export default connect(mapStateToProps)(Loading)

loading样式文件:

.m-loading-wrap{position: fixed;display: none; top: 0;left: 0;right: 0;bottom: 0; background: rgba(0, 0, 0, 0.5);z-index: 999;}.m-loading-wrap.active{display: flex;}.m-loading-img{ display: inline-block;margin: auto; width: 38px;height: 38px;background-image: url(./images/loading.png);background-size: 100% 100%; animation: loading 0.5s linear infinite; }@keyframes loading { from {transform: rotate(0deg);} to{transform: rotate(360deg);}}

路由懒加载时,需要懒加载的路由组件特别小,这时很难观察到加载的loading效果,怎么办呢?

可以把使用Promise模拟延时:

const MyBook = lazy(async () => { return await new Promise((resolve, reject) => { setTimeout(() => { resolve(import('./MyBook')) }, 2000) })})

十三、让所有接口延时返回

目的是测试前端loading效果

//让所有接口延时返回app.use((req, res, next) => { setTimeout(() => { next() }, 500)})

十四、前端使用代理解决跨域问题

To tell the development server to proxy any unknown requests to your API server in development, add a ​​proxy​​​ field to your ​​package.json​​, for example:

"proxy": "add 即 src/setupProxy.js:​​

const proxy = require('= function(app) { app.use( '/api', proxy({ target: ' changeOrigin: true, }) );};

参考链接:

​​add mockjs

const Mock = require('mockjs')const mockDataList = Mock.mock({ 'list|100': [{ 'id|+1': 1, 'name': '@cname', 'title': '@ctitle', 'image': '@image(300x300)', 'address': '@county(true)' }]}).list

//分页app.get('/api/mock_data', (req, res) => { let { page, size } = req.query let start = (page - 1) * size let end = start + Number(size) console.log(start, end) let data = mockDataList.slice(start, end) res.send({ code: 200, data: data, message: '列表' })})

前端分页,滚动到底加载更多:

import React, { Component } from 'react'import axios from 'axios'let updateDone = trueexport default class App extends Component { constructor(props) { super(props) this.state = { list: [], end: '', page: 1 } } handleScroll(e) { let { list, end, page} = this.state if ( e.target.clientHeight + e.target.scrollTop + 200 > e.target.scrollHeight && end === "" && updateDone ) { updateDone = false page = page + 1; axios({ url: `/api/mock_data?page=${page}&size=20` }).then(res => { if (res.data.code === 200) { this.setState({ list: list.concat(res.data.data), page }) if (res.data.data.length < 20) { console.log("到底了"); this.setState({ end: "到底了" }) } } }); } } componentDidUpdate() { updateDone = true } componentDidMount() { axios({ url: '/api/mock_data?page=1&size=20' }).then(res => { if (res.data.code === 200) { this.setState({ list: res.data.data }) } }) } render() { let { list, end } = this.state let listDom = list.map(item => (

{item.name}
)) return (
{listDom}
{end}
) }}

十六、如何上线

打包:

yarn build

利用 Express 托管静态文件:

//静态web服务器app.use(express.static(__dirname + '/public'))

app.use(express.static('public'))

参考链接:

​​connect-history-api-fallback 解决前端history路由刷新报错:

const history = require('connect-history-api-fallback')//处理react前端路由(BrowserRoute),vue前端路由(mode:history),注意:开启后无法用postman和浏览器地址栏调试get接口app.use(history())

参考链接:

​​ handleImageLoad(e) { let { windowWidth } = wx.getSystemInfoSync() let { height, width } = e.detail height = height / width * windowWidth this.setData({ height }) },

十八、父子组件通讯

父组件:

handleNav(e) { let { id } = e.detail this.setData({ currentId: id }) wx.request({ url: `${host}/api/list?id=${id}`, success: (res) => { if (res.data.code === 200) { this.setData({ currentList: res.data.data }) } } }) },

子组件:

// components/book-nav/book-nav.jsComponent({ /** * 组件的属性列表 */ properties: { navList: Array, currentId: Number }, /** * 组件的初始数据 */ data: { }, /** * 组件的方法列表 */ methods: { handleNav(e) { let { id } = e.mark this.triggerEvent('onNav', { id }) } }})

十九、路由

wx.navigateTo({ url: `/pages/detail/detail?id=${id}`, })

onLoad: function (options) { let { id } = options wx.request({ url: `${host}/api/detail?id=${id}`, success: (res) => { if (res.data.code === 200) { this.setData({ detail: res.data.data }) } } }) },

二十、tabBar

"tabBar": { "color": "#333333", "selectedColor": "#ff0000", "list": [ { "pagePath": "pages/home/home", "text": "首页", "iconPath": "/static/images/home.png", "selectedIconPath": "/static/images/home-active.png" }, { "pagePath": "pages/my-book/my-book", "text": "书包", "iconPath": "/static/images/cart.png", "selectedIconPath": "/static/images/cart-active.png" }, { "pagePath": "pages/index/index", "text": "我的", "iconPath": "/static/images/me.png", "selectedIconPath": "/static/images/me-active.png" } ] }

二十一、组件生命周期

lifetimes: { ready() { wx.request({ url: `${host}/api/my_book`, }) } }

pageLifetimes: { show() { wx.request({ url: `${host}/api/my_book`, success: (res) => { if (res.data.code === 200) { this.setData({ myBook: res.data.data }) } } }) } }

二十二、计算属性

npm init -y

装包:

yarn add miniprogram-computed

构建npm:

构建后生成miniprogram_npm:

使用计算属性计算总价,总数:

// pages/my-book/my-book.jsconst computedBehavior = require('miniprogram-computed')const { host } = getApp().globalDataComponent({ behaviors: [computedBehavior], /** * 组件的属性列表 */ properties: { }, /** * 组件的初始数据 */ data: { myBook: [] }, computed: { total(data) { let totalPrice = 0, totalCount = 0 data.myBook.forEach(item => { totalCount += item.count totalPrice += item.count * item.price }) return { totalCount, totalPrice } } }, /** * 组件的方法列表 */ methods: { }, pageLifetimes: { show() { wx.request({ url: `${host}/api/my_book`, success: (res) => { if (res.data.code === 200) { this.setData({ myBook: res.data.data }) } } }) } }})

参考链接:

​​Component 构造器构造页面

页面的生命周期方法(即 ​​on​​​ 开头的方法),应写在 ​​methods​​ 定义段中。

Component({ properties: { paramA: Number, paramB: String, }, methods: { onLoad: function(options) { let { id } = options this.data.paramA // 页面参数 paramA 的值 this.data.paramB // 页面参数 paramB 的值 }, onShow() { } }})

也可以写在pageLifetimes字段里(去掉on):

Component({ properties: { }, data: { detail: {} }, pageLifetimes: { load(options) { let { id } = options }, show() { } }})

二十五、使用组件observers做数据-

可以代替计算属性

observers: { 'myBook': function (myBook) { this.update(myBook) let totalPrice = 0, totalCount = 0 myBook.filter(item => item.checked).forEach(item => { totalCount += item.count totalPrice += item.count * item.price }) let total = { totalCount, totalPrice, selectedAll: myBook.length === myBook.filter(item => item.checked).length && myBook.length > 0 } this.setData({ total }) } },

二十六、antd移动版

​​https://mobile.ant.design/components/carousel-cn/​​

github源码:

​​https://github.com/baweireact/m-app-1706C​​

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:什么是RPC,你知道嘛?(Rpc什么意思)
下一篇:codeforces 1003a Polycarp's Pockets
相关文章

 发表评论

暂时没有评论,来抢沙发吧~