入门

¥Getting Started

Preact 新手?虚拟 DOM 新手?看看 教程

¥New to Preact? New to Virtual DOM? Check out the tutorial.

本指南可帮助你使用 3 个流行的选项开始开发 Preact 应用。如果你是 Preact 新手,我们建议从 Vite 开始。

¥This guide helps you get up and running to start developing Preact apps, using 3 popular options. If you're new to Preact, we recommend starting with Vite.



没有构建工具路由

¥No build tools route

Preact 被打包为可以直接在浏览器中使用,并且不需要任何构建或工具:

¥Preact is packaged to be used directly in the browser, and doesn't require any build or tools:

<script type="module">
  import { h, render } from 'https://esm.sh/preact';

  // Create your app
  const app = h('h1', null, 'Hello World!');

  render(app, document.body);
</script>

这种开发方式的主要缺点是缺少 JSX,这需要构建步骤。下一节将记录 JSX 的符合人机工程学且高性能的替代方案。

¥The primary drawback of developing this way is the lack of JSX, which requires a build step. An ergonomic and performant alternative to JSX is documented in the next section.

JSX 的替代品

¥Alternatives to JSX

编写原始 hcreateElement 调用可能很乏味。JSX 的优点是看起来与 HTML 类似,根据我们的经验,这使得许多开发者更容易理解。不过,JSX 需要构建步骤,因此我们强烈推荐名为 HTM 的替代方案。

¥Writing raw h or createElement calls can be tedious. JSX has the advantage of looking similar to HTML, which makes it easier to understand for many developers in our experience. JSX requires a build step though, so we highly recommend an alternative called HTM.

HTM 是一种类似 JSX 的语法,适用于标准 JavaScript。它不需要构建步骤,而是使用 JavaScript 自己的 标记模板 语法,该语法于 2015 年添加,并在 所有现代浏览器 中受支持。这是一种越来越流行的编写 Preact 应用的方式,因为与传统的前端构建工具设置相比,需要理解的移动部件更少。

¥HTM is a JSX-like syntax that works in standard JavaScript. Instead of requiring a build step, it uses JavaScript's own Tagged Templates syntax, which was added in 2015 and is supported in all modern browsers. This is an increasingly popular way to write Preact apps, since there are fewer moving parts to understand than a traditional front-end build tooling setup.

<script type="module">
  import { h, render } from 'https://esm.sh/preact';
  import htm from 'https://esm.sh/htm';

  // Initialize htm with Preact
  const html = htm.bind(h);

  function App (props) {
    return html`<h1>Hello ${props.name}!</h1>`;
  }

  render(html`<${App} name="World" />`, document.body);
</script>

提示:HTM 还提供了方便的单导入 Preact 版本:

¥Tip: HTM also provides a convenient single-import Preact version:

import { html, render } from 'https://esm.sh/htm/preact/standalone'

有关更完整的示例,请参阅 将 Preact 与 HTM 和 ImportMap 结合使用,有关 HTM 的更多信息,请查看其 文档

¥For a more full example, see Using Preact with HTM and ImportMaps, and for more information on HTM, check out its documentation.

创建由 Vite 支持的 Preact 应用

¥Create a Vite-Powered Preact App

在过去的几年里,Vite 已经成为跨多种框架构建应用的非常流行的工具,Preact 也不例外。它基于 ES 模块、Rollup 和 ESBuild 等流行工具构建。Vite 通过我们的初始化程序或他们的 Preact 模板,无需配置或先验知识即可开始使用,这种简单性使其成为一种非常流行的 Preact 使用方式。

¥Vite has become an incredibly popular tool for building applications across many frameworks in the past couple of years, and Preact is no exception. It's built upon popular tooling like ES modules, Rollup, and ESBuild. Vite, through our initializer or their Preact template, requires no configuration or prior knowledge to get started and this simplicity makes it a very popular way to use Preact.

要快速启动并运行 Vite,你可以使用我们的初始化程序 create-preact。这是一个交互式命令行接口 (CLI) 应用,可以在你计算机上的终端中运行。使用它,你可以通过运行以下命令来创建新应用:

¥To get up and running with Vite quickly, you can use our initializer create-preact. This is an interactive command-line interface (CLI) app that can be run in the terminal on your machine. Using it, you can create a new application by running the following:

npm init preact

这将引导你创建一个新的 Preact 应用,并为你提供一些选项,例如 TypeScript、路由(通过 preact-iso)和 ESLint 支持。

¥This will walk you through creating a new Preact app and gives you some options such as TypeScript, routing (via preact-iso), and ESLint support.

提示:这些决定都不需要是最终的,如果你改变主意,你可以随时在项目中添加或删除它们。

¥Tip: None of these decisions need to be final, you can always add or remove them from your project later if you change your mind.

为开发做好准备

¥Getting ready for development

现在我们准备开始我们的应用。要启动开发服务器,请在新生成的项目文件夹中运行以下命令:

¥Now we're ready to start our application. To start a development server, run the following command inside your newly generated project folder:

# Go into the generated project folder
cd my-preact-app

# Start a development server
npm run dev

