帮助
支持我们

教程

本指南将逐步构建一个简单的“滴答时钟”组件。如果您不熟悉虚拟 DOM,请尝试完整的 Preact 教程

:information_desk_person: 本指南假设您已完成入门文档并已成功设置工具。如果没有,请从Vite开始。



Hello World

开箱即用,您将在任何 Preact 代码库中始终看到的两个函数是h()render()h()函数用于将 JSX 转换为 Preact 理解的结构。但它也可以直接使用,无需涉及任何 JSX

// With JSX
const App = <h1>Hello World!</h1>;

// ...the same without JSX
const App = h('h1', null, 'Hello World');

仅此一项并不能执行任何操作,我们需要一种方法将我们的 Hello-World 应用程序注入 DOM。为此,我们使用render()函数。

import { render } from 'preact';

const App = <h1>Hello World!</h1>;

// Inject our app into the DOM
render(App, document.getElementById('app'));
在 REPL 中运行

恭喜,您构建了第一个 Preact 应用程序!

交互式 Hello World

渲染文本是一个开始,但我们希望让我们的应用程序更具交互性。我们希望在数据更改时对其进行更新。:star2

我们的最终目标是创建一个应用程序,用户可以在其中输入姓名并在提交表单时显示姓名。为此,我们需要一个可以存储我们提交内容的地方。这就是组件发挥作用的地方。

因此,让我们将我们现有的应用程序变成组件

import { h, render, Component } from 'preact';

class App extends Component {
  render() {
    return <h1>Hello, world!</h1>;
  }
}

render(<App />, document.getElementById("app"));
在 REPL 中运行

您会注意到我们在顶部添加了一个新的 Component 导入,并且我们将 App 变成一个类。这本身并没有什么用,但它是我们下一步要做的先决条件。为了让事情变得更有趣,我们将添加一个带有文本输入和提交按钮的表单。

import { h, render, Component } from 'preact';

class App extends Component {
  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <form>
          <input type="text" />
          <button type="submit">Update</button>
        </form>
      </div>
    );
  }
}

render(<App />, document.getElementById("app"));
在 REPL 中运行

现在我们来谈谈!它开始看起来像一个真正的应用程序了!我们仍然需要使其具有交互性。请记住,我们希望将 "Hello world!" 更改为 "Hello, [userinput]!",因此我们需要一种方法来了解当前输入值。

我们将其存储在我们的组件的一个名为 state 的特殊属性中。它很特别,因为当它通过 setState 方法更新时,Preact 不仅会更新状态,还会为该组件安排一个渲染请求。一旦请求得到处理,我们的组件将使用更新后的状态重新渲染。

最后,我们需要通过设置 value 并将事件处理程序附加到 input 事件来将新状态附加到我们的输入。

import { h, render, Component } from 'preact';

class App extends Component {
  // Initialise our state. For now we only store the input value
  state = { value: '' }

  onInput = ev => {
    // This will schedule a state update. Once updated the component
    // will automatically re-render itself.
    this.setState({ value: ev.currentTarget.value });
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <form>
          <input type="text" value={this.state.value} onInput={this.onInput} />
          <button type="submit">Update</button>
        </form>
      </div>
    );
  }
}

render(<App />, document.getElementById("app"));
在 REPL 中运行

在这一点上,该应用程序从用户的角度来看不应该有太大变化,但我们将在下一步中将所有部分组合在一起。

我们将以与我们刚才对输入所做类似的方式向 <form>submit 事件添加一个处理程序。不同之处在于它写入我们 state 的另一个名为 name 的属性。然后,我们交换标题并在那里插入我们的 state.name 值。

import { h, render, Component } from 'preact';

class App extends Component {
  // Add `name` to the initial state
  state = { value: '', name: 'world' }

  onInput = ev => {
    this.setState({ value: ev.currentTarget.value });
  }

  // Add a submit handler that updates the `name` with the latest input value
  onSubmit = ev => {
    // Prevent default browser behavior (aka don't submit the form here)
    ev.preventDefault();

    this.setState({ name: this.state.value });
  }

  render() {
    return (
      <div>
        <h1>Hello, {this.state.name}!</h1>
        <form onSubmit={this.onSubmit}>
          <input type="text" value={this.state.value} onInput={this.onInput} />
          <button type="submit">Update</button>
        </form>
      </div>
    );
  }
}

render(<App />, document.getElementById("app"));
在 REPL 中运行

轰!我们完成了!我们现在可以输入自定义名称,单击“更新”,我们的新名称将出现在我们的标题中。

时钟组件

我们编写了我们的第一个组件,所以让我们多练习一下。这次我们制作一个时钟。

import { h, render, Component } from 'preact';

class Clock extends Component {
  render() {
    let time = new Date().toLocaleTimeString();
    return <span>{time}</span>;
  }
}

render(<Clock />, document.getElementById("app"));
在 REPL 中运行

好的,这很容易!问题是,时间不会改变。在我们渲染时钟组件的那一刻,它就被冻结了。

因此,我们希望在组件添加到 DOM 后启动一个 1 秒的计时器,并在将其移除时停止计时器。我们将在 componentDidMount 中创建计时器并存储对它的引用,并在 componentWillUnmount 中停止计时器。在每次计时器滴答声中,我们都会使用新的时间值更新组件的 state 对象。这样做将自动重新渲染组件。

import { h, render, Component } from 'preact';

class Clock extends Component {
  state = { time: Date.now() };

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

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

render(<Clock />, document.getElementById("app"));
在 REPL 中运行

我们又做到了!现在我们有了 一个滴答作响的时钟