VUE状态管理及源码分析

106 阅读1分钟

风车.png
MVVM

sequenceDiagram
View-)viewmodel: 用户操作view层
viewmodel--)View: viewmodel与model相互影响
viewmodel-)model: 改变了数据结构
model--)viewmodel: 更新数据,可以驱动viewmodel


风车.png
VUEX使用

使用一个独立之外的实例来保存和获取我们需要的变量。那么这个状态机应该具有以下的特性:

  • 全局化 => 全局挂载
  • 全局唯一性 => 单例形态
  • 状态流转链 => 异步转同步

1677135581795.jpg

mutation 内应该避免处理业务,只更改state的值

1.全局引入

//index.js
import store from './store'
new Vue({
  store,
  render: h => h(App)
}).$mount('#app')

2.生成实例

// store.js
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)
const store = new Vuex.Store({
  actions:{
    changeUserInfoAct({commit},userInfo){
       commit('SET_USERINFO',userInfo)
    },
    changeElseAct({commit,dispatch},userInfo){
     //调用其他action
       dispatch('changeUserInfoAct', userInfo) 
       //如果调用其他模块的action,需要添加 root
       //dispatch('changeUserInfoAct', userInfo , {root:true}) 
    }
  },
  mutations:{
   SET_USERINFO(state,{userInfo}){
     state.userInfo = userInfo
     }
  },
  state:{
    userInfo:{
      name:'',
      age:18
    }
  }
})

export default store

3.模块化

import user from './modules/user'
import common from './modules/common'

...
// index.js
const store = new Vuex.Store({
  modules: {
    user,  // 使用模块化
    common,
  }
})
// user.js
const user = {
  state:{
  },
  mutation:{
  },
  action:{
  }
}
export defalut user

4. getter

const getters = {
  age: state => state.user.userInfo.age >= 18 ? "成年" : "未成年",  
}
export default getters

5.使用store

获取数据

import { mapStates , mapGetters } from "vuex";
//...
computed:{
 ...mapStates(['userInfor'])
 ...mapGetters(["age"]),
 
 //或者另外一种写法
 ...mapStates({
   userInfor : state => state.userInfo
 })
 
}

赋值

this.$store.dispatch('changeUserInfoAct',{
  name:"-",
  age:23
})


风车.png
VUEX分析

vuex 本质是一个 vue实例

vuex主体结构:

1677141234027.jpg
if (__DEV__) {
//__DEV__ 就是process.env.NODE_ENV ,就是运行时候的一个状态 
  // 浏览器不支持 promise
  assert(typeof Promise !== 'undefined', `vuex requires a Promise polyfill in this browser.`)
  //store必须实例化调用,
  assert(this instanceof Store, `store must be called with the new operator.`)
}
  • process.env.NODE_ENV 有两个值 developmentproduction ,当取值 development 时,__DEV__ 才为 true,其余为 false
  • assert 是自己定义的异常处理方法
export function assert (condition, msg) {
  if (!condition) throw new Error(`[vuex] ${msg}`)
}

为什么要自己定义告警?
自己定义告警可以控制是否阻断后续操作,可以做告警级别控制

// store internal state
/*
 * 所有变量的初始化
 */
this._committing = false  //内部state
this._actions = Object.create(null)  
this._actionSubscribers = []  //存放action订阅
this._mutations = Object.create(null)
this._wrappedGetters = Object.create(null)
this._modules = new ModuleCollection(options)  //模块收集器 - 树形结构
this._modulesNamespaceMap = Object.create(null)  //命名空间对应关系
//订阅列表
this._subscribers = [] 
//本地getters - 还没更新前的数据
this._makeLocalGettersCache = Object.create(null)
// bind commit and dispatch to self
const store = this //单例模式核心写法,确保this绑定到了实例上
const { dispatch, commit } = this
this.dispatch = function boundDispatch (type, payload) {
  return dispatch.call(store, type, payload)
}
this.commit = function boundCommit (type, payload, options) {
  return commit.call(store, type, payload, options)
}
//进入严格模式,使用mutation以外的方法进行修改state的值都会抛出异常
this.strict = strict
const state = this._modules.root.stat
挂载命名空间 并注册state、mutation、action 并递归注册子module
export function installModule (store, rootState, path, module, hot) {
  const isRoot = !path.length
  const namespace = store._modules.getNamespace(path)

  // 挂载命名空间
  if (module.namespaced) {
    //判断重名,异常
    if (store._modulesNamespaceMap[namespace] && __DEV__) {
      console.error(`[vuex] duplicate namespace ${namespace} for the namespaced module ${path.join('/')}`)
    }
    store._modulesNamespaceMap[namespace] = module
  }

  // set state
  //赋值
  if (!isRoot && !hot) {
    const parentState = getNestedState(rootState, path.slice(0, -1))
    const moduleName = path[path.length - 1]
    store._withCommit(() => {
      if (__DEV__) {
        if (moduleName in parentState) {
          console.warn(
            `[vuex] state field "${moduleName}" was overridden by a module with the same name at "${path.join('.')}"`
          )
        }
      }
      parentState[moduleName] = module.state
    })
  }

  const local = module.context = makeLocalContext(store, namespace, path)

  module.forEachMutation((mutation, key) => {
    const namespacedType = namespace + key
    registerMutation(store, namespacedType, mutation, local)
  })

  module.forEachAction((action, key) => {
    const type = action.root ? key : namespace + key
    const handler = action.handler || action
    registerAction(store, type, handler, local)
  })

  module.forEachGetter((getter, key) => {
    const namespacedType = namespace + key
    registerGetter(store, namespacedType, getter, local)
  })

  module.forEachChild((child, key) => {
    installModule(store, rootState, path.concat(key), child, hot)
  })
}

中心思想是利用单例,将数据存储在全局的store中,并利用vue的颗粒度响应记住来进行状态更新,进行高效处理。