从 Preact 8.x 升级

¥Upgrading from Preact 8.x

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

¥This document is intended to guide you through upgrading an existing Preact 8.x application to Preact X and is divided in 3 main sections

Preact X 带来了许多令人兴奋的新功能,例如 Fragmentshooks 以及与 React 生态系统的兼容性大大提高。我们试图将任何重大更改保持在最低限度,但无法在不影响我们的功能集的情况下完全消除所有更改。

¥Preact X brings many new exciting features such as Fragments, hooks and much improved compatibility with the React ecosystem. We tried to keep any breaking changes to the minimum possible, but couldn't eliminate all of them completely without compromising on our feature set.



升级依赖

¥Upgrading dependencies

注意:在本指南中,我们将使用 npm 客户端,并且这些命令应该很容易适用于其他包管理器,例如 yarn

¥Note: Throughout this guide we'll be using the npm client and the commands should be easily applicable to other package managers such as yarn.

让我们开始!首先安装 Preact X:

¥Let's begin! First install Preact X:

npm install preact

由于 compat 已移至核心,因此不再需要 preact-compat。删除它:

¥Because compat has moved to core, there is no need for preact-compat anymore. Remove it with:

npm remove preact-compat

更新 preact 相关库

¥Updating preact-related libraries

为了保证我们的用户(尤其是企业用户)有一个稳定的生态系统,我们发布了 Preact X 相关库的主要版本更新。如果你使用的是 preact-render-to-string,则需要将其更新到适用于 X 的版本。

¥To guarantee a stable ecosystem for our users (especially for our enterprise users) we've released major version updates to Preact X related libraries. If you're using preact-render-to-string you need to update it to the version that works with X.

Preact 8.x Preact X
preact-render-to-string 4.x 5.x
preact-router 2.x 3.x
preact-jsx-chai 2.x 3.x
preact-markup 1.x 2.x

Compat 已移至核心

¥Compat has moved to core

为了使第三方 React 库与 Preact 配合使用,我们提供了一个可以通过 preact/compat 导入的兼容层。它以前作为单独的包提供,但为了使协调更容易,我们已将其移至核心存储库中。因此,你需要将现有的导入或别名声明从 preact-compat 更改为 preact/compat(注意斜杠)。

¥To make third-party React libraries work with Preact we ship a compatibility layer that can be imported via preact/compat. It was previously available as a separate package, but to make coordination easier we've moved it into the core repository. So you'll need to change existing import or alias declarations from preact-compat to preact/compat (note the slash).

请注意不要在此处引入任何拼写错误。常见的似乎是写 compact 而不是 compat。如果你遇到问题,请将 compat 视为反应的 compatibility 层。这就是这个名字的由来。

¥Be careful not to introduce any spelling errors here. A common one seems to be to write compact instead of compat. If you're having trouble with that, think of compat as the compatibility layer for react. That's where the name is coming from.

第三方库

¥Third party libraries

由于重大变更的性质,某些现有库可能不再与 X 兼容。其中大多数已按照我们的测试计划进行更新,但你可能会遇到情况并非如此的情况。

¥Due to the nature of the breaking changes, some existing libraries may cease to work with X. Most of them have been updated already following our beta schedule but you may encounter one where this is not the case.

preact-redux

preact-redux 是尚未更新的此类库之一。好消息是 preact/compat 更加符合 React,并且可以与名为 react-redux 的 React 绑定一起开箱即用。切换到它将会解决这个问题。确保你已在打包程序中将 reactreact-dom 别名为 preact/compat

¥preact-redux is one of such libraries that hasn't been updated yet. The good news is that preact/compat is much more React-compliant and works out of the box with the React bindings called react-redux. Switching to it will resolve the situation. Make sure that you've aliased react and react-dom to preact/compat in your bundler.

  1. 删除 preact-redux

    ¥Remove preact-redux

  2. 安装 react-redux

    ¥Install react-redux

mobx-preact

由于我们与反应生态系统的兼容性增强,不再需要这个包。请改用 mobx-react

¥Due to our increased compatibility with the react-ecosystem this package isn't needed anymore. Use mobx-react instead.

  1. 删除 mobx-preact

    ¥Remove mobx-preact

  2. 安装 mobx-react

    ¥Install mobx-react

styled-components

Preact 8.x 仅适用于 styled-components@3.x。有了 Preact X,这个障碍就不再存在,我们使用最新版本的 styled-components。确保你的 别名 React 到 preact 正确。

