目录
一、接口封装
二、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 = 'http://localhost' } axios.interceptors.request.use((config) => { 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拦截器参考链接:http://www.axios-js.com/zh-cn/docs/#%E6%8B%A6%E6%88%AA%E5%99%A8
参考链接:https://www.npmjs.com/package/vue-lazyload
注册:
import 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:
<div class="m-list-item-img-wrap"> <img v-lazy="item.avatar" alt="" class="m-list-item-img"> </div>样式:
.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;}参考链接:http://momentjs.cn/
import 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 = routerapp.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/json app.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测试上传接口:
前端:
<input type="file" @change="handleUpload" > 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 = routerapp.js和上传单个文件相同
使用postman测试上传多个文件:
前端代码:
<input type="file" multiple="multiple" @change="handleUpload" > 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) { } }) } },
参考链接:
https://www.npmjs.com/package/react-lazy-load
import LazyLoad from 'react-lazy-load' <div key={item.id} className="m-list-item"> <LazyLoad className="m-list-item-img-wrap"> <img src={item.avatar} className="m-list-item-img"></img> </LazyLoad> <div className="m-list-item-info"> {item.title} </div> </div> .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是facebook发布的一种包管理工具,作用同npm 一样,是一个包管理用具
优点:
快速: 1.会缓存它下载的每个包, 无需重复下载;能并行化操作以最大资源利用率
可靠: 使用格式详尽而又简洁的 lockfile文件 和确定性算法来安装依赖,能够保证在一个系统上的运行的安装过程也会以同样的方式运行在其他系统上。
安全: 安装包被执行前校验其完整性
参考链接:
https://zh-hans.reactjs.org/docs/code-splitting.html#reactlazy
import 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 ( <div> <div> <NavLink to="/index/home" className="m-nav-item">首页</NavLink> <NavLink to="/index/my_book" className="m-nav-item">书包</NavLink> </div> <Suspense fallback={<Loading lazyLoading={true}></Loading>}> <Switch> <Route path="/index/home" component={Home}></Route> <Route path="/index/my_book" component={MyBook}></Route> </Switch> </Suspense> </div> ) } }Loading.js:
import React, { Component } from 'react' import { connect } from 'react-redux' class Loading extends Component { render() { let { loading, lazyLoading } = this.props return ( <div className={"m-loading-wrap " + (loading ? 'active ' : '') + (lazyLoading ? 'active' : '')}> <span className="m-loading-img"></span> </div> ) } } 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": "http://localhost:83"也可以增加一个文件:
装包:
yarn add http-proxy-middleware在src目录下面建一个文件,文件名是setupProxy.js 即 src/setupProxy.js:
const proxy = require('http-proxy-middleware'); module.exports = function(app) { app.use( '/api', proxy({ target: 'http://localhost:83', changeOrigin: true, }) ); };参考链接:
https://create-react-app.dev/docs/proxying-api-requests-in-development/
装包:
yarn add mockjsconst 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 = true export 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 => ( <div key={item.id} className="m-list-item">{item.name}</div> )) return ( <div className="m-warp" onScroll={this.handleScroll.bind(this)}> {listDom} <div className="m-end">{end}</div> </div> ) } }打包:
yarn build利用 Express 托管静态文件:
//静态web服务器 app.use(express.static(__dirname + '/public')) app.use(express.static('public'))参考链接:
http://www.expressjs.com.cn/starter/static-files.html
利用 connect-history-api-fallback 解决前端history路由刷新报错:
const history = require('connect-history-api-fallback') //处理react前端路由(BrowserRoute),vue前端路由(mode:history),注意:开启后无法用postman和浏览器地址栏调试get接口 app.use(history())参考链接:
https://www.npmjs.com/package/connect-history-api-fallback
父组件:
<book-nav navList="{{navList}}" currentId="{{currentId}}" bind:onNav="handleNav"></book-nav>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.js Component({ /** * 组件的属性列表 */ properties: { navList: Array, currentId: Number }, /** * 组件的初始数据 */ data: { }, /** * 组件的方法列表 */ methods: { handleNav(e) { let { id } = e.mark this.triggerEvent('onNav', { id }) } } })在终端进入小程序目录,使用下面的命令省事package.json文件
npm init -y装包:
yarn add miniprogram-computed构建npm:
构建后生成miniprogram_npm:
使用计算属性计算总价,总数:
// pages/my-book/my-book.js const computedBehavior = require('miniprogram-computed') const { host } = getApp().globalData Component({ 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 }) } } }) } } })参考链接:
https://github.com/wechat-miniprogram/computed
https://developers.weixin.qq.com/miniprogram/dev/devtools/npm.html
页面的生命周期方法(即 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: { '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 }) } },https://mobile.ant.design/components/carousel-cn/
https://github.com/baweireact/m-app-1706C