用vue-cli 與vuex一步一步搭建一個筆記應用(三)
前面我們僅僅只是搭建了一個簡單的介面,並沒有使用vuex來進行資料管理。
下面我們開始怎麼使用vuex,因為我也是初學,所以一邊學一邊寫吧。
vuex有中文文件:其實講得很清除 https://vuex.vuejs.org/zh-cn
第一步 安裝vuex
cnpm install vuex -g --save-dev
為什麼要使用vuex?
在vuex文件裡寫得很清楚,一個vue就是這樣的,有data()(狀態),有模板template(檢視),有方法methods(狀態變化)
new Vue({
// state
data () {
return {
count : 0
}
},
// view
template: `
<div>{{ count }}</div>
`,
// actions
methods: {
increment () {
this.count++
}
}
})
這個狀態自管理應用包含以下幾個部分:
state,驅動應用的資料來源;
view,以宣告方式將state對映到檢視;
actions,響應在view上的使用者輸入導致的狀態變化。
以下是一個表示“單向資料流”理念的極簡示意:
但是這個僅僅是vue元件內部的狀態。
那麼多個元件共享狀態又該怎麼辦呢?於是vue提出了vuex。
但是,當我們的應用遇到多個元件共享狀態時,單向資料流的簡潔性很容易被破壞:
- 多個檢視依賴於同一狀態。
- 來自不同檢視的行為需要變更同一狀態。
對於問題一,傳參的方法對於多層巢狀的元件將會非常繁瑣,並且對於兄弟元件間的狀態傳遞無能為力。對於問題二,我們經常會採用父子元件直接引用或者通過事件來變更和同步狀態的多份拷貝。以上的這些模式非常脆弱,通常會導致無法維護的程式碼。
因此,我們為什麼不把元件的共享狀態抽取出來,以一個全域性單例模式管理呢?在這種模式下,我們的元件樹構成了一個巨大的“檢視”,不管在樹的哪個位置,任何元件都能獲取狀態或者觸發行為!
Vuex 把狀態分成元件內部狀態和應用級別狀態:
- 元件內部狀態:僅在一個元件內使用的狀態(data 欄位)
- 應用級別狀態:多個元件共用的狀態
Vuex 要解決的就是這些問題,Vuex 背後有四個核心的概念:
- 狀態樹: 包含所有應用級別狀態的物件
- Getters: 在元件內部獲取 store 中狀態的函式
- Mutations: 修改狀態的事件回撥函式
- Actions: 元件內部用來分發 mutations 事件的函式
Vuex流圖:
對這張圖的理解
- 資料流動是單向的
- 元件可以呼叫 actions
- Actions 是用來分發 mutations 的
- 只有 mutations 可以修改狀態
- store 是反應式的,即,狀態的變化會在元件內部得到反映
測試
vuex應用的核心是倉庫store,有兩點值得說明,
1、vuex狀態儲存是響應式的;
2、不能直接改變store中的狀態,唯一途經是顯式的提交mutation(mutation的中文翻譯是突變)
按照vuex文件中的程式碼測試了一下,寫在main.js中,這裡定義了一個vuex 的store,有狀態state,和mutation,這裡的mutation我的理解是方法,可以去改變state的方法。在外部通過呼叫mutation來改變state儲存的資料。
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment(state) {
state.count++
}
}
});
store.commit('increment')
console.log(store.state.count) // -> 1
vuex概念解析
還是原來的store
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment(state) {
state.count++
},
decrement: state => state.count--
}
});
Vuex 通過 store 選項,提供了一種機制將狀態從根元件『注入』到每一個子元件中(需呼叫 Vue.use(Vuex)):
在根目錄注入store
new Vue({
el: '#app',
template: '<App/>',
store,
components: {
App
}
})
在我們的app.vue中通過this.$store呼叫,可以看到這裡並沒有import store,而且如果寫成了store.commit(‘increment’),會報錯說store沒有定義
app.vue程式碼:
template部分
<button @click="increment">+</button>
<button @click="decrement">-</button>
<p>{{count}}</p>
script部分:
<script>
import Hello from './components/Hello'
import Toolbar from './components/Toolbar.vue'
import NoteList from './components/NoteList.vue'
import Editor from './components/Editor.vue'
export default {
name: 'app',
components: {
Toolbar,
NoteList,
Editor
},
computed:{
count(){
return this.$store.state.count
}
},
methods: {
increment () {
this.$store.commit('increment')
},
decrement () {
this.$store.commit('decrement')
}
}
}
</script>
state
如上面的例子,從 store 例項中讀取狀態最簡單的方法就是在計算屬性computed中返回某個狀態
getters
有時候我們需要從 store 中的 state 中派生出一些狀態,例如對列表進行過濾並計數:
const store = new Vuex.Store({
state: {
count: 0,
todos: [{
id: 1,
text: '...',
done: true
}, {
id: 2,
text: '...',
done: false
}]
},
比如store是這麼寫的,我們只取done為true的todos,如果在每個元件中獲取狀態,上面已經提過了通過computed獲取,這就需要在每個元件中都寫到這個doneTodosCount()
computed: {
doneTodosCount () {
return this.$store.state.todos.filter(todo => todo.done).length
}
}
Vue提供getters,用於專門處理資料,並提供資料的,就可以這麼寫,過濾函式寫在store裡面。
const store = new Vuex.Store({
state: {
count: 0,
todos: [{
id: 1,
text: '...',
done: true
}, {
id: 2,
text: '...',
done: false
}]
},
getters: {
doneTodos: state => {
return state.todos.filter(todo => todo.done)
}
},
mutations: {
increment(state) {
state.count++
},
decrement: state => state.count--
}
});
console.log(store.getters.doneTodos);
getters也可以把getters作為引數,讀取其他的getters,這裡我們可以理解為getters返回的其實是一個物件
getters: {
doneTodos: state => {
return state.todos.filter(todo => todo.done)
},
doneTodosCount: (state, getters) => {
return getters.doneTodos.length;
}
},
在其他元件的使用,不外乎就是把store變為 this.$store
computed: {
doneTodosCount () {
return this.$store.getters.doneTodosCount
}
}
mutations
更改 Vuex 的 store 中的狀態的唯一方法是提交 mutations,前面的例子也展示increment和decrement。
Vuex 中的 mutations 非常類似於事件:每個 mutation 都有一個字串的 事件型別 (type) 和 一個 回撥函式 (handler)。
可以理解為mutation監聽store中的state,並處理返回;
還是應該叫做通過mutation處理store中的state。
你不能直接呼叫一個 mutation handler。這個選項更像是事件註冊:“當觸發一個型別為 increment 的 mutation 時,呼叫此函式。”要喚醒一個 mutation handler,你需要以相應的 type 呼叫 store.commit 方法:
也就是說一個mutation相當於事件名,寫在store裡面,就相當於註冊了一個事件,至於什麼時候呼叫這個事件,由store.commit決定
另外文件中特別強調:在 Vuex 中,mutation 都是同步事務:
Actions
Action 類似於 mutation,不同在於:
- Action 提交的是 mutation,而不是直接變更狀態。
- Action 可以包含任意非同步操作。
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})
感覺像是對mutations的再封裝呢,就像getters對state的重新封裝一樣。
Action 通過 store.dispatch 方法觸發
store.dispatch('increment')
同時actions支援非同步
這裡寫程式碼片
moudules
使用單一狀態樹,導致應用的所有狀態集中到一個很大的物件。但是,當應用變得很大時,store 物件會變得臃腫不堪。
為了解決以上問題,Vuex 允許我們將 store 分割到模組(module)。每個模組擁有自己的 state、mutation、action、getters、甚至是巢狀子模組——從上至下進行類似的分割:
const moduleA = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: { ... },
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})
store.state.a // -> moduleA 的狀態
store.state.b // -> moduleB 的狀態
這樣每個store模組維持一些狀態和函式
對於模組內部的 mutation 和 getter,接收的第一個引數是模組的區域性狀態。
const moduleA = {
state: { count: 0 },
mutations: {
increment (state) {
// state 模組的區域性狀態
state.count++
}
},
getters: {
doubleCount (state) {
return state.count * 2
}
}
}
同樣,對於模組內部的 action,context.state 是區域性狀態,根節點的狀態是 context.rootState,
對於模組內部的 getter,根節點狀態會作為第三個引數:
模組動態註冊
在 store 建立之後,你可以使用 store.registerModule 方法註冊模組:
store.registerModule('myModule', {
// ...
})
嗯,感覺這樣很方便呢
以上就是對Vuex的一些簡單知識的學習,下一步就是在專案中運用進去了。