与 React 的区别

¥Differences to React

Preact 并不是要重新实现 React。存在差异。其中许多差异都是微不足道的,或者可以通过使用 preact/compat 来完全消除,preact/compat 是 Preact 上的一个薄层,试图实现与 React 100% 的兼容性。

¥Preact is not intended to be a reimplementation of React. There are differences. Many of these differences are trivial, or can be completely removed by using preact/compat, which is a thin layer over Preact that attempts to achieve 100% compatibility with React.

Preact 不尝试包含 React 的每一个功能的原因是为了保持小而专注 - 否则,简单地将优化提交给 React 项目会更有意义,因为 React 项目已经是一个非常复杂且架构良好的代码库。

¥The reason Preact does not attempt to include every single feature of React is in order to remain small and focused - otherwise it would make more sense to simply submit optimizations to the React project, which is already a very complex and well-architected codebase.



主要区别

¥Main differences

Preact 和 React 之间的主要区别在于,出于大小和性能原因,Preact 没有实现合成事件系统。Preact 使用浏览器的标准 addEventListener 来注册事件处理程序,这意味着事件命名和行为在 Preact 中的工作方式与在纯 JavaScript / DOM 中的工作方式相同。有关 DOM 事件处理程序的完整列表,请参阅 MDN 的事件参考

¥The main difference between Preact and React is that Preact does not implement a synthetic event system for size and performance reasons. Preact uses the browser's standard addEventListener to register event handlers, which means event naming and behavior works the same in Preact as it does in plain JavaScript / DOM. See MDN's Event Reference for a full list of DOM event handlers.

标准浏览器事件的工作方式与 React 中事件的工作方式非常相似,但有一些细微的差别。在 Preact 中:

¥Standard browser events work very similarly to how events work in React, with a few small differences. In Preact:

  • 事件不会通过 <Portal> 组件冒泡

    ¥events don't bubble up through <Portal> components

  • 对于表单输入,应使用标准 onInput 而不是 React 的 onChange(仅当不使用 preact/compat 时)

    ¥standard onInput should be used instead of React's onChange for form inputs (only if preact/compat is not used)

  • 应使用标准 onDblClick 而不是 React 的 onDoubleClick(仅当不使用 preact/compat 时)

    ¥standard onDblClick should be used instead of React's onDoubleClick (only if preact/compat is not used)

  • onSearch 通常应该用于 <input type="search">,因为清除 "x" 按钮不会在 IE11 中触发 onInput

    ¥onSearch should generally be used for <input type="search">, since the clear "x" button does not fire onInput in IE11

另一个显着的区别是 Preact 更严格地遵循 DOM 规范。像任何其他元素一样支持自定义元素,并且自定义事件支持区分大小写的名称(就像它们在 DOM 中一样)。

¥Another notable difference is that Preact follows the DOM specification more closely. Custom elements are supported like any other element, and custom events are supported with case-sensitive names (as they are in the DOM).

版本兼容性

¥Version Compatibility

对于 preact 和 preact/compat,版本兼容性是根据 React 当前和以前的主要版本来衡量的。当 React 团队宣布新功能时,如果考虑到 项目目标 有意义,它们可能会添加到 Preact 的核心中。这是一个相当民主的过程,通过公开的讨论和决策、使用问题和拉取请求不断发展。

¥For both preact and preact/compat, version compatibility is measured against the current and previous major releases of React. When new features are announced by the React team, they may be added to Preact's core if it makes sense given the Project Goals. This is a fairly democratic process, constantly evolving through discussion and decisions made in the open, using issues and pull requests.

因此,在讨论兼容性或进行比较时,网站和文档反映了 React 15.x17.x

¥Thus, the website and documentation reflect React 15.x through 17.x when discussing compatibility or making comparisons.

调试消息和错误

¥Debug messages and errors

