设为首页 - 加入收藏
广告 1000x90
您的当前位置:主页 > 网站运营 > 正文

vuex的原理是什么?

来源:网络分享 编辑:引流技巧 时间:2025-10-05


在vuex中,我们将从一些概念开始。


这取决于vuex的工作流程。


修改state的唯一方法是提交mutations。


在异步情况下派发(dispatch)动作,其本质仍然是提交mutations。


在提交mutations之后,VueComponents可以动态呈现组件。


感觉有没有减少了什么,没错,当getters下实现时就会说。


一、vuex实现


下一步,我们将模拟vuex的源代码实现一个简单版本的vuex。


您可点击此处查看完整的源码,并随文章观看。


二、建立存储库


第一,我们需要创建一个store,该store有下列属性和方法。


将createStore方法定义为简单的创建一个Store实例并传递options。


扩展函数创建存储(options){returnnewStore(options)}


让我们看一下Store类的实现。

export class Store { constructor(options = {}) { const plugins = options.plugins || [] this._subscribers = [] this._actionSubscribers = [] this._actions = Object.create(null) this._mutations = Object.create(null) this.getters = Object.create(null) // 收集 modules this._modules = new ModuleCollection(options) // 绑定 commit 和 dispatch 到自身 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) { return commit.call(store, type, payload, options) } const state = this._modules.root.state // 安装 module installModule(this, state, [], this._modules.root) // 初始化 state resetStoreState(this, state) // 应用插件 plugins.forEach(plugin => plugin(this)) } }

收集 modules

通过ModuleCollection收集 modules,生成一棵 module 树

// store.js export class Store { constructor(options = {}) { this._modules = new ModuleCollection(options) } } // module-collection.js export default class ModuleCollection { constructor(rawRootModule) { this.register([], rawRootModule) } register(path, rawModule) {} }

在ModuleCollection中调用了一个注册函数register来注册每个 module

其中path参数指的是当前 moduel 的路劲,可以用来判断层级关系,rawModule则是原始的 module 对象

register(path, rawModule) { const newModule = new Module(rawModule) if (path.length === 0) { // root 定义为 rawModule this.root = newModule } else { const parent = this.get(path.slice(0, -1)) parent.addChild(path[path.length - 1], newModule) } // 如果传入有 modules,递归地注册子模块 if (rawModule.modules) { Object.keys(rawModule.modules).forEach(key => { const rawChildModule = rawModule.modules[key] this.register(path.concat(key), rawChildModule) }) } }

在register中,通过rawModule我们实例化了一个Module对象,它具有以下属性

  • _rawModule:传入的原对象

  • _children:子 moduels

  • state:传入的原对象的 state 值

  • getChild:获取子 moduel 的方法

  • addChild:新增子 module 的方法

export default class Module { constructor(rawModule) { this._rawModule = rawModule this._children = Object.create(null) const rawState = rawModule.state this.state = (typeof rawState === 'function' ? rawState() : rawState) || {} } }

当path.length === 0时,将该newModule作为root的值,也可以叫做父 module

设置完root后,会继续判断有没有modules,如果有的话,递归地注册子模块

// 如果传入有 modules,递归地注册子模块 if (rawModule.modules) { Object.keys(rawModule.modules).forEach(key => { const rawChildModule = rawModule.modules[key] this.register(path.concat(key), rawChildModule) }) }

在这里,将每个 module 各自的 key 设置为 path 用来区分层次,加入到root中

get(path) { return path.reduce((module, key) => { return module.getChild(key) }, this.root) } if (path.length === 0) { // ... } else { const parent = this.get(path.slice(0, -1)) parent.addChild(path[path.length - 1], newModule) }

最后,我们就得到了这样的一个 module 树

 

 

绑定 commit 和 dispatch

继续回到 store 的构造函数代码

// 绑定 commit 和 dispatch 到自身 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) { return commit.call(store, type, payload, options) }

封装替换原型中的 dispatch 和 commit 方法,将 this 指向当前 store 对象。dispatch 和 commit 方法具体实现如下:

commit(type, payload) { const mutation = { type, payload } const entry = this._mutations[type] if (!entry) { return } entry(payload) this._subscribers.slice().forEach(sub => sub(mutation, this.state)) }

刚开始我们就提到,修改 state 需要提交 mutation,commit就实现了这一过程

dispath和commit的作用相同,不同的是dispath是派发action来提交mutation修改state,我们通常在action中执行异步函数

dispatch(type, payload) { const action = { type, payload } const entry = this._actions[type] if (!entry) { return } try { this._actionSubscribers .slice() .filter(sub => sub.before) .forEach(sub => sub.before(action, this.state)) } catch (error) { console.error(e) } const result = entry(payload) return new Promise((resolve, reject) => { result .then(res => { try { this._actionSubscribers .filter(sub => sub.after) .forEach(sub => sub.after(action, this.state)) } catch (error) { console.error(e) } resolve(res) }) .catch(error => { try { this._actionSubscribers .filter(sub => sub.error) .forEach(sub => sub.error(action, this.state, error)) } catch (e) { console.error(e) } reject(error) }) }) }

在commit和dispath中,都执行了各自的订阅函数集合_subscribers和_actionSubscribers,

_subscribers的订阅函数中传入了当前的 mutation 对象和当前的 state,这是提供给插件的参数

_actionSubscribers还将函数分为了before,after,error类型

module 安装

module 的安装是为了封装 mutations、actions、getters 函数,传入需要的参数

封装 mutation

if (module._rawModule.mutations) { Object.keys(module._rawModule.mutations).forEach(key => { const mutation = module._rawModule.mutations[key] store._mutations[key] = payload => // 惰性获取 state mutation.call(store, getNestedState(store.state, path), payload) }) }

封装 action

if (module._rawModule.actions) { Object.keys(module._rawModule.actions).forEach(key => { const action = module._rawModule.actions[key] store._actions[key] = payload => { let res = action.call( store, { dispatch: store.dispatch, commit: store.commit, getters: store.getters, state: getNestedState(store.state, path), }, payload ) if (!(res instanceof Promise)) { res = Promise.resolve(res) } return res } }) }

封装 getter

if (module._rawModule.getters) { Object.keys(module._rawModule.getters).forEach(key => { const getter = module._rawModule.getters[key] store.getters[key] = () => // 惰性获取 state getter(getNestedState(store.state, path), store.getters) }) }

最后递归安装子 module

Object.keys(module._children).forEach(key => installModule(store, rootState, path.concat(key), module._children[key]) )

需要注意的一点是,获取 state 的时候需要惰性的获取,因为在使用 vuex 的过程中,state 发生会改变,如果封装函数的时候固定 state,会有不符合预期的行为

初始化 state

然后是resetStoreState函数

export function resetStoreState(store, state) { store._state = reactive({ data: state, }) }

将state变为响应式的,这样就可以在 vuex 修改了 state 之后,更新视图了

关于 vue3 的数据响应式原理可以看我的这篇文章 vue3 数据响应式原理分析

看到这你可能会有点迷惑,怎么实例上的是_state而不是state呢?其实还有store中还有一个 getter 取 state 的值

get state() { return this._state.data } set state(v) {}

这里我们也可以发现,直接设置 state 的值是无效的

应用插件

我们先来实现一个打印修改 state 前后变化的插件:logger

export const logger = store => { let prevState = deepClone(store.state) store.subscribe((mutation, state) => { const nextState = deepClone(state) const formattedTime = getFormattedTime() const message = `${mutation.type}${formattedTime}` console.log('%c prev state', 'color: #9E9E9E; font-weight: bold', prevState) console.log('%c mutation', 'color: #03A9F4; font-weight: bold', message) console.log('%c next state', 'color: #4CAF50; font-weight: bold', nextState) prevState = nextState }) }

很简单,监听一下mutation就可以啦

store中会注入每个插件

plugins.forEach(plugin => plugin(this)

实验一下效果,如果你 clone 了源码,那么你可以在安装了依赖之后,执行

yarn dev

打开控制台,来查看效果

相关文章:

相关推荐:

栏目分类

微商引流技巧网 www.yinliujiqiao.com 联系QQ:1716014443 邮箱:1716014443@qq.com

Copyright © 2019-2024 强大传媒 吉ICP备19000289号-9 网站地图 rss地图

Top