4

总算把最近尝试的东西实现出来了,写点文章沉淀一下。

前言

单页面应用在使用单向数据流的设计方案后,状态树的控制就变得至关重要。这里面对的问题在于一个最基础的点,同时也是最常用的一点 -- 初始状态的控制与恢复
每次切换到一个页面,如果是动态数据页面,总是会先加载一些默认数据,或者清空现有数据,换句话说,就是初始化一下。
在之前使用vue + vuex的过程中,对于状态初始化的设计思路,是将其放到一个全局服务中,这样每次进到不同页面,只需要调用同一个全局的动作,就能完成初始化。同时,具体的初始化状态值,则由每个模块自己控制。
vuex init design

问题

在查看了vuex源码后,发现对于vuex的module而言,只是将所有module放到了同一个vm对象中,然后watch了一下。笔者之前在申明module中state对象时,经常使用const关键字,用意是认为申明时的state值一直作为静态值存在着,代表可返回的初始状态。而看了源码后,被打脸打飞了。。。。
那么怎么才能有一个静态的初始状态,让我们在切换页面时能够恢复呢?当然,最好这个初始状态时能够根据情况,可更替的。换句话说,这个所谓初始状态,应该是一个可定义的用于恢复的静态状态。

解决方案

Talk is cheap, show me the code.

来来来,一言不合就上代码~
源码以及相关样例可以在我的github上找到,链接点此

import Vue from 'vue'

function deepClone(obj) {
  if (Array.isArray(obj)) {
    return obj.map(deepClone)
  } else if (obj && typeof obj === 'object') {
    var cloned = {}
    var keys = Object.keys(obj)
    for (var i = 0, l = keys.length; i < l; i++) {
      var key = keys[i]
      cloned[key] = deepClone(obj[key])
    }
    return cloned
  } else {
    return obj
  }
}

const commonMutations = {
  INIT_STATE(state, moduleName) {
    state[moduleName] = deepClone(state.cache[moduleName])
  },
  CACHE_STATE(state, moduleName, newState) {
    state.cache[moduleName] = deepClone(newState)
  },
}

const commonActions = {
  initComponent({ dispatch }, name) {
    dispatch('INIT_STATE', name)
  },
  changeCacheState(store, module, state = store._vm[module]) {
    store.dispatch('CACHE_STATE', module, state)
  }
}

export default {
  onInit(state, store) {
    // hot load common mutations
    store.hotUpdate({
      mutations: commonMutations
    })
    // cache init state
    store._setupModuleState(state, {
        cache: {
          state: deepClone(state)
        }
      })
      // mixin all actions to the root vm
    Vue.mixin({
      vuex: {
        actions: commonActions
      }
    })
  }
}

关键点解读

1. 深层克隆

这里deep copy用了vuex源码里util的一个方法,官方注释上写着说比JSON.parse(JSON.stringify(obj))要来的更快,于是我就参(fu)考(zhi)过来了~~
什么?为什么要深拷贝?因为我们要维护一个静态状态的前端临时仓库呀~就像是前端的临时数据库一样,所谓数据驱动嘛~
于是下一步就是我们怎么把这个临时的静态状态对象让全局能够共享。这里用的方法就是一起扔到store绑定的vm对象上去

2. 全局使用便捷性

为了能够让代码全局都享受到这个便捷性,笔者在这里利用了vuex的中间件。vuex的中间件有2个特点,首先它提供了init与mutate动作的切入口。其次是它与store有着紧密联系。也因此,可以满足我们便捷性需要用到的2个需求:

  1. 应用启动时注册

  2. 应用全局可调用,不需要另外引入

所以就将init需要用到的commonMutation通过hotupdate的方式,在module初始化的时候,将init模块注册到全局,同时在store中加上当前模块初始状态的深拷贝
此外,利用了vue本身的mixin机制,将commonActions注册到全局的vm对象上
这样做的结果是什么呢?
去看看demo吧~~
地址在此


磐冲
1k 声望1.4k 粉丝

gopher的注视