WEB架構師學習筆記_Vue_03簡單實現Vuex

知識導航

  • vuex的使用
  • Vuex的基本實現

知識點:
在這裏插入圖片描述

1. vuex的使用

Vuex想必我們都很熟悉了,它就是實現全局狀態的一種管理機制。即可以實現所有組件共享同一數據。

1.1 實例化store時傳入的參數

state:放數據;mutations:改變數據的一些方法;actions:異步方法或一些複雜邏輯;getters:類似於計算屬性,對數據的包裝。

{
    state: {
        count: 0
    },
    mutations: {
        add(state, a) {
            state.count += a;
        },
        sub(state, step) {
            state.count = state.count - step;
        }
    },
    actions: {
    //第一個參數是希望給一個store的實例,因爲只有store實例纔有commit方法
        asyncAdd(context, step) {
            setTimeout(function() {
                context.commit("add", step);
            }, 1000)
        },
        asyncsub(context, step) {
            setTimeout(function() {
                context.commit("sub", step);
            }, 1000)
        }
    },
    getters: {
        showData(state) {
            return `數字:${state.count}`
        }
    },
    modules: {}
}

1.2 組件1中的使用(我比較喜歡的方式)

commit:可調用mutations中的方法,dispatch:可調用actions中的方法

<template>
    <div>
        {{$store.state.count}}
        {{$store.getters.showData}}
        <button @click="add"></button>
         <button @click="addAsy">延時加</button>
    </div>
</template>

<script>
export default {
    methods: {
        add(){
            this.$store.commit("add",2);
        },
        addAsy(){
            this.$store.dispatch('asyncAdd', 3)
        }
    },
}
</script>

1.2 組件2中的使用

這裏只是用了一下vuex提供的一些輔助函數

<template>
  <div>
    {{$store.state.count}}{{showData()}}
    <button @click="subHander"></button>
    <button @click="subHanderAsy">延時減</button>
  </div>
</template>

<script>
import { mapMutations, mapActions,mapGetters } from "vuex";
export default {
  methods: {
    ...mapMutations(["sub"]),//mapMutations輔助函數可將this.sub()映射爲this.$store.commit('sub'),因爲我們可能要的不止是一個所以用下擴展運算符展開它
    ...mapActions(["asyncsub"]),
    ...mapGetters(['showData']),

    subHander() {
      this.sub(2);
    },
    subHanderAsy() {
      this.asyncsub(2);
    }
  }
};
</script>

2. Vuex的基本實現

2.1 老樣子,還是看使用時做了哪些工作

配置js中:
在這裏插入圖片描述
入口文件main.js中:
在這裏插入圖片描述

思路

通過觀察我們可以知道插件中要實現的事情。

  • 實現install方法
  • 接收參數
  • 實現commit和dispatch
  • 注意getters(我們在組件中使用getters中的東西時是這樣this.$store.getters.showData。這可不是一個方法的調用。它更像一個去對象中取值的操作。其實仔細觀察它其實更類似於雙向數據綁定,即數據一變,數據包裝跟着改變,getters裏的方法只是決定了如何包裝數據而已)
  1. 實現install
    這裏的主要工作還是用混入將這個store實例掛到vue原型上去
function install(_vue) {
    Vue = _vue;
    Vue.mixin({
        beforeCreate() {
            if (this.$options.store) {
                Vue.prototype.$store = this.$options.store;
            }
        },
    })
}
  1. 接收參數
    接收參數分別存儲時要注意state中的數據是響應式的
class Store {
    constructor(args = {}) {
        this.state = new Vue({
            data: args.state
        });
        this.actions = args.actions || {};
        this.mutations = args.mutations || {};
       
    }
  1. 實現commit
    cmmit要做的工作很簡單,觀察到組件中使用commit時會傳一個mutations 中的方法名,和一些其他的參數
   commit(fnName, arg) {
        let fn = this.mutations[fnName];//利用傳過來的方法名去mutations找對應的函數
        fn(this.state, arg);//調用這個函數即可,傳入要改的state數據源和組件中該過來的參數
    }
  1. 實現dispatch
    它的實現和commit類似,只不過因爲它裏面要調用的可能是有返回值的異步函數。所以可能需要返回函數執行結果。同時這次調用參數中的方法時要傳的第一個參數是這個store實例,或者說是此處的上下文
    dispatch(fnName, arg) {
        let fn = this.actions[fnName];
        return fn(this, arg);
    }
  1. 包裝數據
    這個實現getters應該是目前這裏最難的,首先在接收參數時我們先要判斷參數中是否有getters的東西,有則需要一個方法來處理它。這個方法要實現什麼功能呢?其實也很簡單就是把傳過來的getters中的方法全部執行一遍。把執行完的數據存起來就可以了。

我首先想到的是像這樣(錯誤示例):

    gettersHander(objGetFn) {
        this.getters = {};
        Object.keys(objGetFn).forEach(key => {
            this.getters[key] = objGetFn[key](this.state);
        })
    }

我因爲this.state是響應式的,故只需state的狀態一發生變化我this.getters中會跟着變化。但是這裏程序好像不是按照我期望的流程走的。
我現在是希望在訪問 this.getters中數據時有一段劫持代碼用來做中間件來處理一下,即一訪問這個 this.getters中數據,中間件會幫我們更新this.getters中的數據。以到達響應式的結果。

可以實現數據劫持的有兩個方法:Object.defineProperty()和Proxy。(後面還會用到這裏不寫使用了,去MDN看一下就行)
修改上面代碼:

    gettersHander(objGetFn) {
        this.getters = {};

        Object.keys(objGetFn).forEach(key => {
            Object.defineProperty(this.getters, key, {
                get: () => {
                    return objGetFn[key](this.state);
                }
            })
        })

    }

效果:
在這裏插入圖片描述
全部代碼:

// 思路
// 1. 首先實現install
// 2. 接收參數並找分類容器保存
// 3. 將state裏面的數據做出響應式
// 4. 實現三方法,commit,dispath
let Vue;

function install(_vue) {
    Vue = _vue;
    Vue.mixin({
        beforeCreate() {
            if (this.$options.store) {
                Vue.prototype.$store = this.$options.store;
            }
        },
    })
}

class Store {
    constructor(args = {}) {
        this.state = new Vue({
            data: args.state
        });
        this.actions = args.actions || {};
        this.mutations = args.mutations || {};
        args.getters && this.gettersHander(args.getters);
    }
    commit(fnName, arg) {
        let fn = this.mutations[fnName];
        fn(this.state, arg);
    }
    dispatch(fnName, arg) {
        let fn = this.actions[fnName];
        return fn(this, arg);
    }
    gettersHander(objGetFn) {
        this.getters = {};

        Object.keys(objGetFn).forEach(key => {
            Object.defineProperty(this.getters, key, {
                get: () => {
                    return objGetFn[key](this.state);
                }
            })
        })

    }

}
export default { install, Store }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章