At the end of last year, I built a vUE development framework on the mobile terminal, and felt that the experience was not very good. I was working on a mobile project last week. So I spent two days making the following improvements to the previous framework

  • Custom vuex – plugins – loading
  • Route switch animation + Keep Alive Dynamic management cache component
  • Best Practices for Better Scroll and VUE (Vue of Better Scroll)
  • Custom commands (Vue-finger: including click, long press, double click, drag and move, multi-touch, slide, rotate, zoom gestures)
  • Mobile terminal adaptation scheme
  • How to handle page top by case
  • Route lazy loading

Custom vuex – plugins – loading

Loading is displayed on each page before data is loaded. The first thing that comes to mind is the per-page setup states, show and hide. But there’s too much redundant code, and it’s annoying to write it yourself. I used DVA in the react project before, and there is a dVa-loading library, which has been studied before, so I used his ideas and wrote a vuex-loading by myself. Implementation idea: Register a loading module in Vuex. By binding asynchronous actions, the loading of each action is stored in Vuex. In this way, I only need to take the corresponding action loading from vuex store in each page to achieve this purpose

 ## Core code
    store.subscribeAction({
      before: action => {
        if (shouldEffect(action, includes, excludes)) {
          store.commit({ type: namespace + '/SHOW', payload: action.type })
        }
      },
      after: action => {
        if (shouldEffect(action, includes, excludes)) {
          store.commit({ type: namespace + '/HIDE', payload: action.type })
        }
      }
    })
  }
}
Copy the code

Note: Vuex must be version 3.1.0 to use the code described above. Because subscribeAction was added in 3.1.0

supplement

I thought I’d write about why vuex-plugins-loading is recommended.

When I came into contact with DVA. js last year, I felt that this idea of dealing with data flow would make your whole project clearer and more iterative. It’s easier for someone else to take over your entire framework.

Now I will talk about how I package vuex according to the idea of DVA.js. It’s actually quite simple

## store file directory index.js
import Vue from 'vue'
import Vuex from 'vuex'
import home from './modules/home'
import createLoadingPlugin from 'vuex-plugins-loading'
Vue.use(Vuex)

export default new Vuex.Store({
  modules: {
    home,
  },
  plugins: [createLoadingPlugin()]
})
Copy the code
# home.js
import { loadDataApi } from '.. /.. /service/api'

const state = {
  listData: [],
}

const mutations = {
  getData (state, payload) {
    state.listData = state.listData.concat(payload.res.data.data)
    state.page = payload.res.data.page
    state.pageNumber = Math.ceil(payload.res.data.total / payload.res.data.pageSize)
  },
  refreshData (state, payload) {
    state.listData = payload.res.data.data
  },
}
const getters = {

}
const actions = {
  loadMore ({ commit, state }, data) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        const { page, type } = data
        loadDataApi({ page }).then(res => {
          if (res.code === 200) {
            if (type= = ='loadMore') {
              commit({
                type: 'getData',
                res: res
              })
            } else {
              commit({
                type: 'refreshData',
                res: res
              })
            }
            resolve()
          } else {
            reject(res.error)
            Toast(res.error)
          }
        })
      }, 1000)
    })
  }
}
Copy the code
## Home.vuecomputed: { // Getting Vuex State from store/modules/home ... mapState({ listData: state => state.home.listData, loading: state => state['@@loading'].effects['home/loadMore'] }), }, methods: { ... mapActions('home'['initData'.'plusPage'.'initPage'], // onLoad loads dataonLoad () {
      this.requestData('loadMore')
    },
    requestData (type) {
      setTimeout(() => {
        this.$store.dispatch('home/loadMore', {

        }).then(() => {
        })
      }, 1000)
    },
    onRefresh () {
      this.initPage().then(() => {
        this.requestData('refresh')})},Copy the code

1. How to implement vuex-plugins-loading inner layer?

1.plugins: [createLoadingPlugin()],

2.loading: state => state[‘@@loading’].effects[‘home/loadMore’]

Using the plugin 1 will bind every action you have used, while using the plugin 2 will load the action binding. There are two processes involved. Before and after in store.subscribeAction. My loading is always true after I call the action, which is what happens in before. If MY action contains a promise, then resolve() or reject() is complete. And then you go after.

before

after

I know people ask why they put so much logic in vuex. In fact, I think it will make your whole project clearer and more maintainable. Easy to iterate.

The.vue file simply renders the data in vuex.

Store is where data is accessed and filtered.

The Serve layer encapsulates your API

It’s easy to see

Route switch animation + Keep Alive Dynamic management cache component

Before the global setting of route switching animation, but the experience effect is not very good, especially back to the list page, the page will cause a rebound, there will be a temporary blank page switching.

Before transformation, but also refer to the practice of others

## app.vue
 <transition :name="transitionName"> 
    <keep-alive :include="data">< router-view></router-view> </keep-alive> </transition> computed: {// Data is stored in vuex... mapState({ data: state => {returnState.global.data}})}, methods: {// Set the Keep_alive routesetKeep_alive (to) {
      if (to.meta.keepAlive) {
        this.$store.dispatch({
          type: 'global/setData',
          payload: to.name
        })
      }
    }
  },
  watch: {
    '$route'SetKeep_alive (to) const routeDeep = [(to, from) {this.setkeep_alive (to) const routeDeep = ['/'.'/list'.'/detail'.'/reservation'.'/addCars']
      const toDepth = routeDeep.indexOf(to.path)
      const fromDepth = routeDeep.indexOf(from.path)
      if(! from.name) { this.transitionName ='fold'
        return
      }
      this.transitionName = toDepth > fromDepth ? 'fold-left' : 'fold-right'}},Copy the code
## router.jsScrollBehavior (to, from, savedPosition) {// keep-alive returns the cached page and records the browsing positionif (savedPosition && to.meta.keepAlive) {
      returnSavedPosition} // Asynchronous scroll operationreturn new Promise((resolve) => {
      setTimeout(() => {
        resolve({ x: 0, y: 1 })
      }, 0)
    })
  },
