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/…