服务器启动后,它将打印本地开发 URL 以在浏览器中打开。现在你已准备好开始编写你的应用了!

¥Once the server has started, it will print a local development URL to open in your browser. Now you're ready to start coding your app!

进行生产构建

¥Making a production build

有时你需要将应用部署到某个地方。Vite 附带了一个方便的 build 命令,它将生成高度优化的生产版本。

¥There comes a time when you need to deploy your app somewhere. Vite ships with a handy build command which will generate a highly-optimized production build.

npm run build

完成后,你将拥有一个新的 dist/ 文件夹,可以将其直接部署到服务器。

¥Upon completion, you'll have a new dist/ folder which can be deployed directly to a server.

有关所有可用命令及其选项的完整列表,请查看 Vite CLI 文档

¥For a full list of all available commands and their options, check out the Vite CLI Documentation.

集成到现有管道中

¥Integrating Into An Existing Pipeline

如果你已经设置了现有的工具管道,那么它很可能包含打包器。最流行的选择是 webpackrollupparcel。Preact 开箱即用,无需进行重大更改!

¥If you already have an existing tooling pipeline set up, it's very likely that this includes a bundler. The most popular choices are webpack, rollup or parcel. Preact works out of the box with all of them, no major changes needed!

设置 JSX

¥Setting up JSX

要转译 JSX,你需要一个 Babel 插件将其转换为有效的 JavaScript 代码。我们都用的是 @babel/plugin-transform-react-jsx。安装后,你需要指定应使用的 JSX 函数:

¥To transpile JSX, you need a Babel plugin that converts it to valid JavaScript code. The one we all use is @babel/plugin-transform-react-jsx. Once installed, you need to specify the function for JSX that should be used:

{
  "plugins": [
    ["@babel/plugin-transform-react-jsx", {
      "pragma": "h",
      "pragmaFrag": "Fragment",
    }]
  ]
}

Babel 拥有一些最好的文档。我们强烈建议你查看有关 Babel 以及如何设置它的问题。

¥Babel has some of the best documentation out there. We highly recommend checking it out for questions surrounding Babel and how to set it up.

将 React 别名为 Preact

¥Aliasing React to Preact

在某些时候,你可能想要利用庞大的 React 生态系统。最初为 React 编写的库和组件与我们的兼容层无缝协作。为了使用它,我们需要将所有 reactreact-dom 导入指向 Preact。此步骤称为别名。

¥At some point, you'll probably want to make use of the vast React ecosystem. Libraries and Components originally written for React work seamlessly with our compatibility layer. To make use of it, we need to point all react and react-dom imports to Preact. This step is called aliasing.

注意:如果你使用的是 Vite、Preact CLI 或 WMR,默认情况下会自动为你处理这些别名。

¥Note: If you're using Vite, Preact CLI, or WMR, these aliases are automatically handled for you by default.

Webpack 中的别名

¥Aliasing in Webpack

要为 Webpack 中的任何包添加别名,你需要将 resolve.alias 部分添加到你的配置中。根据你使用的配置,此部分可能已经存在,但缺少 Preact 的别名。

¥To alias any package in Webpack, you need to add the resolve.alias section to your config. Depending on the configuration you're using, this section may already be present, but missing the aliases for Preact.

const config = {
   //...snip
  "resolve": {
    "alias": {
      "react": "preact/compat",
      "react-dom/test-utils": "preact/test-utils",
      "react-dom": "preact/compat",     // Must be below test-utils
      "react/jsx-runtime": "preact/jsx-runtime"
    },
  }
}

Node 中的别名

¥Aliasing in Node

在 Node 中运行时,打包器别名(Webpack、Rollup 等)将不起作用,如 NextJS 中所示。为了解决这个问题,我们可以直接在 package.json 中使用别名:

¥When running in Node, bundler aliases (Webpack, Rollup, etc.) will not work, as can be seen in NextJS. To fix this, we can use aliases directly in our package.json:

{
  "dependencies": {
    "react": "npm:@preact/compat",
    "react-dom": "npm:@preact/compat",
  }
}

Parcel 中的别名

¥Aliasing in Parcel

Parcel 使用标准 package.json 文件来读取 alias 键下的配置选项。

¥Parcel uses the standard package.json file to read configuration options under an alias key.

{
  "alias": {
    "react": "preact/compat",
    "react-dom/test-utils": "preact/test-utils",
    "react-dom": "preact/compat",
    "react/jsx-runtime": "preact/jsx-runtime"
  },
}

Rollup 中的别名

¥Aliasing in Rollup

要在 Rollup 中使用别名,你需要安装 @rollup/plugin-alias。该插件需要放置在 @rollup/plugin-node-resolve 之前

¥To alias within Rollup, you'll need to install @rollup/plugin-alias. The plugin will need to be placed before your @rollup/plugin-node-resolve

import alias from '@rollup/plugin-alias';

module.exports = {
  plugins: [
    alias({
      entries: [
        { find: 'react', replacement: 'preact/compat' },
        { find: 'react-dom/test-utils', replacement: 'preact/test-utils' },
        { find: 'react-dom', replacement: 'preact/compat' },
        { find: 'react/jsx-runtime', replacement: 'preact/jsx-runtime' }
      ]
    })
  ]
};

