[譯] VUE 和 VUEX 中的數(shù)據(jù)流

看起來在 Vue 里面困擾開發(fā)者的事情之一是如何在組件之間共享狀態(tài)社搅。對于剛剛接觸響應式編程的開發(fā)者來說吭净,像Vuex 這種庫添瓷,有著繁多的新名詞及其關注點分離的方式丧诺,往往令人望而生畏往产。特別是當你只希望分享一兩個數(shù)據(jù)片段時蝶防,(這一套邏輯的復雜性)就顯得有點過分了窄锅。

考慮到這一點的話,我想我應該把兩個簡短的演示放到一起展示出來木柬。第一個通過使用一個簡單的 JavaScript 對象皆串,在每個新組件當中引用來實現(xiàn)共享狀態(tài)。第二個做了和 Vuex 一樣的事情弄诲,當它運行成功的時候愚战,也是一個你絕對不應該做的事情的示例(我們將在最后看看為什么)。

你可以通過查看下面這些演示來開始:

或者獲取這個倉庫并在本地運行試試看齐遵!代碼里很多地方是2.0版本的特性寂玲,但我接下來想講的數(shù)據(jù)流概念在任何版本里都是相關的,并且它可以通過一些改變很輕易地向下兼容到1.0梗摇。

這些演示都是一樣的功能拓哟,只是實現(xiàn)的方法不同。應用程序由兩個獨立的聊天組件實例組成伶授。當用戶在一個實例里提交一個消息的時候断序,它應該在兩個聊天窗口都出現(xiàn),因為消息狀態(tài)是共享的糜烹,下面是一個截圖:

用一個對象共享狀態(tài)

開始前违诗,讓我們先來看看數(shù)據(jù)是如何在示例的應用程序當中流轉的。

在這個演示里疮蹦,我們將使用一個簡單的 JavaScript 對象:var store = {...}诸迟,在Client.vue組件的實例之間共享狀態(tài)。下面是關鍵文件的重要代碼部分:

index.html
<div id="app"></div>
<script>
  var store = {
    state: {
      messages: []
    },
    newMessage (msg) {
      this.state.messages.push(msg)
    }
  }
</script>

這里有兩個關鍵的地方:

  1. 我們通過把這個對象直接添加到index.html里來讓其對整個應用程序可用,也可以將它注入到應用程序里更下一層的作用鏈阵苇,但目前直接添加顯然更快捷簡單壁公。
  2. 我們在這里保存狀態(tài),但同時也提供了一個函數(shù)來調用它绅项。相比起分散在組件各處的函數(shù)紊册,我們更傾向于讓它們保持在一個地方(便于維護),并在任何需要它們的地方簡單使用快耿。
App.vue
<template>
  <div id="app">
    <div class="row">
      <div class="col">
        <client clientid="Client A"></client>
      </div>
      <div class="col">
        <client clientid="Client B"></client>
      </div>
    </div>
  </div>
</template>

<script>
import Client from './components/Client.vue'

export default {
  components: {
    Client
  }
}
</script>

這里我們引入了 Client 組件囊陡,并創(chuàng)建了兩個它的實例,使用一個屬性:clientid润努,來對每個實例進行區(qū)分关斜。事實上示括,你應該更動態(tài)地去實現(xiàn)這些铺浇,但別忘了,目前快捷簡單更重要垛膝。

注意一點鳍侣,到這里我們還完全沒有同步任何狀態(tài)。

Client.vue
<template>
  <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">
      </div>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      msg: '',
      messages: store.state.messages
    }
  },
  props: ['clientid'],
  methods: {
    trySendMessage() {
      store.newMessage({
        text: this.msg,
        sender: this.clientid
      })
      this.resetMessage()
    },
    resetMessage() {
      this.msg = ''
    }
  }
}
</script>

下面是應用程序的主要內容:

  1. 在該模板里吼拥,設置一個v-for循環(huán)去遍歷messages集合倚聚。
  2. 綁定在文本輸入框上的v-model簡單地存儲了組件的本地數(shù)據(jù)對象msg
  3. 同樣在數(shù)據(jù)對象里凿可,我們創(chuàng)建了一個store.state.messages的引用惑折,它將觸發(fā)組件的更新。
  4. 最后枯跑,將 enter 鍵綁定到trySendMessage函數(shù)惨驶,這個函數(shù)包含了以下幾個功能:
    1. 準備好需要存儲的數(shù)據(jù)(發(fā)送者和消息的一個字典對象)。
    2. 調用定義在共享存儲里的newMessage函數(shù)敛助。
    3. 調用一個清理函數(shù):resetMessage粗卜,重置輸入框。通常你更應該在一個promise完成之后再調用它纳击。

這就是使用對象的方法续扔,來試一試

用 Vuex 共享狀態(tài)

