[譯] Hooks 對 Vue 而言意味着什麼

不要把 Hooks 和 Vue 的生命週期鉤子(Lifecycle Hooks) 弄混了,Hooks 是 React 在 V16.7.0-alpha 版本中引入的,並且幾天後 Vue 發佈了其概念驗證版本。雖然 Hooks 是由 React 提出的,它是一個對各 JavaScript 框架生態系統都有價值的、重要的組合機制,所以咱們今天會花一點時間討論 Hooks 意味着什麼。css

Hooks 主要是對模式的複用提供了一種更明確的思路 —— 避免重寫組件自己,並容許有狀態邏輯的不一樣部分能無縫地進行協同工做。html

最初的問題

就 React 而言,問題在於:在表達狀態的概念時,類是最多見的組件形式。無狀態函數式組件也很是受歡迎,但因爲它們只能單純地渲染,因此它們的用途僅限於展現任務。前端

類自己存在一些問題。例如,隨着 React 變得愈來愈流行,類的問題也廣泛成爲新手的阻礙。開發者爲了理解 React,也必須理解類。綁定使得代碼冗長且可讀性差,而且須要理解 JavaScript 中的 this這裏還討論了使用類所帶來的一些優化障礙。vue

在邏輯複用方面,咱們一般使用 render props 和高階組件等模式。但使用這些模式後會發現本身處於相似的「厄運金字塔」中 —— 樣式實現地獄,即過分使用嵌套可能會致使組件難以維護。這致使我想對 Dan Abramov 像喝醉了同樣大吼大叫,沒有人想要那樣。react

Hooks 容許咱們使用函數調用來定義組件的有狀態邏輯,從而解決這些問題。這些函數調用變得更具備組合性、可複用性,而且容許咱們在使用函數式組件的同時可以訪問和維護狀態。React 發佈 Hooks 時,人們很興奮 —— 下面你能夠看到 Hooks 展現的一些優點,關於它們如何減小代碼和重複:android

