开始

上一期《微信小程序实现数据侦听器watch》,在小程序中实现了Vue的watch功能。但小程序没有状态管理,目前的状况下,跨页通讯和数据传递非常的蛋疼,会造成难以维护和调试。这次就来实现store,全局状态管理功能,来解决这个蛋疼的问题,有以下几个目标:

  • 在Page和Component中所有生命周期和函数中均可使用store,均可修改state
  • 少量几行代码即可引入,不修改之前代码结构,不需要每个页面都引入
  • 使用方式和vue一样

方案

store其实就是一个全局变量,所有页面都共享,实现store功能有2个关键点

  1. Page和Component中怎么引用全局变量state,怎么将数据渲染到页面上
  2. 修改了全局变量state,怎么将其他Page和Component引用的全局变量state也更新并渲染到页面上

第一个问题,在Page的onLoad函数和Component的attached函数里,在options中增加一个对全局变量state的引用,并调用this.setData方法将变量state的数据渲染到页面上

第二个问题,需要维持一个列表,将当前已加载的Page和Component都储存起来,也就是在Page的onLoad函数和Component的attached函数中将当前对象添加到列表中,在Page的onUnload函数和Component的detached函数中将当前对象从列表中移除。然后当有一处调用setState方法时,遍历这个列表中所有对象,调用this.setData方法将变量state的数据渲染到页面上。

下面来看实现代码,写一个store.js,内容如下:

// 全局变量
let store = {
  msg: 'xxxxx',
  user: {}
}
// 所有已加载的页面和组件
let pcList = []
function initPage() {
  let oldPage = Page
  Page = function(obj) {
    let oldOnLoad = obj.onLoad || function() {}
    let oldOnUnload = obj.onUnload || function() {}
    obj.onLoad = function() {
      // 实现store功能,onLoad的时候将state增加到Page的data中
      loadStore.call(this)
      // 实现store的setState功能
      this.setState = function(obj) {
        setState.call(this, obj)
      }
      oldOnLoad.call(this, ...arguments)
    }
    obj.onUnload = function() {
      // 把this传进function
      unloadStore.call(this)
      oldOnUnload.call(this, ...arguments)
    }
    return oldPage(obj)
  }
}
function initComponent() {
  let oldComponent = Component
  Component = function(obj) {
    let oldAttached = obj.attached || function() {}
    let oldDetached = obj.detached || function() {}
    obj.attached = function() {
      // 实现store功能,onLoad的时候将state增加到Component的data中
      loadStore.call(this)
      // 实现store的setState功能
      this.setState = function(obj) {
        setState.call(this, obj)
      }
      oldAttached.call(this, ...arguments)
    }
    obj.detached = function() {
      // 把this传进function
      unloadStore.call(this)
      oldDetached.call(this, ...arguments)
    }
    return oldComponent(obj)
  }
}
function setState(obj) {
  let keys = Object.keys(obj)
  let newObj = {}
  keys.forEach(key => {
    newObj['$state.' + key] = obj[key]
  })
  // 更新所有的组件和页面
  pcList.forEach(item => {
    item.setData(newObj)
  })
}
function loadStore() {
  // 加入pcList,待setState的时候遍历
  pcList.push(this)
  // 每个页面或组件都在data中储存一份state的引用
  this.$state = store
  this.setData({
    $state: store
  })
}
function unloadStore() {
  // 从pcList中移除
  let index = pcList.findIndex(item => {
    return item === this
  })
  if (index > -1) {
    pcList.splice(index, 1)
  }
}
initPage()
initComponent()

使用

首先是引入,在app.js引入store.js即可

import './utils/store.js'

然后使用方式,在wxml中使用

 <view>{
  {$state.msg}}</view>

在js中获取state和更新state

Page({
  //...
  func(){
    // 获取state中的user
    let storeUser = this.$state.user
    storeUser.name = 'Becky'
    // 更新state中的user
    this.setState({
      user:storeUser
    })
  }
})

结语

我们实现了store功能,并实现了开头写得三个目标,所有生命周期和函数中均可使用,一行代码即可引入,使用方式和Vue一样。

不过只是实现了最基础的store功能,但能满足目前的项目需要,若以后有其他的需求,再在这个基础上增加。