无需构建的工作流程

¥No-Build Workflows

虽然 Webpack、Rollup 和 Vite 等构建工具非常强大且有用,但 Preact 完全支持在没有它们的情况下构建应用。

¥Whilst build tools like Webpack, Rollup, and Vite are incredibly powerful and useful, Preact fully supports building applications without them.

无构建工作流是一种开发 Web 应用的方式,同时放弃构建工具,而是依靠浏览器来促进模块加载和执行。这是开始使用 Preact 的好方法,并且可以在所有规模下继续很好地工作。

¥No-build workflows are a way to develop web applications while forgoing build tooling, instead relying on the browser to facilitate module loading and execution. This is a great way to get started with Preact and can continue to work very well at all scales.



导入映射

¥Import Maps

导入映射 是一种较新的浏览器功能,可让你控制浏览器如何解析模块说明符,通常将裸说明符(例如 preact)转换为 CDN URL(例如 https://esm.sh/preact)。虽然许多人确实更喜欢导入映射可以提供的美感,但依赖的集中化也有客观优势,例如更容易的版本控制、减少/删除重复以及更好地访问更强大的 CDN 功能。

¥An Import Map is a newer browser feature that allows you to control how browsers resolve module specifiers, often 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 objective advantages to the centralization of dependencies such as easier versioning, reduced/removed duplication, and better access to more powerful CDN features.

我们通常建议那些选择放弃构建工具的人使用导入映射,因为它们可以解决你在导入说明符中使用裸 CDN URL 时可能遇到的一些问题(更多信息见下文)。

¥We do generally recommend using import maps for those choosing to forgo build tooling as they work around some issues you may encounter using bare CDN URLs in your import specifiers (more on that below).

基本用法

¥Basic Usage

MDN 有大量关于如何使用导入映射的信息,但一个基本示例如下所示:

¥MDN has a great deal of information on how to utilize import maps, but a basic example looks like the following:

<!DOCTYPE html>
<html>
  <head>
    <script type="importmap">
      {
        "imports": {
          "preact": "https://esm.sh/preact@10.23.1",
          "htm/preact": "https://esm.sh/htm@3.1.1/preact?external=preact"
        }
      }
    </script>
  </head>
  <body>
    <div id="app"></div>

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

      export function App() {
        return html`<h1>Hello, World!</h1>`;
      }

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

我们创建一个带有 type="importmap" 属性的 <script> 标签,然后将我们想要在其中使用的模块定义为 JSON。稍后,在 <script type="module"> 标签中,我们可以使用裸说明符导入这些模块,类似于你在 Node 中看到的。

¥We create a <script> tag with a type="importmap" attribute, and then define the modules we'd like to use inside of it as JSON. Later, in a <script type="module"> tag, we can import these modules using bare specifiers, similar to what you'd see in Node.

重要:我们在上面的例子中使用 ?external=preact,因为 https://esm.sh 将有帮助地提供你要求的模块及其依赖 - 对于 htm/preact,这意味着还提供 preact 的副本。但是,Preact 必须仅用作单例,并且你的应用中只包含一个副本。

¥Important: We use ?external=preact in the example above as https://esm.sh will helpfully provide the module you're asking for as well as its dependencies -- for htm/preact, this means also providing a copy of preact. However, Preact must be used only as a singleton with only a single copy included in your app.

通过使用 ?external=preact,我们告诉 esm.sh 它不应该提供 preact 的副本,我们可以自己处理。因此,浏览器将使用我们的 importmap 来解析 preact,使用与我们其余代码相同的 Preact 实例。

¥By using ?external=preact, we tell esm.sh that it shouldn't provide a copy of preact, we can handle that ourselves. Therefore, the browser will use our importmap to resolve preact, using the same Preact instance as the rest of our code.

秘诀和常见模式

¥Recipes and Common Patterns

虽然不是详尽的列表,但这里有一些在使用导入映射时可能会有用的常见模式和方法。如果你有想要看到的模式,让我们知道

¥While not an exhaustive list, here are some common patterns and recipes you may find useful when working with import maps. If you have a pattern you'd like to see, let us know!

对于这些示例,我们将使用 https://esm.sh 作为我们的 CDN - 它是一个出色的、以 ESM 为中心的 CDN,比其他一些 CDN 更灵活、更强大,但你绝不会局限于它。无论你选择如何提供模块,请确保你熟悉有关依赖的策略:preact 和其他一些库的重复会导致(通常是微妙和意想不到的)问题。对于 esm.sh,我们使用 ?external 查询参数来解决这个问题,但其他 CDN 可能以不同的方式工作。

¥For these examples we'll be using https://esm.sh as our CDN -- it's a brilliant, ESM-focused CDN that's a bit more flexible and powerful than some others, but by no means are you limited to it. However you choose to serve your modules, make sure you're familiar with the policy regarding dependencies: duplication of preact and some other libraries will cause (often subtle and unexpected) issues. For esm.sh, we address this with the ?external query parameter, but other CDNs may work differently.

使用 Hooks、信号和 HTM 的 Preact

¥Preact with Hooks, Signals, and HTM

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

将 React 别名为 Preact

¥Aliasing React to Preact

<script type="importmap">
  {
    "imports": {
      "preact": "https://esm.sh/preact@10.23.1",
      "preact/": "https://esm.sh/preact@10.23.1/",
      "react": "https://esm.sh/preact@10.23.1/compat",
      "react/": "https://esm.sh/preact@10.23.1/compat/",
      "react-dom": "https://esm.sh/preact@10.23.1/compat",
      "@mui/material": "https://esm.sh/@mui/material@5.16.7?external=react,react-dom"
    }
  }
</script>

HTM

虽然 JSX 通常是编写 Preact 应用最流行的方式,但它需要一个构建步骤来将非标准语法转换为浏览器和其他运行时可以原生理解的东西。手工编写 h/createElement 调用可能有点乏味,而且不太符合人机工程学,因此我们推荐一种类似 JSX 的替代方案,称为 HTM

¥Whilst JSX is generally the most popular way to write Preact applications, it requires a build step to convert the non-standard syntax into something browsers and other runtimes can understand natively. Writing h/createElement calls by hand can be a bit tedious though with less than ideal ergonomics, so we instead recommend a JSX-like alternative called HTM.

HTM 不需要构建步骤(尽管它可以使用它,请参阅 `babel-plugin-htm`),而是使用 标记模板 语法,这是 JavaScript 的一项功能,自 2015 年以来一直存在,并且受所有现代浏览器支持。这是一种越来越流行的编写 Preact 应用的方式,对于那些选择放弃构建步骤的人来说,这可能是最受欢迎的方式。

¥Instead of requiring a build step (though it can use one, see `babel-plugin-htm`), HTM uses Tagged Templates syntax, a feature of JavaScript that's been around since 2015 and is supported in all modern browsers. This is an increasingly popular way to write Preact apps and is likely the most popular for those choosing to forgo a build step.

HTM 支持所有标准 Preact 功能,包括组件、钩子、信号等,唯一的区别是用于编写 "JSX" 返回值的语法。

¥HTM supports all standard Preact features, including Components, Hooks, Signals, etc., the only difference being the syntax used to write the "JSX" return value.

import { useState } from 'preact/hooks';
import { html } from 'htm/preact';

function Button({ action, children }) {
	return html`<button onClick=${action}>${children}</button>`;
}

function Counter() {
	const [count, setCount] = useState(0);

	return html`
		<div class="counter-container">
			<${Button} action=${() => setCount(count + 1)}>Increment<//>
			<input readonly value=${count} />
			<${Button} action=${() => setCount(count - 1)}>Decrement<//>
		</div>
	`;
}
Run in REPL