从 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 带来了许多令人兴奋的新功能,例如 Fragments
、hooks
以及与 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 绑定一起开箱即用。切换到它将会解决这个问题。确保你已在打包程序中将 react
和 react-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.
删除
preact-redux
¥Remove
preact-redux
安装
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.
删除
mobx-preact
¥Remove
mobx-preact
安装
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
.
删除
preact-portal
¥Remove
preact-portal
从
preact/compat
导入createPortal
¥Import
createPortal
frompreact/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