你也許不知道的Vuejs - 狀態管理

you-may-not-know-vuejs.png

by yugasun from https://yugasun.com/post/you-may-not-know-vuejs-12.html
本文可全文轉載,但需要保留原作者和出處。

前言

小擼怡情,大擼傷身,強擼灰飛煙滅,恩,大概是本人代碼擼多了的緣故,我的女朋友就像 復聯3 結局一樣,灰飛煙滅了(分手了,當然原因不只是這么簡單,人總得給自己的失敗找個借口,不是嗎......)。
所以從3月份開始,心情一直很差,文章也更新的越來越慢。恰巧今天是 520,從一大早起來開始,朋友圈就異常的熱鬧,有秀恩愛的,有秀悲傷地,還有少數微商依舊堅挺的發著廣告。正是這些堅強的微商讓我明白了,無論別人如何秀,堅定自己的目標繼續做下去就是了,也就在這一天我徹底的從失戀中走了出來......

好了,言歸正傳,今天來聊聊 Vue.js 中的狀態管理,也許一提到狀態管理,大家首先想到的就是 vuex,但是如果你的應用夠簡單,其實是不需要使用 vuex 的,反而會讓你的項目變得繁瑣起來。

本文總結了4種實現組件間狀態的共享的方法:

  1. 共享對象
  2. mixins 混入
  3. 全局的 Event Bus
  4. Vuex

下面我將分別實現一遍。

共享對象

在日常工作中,我們會經常提到對象的拷貝,由于:

Javascript 語言的對象在賦值的時候,只是將其引用地址賦值給了一個新的變量而已,所以在改變新對象屬性是,會同時改變原對象屬性。

所以大家在寫代碼的時候會盡量的避免此類事情的發生,但是也正是因為這個特性,我們可以實現 Vuejs 中的狀態共享。

思路如下:

首先初始化一個全局對象,維護著全局數據,然后將其注入到所有用到的組件中,那么在組建內改變某個屬性值,其他組件也就同步更新了。

明白了原理就很好實現了。同樣的通過 vue-cli 初始化我們的項目模板,然后新建 src/state.js 文件:

export default {
  msg: 'Hello world',
};

然后,分別創建需要引用的組件:

<!-- comp1 -->
<template>
  <div class="comp1">
    <h1>Component 1</h1>
    <input type="text" v-model="state.msg">
  </div>
</template>
<script>
import state from '../state';

export default {
  name: 'comp1',
  data() {
    return {
      state,
    };
  },
};
</script>

<!-- comp2 -->
<template>
  <div class="comp2">
    <h1>Component 2</h1>
    <input type="text" v-model="state.msg">
  </div>
</template>
<script>
import state from '../state';

export default {
  name: 'comp2',
  data() {
    return {
      state,
    };
  },
};
</script>

然后在 src/App.vue 文件中使用 comp1comp2:

<template>
  <div class="app">
    <comp1></comp1>
    <comp2></comp2>
  </div>
</template>
<script>
import comp1 from './components/comp1';
import comp2 from './components/comp2';

export default {
  name: 'App',
  components: {
    comp1,
    comp2,
  },
};
</script>

ok,這樣就完成了,運行項目,你會發現,改變 comp1 中的輸入框的值,comp2 也同步發生改變,反過來也是一樣的。至于原理,上面已經說過了,這里就不再贅述,趕緊動手實現下吧。

最終實現代碼

mixins - 混入

我們先來看看官方對 mixins 介紹:

混入 (mixins) 是一種分發 Vue 組件中可復用功能的非常靈活的方式。混入對象可以包含任意組件選項。當組件使用混入對象時,所有混入對象的選項將被混入該組件本身的選項。

所以我們也可以通過 mixins 的方式,將我的的全局狀態 state 通過 mixins 的方式引入到各個組件中,當然數據其實還是通過 對象引用 實現的。

我們修改一下 src/state.js

const state = {
  msg: 'Hello world',
};

export default {
  data() {
    return {
      state,
    };
  },
};

然后 comp1 中引入:

<template>
  <div class="comp1">
    <h1>Component 1</h1>
    <input type="text" v-model="state.msg">
  </div>
</template>
<script>
import state from '../state';

export default {
  mixins: [state],
  name: 'comp1',
  data() {
    return {};
  },
};
</script>

comp2 也是相同方法引入就行了。

其實,通過 mixins 方式跟 方法1 基本差不多,只不過相對于 方法1 的好處就是,在使用組件內,將狀態數據屬性跟 data 進行了代碼上隔離(實際上還是混入到了 data 屬性中)。

最終實現代碼

