用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的一些簡單知識的學習,下一步就是在項目中運用進去了。

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