[譯] Hooks 對 Vue 而言意味著什麼
- 原文地址:What Hooks Mean for Vue
- 原文作者:Sarah Drasner
- 譯文出自:掘金翻譯計劃
- 本文永久連結:github.com/xitu/gold-m…
- 譯者:Ivocin
- 校對者:LeoYang ,TUARAN
不要把 Hooks 和 Vue 的生命週期鉤子(Lifecycle Hooks) 弄混了,Hooks 是 React 在 V16.7.0-alpha 版本中引入的,而且幾天後 Vue 釋出了其概念驗證版本。雖然 Hooks 是由 React 提出的,它是一個對各 JavaScript 框架生態系統都有價值的、重要的組合機制,因此我們今天會花一點時間討論 Hooks 意味著什麼。
Hooks 主要是對模式的複用提供了一種更明確的思路 —— 避免重寫元件本身,並允許有狀態邏輯的不同部分能無縫地進行協同工作。
最初的問題
就 React 而言,問題在於:在表達狀態的概念時,類是最常見的元件形式。無狀態函式式元件也非常受歡迎,但由於它們只能單純地渲染,所以它們的用途僅限於展示任務。
類本身存在一些問題。例如,隨著 React 變得越來越流行,類的問題也普遍成為新手的阻礙。開發者為了理解 React,也必須理解類。繫結使得程式碼冗長且可讀性差,並且需要理解 JavaScript 中的this
。這裡還討論了使用類所帶來的一些優化障礙。
在邏輯複用方面,我們通常使用 render props 和高階元件等模式。但使用這些模式後會發現自己處於類似的“厄運金字塔”中 —— 樣式實現地獄,即過度使用巢狀可能會導致元件難以維護。這導致我想對 Dan Abramov 像喝醉了一樣大吼大叫,沒有人想要那樣。
Hooks 允許我們使用函式呼叫來定義元件的有狀態邏輯,從而解決這些問題。這些函式呼叫變得更具有組合性、可複用性,並且允許我們在使用函式式元件的同時能夠訪問和維護狀態。React 釋出 Hooks 時,人們很興奮 —— 下面你可以看到 Hooks 展示的一些優勢,關於它們如何減少程式碼和重複:
將@dan_abramov 的程式碼(來自#ReactConf2018)視覺化,你能看到 React Hooks 為我們帶來的好處。pic.twitter.com/dKyOQsG0Gd
— Pavel Prichodko (@prchdk)2018 年 10 月 29 日
在維護方面,簡單性是關鍵,Hooks 提供了一種單一的、函式式的方式來實現邏輯共享,並且可能程式碼量更小。
為什麼 Vue 中需要 Hooks?
讀到這裡你肯定想知道 Hooks 在 Vue 中必須提供什麼。這似乎是一個不需要解決的問題。畢竟,類並不是 Vue 主要使用的模式。Vue 提供無狀態函式式元件(如果需要它們),但為什麼我們需要在函式式元件中攜帶狀態呢?我們有 mixins 用於組合可以在多個元件複用的相同邏輯。問題解決了。
我想到了同樣的事情,但在與 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 的名稱都會像useState
、useEffect
等。更多資訊可以檢視這裡。
在Evan 的線上 demo 裡,你可以看到他在何處訪問useState
和useEffect
並用於 render 函式。
如果你不熟悉 Vue 中的 render 函式,那麼看一看官網文件可能會有所幫助。
但是當我們使用 Vue 風格的 Hooks 時,我們會如何命名呢 —— 你猜對了 —— 比如:useData
,useComputed
等。
因此,為了讓我們看看如何在 Vue 中使用 Hooks,我建立了一個示例應用程式供我們探索。
詳見視訊演示:css-tricks.com/wp-content/…
在 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 連結。
掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為掘金 上的英文分享文章。內容覆蓋Android 、iOS 、前端 、後端 、區塊鏈 、產品 、設計 、人工智慧 等領域,想要檢視更多優質譯文請持續關注掘金翻譯計劃 、官方微博、知乎專欄 。