详解如何实现一个简单的 vuex

作者: 老王420 | 来源:发表于2018-12-12 20:42 被阅读9次

    首先我们需要知道为何要使用 vuex。父子组件通信用 prop 和自定义事件可以搞定,简单的非父子组件通信用 bus(一个空的 Vue 实例)。那么使用 vuex 就是为了解决复杂的非父子组件通信。

    仅仅会使用 vuex 没什么,看过文档敲敲代码大家都会。难道你就不想知道 vuex 是如何实现的?!

    抛开 vuex 的源码,我们先来想想如何实现一个简单的 "vuex"。有多简单呢,我不要 getter、mutation、action 等,我只要 state 就行了。

    非父子组件通信

    在实现之前,我们得来温故一下 bus 的实现,借用官网的例子:

    `var` `bus =` `new` `Vue()`
    `// 触发组件 A 中的事件`
    `bus.$emit(``'id-selected'``, 1)`
    `// 在组件 B 创建的钩子中监听事件`
    `bus.$on(``'id-selected'``,` `function` `(id) {`
    `// ...`
    `})`
    

    遥想当年,实例化后的 bus 不知放哪好,最后无奈将其放到了 window 下,一直 window.bus 的使用。虽然这样也没问题,但还是影响到了全局作用域。

    突然的某一天,我发现可以挂载在 vue 的根实例下(从此告别 window.bus),于是便有了:

    `var` `app =` `new` `Vue({`
    `el:` `'#app'``,`
    `bus: bus`
    `})`
    `// 使用 bus`
    `app.$options.bus`
    `// or`
    `this``.$root.$options.bus`
    

    然后又发现了,bus 其实不只是 on 事件才可以通信。其实 bus 是一个 Vue 实例,其中 data 是响应的。比如在 app 这个根实例下有两个非父子组件,都使用到了 bus 的 data,那么它们是响应同步的。

    `var` `bus =` `new` `Vue({`
    `data: {`
    `count: 0`
    `}`
    `})`
    

    以上,子组件 a 修改了 count,如果子组件 b 有用到 count,那么它就能响应到最新 count 的值。

    说了这么多,你还没发现吗?这个不就是实现了非组件之间通信,vuex 的 state 吗?!

    封装 bus

    是的,把刚刚的 bus 封装一下,这个就是一个最简单的 "vuex" (仅仅只有 state 的功能)。首先,我们将有一个根实例 app ,实例下有两个非父子组件 childA 和 childB 。

    html 代码的实现如下:

    `<``div` `id``=``"app"``>`
    `<``child-a``></``child-a``>`
    `<``child-b``></``child-b``>`
    `</``div``>`
    

    非父子组件的实现

    然后是两个非父子组件和 app 的实现,子组件都使用到了 bus 的 count,这里用 store.state 表示,跟 vuex 一致:

    `// 待实现`
    `const store =` `new` `Store(Vue, {`
    `state: {`
    `count: 0`
    `}`
    `})`
    `// 子组件 a`
    `const childA = {`
    `template:` `'<button @click="handleClick">click me</button>'``,`
    `methods: {`
    `handleClick () {`
    `this``.$store.state.count += 1`
    `}`
    `}`
    `}`
    `// 子组件 b`
    `const childB = {`
    `template:` `'<div>count: {{ count }}</div>'``,`
    `computed: {`
    `count () {`
    `return` `this``.$store.state.count`
    `}`
    `}`
    `}`
    `new` `Vue({`
    `el:` `'#app'``,`
    `components: {`
    `'child-a'``: childA,`
    `'child-b'``: childB`
    `},`
    `store: store`
    `})`
    

    看到代码里还有一个 Store 待实现。所需要的参数,因为这里懒得用 Vue.use() ,所以直接将 Vue 作为参数传入以供使用,然后第二个参数跟我们使用 vuex 传入的参数一致。

    Store 的实现

    接下来就是 Store 的实现,两步实现:

    1. 创建一个 bus 实例;
    2. 让子组件都能访问到 this.$store。

    第 1 步骤上面已经有了,第 2 步骤主要用到了 Vue.mixin 来全局混入,但仅仅只是找到有 store 的根实例并赋值 Vue 原型上的 store,也能够让根实例 app 不用专门写 mixins 混入。

    `class Store {`
    `constructor (Vue, options) {`
    `var` `bus =` `new` `Vue({`
    `data: {`
    `state: options.state`
    `}`
    `})`
    `this``.install(Vue, bus)`
    `}`
    `install (Vue, bus) {`
    `Vue.mixin({`
    `beforeCreate () {`
    `if` `(``this``.$options.store) {`
    `Vue.prototype.$store = bus`
    `}`
    `}`
    `})`
    `}`
    `}`
    

    实现的 Store 就是一个简单的 "vuex",它拥有了 vuex 的 state,足够让非父子组件之间进行简单通信。

    在 Store 的构造函数里创建一个 bus 实例,并将其注入 Vue 的原型,实现了组件都能访问到 this.store 即 bus 实例。 this.store 就是一个 Vue 实例,所以访问了 this.$store.state.count 实际上就是访问到了 data,从而实现了非父子组件之间的响应同步。全部源码参考这里

    本次给大家推荐一个交流圈,里面概括移动应用网站开发,css,html,webpack,vue node angular以及面试资源等。
    对web开发技术感兴趣的同学,欢迎加入:582735936,不管你是小白还是大牛我都欢迎,还有大牛整理的一套高效率学习路线和教程与您免费分享,同时每天更新视频资料。
    最后,祝大家早日学有所成,拿到满意offer,快速升职加薪,走上人生巅峰。


    相关文章

      网友评论

      • nathan_ll:那个能把上面贴的代码格式整理一下吗,读起来有点累

      本文标题:详解如何实现一个简单的 vuex

      本文链接:https://www.haomeiwen.com/subject/qaaphqtx.html