WEB架构师学习笔记_Vue_03简单实现Vuex

知识导航

  • vuex的使用
  • Vuex的基本实现

知识点:
在这里插入图片描述

1. vuex的使用

Vuex想必我们都很熟悉了,它就是实现全局状态的一种管理机制。即可以实现所有组件共享同一数据。

1.1 实例化store时传入的参数

state:放数据;mutations:改变数据的一些方法;actions:异步方法或一些复杂逻辑;getters:类似于计算属性,对数据的包装。

{
    state: {
        count: 0
    },
    mutations: {
        add(state, a) {
            state.count += a;
        },
        sub(state, step) {
            state.count = state.count - step;
        }
    },
    actions: {
    //第一个参数是希望给一个store的实例,因为只有store实例才有commit方法
        asyncAdd(context, step) {
            setTimeout(function() {
                context.commit("add", step);
            }, 1000)
        },
        asyncsub(context, step) {
            setTimeout(function() {
                context.commit("sub", step);
            }, 1000)
        }
    },
    getters: {
        showData(state) {
            return `数字:${state.count}`
        }
    },
    modules: {}
}

1.2 组件1中的使用(我比较喜欢的方式)

commit:可调用mutations中的方法,dispatch:可调用actions中的方法

<template>
    <div>
        {{$store.state.count}}
        {{$store.getters.showData}}
        <button @click="add"></button>
         <button @click="addAsy">延时加</button>
    </div>
</template>

<script>
export default {
    methods: {
        add(){
            this.$store.commit("add",2);
        },
        addAsy(){
            this.$store.dispatch('asyncAdd', 3)
        }
    },
}
</script>

1.2 组件2中的使用

这里只是用了一下vuex提供的一些辅助函数

<template>
  <div>
    {{$store.state.count}}{{showData()}}
    <button @click="subHander"></button>
    <button @click="subHanderAsy">延时减</button>
  </div>
</template>

<script>
import { mapMutations, mapActions,mapGetters } from "vuex";
export default {
  methods: {
    ...mapMutations(["sub"]),//mapMutations辅助函数可将this.sub()映射为this.$store.commit('sub'),因为我们可能要的不止是一个所以用下扩展运算符展开它
    ...mapActions(["asyncsub"]),
    ...mapGetters(['showData']),

    subHander() {
      this.sub(2);
    },
    subHanderAsy() {
      this.asyncsub(2);
    }
  }
};
</script>

2. Vuex的基本实现

2.1 老样子,还是看使用时做了哪些工作

配置js中:
在这里插入图片描述
入口文件main.js中:
在这里插入图片描述

思路

通过观察我们可以知道插件中要实现的事情。

  • 实现install方法
  • 接收参数
  • 实现commit和dispatch
  • 注意getters(我们在组件中使用getters中的东西时是这样this.$store.getters.showData。这可不是一个方法的调用。它更像一个去对象中取值的操作。其实仔细观察它其实更类似于双向数据绑定,即数据一变,数据包装跟着改变,getters里的方法只是决定了如何包装数据而已)
  1. 实现install
    这里的主要工作还是用混入将这个store实例挂到vue原型上去
function install(_vue) {
    Vue = _vue;
    Vue.mixin({
        beforeCreate() {
            if (this.$options.store) {
                Vue.prototype.$store = this.$options.store;
            }
        },
    })
}
  1. 接收参数
    接收参数分别存储时要注意state中的数据是响应式的
class Store {
    constructor(args = {}) {
        this.state = new Vue({
            data: args.state
        });
        this.actions = args.actions || {};
        this.mutations = args.mutations || {};
       
    }
  1. 实现commit
    cmmit要做的工作很简单,观察到组件中使用commit时会传一个mutations 中的方法名,和一些其他的参数
   commit(fnName, arg) {
        let fn = this.mutations[fnName];//利用传过来的方法名去mutations找对应的函数
        fn(this.state, arg);//调用这个函数即可,传入要改的state数据源和组件中该过来的参数
    }
  1. 实现dispatch
    它的实现和commit类似,只不过因为它里面要调用的可能是有返回值的异步函数。所以可能需要返回函数执行结果。同时这次调用参数中的方法时要传的第一个参数是这个store实例,或者说是此处的上下文
    dispatch(fnName, arg) {
        let fn = this.actions[fnName];
        return fn(this, arg);
    }
  1. 包装数据
    这个实现getters应该是目前这里最难的,首先在接收参数时我们先要判断参数中是否有getters的东西,有则需要一个方法来处理它。这个方法要实现什么功能呢?其实也很简单就是把传过来的getters中的方法全部执行一遍。把执行完的数据存起来就可以了。

我首先想到的是像这样(错误示例):

    gettersHander(objGetFn) {
        this.getters = {};
        Object.keys(objGetFn).forEach(key => {
            this.getters[key] = objGetFn[key](this.state);
        })
    }

我因为this.state是响应式的,故只需state的状态一发生变化我this.getters中会跟着变化。但是这里程序好像不是按照我期望的流程走的。
我现在是希望在访问 this.getters中数据时有一段劫持代码用来做中间件来处理一下,即一访问这个 this.getters中数据,中间件会帮我们更新this.getters中的数据。以到达响应式的结果。

可以实现数据劫持的有两个方法:Object.defineProperty()和Proxy。(后面还会用到这里不写使用了,去MDN看一下就行)
修改上面代码:

    gettersHander(objGetFn) {
        this.getters = {};

        Object.keys(objGetFn).forEach(key => {
            Object.defineProperty(this.getters, key, {
                get: () => {
                    return objGetFn[key](this.state);
                }
            })
        })

    }

效果:
在这里插入图片描述
全部代码:

// 思路
// 1. 首先实现install
// 2. 接收参数并找分类容器保存
// 3. 将state里面的数据做出响应式
// 4. 实现三方法,commit,dispath
let Vue;

function install(_vue) {
    Vue = _vue;
    Vue.mixin({
        beforeCreate() {
            if (this.$options.store) {
                Vue.prototype.$store = this.$options.store;
            }
        },
    })
}

class Store {
    constructor(args = {}) {
        this.state = new Vue({
            data: args.state
        });
        this.actions = args.actions || {};
        this.mutations = args.mutations || {};
        args.getters && this.gettersHander(args.getters);
    }
    commit(fnName, arg) {
        let fn = this.mutations[fnName];
        fn(this.state, arg);
    }
    dispatch(fnName, arg) {
        let fn = this.actions[fnName];
        return fn(this, arg);
    }
    gettersHander(objGetFn) {
        this.getters = {};

        Object.keys(objGetFn).forEach(key => {
            Object.defineProperty(this.getters, key, {
                get: () => {
                    return objGetFn[key](this.state);
                }
            })
        })

    }

}
export default { install, Store }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章