目录
一、transition过渡动画
二、比较两个对象是否相等
三、图片懒加载使用gif图做loading
四、图片懒加载使用动画做loading
五、forEach
六、vuex getters
七、详情页对象变更检查注意事项
八、对话框组件
九、自定义alert,全局函数使用
十、瀑布流
十一、reduce和immutable.js
十二、使用immutable实现对象深拷贝
十三、路由懒加载
十四、小程序自定义对话框组件
十五、小程序案例
github源代码:
过渡:
首页
书包
新闻
动画:
首页
书包
新闻
使用immutable:
immutable.js实例
使用原生js(核心代码):
function compare2Objects(x, y) {
for (let p in x) {
// if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) {
// return false;
// } else if (typeof y[p] !== typeof x[p]) {
// return false;
// }
switch (typeof (x[p])) {
case 'object':
if (!compare2Objects(x[p], y[p])) {
return false;
}
break;
default:
if (x[p] !== y[p]) {
return false;
}
break;
}
}
return true;
}
let obj3 = {
a: {
b: 1,
c: [1, 2]
}
}
let obj4 = {
a: {
b: 1,
c: [1, 3]
}
}
console.log(compare2Objects(obj3, obj4))
详细代码请参考:
https://www.jianshu.com/p/90ed8b728975
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import lazyload from 'vue-lazyload'
import './index.css'
import loading from './images/loading.gif'
Vue.use(lazyload, {
loading: loading
})
Vue.config.productionTip = false
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
{{item.title}}
.m-list-item{display: flex;margin: 5px;}
.m-img-wrap{display: flex; width: 122px;height: 150px;background: #dddddd;}
.m-img{width: 122px;height: 150px;}
.m-img[lazy=loading]{margin: auto; width: 38px;height: 38px;}
.m-info{flex:1}
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import lazyload from 'vue-lazyload'
import './index.css'
Vue.use(lazyload)
Vue.config.productionTip = false
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
{{item.title}}
.m-list-item{display: flex;margin: 5px;}
.m-img-wrap{display: flex; width: 122px;height: 150px;background: #dddddd;}
.m-img-wrap::before{content: '';margin: auto;width: 38px;height: 38px;background-image: url(./images/loading.png);animation: loading 1s linear infinite;}
.m-img{position: absolute; width: 122px;height: 150px;}
.m-img[lazy=loading]{margin: auto; width: 38px;height: 38px;}
.m-info{flex:1}
@keyframes loading {
0% {transform: rotate(0);}
100%{transform: rotate(360deg);}
}
在forEach中,不能使用 continue 和 break ,可以使用 return 或 return false 跳出循环,效果与 for 中 continue 一样。注意该方法无法一次结束所有循环,需要一次性结束所有循环,还是老老实实使用for方法。
var arr = ['a', 'b', 'c', 'd', 'e'];
arr.forEach(function(item, index) {
if (item === 'b') {
return true; //相当于continue
}
if (item === 'c') {
return false; //相当于continue
}
console.log(index + ':' + item);
});
通过属性访问:
getters: {
getCurrentList(state) {
return state.currentList
}
},
export default {
computed: {
list() {
return this.$store.getters.getCurrentList
}
}
}
通过方法访问:
getters: {
getTaskList(state) {
return (type, search) => {
return state.taskList.filter(item => {
if (type) {
return item.type === type
} else {
return true
}
}).filter(item => {
if (search) {
return item.name.includes(search)
} else {
return true
}
})
}
}
},
methods: {
handleTask(type) {
this.taskList = this.$store.getters.getTaskList(type, this.search)
this.type = type
console.log(this.taskList)
},
handleSearch() {
this.taskList = this.$store.getters.getTaskList(this.type, this.search)
console.log(this.taskList)
}
},
{{detail.summary}}
参考链接:
https://cn.vuejs.org/v2/guide/list.html#%E5%AF%B9%E8%B1%A1%E5%8F%98%E6%9B%B4%E6%A3%80%E6%B5%8B%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9
Dialog.vue:
{{title}}
css:
.m-dialog-wrap{display: none;position: fixed;top: 0;left: 0;right: 0;bottom: 0;background: rgba(0, 0, 0, 0.2);}
.m-dialog-wrap.active{display: flex;}
.m-dialog{display: flex;flex-direction: column; margin: auto;padding: 10px; min-width: 200px;min-height: 150px; background: #ffffff;border-radius: 5px;}
.m-dialog-title{line-height: 50px;font-weight: bold;}
.m-dialog-content{flex: 1;}
.m-dialog-footer{line-height: 50px; text-align: right;}
使用:
参考链接:
https://www.jb51.net/article/159958.htm
vue.extend():
https://cn.vuejs.org/v2/api/#Vue-extend
Vue.extend() 参数是一个vue组件,使用脚手架开发时即.vue的文件,返回值是一个构造函数,这个构造函数使用new创建一个组件实例
Alert.vue:
{{title}}
{{message}}
index.js:
import Vue from 'vue'
import Alert from './Alert'
const AlertConstructor = Vue.extend(Alert)
const AlertFun = (options) => {
let instance = new AlertConstructor({data: options}).$mount()
document.body.appendChild(instance.$el)
}
export default AlertFun
vue入口函数在原型链上添加函数:
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import Alert from './components/Alert/index.js'
Vue.prototype.$MyAlert = Alert
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
在组件里使用:
this.$MyAlert({title: '标题', message: '请选择要删除的图书~'})
在拦截器里使用:
import MyAlert from '../components/Alert'
axios.defaults.baseURL = 'http://localhost:85'
axios.interceptors.response.use((res) => {
if (res.data.code === 400) {
MyAlert({title: '标题', message: res.data.message})
}
return res
})
Waterfall.vue:
{{item.id}} {{item.name}}
{{end}}
css:
.m-waterfall{position: relative; flex:1; overflow-y: auto;overflow-x: hidden; background: #eeeeee;}
.m-waterfall-item-wrap{display: inline-block;position: absolute; width: 50%;padding: 10px;box-sizing: border-box;vertical-align: top;}
.m-waterfall-item-wrap:last-child{margin: 0 0 50px 0;}
.m-waterfall-item{border-radius: 10px;}
.m-waterfall-img{width: 100%; border-top-left-radius: 10px;border-top-right-radius: 10px;}
.m-waterfall-info{margin: -4px 0 0;padding: 5px; height: 100px;background: #ffffff;border-bottom-right-radius: 10px;border-bottom-left-radius: 10px;}
.m-waterfall-end{position: fixed;width: 100%;line-height: 50px; bottom: 0; text-align: center;color: #aaaaaa;}
mock数据:
const news = Mock.mock({
'list|500': [{
'id|+1': 1,
'name': '@cname',
//'image': Mock.Random.image(null, '#ff0000', '#ffff00', 'hello'),
'image': '@image()',
'title': '@ctitle',
'paragraph': '@cparagraph',
'datetime': '@datetime'
}]
}).list
分页+搜索接口:
app.get('/api/news', (req, res) => {
let { page, size, search = '' } = req.query
let newsSearchResult = news.filter(item => item.name.includes(search))
let start = (page - 1) * size
let end = start + size * 1
res.send({
code: 200,
data: newsSearchResult.slice(start, end),
message: '新闻'
})
})
Immutable Data 就是一旦创建,就不能再被更改的数据。对 Immutable 对象的任何修改或添加删除操作都会返回一个新的 Immutable 对象。Immutable 实现的原理是 Persistent Data Structure(持久化数据结构),也就是使用旧数据创建新数据时,要保证旧数据同时可用且不变。同时为了避免 deepCopy 把所有节点都复制一遍带来的性能损耗,Immutable 使用了Structural Sharing(结构共享),即如果对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其它节点则进行共享。请看下面动画:
1. 降低 Mutable 带来的复杂度
2. 节省内存空间
3. Undo/Redo,Copy/Paste,随意穿越!
4. 拥抱函数式编程
不要修改 state:
https://www.redux.org.cn/docs/basics/Reducers.html
https://segmentfault.com/a/1190000017294051
immutable官网:
https://immutable-js.github.io/immutable-js/
为啥要使用immutable:
http://yunlaiwu.github.io/blog/2016/12/01/react+redux+immutablejs/#2
简介:
Facebook 工程师 Lee Byron 花费 3 年时间打造!
https://www.jianshu.com/p/0fa8c7456c15
import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import { fromJS } from 'immutable'
const defaultState = fromJS({
listAll: [],
currentId: 0,
myBook: []
})
const reducer = (state = defaultState, action) => {
switch (action.type) {
case 'SET_STATE':
// let newState = JSON.parse(JSON.stringify(state))
// newState[action.key] = action.value
//state是一个immutable对象,调用setIn方法并不会修改state的值,因为state是immutable对象
//newState是基于state和action创建出来的新immutable对象,这个创建过程并不是深拷贝,深拷贝性能很低
//这个创建过程会共享没有改变的部分,避免深拷贝把所有节点都复制一遍带来的性能损耗
//对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其它节点则进行共享
let newState = state.setIn(action.key, fromJS(action.value))
console.log(state.toJS())
console.log(newState.toJS())
return newState
default:
return state
}
}
const store = createStore(reducer, applyMiddleware(thunk))
store.subscribe(() => {
//console.log(store.getState().toJS())
})
export default store
const mapStateToProps = (state) => {
state = state.toJS()
return {
navList: state.navList,
currentId: state.currentId,
currentList: state.currentList
}
}
const mapDispatchToProps = (dispatch) => {
return ({
setState(key, value) {
dispatch({ type: 'SET_STATE', key, value })
}
})
}
export default connect(mapStateToProps, mapDispatchToProps)(List)
import React, { Component, Suspense, lazy } from 'react'
import { Switch, Route } from 'react-router-dom'
import Header from '../components/Header'
import Footer from '../components/Footer'
import Home from './Home'
//import MyBook from './MyBook' //无懒加载
import Me from './Me'
//const MyBook = lazy(() => import('./MyBook')) //有懒加载
import Loading from '../components/Loading'
//懒加载延时1
// const MyBook = lazy(async () => {
// return await new Promise((resolve, reject) => {
// setTimeout(() => {
// resolve(import('./MyBook'))
// }, 500)
// })
// })
//懒加载延时2
const MyBook = lazy(async () => {
let component
await new Promise((resolve, reject) => {
setTimeout(() => {
resolve(import('./MyBook'))
}, 500)
}).then(res => {
component = res
})
return component
})
export default class Index extends Component {
render() {
return (
}>
dialog.js:
// components/dialog/dialog.js
Component({
options: {
styleIsolation: 'apply-shared', //isolatiion apply-shared shared
multipleSlots: true
},
/**
* 组件的属性列表
*/
properties: {
visible: Boolean,
title: String
},
/**
* 组件的初始数据
*/
data: {
},
/**
* 组件的方法列表
*/
methods: {
//小程序阻止遮罩层下的页面滚动
handleMove(e) {
}
}
})
dialog.wxml:
除 bind
外,也可以用 catch
来绑定事件。与 bind
不同, catch
会阻止事件向上冒泡。
{{title}}
dialog.wxss:
/* components/dialog/dialog.wxss */
.m-dialog-wrap{display: none; position: fixed;top: 0;left: 0;right: 0;bottom: 0;background: rgba(0, 0, 0, 0.5);z-index: 99;}
.m-dialog-wrap.active{display: flex;}
.m-dialog{display: flex;flex-direction: column; margin: auto;padding: 10rpx; min-width: 600rpx;min-height: 400rpx;background: #ffffff;border-radius: 10rpx;}
.m-dialog-header{height: 80rpx;font-weight: bold;}
.m-dialog-content{flex: 1;}
.m-dialog-footer{height: 80rpx;text-align: right;}
使用dialog组件:
https://github.com/baweireact/m-apps/tree/master/m-app-1707B