IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> Vuex 4源码学习笔记 - Store 构造函数都干了什么(四) -> 正文阅读

[JavaScript知识库]Vuex 4源码学习笔记 - Store 构造函数都干了什么(四)

在上一篇笔记中:Vuex 4源码学习笔记 - Vuex是怎么与Vue结合?(三)

我们复习了Vuex的使用方式,以及为什么要使用Vuex,同时看到了Vuex是如何与Vue去结合到一起的。

Vuex是通过Vue插件的方式,通过use函数将Vuex实例对象绑定到Vue中。

然后我们在页面或者组件中可以通过useStore函数或者this.$store两种方式在页面中访问到store实例。

首先创建Vuex实例的方式为调用createStore函数来调用

import { createStore } from 'vuex'

export default createStore({
  state: { /**...**/ },
  getters: { /**...**/ },
  actions: { /**...**/ },
  mutations: { /**...**/ }
})

createStore函数的源代码,可以看到实际是new了一个Store类的对象

export function createStore (options) {
  return new Store(options)
}

Store 构造函数

然后我们看到Store类的源代码,new的第一步首先就是调用constructor构造函数

export class Store {
  constructor (options = {}) {
    if (__DEV__) {
      assert(typeof Promise !== 'undefined', `vuex requires a Promise polyfill in this browser.`)
      assert(this instanceof Store, `store must be called with the new operator.`)
    }

    const {
      /* 一个数组,包含应用在 store 上的插件方法。*/
      plugins = [],
      /* 使 Vuex store 进入严格模式,在严格模式下,任何 mutation 处理函数以外修改 Vuex state 都会抛出错误。*/
      strict = false,
      /* devtool插件状态*/
      devtools
    } = options

    // store internal state -> store的内部状态
    /* 用来判断严格模式下是否是用mutation修改state的 */
    this._committing = false
    /* 存放action */
    this._actions = Object.create(null)
    /* 存放action订阅器*/
    this._actionSubscribers = []
    /* 存放mutation */
    this._mutations = Object.create(null)
    /* 存放getter */
    this._wrappedGetters = Object.create(null)
    /* module收集器 */
    this._modules = new ModuleCollection(options)
    /* 根据namespace存放module */
    this._modulesNamespaceMap = Object.create(null)
    /* 存放订阅者 */
    this._subscribers = []
    /* 缓存Getter */
    this._makeLocalGettersCache = Object.create(null)

    // EffectScope instance. when registering new getters, we wrap them inside
    // EffectScope so that getters (computed) would not be destroyed on
    // component unmount.
    // EffectScope 实例。 当注册新的 getter 时,我们将它们包裹在里面
    // EffectScope 使 getters (computed) 不会在组件unmount时销毁。
    this._scope = null

    this._devtools = devtools

    // bind commit and dispatch to self
    /* 将dispatch与commit调用的this绑定为store对象本身*/
    const store = 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)
    }

    // strict mode
    /* 严格模式(使 Vuex store 进入严格模式,在严格模式下,任何 mutation 处理函数以外修改 Vuex state 都会抛出错误)*/
    this.strict = strict

    // 取出state
    const state = this._modules.root.state

    // init root module.
    // this also recursively registers all sub-modules
    // and collects all module getters inside this._wrappedGetters
    /**
     * 初始化根模块
     * 递归注册所有子模块
     * 并收集 this._wrappedGetters 中的所有模块 getter
     */
    installModule(this, state, [], this._modules.root)

    // initialize the store state, which is responsible for the reactivity
    // (also registers _wrappedGetters as computed properties)
    // 初始化store的状态,为reactive响应式属性
    // 还将 _wrappedGetters 注册为computed计算属性
    resetStoreState(this, state)

    // apply plugins
    /* 调用插件 */
    plugins.forEach(plugin => plugin(this))
  }
  //... 
}

可以看到Store的构造函数除了初始化一些内部变量以外,主要做了两件事情,一是执行了installModule(初始化module),二是通过resetStoreState(通过Vue3的reactive使store的状态实现“响应式”)。

installModule

installModule的作用主要是为module加上namespace名字空间(如果有)后,注册mutation、action以及getter,同时递归安装所有子module。

