Barrel 文件
本指南介绍 AdonisJS 中的 barrel 文件,以及它们如何减少代码库中的导入杂乱。你将了解:
- 什么是 barrel 文件以及它们存储在哪里
- 它们为何存在(减少导入杂乱)
- 自动生成是如何工作的
- 如何在需要时禁用它们
概述
barrel 文件是为你应用中特定实体类型自动生成的导出集合。AdonisJS 会为控制器、Bouncer policies(策略)、events(事件)和 event listeners(事件监听器)创建 barrel 文件,并将它们存储在 .adonisjs/server 目录中。
barrel 文件是完全可选的。如果你愿意,可以继续使用直接导入,并通过配置来 完全禁用 barrel 文件生成 。
问题:导入杂乱
随着你的应用不断增长,start/routes.ts 之类的文件会累积数十个控制器导入。
在以下示例中,仅使用四个控制器,导入就已经占用了相当大的纵向空间。在拥有 20 多个控制器的生产应用中,你花在滚动跳过导入上的时间比处理路由的时间还多。
import router from '@adonisjs/core/services/router'
const NewAccountController = () => import('#controllers/new_account_controller')
const SessionController = () => import('#controllers/session_controller')
const PostsController = () => import('#controllers/posts_controller')
const PostCommentsController = () => import('#controllers/post_comments_controller')
router.get('signup', [NewAccountController, 'create'])
router.post('signup', [NewAccountController, 'store'])
router.get('login', [SessionController, 'create'])
router.post('login', [SessionController, 'store'])
router.get('posts', [PostsController, 'index'])
router.get('posts/:id', [PostsController, 'show'])
router.get('posts/:id/comments', [PostCommentsController, 'index'])
解决方案:Barrel 文件
barrel 文件将所有那些单独的导入合并到一个导入语句中。以下是使用 controllers barrel 文件的相同路由文件:
import router from '@adonisjs/core/services/router'
import { controllers } from '#generated/controllers'
router.get('signup', [controllers.NewAccount, 'create'])
router.post('signup', [controllers.NewAccount, 'store'])
router.get('login', [controllers.Session, 'create'])
router.post('login', [controllers.Session, 'store'])
router.get('posts', [controllers.Posts, 'index'])
router.get('posts/:id', [controllers.Posts, 'show'])
router.get('posts/:id/comments', [controllers.PostComments, 'index'])
区别是立即可见的。四个导入变成了一个,而你的路由就位于文件顶部你需要它们的地方。对你的路由定义唯一的更改是使用 controllers 命名空间来访问每个控制器。
Barrel 文件如何工作
barrel 文件本身非常简单。它只是一个将控制器名称映射到惰性导入函数的 JavaScript 对象。以下是 .adonisjs/server/controllers.ts 的样子:
export const controllers = {
NewAccount: () => import('#controllers/new_account_controller'),
Session: () => import('#controllers/session_controller'),
Posts: () => import('#controllers/posts_controller'),
PostComments: () => import('#controllers/post_comments_controller')
}
开发服务器会在你运行 node ace serve 时自动生成此文件。在开发过程中,当你创建或删除控制器时,开发服务器的观察器会更新 barrel 文件,使其与你的代码库保持同步。
文件位置与导入别名
barrel 文件组织在 .adonisjs/server 目录中,并带有相应的导入别名:
| Barrel File | Import Path | Purpose |
|---|---|---|
controllers.ts | #generated/controllers | 控制器导出 |
policies.ts | #generated/policies | Bouncer policies 导出 |
events.ts | #generated/events | 事件导出 |
listeners.ts | #generated/listeners | 事件监听器导出 |
.adonisjs/server 目录在 package.json 中注册为子路径导入别名,允许你使用 #generated 前缀。
import { controllers } from '#generated/controllers'
import { events } from '#generated/events'
import { listeners } from '#generated/listeners'
.adonisjs 目录包含由框架管理的自动生成文件。你不应手动编辑此目录中的文件,因为当开发服务器重新生成它们时,你的更改会被覆盖。
你应该将 .adonisjs 目录提交到版本控制中。这些文件是 TypeScript 解析像 #generated/controllers 这样的导入所必需的,没有它们,你的生产构建和 CI 流水线将会失败。
性能与惰性加载
你可能会想,一次导入所有控制器是否会损害性能。答案是否定的,因为 barrel 文件使用的是惰性导入。
barrel 文件中的每个控制器都被包装在一个返回动态导入的函数中。
{
Posts: () => import('#controllers/posts_controller')
}
() => import() 函数只有当你在路由中实际使用该控制器时才会被调用。在那之前,控制器模块永远不会被加载。这意味着 barrel 文件对性能零影响。控制器仍然是按需加载的,与使用直接导入时完全一样。
禁用 barrel 文件
如果你不想使用 barrel 文件,可以通过 adonisrc.ts 配置文件禁用它们的生成。生成是通过 init assembler 钩子来管理的。
import { indexEntities } from '@adonisjs/core'
import { defineConfig } from '@adonisjs/core/app'
export default defineConfig({
// ...其他配置
hooks: {
init: [
indexEntities({
controllers: {
enabled: false,
},
events: {
enabled: false,
},
listeners: {
enabled: false,
}
})
]
}
})
禁用 barrel 文件生成后,现有的 barrel 文件仍会保留在 .adonisjs/server 目录中。你需要手动移除它们,并更新任何引用它们的代码,改用直接导入。