我们灵活的架构允许插件以任何他们想要的方式增强 Preact 体验。这些插件之一是 preact/debug,它添加 有用的警告和错误 并附加 Preact 开发者工具 浏览器扩展(如果已安装)。这些指南可以指导你开发 Preact 应用,并让你更轻松地检查正在发生的情况。你可以通过添加相关的导入语句来启用它们:

¥Our flexible architecture allows addons to enhance the Preact experience in any way they want. One of those addons is preact/debug which adds helpful warnings and errors and attaches the Preact Developer Tools browser extension, if installed. Those guide you when developing Preact applications and make it a lot easier to inspect what's going on. You can enable them by adding the relevant import statement:

import "preact/debug"; // <-- Add this line at the top of your main entry file

这与 React 不同,React 需要存在一个打包器,通过检查 NODE_ENV != "production" 在构建时去除调试消息。

¥This is different from React which requires a bundler being present that strips out debugging messages at build time by checking for NODE_ENV != "production".

Preact 独有的功能

¥Features unique to Preact

Preact 实际上添加了一些受 (P)React 社区工作启发的便捷功能:

¥Preact actually adds a few convenient features inspired by work in the (P)React community:

对 ES 模块的原生支持

¥Native support for ES Modules

Preact 从一开始就考虑到了 ES 模块,并且是最早支持 ES 模块的框架之一。你可以直接在浏览器中通过 import 关键字加载 Preact,而无需先通过打包程序。

¥Preact was built with ES Modules in mind from the beginning, and was one of the first frameworks to support them. You can load Preact via the import keyword directly in browsers without having it to pass through a bundler first.

Component.render() 中的参数

¥Arguments in Component.render()

为了方便起见,我们将 this.propsthis.state 传递给类组件上的 render() 方法。看一下这个使用一个 prop 和一个 state 属性的组件。

¥For convenience, we pass this.props and this.state to the render() method on class components. Take a look at this component which uses one prop and one state property.

// Works in both Preact and React
class Foo extends Component {
  state = { age: 1 };

  render() {
    return <div>Name: {this.props.name}, Age: {this.state.age}</div>;
  }
}

在 Preact 中也可以这样写:

¥In Preact this can be also written like this:

// Only works in Preact
class Foo extends Component {
  state = { age: 1 };

  render({ name }, { age }) {
    return <div>Name: {name}, Age: {age}</div>;
  }
}

两个片段渲染完全相同的东西,提供渲染参数是为了方便。

¥Both snippets render the exact same thing, render arguments are provided for convenience.

原始 HTML 属性/属性名称

¥Raw HTML attribute/property names

Preact 旨在紧密匹配所有主流浏览器支持的 DOM 规范。将 props 应用于元素时,Preact 会检测每个 prop 是否应设置为属性或 HTML 属性。这使得在自定义元素上设置复杂的属性成为可能,但这也意味着你可以在 JSX 中使用像 class 这样的属性名称:

¥Preact aims to closely match the DOM specification supported by all major browsers. When applying props to an element, Preact detects whether each prop should be set as a property or HTML attribute. This makes it possible to set complex properties on Custom Elements, but it also means you can use attribute names like class in JSX:

// This:
<div class="foo" />

// ...is the same as:
<div className="foo" />

大多数 Preact 开发者更喜欢使用 class,因为它编写起来更短,但两者都受支持。

¥Most Preact developers prefer to use class because it's shorter to write, but both are supported.

JSX 中的 SVG

¥SVG inside JSX

SVG 的特性和属性名称非常有趣。SVG 对象上的一些属性(及其属性)是驼峰命名法(例如 ClipPath 元素上的 ClipPathUnits),一些属性是短横线命名法(例如 许多 SVG 元素上的剪辑路径),而其他属性(通常是从 DOM 继承的属性,例如 oninput)都是小写的。

¥SVG is pretty interesting when it comes to the names of its properties and attributes. Some properties (and their attributes) on SVG objects are camelCased (e.g. clipPathUnits on a clipPath element), some attributes are kebab-case (e.g. clip-path on many SVG elements), and other attributes (usually ones inherited from the DOM, e.g. oninput) are all lowercase.