¥Preact 8.x only worked up to styled-components@3.x. With Preact X this barrier is no more and we work with the latest version of styled-components. Make sure that you've aliased react to preact correctly.

preact-portal

Portal 组件现在是 preact/compat 的一部分。

¥The Portal component is now part of preact/compat.

  1. 删除 preact-portal

    ¥Remove preact-portal

  2. preact/compat 导入 createPortal

    ¥Import createPortal from preact/compat

准备好你的代码

¥Getting your code ready

使用命名导出

¥Using named exports

为了更好地支持 tree-shaking,我们不再在 preact 核心中附带 default 导出。这种方法的优点是只有你需要的代码才会包含在你的打包包中。

¥To better support tree-shaking we don't ship with a default export in preact core anymore. The advantage of this approach is that only the code you need will be included in your bundle.

// 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。它仍然具有命名导出和默认导出以保持与反应兼容。

¥Note: This change doesn't affect preact/compat. It still has both named and a default export to remain compatible with react.

render() 总是区分现有的子项

¥render() always diffs existing children

在 Preact 8.x 中,对 render() 的调用始终会将元素追加到容器中。

¥In Preact 8.x, the calls to render() would always append the elements to the container.

// 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 节点。

¥In order to diff existing children in Preact 8, an existing DOM node had to be provided.

// 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 库的行为更加匹配。

¥In Preact X, render() always diffs DOM children inside of the container. So if your container contains DOM that was not rendered by Preact, Preact will try to diff it with the elements you pass it. This new behavior more closely matches the behavior of other VDOM libraries.

// 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 方法。

¥If you are looking for behavior that exactly matches how React's render method works, use the render method exported by preact/compat.

props.children 并不总是 array

¥props.children is not always an array

在 Preact X 中,我们不能再保证 props.children 始终是 array 类型。此更改对于解决有关 Fragments 和返回 array 子级的组件的解析歧义是必要的。在大多数情况下,你甚至可能没有注意到它。只有在 props.children 上直接使用数组方法的地方才需要用 toChildArray 封装。该函数将始终返回一个数组。

¥In Preact X we can't guarantee props.children to always be of type array anymore. This change was necessary to resolve parsing ambiguities in regards to Fragments and components that return an array of children. In most cases you may not even notice it. Only in places where you'll use array methods on props.children directly need to be wrapped with toChildArray. This function will always return an array.

// 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

¥Don't access this.state synchronously

在 Preact X 中,组件的状态将不再同步改变。这意味着在 setState 调用之后立即从 this.state 读取将返回之前的值。相反,你应该使用回调函数来修改取决于先前值的状态。

¥In Preact X the state of a component will no longer be mutated synchronously. This means that reading from this.state right after a setState call will return the previous values. Instead you should use a callback function to modify state that depends on the previous values.

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 将跳过子项的比较

¥dangerouslySetInnerHTML will skip diffing of children

vnode 设置了 dangerouslySetInnerHTML 属性时,Preact 将跳过 vnode's 子项的比较。

¥When a vnode has the property dangerouslySetInnerHTML set Preact will skip diffing the vnode's children.

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

库作者须知

¥Notes for library authors

本节适用于维护与 Preact X 一起使用的包的库作者。如果你不编写此部分,则可以安全地跳过此部分。

¥This section is intended for library authors who are maintaining packages to be used with Preact X. You can safely skip this section if you're not writing one.

VNode 形状发生了变化

¥The VNode shape has changed

我们重命名/移动了以下属性:

¥We renamed/moved the following properties:

  • attributes -> props

  • nodeName -> type

  • children -> props.children

尽管我们尝试了很多,但我们总是会遇到为 React 编写的第三方库的边缘情况。对 vnode 形状的更改消除了许多难以发现的错误,并使我们的 compat 代码更加简洁。

¥As much as we tried, we always ran into edge-cases with third-party libraries written for react. This change to our vnode shape removed many difficult to spot bugs and makes our compat code a lot cleaner.

相邻文本节点不再连接

¥Adjacent text nodes are not joined anymore

在 Preact 8.x 中,我们有这个功能,我们可以将相邻的文本注释连接起来作为优化。这对于 X 来说不再适用,因为我们不再直接与 dom 进行比较。事实上,我们注意到它会损害 X 中的性能,这就是我们删除它的原因。举个例子:

¥In Preact 8.x we had this feature where we would join adjacent text notes as an optimization. This doesn't hold true for X anymore because we're not diffing directly against the dom anymore. In fact we noticed that it hurt performance in X which is why we removed it. Take the following example:

// 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
Preact 中文网 - 粤ICP备13048890号