系列文章的目錄在 ? 這裏javascript
Vuex 官方文檔html
Vuex 是一個專爲 Vue.js 應用程序開發的 狀態管理模式。它採用集中式存儲管理應用的全部組件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。前端
Vuex 的核心工做是狀態管理,主要包含了 State
, View
Actions
這三部分,組成了一個簡單的「單項數據流」,避免了管理多狀態形成的數據不一致問題。vue
Vuex 是專門爲 Vue.js 設計的狀態管理庫,貼合 Vue 自己的數據更新特性,可以利用 Vue.js 的細粒度數據響應機制來進行高效的狀態更新。並且能夠是以 插件(plugin)的形式提供,安裝完成以後能夠很方便的在 VueComponent 實例中獲取到全局狀態。模塊之間的關係和操做以下圖所示:java
State
: 應用的單一狀態樹。一般一個應用裏只保存同一份狀態(組件仍然能夠保有局部狀態),爲的是便於多個組件之間的狀態同步。狀態的更新將會觸發指定組件的從新渲染。git
Action
: 描述組件觸發的操做。它能夠經過 commit
產生 mutation
,是對邏輯的封裝,組件只須要派發 Action 而不用關心數據究竟是如何更新的。github
Mutations
: 描述狀態應該如何更新。只有提交 mutation
才能夠更新 Store 中的狀態,它是對數據操做的封裝,定義瞭如何更新狀態數據。web
以上只是對 Vuex 的概述,想要了解詳細的原理和用法,仍是得看官方文檔。vue-router
Vuex 是個狀態管理的庫,用的都是 javascript 自己的語法特性,是與平臺無關的,因此它能夠徹底正常的用在 Weex 裏。vuex
不過由於 Vue.js 框架代碼已經集成在 WeexSDK (0.9.5 以上)中,因此你不須要再引入一遍 Vue 。另外由於 Vuex 在瀏覽器環境下會自動註冊,只須要在非 Web 環境下注冊 Vuex 插件便可,重複註冊的話會拋出警告的。引入 Vuex 的代碼以下:
// import Vue from 'vue' import Vuex from 'vuex' // Vuex is auto installed on the web if (WXEnvironment.platform !== 'Web') { Vue.use(Vuex) }
註冊成功以後,全部 Vuex 的特性都能在 Weex 裏使用! 具體用法以官方文檔爲準。
首先要建立全局惟一的 Store 對象,包含了惟一的狀態樹和一些操做。
const store = new Vuex.Store({ state: { count: 0 }, mutations: { increment (state) { state.count++ } } })
除了 state
和 mutations
之外,還能夠傳入 actions
、getters
、modules
這些屬性的關係和上邊圖中一一對應。參考其 API 文檔瞭解更詳細的用法。在 weex-hackernews 項目的 src/store/index.js 中也有一個更復雜的例子。
而後在建立實例的時候傳入 store
對象,這樣 Store 就和組件創建了聯繫,每一個組件均可以經過 this.$store
的方式獲取到 Store 中的狀態和操做。
import App from 'path/to/App.vue' import store from 'path/to/store.js' App.el = '#root' App.store = store new Vue(App)
光有數據是不行的,還得定義 觸發數據修改的行爲(Actions) 和 對數據的操做(Mutations)。以 weex-hackernews 裏的加載用戶數據爲例,能夠簡化成下邊的代碼:
// 引入網絡操做的接口 import { fetchUser } from './fetch' const store = new Vuex.Store({ state: { users: {}, }, actions: { FETCH_USER ({ commit }, { id }) { // 獲取新的用戶數據,而後提交給 mutation return fetchUser(id).then(user => commit('SET_USER', { user })) } }, mutations: { SET_USER (state, { user }) { // 修改 users 中的數據,而且觸發界面更新 Vue.set(state.users, user.id, user) } } })
在上邊的代碼中,state
裏有一個 user
對象,全部須要用戶數據的地方都將從這個變量中獲取,全部對用戶數據的修改實際上也都是改的這個變量。這是全局惟一狀態的意義。
而後代碼裏定義了 FETCH_USER
的 action,和 SET_USER
的 mutation ,注意二者的差異。真正修改 user
數據的是 SET_USER 這個 mutation,也只有 mutation 能修改 state 中的數據。FETCH_USER 負責獲取新數據而後派發 mutation,它調用了 fetchUser
獲取用戶數據,而後經過把這個新數據「提交」給 SET_USER 這個 mutation;而後在 mutation 裏執行數據更新的操做,Vue.set
這個方法會觸發更新 state.users
裏綁定的界面元素。
Getters 相似於 Vue 組件裏的 computed
屬性,能夠根據現有的基礎狀態作運算,而後返回一個新的值。把取值過程寫成 Getter 是一種惰性求值,也減小了狀態同步的負擔。
舉個例子,假如你想渲染一個列表中的一組數據,又想渲染這組數據的總和,若是再加一條「數據總和」的屬性的話,在列表更新後還得手動更新「數據總和」這條屬性,比較麻煩也容易出錯。這種狀況下就很適合寫成一個 Getter。
const store = new Vuex.Store({ state: { lists: [ { count: 4 }, { count: 8 }, { count: 3 }, { count: 9 } ], }, getters: { summary ({ lists }) { return lists.reduce((sum, curr) => sum + curr.count, 0) } } })
上邊代碼中的 summary
就是一個對 lists
數組求和的 Getter。在實際使用中,組件裏能夠經過 this.$store.state.lists
獲取 lists
列表數據,能夠經過 this.$store.getters.summary
獲取數據總和,更新列表的時候數據總和也會自動更新。
在 weex-hackernews 的項目的 src/store/index.js 文件裏定義了 activeIds
的 Getter,用來獲取當前首屏 feed 列表中須要展現的數據 ID。而後還定義了 activeItems
,在其中有調用了 activeIds
,會根據活躍的 ID 獲取相應的數據對象。
{ //... getters: { // ids of the items that should be currently displayed based on // current list type and current pagination activeIds (state) { const { activeType, lists, counts } = state return activeType ? lists[activeType].slice(0, counts[activeType]) : [] }, // items that should be currently displayed. // this Array may not be fully fetched. activeItems (state, getters) { return getters.activeIds.map(id => state.items[id]).filter(_ => _) } } }
而後在 src/views/StoriesView.vue 這個文件裏定義了一個 computed
屬性,從 Store 中獲取列表須要展現的數據。
{ //... computed: { stories () { return this.$store.getters.activeItems } } }
在 StoriesView.vue 的模板中根據 stories
這個數據循環建立 <story>
組件。
<list> <cell v-for="story in stories" :key="story.id"> <story :story="story"></story> </cell> </list>
如下都是我的觀點。
Vuex 衍生自 Flux 架構,用來管理應用的狀態,強調單向數據流和全局惟一狀態。對於一些數據狀態複雜,並且又自上而下實現了組件化的應用裏,能發揮很大做用。狀態的更新大概能簡化爲 nextState = f(state, action)
,有一種函數式的感受,能夠節約邏輯。
雖然 Vuex 與其餘技術沒有耦合關係,可是一般都是用在單頁應用(SPA)裏,還常常搭配着 vue-router 使用。不過我在《【使用 Weex 和 Vue 開發原生應用】 2 編寫獨立頁面》 這篇文章裏說過,Weex 的實例在 Web 上是和「瀏覽器頁籤」的概念相對應的,一般一個 Weex 實例就是一個「頁面」,也就是說,Weex 的設計是個「多頁應用」,是多實例的。在 Weex 中使用 Vuex,它的做用域是實例級別的,不一樣頁面(實例)之間是不能經過 Vuex 共享狀態的。
Weex 畢竟渲染的是原生界面,雖然語法上貼近 Web,可是在一些基本概念上和 Native 更近一些。「單頁應用」、「單向數據流」這些概念主要是在前端裏比較流行,Weex 只是一個 SDK,在開發原生應用的時候,頁面跳轉策略這類問題,我以爲仍是應該以客戶端自身的架構設計爲主。
weex-hackernews 這個項目是爲了驗證 Vuex 和 vue-router 接入的可能性,並不必定是最佳實踐。
我以爲既然 Weex 在原生端是多實例的,就未必適合寫單頁應用。即便像 Vuex 這種相對獨立的狀態管理的庫,在 「不一樣頁面是不一樣的 Weex 實例」 這種前提下,就須要根據 App 自身的技術特性,
再考慮一下應不該該使用。
關於單頁應用,會在《使用 vue-router》裏有更多討論。