Preact 按原样应用 SVG 属性。这意味着你可以将未修改的 SVG 片段直接复制并粘贴到你的代码中,并让它们开箱即用。这使得与设计人员用来生成图标或 SVG 插图的工具具有更好的互操作性。

¥Preact applies SVG attributes as-written. This means you can copy and paste unmodified SVG snippets right into your code and have them work out of the box. This allows greater interoperability with tools designers tend to use to generate icons or SVG illustrations.

// React
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
  <circle fill="none" strokeWidth="2" strokeLinejoin="round" cx="24" cy="24" r="20" />
</svg>
// Preact (note stroke-width and stroke-linejoin)
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
  <circle fill="none" stroke-width="2" stroke-linejoin="round" cx="24" cy="24" r="20" />
</svg>

如果你来自 React,你可能习惯于以驼峰命名法指定所有属性。你可以通过将 preact/compat 添加到项目中来继续使用始终采用驼峰命名法的 SVG 属性名称,这会镜像 React API 并规范化这些属性。

¥If you're coming from React, you may be used to specifying all attributes in camelCase. You can continue to use always-camelCase SVG attribute names by adding preact/compat to your project, which mirrors the React API and normalizes these attributes.

使用 onInput 代替 onChange

¥Use onInput instead of onChange

很大程度上由于历史原因,React 的 onChange 事件的语义实际上与浏览器提供的 onInput 事件相同,到处都支持。对于你希望在修改表单控件时做出反应的大多数情况,input 事件是最适合的事件。在 Preact 核心中,onChange 是标准 DOM 更改事件,当用户提交元素的值时会触发它。

¥Largely for historical reasons, the semantics of React's onChange event are actually the same as the onInput event provided by browsers, which is supported everywhere. The input event is the best-suited event for the majority of cases where you want to react when a form control is modified. In Preact core, onChange is the standard DOM change event that gets fired when an element's value is committed by the user.

// React
<input onChange={e => console.log(e.currentTarget.value)} />

// Preact
<input onInput={e => console.log(e.currentTarget.value)} />

如果你使用 preact/compat,则大多数 onChange 事件会在内部转换为 onInput 以模拟 React 的行为。这是我们用来确保与 React 生态系统最大兼容性的技巧之一。

¥If you're using preact/compat, most onChange events are internally converted to onInput to emulate React's behavior. This is one of the tricks we use to ensure maximum compatibility with the React ecosystem.

JSX 构造函数

¥JSX Constructor

JSX 是 JavaScript 的语法扩展,可转换为嵌套函数调用。使用这些嵌套调用来构建树结构的想法早在 JSX 之前就已存在,并且之前由 hyperscript 项目在 JavaScript 中普及。这种方法的价值远远超出了 React 生态系统的范围,因此 Preact 推广了最初的通用社区标准。要更深入地讨论 JSX 及其与 Hyperscript 的关系,请参阅 阅读这篇关于 JSX 如何工作的文章

¥JSX is a syntax extension for JavaScript that is converted to nested function calls. The idea of using these nested calls to build up tree structures long predates JSX, and was previously popularized in JavaScript by the hyperscript project. This approach has value well beyond the scope of the React ecosystem, so Preact promotes the original generalized community-standard. For a more in-depth discussion of JSX and its relationship to Hyperscript, read this article on how JSX works.

来源:(JSX)

¥Source: (JSX)

<a href="/">
  <span>Home</span>
</a>

输出:

¥Output:

// Preact:
h(
  'a',
  { href:'/' },
  h('span', null, 'Home')
);

// React:
React.createElement(
  'a',
  { href:'/' },
  React.createElement('span', null, 'Home')
);

最后,如果你查看 Preact 应用生成的输出代码,很明显较短的无命名空间 "JSX 杂注" 更易于阅读,并且更适合缩小等优化。在大多数 Preact 应用中,你都会遇到 h(),尽管你使用哪个名称并不重要,因为还提供了 createElement 别名导出。