好了焕数,現(xiàn)在來試試看用 Vuex 實現(xiàn)纱昧。同樣的,先上圖堡赔,也便于我們將 Vuex 的術語(actions识脆,mutations等等)對應到我們剛剛完成的示例中。

正如你所看到的,Vuex 簡單地形式化了我們剛剛完成的過程存璃。使用它的時候仑荐,所做的事情其實和我們上面做過的非常像:

  1. 創(chuàng)建一個用來共享的存儲,在這個例子中它將通過 vue/vuex 注入到組件當中纵东。
  2. 定義組件可以調用的 actions粘招,它們仍然是集中定義的。
  3. 定義實際接觸存儲狀態(tài)的 mutations偎球。我們這么做洒扎,actions 就可以形成不止一個 mutation,或者執(zhí)行邏輯去決定調用哪一個 mutation衰絮。這意味著你再也不用擔心組件當中的業(yè)務邏輯了袍冷,成功!
  4. 當狀態(tài)更新時猫牡,任何擁有 getter胡诗,動態(tài)屬性和映射到 store 的組件都會被立即更新。

同樣再來看看代碼:

main.js
import store from './vuex/store'

new Vue({ // eslint-disable-line no-new
  el: '#app',
  render: (h) => h(App),
  store: store
})

這次淌友,我們用 Vuex 創(chuàng)建了一個存儲并將其直接傳入應用程序當中煌恢,替代掉了之前index.html中的 store 對象。在繼續(xù)之前震庭,先來看一下這個存儲:

store.js
export default new Vuex.Store({

  state: {
    messages: []
  },

  actions: {
    newMessage ({commit}, msg) {
      commit('NEW_MESSAGE', msg)
    }
  },

  mutations: {
    NEW_MESSAGE (state, msg) {
      state.messages.push(msg)
    }
  },

  strict: debug

})

和我們自己創(chuàng)建的對象非常相似瑰抵,但是多了一個mutations對象。

Client.vue
<div class="row">
  <div class="col">
    <client clientid="Client A"></client>
  </div>
  <div class="col">
    <client clientid="Client B"></client>
  </div>
</div>

和上次一樣的配方器联。(驚人的相似二汛,對吧?)

Client.vue
<script>
import { mapState, mapActions } from 'vuex'

export default {
  data() {
    return {
      msg: ''
    }
  },
  props: ['clientid'],
  computed: {
    ...mapState({
      messages: state => state.messages
    })
  },
  methods: {
    trySendMessage() {
      this.newMessage({
        text: this.msg,
        sender: this.clientid
      })
      this.resetMessage()
    },
    resetMessage() {
      this.msg = ''
    },
    ...mapActions(['newMessage'])
  }
}
</script>

模板仍然剛好一樣拨拓,所以我甚至不需要費心怎么去引入它肴颊。最大的不同在于:

  1. 使用mapState來生成對共享消息集合的引用。
  2. 使用mapActions來生成創(chuàng)建一個新消息的動作(action)千元。

(注意:這些都是 Vuex 2.0特性苫昌。)

好的,做完啦幸海!也來看一下這個演示吧祟身。

結論

所以,正如你所希望看到的物独,自己進行簡單的狀態(tài)共享和使用 Vuex 進行共享并沒有多大區(qū)別袜硫。而 Vuex 最大的優(yōu)點在于它為你形式化了集中處理數(shù)據(jù)存儲的過程,并提供了所有功能方法去處理那些數(shù)據(jù)挡篓。

最初婉陷,當你閱讀 Vuex 的文檔和示例的時候帚称,它那些針對 mutations,actions 和 modules 的單獨文檔很容易讓人感覺困擾秽澳。但是如果你敢于跨出那一步闯睹,簡單地在store.js文件里寫一些關于它們的代碼來開始學習。隨著這個文件的大小增加担神,你就將找到正確的時間移步到actions.js里楼吃,或者是把它們更進一步地分離開來。

不要著急妄讯,慢慢來孩锡,一步一個臺階。當然也可以使用vue-cli從創(chuàng)建一個模板開始亥贸,我使用browserify模板躬窜,并把下面的代碼添加進我的package.json文件。

"dependencies": {
    "vue": "^2.0.0-rc.6",
    "vuex": "^2.0.0-rc.5"
}

還在看嗎炕置?

我知道我還說過要再講一個“不好的”方式荣挨。再次,這個演示恰好也是一樣的讹俊。不好的地方在于我利用了 Vue 2.0 里單向綁定的特性來注入回調函數(shù)垦沉,從而允許了父子模板之間順序的雙向綁定。首先仍劈,來看一下2.0文檔中的這個部分,然后再來看看我這個不好的方法寡壮。

App.vue
<div class="row">
  <div class="col">
    <client clientid="Client A" :messages="messages" :callback="newMessage"></client>
  </div>
  <div class="col">
    <client clientid="Client B" :messages="messages" :callback="newMessage"></client>
  </div>