全局的事件總線(Event Bus)

我們知道 Vuejs 有一對API $on/$emit,是用來自定義事件監聽和觸發的,而且在觸發的時候可以攜帶數據。這樣我們就可以通過創建一個全局的 Vue 實例 $bus,然后在各個組件中進行相關事件的觸發和監聽,數據流的傳遞和共享,這也就是大家常說的 發布訂閱模式

熟悉 $on/$emit API的同學,應該很容易就可以實現了,先上代碼:

簡潔版

src/state.js:

import Vue from 'vue';

const EventBus = new Vue();

export default EventBus;

src/components/comp1.vue:

<template>
  <div class="comp1">
    Change by event: <input type="text" v-model="msg" @change="handleChange">
  </div>
</template>
<script>
export default {
  name: 'comp1',
  data() {
    return {
      msg: this.$bus.msg,
    };
  },
  methods: {
    handleChange(e) {
      const newVal = e.target.value;
      this.$bus.$emit('msg-change', { value: newVal });
    },
  },
  created() {
    this.$bus.$on('msg-change', (payload) => {
      this.msg = payload.value;
    });
  },
};
</script>

src/components/comp2 代碼同 src/components/comp1

好了,先運行一下,同樣達到了我們想要的效果。上面代碼中,我們先在 src/state.js 中創建了一個 Vue 實例,然后在組件中 created 的時候監聽 msg-change 事件,在 input 值改變的時候,觸發 msg-change 事件,這樣就實現了 comp1comp2msg 數據共享。

升級版

我們知道 $bus 實際上也是個對象,那么我們其實也可以通過給他添加 data 屬性來進行數據共享的,現在將上面代碼再稍作修改:

src/state.js:

import Vue from 'vue';

const EventBus = new Vue({
  data: {
    msg: 'Hello world',
  },
});

export default EventBus;

然后分別在 src/components/comp1src/components/comp1 中添加數據引用:

<!-- ... -->
Change by object reference: <input type="text" v-model="$bus.msg"><br/>
<!-- ... -->

然后試著改變新添加的輸入框數據,你會發現,新添加的兩個輸入框,數據也是同步更新的。

細心的同學,會發現有個問題,雖然新添加的兩個輸入框同步了,但是之前的兩個事件觸發的輸入框并未同步更新。因為我們只是單純地修改了對象屬性,并未觸發 msg-change 事件,所以還需要在 $bus 中添加 watcher,在 msg 變化時,觸發 msg-change 事件;反過來,$bus自身需要監聽 msg-change 事件,在觸發的時候,修改自身 msg 的值就可以了。

我們再修改下 src/state.js 代碼:

import Vue from 'vue';

const EventBus = new Vue({
  data: {
    msg: 'Hello world',
  },
  watch: {
    // 這里為了實現對象引用監聽,然后出發change事件,實現狀態同步
    msg(val) {
      this.$emit('msg-change', { value: val });
    },
  },
});

EventBus.$on('msg-change', (payload) => {
  console.log(`Msg has changed to ${payload.value}`);
  EventBus.msg = payload.value;
});

export default EventBus;

當然我們這里只是為了demo,實際開發過程中,自己一定要清楚,這里其實有兩個數據管道,一個是 發布/訂閱 可以理解為單工(單向數據流)模式,一個是 對象共享 也就是 方法2,可以理解為雙工(雙向數據流)模式。首先你需要理解清楚 當前需要共享的數據是單向還是雙向,然后根據場景靈活運用。

最終實現代碼

Vuex

至于 Vuex 的使用,本文就不再實現了,官方文檔已經寫得很清楚,請細心閱讀 官方文章

總結

本文寫了這么多其實想告訴大家,對于 Vuejs 狀態管理,我們不僅僅只有 vuex,框架或工具的選擇不是固定的,實際開發中,可以多嘗試,找到最適合的架構才是最好的。引用 Vue 的作者的話就是:

如果您不打算開發大型單頁應用,使用 Vuex 可能是繁瑣冗余的。確實是如此——如果您的應用夠簡單,您最好不要使用 Vuex。一個簡單的 global event bus 就足夠您所需了。但是,如果您需要構建一個中大型單頁應用,您很可能會考慮如何更好地在組件外部管理狀態,Vuex 將會成為自然而然的選擇。

專題目錄

You-May-Not-Know-Vuejs

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,363評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,497評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,305評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,962評論 1 311
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,727評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,193評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,257評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,411評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,945評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,777評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,978評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,519評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,216評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,642評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,878評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,657評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,960評論 2 373

推薦閱讀更多精彩內容