组件

¥Components

组件代表 Preact 中的基本构建块。它们对于利用小构建块轻松构建复杂的 UI 至关重要。他们还负责将状态附加到我们渲染的输出。

¥Components represent the basic building block in Preact. They are fundamental in making it easy to build complex UIs from little building blocks. They're also responsible for attaching state to our rendered output.

Preact 中有两种组件,我们将在本指南中讨论。

¥There are two kinds of components in Preact, which we'll talk about in this guide.



函数组件

¥Functional Components

函数组件是接收 props 作为第一个参数的普通函数。函数名称必须以大写字母开头才能在 JSX 中工作。

¥Functional components are plain functions that receive props as the first argument. The function name must start with an uppercase letter in order for them to work in JSX.

function MyComponent(props) {
  return <div>My name is {props.name}.</div>;
}

// Usage
const App = <MyComponent name="John Doe" />;

// Renders: <div>My name is John Doe.</div>
render(App, document.body);
Run in REPL

请注意,在早期版本中,它们被称为 "Stateless Components"。这对于 hooks-addon 来说不再适用。

¥Note in earlier versions they were known as "Stateless Components". This doesn't hold true anymore with the hooks-addon.

类组件

¥Class Components

类组件可以具有状态和生命周期方法。后者是特殊方法,例如,当组件附加到 DOM 或销毁时将调用这些方法。

¥Class components can have state and lifecycle methods. The latter are special methods, that will be called when a component is attached to the DOM or destroyed for example.

这里我们有一个名为 <Clock> 的简单类组件,用于显示当前时间:

¥Here we have a simple class component called <Clock> that displays the current time:

class Clock extends Component {

  constructor() {
    super();
    this.state = { time: Date.now() };
  }

  // Lifecycle: Called whenever our component is created
  componentDidMount() {
    // update time every second
    this.timer = setInterval(() => {
      this.setState({ time: Date.now() });
    }, 1000);
  }

  // Lifecycle: Called just before our component will be destroyed
  componentWillUnmount() {
    // stop when not renderable
    clearInterval(this.timer);
  }

  render() {
    let time = new Date(this.state.time).toLocaleTimeString();
    return <span>{time}</span>;
  }
}
Run in REPL

生命周期方法

¥Lifecycle Methods

为了让时钟的时间每秒更新,我们需要知道 <Clock> 何时挂载到 DOM。如果你使用过 HTML5 自定义元素,这类似于 attachedCallbackdetachedCallback 生命周期方法。如果为组件定义了以下生命周期方法,Preact 会调用它们:

¥In order to have the clock's time update every second, we need to know when <Clock> gets mounted to the DOM. If you've used HTML5 Custom Elements, this is similar to the attachedCallback and detachedCallback lifecycle methods. Preact invokes the following lifecycle methods if they are defined for a Component:

生命周期法 当它被调用时
componentWillMount() (已弃用)在组件挂载到 DOM 之前
componentDidMount() 组件挂载到 DOM 后
componentWillUnmount() 从 DOM 中删除之前
componentWillReceiveProps(nextProps, nextContext) 在新的 props 被接受之前(已弃用)
getDerivedStateFromProps(nextProps, prevState) 就在 shouldComponentUpdate 之前。返回对象以更新状态或 null 以跳过更新。小心使用。
shouldComponentUpdate(nextProps, nextState, nextContext) render() 之前。返回 false 以跳过渲染
componentWillUpdate(nextProps, nextState, nextContext) render() 之前(已弃用)
getSnapshotBeforeUpdate(prevProps, prevState) 就在 render() 之前调用。返回值传递给 componentDidUpdate
componentDidUpdate(prevProps, prevState, snapshot) render() 之后

以下是它们如何相互关联的直观概述(最初由 Dan Abramov 在 一条推文 中发布):

¥Here's a visual overview of how they relate to each other (originally posted in a tweet by Dan Abramov):

Diagram of component lifecycle methods

错误边界

¥Error Boundaries

错误边界是实现 componentDidCatch() 或静态方法 getDerivedStateFromError()(或两者)的组件。这些是特殊方法,允许你捕获渲染期间发生的任何错误,通常用于提供更好的错误消息或其他后备内容并保存信息以用于记录目的。需要注意的是,错误边界无法捕获所有错误,并且在事件处理程序或异步代码(如 fetch() 调用)中抛出的错误需要单独处理。

¥An error boundary is a component that implements either componentDidCatch() or the static method getDerivedStateFromError() (or both). These are special methods that allow you to catch any errors that happen during rendering and are typically used to provide nicer error messages or other fallback content and save information for logging purposes. It's important to note that error boundaries cannot catch all errors and those thrown in event handlers or asynchronous code (like a fetch() call) need to be handled separately.

当捕获到错误时,我们可以使用这些方法对任何错误做出反应并显示一条不错的错误消息或任何其他后备内容。

¥When an error is caught, we can use these methods to react to any errors and display a nice error message or any other fallback content.

class ErrorBoundary extends Component {
  constructor() {
    super();
    this.state = { errored: false };
  }

  static getDerivedStateFromError(error) {
    return { errored: true };
  }

  componentDidCatch(error, errorInfo) {
    errorReportingService(error, errorInfo);
  }

  render(props, state) {
    if (state.errored) {
      return <p>Something went badly wrong</p>;
    }
    return props.children;
  }
}
Run in REPL

片段

¥Fragments

Fragment 允许你一次返回多个元素。它们解决了 JSX 的限制,即每个 "block" 必须有一个根元素。你经常会遇到它们与列表、表格或 CSS Flexbox 结合使用,否则任何中间元素都会影响样式。

¥A Fragment allows you to return multiple elements at once. They solve the limitation of JSX where every "block" must have a single root element. You'll often encounter them in combination with lists, tables or with CSS flexbox where any intermediate element would otherwise affect styling.

import { Fragment, render } from 'preact';

function TodoItems() {
  return (
    <Fragment>
      <li>A</li>
      <li>B</li>
      <li>C</li>
    </Fragment>
  )
}

const App = (
  <ul>
    <TodoItems />
    <li>D</li>
  </ul>
);

render(App, container);
// Renders:
// <ul>
//   <li>A</li>
//   <li>B</li>
//   <li>C</li>
//   <li>D</li>
// </ul>
Run in REPL

请注意,大多数现代转译器允许你对 Fragments 使用更短的语法。较短的一种更为常见,也是你通常会遇到的一种。

¥Note that most modern transpilers allow you to use a shorter syntax for Fragments. The shorter one is a lot more common and is the one you'll typically encounter.

// This:
const Foo = <Fragment>foo</Fragment>;
// ...is the same as this:
const Bar = <>foo</>;

你还可以从组件返回数组:

¥You can also return arrays from your components:

function Columns() {
  return [
    <td>Hello</td>,
    <td>World</td>
  ];
}

如果你在循环中创建密钥,请不要忘记将密钥添加到 Fragments

¥Don't forget to add keys to Fragments if you create them in a loop:

function Glossary(props) {
  return (
    <dl>
      {props.items.map(item => (
        // Without a key, Preact has to guess which elements have
        // changed when re-rendering.
        <Fragment key={item.id}>
          <dt>{item.term}</dt>
          <dd>{item.description}</dd>
        </Fragment>
      ))}
    </dl>
  );
}
Preact 中文网 - 粤ICP备13048890号