Jest 中的别名

¥Aliasing in Jest

Jest 允许重写类似于打包器的模块路径。这些重写是在 Jest 配置中使用正则表达式进行配置的:

¥Jest allows the rewriting of module paths similar to bundlers. These rewrites are configured using regular expressions in your Jest configuration:

{
  "moduleNameMapper": {
    "^react$": "preact/compat",
    "^react-dom/test-utils$": "preact/test-utils",
    "^react-dom$": "preact/compat",
    "^react/jsx-runtime$": "preact/jsx-runtime"
  }
}

TypeScript 中的别名

¥Aliasing in TypeScript

即使与打包器一起使用,TypeScript 也有自己的解析类型的过程。为了确保使用 Preact 的类型代替 React 的类型,你需要将以下配置添加到 tsconfig.json(或 jsconfig.json):

¥TypeScript, even when used alongside a bundler, has its own process of resolving types. In order to ensure Preact's types are used in place of React's, you will want to add the following configuration to your tsconfig.json (or jsconfig.json):

{
  "compilerOptions": {
    ...
    "skipLibCheck": true,
    "baseUrl": "./",
    "paths": {
      "react": ["./node_modules/preact/compat/"],
      "react/jsx-runtime": ["./node_modules/preact/jsx-runtime"],
      "react-dom": ["./node_modules/preact/compat/"],
      "react-dom/*": ["./node_modules/preact/compat/*"]
    }
  }
}

此外,你可能想要启用 skipLibCheck,就像我们在上面的示例中所做的那样。一些 React 库使用了 preact/compat 可能未提供的类型(尽管我们尽力修复这些类型),因此,这些库可能是 TypeScript 编译错误的根源。通过设置 skipLibCheck,你可以告诉 TS 它不需要对所有 .d.ts 文件进行完整检查(通常这些文件仅限于 node_modules 中的库),这将修复这些错误。

¥Additionally, you may want to enable skipLibCheck as we do in the example above. Some React libraries make use of types that may not be provided by preact/compat (though we do our best to fix these), and as such, these libraries could be the source of TypeScript compilation errors. By setting skipLibCheck, you can tell TS that it doesn't need to do a full check of all .d.ts files (usually these are limited to your libraries in node_modules) which will fix these errors.

将 Preact 与 HTM 和 ImportMap 结合使用

¥Using Preact with HTM and ImportMaps

导入映射 是一项较新的功能,允许你控制浏览器解析模块说明符的方式,通常是将 preact 等裸说明符转换为 https://esm.sh/preact 等 CDN URL。虽然许多人确实更喜欢导入映射可以提供的美观性,但使用它们也有真正的优势,例如对模块解析的更多控制(继续阅读以了解如何别名)以及解决随之而来的负担(以及可能的错误) 将 CDN URL 从一个文件复制到另一个文件。

¥An Import Map is a newer feature that allows you to control how browsers resolve module specifiers, usually to convert bare specifiers such as preact to a CDN URL like https://esm.sh/preact. While many do prefer the aesthetics import maps can provide, there are also real advantages to using them, such as more control over module resolution (read on to see how to alias) and solving the burden (as well as possible bugs) that comes with copying CDN URLs from file to file.

以下是正在使用的导入映射的示例:

¥Here's an example of a import map in use:

<script type="importmap">
  {
    "imports": {
      "preact": "https://esm.sh/preact@10.19.2",
      "preact/": "https://esm.sh/preact@10.19.2/",
      "htm/preact": "https://esm.sh/htm@3.1.1/preact?external=preact"
    }
  }
</script>

<script type="module">
  import { render } from 'preact';
  import { useReducer } from 'preact/hooks';
  import { html } from 'htm/preact';

  export function App() {
    const [count, add] = useReducer((a, b) => a + b, 0);

    return html`
      <button onClick=${() => add(-1)}>Decrement</button>
      <input readonly size="4" value=${count} />
      <button onClick=${() => add(1)}>Increment</button>
    `;
  }

  render(html`<${App} />`, document.body);
</script>

注意:我们在上面的示例中使用 ?external=preact,因为许多 CDN 将有助于提供你所需的模块及其依赖。然而,这可能会导致 Preact 出错,因为它(以及 React)期望作为单例加载(一次只有 1 个实例处于活动状态)。使用 ?external 告诉 esm.sh 它不需要提供 preact 的副本,我们可以使用导入映射自行处理

¥Note: We use ?external=preact in the example above as many CDNs will helpfully provide the module you're asking for as well as its dependencies. However, this can trip up Preact as it (and React too) expect to be loaded as singletons (only 1 instance active at a time). Using ?external tells esm.sh that it doesn't need to provide a copy of preact, we can handle that ourselves with our import map

你甚至可以使用导入映射来支持别名:

¥You can even use import maps to support aliasing:

<script type="importmap">
  {
    "imports": {
      "react": "https://esm.sh/preact@10.19.2/compat",
      "react-dom": "https://esm.sh/preact@10.19.2/compat"
    }
  }
</script>
Preact 中文网 - 粤ICP备13048890号