帮助
支持我们

从 Preact 8.x 升级

本文档旨在指导你将现有的 Preact 8.x 应用程序升级到 Preact X,并分为 3 个主要部分

Preact X 带来了许多令人兴奋的新功能,例如 Fragmentshooks 以及与 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(注意斜杠)。

小心不要在此处引入任何拼写错误。一个常见的错误似乎是将 compact 写成 compat。如果你对此有疑问,可以将 compat 视为 react 的兼容性层。这就是名称的由来。

如果你正在使用 preact-cli,那么这一步已经为你完成了 :tada

第三方库

由于重大更改的性质,一些现有的库可能无法与 X 配合使用。其中大多数已根据我们的 beta 计划进行了更新,但你可能会遇到一个尚未更新的库。

preact-redux

preact-redux 就是尚未更新的此类库之一。好消息是 preact/compat 与 React 更加兼容,并且与名为 react-redux 的 React 绑定开箱即用。切换到它将解决此问题。确保已在打包器中将 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 仅适用于 [email protected]。使用 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() 始终对现有子项进行差异化

在 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 中读取将返回前一个值。相反,您应该使用回调函数来修改依赖于前一个值的 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