如何手寫簡易版的vuex插件

1. 實現思路

vuex插件主要用於統一管理vue項目中的數據流動狀態。詳細介紹見官網:vuex.vuejs.org/vue

實現簡易版的vuex的步驟以下:vuex

  • 實現插件的install方法
  • 實現Store類,並在Store類中提供commit, dispatch等方法,state只讀屬性等
  • Store類中能讀取getters裏的值

1. 源碼

直接上源碼,註釋比較清楚。bash

// rvuex.js

let Vue;

class Store{
    constructor(options){
        // 保存全部mutations
        this._mutations = options.mutations;

        // 保存全部actions
        this._actions = options.actions;

        // 處理全部getters
        // 定義computed選項
        const { computed } = this.dealGetters(options.getters);

        // 響應化處理state,當state值發生變化時,觸發渲染函數從新渲染視圖
        // 能夠經過實例化Vue,在data中設置使屬性變爲響應式
        this._vm = new Vue({
            data: {
                $$state: options.state
            },
            computed
        });

        // 綁定commit,dispatch的上下文爲當前store實例
        this.commit = this.commit.bind(this);
        this.dispatch = this.dispatch.bind(this);
    }

    // store中getters定義方式{ doubleCounter(state){return state.counter * 2} }
    // store中讀取getters裏的值:{{$store.getters.doubleCounter}}
    dealGetters(getters = {}){
        let computed = {};
        let store = this;
        store.getters = {};
        // 遍歷用戶定義的getters
        Object.keys(getters).forEach(key => {
            // 獲取用戶定義的getter
            const getter = getters[key];  // 好比 doubleCounter(state){return state.counter * 2}
            // 將getter轉換爲computed形式,computed裏的函數是無參數的
            // computed計算屬性,其實是調用getters裏的方法
            computed[key]= function(){
                return getter(store.state);
            };
            // 爲getters定義只讀屬性
            // 當讀取getters裏面的屬性值時,實際上是讀取的vue實例裏的computed計算屬性
            Object.defineProperty(store.getters, key, {
                get: () => store._vm[key]
            });
        });
        
        return {
            computed
        };
    }

    // 存取器
    get state(){
        return this._vm._data.$$state;
    }
    set state(value){
        console.error('不能直接設置state的值');
    }

    // commit mutation來觸發state的更新
    // $store.commit('add', 1)
    // params: 
    //  type: mutation的類型
    //  payload: 載荷,多餘的參數
    commit(type, payload){
        const entry = this._mutations[type];
        if(entry) {
            entry(this.state, payload);
        }
    }

    dispatch(type, payload){
        const entry = this._actions[type];
        if(entry){
            entry(this, payload);
        }
    }
}

function install(_Vue){
    Vue = _Vue;
    
    // $store的全局掛載
    Vue.mixin({
        beforeCreate() {
            // 只有根組件(入口文件)纔會傳入store
            // 而後將this.$options.store掛載到vue原型上,這樣vue組件內部能夠經過this.$store來訪問
            if(this.$options.store){
                Vue.prototype.$store = this.$options.store;
            }
        }
    })
}

// Vuex
export default {
    Store,
    install
}
複製代碼

2. 使用方法

先定義store.jsmarkdown

// store.js

import Vue from 'vue'
import Vuex from 'rvuex'  // 上面rvuex.js文件

Vue.use(Vuex)

export default new Vuex.Store({
    state: {
        counter: 0
    },
    getters:{
        doubleCounter(state){
            return state.counter * 2
        }
    },
    mutations: {
        add(state){
            state.counter ++
        }
    },
    actions: {
        // 參數爲執行上下文
        add({commit}){
            setTimeout(() => {
                commit('add')
            }, 1000)
        }
    }
})
複製代碼

在組件中使用store裏的數據。async

// index.vue

<template>
    <div>
        <p @click="$store.commit('add')">counter: {{$store.state.counter}}</p>
        <p @click="$store.dispatch('add')">async counter: {{$store.state.counter}}</p>
        <p>double counter: {{$store.getters.doubleCounter}}</p>
        <router-view />
    </div>
</template>
複製代碼

以上內容爲網上學習課程的複習總結。函數

相關文章
相關標籤/搜索