开始使用
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>
以这种方式进行开发的主要缺点是缺少 JSX,这需要构建步骤。下一节中记录了 JSX 的一种符合人体工程学且性能良好的替代方案。
JSX 的替代方案
编写原始的 h
或 createElement
调用可能会很乏味。根据我们的经验,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>
提示: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 文档。
集成到现有管道
如果你已经设置了现有的工具管道,则很可能其中包括一个打包器。最流行的选择是 webpack、rollup 或 parcel。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 编写的库和组件可与我们的兼容层无缝协作。要使用它,我们需要将所有 react
和 react-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>