<b>本文講的是VUE 和 VUEX 中的資料流,</b>
考慮到這一點的話,我想我應該把兩個簡短的示範放到一起展示出來。第一個通過使用一個簡單的 JavaScript 對象,在每個新元件當中引用來實作共享狀态。第二個做了和 Vuex 一樣的事情,當它運作成功的時候,也是一個你絕對不應該做的事情的示例(我們将在最後看看為什麼)。
你可以通過檢視下面這些示範來開始:
<a href="https://benjaminlistwon.com/demo/dataflow/shared/index.html" target="_blank">Using shared object</a>
<a href="https://benjaminlistwon.com/demo/dataflow/vuex/index.html" target="_blank">Using vuex</a>
<a href="https://benjaminlistwon.com/demo/dataflow/evil/index.html" target="_blank">Using evil bindings</a>
這些示範都是一樣的功能,隻是實作的方法不同。應用程式由兩個獨立的聊天元件執行個體組成。當使用者在一個執行個體裡送出一個消息的時候,它應該在兩個聊天視窗都出現,因為消息狀态是共享的,下面是一個截圖:

開始前,讓我們先來看看資料是如何在示例的應用程式當中流轉的。
在這個示範裡,我們将使用一個簡單的 JavaScript 對象:<code>var store = {...}</code>,在<code>Client.vue</code>元件的執行個體之間共享狀态。下面是關鍵檔案的重要代碼部分:
1
2
3
4
5
6
7
8
9
10
11
<div id="app"></div>
<script>
var store = {
state: {
messages: []
},
newMessage (msg) {
this.state.messages.push(msg)
}
</script>
這裡有兩個關鍵的地方:
我們通過把這個對象直接添加到<code>index.html</code>裡來讓其對整個應用程式可用,也可以将它注入到應用程式裡更下一層的作用鍊,但目前直接添加顯然更快捷簡單。
我們在這裡儲存狀态,但同時也提供了一個函數來調用它。相比起分散在元件各處的函數,我們更傾向于讓它們保持在一個地方(便于維護),并在任何需要它們的地方簡單使用。
12
13
14
15
16
17
18
19
20
21
22
<template>
<div id="app">
<div class="row">
<div class="col">
<client clientid="Client A"></client>
</div>
<client clientid="Client B"></client>
</template>
import Client from './components/Client.vue'
export default {
components: {
Client
這裡我們引入了 Client 元件,并建立了兩個它的執行個體,使用一個屬性:<code>clientid</code>,來對每個執行個體進行區分。事實上,你應該更動态地去實作這些,但别忘了,目前快捷簡單更重要。
注意一點,到這裡我們還完全沒有同步任何狀态。
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<div>
<h1>{{ clientid }}</h1>
<div class="client">
<ul>
<li v-for="message in messages">
<label>{{ message.sender }}:</label> {{ message.text }}
</li>
</ul>
<div class="msgbox">
<input v-model="msg" placeholder="Enter a message, then hit [enter]" @keyup.enter="trySendMessage">
data() {
return {
msg: '',
messages: store.state.messages
props: ['clientid'],
methods: {
trySendMessage() {
store.newMessage({
text: this.msg,
sender: this.clientid
})
this.resetMessage()
resetMessage() {
this.msg = ''
下面是應用程式的主要内容:
在該模闆裡,設定一個<code>v-for</code>循環去周遊<code>messages</code>集合。
綁定在文本輸入框上的<code>v-model</code>簡單地存儲了元件的本地資料對象<code>msg</code>。
同樣在資料對象裡,我們建立了一個<code>store.state.messages</code>的引用,它将觸發元件的更新。
最後,将 enter 鍵綁定到<code>trySendMessage</code>函數,這個函數包含了以下幾個功能:
準備好需要存儲的資料(發送者和消息的一個字典對象)。
調用定義在共享存儲裡的<code>newMessage</code>函數。
調用一個清理函數:<code>resetMessage</code>,重置輸入框。通常你更應該在一個<code>promise</code>完成之後再調用它。
好了,現在來試試看用 Vuex 實作。同樣的,先上圖,也便于我們将 Vuex 的術語(actions,mutations等等)對應到我們剛剛完成的示例中。
正如你所看到的,Vuex 簡單地形式化了我們剛剛完成的過程。使用它的時候,所做的事情其實和我們上面做過的非常像:
建立一個用來共享的存儲,在這個例子中它将通過 vue/vuex 注入到元件當中。
定義元件可以調用的 actions,它們仍然是集中定義的。
定義實際接觸 store 中 state 的 mutations。我們這麼做,actions 内就可以将不止一個 mutation 組合在一起,或者執行邏輯去決定調用哪一個 mutation。這意味着你再也不用擔心元件當中的業務邏輯了,成功!
當狀态更新時,任何擁有 getter,計算屬性和映射到 store 的元件都會被立即更新。
同樣再來看看代碼:
import store from './vuex/store'
new Vue({ // eslint-disable-line no-new
el: '#app',
render: (h) => h(App),
store: store
這次,我們用 Vuex 建立了一個存儲并将其直接傳入應用程式當中,替代掉了之前<code>index.html</code>中的 <code>store</code> 對象。在繼續之前,先來看一下這個存儲:
export default new Vuex.Store({
actions: {
newMessage ({commit}, msg) {
commit('NEW_MESSAGE', msg)
mutations: {
NEW_MESSAGE (state, msg) {
state.messages.push(msg)
strict: debug
和我們自己建立的對象非常相似,但是多了一個<code>mutations</code>對象。
和上次一樣的配方。(驚人的相似,對吧?)
import { mapState, mapActions } from 'vuex'
msg: ''
computed: {
...mapState({
messages: state => state.messages
this.newMessage({
...mapActions(['newMessage'])
模闆仍然剛好一樣,是以我甚至不需要費心怎麼去引入它。最大的不同在于:
使用<code>mapState</code>來生成對共享消息集合的引用。
使用<code>mapActions</code>來生成建立一個新消息的動作(action)。
(注意:這些都是 Vuex 2.0特性。)
是以,正如你所希望看到的,自己進行簡單的狀态共享和使用 Vuex 進行共享并沒有多大差別。而 Vuex 最大的優點在于它為你形式化了集中處理資料存儲的過程,并提供了所有功能方法去處理那些資料。
最初,當你閱讀 Vuex 的文檔和示例的時候,它那些針對 mutations,actions 和 modules 的單獨文檔很容易讓人感覺困擾。但是如果你敢于跨出那一步,簡單地在<code>store.js</code>檔案裡寫一些關于它們的代碼來開始學習。随着這個檔案的大小增加,你就将找到正确的時間移步到<code>actions.js</code>裡,或者是把它們更進一步地分離開來。
"dependencies": {
"vue": "^2.0.0-rc.6",
"vuex": "^2.0.0-rc.5"
<client clientid="Client A" :messages="messages" :callback="newMessage"></client>
<client clientid="Client B" :messages="messages" :callback="newMessage"></client>
這裡,我在元件上使用了一個屬性将一個動态綁定傳遞到<code>messages</code>集合裡。但是,我同時還傳遞了一個動作函數,是以可以在子元件裡調用它。
props: ['clientid', 'messages', 'callback'],
this.callback({
這裡就是不好的做法。
要問為什麼有這麼不好嗎?
我們正在破壞之前圖中所展示的單向循環。
我們建立了一個在元件及其父元件之間的緊密耦合。
這将變得不可維護。如果你在元件裡需要20個函數,你就将添加20個屬性,管理它們的命名等等,然後,如果任何東西發生改變,呃!
是以為什麼還要再展示這段?因為我和其他人一樣很懶。有時我就會做這樣的事情,僅僅想知道再繼續做下去會有多麼糟糕,然後我就會咒罵自己的懶惰,因為我可能要花上一小時或者一天的時間去清理它們。鑒于這種情況,我希望我可以幫助你盡早避免無謂的決定和錯誤,千萬不要傳遞任何你不需要的東西。99%的情況下,一個單獨的共享狀态已經足夠完美。(不久再詳細講講那1%的情況)
<b></b>
<b>原文釋出時間為:2016年10月14日</b>
<b>本文來自雲栖社群合作夥伴掘金,了解相關資訊可以關注掘金網站。</b>