关于VueX
VueX是适用于在Vue项目开发时使用的状态管理工具。试想一下,如果在一个项目开发中频繁的使用组件传参的方式来同步data中的值,一旦项目变得很庞大,管理和维护这些值将是相当棘手的工作。为此,Vue为这些被多个组件频繁使用的值提供了一个统一管理的工具——VueX。在具有VueX的Vue项目中,我们只需要把这些值定义在VueX中,即可在整个Vue项目的组件中使用。
VueX中的核心内容
在VueX对象中,其实不止有state,还有用来操作state中数据的方法集,以及当我们需要对state中的数据需要加工的方法集等等成员。
成员列表:
- state 存放状态;
- mutations:操作state数据的方法;
- getters 加工state成员给外界;
- actions 异步操作state数据;
- modules 模块化状态管理。
具体怎样使用,大家可以直接查看VueX官方文档,这里不再赘述。下面用源代码的方式实现一个简易版的VueX。
源代码的方式实现简易版的VueX
这里只是VueX的简单实现,更加全面的请看下一遍文章《深入解析VueX 源码》
首先实现一个VueX的install方法,Vue.use 方法默认会调用插件的install方法,此方法中的参数就是Vue的构造函数。
Vue.use = function (plugin) {
plugin.install(this);
}
install
install方法的源代码:
let Vue;
export const install = (_Vue) => {
// 插件的安装 Vue.use(Vuex)
// _Vue 是Vue的构造函数
Vue = _Vue;
// 需要将根组件中注入的store 分派给每一个组件 (子组件) Vue.mixin
Vue.mixin({
// 内部会把生命周期函数 拍平成一个数组
beforeCreate() {
// 给所有的组件增加$store 属性 指向我们创建的store实例
const options = this.$options; // 获取用户所有的选项
if (options.store) {
// 根实例挂载store
this.$store = options.store;
} else if (options.parent && options.parent.$store) {
// 儿子或者孙子依次递归从父辈获得store
this.$store = options.parent.$store;
}
},
});
};
Store构造函数
Store构造函数在实例化后,用于将VueX中的state,getters方法挂载到实例上,并且通过commit和dispatch分别调用mutations、actions对象中的函数。
Store构造函数的源代码:
// 1.功能是遍历对象
export const forEachValue = (obj, callback) => {
Object.keys(obj).forEach((key) => callback(obj[key], key));
};
export class Store {
// 容器的初始化
constructor(options) {
// options 就是你new Vuex.Store({state,mutation,actions})
const state = options.state; // 数据变化要更新视图 (vue的核心逻辑依赖收集)
// 响应式的数据 new Vue({data})
// 1.添加状态逻辑 数据在哪使用 就会收集对应的依赖
const computed = {};
// 2.处理getters属性 具有缓存的 computed 带有缓存 (多次取值是如果值不变是不会重新取值)
this.getters = {};
forEachValue(options.getters, (fn, key) => {
computed[key] = () => {
// 将用户的getters 定义在实例上, 计算属性是如何实现环翠
return fn(this.state);
};
Object.defineProperty(this.getters, key, {
// 当我取值时 执行计算属性的逻辑
get: () => this._vm[key],
});
});
// 3.计算属性的实现
this._vm = new Vue({
data: {
// 属性如果是通过$开头的 默认不会将这个属性挂载到vm上
$$state: state, // 会将$$state 对应的对象 都通过defineProperty来进行属性劫持
},
computed: computed,
});
// 4.实现mutations
this.mutations = {};
forEachValue(options.mutations, (fn, key) => {
// this.mutations = {myAge:(payload)=>用户定义的逻辑(state,payload)}
this.mutations[key] = (payload) => fn(this.state, payload);
});
// 5.实现actions
this.actions = {};
forEachValue(options.actions, (fn, key) => {
this.actions[key] = (payload) => fn(this, payload);
});
// 在严格模式下 actions 和 mutations是有区别
this.commit = (type, payload) => {
//保证当前this 当前store实例
// 调用commit其实就是去找 刚才绑定的好的mutation
this.mutations[type](payload);
};
this.dispatch = (type, payload) => {
this.actions[type](payload);
};
}
get state() {
// 属性访问器 new Store().state Object.defineProperty({get()})
return this._vm._data.$$state;
}
}
在vuex目录下的index.js将Store, install暴露出去:
import { Store, install } from './store'; //
export default {
Store,
install
}
使用
初始化store下index.js中的内容:
import Vue from "vue";
// import Vuex from "vuex";
import Vuex from "../vuex/index.js";
Vue.use(Vuex);
let store = new Vuex.Store({
state: { count: localStorage.getItem("qqq") / 1 || 100 },
getters: {
myAge(state) {
return state.count + 20;
},
},
mutations: {
syncChange(state, payload) {
state.count += payload;
localStorage.setItem("qqq", state.count);
},
},
actions: {
changeCountFn(store, payload) {
setTimeout(() => {
store.commit("syncChange", payload);
}, 500);
},
},
}
将store挂载到当前项目的Vue实例当中去
打开main.js
import Vue from 'vue'
import App from './App'
import router from './router'
import store from './store'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store, //store:store 和router一样,将我们创建的Vuex实例挂载到这个vue实例中
render: h => h(App)
})
在组件中使用Vuex
<template>
<div>
<h2>当前数字是{{$store.state.count}}</h2>
<button @click="add">+1</button>
<button @click="minus">-1</button>
</div>
</template>
<script>
export default {
name: "bro2",
methods: {
add() {
this.$store.dispatch("changeCountFn", 1);
},
minus() {
this.$store.commit("syncChange", -1);
},
},
mounted() {
console.log(this.$store);
},
};
</script>
结果:
实现了一个简易版的VueX,但是还有很多功能没有实现呀,比如模块化、命名空间、持久化等等。接下来新开一遍博客,详细实现VueX源码。