天天看點

VUE 和 VUEX 中的資料流

<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>

這些示範都是一樣的功能,隻是實作的方法不同。應用程式由兩個獨立的聊天元件執行個體組成。當使用者在一個執行個體裡送出一個消息的時候,它應該在兩個聊天視窗都出現,因為消息狀态是共享的,下面是一個截圖:

VUE 和 VUEX 中的資料流

開始前,讓我們先來看看資料是如何在示例的應用程式當中流轉的。

VUE 和 VUEX 中的資料流

在這個示範裡,我們将使用一個簡單的 JavaScript 對象:<code>var store = {...}</code>,在<code>Client.vue</code>元件的執行個體之間共享狀态。下面是關鍵檔案的重要代碼部分:

1

2

3

4

5

6

7

8

9

10

11

&lt;div id="app"&gt;&lt;/div&gt;

&lt;script&gt;

var store = {

state: {

messages: []

},

newMessage (msg) {

this.state.messages.push(msg)

}

&lt;/script&gt;

這裡有兩個關鍵的地方:

我們通過把這個對象直接添加到<code>index.html</code>裡來讓其對整個應用程式可用,也可以将它注入到應用程式裡更下一層的作用鍊,但目前直接添加顯然更快捷簡單。

我們在這裡儲存狀态,但同時也提供了一個函數來調用它。相比起分散在元件各處的函數,我們更傾向于讓它們保持在一個地方(便于維護),并在任何需要它們的地方簡單使用。

12

13

14

15

16

17

18

19

20

21

22

&lt;template&gt;

&lt;div id="app"&gt;

&lt;div class="row"&gt;

&lt;div class="col"&gt;

&lt;client clientid="Client A"&gt;&lt;/client&gt;

&lt;/div&gt;

&lt;client clientid="Client B"&gt;&lt;/client&gt;

&lt;/template&gt;

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

&lt;div&gt;

&lt;h1&gt;{{ clientid }}&lt;/h1&gt;

&lt;div class="client"&gt;

&lt;ul&gt;

&lt;li v-for="message in messages"&gt;

&lt;label&gt;{{ message.sender }}:&lt;/label&gt; {{ message.text }}

&lt;/li&gt;

&lt;/ul&gt;

&lt;div class="msgbox"&gt;

&lt;input v-model="msg" placeholder="Enter a message, then hit [enter]" @keyup.enter="trySendMessage"&gt;

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等等)對應到我們剛剛完成的示例中。

VUE 和 VUEX 中的資料流

正如你所看到的,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) =&gt; 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 =&gt; 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"

&lt;client clientid="Client A" :messages="messages" :callback="newMessage"&gt;&lt;/client&gt;

&lt;client clientid="Client B" :messages="messages" :callback="newMessage"&gt;&lt;/client&gt;

這裡,我在元件上使用了一個屬性将一個動态綁定傳遞到<code>messages</code>集合裡。但是,我同時還傳遞了一個動作函數,是以可以在子元件裡調用它。

props: ['clientid', 'messages', 'callback'],

this.callback({

這裡就是不好的做法。

要問為什麼有這麼不好嗎?

我們正在破壞之前圖中所展示的單向循環。

我們建立了一個在元件及其父元件之間的緊密耦合。

這将變得不可維護。如果你在元件裡需要20個函數,你就将添加20個屬性,管理它們的命名等等,然後,如果任何東西發生改變,呃!

是以為什麼還要再展示這段?因為我和其他人一樣很懶。有時我就會做這樣的事情,僅僅想知道再繼續做下去會有多麼糟糕,然後我就會咒罵自己的懶惰,因為我可能要花上一小時或者一天的時間去清理它們。鑒于這種情況,我希望我可以幫助你盡早避免無謂的決定和錯誤,千萬不要傳遞任何你不需要的東西。99%的情況下,一個單獨的共享狀态已經足夠完美。(不久再詳細講講那1%的情況)

<b></b>

<b>原文釋出時間為:2016年10月14日</b>

<b>本文來自雲栖社群合作夥伴掘金,了解相關資訊可以關注掘金網站。</b>

繼續閱讀