手寫一個簡易的vuex
目錄
一、介紹
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
,檢視效果,實現了最簡單的狀態管理功能。