封装的插件,install和vue.use()的关系

对于一个插件,如果是用vue.use来使用,则这个插件里面必须是有一个install方法的,这个方法里面可以自定义一些内容,比如说把某一个组件挂载到vue实例上,所以使用该插件的时候,可以直接使用this.$toast

https://blog.csdn.net/Fabulous1111/article/details/88696006

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)的时候进行调用

    1. const install = (_Vue) => {
    2. // vue内部的use方法,会把当前的vue对象传递到install函数里面
    3. Vue = _Vue;
    4. // 给每个vue实例绑定store
    5. Vue.mixin({
    6. // mixin混入,作用于每个vue实例
    7. beforeCreate () {
    8. // this.$options包括了当前组件的一些属性
    9. if (this.$options && this.$options.store) {
    10. // $store为我们自己命名的
    11. this.$store = this.$options.store;
    12. } else {
    13. this.$store = this.$parent && this.$parent.$store;
    14. }
    15. }
    16. });
    17. }

    store类
    初始化类
    根据原生vuex的写,store会到处一个index,里面会new出来一个store,可以说明,store在源码中,实际上是一个类,所以首先我们现在vuex.js中定义一个class为Store

    1. class Store {
    2. constructor(options) {
    3. this.state = options.state;
    4. }
    5. }
    6. export default {
    7. install,
    8. Store
    9. }

    初始化的时候,是可以正常拿到state里面的值,但是如果我想要修改,怎么做呢?

    1. mounted() {
    2. setTimeout(() => {
    3. this.$store.state.age = 100;
    4. }, 1000);
    5. }

    可以发现,这种修改是不会引起视图更新的,所以我们需要使用vue自带的data属性来进行绑定,添加state的监听

    1. class Store {
    2. constructor(options) {
    3. // 使用vue自动的数据监听
    4. this._s = new Vue({
    5. data: {
    6. state: options.state
    7. }
    8. });
    9. }
    10. // 由于上面赋值用的是this._s,所以需要有一个获取state的
    11. get state() {
    12. return this._s.state;
    13. }
    14. }

    到这儿的话,就可以做到在每个组件中拿到store,并且可以试试修改state的值,然后同步到视图上
    getters 类似于计算属性
    根据原生的vuex,可以在任意组件拿到getter的值,比如:

    1. this.$store.getters.getAge

    getters实际上就是store里面的一个属性,所以我们可以在store类中进行定义

    1. // 定义getters
    2. let getters = options.getters || {};
    3. this.getters = {};

    接下来我们要做的,就是拿到getter里面的函数名的时候,调用那个函数,也就是给this.getters定义立即执行的属性,这里我们就用到了Object.defineProperty们vue的双向数据绑定就是利用了这个函数,此处用这个是为了实时返回这个函数对应的计算值,getters实际上是要返回一个值

  • 先封装一个forEach方法,为了方便操作,第一个参数是要循环的对象,第二个是要对这个对象进行操作的函数

    1. // 封装forEach
    2. const forEach = (obj, fn) => {
    3. Object.keys(obj).forEach(objName => {
    4. fn(objName, obj[objName]);
    5. });
    6. };
  • 将getters的函数名放到this.getters上,同时将对应的函数返回值定义到this.getters对应的方法名称上

    1. let getters = options.getters || {};
    2. this.getters = {};
    3. forEach(getters, (getterName, fn) => {
    4. Object.defineProperty(this.getters, getterName, {
    5. get: () => {
    6. return fn(this.state);
    7. }
    8. });
    9. });

    mutations 同步操作
    vuex的同步操作,可以直接使用mutation来进行提交,也就是this.$store.commit(mutation函数名,参数),接下来我们定义mutations,按照我们平常的操作,其实mutations里面是一个一个的函数,我们先分别把自定义的mutations对应到this.$store上面

    1. // 定义mutations
    2. let mutations = options.mutations || {};
    3. this.mutations = {};
    4. forEach(mutations, (mutationName, fn) => {
    5. this.mutations[mutationName] = (payload) => {
    6. // 默认需要传递一个state,因为commit中没有传
    7. fn(this.state, payload);
    8. };
    9. });

    上面是对应好mutations了,通过commit方法就可以对state进行修改,这是如果做到的呢?

  • 封装好的commit方法只是传入了mutation的方法名称和改变的值,所以直接根据this.mutations找到对应方法,然后执行这个方法即可

  • 所以在this.mutations里面,对应方法的时候,需要自己默认传state值,这样才能将commit中传入的值赋给state
    1. // 某一个vue文件
    2. this.$store.commit('add', 10);
    3. // vuex.js========================
    4. class Store {
    5. constructor(options) {
    6. /**
    7. * 中间省略
    8. */
    9. // 定义mutations
    10. // ...........省略
    11. }
    12. commit(mutationName, payload) {
    13. this.mutations[mutationName](payload);
    14. }
    15. }
    actions 异步操作
    同理,actions的定义和mutations一样,只不过我们看看action在原生中是如何使用的,调用action,实际上是再调用了一次commit操作,另外,在调用commit之前,可以进行一些异步的操作
    1. this.$store.dispatch('minus', 1);
    下面我们看定义aciton,唯一跟mutation的区别就在于,调用定义store实例的action对应方法时候,默认传递的不再是this.state了,而是this,因为action操作实际上是需要再调用一次commit,所以需要把这个this传递过去才行
    1. class Store {
    2. constructor(options) {
    3. /**
    4. * 中间省略
    5. */
    6. // 定义mutations
    7. // ...........省略
    8. // 定义actions
    9. let actions = options.actions || {};
    10. this.actions = {};
    11. forEach(actions, (actionName, fn) => {
    12. this.actions[actionName] = (payload) => {
    13. fn(this, payload);
    14. };
    15. });
    16. }
    17. // 由于在actions方法传递出去了this,所以内部封装的action需要使用箭头函数,这样才能找到正确的this对象
    18. commit = (mutationName, payload) => {
    19. this.mutations[mutationName](payload);
    20. }
    21. // 这个地方其实是可以不用箭头函数的,但是我们可以统一
    22. dispatch = (actionName, payload) => {
    23. this.actions[actionName](payload);
    24. }
    25. }
    到此,我们的vuex最基本的代码就写出来了