1. 程式人生 > 其它 >手寫一個簡易的vuex

手寫一個簡易的vuex

技術標籤:Vuevue前端

目錄

一、介紹

Vuex 是一個專為 Vue.js 應用程式開發的全域性狀態管理器。它的作用就是進行狀態管理,解決複雜元件通訊,資料共享。

參考連結:

下面將實現一個簡單的Vuex 的功能,如不想看前置知識可以直接跳到最後的程式碼實現部分。

二、前置知識

1、Vuex的核心概念

  • Store:每個應用僅有一個Store,是一個容器
    • 匯入 Vuex
    • 註冊 Vuex
    • 注入 $store 到 Vue 例項
  • state:狀態(響應式)
  • getters:類似計算屬性,用於派生其他值(內部對計算結果進行快取,只有狀態發生改變時才重新計算)
  • mutations:只能同步,用於修改State,所有的狀態更改必須通過Mutations
  • actions:支援非同步,提交多個Mutation
  • modules:模組,每個都有自己的state、getters、mutations、actions甚至是子模組modules

2、Vuex的基本使用

// ./store/index.js
// 1.匯入
import Vue from 'vue'
import Vuex from 'vuex'
// 2. 註冊外掛 Vue.use(Vuex) // 3.建立Vuex的Store物件,並匯出 export default new Vuex.Store({ state: {}, getters: {}, mutations: {}, actions: {}, modules: {} })
// main.js
import store from './store'

// 建立Vue例項時注入store選項
new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

3、元件內的狀態管理

Vue

框架來說,最核心的兩個功能:資料驅動元件化

而每個元件都有自己的狀態、檢視和行為等組成部分。比如Vue的一個例項化程式碼:

new Vue({
    // state
    data () {
        return {
            count: 0
        }
    },
    // view
    template: `    <div>{{ count }}</div>  `,
    // actions
    methods: {
        increment () {
            this.count++
        }
    }
})

狀態管理應包含以下幾部分:

  • state,驅動應用的資料來源;
  • view,以宣告方式將 state 對映到檢視;
  • actions,響應在 view 上的使用者輸入導致的狀態變化。

4、什麼情況下使用 Vuex

引用官方文件:

Vuex 可以幫助我們管理共享狀態,並附帶了更多的概念和框架。這需要對短期和長期效益進行權衡。

如果您不打算開發大型單頁應用,使用 Vuex 可能是繁瑣冗餘的。確實是如此——如果您的應用夠簡單,您最好不要使用 Vuex。一個簡單的 store 模式就足夠您所需了。但是,如果您需要構建 一箇中大型單頁應用,您很可能會考慮如何更好地在元件外部管理狀態,Vuex 將會成為自然而然 的選擇。
引用 Redux 的作者 Dan Abramov 的話說就是:Flux 架構就像眼鏡:您自會知道什麼時候需要它。

當你的應用中具有以下需求場景的時候:

  • 多個檢視依賴於同一狀態
  • 來自不同檢視的行為需要變更同一狀態

建議符合這種場景的業務使用 Vuex 來進行資料管理,例如非常典型的場景:購物車、全域性設定。

注意:Vuex 不要濫用,不符合以上需求的業務不要使用,反而會讓你的應用變得更麻煩。

三、Vuex實現

實現思路

  • 1.實現 install 方法
    • Vuex 是 Vue 的一個外掛,所以和模擬 VueRouter 類似,先實現 Vue 外掛約定的 install 方法
  • 2.實現 Store 類
    • 實現建構函式,接收 options
    • state 的響應化處理
    • getter 的實現
    • commit、dispatch 方法

1、install 方法

let _Vue = null
function install (Vue) {
    _Vue = Vue
    _Vue.mixin({
        beforeCreate () {
            if (this.$options.store) {
                // 在Vue原型掛載$store物件
                Vue.prototype.$store = this.$options.store
            }
        }
    })
}

2、Store 類

class Store {
    constructor (options) {
        const {
            state = {},
            getters = {},
            mutations = {},
            actions = {}
        } = options
        this.state = _Vue.observable(state)
        // 此處不直接 this.getters = getters,是因為下面的程式碼中要方法 getters 中的 key
        // 如果這麼寫的話,會導致 this.getters 和 getters 指向同一個物件
        // 當訪問 getters 的 key 的時候,實際上就是訪問 this.getters 的 key 會觸發 key 屬性的 getter
        // 會產生死遞迴
        this.getters = Object.create(null)

        Object.keys(getters).forEach(key => {
            Object.defineProperty(this.getters, key, {
                get: () => getters[key](this.state)
            })
        })
        this.mutations = mutations
        this.actions = actions
    }
	commit (type, payload) {
    	this.mutations[type](this.state, payload)
    }
    dispatch (type, payload) {
    	this.actions[type](this, payload)
    }
}
// 匯出模組
export default {
    Store,
    install
}

四、執行測試效果

通過vue-cli建立一個包含vuex的專案:vue create my-vuex

src資料夾下新建一個my-vuex資料夾,用於存放自己手寫的vuex。

找到store資料夾下的index.js,將原本import的程式碼替換為自己的vuex,並寫一些簡單的vuex狀態程式碼:

import Vue from 'vue'
// import Vuex from 'vuex'
import Vuex from '../my-vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    count: 0,
    msg: 'Hello World'
  },
  getters: {
    reverseMsg (state) {
      return state.msg.split('').reverse().join('')
    }
  },
  mutations: {
    increate (state, payload) {
      state.count += payload
    }
  },
  actions: {
    increateAsync (context, payload) {
      setTimeout(() => {
        context.commit('increate', payload)
      }, 2000)
    }
  }
})

App.vue裡寫一些html標籤和JS事件,用於觸發vuex的方法,比如:

<template>
  <div id="app">
    <h1>Vuex - Demo</h1>
    count:{{ $store.state.count }} <br>
    msg: {{ $store.state.msg }}

    <h2>Getter</h2>
    reverseMsg: {{ $store.getters.reverseMsg }}

    <h2>Mutation</h2>
    <button @click="$store.commit('increate', 2)">Mutation</button>

    <h2>Action</h2>
    <button @click="$store.dispatch('increateAsync', 5)">Action</button>
  </div>
</template>

最後啟動專案 yarn serve,檢視效果,實現了最簡單的狀態管理功能。