Vue3 Composition API如何替換Vue Mixins

想在你的Vue組件之間共享代碼?若是你熟悉Vue 2 則可能知道使用mixin,可是新的Composition API 提供了更好的解決方案。javascript

在本文中,咱們將研究mixins的缺點,並瞭解Composition API如何克服它們,並使Vue應用程序具備更大的可伸縮性。css

回顧Mixins功能

讓咱們快速回顧一下mixins模式,由於對於下一部分咱們將要講到的內容,請務必將其放在首位。前端

一般,Vue組件是由一個JavaScript對象定義的,它具備表示咱們所需功能的各類屬性——諸如 datamethodscomputed 等。vue

// MyComponent.js
export default {
  data: () => ({
    myDataProperty: null
  }),
  methods: {
    myMethod () { ... }
  }
  // ...
}
複製代碼

當咱們想在組件之間共享相同的屬性時,能夠將公共屬性提取到一個單獨的模塊中:java

// MyMixin.js
export default {
  data: () => ({
    mySharedDataProperty: null
  }),
  methods: {
    mySharedMethod () { ... }
  }
}
複製代碼

如今,咱們能夠經過將其分配給 mixin config屬性將其添加到任何使用的組件中。在運行時,Vue會將組件的屬性與任何添加的mixin合併。npm

// ConsumingComponent.js
import MyMixin from "./MyMixin.js";

export default {
  mixins: [MyMixin],
  data: () => ({
    myLocalDataProperty: null
  }),
  methods: {
    myLocalMethod () { ... }
  }
}
複製代碼

對於這個特定的例子,在運行時使用的組件定義應該是這樣的:api

export default {
  data: () => ({
    mySharedDataProperty: null
    myLocalDataProperty: null
  }),
  methods: {
    mySharedMethod () { ... },
    myLocalMethod () { ... }
  }
}
複製代碼

Mixins被認爲「有害」

早在2016年中期,丹·阿布拉莫夫(Dan Abramov)就寫了《mixin被認爲是有害的》(mixin Considered Harmful),他在書中辯稱,將mixin用於在React組件中重用邏輯是一種反模式,主張遠離它們。數組

不幸的是,他提到的關於React mixins的缺點一樣適用於Vue。在瞭解Composition API如何克服這些缺點以前,讓咱們熟悉這些缺點。安全

命名衝突

咱們看到了mixin模式如何在運行時合併兩個對象。若是他們兩個都共享同名屬性,會發生什麼?ide

const mixin = {
  data: () => ({
    myProp: null
  })
}

export default {
  mixins: [mixin],
  data: () => ({
    // 同名!
    myProp: null
  })
}
複製代碼

這就是合併策略發揮做用的地方。這是一組規則,用於肯定當一個組件包含多個具備相同名稱的選項時會發生什麼。

Vue組件的默認(但能夠配置)合併策略指示本地選項將覆蓋mixin選項。Vue組件的默認(可選配置)合併策略指示本地選項將覆蓋mixin選項。不過也有例外,例如,若是咱們有多個相同類型的生命週期鉤子,這些鉤子將被添加到一個鉤子數組中,而且全部的鉤子都將被依次調用。

儘管咱們不該該遇到任何實際的錯誤,可是在跨多個組件和mixin處理命名屬性時,編寫代碼變得愈來愈困難。一旦第三方mixin做爲帶有本身命名屬性的npm包被添加進來,就會特別困難,由於它們可能會致使衝突。

隱式依賴

mixin和使用它的組件之間沒有層次關係。這意味着組件可使用mixin中定義的數據屬性(例如mySharedDataProperty),可是mixin也可使用假定在組件中定義的數據屬性(例如myLocalDataProperty)。這種狀況一般是在mixin被用於共享輸入驗證時出現的,mixin可能會指望一個組件有一個輸入值,它將在本身的validate方法中使用。

不過,這可能會引發一些問題。若是咱們之後想重構一個組件,改變了mixin須要的變量的名稱,會發生什麼狀況呢?咱們在看這個組件時,不會發現有什麼問題。linter也不會發現它,咱們只會在運行時看到錯誤。

如今想象一個有不少mixin的組件。咱們能夠重構本地數據屬性嗎?或者它會破壞mixin嗎?咱們得手動搜索才能知道。

從mixins遷移

mixin的替代方案,包括高階組件,utility 方法和其餘一些組件組成模式。

mixins的缺點是Composition API背後的主要推進因素之一,讓咱們快速瞭解一下它是如何工做的,而後再看它如何克服mixin問題。

快速入門Composition API

