封装的插件,install和vue.use()的关系
对于一个插件,如果是用vue.use来使用,则这个插件里面必须是有一个install方法的,这个方法里面可以自定义一些内容,比如说把某一个组件挂载到vue实例上,所以使用该插件的时候,可以直接使用this.$toast
vuex封装
mixins 混入
新建一个vuex.js,用来替代原本的vuex(自己封装的时候注意注释掉原本的vuex,引入我们新疆的vuejs)
https://cn.vuejs.org/v2/guide/mixins.html 混入 (mixin) 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。一个混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被“混合”进入该组件本身的选项。 |
---|
为什么每个vue对象,都可以获取到this.$store.state?
- 其实就是用到了mixins,给根组件(App.vue)混入一个mixins,这个mixins对象里面就循环给每个组件都添加上了store实例
mixins操作就是放在了vuex自身的install方法中,宿主项目使用Vue.use(vuex)的时候进行调用
const install = (_Vue) => {
// vue内部的use方法,会把当前的vue对象传递到install函数里面
Vue = _Vue;
// 给每个vue实例绑定store
Vue.mixin({
// mixin混入,作用于每个vue实例
beforeCreate () {
// this.$options包括了当前组件的一些属性
if (this.$options && this.$options.store) {
// $store为我们自己命名的
this.$store = this.$options.store;
} else {
this.$store = this.$parent && this.$parent.$store;
}
}
});
}
store类
初始化类
根据原生vuex的写,store会到处一个index,里面会new出来一个store,可以说明,store在源码中,实际上是一个类,所以首先我们现在vuex.js中定义一个class为Storeclass Store {
constructor(options) {
this.state = options.state;
}
}
export default {
install,
Store
}
初始化的时候,是可以正常拿到state里面的值,但是如果我想要修改,怎么做呢?
mounted() {
setTimeout(() => {
this.$store.state.age = 100;
}, 1000);
}
可以发现,这种修改是不会引起视图更新的,所以我们需要使用vue自带的data属性来进行绑定,添加state的监听
class Store {
constructor(options) {
// 使用vue自动的数据监听
this._s = new Vue({
data: {
state: options.state
}
});
}
// 由于上面赋值用的是this._s,所以需要有一个获取state的
get state() {
return this._s.state;
}
}
到这儿的话,就可以做到在每个组件中拿到store,并且可以试试修改state的值,然后同步到视图上
getters 类似于计算属性
根据原生的vuex,可以在任意组件拿到getter的值,比如:this.$store.getters.getAge
getters实际上就是store里面的一个属性,所以我们可以在store类中进行定义
// 定义getters
let getters = options.getters || {};
this.getters = {};
接下来我们要做的,就是拿到getter里面的函数名的时候,调用那个函数,也就是给this.getters定义立即执行的属性,这里我们就用到了Object.defineProperty们vue的双向数据绑定就是利用了这个函数,此处用这个是为了实时返回这个函数对应的计算值,getters实际上是要返回一个值
先封装一个forEach方法,为了方便操作,第一个参数是要循环的对象,第二个是要对这个对象进行操作的函数
// 封装forEach
const forEach = (obj, fn) => {
Object.keys(obj).forEach(objName => {
fn(objName, obj[objName]);
});
};
将getters的函数名放到this.getters上,同时将对应的函数返回值定义到this.getters对应的方法名称上
let getters = options.getters || {};
this.getters = {};
forEach(getters, (getterName, fn) => {
Object.defineProperty(this.getters, getterName, {
get: () => {
return fn(this.state);
}
});
});
mutations 同步操作
vuex的同步操作,可以直接使用mutation来进行提交,也就是this.$store.commit(mutation函数名,参数),接下来我们定义mutations,按照我们平常的操作,其实mutations里面是一个一个的函数,我们先分别把自定义的mutations对应到this.$store上面// 定义mutations
let mutations = options.mutations || {};
this.mutations = {};
forEach(mutations, (mutationName, fn) => {
this.mutations[mutationName] = (payload) => {
// 默认需要传递一个state,因为commit中没有传
fn(this.state, payload);
};
});
上面是对应好mutations了,通过commit方法就可以对state进行修改,这是如果做到的呢?
封装好的commit方法只是传入了mutation的方法名称和改变的值,所以直接根据this.mutations找到对应方法,然后执行这个方法即可
- 所以在this.mutations里面,对应方法的时候,需要自己默认传state值,这样才能将commit中传入的值赋给state
actions 异步操作// 某一个vue文件
this.$store.commit('add', 10);
// vuex.js========================
class Store {
constructor(options) {
/**
* 中间省略
*/
// 定义mutations
// ...........省略
}
commit(mutationName, payload) {
this.mutations[mutationName](payload);
}
}
同理,actions的定义和mutations一样,只不过我们看看action在原生中是如何使用的,调用action,实际上是再调用了一次commit操作,另外,在调用commit之前,可以进行一些异步的操作
下面我们看定义aciton,唯一跟mutation的区别就在于,调用定义store实例的action对应方法时候,默认传递的不再是this.state了,而是this,因为action操作实际上是需要再调用一次commit,所以需要把这个this传递过去才行this.$store.dispatch('minus', 1);
到此,我们的vuex最基本的代码就写出来了class Store {
constructor(options) {
/**
* 中间省略
*/
// 定义mutations
// ...........省略
// 定义actions
let actions = options.actions || {};
this.actions = {};
forEach(actions, (actionName, fn) => {
this.actions[actionName] = (payload) => {
fn(this, payload);
};
});
}
// 由于在actions方法传递出去了this,所以内部封装的action需要使用箭头函数,这样才能找到正确的this对象
commit = (mutationName, payload) => {
this.mutations[mutationName](payload);
}
// 这个地方其实是可以不用箭头函数的,但是我们可以统一
dispatch = (actionName, payload) => {
this.actions[actionName](payload);
}
}