</div>

這里贩疙,我在組件上使用了一個屬性將一個動態(tài)綁定傳遞到messages集合里。但是况既,我同時還傳遞了一個動作函數(shù)这溅,所以可以在子組件里調用它。

Client.vue
<script>
export default {
  data() {
    return {
      msg: ''
    }
  },
  props: ['clientid', 'messages', 'callback'],
  methods: {
    trySendMessage() {
      this.callback({
        text: this.msg,
        sender: this.clientid
      })
      this.resetMessage()
    },
    resetMessage() {
      this.msg = ''
    }
  }
}
</script>

這里就是不好的做法棒仍。

要問為什么有這么不好嗎悲靴?

  1. 我們正在破壞之前圖中所展示的單向循環(huán)。
  2. 我們創(chuàng)建了一個在組件及其父組件之間的緊密耦合莫其。
  3. 這將變得不可維護癞尚。如果你在組件里需要20個函數(shù),你就將添加20個屬性乱陡,管理它們的命名等等浇揩,然后,如果任何東西發(fā)生改變憨颠,呃胳徽!

所以為什么還要再展示這段积锅?因為我和其他人一樣很懶。有時我就會做這樣的事情养盗,僅僅想知道再繼續(xù)做下去會有多么糟糕缚陷,然后我就會咒罵自己的懶惰,因為我可能要花上一小時或者一天的時間去清理它們往核。鑒于這種情況蹬跃,我希望我可以幫助你盡早避免無謂的決定和錯誤,千萬不要傳遞任何你不需要的東西铆铆。99%的情況下蝶缀,一個單獨的共享狀態(tài)已經(jīng)足夠完美。(不久再詳細講講那1%的情況)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末薄货,一起剝皮案震驚了整個濱河市翁都,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌谅猾,老刑警劉巖柄慰,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異税娜,居然都是意外死亡坐搔,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門敬矩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來概行,“玉大人,你說我怎么就攤上這事弧岳〉拭Γ” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵禽炬,是天一觀的道長涧卵。 經(jīng)常有香客問我,道長腹尖,這世上最難降的妖魔是什么柳恐? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮热幔,結果婚禮上乐设,老公的妹妹穿的比我還像新娘。我一直安慰自己断凶,他們只是感情好伤提,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著认烁,像睡著了一般肿男。 火紅的嫁衣襯著肌膚如雪介汹。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天舶沛,我揣著相機與錄音嘹承,去河邊找鬼。 笑死如庭,一個胖子當著我的面吹牛叹卷,可吹牛的內容都是我干的。 我是一名探鬼主播坪它,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼骤竹,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了往毡?” 一聲冷哼從身側響起蒙揣,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎开瞭,沒想到半個月后懒震,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡嗤详,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年个扰,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片葱色。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡递宅,死狀恐怖,靈堂內的尸體忽然破棺而出冬筒,到底是詐尸還是另有隱情恐锣,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布舞痰,位于F島的核電站,受9級特大地震影響诀姚,放射性物質發(fā)生泄漏响牛。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一赫段、第九天 我趴在偏房一處隱蔽的房頂上張望呀打。 院中可真熱鬧,春花似錦糯笙、人聲如沸贬丛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽豺憔。三九已至额获,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間恭应,已是汗流浹背抄邀。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留昼榛,地道東北人境肾。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像胆屿,于是被迫代替她去往敵國和親奥喻。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內容

  • Vuex是什么非迹? Vuex 是一個專為 Vue.js應用程序開發(fā)的狀態(tài)管理模式环鲤。它采用集中式存儲管理應用的所有組件...
    蕭玄辭閱讀 3,114評論 0 6
  • “春風又吹綠了花蕾,你已經(jīng)也添了新歲彻秆⌒ń剩” 那個夏天,我?guī)е鴻幟饰兜男邼兔厶椅兜钠诖みM高一四班唇兑,遇到了正洋溢著青...
    百里流笙閱讀 268評論 0 2
  • 昨夜熄了燈正準備入睡酒朵,一串蛙鳴清清楚楚地從遠處傳來,透過玻璃扎附,來到我的耳畔蔫耽。間隔一會兒再次響起,似乎不敢那么起勁留夜,...
    滋小然閱讀 717評論 34 34
  • 活動結束后碍粥,活動策劃人員以為就沒什么事情鳖眼,終于可以休息了。事實上嚼摩,還有一些收尾工作需要對活動繼續(xù)跟進钦讳!這里列舉一個...
    寧小南閱讀 17,360評論 0 1
  • 想念是一種時時刻刻都想擁你入懷的感覺,呼吸時也會想枕面,吃飯時也會想愿卒,以為閉上眼睛就好了,才發(fā)現(xiàn)腦海里都是你潮秘。那么琼开,我...
    陽光雜貨鋪閱讀 359評論 0 0