Vite
本指南介绍在 AdonisJS 中使用 Vite 进行前端资源打包。你将学习如何:
- 安装并配置 Vite 集成
- 定义入口点并在 Edge 模板中引用资源
- 处理图片和字体等静态资源
- 为前端代码配置 TypeScript
- 启用 React 的热模块替换(HMR)
- 将打包后的资源部署到 CDN
概述
Vite 是一个现代化的前端构建工具,提供快速的开发服务器启动和即时的热模块替换。AdonisJS 将 Vite 直接嵌入到开发服务器中,而不是作为一个独立进程运行。这种嵌入式方法意味着你在开发期间只需管理一个服务器,并且 AdonisJS 可以直接访问 Vite 的运行时 API 来实现服务端渲染等特性。
该集成处理了将 Vite 与后端框架连接起来的复杂性。在开发期间,AdonisJS 通过中间件将资源请求代理给 Vite。在生产环境中,AdonisJS 读取 Vite 生成的 manifest 文件来解析打包后资源的正确路径。
官方的 @adonisjs/vite 包提供了用于生成资源 URL 的 Edge 辅助函数和标签、一个简化配置的专用 Vite 插件,以及用于服务端渲染的 Vite Runtime API 访问能力。
另请参阅: Vite 文档
安装
运行以下命令安装并配置该包。这会同时安装 @adonisjs/vite 和 vite,然后创建必要的配置文件。
node ace add @adonisjs/vite
查看配置命令执行的步骤
- 在
adonisrc.ts文件中注册以下服务提供者。
{
providers: [
// ...其他 providers
() => import('@adonisjs/vite/vite_provider')
]
}
- 创建
vite.config.ts和config/vite.ts配置文件。 - 在
resources/js/app.js创建前端入口点文件。
安装后,将以下内容添加到你的 adonisrc.ts 文件中,以将 Vite 与构建过程集成。
import { defineConfig } from '@adonisjs/core/build/standalone'
export default defineConfig({
hooks: {
buildStarting: [() => import('@adonisjs/vite/build_hook')],
},
})
assetsBundler 属性会禁用 AdonisJS Assembler 中默认的资源打包器管理。而 hooks 属性注册 Vite 构建钩子,以便在你运行 node ace build 时执行 Vite 构建过程。
另请参阅: Assembler 钩子
配置
设置过程会创建两个配置文件。vite.config.ts 文件配置 Vite 打包器本身,而 config/vite.ts 则配置 AdonisJS 在后端如何与 Vite 交互。
Vite 配置
vite.config.ts 文件是一个标准的 Vite 配置文件。你可以根据项目需求在这里安装并注册额外的 Vite 插件。
AdonisJS 插件接受以下选项。
import { defineConfig } from 'vite'
import adonisjs from '@adonisjs/vite/client'
export default defineConfig({
plugins: [
adonisjs({
/**
* 前端代码的入口点文件。每个入口点
* 会产生一个单独的输出 bundle。你可以为应用的
* 不同部分定义多个入口点。
*/
entrypoints: ['resources/js/app.js'],
/**
* 文件更改时触发浏览器重新加载的 glob 模式。
* 适用于 Vite 不追踪的模板文件。
*/
reload: ['resources/views/**/*.edge'],
}),
]
})
| Option | Description | Default |
|---|---|---|
entrypoints | 前端代码的入口点文件数组。每个入口点产生一个单独的 bundle。 | 必填 |
buildDirectory | 输出目录的相对路径。作为 build.outDir 传给 Vite。 | public/assets |
reload | 更改时触发浏览器重新加载的文件 glob 模式数组。 | [] |
assetsUrl | 生产环境中资源链接的 URL 前缀。将资源部署到 CDN 时,将其设置为你的 CDN URL。 | /assets |
如果你更改了 buildDirectory,你必须更新 config/vite.ts 中的相同值,以使两个配置保持同步。
AdonisJS 配置
config/vite.ts 文件告诉 AdonisJS 在哪里可以找到 Vite 的构建输出,以及如何生成资源 URL。
import { defineConfig } from '@adonisjs/vite'
export default defineConfig({
/**
* Vite 构建输出目录的路径。必须与
* vite.config.ts 中的 buildDirectory 选项匹配。
*/
buildDirectory: 'public/assets',
/**
* 资源链接的 URL 前缀。将资源部署到 CDN 时,
* 在生产环境中将其设置为你的 CDN URL。
*/
assetsUrl: '/assets',
})
| Option | Description |
|---|---|
buildDirectory | Vite 构建输出目录的路径。必须与 vite.config.ts 中的值匹配。 |
assetsUrl | 生产环境中资源链接的 URL 前缀。将资源部署到 CDN 时,将其设置为你的 CDN URL。 |
scriptAttributes | 键值对,用于添加到 @vite 标签生成的 script 标签上的属性。 |
styleAttributes | 键值对,用于添加到 @vite 标签生成的 link 标签上的属性。 |
你可以为生成的 script 和 link 标签添加自定义属性。
import { defineConfig } from '@adonisjs/vite'
export default defineConfig({
buildDirectory: 'public/assets',
assetsUrl: '/assets',
scriptAttributes: {
defer: true,
},
})
对于基于所加载资源的、有条件的属性,请改为传入一个函数。
import { defineConfig } from '@adonisjs/vite'
export default defineConfig({
buildDirectory: 'public/assets',
assetsUrl: '/assets',
styleAttributes: ({ src, url }) => {
if (src === 'resources/css/admin.css') {
return {
'data-turbo-track': 'reload'
}
}
}
})
文件夹结构
AdonisJS 不强制前端资源采用特定的文件夹结构。但是,我们建议将它们存放在 resources 目录中,并按资源类型划分子目录。
resources
├── css
│ └── app.css
├── js
│ └── app.js
├── fonts
└── images
Vite 默认将打包后的文件输出到 public/assets。/assets 子目录将 Vite 的输出与 public 文件夹中可能不想让 Vite 处理的其他静态文件分隔开。
启动开发服务器
使用 --hmr 标志启动你的应用以启用热模块替换(Hot Module Replacement)。AdonisJS 会自动将资源请求代理给嵌入的 Vite 服务器。
node ace serve --hmr
热模块替换(HMR) 允许 Vite 在不重新加载整个页面的情况下更新浏览器中的模块。当你编辑 CSS 文件或 JavaScript 模块时,更改会立即出现,同时保留应用状态。
在模板中包含入口点
使用 @vite Edge 标签为你的入口点渲染 script 和 link 标签。该标签接受一个入口点路径数组,并生成相应的 HTML 标签。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
@vite(['resources/js/app.js'])
</head>
<body>
@!section('content')
</body>
</html>
我们建议将 CSS 文件导入到你的 JavaScript 入口点中,而不是将它们注册为单独的入口点。这种方式让 Vite 自动处理 CSS 处理和热替换。
/**
* 在你的 JavaScript 入口点中导入 CSS。Vite 会处理
* CSS 并自动处理热替换。
*/
import '../css/app.css'
在模板中引用资源
Vite 会为你的入口点导入的文件构建依赖图,并在打包输出中更新它们的路径。但是,Vite 无法检测到仅在 Edge 模板中引用的资源,因为它不解析模板文件。
使用 asset 辅助函数为 Vite 处理的文件创建 URL。在开发期间,该辅助函数返回指向 Vite 开发服务器的 URL。在生产环境中,它返回带有文件名内容哈希的打包文件路径。
<img src="{{ asset('resources/images/logo.png') }}" alt="Logo">
<!-- 开发环境中的输出 -->
<img src="http://localhost:5173/resources/images/logo.png" alt="Logo">
<!-- 生产环境中的输出 -->
<img src="/assets/logo-3bc29777.png" alt="Logo">
处理静态资源
Vite 会忽略未被前端代码导入的静态资源。仅在 Edge 模板中引用的图片、字体和图标就属于这一类。
要将这些资源包含到构建中,请在你的入口点文件中使用 Vite 的 glob 导入 API。这告诉 Vite 处理被匹配的文件,即使它们没有被直接导入。
import '../css/app.css'
/**
* 告诉 Vite 处理 resources/images 目录中的所有图片。
* 如果没有这个,仅在模板中引用的图片会从
* 生产构建中缺失。
*/
import.meta.glob(['../images/**'])
添加 glob 导入后,你可以在模板中使用 asset 辅助函数引用这些图片。
<img src="{{ asset('resources/images/hero.jpg') }}" alt="Hero image">
TypeScript 配置
如果你将 TypeScript 用于前端代码,请在 resources 目录中创建一个单独的 tsconfig.json。Vite 和你的代码编辑器会将该配置用于 resources 目录中的 TypeScript 文件。
{
"extends": "../tsconfig.json",
"compilerOptions": {
"baseUrl": ".",
"lib": ["DOM"],
"paths": {
"@/*": ["./js/*"]
}
}
}
如果你使用 React,请添加 jsx 选项。
{
"extends": "../tsconfig.json",
"compilerOptions": {
"baseUrl": ".",
"lib": ["DOM"],
"jsx": "preserve",
"paths": {
"@/*": ["./js/*"]
}
}
}
React 与热模块替换
要在开发期间启用 React Fast Refresh,请在布局中的 @vite 标签之前添加 @viteReactRefresh Edge 标签。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
@viteReactRefresh()
@vite(['resources/js/app.js'])
</head>
<body>
<div id="root"></div>
</body>
</html>
然后在你的 Vite 配置中配置 React 插件。
import { defineConfig } from 'vite'
import adonisjs from '@adonisjs/vite/client'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [
adonisjs({
entrypoints: ['resources/js/app.js'],
}),
react(),
],
})
将资源部署到 CDN
要在生产环境中从 CDN 提供打包后的资源,请在两个配置文件中配置 assetsUrl 选项。这确保 manifest 文件中的 URL 和懒加载的 chunk 都指向你的 CDN 服务器。
import { defineConfig } from 'vite'
import adonisjs from '@adonisjs/vite/client'
export default defineConfig({
plugins: [
adonisjs({
entrypoints: ['resources/js/app.js'],
reload: ['resources/views/**/*.edge'],
assetsUrl: 'https://cdn.example.com/',
}),
]
})
import { defineConfig } from '@adonisjs/vite'
export default defineConfig({
buildDirectory: 'public/assets',
assetsUrl: 'https://cdn.example.com/',
})
使用 node ace build 构建应用后,将 public/assets 的内容上传到你的 CDN。
常见问题
资源在生产环境中未加载
如果资源在生产环境中加载失败,请验证 Vite 是否已在 public/assets/.vite/manifest.json 生成 manifest 文件。该 manifest 将源文件映射到它们的打包输出路径。
如果 manifest 缺失,请确保构建钩子已在 adonisrc.ts 中注册,并再次运行 node ace build。
静态图片从构建中缺失
仅在模板中引用的图片和其他静态资源不会自动包含在构建中。Vite 只处理被你的 JavaScript 代码导入的文件。
向你的入口点添加一个 glob 导入以包含静态资源。
import '../css/app.css'
// 将所有图片包含到构建中
import.meta.glob(['../images/**', '../fonts/**'])
HMR 不工作
热模块替换在启动开发服务器时需要 --hmr 标志。
node ace serve --hmr
如果 HMR 仍然不工作,请检查你的浏览器控制台是否有 WebSocket 连接错误。防火墙或代理配置可能会阻止 HMR 的 WebSocket 连接。
中间件模式
在 3.x 版本中,Vite 运行在中间件模式(middleware mode)下。AdonisJS 不是将 Vite 作为一个带自己服务器的独立进程启动,而是嵌入 Vite 并通过中间件代理匹配的请求。
中间件模式的优势包括:可以直接访问 Vite Runtime API 以进行服务端渲染,以及只需管理一个开发服务器。所有资源都通过 AdonisJS 提供,而不是通过独立的 Vite 进程。
另请参阅: Vite SSR 文档
Manifest 文件
当你为生产环境构建时,Vite 会在打包资源旁边生成一个 manifest 文件。该 manifest 是一个 JSON 文件,将源文件路径映射到它们的打包输出路径,包含内容哈希。
AdonisJS 读取此 manifest 来解析资源 URL。当你在生产环境中调用 asset 辅助函数或使用 @vite 标签时,AdonisJS 会在 manifest 中查找该文件并返回正确的打包路径。
manifest 文件默认位于 public/assets/.vite/manifest.json。
另请参阅: Vite 后端集成