vue狀態管理演進
概述
在vue中涉及到比較複雜的資料流轉、互動,我們一般都會考慮用vux來進行資料的狀態管理。經常使用,時常想它是怎麼實現的,嘗試簡易實現一下。
- 以選舉日為例,一般的元件寫法
<template>
<div>
<h1>Election day!</h1>
<button @click="voteForRed">Vote for ?</button>
<button @click="voteForBlue">Vote for ?</button>
<h2>Results</h2>
<results :red="red" :blue="blue" />
<total-votes :total="red + blue" />
</div>
</template>
<script>
const TotalVotes = {
props: ['total'],
render (h) {
return h('div', `Total votes: ${this.total}`)
}
}
const Results = {
props: ['red', 'blue'],
render (h) {
return h('div', `Red: ${this.red} - Blue: ${this.blue}`)
}
}
export default {
components: { TotalVotes, Results, },
data: () => ({ red: 0, blue: 0 }),
methods: {
voteForRed () { this.red++ },
voteForBlue () { this.blue++ },
}
}
</script>
複製程式碼
- 隔離狀態 讓我們建立一個state物件,並從那裡管理我們的整轉態。
const state = {
red: 0,
blue: 0,
}
const TotalVotes = {
render: h => h('div', `Total votes: ${state.red + state.blue}`)
}
const Results = {
render: h => h('div', `Red: ${state.red} - Blue: ${state.blue}`),
}
// ...and, inside our main component,...
methods: {
voteForRed () { state.red++ },
voteForBlue () { state.blue++ },
},
複製程式碼
遺憾的是這不能正常工作,這是為什麼?因為Vue使用資料方法來觸發其“響應式反應”。如果不將資料傳遞給data,Vue將無法跟蹤值更改並更新我們的元件以作為響應,更新檢視。修改一下。
<template>
<div>
<h1>Election day!</h1>
<button @click="voteForRed">Vote for ?</button>
<button @click="voteForBlue">Vote for ?</button>
<h2>Results</h2>
<results/>
<total-votes/>
</div>
</template>
<script>
const state = {
red: 0,
blue: 0,
}
const TotalVotes = {
data () { return state },
render (h) {
return h('div', `Total votes: ${this.red + this.blue}`)
},
}
const Results = {
data () { return state },
render (h) {
return h('div', `Red: ${this.red} - Blue: ${this.blue}`)
},
}
export default {
components: { TotalVotes, Results },
data () { return state },
methods: {
voteForRed () { this.red++ },
voteForBlue () { this.blue++ },
},
}
</script>
複製程式碼
注意
-
元件TotalVotes、Results的props已經去除
-
每一個元件在state中註冊了state,Vue能夠追蹤狀態變化,因此當我們投票給所有元件時,所有元件都會以適當的值進行重新渲染
-
渲染函式中刪除了箭頭函式
-
上面的實現方式存在缺陷,每個元件都要註冊state,耦合性較高,繼續改進
-
建立共享Vue例項以保持響應式(資料變化觸發檢視更新)
import Vue from 'vue'
const state = new Vue({
data () {
return { red: 0, blue: 0 }
},
methods: {
voteForRed () { this.red++ },
voteForBlue () { this.blue++ },
},
})
const TotalVotes = {
render: h => h('div', `Total votes: ${state.red + state.blue}`),
}
const Results = {
render: h => h('div', `Red: ${state.red} - Blue: ${state.blue}`),
}
export default {
components: { TotalVotes, Results },
methods: {
voteForRed () { state.voteForRed() },
voteForBlue () { state.voteForBlue() },
},
}
複製程式碼
至此實現了簡單的狀態管理,但我們的解決方案目前無法在專案之間共享。我需要建立一個Vue例項,填充其資料方法,然後註冊一些方法來修改狀態,繼續封裝。
- 封裝
<!--模板-->
<template>
<div>
<h1>Election day!</h1>
<button @click="voteForRed">Vote for ?</button>
<button @click="voteForBlue">Vote for ?</button>
<h2>Results</h2>
<results :red="red" :blue="blue" />
<total-votes :total="red + blue" />
</div>
</template>
<!--js-->
<script>
import Vue from 'vue'
const createStore = ({state, mutations}) => {
return new Vue({
data () {
return {state}
},
methods: {
commit (mutationName) {
mutations[mutationName](this.state)
}
}
})
}
const store = createStore({
state: { red: 0, blue: 0 },
mutations: {
voteForRed (state) { state.red++ },
voteForBlue (state) { state.blue++ }
}
})
const TotalVotes = {
render: h => h('div', `Total votes: ${store.state.red + store.state.blue}`)
}
const Results = {
render: h => h('div', `Red: ${store.state.red} - Blue: ${store.state.blue}`)
}
export default {
components: { TotalVotes, Results },
methods: {
voteForRed () { store.commit('voteForRed') },
voteForBlue () { store.commit('voteForBlue') }
}
}
</script>
複製程式碼