帮助
支持我们

开始使用

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

本指南帮助你开始运行,以使用 3 个流行选项开始开发 Preact 应用程序。如果你不熟悉 Preact,我们建议从Vite开始。



无构建工具路线

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

<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>

🔨 在 Glitch 上编辑

以这种方式进行开发的主要缺点是缺少 JSX,这需要构建步骤。下一节中记录了 JSX 的一种符合人体工程学且性能良好的替代方案。

JSX 的替代方案

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

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

<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>

🔨 在 Glitch 上编辑

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

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

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

创建 Vite 驱动的 Preact 应用程序

Vite 已成为过去几年中跨许多框架构建应用程序的非常流行的工具,Preact 也不例外。它建立在 ES 模块、Rollup 和 ESBuild 等流行工具之上。Vite 通过我们的初始化程序或其 Preact 模板,无需配置或事先了解即可开始使用,这种简单性使其成为使用 Preact 的非常流行的方式。

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

npm init preact

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

提示:这些决定都不必是最终决定,如果你改变主意,以后可以随时将它们添加到项目中或从中删除。

为开发做好准备

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

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

# Start a development server
npm run dev

服务器启动后,它将打印一个本地开发 URL,以便在浏览器中打开。现在你可以开始编写你的应用程序代码了!

制作生产版本

总有一天你需要在某处部署你的应用程序。Vite 附带了一个方便的 build 命令,它将生成一个经过高度优化的生产版本。

npm run build

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

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

集成到现有管道

如果你已经设置了现有的工具管道,则很可能其中包括一个打包器。最流行的选择是 webpackrollupparcel。Preact 与所有这些工具开箱即用,无需进行重大更改!

设置 JSX

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

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

Babel 拥有目前最好的部分文档。我们强烈建议你查看它,以了解有关 Babel 及其设置的问题。

将 React 别名化为 Preact

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

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

在 Webpack 中别名化

要在 Webpack 中为任何包创建别名,你需要将 resolve.alias 部分添加到你的配置中。根据你使用的配置,此部分可能已经存在,但缺少 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 中别名化

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

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

在 Parcel 中别名化

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

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

在 Rollup 中别名化

要在 Rollup 中创建别名,你需要安装 @rollup/plugin-alias。该插件需要放在 @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 中别名化

Jest 允许重写模块路径,类似于捆绑器。这些重写操作使用正则表达式在 Jest 配置中进行配置

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

在 TypeScript 中使用别名

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

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

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

将 Preact 与 HTM 和 ImportMaps 一起使用

Import Map 是一项较新的功能,允许你控制浏览器如何解析模块说明符,通常将裸说明符(如 preact)转换为 CDN URL(如 https://esm.sh/preact)。虽然许多人更喜欢导入映射可以提供的审美效果,但使用它们也有实际好处,例如对模块解析有更多控制(继续阅读以了解如何使用别名)以及解决从文件到文件复制 CDN URL 带来的负担(以及可能的错误)。

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

<script type="importmap">
  {
    "imports": {
      "preact": "https://esm.sh/[email protected]",
      "preact/": "https://esm.sh/[email protected]/",
      "htm/preact": "https://esm.sh/[email protected]/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 的副本,我们可以使用我们的导入映射自己处理它

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

<script type="importmap">
  {
    "imports": {
      "react": "https://esm.sh/[email protected]/compat",
      "react-dom": "https://esm.sh/[email protected]/compat"
    }
  }
</script>