Composition API的主要思想是,咱們將它們定義爲重新的 setup 函數返回的JavaScript變量,而不是將組件的功能(例如state、method、computed等)定義爲對象屬性。

以這個經典的Vue 2組件爲例,它定義了一個「計數器」功能:

//Counter.vue
export default {
  data: () => ({
    count: 0
  }),
  methods: {
    increment() {
      this.count++;
    }
  },
  computed: {
    double () {
      return this.count * 2;
    }
  }
}
複製代碼

下面是使用Composition API定義的徹底相同的組件。

// Counter.vue
import { ref, computed } from "vue";

export default {
  setup() {
    const count = ref(0);
    const double = computed(() => count * 2)
    function increment() {
      count.value++;
    }
    return {
      count,
      double,
      increment
    }
  }
}
複製代碼

首先會注意到,咱們導入了 ref 函數,該函數容許咱們定義一個響應式變量,其做用與 data 變量幾乎相同。計算屬性的狀況與此相同。

increment 方法不是被動的,因此它能夠被聲明爲一個普通的JavaScript函數。注意,咱們須要更改子屬性 countvalue 才能更改響應式變量。這是由於使用 ref 建立的響應式變量必須是對象,以便在傳遞時保持其響應式。

定義完這些功能後,咱們將從 setup 函數中將其返回。上面兩個組件之間的功能沒有區別,咱們所作的只是使用替代API。

代碼提取

Composition API的第一個明顯優勢是提取邏輯很容易。

讓咱們使用Composition API重構上面定義的組件,以使咱們定義的功能位於JavaScript模塊 useCounter 中(在特性描述前面加上「use」是一種Composition API命名約定。)。

//useCounter.js
import { ref, computed } from "vue";

export default function () {
  const count = ref(0);
  const double = computed(() => count * 2)
  function increment() {
    count.value++;
  }
  return {
    count,
    double,
    increment
  }
}
複製代碼

代碼重用

要在組件中使用該函數,咱們只需將模塊導入組件文件並調用它(注意導入是一個函數)。這將返回咱們定義的變量,隨後咱們能夠從 setup 函數中返回它們。

// MyComponent.js
import useCounter from "./useCounter.js";

export default {
  setup() {
    const { count, double, increment } = useCounter();
    return {
      count,
      double,
      increment
    }
  }
}
複製代碼

乍一看,這彷佛有點冗長而毫無心義,但讓咱們來看看這種模式如何克服了前面討論的mixins問題。

命名衝突解決了

咱們以前已經瞭解了mixin如何使用與消費者組件中的名稱相同的屬性,或者甚至更隱蔽地使用了消費者組件使用的其餘mixin中的屬性。

這不是Composition API的問題,由於咱們須要顯式命名任何狀態或從合成函數返回的方法。

export default {
  setup () {
    const { someVar1, someMethod1 } = useCompFunction1();
    const { someVar2, someMethod2 } = useCompFunction2();
    return {
      someVar1,
      someMethod1,
      someVar2,
      someMethod2
    }
  }
}
複製代碼

命名衝突的解決方式與其餘任何JavaScript變量相同。

隱式依賴...解決了!

前面還看到mixin如何使用在消費組件上定義的 data 屬性,這可能會使代碼變得脆弱,而且很難進行推理。

合成函數(Composition Function)還能夠調用消費組件中定義的局部變量。不過,不一樣之處在於,如今必須將此變量顯式傳遞給合成函數。

import useCompFunction from "./useCompFunction";

export default {
  setup () {
    // 某個局部值的合成函數須要用到
    const myLocalVal = ref(0);

    // 它必須做爲參數顯式地傳遞
    const { ... } = useCompFunction(myLocalVal);
  }
}
複製代碼

總結

mixin模式表面上看起來很安全。然而,經過合併對象來共享代碼,因爲它給代碼增長了脆弱性,而且掩蓋了推理功能的能力,所以成爲一種反模式。

Composition API最聰明的部分是,它容許Vue依靠原生JavaScript中內置的保障措施來共享代碼,好比將變量傳遞給函數和模塊系統。

這是否意味着Composition API在各方面都比Vue的經典API優越?不是的。在大多數狀況下,你堅持使用經典API是沒有問題的。可是,若是你打算重用代碼,Composition API無疑是優越的。


原文:css-tricks.com/how-the-vue…

做者:Anthony Gore


文章首發於公衆號 《前端外文精選》,私信回覆:大禮包,送某網精品視頻課程網盤資料,準能爲你節省很多錢!

相關文章
相關標籤/搜索