首页 > 其他分享 >Vuex与Vuex-Class的底层原理简单实现

Vuex与Vuex-Class的底层原理简单实现

时间:2024-01-06 18:00:46浏览次数:28  
标签:Vue vuex options state 组件 Vuex Class store 底层

vuex-class是在class-component中使用vuex的辅助工具。

学习任何技术栈的使用,最透彻的掌握方法就是去简单实现一下,下面先简单实现一下vuex,然后基于我们自己实现的vuex再去实现一个vuex-class,彻底搞定vuex-class的使用。

首先回忆一下vuex的使用(配置)方法,首先我们需要在某个位置执行Vue.use(Vuex),然后通过new Vuex.Store的方式创建一个Store实例,在实例化Vue时将其传入配置对象:new Vue({store})

src/store/index.js

import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

export default new Vuex.Store({
    state: {
        count: 0
    }
})

main.js

import Vue from 'vue'
import App from './App.vue'
import store from './store'

Vue.config.productionTip = false

new Vue({
  store,
  render: h => h(App),
}).$mount('#app')

经过如上的配置,我们就可以在组件中通过this.$store.xxx去访问Store对象的属性,使用statecommit...

为了保留核心,删繁就简,我们自己的vuex只实现在组件中通过this.$store.state访问响应式state的能力。

实现vuex

思路分析

因为vue2中的组件其实就是一个对象,整个项目以App.vue作为根组件,与所有子组件构成了一个巨大的组件树,说白了就是一个对象树,并且基于当前组件对象可以通过$parent等属性访问组件树的相邻(父)组件。

那么我们既然要让所有组件都可以通过$store属性访问到store对象,那么就是很单纯的给所有组件都加一个$store属性就好了。

实现切入点

借助Vue的插件机制:Vue.use方法可以接收一个对象,并执行这个对象的install方法,Vue构造函数将作为install的参数,我们可以在install的逻辑中通过Vue.mixin给未来要实例化的所有组件注入逻辑!

代码实现

整个组件树挂载$store

Vuex对象中除了install方法之外,还要有一个Store构造函数(new Vuex.Store(...)),我们暂且不管这个构造函数究竟如何创建实例store,我们先让所有组件都能拿到它创建的实例store

因为在main.js中我们把store实例传给了new Vue的配置对象,也就是说可以通过根组件对象的$options.store拿到store

对于App.vue这个根组件,为了达成通过this.$store访问store对象的目的,我们完全可以在其beforeCreate生命周期中执行this.$store = this.$options.store,但是对于组件树上的任意组件我们无法访问到根组件,但熟悉vue渲染机制的话我们知道:组件的渲染是从外至内的,也就是先创建父组件再是其子组件,所以借助整个App渲染时从外至内的“遍历”顺序,我们可以轻松写出如下算法,即除了根组件之外,让所有组件都去$parent上去找store对象并给自己的$store属性赋值,因为当前组件渲染时可以确定其父组件已经完成渲染:

src/store/myVuex.js(未来替换vuex):

class Store {
    constructor(options) {
    }
}

const install = function(Vue) {
    Vue.mixin({
        beforeCreate(){
            if (this.$options && this.$options.store){ // 如果是根组件($options.store非空)
                this.$store = this.$options.store
            }else { //如果是任意子组件
                this.$store = this.$parent && this.$parent.$store
            }
        }
    })
}

const Vuex = {
    Store,
    install,
}

export default Vuex;

store数据响应式处理

现在的问题在于如何让我们Store构造函数能根据option.state创建一个响应式的state对象呢?vue2并没有像vue3中提供与组件解绑的方法(如refreactive)来创建响应式数据,所以最朴素也是唯一的一个方法就是创建一个vue组件实例,并利用它的data配置来获取一个响应式数据!

install方法的执行早于Store实例的创建,所以在install中对Vue进行引用记录,让Store的构造函数中可以使用它来创建组件。

+ let _Vue;
  class Store {
    constructor(options) {
+       this.vm = new _Vue({
+           data: {
+               state: options.state
+           }
+       })
    }
+   get state() {
+       return this.vm.state;
+   }
  }

const install = function(Vue) {
+   _Vue = Vue;
    Vue.mixin({
        beforeCreate(){
            if (this.$options && this.$options.store){
                this.$store = this.$options.store
            }else {
                this.$store = this.$parent && this.$parent.$store
            }
        }
    })
}

const Vuex = {
    Store,
    install,
}

export default Vuex;

这样我们的vuex就基本实现了,替换vuex的导入地址为:

import Vuex from './myVuex';

测试组件:

// 父组件
<template>
  <div id="app">
    vuex中的数据:count--{{ this.$store.state.count }}
    <ChildComponent />
    <ClassChildComponent />
  </div>
</template>

<script>
import ChildComponent from './components/ChildComponent.vue'

export default {
  name: 'App',
  components: {
    ChildComponent,
  },
  mounted() {
    console.log(this);
  }
}
</script>

// 子组件
<template>
  <div>
    <hr />
    child
    <button @click="addCount">addCount</button>
  </div>
</template>

<script>
export default {
  name: 'ChildComponent',
  methods: {
    addCount() {
      this.$store.state.count += 1;
    }
  }
}
</script>

子组件中点击按钮父组件ui发生变化。

实现vuex-class

同样,为了理清核心逻辑,我们只实现一下State方法,先回顾一下store中的数据如何在class-component中使用的。

