在网上有找到一些关于vuex源码分析的文章,有的写的很不错,挺详细的。但是在阅读的过程中还是发现很多地方不理解,所以搭建了环境调试,把不明白的地方逐个解决,这里把遇到的困惑记录并分享。
vuex是如何注册到全局,并通过this.$store使用的
//入口文件store.js
//在store的入口文件中,引入vue
import Vue from 'vue';
//在入口文件中引入自己手写 vuex
import Vuex from '../vuex';
//关键在于这个步骤,vue.use方法调用的时候触发Vuex实例中的install方法
Vue.use(Vuex);
//vuex.js文件
//所以在Vuex的实例中必须对外输出install方法
export default {
install
}
//全局保存下Vue
let Vue
//定义下install方法
//调用时候会传入Vue实例
const install = (_Vue) => {
//判断如果全局保存的Vue已经存在,还重新注册则抛出异常
if(Vue){
console.error('[vuex] already installed. Vue.use(Vuex) should be called only once.')
return;
}
//把vue实例赋值给全局
Vue = _vue;
//调用混入方法,把$store注册到Vue实例
privateMethods.applyMixin(_Vue);
}
//定义初始化方法(这边有点坑,这个方法不能用箭头函数,会影响this指向)
const vuexInit = function(){
//拿到当前组件实例的配置信息
const options = this.$options;
//如果配置信息里有store则,把store赋值给this.$store
if(options.store){
this.$store = options.store;
}
//如果当前没有store,但是父实例注册了$store,则把父实例的$store赋值给当前组件实例
else if(options.parent && options.parent.$store){
this.$store = this.$parent.$store;
}
}
//之前我有个疑问,第一个options.store哪里来的。再看次配置信息才发现,是在main.js中初始化vue实例中给到的
//new Vue({el : '#app',store})
//定义PrivateMethods类,把需要用到的方法放在这里
class PrivateMethods {
static applyMixin(vue){
//获取vue版本号
const version = vue.version.split('.')[0];
//如果版本大于1
if(version > 1){
const usesInit = Vue.config._lifecycleHooks.indexOf('init') > -1;
// 判断生命周期中是否有Init,有则把vuexInit放在Init中执行,如果没有则放在beforeCreate中执行
vue.mixin(usesInit ? {init : vuexInit} : {beforeCreate : vuexInit});
}
}
}
增加Store类对外输出
//文件vuex.js
//对外输出Store类
export default {
Store,
install
}
class Store{
//构造器中接收一个配置对象
constructor(options = {}){
//判断缓存的Vue实例是否存在
if(!Vue){
throw new Error(`must call Vue.use(Vuex) before creating a store instance.`);
}
//判断浏览器是否支持Promise,否则报错
//由于dispatch要用到promise实现,所以对promise依赖
if(typeof Promise === 'undefind'){
throw new Error(`vuex requires a Promise polyfill in this browser.`);
}
}
}
//这样就可以在vuex.js配置文件中这么写了
export default new Vuex.Store({})
$store.state和严格模式实现
//vuex.js文件中
class Store{
constructor(options={}){
//获取严格模式配置,默认值为flase
const {strict = false} = options;
//获取配置的state信息
let {state = {}} = options;
//如果state为函数,则state为state函数返回值
if(typeof state === 'function'){
state = state();
}
//初始化state方法
privateMethods.resetStoreVM(this,state);
}
//增加获取 state方法
get state(){
//_vm是构造的vue实例赋值在store上
//_data是通过vue实例获取data的方式
return this._vm._data.$$state
}
//增加修改state方法
//不允许直接赋值或者修改state
set state(){
throw new Error(`Use store.replaceState() to explicit replace store state.`)
}
}
//在私有方法中增加初始化state方法
class PrivateMethods{
//参数命名都比较明显,就不要解释了
resetStoreVM(store,state,hot){
//获取旧的挂载实例
const oldVm = store._vm;
//利用vue实例,实现state
store._vm = new Vue({
data : {
$$state : state
}
}
//如果定义了严格模式
if(store.strict){
//执行严格模式方法
this.enableStrictMode(store);
}
//如果存在旧的_vm实例则销毁
if(oldVm){
Vue.nextTick(()=> oldVm.$destory());
}
}
//使用严格模式
enableStrictMode(store){
//使用$watch api监控store实例中$$state变化
//当前指向是vue实例,所以不能用箭头函数
store._vm.$watch(function(){
//监控$$state
return this._data.$$state;
},()=>{
//如果不是通过commit修改state值则抛出异常
if(!store._committing){
throw new Error(`Do not mutate vuex store state outside mutation handlers.`);
}
},{
//深度监听,true
deep : true,
//监听子组件改变父组件 (vue1.0配置)
sync:true
})
}
}
$store.commit实现
//期待整理
$store.dispatch实现
//期待整理
$store.subscribe实现
//期待整理
网友评论