Copy the code

Two questions

  1. Reason for page rebound: The position of the original scroll bar is unchanged. Using scrollBehavior, according to the code above, it can be seen that the scroll bar will have a flashing process, first top, and then scroll to the position retained last time.
  2. There will be temporary blank page switch, the transition is not normal.

After transforming

## app.vue
<keep-alive :include="data">< router-view></router-view> </keep-alive> computed: {// Data is stored in vuex... mapState({ data: state => {returnState.global.data}})}, methods: {// Set the Keep_alive routesetKeep_alive (to) {
      if (to.meta.keepAlive) {
        this.$store.dispatch({
          type: 'global/setData',
          payload: to.name
        })
      }
    }
  },
  watch: {
    '$route'(to, from) {this.setkeep_alive (to)}},Copy the code
list.vue
<Scroll
  ref="scroll"
  class="scroll-home"
  :scrollbar="scrollbar"
  :probeType="3"
  :pullDownRefresh="pullDownRefresh"
  :pullUpLoad="true"
  @pullingDown="onRefresh"
  @scroll="scroll"
  @pullingUp="onLoad"
>
 <div class="contantView">
 </div>
</Scroll>
Copy the code

1. After using Better-Scroll, the first problem can be solved directly. And you don’t have to set scrollBehavior, so if you don’t know, you can look at better Scroll

2. Add “Position: Absolute;” to the CSS. At this point, the page is separated from the document flow, does not take up space, so that the next page will not be crowded down, complete a smooth transition. Use better Scroll to add “Position: Fixed;” to the CSS page. .

If a flex layout is included in the page layout, be sure to add a DIV with position absolute or Fixed to the Flex component.

Keep Alive manages cached routes dynamically.

Better scroll vs VUE best practices

I read that BetterScroll is probably the best mobile scroll plugin, so I decided to give it a try. Most of the sliding components used in Didi’s cube UI component library are based on BetterScroll. Why not use cube? Because I feel the theme color is a little ugly. So I plan to encapsulate a VUE version of Scroll component based on better-Scroll. Without further ado, here’s the picture:

There’s another reason I want to use Better Scroll, because I want to customize the pull-up animation.

To see demo and source code please click here. Make sure you give it a star

Custom command vue-finger

These include clicking, long pressing, double clicking, dragging and moving, multi-touch, sliding, rotating, zooming gestures

This part of my side is based on other people’s demo modification, in these commands you can do a lot of mobile gestures want to do. I’ll continue to iterate on these instructions and come up with components that feel close to native, so keep an eye on github

Mobile terminal adaptation scheme

## rem.jsConst baseSize = 32 // Set rem functionfunction setRem() {// The zoom ratio of the current page width to 750 width can be modified according to your needs. Const scale = document. DocumentElement. ClientWidth / 750 / / set the page root node font size document. The documentElement. Style. FontSize = (baseSize * Math.min(scale, 2)) +'px'} // initializesetRem() // Resets Rem window.addeventListener ('resize'.function () {
  setRem()
})

## main.js
import './rem'
Copy the code

There’s one last step. For students who often write styles, px to REM is not a feeling very annoying. For my part, I create a new postcss.config.js file in the project root directory. This way, you just need to follow the design style and write px normally. When running a project, it automatically converts you to REM.

module.exports = {
  plugins: {
    'autoprefixer': {
      browsers: ['Android > = 4.0'.'iOS >= 7']},'postcss-pxtorem': {
      rootValue: 16,
      propList: [The '*']}}}Copy the code

How to handle page top by case

The scrollBehavior method in vue-router was mentioned above.

## router.jsScrollBehavior (to, from, savedPosition) {// keep-alive returns the cached page and records the browsing positionif (savedPosition && to.meta.keepAlive) {
      returnSavedPosition} // Asynchronous scroll operationreturn new Promise((resolve) => {
      setTimeout(() => {
        resolve({ x: 0, y: 1 })
      }, 0)
    })
  },
Copy the code

But feel after adding a page transition animation. The page will bounce back. So I gave it up. Do not add animation can be considered. I don’t have to worry about that if I use better Scroll here. If you look at the better Scroll documentation, you will find that better Scroll is designed for mobile use.

Route lazy loading

JavaScript packages can become very large when packaged to build applications, affecting page loads. It would be much more efficient if we could split the components corresponding to different routes into different code blocks and then load the components only when the routes are accessed. This is where lazy loading is important. Read the official document you should be able to use, here I will not introduce.

Const _import_ = file => () => import()'./views/' + file + '.vue')

routes: [
    {
      path: '/',
      name: 'home',
      component: _import_('Home/Home'),
      meta: {
        title: 'home',
        keepAlive: true}},]Copy the code

Finally finished, these are my mobile terminal experience optimization combat. Hope I can help you. I will continue to update if there is any good optimization plan in the future. Thank you for watching. Feel good point praise yo 👍