import { State } from 'vuex-class';

export default class Xxx extends Vue {
  @State((state) => state.xxx) xxxS!: any;
  
  someFun() {
    // this.xxxS
  }
}

其实概括来讲就是利用装饰器在执行class代码进行组件初始化的同时,对options组件对象进行同步构建。并且在文章中详解了vue-property-decorator库的@Ref方法的实现,概括来说就是将class属性映射为options组件的计算属性,实现class组件代码中this.xxxRefthis.$refs.xxxRef的映射。

其实明白了上面的思路,这里要实现State方法就手到擒来了,与@Ref的实现可谓“换汤不换药”。

src/store/myVuexClass.js

import { createDecorator } from "vue-class-component";

export function State(selector) {
    return createDecorator((options, key) => {
        options.computed = options.computed || {};
        options.computed[key] = {
            cache: false,
            get() {
              return selector(this.$store.state);
            },
          }
    });
}

我们丢给State方法一个selector函数参数,最终构造的计算属性中调用selector时把this.$store.state扔进去让使用者去选所需的state中的状态就好了。

总结来说,如果要实现vuex-class,本质还是对于options组件对象的构建,实现State方法,是对其计算属性的构建,如果要实现mutation或者action,那么就是对option组件对象的methods的构建了,这里不再赘述。

完整附件:点此下载

标签:Vue,vuex,options,state,组件,Vuex,Class,store,底层
From: https://blog.51cto.com/u_15723831/9127371

相关文章

  • 方法补充issubclass和isinstance
    issubclass和isinstance方法补充isinstance查看对象的类型"""isinstance检查对象的类型"""print(isinstance('name',str))#Trueprint(isinstance('name',int))#FalseclassPerson():passprint(isinstance(Person,object)......
  • 【Redis深度专题】「核心技术提升」从源码角度探究Redis服务的内存使用、清理以及逐出
    背景介绍Redis作为一种高性能的内存NoSQL数据库,其容量受限于最大内存的限制。用户在使用阿里云Redis时,除了对性能和稳定性有较高的要求外,对内存占用也非常敏感。然而,在实际使用中,一些用户可能会发现他们的线上实例的内存占用比预期的要大。内存较高的场景在使用Redis时,以下是一些可......
  • 手撕Vuex-模块化共享数据上
    前言好,经过上一篇的介绍,实现了Vuex当中的actions方法,接下来我们来实现Vuex当中的模块化共享数据(modules)。modules方法用于模块化共享数据,那么什么叫模块化共享数据呢?其实非常简单。过去我们将所有模块的数据都放到state中共享,例如:我们有三个模块首页/个人中心/登录......
  • 手撕Vuex-实现actions方法
    经过上一篇章介绍,完成了实现mutations的功能,那么接下来本篇将会实现actions的功能。本篇我先介绍一下actions的作用,然后再介绍一下实现的思路,最后再实现代码。actions的作用是用来异步修改共享数据的,怎么异步修改,这个时候我们回到Vue的官方Vuex文档中,有如下这么一个图:从......
  • 手撕Vuex-实现mutations方法
    经过上一篇章介绍,完成了实现getters的功能,那么接下来本篇将会实现mutations的功能。在实现之前我们先来回顾一下mutations的使用。将官方的Vuex导入进来,因为我们的还没有实现,现用一下官方的,来演示一下mutations的使用。mutations是用来修改共享数据的,先在mutations中......
  • 手撕Vuex-实现共享数据
    经过上一篇章介绍,完成了添加全局$store,接下来就是实现共享数据的功能。在Vuex中,共享数据是通过state来实现的,所以我们需要在Nuex.js文件中实现state的功能。在Vuex中,state是一个对象,这个对象中存放的就是我们的共享数据,所以我们需要在Nuex.js文件中定义一个state对......
  • 手撕Vuex-实现getters方法
    经上一篇章介绍,完成了实现共享数据的功能,实现方式是在Store构造函数中将创建Store时将需要共享的数据添加到Store上面,这样将来我们就能通过this.$store拿到这个Store,既然能拿到这个Store,我们就可以通过.state拿到需要共享的属性。除了可以通过.state拿到共享数据之外......
  • 手撕Vuex-添加全局$store
    经过上一篇的介绍,了解到了Vuex的实现本质就是一个插件,所以要做的事情就是实现这个插件的代码编写即可。本篇文章主要是实现一个全局的$store,这个$store是挂载在Vue的原型上的,所以在任何一个组件当中都可以通过this.$store访问到。我们先来看看Vue官方的,我们分别在App.v......
  • 手撕Vuex-Vuex实现原理分析
    本章节主要围绕着手撕Vuex,那么在手撕之前,先来回顾一下Vuex的基本使用。创建一个Vuex项目,我这里采用vue-cli创建一个项目,然后安装Vuex。vuecreatevuex-demo选择Manuallyselectfeatures。这里只需要,Babel与Vuex。选择2.X版本的Vue:创建package.json:是否保存为模板......
  • 手撕Vuex-模块化共享数据下
    前言好,经过上一篇的介绍,了解了Vuex当中的模块化,本章主要介绍Vuex当中的模块化共享数据下篇。我们知道在全局的Store对象当中,我们可以定义全局的数据,那么如果我们在模块当中也定义了一个属性名称和全局的属性名称相同,那么会发生什么呢?我们先来看看,在全局当中定义了一个global......