@dan_abramov 的代碼(來自 #ReactConf2018)可視化,你能看到 React Hooks 爲咱們帶來的好處。pic.twitter.com/dKyOQsG0Gdios

— Pavel Prichodko (@prchdk) 2018 年 10 月 29 日git

在維護方面,簡單性是關鍵,Hooks 提供了一種單一的、函數式的方式來實現邏輯共享,而且可能代碼量更小。github

爲何 Vue 中須要 Hooks?

讀到這裏你確定想知道 Hooks 在 Vue 中必須提供什麼。這彷佛是一個不須要解決的問題。畢竟,類並非 Vue 主要使用的模式。Vue 提供無狀態函數式組件(若是須要它們),但爲何咱們須要在函數式組件中攜帶狀態呢?咱們有 mixins 用於組合能夠在多個組件複用的相同邏輯。問題解決了。vue-cli

我想到了一樣的事情,但在與 Evan You 交談後,他指出了我忽略的一個主要用例:mixins 不能相互消費和使用狀態,但 Hooks 能夠。這意味着若是咱們須要鏈式封裝邏輯,可使用 Hooks。

Hooks 實現了 mixins 的功能,但避免了 mixins 帶來的兩個主要問題:

  • 容許相互傳遞狀態。
  • 明確指出邏輯來自哪裏。

若是使用多個 mixins,咱們不清楚哪一個屬性是由哪一個 mixins 提供的。使用 Hooks,函數的返回值會記錄消費的值。

那麼,這在 Vue 中如何運行呢?咱們以前提到過,在使用 Hooks 時,邏輯在函數調用時表達從而可複用。在 Vue 中,這意味着咱們能夠將數據調用、方法調用或計算屬性調用封裝到另外一個自定義函數中,並使它們能夠自由組合。數據、方法和計算屬性如今可用於函數式組件了。

例子

讓咱們來看一個很是簡單的 hook,以便咱們在繼續學習 Hooks 中的組合例子以前理解構建塊。

useWat?

好的,Vue Hooks 和 React Hooks 之間存在交叉部分。使用 use 做爲前綴是 React 的約定,因此若是你在 React 中查找 Hooks,你會發現 Hooks 的名稱都會像 useStateuseEffect 等。更多信息能夠查看這裏。

Evan 的在線 demo 裏,你能夠看到他在何處訪問 useStateuseEffect 並用於 render 函數。

若是你不熟悉 Vue 中的 render 函數,那麼看一看官網文檔可能會有所幫助。

可是當咱們使用 Vue 風格的 Hooks 時,咱們會如何命名呢 —— 你猜對了 —— 好比:useDatauseComputed等。

所以,爲了讓咱們看看如何在 Vue 中使用 Hooks,我建立了一個示例應用程序供咱們探索。

詳見視頻演示:css-tricks.com/wp-content/…

演示網站

GitHub 倉庫

在 src/hooks 文件夾中,我建立了一個 hook,它在 useMounted hook 上阻止了滾動,並在 useDestroyed 上從新啓用滾動。這有助於我在打開查看內容的對話框時暫停頁面滾動,並在查看對話框結束時再次容許滾動。這是一個好的抽象功能,由於它在整個應用程序中可能會屢次使用。

import { useDestroyed, useMounted } from "vue-hooks";

export function preventscroll() {
  const preventDefault = (e) => {
    e = e || window.event;
    if (e.preventDefault)
      e.preventDefault();
    e.returnValue = false;
  }

  // keycodes for left, up, right, down
  const keys = { 37: 1, 38: 1, 39: 1, 40: 1 };

  const preventDefaultForScrollKeys = (e) => {
    if (keys[e.keyCode]) {
      preventDefault(e);
      return false;
    }
  }

  useMounted(() => {
    if (window.addEventListener) // older FF
      window.addEventListener('DOMMouseScroll', preventDefault, false);
    window.onwheel = preventDefault; // modern standard
    window.onmousewheel = document.onmousewheel = preventDefault; // older browsers, IE
    window.touchmove = preventDefault; // mobile
    window.touchstart = preventDefault; // mobile
    document.onkeydown = preventDefaultForScrollKeys;
  });

  useDestroyed(() => {
    if (window.removeEventListener)
      window.removeEventListener('DOMMouseScroll', preventDefault, false);

    //firefox
    window.addEventListener('DOMMouseScroll', (e) => {
      e.stopPropagation();
    }, true);

    window.onmousewheel = document.onmousewheel = null;
    window.onwheel = null;
    window.touchmove = null;
    window.touchstart = null;
    document.onkeydown = null;
  });
} 
複製代碼

而後咱們能夠在像 AppDetails.vue 同樣的 Vue 組件中調用它:

<script>
import { preventscroll } from "./../hooks/preventscroll.js";
...

export default {
  ...
  hooks() {
    preventscroll();
  }
}
</script>
複製代碼

咱們不只能夠在該組件中使用它,還能夠在整個應用程序中使用相同的功能!

可以相互理解的兩個 Hooks

咱們以前提到過,Hooks 和 mixins 之間的主要區別之一是 Hooks 實際上能夠互相傳值。讓咱們看一下這個簡單但有點不天然的例子。

在咱們的應用程序中,咱們須要在一個可複用的 hook 中進行計算,還有一些須要使用該計算結果的東西。在咱們的例子中,咱們有一個 hook,它獲取窗口寬度並將其傳遞給動畫,讓它知道只有當咱們在更大的屏幕上時纔會觸發。

詳見視頻演示:css-tricks.com/wp-content/…

第一個 hook:

import { useData, useMounted } from 'vue-hooks';

export function windowwidth() {
  const data = useData({
    width: 0
  })

  useMounted(() => {
    data.width = window.innerWidth
  })

  // this is something we can consume with the other hook
  return {
    data
  }
}
複製代碼

而後,在第二個 hook 中,咱們使用它來建立一個觸發動畫邏輯的條件:

// the data comes from the other hook
export function logolettering(data) {
  useMounted(function () {
    // this is the width that we stored in data from the previous hook
    if (data.data.width > 1200) {
      // we can use refs if they are called in the useMounted hook
      const logoname = this.$refs.logoname;
      Splitting({ target: logoname, by: "chars" });

      TweenMax.staggerFromTo(".char", 5,
        {
          opacity: 0,
          transformOrigin: "50% 50% -30px",
          cycle: {
            color: ["red", "purple", "teal"],
            rotationY(i) {
              return i * 50
            }
          }
        },
        ...
複製代碼

而後,在組件內部,咱們將一個 hook 做爲參數傳遞給另外一個 hook:

<script>
import { logolettering } from "./../hooks/logolettering.js";
import { windowwidth } from "./../hooks/windowwidth.js";

export default {
  hooks() {
    logolettering(windowwidth());
  }
};
</script>
複製代碼

如今咱們能夠在整個應用程序中使用 Hooks 來編寫邏輯!再提一下,這是一個用於演示目的不太天然的例子,但你能夠看到這對於大型應用程序,將邏輯保存在較小的、可複用的函數中是有效的。

將來的計劃

Vue Hooks 如今已經能夠與 Vue 2.x 一塊兒使用了,但仍然是實驗性的。咱們計劃將 Hooks 集成到 Vue 3 中,但在咱們本身的實現中可能會偏離 React 的 API。咱們發現 React Hooks 很是鼓舞人心,正在考慮如何向 Vue 開發人員介紹其優點。咱們想以一種符合 Vue 習慣用法的方式來作,因此還有不少實驗要作。

你能夠查看這個倉庫做爲起步。Hooks 可能會成爲 mixins 的替代品,因此雖然這個功能還處於早期階段,可是一個在此期間探索其概念是有好處的。

(真誠地感謝 Evan You 和 Dan Abramov 爲本文審閱。)

若是發現譯文存在錯誤或其餘須要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可得到相應獎勵積分。文章開頭的 本文永久連接 即爲本文在 GitHub 上的 MarkDown 連接。


掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章
相關標籤/搜索