一、核心原理
-
Vuex本质是一个对象
-
Vuex对象有两个属性,一个是install方法,一个是Store这个类
-
install方法的作用是将store这个实例挂载到所有的组件上,注意是同一个store实例。
-
Store这个类拥有commit,dispatch这些方法,Store类里将用户传入的state包装成data,作为new Vue的参数,从而实现了state 值的响应式。
二丶先搭建一个vue项目
- 可以看到vuex中主要就是引入了vuex和调用Vue.use()调用插件的方法
- 调用Vuex类下面的Store
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
},
mutations: {
},
actions: {
},
modules: {
}
})
三丶重写vuex包
- 先替换vuex中的引入路径
import Vue from 'vue'
import Vuex from './../vuex/index'
Vue.use(Vuex)
- 引入intall方法和Strore类
四丶install方法的实现
- 在beforeCreate中混入保证每个组件中都挂载上$store
- 保证我们在每个组件中可以拿到$store
export let Vue;
function install(_Vue) {
Vue = _Vue;
Vue.mixin({
beforeCreate(){ //this代表的是每个组件实例
// 获取根组件上的store 将他共享给每个组件
// 每个组件中都应该有$store
let options= this.$options;
if(options.store){
// 根
// console.log('根',options.name)
this.$store = options.store
}else{
// 先保证他是一个子组件,并且父亲上有$store
if(this.$parent && this.$parent.$store){
this.$store = this.$parent.$store
}
}
}
})
}
// 父 this.$store -》 子 this.$store -》孙子 this.$store
export default install
五丶store的实现
- 首先创建一个Store类,并接受options的参数
class Store {
constructor(options) {
console.log(options)
}
}
export default Store
//可以看到ooptions中输出的z值就是我们在new Vuex.Store()传入的参数
{
"state": {},
"getters": {},
"mutations": {},
"actions": {},
"modules": {}
}
2.初始化state,重点:为保证数据的双向绑定原理,将数据放在data中
import { Vue } from './install'
class Store {
constructor(options) {
let {state,getters,mutations,actions} = options
this._vm = new Vue({
data:{
$$state:state
}
})
}
//类的属性访问器
get state(){
return this._vm._data.$$state
}
}
export default Store
- 将数据放在state中,然后首页中打印出来,可以在页面中看到输入
state: {
age:18,
sex:'男'
},
<div id="app">
<div>年龄:{{$store.state.age}}</div>
<div>性别:{{$store.state.sex}}</div>
</div>
3.getters的实现
- 如何实现getters中的缓存功能?是否考虑到computed中的数据是具有缓存的,并且根据对应数据的变化而变化,思考清楚了,那我们来实现gettters
let {state,getters,mutations,actions} = options
this.getters = {}
const computed = {}
Object.keys(getters).forEach((key) => {
computed[key] =() => {
return getters[key](this.state) //保证参数是state
}
Object.defineProperty(this.getters,key,{
get:() => this._vm[key] //具备了缓存功能
})
})
// 这个状态在页面渲染时需要收集对应的渲染watcher,这样状态更新才会更新视图
this._vm = new Vue({
data:{
$$state:state
},
computed
})
- 当我们改变state中的age,页面中的值也会发生相应的变化
4.mutations和actions的实现
- mutations和actions就是将用户传入的数据遍历挂载在Store自身属性上
- dispatch和commit实现就是根据对应的类型,找对应的存储结果
this.mutations = {}
Object.keys(mutations).forEach(key => {
this.mutations[key] = (payload) => mutations[key].call(this,this.state,payload)
})
this.actions ={}
Object.keys(mutations).forEach(key => {
this.actions[key] = (payload) => actions[key].call(this,this.state,payload)
})
dispatch = (type, payload) => {
this.actions[type](payload)
}
commit = (type, payload) => {
this.mutations[type](payload)
}
5.代码优化
- 上面代码中getters和mutations和actions中都是在循环调用,于是我们提出一个共同方法
const forEach = (obj,fn)=>{
Object.keys(obj).forEach((key)=>{
fn(obj[key],key)
})
}
- 优化完成后,Store中代码
import { Vue } from './install'
// import { forEach } from './util'
const forEach = (obj,fn)=>{
Object.keys(obj).forEach((key)=>{
fn(obj[key],key)
})
}
class Store { // new Vue.Store 缠身一个实例
constructor(options) {
// 以下这些变量都是用户传递的
let { state, getters, mutations, actions, module, strict } = options;
this.getters = {}; // 我再取getters属性的时候 把他代理到计算属性上
const computed = {};
forEach(getters, (fn, key) => {
computed[key] = () => {
return fn(this.state); // 为了保证参数是state
}
// 当我们去getters上取值 需要对computed取值
Object.defineProperty(this.getters, key, {
get: () => this._vm[key] // 具备了缓存的功能
})
});
// ----------
this.mutations = {};
forEach(mutations, (fn, key) => {
this.mutations[key] = (payload) => fn.call(this, this.state, payload);
});
// ------dispatch中派发的是动作,里面可以有异步逻辑,更改状态都要通过mutation,mutation是同步更改的-------
this.actions = {}
forEach(actions, (fn, key) => {
this.actions[key] = (payload) => fn.call(this, this, payload);
});
// 这个状态在页面渲染时需要收集对应的渲染watcher,这样状态更新才会更新视图
this._vm = new Vue({
data: { // $符号开头的数据不会被挂载到实例上,但是会挂载到当前的_data上,减少了一次代理
$$state: state // 状态在哪里取值,就会收集对应的依赖
},
computed
});
// 用户组件中使用的$store = this
}
// 类的属性访问器
get state() { // this.$store.state => defineProperty中的get
// 依赖于 vue的响应式原理
return this._vm._data.$$state
}
dispatch = (type, payload) => { // 根据commit还是dispatch 找对应的存储结果
this.actions[type](payload)
}
commit = (type, payload) => {
this.mutations[type](payload)
}
}
// state getters action mutation (modules 分层)
export default Store;