說明
支持我們

從 Preact 8.x 升級

本文件旨在指導您將現有的 Preact 8.x 應用程式升級到 Preact X,並分為 3 個主要部分

Preact X 帶來了許多令人興奮的新功能,例如 片段Hook,以及與 React 生態系統的相容性大幅提升。我們已盡量將任何重大變更降至最低,但無法在不影響我們的功能集的情況下完全消除所有變更。



升級依賴項

注意:在整個指南中,我們將使用 npm 客户端,而命令應易於應用於其他套件管理員,例如 yarn

讓我們開始吧!首先安裝 Preact X

npm install preact

由於 compat 已移至核心,因此不再需要 preact-compat。使用以下指令移除它

npm remove preact-compat

為了保證使用者(特別是企業使用者)有穩定的生態系統,我們已發布與 Preact X 相關函式庫的主要版本更新。如果您正在使用 preact-render-to-string,則需要將其更新為與 X 相容的版本。

函式庫Preact 8.xPreact X
preact-render-to-string4.x5.x
preact-router2.x3.x
preact-jsx-chai2.x3.x
preact-markup1.x2.x

Compat 已移至核心

為了讓第三方 React 函式庫與 Preact 相容,我們提供了一個相容性層,可透過 preact/compat 匯入。它以前是作為一個獨立的套件提供,但為了簡化協調,我們已將它移至核心儲存庫。因此,您需要將現有的匯入或別名宣告從 preact-compat 變更為 preact/compat(請注意斜線)。

小心不要在此處輸入任何拼寫錯誤。常見的錯誤是將 compat 寫成 compact。如果您遇到此問題,請將 compat 視為 react 的相容性層。這就是名稱的由來。

如果您使用 preact-cli,則此步驟已為您完成 :tada

第三方函式庫

由於重大變更的特性,某些現有的函式庫可能無法與 X 搭配使用。其中大部分已依照我們的 beta 排程進行更新,但您可能會遇到尚未更新的函式庫。

preact-redux

preact-redux 就是其中一個尚未更新的函式庫。好消息是 preact/compat 更符合 React,並可與稱為 react-redux 的 React 繫結搭配使用。切換到它將能解決此問題。請確定您已在 bundler 中將 reactreact-dom 別名設為 preact/compat

  1. 移除 preact-redux
  2. 安裝 react-redux

mobx-preact

由於我們與 react 生態系相容性提高,此套件不再需要。請改用 mobx-react

  1. 移除 mobx-preact
  2. 安裝 mobx-react

styled-components

Preact 8.x 僅支援到 styled-components@3.x。有了 Preact X,此限制已不存在,我們支援最新版本的 styled-components。請確定您已正確將 react 別名設為 preact

preact-portal

Portal 元件現在是 preact/compat 的一部分。

  1. 移除 preact-portal
  2. preact/compat 匯入 createPortal

讓您的程式碼準備好

使用命名匯出

為了更好地支援 tree-shaking,我們不再在 preact 核心提供 default 匯出。此方法的優點是,您的套件只會包含您需要的程式碼。

// Preact 8.x
import Preact from "preact";

// Preact X
import * as preact from "preact";

// Preferred: Named exports (works in 8.x and Preact X)
import { h, Component } from "preact";

注意:此變更不會影響 preact/compat。它仍然有命名和預設匯出,以維持與 react 的相容性。

render() 始終對現有子項進行 diff

在 Preact 8.x 中,呼叫 render() 始終會將元素附加到容器。

// Existing markup:
<body>
  <div>hello</div>
</body>

render(<p>foo</p>, document.body);
render(<p>bar</p>, document.body);

// Preact 8.x output:
<body>
  <div>hello</div>
  <p>foo</p>
  <p>bar</p>
</body>

為了在 Preact 8 中比較現有的子元素,必須提供現有的 DOM 節點。

// Existing markup:
<body>
  <div>hello</div>
</body>

let element;
element = render(<p>foo</p>, document.body);
element = render(<p>bar</p>, document.body, element);

// Preact 8.x output:
<body>
  <div>hello</div>
  <p>bar</p>
</body>

在 Preact X 中,render() 始終會在容器內比較 DOM 子元素。因此,如果您的容器包含未由 Preact 呈現的 DOM,Preact 會嘗試將它與您傳遞給它的元素進行比較。此新行為更接近其他 VDOM 函式庫的行為。

// Existing markup:
<body>
  <div>hello</div>
</body>

render(<p>foo</p>, document.body);
render(<p>bar</p>, document.body);

// Preact X output:
<body>
  <p>bar</p>
  <div>hello</div>
</body>

如果您正在尋找與 React 的 render 方法工作方式完全匹配的行為,請使用 preact/compat 導出的 render 方法。

props.children 不總是 array

在 Preact X 中,我們不能再保證 props.children 永遠是 array 類型。此變更對於解決關於 Fragments 和傳回子元素 array 的元件的解析歧義是必要的。在多數情況下,您可能甚至不會注意到它。只有在您直接對 props.children 使用陣列方法的地方才需要用 toChildArray 進行包裝。此函式將永遠傳回一個陣列。

// Preact 8.x
function Foo(props) {
  // `.length` is an array method. In Preact X when `props.children` is not an
  // array, this line will throw an exception
  const count = props.children.length;
  return <div>I have {count} children </div>;
}

// Preact X
import { toChildArray } from "preact";

function Foo(props) {
  const count = toChildArray(props.children).length;
  return <div>I have {count} children </div>;
}

不要同步存取 this.state

在 Preact X 中,元件的狀態將不再同步變異。這表示在 setState 呼叫後立即從 this.state 讀取會傳回先前的值。您應該使用回呼函式來修改依賴於先前值的狀態。

this.state = { counter: 0 };

// Preact 8.x
this.setState({ counter: this.state.counter + 1 });

// Preact X
this.setState(prevState => {
  // Alternatively return `null` here to abort the state update
  return { counter: prevState.counter + 1 };
});

dangerouslySetInnerHTML 會略過子元素的比較

vnode 設定了 dangerouslySetInnerHTML 屬性時,Preact 會略過比較 vnode 的子元素。

<div dangerouslySetInnerHTML="foo">
  <span>I will be skipped</span>
  <p>So will I</p>
</div>

函式庫作者注意事項

此章節適用於維護套件供 Preact X 使用的函式庫作者。如果您沒有撰寫函式庫,可以跳過此章節。

VNode 形狀已變更

我們重新命名/移動了以下屬性

  • attributes -> props
  • nodeName -> type
  • children -> props.children

儘管我們盡了全力,但我們總是會遇到為 React 編寫的第三方函式庫的邊緣案例。對我們的 vnode 形狀進行此變更消除了許多難以發現的錯誤,並使我們的 compat 程式碼更為簡潔。

相鄰的文字節點不再合併

在 Preact 8.x 中,我們有此功能,其中我們將相鄰的文字節點合併作為最佳化。這不再適用於 X,因為我們不再直接與 DOM 進行比較。事實上,我們注意到它損害了 X 的效能,因此我們移除了它。以下為範例

// Preact 8.x
console.log(<div>foo{"bar"}</div>);
// Logs a structure like this:
//   div
//     text

// Preact X
console.log(<div>foo{"bar"}</div>);
// Logs a structure like this:
//   div
//     text
//     text