export function installModule (store, rootState, path, module, hot) {
  // 判断是否是根目录
  const isRoot = !path.length
  // 是否设置了命名空间
  const namespace = store._modules.getNamespace(path)

  // register in namespace map
  // 在namespace中进行module的存储
  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
  // 在不是根组件且不是 hot 条件的情况下,通过getNestedState方法拿到该module父级的state,拿到其所在的moduleName
  if (!isRoot && !hot) {
    /* 获取父级的state */
    const parentState = getNestedState(rootState, path.slice(0, -1))
    /* module的name */
    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('.')}"`
          )
        }
      }
      // 将其state设置到父级state对象的moduleName属性中,由此实现该模块的state注册(首次执行这里,因为是根目录注册,所以并不会执行该条件中的方法)
      parentState[moduleName] = module.state
    })
  }

  // module上下文环境设置
  // 命名空间和根目录条件判断完毕后,接下来定义local变量和module.context的值,执行makeLocalContext方法,为该module设置局部的 dispatch、commit方法以及getters和state(由于namespace的存在需要做兼容处理)。
  const local = module.context = makeLocalContext(store, namespace, path)

  // 定义local环境后,循环注册我们在options中配置的action以及mutation等。

  // 注册对应模块的mutation,供state修改使用
  module.forEachMutation((mutation, key) => {
    const namespacedType = namespace + key
    registerMutation(store, namespacedType, mutation, local)
  })

  // 注册对应模块的action,供数据操作、提交mutation等异步操作使用
  module.forEachAction((action, key) => {
    const type = action.root ? key : namespace + key
    const handler = action.handler || action
    registerAction(store, type, handler, local)
  })

  // 注册对应模块的getters,供state读取使用
  module.forEachGetter((getter, key) => {
    const namespacedType = namespace + key
    registerGetter(store, namespacedType, getter, local)
  })

  // 注册完了根组件的actions、mutations以及getters后,
  // 递归调用自身,为子组件注册其state,actions、mutations以及getters等。
  module.forEachChild((child, key) => {
    installModule(store, rootState, path.concat(key), child, hot)
  })
}

resetStoreState

执行完成各module的install后,执行resetStoreState方法,进行store组件的初始化。

// initialize the store state, which is responsible for the reactivity
// (also registers _wrappedGetters as computed properties)
// 初始化store的状态,为reactive响应式属性
// 还将 _wrappedGetters 注册为computed计算属性
resetStoreState(this, state)

我们可以把Vuex理解为Vue的组件,所有配置的state、actions、mutations以及getters都是其组件的属性,所有操作都是围绕着这个组件。下面我们来看其细节:

export function resetStoreState (store, state, hot) {
  // 之前的 _state 状态
  const oldState = store._state
  // 之前的 effectScope
  const oldScope = store._scope

  // bind store public getters
  store.getters = {}
  // reset local getters cache
  store._makeLocalGettersCache = Object.create(null)
  const wrappedGetters = store._wrappedGetters
  const computedObj = {}
  const computedCache = {}

  // create a new effect scope and create computed object inside it to avoid
  // getters (computed) getting destroyed on component unmount.

  // 创建一个新的effectScope并在其中创建computed计算属性,
  // 以避免组件unmount时 getter(computed)被破坏。
  const scope = effectScope(true)

  scope.run(() => {
    /* 通过Object.defineProperty为每一个getter方法设置get方法,比如获取this.$store.getters.test的时候获取的是Vue的computed计算属性*/
    forEachValue(wrappedGetters, (fn, key) => {
      // use computed to leverage its lazy-caching mechanism
      // direct inline function use will lead to closure preserving oldState.
      // using partial to return function with only arguments preserved in closure environment.
      // 使用计算属性的延迟缓存机制
      // 直接使用内联函数会导致闭包保留 oldState。
      // 使用partial返回函数,仅在闭包环境中保留参数。
      computedObj[key] = partial(fn, store)
      computedCache[key] = computed(() => computedObj[key]())
      Object.defineProperty(store.getters, key, {
        get: () => computedCache[key].value,
        enumerable: true // for local getters
      })
    })
  })

  /* 这里使用Vue3的reactive来将state实现为响应式对象 */
  store._state = reactive({
    data: state
  })

  // register the newly created effect scope to the store so that we can
  // dispose the effects when this method runs again in the future.
  // 将新创建的 effectScope 作用域注册到 store 中,
  // 以便我们在以后再次运行该方法时可以处理这些 effects。
  store._scope = scope

  // enable strict mode for new state
  /* 使能严格模式,保证修改store只能通过mutation修改 */
  if (store.strict) {
    enableStrictMode(store)
  }

  if (oldState) {
    if (hot) {
      // dispatch changes in all subscribed watchers
      // to force getter re-evaluation for hot reloading.
      // 在所有订阅的观察者中dispatch更改
      // 以强制重新计算热重载的 getter
      store._withCommit(() => {
        oldState.data = null
      })
    }
  }

  // dispose previously registered effect scope if there is one.
  // 如果有,则处理之前注册的 effectScope。
  if (oldScope) {
    oldScope.stop()
  }
}

今天我们看了Store的构造函数,和构造函数中的installModule和resetStoreState函数的作用。

installModule函数主要用来处理初始化module,和所有子module的注册。

resetStoreState函数主要用俩处理store的state和getter的响应式初始化。

明天我们接着分析其中的一些函数细节,和组件中的获取state和action函数连起来看。

一起学习更多前端知识,微信搜索【小帅的编程笔记】,每天更新

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2021-11-23 12:15:16  更:2021-11-23 12:16:50 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 5:35:49-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码