Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的全部组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。vue
一般咱们能够这样来使用vuex
new Vue({
// state
data () {
return {
count: 0
}
},
// view
template: ` <div>{{ count }}</div> `,
// actions
methods: {
increment () {
this.count++
}
}
})
复制代码
这个状态自管理应用包含如下几个部分:数据结构
vuex的核心就是定义了一个store(容器)来管理应用的的数据状态,与单纯的全局对象数据存储相比,vuex的功能更强大。异步
在项目中使用vuex
**相信下面的代码,用vue开发过项目的人都很熟悉import Vue from 'vue'
import Vuex from 'vuex'
// 这里会执行vuex的install方法
Vue.use(Vuex);
export default new Vuex.store({
state: { // 统一的状态管理
age:10,
a:100
},
getters:{
// 计算属性
myAge(state){ // object.defineProperty
return state.age + 18;
}
},
mutations: { // 能够更改状态
syncAdd(state,payload){ // $store.commit()
state.age += payload;
},
syncMinus(state,payload){
state.age -= payload
}
},
actions: { // 异步提交更改
asyncMinus({commit},payload){ // action 异步获取完后 提交到mutation中
setTimeout(()=>{
commit('syncMinus',payload);
},1000)
}
}
})
复制代码
这样咱们就能够在项目中集中管理公用的状态了。那么vuex是怎么实现这一切的呢?vuex的入口提供的install
方法就能够完成初始化工做async
const install = (_vue) => {
//vue.use 传入vue的构造函数
Vue = _vue;
// 给vue的组件实例都混入一个钩子函数
Vue.mixin({
beforeCreate() {
// 首先 store是放在根组件上,咱们须要获取,并给每一个组件挂上 即 this.$store = store
if(this.$options && this.$options.store) {
// 给根实例增长$store属性
this.$store = this.$options.store
}else{
// 其余组件只需从它的父级获取
this.$store = this.$parent && this.$parent.$store
}
}
})
}
// store类
class Store{
}
export default {
install,
Store
}
复制代码
vuex的初始化工做很简单,咱们在 import Vuex 以后,会实例化其中的 Store 对象,返回 store 实例并传入 new Vue 的 options 中,也就是咱们刚才提到的 options.store。Store 对象的构造函数接收一个对象参数,它包含 actions、getters、state、mutations、modules 等 Vuex 的核心概念。函数
// 迭代对象的 会将对象的 key 和value 拿到
const forEach = (obj,cb)=>{
Object.keys(obj).forEach(key=>{
cb(key,obj[key]);
})
}
class Store {
constructor(options={}) {
// 将用户的状态放到了store中 定义了响应式变化 数据更新 更新视图
this.s = new Vue({
data() {
return {
state: options.state
}
}
})
this.getters = {};
this.mutations = {};
this.actions = {};
// 计算属性
forEach(options.getters,(getterName,fn)=>{
Object.defineProperty(this.getters,getterName,{
get() {
return fn(this.state)
}
})
})
//mutations
forEach(options.mutations,(mutationName,fn)=>{
this.mutations[mutationName] = (payload)=>{
// 内部的第一个参数是状态
fn(this.state,payload)
}
})
//actions
forEach(options.actions,(actionName,fn)=>{
this.actions[actionName] = (payload)=>{
fn(this,payload)
}
})
}
get state(){ // 类的属性访问器
return this.s.state
}
//提交更改 找到对应的mutations函数执行
commit = (mutationName,payload)=>{
this.mutations[mutationName](payload)
}
dispatch = (actionName,payload)=>{
this.actions[actionName](payload)
}
}
复制代码
到此,一个简陋的vuex状态管理就实现了,可是把全部的状态都放到store存储,那么这个对象会变的十分的庞大,为了解决以上问题,Vuex 容许咱们将 store 分割成模块(module)。每一个模块拥有本身的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行一样方式的分割:ui
const moduleA = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: { ... },
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})
store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态
复制代码
vuex的模块是默认是没有强制命名空间的,除了state按模块区分了,若是模块中的matutions和actions和主对象有同名,那么他们会一块儿执行。this
class Store {
constructor(options = {}){
this.s = new Vue({
data(){
return {state:options.state}
}
});
this.getters = {};
this.mutations = {};
this.actions = {};
// 模块把数据格式化成一个有层次的树状结构,方面后面解析
this._modules = new ModuleCollection(options);
}
get state() {
return this.s.state
}
}
复制代码
从数据结构上来看,模块的设计就是一个树型结构,store 自己能够理解为一个 root module,它下面的 modules 就是子模块,Vuex 须要完成这颗树的构建spa
// 模块的数据结构
let root = {
_raw:options,
_chidlren:{
a:{
_raw:{},
_chidlren:{},
state:{a:1}
},
b:{}
},
state:options.state
}
复制代码
class ModuleCollection {
constructor(options) {
this.register([], options); // 注册模块 将模块注册成树结构
}
register(path,rootModule) {
let module = { // 将模块格式化
_rawModule: rootModule,
_chidlren: {},
state: rootModule.state
}
if(path.length ==0 ) {
// 若是是根模块 将这个模块挂在到根实例上
this.root = module;
}else {
// 若是是modules 里面的内容
// _children 属性 找到上一级路径
let parent = path.slice(0,-1).reduce((root,current)=>{
return root._children[current]
},this.root)
parent._children[path[path.length - 1]] = module
}
//看当前模块是否有modules
if(rootModule.modules) {
forEach(rootModule.modules,(moduleName, module) => {
this.register(path.concat(moduleName),module)
})
}
}
}
复制代码
ModuleCollection 实例化的过程就是执行了 register 方法,在源码中register有3个参数,其中 path 表示路径,由于咱们总体目标是要构建一颗模块树,path 是在构建树的过程当中维护的路径;rawModule 表示定义模块的原始配置,另外一个是runtime表示是不是一个运行时建立的模块,这里不影响。设计
初始化模块后,咱们就有了一个模块的树状结构,咱们就能够对对模块中的 state、getters、mutations、actions 作初始化工做,把它们和store对象链接起来。
/* rootState:表示根上面的状态 path: 模块路径 rootMudule: 当前模块对象内容 */
const installModule = (store, rootState, path, rootModule) => {
if(path.length > 0) {
let parent = path.slice(0,-1).reduce((root,currnet)=>{
return root[current]
},rootSate)
// 找到模块对应得状态,按路径 挂在 store.state
Vue.set(parent,path[path.lenght-1],rootModule.state)
}
// 处理 模块中 getters actions mutation
let getters = rootModule._rawModule.getters;
if(getters) {
forEach(getters,(getterName,fn)=>{
Object.defineProperty(store.getters,getterName,{
get() {
// 让getter执行当本身的状态 传入
return fn(rootModule.state); // 让对应的函数执行
}
})
})
}
let mutations = rootModule._rawModule.mutations;
if (mutations) {
forEach(mutations, (mutationName, fn) => {
let mutations = store.mutations[mutationName] || [];
// 默认状况下,没有强制命名空间,因此 同名得mutations 会一块儿执行
mutations.push((payload) => {
fn(rootModule.state, payload);
})
store.mutations[mutationName] = mutations;
})
}
let actions = rootModule._rawModule.actions;
if (actions) {
forEach(actions, (actionName, fn) => {
let actions = store.actions[actionName] || [];
actions.push((payload) => {
fn(store, payload);
})
store.actions[actionName] = actions;
})
}
// 处理_children里面得module
forEach(rootModule._chidlren,(moduleName, module) => {
installModule(store, rootState, path.concat(moduleName), module)
})
}
复制代码
有了installModule
这个处理模块数据得方法后,咱们再改下Store的代码:
class Store {
constructor(options = {}) {
// 将用户的状态放到了store中
this.s = new Vue({ // 核心 定义了响应式变化 数据更新 更新视图
data() {
return { state: options.state }
}
}); // 用来维护全局数据的
this.getters = {};
this.mutations = {};
this.actions = {};
this._subscribes = [];
this._modules = new ModuleCollection(options); // 把数据格式化成一个 想要的树结构
// this._modules.root 从根模块开始安装
installModule(this, this.state, [], this._modules.root);
}
// 提交更改 会在当前的 store上 找到对应的函数执行
commit = (mutationName, payload) => { // 保证this
this.mutations[mutationName].forEach(fn => fn(payload))
}
dispatch = (actionName, payload) => {
this.actions[actionName](payload); //源码里有一个变量 来控制是不是经过mutation 来更新状态的
}
get state() { // 类的属性访问器
return this.s.state
}
}
复制代码
以上就是一个简易vuex的完整代码了,有错误的地方请你们指正。