說明
支持我們

教學

本指南將逐步說明如何建構一個簡單的「計時器」元件。如果您不熟悉虛擬 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

我們的最終目標是有一個應用程式,讓使用者可以在提交表單時輸入名稱並顯示它。為此,我們需要一個可以儲存我們提交內容的地方。這正是 元件 發揮作用的地方。

因此,讓我們將現有的 App 轉換為 元件

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 中執行

我們又做到了!現在我們有一個 滴答作響的時鐘