如何用一行 require.context,让 d2-admin 自动注册 15 个 Vuex 状态模块
【免费下载链接】d2-adminAn elegant dashboard项目地址: https://gitcode.com/gh_mirrors/d2/d2-admin
Vuex 入口文件的 import 清单:模块增长下的隐藏坑
当 Vuex 模块涨到几十个时,store 入口文件会成长串 import,漏写一行就是「模块未注册」的报错满天飞。Vue 2 中后台模板 d2-admin 用一行require.context解掉了这个痛点:15 个 Vuex 模块自动注册,新增模块时入口文件零改动,这也是它模块化状态管理最巧的一笔。
require.context 自动注册:16 行聚合器如何运转 🔧
先看运行时数据怎么流转,再落到源码。入口文件 src/store/index.js 只有一行new Vuex.Store({ modules: { d2admin } }),所有魔法都在聚合器 src/store/modules/d2admin/index.js 里,共 16 行。整个机制横跨三个阶段:构建时、启动时、调用时。
构建时:webpack 先把「模块名单」写进打包产物
require.context是 webpack 的构建时 API,三个参数限定范围:./modules只扫本目录,false不递归子目录,/\.js$/只匹配 js 文件。编译阶段 webpack 就把目录里所有 .js 文件逐一登记,运行时files.keys()只是读这份名单,几乎没有开销——注册范围在构建那一刻就确定了。
const files = require.context('./modules', false, /\.js$/) files.keys().forEach(key => { modules[key.replace(/(\.\/|\.js)/g, '')] = files(key).default })启动时:文件名就是模块名
files.keys()返回的是./theme.js这类路径,正则去掉./前缀和.js后缀,剩下theme当键,值是每个文件export default的模块对象。说人话:文件叫什么名,模块就注册成什么名。设计动机是把「注册信息」从代码挪进目录结构——文件本身就是注册声明,约定优于配置,新人看懂目录就会扩展。
注册:聚合器统一声明命名空间
export default { namespaced: true, modules }子模块不管命名空间,聚合器收口声明namespaced: true,再配合入口文件的顶层d2admin注册,形成d2admin/子模块/action的三级调用地址,规则只存在一处。文件头注释特意写着「不应该有任何理由修改此文件」——它是被设计成无人触碰的。
调用时:三级地址如何解析
运行时组件调用this.$store.dispatch('d2admin/theme/set', 'star'),Vuex 按三级地址定位到 theme.js 的setaction;action 内部再跨命名空间调用持久化模块,注意第二参{ root: true },它是「从根 store 找路径」的跨命名空间标志:
await dispatch('d2admin/db/set', { dbName: 'sys', path: 'theme.activeName', value: state.activeName, user: true }, { root: true })db.js 的持久化 action 把主题名写进本地数据库,启动时各模块的loadaction 再取回——「自动注册 + 持久化双写」闭环,src/setting.js 里定义的 7 套主题刷新后原样恢复。
最省心的部分:新增模块只需在 modules 目录丢一个report.js,下次编译它就在名单里,report自动完成注册,其余文件一行不用改。
横向对比:手写注册 vs 自动扫描注册
| 对比维度 | 传统手写方式 | d2-admin 聚合器方式 |
|---|---|---|
| 新增模块的注册成本 | 补 import 和入口配置,两处改动 | 新建文件放 modules 目录即可 |
| 命名空间声明 | 各子模块自行声明,容易漏写 | 聚合器统一声明 namespaced |
| 遗漏的兜底 | 靠人脑记忆,Code Review 才能发现 | 目录即唯一事实来源,结构上无法遗漏 |
| 构建工具依赖 | 无,纯 ES import | 依赖 webpack 的 require.context |
最小迁移方案:拷走 3 个文件就能用
最小集合就 3 个文件:store 入口、聚合器、一个业务模块做样板。聚合器文件可以直接抄:
// modules/index.js ← 聚合器,可直接抄 const files = require.context('./modules', false, /\.js$/) export default { namespaced: true, modules: Object.fromEntries( files.keys().map(k => [k.replace(/(\.\/|\.js)/g, ''), files(k).default]) ) }- 可以直接抄的部分:上面整个聚合器(含正则与
namespaced: true声明);入口文件只需一行new Vuex.Store({ modules: { app: modules } })。 - 必须按自己场景改的部分:目录名
./modules和匹配正则要改成你的目录;业务模块文件里的 state、actions 换成你自己的业务;没有统一持久化层的,可以砍掉 db 模块,由各模块直接操作 localStorage。
边界与适用场景:什么时候别用 ⚠️
这套「聚合器 + 自动注册」适合扁平、同构的模块目录、webpack 构建、且团队接受「文件名即注册名」约定。三类情况不建议用:
- 项目用 Vite 构建:
require.context是 webpack 专属 API,Vite 的替代是import.meta.glob,语法与行为都不同,直接抄会跑不起来。 - 一个模块要拆多个文件:方案前提是「一个文件一个模块」,主题逻辑若拆成 theme.js 与 theme.util.js,自动扫描只认得一个,约定就破了。
- 目录混入非模块文件:工具函数、常量放同一目录会被误注册,要么加正则过滤、要么靠目录纪律,优雅度打折。
如果你的 Vuex 模块天然是嵌套多文件结构,或构建链不是 webpack,老老实实手写 import 反而更稳。
收束
下次想往入口文件添第 16 行 import 时,把文件丢进 modules 目录就行,注册自动完成。完整实现见 src/store/modules/d2admin/index.js,一共 16 行。
【免费下载链接】d2-adminAn elegant dashboard项目地址: https://gitcode.com/gh_mirrors/d2/d2-admin
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考