全家桶:vuex从使用到实现(超简单面霸必备知识点)

243 阅读4分钟

一 功能简介

vuex的定义是全局状态管理工具,通俗的说是一个管理全局变量的工具。

vue组件化的开发基础上,暴露了很多变量的通讯方式,如父向子传递使用props,子向父传递使用emit事件传参等。

尽管有这些方法,当项目达到规模后,有一些变量是希望在大量组件可用的,如登录状态,用户名,主题颜色等。

这种变量如果采用组件间传递的方式,会变得混乱繁琐。

这是我们希望有一个统一的机制,对数据进行存储和发放。这就是vuex的由来。

二 快速上手

我们先体验一下vuex,列出要实现的功能点。

npm install vuex@next

2.1 数据存储

首先我们使用vuex,将我们准备好的数据存储起来,这里使用vuex暴露的方法createStore。

import { createStore } from 'vuex'const store = createStore({ 
  state () { 
    return { count: 666 } 
  }, 
  mutations: {
    add (state) { state.count++ } 
  }
})
​
export default store

我们的数据格式是安装store的要求准备的,是一个含有state和mutations属性的对象。

将该对象作为参数调用vuex暴露的方法createStore,可以返回一个store变量,导出store。

2.2 Use注册

在main.js中引入并注册store。

// vue3版示例
import { createApp } from 'vue'
import App from './App.vue'
import store from './store/index'createApp(App)
.use(store)
.mount('#app')

2.3 数据使用

使用数据用到vuex暴露的方法useStore,这个方法可以返回一个对象,通过这个对象我们可以调用开始时传入的数据中的值和方法。

<script setup>
import {useStore} from 'vuex'
let store = useStore()
</script>
​
<template>
  {{store.state.count}}
  <button @click="store.commit('add')"></button>
</template>

三 实现解析

3.1 思路梳理

我们整理出需要暴露的两个方法。

createStore:用来存储数据,返回值可use;

useStore:用来暴露数据,返回值含有state,commit。

function createStore(options){}
function useStore(){}
​
export { createStore, useStore }

数据存储后放在哪儿?store在use(注册)时做了什么?useStore又是从哪儿取得的数据?

搞明白这些,需要做一点知识补充。

如何实现在vue中自由的使用数据而不必在意其组件位置,官网上有一段话:

我们可以使用一对 provideinject。无论组件层次结构有多深,父组件都可以作为其所有子组件的依赖提供者。这个特性有两个部分:父组件有一个 provide 选项来提供数据,子组件有一个 inject 选项来开始使用这些数据。

3.2 实现:state

整理脉络可得,实现vuex分为三步:

数据存储:在createStore中对输入数据整理后返回,返回的对象包含注册函数install和所有要用的数据。

use注册:在install中,我们使用provide()对数据全局注册到app实例上。

数据使用:在useStore中,使用inject对数据进行读取,返回读取后的对象。

import { inject } from "vue"
// 数据存储
function createStore(options){
    return new Store(options)
}
// 数据使用
function useStore(){
    return inject('__store__')
}
class Store {
    constructor(options) {
        this._state = options.state
        this._mutation = options.mutations
    }
    // use注册
    install(app) {
        app.provide('__store__', this)
    }
}
export { createStore, useStore }

这时基本功能已经可以使用了,我们可以试试。

存储数据:

import { createStore } from './gvuex'const store = createStore({
    state () {
        return { count: 123 } 
    },
    mutations: { 
        add (state: any){
            state.count++
        } 
    }
})
export default store

将这个导出的store注册。

数据使用:

<script setup>
import {useStore} from './store/gvuex'
let store = useStore()
</script><template>
  {{store._state}}
</template>

可以看到store.state是一个对象,我们可以通过store. state.count访问数据。

3.3 实现:mutation

我们在修改数据的时,希望通过commit调用mutation,进行修改。

通过调用方式我们可知:commit是挂在Store上的一个函数,根据传入的参数调用对应的mutation。

store.commit('add')

向Store类中加入commit方法。

class Store {
    ...
    commit = (type: any) => {
        this._mutation[type](this.state)
    }
}

我们知道在commit中。

如果我们传入不正确的type值怎么处理?如果我们需要携带参数怎么处理?

在原来代码基础上更改如下,我们添加了type校验和参数传递。

class Store {
    ...
    commit = (type: any, payload: any) => {
        const entry = this._mutation[type]
        entry && entry(this.state, payload)
    }
}

在页面中调用mutation事件。

<template>
    {{store._state}}
    <button @click="store.commit('add')"></button>
</template><script setup>
import {useStore} from '../store/gvuex'
let store = useStore()
</script>

但是点击按钮后,页面上的数据没有发生变化。

3.4 实现:数据响应

我们在commit中打印log。

class Store {
    ...
    commit = (type: any, payload: any) => {
        const entry = this._mutation[type]
        entry && entry(this.state, payload)
        console.log(this.state)
    }
}

点击按钮后发现,数据已经成功改变。

但是页面没有更新。

这是由于provideinject默认为非响应式操作,官网在provideinject一节出给出了响应式的解决方案。

Vue3:这是因为默认情况下,provide/inject 绑定并不是响应式的。我们可以通过传递一个 ref property 或 reactive 对象给 provide 来改变这种行为。

Vue2:provideinject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的 property 还是可响应的。

这里给出vue3的响应实现方式:

import { inject, reactive } from "vue"
...
class Store {
    constructor(options: any) {
        this._state = reactive({data: options.state()})
        this._mutation = options.mutations
    }
    get state() {
        return this._state.data
    }
    ...
}

vue2是通过new一个vue实例,数据挂在vue实例的data下,直接访问返回的vue实例就可以访问到变化的值。

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