¥Ultimately, if you're looking at the generated output code for a Preact application, it's clear that a shorter un-namespaced "JSX pragma" is both easier to read and more suitable for optimizations like minification. In most Preact apps you'll encounter h(), though it doesn't really matter which name you use since a createElement alias export is also provided.

不需要 contextTypes

¥No contextTypes needed

旧版 Context API 要求组件使用 React 的 contextTypeschildContextTypes 声明特定属性才能接收这些值。Preact 没有这个要求:默认情况下,所有组件都会接收 getChildContext() 生成的所有 context 属性。

¥The legacy Context API requires Components to declare specific properties using React's contextTypes or childContextTypes in order to receive those values. Preact does not have this requirement: all Components receive all context properties produced by getChildContext() by default.

preact/compat 独有的功能

¥Features exclusive to preact/compat

preact/compat 是我们的兼容层,它将 React 代码转换为 Preact。对于现有的 React 用户来说,这是一种无需更改任何代码即可通过打包器配置中的 设置一些别名 尝试 Preact 的简单方法。

¥preact/compat is our compatibility layer that translates React code to Preact. For existing React users this can be an easy way to try out Preact without changing any of your code, by setting up a few aliases in your bundler configuration.

子 API

¥Children API

Children API 是一组专门用于处理 props.children 值的方法。对于 Preact 这通常是不必要的,我们建议使用内置的数组方法。在 Preact 中,props.children 要么是一个 Virtual DOM 节点,要么是一个像 null 这样的空值,要么是一个 Virtual DOM 节点数组。前两种情况是最简单和最常见的,因为可以按原样使用或返回 children

¥The Children API is a specialized set of methods for working with the value of props.children. For Preact this is generally unnecessary, and we recommend using the built-in array methods instead. In Preact, props.children is either a Virtual DOM node, an empty value like null, or an Array of Virtual DOM nodes. The first two cases are the simplest and most common, since it's possible to use or return children as-is:

// React:
function App(props) {
  return <Modal content={Children.only(props.children)} />
}

// Preact: use props.children directly:
function App(props) {
  return <Modal content={props.children} />
}

对于需要迭代传递给组件的子级的特殊情况,Preact 提供了 toChildArray() 方法,该方法接受任何 props.children 值并返回扁平化和规范化的虚拟 DOM 节点数组。

¥For specialized cases where you need to iterate over the children passed to a component, Preact provides a toChildArray() method that accepts any props.children value and returns a flattened and normalized Array of Virtual DOM nodes.

// React
function App(props) {
  const cols = Children.count(props.children);
  return <div data-columns={cols}>{props.children}</div>
}

// Preact
function App(props) {
  const cols = toChildArray(props.children).length;
  return <div data-columns={cols}>{props.children}</div>
}

preact/compat 提供与 React 兼容的 Children API,可与现有组件库无缝集成。

¥A React-compatible Children API is available from preact/compat to make integration with existing component libraries seamless.

专用组件

¥Specialised Components

preact/compat 附带了并非每个应用都必需的专用组件。这些包括

¥preact/compat ships with specialised components that are not necessary for every app. These include

  • PureComponent:仅当 propsstate 发生更改时更新

    ¥PureComponent: Only updates if props or state have changed

  • memo:本质上与 PureComponent 类似,但允许使用自定义比较函数

    ¥memo: Similar in spirit to PureComponent but allows to use a custom comparison function

  • forwardRef:向指定的子组件提供 ref

    ¥forwardRef: Supply a ref to a specified child component.

  • 门户网站:继续将当前树渲染到不同的 DOM 容器中

    ¥Portals: Continues rendering the current tree into a different DOM container

  • Suspense:实验性允许在树未准备好时显示后备内容

    ¥Suspense: experimental Allows to display fallback content in case the tree is not ready

  • lazy:实验性延迟加载异步代码并相应地将树标记为就绪/未就绪。

    ¥lazy: experimental Lazy load async code and mark a tree as ready/not ready accordingly.

Preact 中文网 - 粤ICP备13048890号