directory
Transition animation
Compare two objects for equality
Loading with GIF images
Lazy loading using animation to do loading
Five, the forEach
Six, vuex getters
7. Notice for object change check on detail page
Dialog box components
9, custom alert, global function use
10. Waterfall Flow
Reduce and immutable
Use immutable to implement deep copy objects
Lazy route loading
Small program custom dialog box components
15. Small program cases
Github source code:
Transition animation
Transition:
<template>
<div>
<div>
<router-link to="/index/home" class="m-nav-item">Home page</router-link>
<router-link to="/index/my_book" class="m-nav-item">School bag</router-link>
<router-link to="/index/news" class="m-nav-item">news</router-link>
</div>
<transition name="m-slide">
<router-view class="m-router"></router-view>
</transition>
</div>
</template>
<script>
export default {
watch: {
$route(to, from) {
console.log(to); }},methods: {}};</script>
<style>
.m-router {
position: absolute;
width: 100%;
}
/* The initial style of the new route */
.m-slide-enter {
transform: scale(2.2) rotate(-5deg) translate(100%.0);
opacity: 0;
}
/* The target style of the new route */
.m-slide-enter-to {
color: red! important;
}
/* New route animation */
.m-slide-enter-active {
transition: all 1s ease-in-out;
}
/* Old route target style */
.m-slide-leave-to {
transform: translate(-100%.0);
}
/* Old path animation */
.m-slide-leave-active {
transition: all 1s ease-in-out;
}
</style>
Copy the code
Animation:
<template>
<div>
<div>
<router-link to="/index/home" class="m-nav-item">Home page</router-link>
<router-link to="/index/my_book" class="m-nav-item">School bag</router-link>
<router-link to="/index/news" class="m-nav-item">news</router-link>
</div>
<transition name="m-slide">
<router-view class="m-router"></router-view>
</transition>
</div>
</template>
<script>
export default {
watch: {
$route(to, from) {
console.log(to); }},methods: {}};</script>
<style>
.m-router {
position: absolute;
width: 100%;
}
/* New route animation */
.m-slide-enter-active {
animation: new-router 1s ease-in-out;
}
/* Old path animation */
.m-slide-leave-active {
animation: old-router 1s ease-in-out;
}
@keyframes new-router {
0% {
transform: scale(2.2) rotate(-5deg) translate(100%.0);
opacity: 0;
}
50% {
color: red;
}
100%{}}@keyframes old-router {
0% {
transform: translate(100px)}100% {
transform: translate(-100%.0); }}</style>
Copy the code
Compare two objects for equality
Using immutable:
<! DOCTYPE html><html lang="en">
<head>
<meta charset="UTF-8">
<title>Immutable. Js instance</title>
<style type="text/css">
</style>
</head>
<body>
<script src="https://cdn.bootcss.com/immutable/4.0.0-rc.12/immutable.js"></script>
<script type="text/javascript">
// The original way of writing
var foo = { a: { b: 1}};var bar = foo;
bar.a.b = 2;
console.log(foo.a.b); / / print 2
console.log(foo === bar); / / print true
// use immutable
var foo = Immutable.fromJS({ a: { b: 1}});var bar = foo.setIn(['a'.'b'].2); // Use setIn assignment
console.log(foo.getIn(['a'.'b'])); // Use getIn to print 1
console.log(foo === bar); / / print false
console.log(bar.getIn(['a'.'b'])) / / 2
console.log(bar.toJS()) // Convert to a js object
let obj1 = Immutable.fromJS({
a: {
b: 1.c: [1.2]}})let obj2 = Immutable.fromJS({
a: {
b: 1.c: [1.2]}})// Compare whether two objects are equal
console.log(Immutable.is(obj1, obj2))
</script>
</body>
</html>
Copy the code
Using native JS (core code) :
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))
Copy the code
Please refer to:
www.jianshu.com/p/90ed8b728…
Loading with GIF images
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')
Copy the code
<div v-for="item in list" :key="item.id" class="m-list-item">
<div class="m-img-wrap">
<img v-lazy="item.avatar" class="m-img">
</div>
<div class="m-info">
{{item.title}}
</div>
</div>
Copy the code
.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}
Copy the code
Lazy loading using animation to do loading
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')
Copy the code
<div v-for="item in list" :key="item.id" class="m-list-item">
<div class="m-img-wrap">
<img v-lazy="item.avatar" class="m-img">
</div>
<div class="m-info">
{{item.title}}
</div>
</div>
Copy the code
.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 1slinear 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);}
}
Copy the code
Five, the forEach
In forEach, instead of using continue and break, you can use return or return false to break out of the loop, just like a continue in for. Note that this method does not end all loops at once. You need to end all loops at once, or use the for method honestly.
var arr = ['a'.'b'.'c'.'d'.'e'];
arr.forEach(function(item, index) {
if (item === 'b') {
return true; // equivalent to continue
}
if (item === 'c') {
return false; // equivalent to continue
}
console.log(index + ':' + item);
});
Copy the code
Six, vuex getters
Access via properties:
getters: {
getCurrentList(state) {
return state.currentList
}
},
Copy the code
export default {
computed: {
list() {
return this.$store.getters.getCurrentList
}
}
}
Copy the code
Access by method:
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}})}}},Copy the code
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)
}
},
Copy the code
7. Notice for object change check on detail page
<template>
<div>
<img :src="detail.avatar" />
<div>
{{detail.summary}}
</div>
<div>
<button v-if="detail.is_in_my_book">Already collected</button>
<button v-else @click="handleAdd(detail)">collection</button>
</div>
</div>
</template>
<script>
import Api from '.. /api'
export default {
data() {
return {
detail: {}}; },methods: {
handleAdd(detail) {
detail.count = 1
Api.add({
book: detail
}).then(res= > {
if(res.code === 200) {
Is_in_my_book = true // Not recommended
$set(this.detail, 'is_in_my_book', true
//this.detail = {... This. detail, is_in_my_book: true
this.detail = Object.assign({}, this.detail, {is_in_my_book: true}) // The recommended way to write it}}}}),mounted() {
let { id } = this.$route.params;
Api.getDetail(`? id=${id}`).then(res= > {
if (res.code === 200) {
this.detail = res.data; }}); }};</script>
<style>
</style>
Copy the code
Reference links:
Cn.vuejs.org/v2/guide/li…
Dialog box components
Dialog.vue:
<template>
<div class="m-dialog-wrap" :class="{active: visible}">
<div class="m-dialog">
<div class="m-dialog-title">{{title}}</div>
<div class="m-dialog-content">
<slot></slot>
</div>
<div class="m-dialog-footer">
<slot name="footer"></slot>
</div>
</div>
</div>
</template>
<script>
export default {
props: ['visible'.'title']}</script>
<style>
</style>
Copy the code
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; }Copy the code
Use:
<Dialog :visible="visible" title="Tip">
<div>Are you sure you want to delete the selected book?</div>
<div>.</div>
<template v-slot:footer>
<div>
<button @click="handleHideDialog">cancel</button>
<button @click="handleDeleteSelected">determine</button>
</div>
</template>
</Dialog>
Copy the code
9, custom alert, global function use
Reference links:
www.jb51.net/article/159…
vue.extend():
Cn.vuejs.org/v2/api/#Vue…
The vue.extend () argument is a Vue component, a.vue file when developed with scaffolding, and the return value is a constructor that creates an instance of the component using new
Alert.vue:
<template>
<div class="m-dialog-wrap" :class="{active: visible}">
<div class="m-dialog">
<div class="m-dialog-title">{{title}}</div>
<div class="m-dialog-content">
{{message}}
</div>
<div class="m-dialog-footer">
<button @click="handleHideDialog">determine</button>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
title: ' '.message: ' '.visible: true}},methods: {
handleHideDialog() {
this.visible = false}}}</script>
<style>
</style>
Copy the code
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
Copy the code
Vue entry functions add functions to the prototype chain:
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')
Copy the code
Used in components:
this.$MyAlert({title: 'title'.message: 'Please select book to delete ~'})
Copy the code
Used in interceptors:
import MyAlert from '.. /components/Alert'
axios.defaults.baseURL = 'http://localhost:85'
axios.interceptors.response.use((res) = > {
if (res.data.code === 400) {
MyAlert({title: 'title'.message: res.data.message})
}
return res
})
Copy the code
10. Waterfall Flow
Waterfall.vue:
<template>
<div class="m-news-wrap">
<div class="m-news-serarch">
<input type="text" placeholder="Please enter keywords" v-model="search" @keyup.enter="handleSearch" />
</div>
<div class="m-waterfall js-waterfall" @scroll="handleScroll">
<div id="m-waterfall-top"></div>
<div>
<div v-for="item in list" :key="item.id" class="m-waterfall-item-wrap js-waterfall-item-wrap">
<div class="m-waterfall-item">
<img :src="item.image" class="m-waterfall-img" :height="imageHeight(item.image)" />
<div class="m-waterfall-info">{{item.id}} {{item.name}}</div>
</div>
</div>
</div>
<div class="m-waterfall-end">{{end}}</div>
</div>
<el-backtop target=".js-waterfall"></el-backtop>
</div>
</template>
<script>
import Api from ".. /api";
let isUpdated = true;
export default {
data() {
return {
list: [].page: 1.search: "".end: ""
};
},
methods: {
handleScroll(e) {
console.log(
e.target.clientHeight,
e.target.scrollTop,
e.target.scrollHeight
);
let { scrollTop, clientHeight, scrollHeight } = e.target;
if (
scrollTop + clientHeight + 200 > scrollHeight &&
isUpdated &&
this.end === ""
) {
this.page++;
isUpdated = false;
Api.news(`? page=The ${this.page}&size=10&search=The ${this.search}`).then(
res= > {
if (res.code === 200) {
this.list = [...this.list, ...res.data];
if (res.data.length < 10) {
this.end = "I have my limits."; }}}); }},handleSearch() {
document.getElementById("m-waterfall-top").scrollIntoView(true);
Api.news(`? page=1&size=10&search=The ${this.search}`).then(res= > {
if (res.code === 200) {
this.list = res.data;
this.page = 1;
this.end = ""; }}); },waterFall() {
let items = document.getElementsByClassName("js-waterfall-item-wrap");
items = Array.from(items);
items[0].style.top = 0; // The first element
items[0].style.left = 0;
items[1].style.top = 0; // The second element
items[1].style.left = "50%";
let arr = [];
arr.push(items[0].offsetHeight); // The first element
arr.push(items[1].offsetHeight); // The second element
for (let i = 2; i < items.length; i++) {
let minHeight = arr[0];
let index = 0;
for (let j = 0; j < arr.length; j++) {
if (minHeight > arr[j]) {
minHeight = arr[j];
index = j;
}
}
items[i].style.top = arr[index] + "px";
items[i].style.left = items[index].offsetLeft + "px"; arr[index] = arr[index] + items[i].offsetHeight; }},imageHeight(url) {
let size = url.slice(url.lastIndexOf("/") + 1).split("x");
// True width/true height = original width/original height
// True height = true width/original width * original height
let height =
((window.document.body.clientWidth / 2 - 20) / size[0]) * size[1];
returnheight; }},updated() {
isUpdated = true;
this.waterFall();
},
mounted() {
Api.news("? page=1&size=10").then(res= > {
if (res.code === 200) {
this.list = res.data; }}); }};</script>
<style>
</style>
Copy the code
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; }Copy the code
The mock data:
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
Copy the code
Paging + search interface:
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: 'news'})})Copy the code
Reduce and immutable
Immutable Data is Data that, once created, cannot be changed. Any modification or addition or deletion of an Immutable object returns a new Immutable object. Immutable is a Persistent Data Structure, which ensures that old Data can be used to create new Data without changing it. And to avoid the performance cost of deepCopy copying all nodes once, Immutable uses Structural Sharing, where if a node in the object tree changes, only that node and its affected parent are modified, and the other nodes are shared. Watch the animation below:
1. Reduce the complexity of Mutable
2. Save memory space
3. Undo/Redo, Copy/Paste,
4. Embrace functional programming
Do not modifyThe state:
www.redux.org.cn/docs/basics…
Segmentfault.com/a/119000001…
Immutable website:
Immutable – js. Making. IO/immutable – j…
Why use immutable:
Yunlaiwu. Making. IO/blog / 2016/1…
Brief introduction:
Facebook engineer Lee Byron spent 3 years building it!
www.jianshu.com/p/0fa8c7456…
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 is an immutable object. Calling setIn does not change the value of state, because state is an immutable object
//newState is a new IMmutable object created based on state and action. This creation process is not deep copy, which has low performance
// The creation process shares unchanged portions to avoid the performance penalty of deep copy copying all nodes once
If a node in the object tree changes, only that node and its affected parent are modified. The other nodes are shared
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
Copy the code
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)
Copy the code
Use immutable to implement deep copy objects
<! DOCTYPEhtml>
<html>
<head>
</head>
<body>
<div>
<script src="https://cdn.bootcss.com/immutable/4.0.0-rc.12/immutable.js"></script>
<script>
let obj = {
a: 1.b: {
c: '1'
},
fun() {
console.log(1)},time: new Date(),
d: undefined
}
/ / there are compatibility issues, https://www.cnblogs.com/sweet-ice/p/10583192.html
let objClone = JSON.parse(JSON.stringify(obj))
console.log(objClone)
// Use immutable to deeply copy keys whose values are undefined, functions, and dates
let objClone2 = Immutable.fromJS(obj).toJS()
console.log(objClone2)
objClone2.a = 2
console.log(obj.a) / / 1
console.log(objClone2.a) / / 2
</script>
</div>
</body>
</html>
Copy the code
Lazy route loading
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' // no lazy loading
import Me from './Me'
/ / const MyBook = lazy (() = > import ('. / MyBook)) / / a lazy loading
import Loading from '.. /components/Loading'
// Lazy load delay 1
// const MyBook = lazy(async () => {
// return await new Promise((resolve, reject) => {
// setTimeout(() => {
// resolve(import('./MyBook'))
/ /}, 500)
/ /})
// })
// Lazy load delay 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 (
<div className="m-wrap">
<Header></Header>
<Suspense fallback={<div className="m-main"><Loading lazyLoading={true}></Loading></div>} ><Switch>
<Route path="/index/home" component={Home}></Route>
<Route path="/index/my_book" component={MyBook}></Route>
<Route path="/index/me" component={Me}></Route>
</Switch>
</Suspense>
<Footer></Footer>
<Loading></Loading>
</div>)}}Copy the code
Small program custom dialog box components
dialog.js:
// components/dialog/dialog.js
Component({
options: {
styleIsolation: 'apply-shared'.//isolatiion apply-shared shared
multipleSlots: true
},
/** * Component property list */
properties: {
visible: Boolean.title: String
},
/** * The initial data of the component */
data: {},/** * list of component methods */
methods: {
// Applet prevents pages from scrolling under the mask layer
handleMove(e){}}})Copy the code
dialog.wxml:
In addition to bind, you can also use catch to bind events. Unlike bind, catch prevents events from bubbling up.
<! --components/dialog/dialog.wxml-->
<view class="m-dialog-wrap {{visible ? 'active' : ''}}" catch:touchmove="handleMove">
<view class="m-dialog">
<view class="m-dialog-header">{{title}}</view>
<view class="m-dialog-content">
<slot name="content"></slot>
</view>
<view class="m-dialog-footer">
<slot name="footer"></slot>
</view>
</view>
</view>
Copy the code
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; }Copy the code
Using the Dialog component:
<dialog visible="{{visible}}" title="Add">
<view slot="content" class="m-count-wrap">
<view class="m-count">
<text class="m-text-btn" bind:tap="handleSub">-</text>
<input class="m-input" placeholder="Please enter" value="{{item.count}}"></input>
<text class="m-text-btn" bind:tap="handleAdd">+</text>
</view>
</view>
<view slot="footer">
<button size="mini" class="m-btn" bind:tap="handleHideDialog">cancel</button>
<button size="mini" class="m-btn" bind:tap="handleAddToMyBook">determine</button>
</view>
</dialog>
Copy the code
15. Small program cases
Github source code:
Github.com/baweireact/…