vuex原理解析并实现一个简单的vuex

vuex原理解析并实现一个简单的vuex

vuex的作用

官方

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

个人理解

简单来说当我们使用 vue 进行项目开发的时候往往会遇到多个组件共享状态的情况,它们的关系有可能是父子、兄弟、爷孙甚至嵌套的更深,此时就使得状态难以管理,因此 vuex 应用而生,他就相当于一个仓库,存放需要共享的状态,并设置了一些方法可以更改、获取状态。(将就着看看,手笨)
在这里插入图片描述

简单介绍

  1. State仓库,vuex使用单一状态树,每一个应用最好仅包含一个store实例,不建议直接修改state的值,最好是通过commit方法调用mutation任务进行修改,方便后期数据的追踪;
  2. Mutations,定义方法动态修改state中的数据,不建议包含逻辑业务处理,处理一些同步任务;
  3. Actions,定义方法执行异步任务一些复杂的逻辑代码,view层通过store.dispath分发Action任务;
  4. Getter,类似vue实例中的计算属性特点,用来过滤、规范、改造数据;
  5. Module,项目特别复杂的时候使用,每一个模块拥有自己的state,mutation,Action,getter,代码逻辑更加清晰;库内的数据。

vuex的用法

  1. 先新建一个文件引入 Vuex,Vue, 然后使用 Vue.use(Vuex) 注册组件。
  2. 再暴露实例化的一个Store类。
  3. 在 main.js 中引入,在实例化 Vue 的时候作为参数传入。

所以我们在模拟 vuex 功能写代码时需要暴露 install 函数 (Vue.use(Vuex)会自动调用 Vuex 的 install 方法安装插件) 和 Store类。

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

Vue.use(Vuex)
export default new Vuex.Store({
  namespaced: true,
  state: {
  },
  getters: {
  },
  mutations: {
  },
  actions: {
  },
  modules: {
  }
})

一点思考

开始撸代码前先想一想最简单的 vuex 是怎么工作的呢?使用过 vuex 的同学知道,我们可以将状态及状态的操作方法分别配置在多个文件中,但最后在使用的的时候都可以通过 this.$store.state.xxx 访问状态、dispatch 触发 action 方法、 commit 触发 mutations 方法等等。而且其他文件中的配置与 Store 类的配置一致。由此可以联想到 vuex 简单使用是将多个文件中的配置都合并到了 Store 类上作为配置使用

开始实现

暴露 Store 类和 install 方法

还记得前面说过什么吗?vuex 要暴露 Store 类和 install 方法。
index.js

import { Store, install } from './store';
export default {
  Store,
  install
}

forEach 工具函数

可以看到在文件开头引入了 forEach,它是我们写的一个工具函数,作用是遍历一个对象中的所有的属性,并返回该函数的属性名和属性值

export const forEach = (obj = {}, fu) => {
  Object.keys(obj).forEach(key => fu(obj[key], key))
}

核心部分

下面是vuex的核心部分,定义了 Store 类和 install方法。

Store 类

Store类的构造函数中会分别调用工具函数 forEach 将传入的 getters、mutattions、actions 添加到对象上。同时定义 commit 函数,使实例可以调用 mutatiaons 中的方法,定义 dispatch 函数,使实例可以调用 actions 中的方法。

重点

  • Vuex 是通过 new 一个 vue 实例,并把 state 作为 vue 的 data 利用 vue 的响应式系统把 state 中的数据变为了 响应式数据。

  • getter 本质上是依赖了 vue 的 computed 实现了缓存机制。

store.js

import { forEach } from "./util";
import applyMixin from "./mixin";
let Vue;
class Store{
  constructor(options) {
    // 保存getters
    this.getters = {};
    const computed = {};
    // 遍历得到所有getters
    forEach(options.getters, (fu, fuName) => { 
      // 增加缓存机制
      computed[fuName] = () => {
        return fu(this.state);
      }
      Object.defineProperty(this.getters, fuName, {
        get: () => this._vm[fuName]
      })
    })
    // 发布订阅者模式,将用户定义的mutation和actions先保存起来,当调用commit十九调用订阅的mutation,调用dipatch时就调用订阅action
    this._mutations = {};
    forEach(options.mutations, (fu, fuName) => {
      this._mutations[fuName] = (payload) => fu.call(this, this.state, payload);
    })
      
    this._actions = {};
    forEach(options.actions, (fu, fuName) => {
      this._actions[fuName] = (payload) => fu.call(this, this, payload);
    })
    // 让数据变为响应式
    this._vm = new Vue({
      data: {//内部状态
        $$state: options.state
      },
      computed //计算属性会将自己的属性放到实例上
    })
  }
  // 可以使用本方法调用订阅的actions方法
  dispatch = (fuName, payload) => {
    this._actions[fuName](payload);
  }
   // 可以使用本方法调用订阅的mutation方法
  commit = (fuName, payload) => {
    this._mutations[fuName](payload);
  }
  // 类的属性访问器,当访问这个实例的state属性时会执行此方法
  get state() {
    return this._vm._data.$$state;
  }
}
// 定义的install方法
const install = (_Vue) => {
  Vue = _Vue;
  applyMixin(Vue);
}
export {
  Store,
  install
}
install 方法

在上面的文件中还定义了 install 方法,该方法在执行 Vue.use(Vuex) 时会自动调用,install 方法中主要是调用 mixin 方法,在 beforeCreate 勾子中为每个组件都混入一个$store 属性,他们都指向同一个 $store 实例。这样就可以通过 this.$store 拿到同一个实例达到数据共享的目的。

mixin.js

const applyMixin = (Vue) => {
  Vue.mixin({// 每个组件都会混入beforeCreate函数定义一个$store属性,指向同一个Sotre
    beforeCreate() {
      // 如果是根组件就为根组件添加$store属性
      if (this.$options && this.$options.store) {
        this.$store = this.$options.store;
      } else {// 如果是子组件就将根组件的$store属性值赋值给当前组件的$store属性
        this.$store = this.$parent && this.$parent.$store;
      }
    }
  })
}
export default applyMixin;

总结

vuex 原理还是很简单,很好理解的,要点在于对 Vue.mixin 的方法的应用,该方法可以让所有组件共享一个 $store 实例,实现数据共享。另一要点就是对 getters、mutattions、actions 的搜集,以及state的响应式和 getter 的缓存机制十分巧妙。最后的难点是 vuex 的模块与命名空间的实现,它的嵌套模块递归处理很复杂,当然本项目里边并未涉及,感兴趣的小伙伴可以自己去看一看。

好了,本篇博客就到这里了,我是孤城浪人,一名正在前端路上摸爬滚打的菜鸟,此项目已开源到github

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值