如何用一行 require.context,让 d2-admin 自动注册 15 个 Vuex 状态模块

发布时间:2026/11/3 2:33:03
如何用一行 require.context,让 d2-admin 自动注册 15 个 Vuex 状态模块

如何用一行 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),仅供参考