目录
Vuex
vuex基础
阶段小结(vuex的四种对象)
vuex响应式原理
vuex.js的实现
index.js
Module
局部状态
命名空间
跨模块访问数据
小结
目标
- Vuex相关基本概念和使用方式
 - vuex响应式原理过程
 - vuex中如何进行模块式开发
 
Vue2.0是单向数据流的

Vuex
vuex基础
Vuex使用单一的Store状态树,包含全部层级状态
const store = new Vuex.Store({
    state: { // 状态值
        count: 0    
    },
    getter: {// 衍生值, 计算属性,变化后会再计算
      doubleCount: state => state.count * 2      
    },
    mutations: { // 变更state属性,同步函数
        addCount (state,payload=1) {
            state.count += payload    
        }    
    },
    actions: {// 异步操作
        asyncAddCount({commit}) { // 结构获得commit方法
            setTimeout(() => {
            	commit('addCount');// 通知状态变更 count++
            },1500);        
        }    
    }
}) 

cmd指令 :vue add vuex
快速在vue工程中添加vuex依赖
// 父组件简写
{{count}}{{count2}}{{doubleCount}}
import {mapState, mapGetters, mapMutations, mapActions} from "vuex"
// map方法,快速开发
export default {
    name: "App",
    components: {
        HelloWord    
    },
    computed: {
        // ...mapState(["count"])// 映射下面
        // count() {
        //   return this.$store.state.count;        
        // },
        // ---------------------------------
        ...mapGetters(["doubleCount"])// 映射下面
        // doubleCount() { // 派生属性
        //  return this.$store.getters.doubleCount;            
        // },
        // ---------------------------------
        ...mapState({ // 映射
            count: state => state.count, // 自定义变量
            count2: state => state.count // 别名修改   
        }),
    },
    methods: {
        ...mapMutations(["addCount"]),// 对象扩展语法 @click="addcount(2)"
        //addCount() {
        //    this.$store.commit("addCount", 3) // mutations  
        //},
        // ---------------------------------
        ...mapActions(["asyncAddCount"]),// 对象扩展语法
        //asyncAddCount() {
        //    // dispatch("【actions里面约定的函数】asyncAddCount") 异步
        //   this.$store.dispatch("asyncAddCount"); // actions   1.5s后操作变更
        }
    }
}
 
vuex大大简化了组件通信的过程
// 子组件简写
<button @click="addCount(2)">addCount</button>
<button @click="asyncAddCount">asyncAddCount</button>
import {mapMutations, mapActions} from "vuex"
methods: {
        ...mapMutations(["addCount"]),// 对象扩展语法 @click="addcount(2)"
        //addCount() {
        //    this.$store.commit("addCount", 3) // mutations  
        //},
        // ---------------------------------
        ...mapActions(["asyncAddCount"]),// 对象扩展语法
        //asyncAddCount() {
        //    // dispatch("【actions里面约定的函数】asyncAddCount") 异步
        //   this.$store.dispatch("asyncAddCount"); // actions   1.5s后操作变更
        }
    } 
阶段小结(vuex的四种对象)
- State【单一Store、响应式】
 - Getter【由state派生出的状态、store的计算属性】
 - Mutations【由commit触发、必须是同步函数】
 - Actions【由dispatch触发、可以包含任意异步操作、不直接变更状态,可以通过mutation变化】
 
Mutations—修改—>State—响应式—>Vue Component【响应式变化,进行渲染】
vuex响应式和vue响应式串联,使vuex里的变化同步到vue组件中
vuex响应式原理
vuex.js的实现
import { reactive } from "./index.js"; // 响应式劫持
export class Store { // export 暴露Store
    // 构造器
    constructor(options = {}) {
        let { state, mutations, plugins, actions, getter } = options;// plugins插件
        this._vm = reactive(state);// reactive响应式劫持
        this._mutations = mutations;
        
        this._subscribe = [] // 订阅函数队列
        // 初始化之后
        // 遍历,
        plugins.forEach(plugin => {
            plugin(this) // 每个插件执行一下,把Store传进去     
        })
    }
    get state() {
        return this._vm;    
    }
    
    commit(type, payload) {// 执行_muations的type函数
        const entry = this._muations[type];// 通过枚举取出  this._muations[type]  
        if(!entry) {
        	return;          
        }
        entry(this.state, payload);
        
        this._subscribe.forEach(sub => sub({type, payload}, this.state));
    }
    
