zoukankan      html  css  js  c++  java
  • 了解Vuex4.x 简单实现原理

    参考文档:

    Vuex是什么

    Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

    这个状态自管理应用包含以下几个部分:

    • 状态,驱动应用的数据源;
    • 视图,以声明方式将状态映射到视图;
    • 操作,响应在视图上的用户输入导致的状态变化。

    Vuex的工作原理如下图所示:
    Vuex

    Vuex4.x的简单用法

    创建一个 store 实例

    import { createApp } from 'vue'
    import { createStore } from 'vuex'
    
    // 创建一个新的 store 实例
    const store = createStore({
      state () {
        return {
          count: 0
        }
      },
      getters: {
          double(state){
              return state.count * 2
          }
      },
      mutations: {
        increment (state, payload) {
          state.count++
        }
      },
      actions: {
         AsyncIncrement(state, payload){
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    commit('someMutation', payload)
                    resolve()
                }, 1000)
            })
         }
      }
    })
    
    const app = createApp({ /* 根组件 */ })
    
    // 将 store 实例作为插件安装
    app.use(store)
    

    通过组合式API的方法使用

    访问 State 和 Getter

    import { computed } from 'vue'
    import { useStore } from 'vuex'
    
    export default {
      setup () {
        const store = useStore()
    
        return {
          // 在 computed 函数中访问 state
          count: computed(() => store.state.count),
    
          // 在 computed 函数中访问 getter
          double: computed(() => store.getters.double)
        }
      }
    }
    

    访问 Mutation 和 Action

    import { useStore } from 'vuex'
    
    export default {
      setup () {
        const store = useStore()
    
        return {
          // 使用 mutation
          increment: () => store.commit('increment'),
    
          // 使用 action
          asyncIncrement: () => store.dispatch('asyncIncrement')
        }
      }
    }
    

    从用法上对比Vuex3.x

    • 创建 store 的方法不同,之前是通过new Store() 来创建一个 store实例。现在是通过 createStore 方法来创建。
    • Vuex4.x兼容之前的使用方法。但是在Vue3.x中如果我们想要通过组合式API的方法使用Vuex,我们需要借助与 useStore 这个函数。

    实现一个简单的 Vuex4.x;

    实现一个 createStore方法

    store实际上还是基于一个Class来实现的,只是没有直接将 Store 这个类暴露出来,而是包装成了一个函数,直接返回实例。

    import { reactive } from 'vue'
    
    export function createStore(options){
        return new Store(options)
    }
    
    class Store{
        constructor(options){
            const store = this;
            // 使用reactive使state是响应式的
            store._state = reactive({data: options.state})
        }
        get state(){ 
            return this._state.data
        }
    }
    
    

    通过 Object.defineProperty 来重新定义getters

    将用户传入的getters 全部定义到store实例的getters属性上,在获取值的时候调用

    function forEachValue(obj, fn){
        Object.keys(obj).forEach(key => fn(obj[key], key))
    }
    
    class Store{
        constructor(options){
            const store = this;
    
            // 使用reactive使state是响应式的
            store._state = reactive({data: options.state})
            
            // 保存getters
            const _getters = options.getters;
    
            store.getters = {};
    
            forEachValue(_getters, function(fn, key){
                Object.defineProperty(store.getters, key, {
                    get: ()=> fn(store.state),
                    enumerable: true
                })
            })
        }
    }
    

    基于发布订阅模式实现 mutationsactions

    class Store{
        constructor(options){
            // ...
            const store = this;
            store._mutations = Object.create(null)
            const _mutations = options.mutations;
    
            forEachValue(_mutations, (mutation, key)=>{
                store._mutations[key] = (payload) => {
                    mutation.call(store, store.state, payload)
                }
            })
        }
        commit = (type, payload) => {
            this._mutations[type](payload)
        }
    }
    

    Action 类似于 mutation,不同在于:

    • Action 提交的是 mutation,而不是直接变更状态。
    • Action 可以包含任意异步操作

    由于action可以执行异步函数,所以action在执行完成后应该返回一个Promise实例,对dispact的执行结果进行判断,如果不是一个Promise,需要给处理成一个promise。

    class Store{
        constructor(options){
            // ...
            const store = this;
    
            store._actions = Object.create(null)
    
            const _actions = options.actions;
        
            forEachValue(_actions, (action, key)=>{
                store._actions[key] = (payload) => {
                    const res = action.call(store, store.state, payload)
                    if(!isPromise(res)){
                        return Promise.resolve(res)
                    }
                    return res
                }
            })
        }
        dispatch = (type, payload) => {
            this._actions[type](payload)
        }
    }
    

    useStore

    通过inject来获取注册的store实例

    import {inject} from 'vue'
    export function useStore(injectKey = null) {
        return inject('store')
    }
    
    

    install

    在Vue项目中使用Vuex,我们需要通过use()来注册store

    
    const app = createApp({ /* 根组件 */ })
    
    // 将 store 实例作为插件安装
    app.use(store)
    
    
    class Store{
        install(app, injectKey){ 
            // Vue3.x createApp().use(store)
            // 全局暴露一个 变量,暴露的是store的实例
            app.provide('store')
    
            // Vue2.x Vue.prototype.$store = this
            // 增添$store属性 使得可以直接在模板中使用 $store.state.count
            app.config.globalProperties.$store = this;
        }
    }
    
    
  • 相关阅读:
    Spring学习之旅(二)--容器
    Spring学习之旅(一)--初始Spring
    Logback的使用
    DES加解密工具类
    Lombok插件的使用
    from 表单用 GET 方法进行 URL 传值时后台无法获取问题
    组播
    linux头文件路径
    IANA
    6号板获取或放文件
  • 原文地址:https://www.cnblogs.com/recode-hyh/p/15302642.html
Copyright © 2011-2022 走看看