    subscribe(fn) {
        if(!this._subscribe.includes(fn)) {// 判断fn不在订阅队列里
           this._subscribe.push(fn);          
        }           
    }
} 
index.js
let active;
let watch = function(cb) {
  active = cb;
  active();
  active = null;
};
let queue = [];
let nextTick = cb => Promise.resolve().then(cb);
let queueJob = job => {
  if (!queue.includes(job)) {
    queue.push(job);
    nextTick(flushJobs);
  }
};
let flushJobs = () => {
  let job;
  while ((job = queue.shift()) !== undefined) {
    job();
  }
};
class Dep {
  constructor() {
    this.deps = new Set();
  }
  depend() {
    if (active) {
      this.deps.add(active);
    }
  }
  notify() {
    this.deps.forEach(dep => queueJob(dep));
  }
}
let ref = initValue => {
  let value = initValue;
  let dep = new Dep();
  return Object.defineProperty({}, "value", {
    get() {
      dep.depend();
      return value;
    },
    set(newValue) {
      value = newValue;
      dep.notify();
    }
  });
};
let createReactive = (target, prop, value) => {
  let dep = new Dep();
  // return new Proxy(target, {
  //   get(target, prop) {
  //     dep.depend();
  //     return Reflect.get(target, prop);
  //   },
  //   set(target, prop, value) {
  //     Reflect.set(target, prop, value);
  //     dep.notify();
  //   },
  // });
  return Object.defineProperty(target, prop, {
    get() {
      dep.depend();
      return value;
    },
    set(newValue) {
      value = newValue;
      dep.notify();
    }
  });
};
export let reacitve = obj => {
  let dep = new Dep();
  Object.keys(obj).forEach(key => {
    let value = obj[key];
    createReactive(obj, key, value);
  });
  return obj;
};
// let data = reacitve({
//   count: 0
// });
import { Store } from "./vuex";
let store = new Store({
  state: {
    count: 0
  },
  mutations: {
    addCount(state, payload = 1) {
      state.count += payload;
    }
  },
  plugins: [// 定义一个插件
  	function(store) {
       // 订阅mutation, state
      store.subscribe((mutation, state) => {
        console.log(mutation);// 监听mutation变化,如日志上报
      })    
   },
    store =>
      store.subscribe((mutation, state) => {
        console.log(mutation);
      })
  ]
});
document.getElementById("add").addEventListener("click", function() {
  // data.count++;
  store.commit("addCount", 1);
});
let str;
watch(() => {
  str = `hello ${store.state.count}`;
  document.getElementById("app").innerText = str;
}); 
Module
因为使用了单一对象树,应用的所有状态都会集中在一个比较大的对象当中,应用变的复杂时,Store对象就会变的非常臃肿
为了解决上面的问题,Vuex允许我们将Store分割成模块
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 的状态 
局部状态
const moduleA = {
    state: { count: 0 },
    mutations: {
        increment (state) {
            //这里的`state`对象时模块的局部状态
            state.count++        
        }    
    },
    actions: {
        doubleCount (state, getters) {
            return state.count * 2        
        }    
    },
    getters: {
        incrementIfOddOnRootSum ({state, commit}) {
            if(state.count%2 === 1) {
                commit('increment')            
            }        
        }    
    }
} 
命名空间
默认情况下,模块内部action、mutation和getter是注册在全局命名空间的,这就导致不同的module中间出现命名冲突问题const
const store = new Vuex.Store({
    modules: {
    	moduleA: {
         	 namespaced: true, // 命名空间开启
            state: { count: 0 },
            mutations: {
                increment (state) {// 使用时->mutations['moduleA/increment']
                    //这里的`state`对象时模块的局部状态
                    state.count++        
                }    
            },
            actions: {
                doubleCount (state, getters) {// actions['moduleA/doubleCount']
                    return state.count * 2        
                }    
            },
            getters: {
                incrementWhenOdd ({state, commit}) {// getters['moduleA/incrementWhenOdd']
                    if(state.count%2 === 1) {
                        commit('increment')            
                    }        
                }    
            }
        }    
    }
}) 
跨模块访问数据
modules: {
    foo: {
        namespaced: true,
        getters: {
            // 在中国模块的getter中,`getters`被局部化了
            // 你可以使用getter的第4个参数来调用`rootGetters`
            someGetter(state, getters, rootState, rootGetters) {
                getters.someOtherGetter// -> 'foo/someOtherGetter' foo命名空间下的方法
                rootGetters.someOtherGetter// -> 'someOtherGetter' 根模块的方法           
            },
            someOtherGetter: state => {...}        
        },
        actions: {
            // 在这个模块中,dispatch和commit也被局部化了
            // 它们可以接受`root`属性以访问根dispatch或commit
            someAction({dispatch,commit,getters,rootGetters}) {
                getters.someGetter // -> 'foo/someGetter' foo命名空间下的方法
                rooGetters.someGetter// -> 'someGetter'  根模块的方法    
                
                dispatch('someOtherAction') // -> 'foo/someOtherAction'
                dispatch('someOtherAction', null, { root: true}) //到根模块事件 -> 'someOtherAction'                                                                                                               
            		
                commit('someMutation') // -> 'foo/someMutation'
            	 commit('someMutation', null, { root: true}) // ->到根模块事件 'someMutation'
            },
            someOtherAction(ctx, payload) {...}       
        }
        // Mutations 在设计时候,不会暴露rootState,rootGetters,Mutation都是易于测试的同步纯函数,
        // 所以获取rootState这种具有副作用的参数,统一需要大家放在action里面操作
        // 在action里去拼接rootState和rootGetters和当前的state
        // 在通过commit的纯函数去修改我们的state
    }
} 
小结
- vuex响应式源码分析
 - 如何去通过vuex的插件机制去订阅mutation的改变
 - 在vuex当中模块化设计module
 



















