使用 Webpack 构建 Vue 项目时,借助 vue-loader 和 vue-hot-reload-api,我们在开发的时候可以获得很好的组件热加载(Hot Module Replacement)体验。然而 vue-loader 中却没有关于 vuex 的配置(当然这也的确不是它应该插手的地方)。
官方 vue-cli 的 vuex 插件也没有相应支持(在 cli serve 下如果更改 store 或是其依赖的模块,页面会自动刷新,这个行为是 hot reload 而不是 HMR)。
Nuxt.js 框架秉承着 convention over configuration 的思想,在一定的目录和文件结构约定下,通过目录分析和脚手架文件模板,很好地解决了 HMR 的问题,生成 store
入口模块的相关代码在这里。
在不使用 Nuxt 的情况下,我们也可以通过在项目中保持一定的模块规范来简单实现 Vuex HMR 的配置。
Vuex 的 API
Vuex 自身是提供了 hotUpdate api 以及 一个 HMR 的代码示例 的。
|
这个例子稍显简单,需要手动指定每一个 submodule 的路径。
解决方案
假设项目中 vuex 相关文件的目录结构如下。
|
其中省略了上例中的 mutations
文件,将全局根模块的内容都写在 store/index.js
中。 modules
文件夹里存放模块的定义内容。
那我们就可以使用 require.context 来动态得出依赖的模块列表。
首先 sub.js
和 complex/index.js
需要服从一些我们预设的规则:
- vuex module 定义文件都使用
export default
导出 - 如果是
namespaced
模块,需要通过export const VUEX_NS
或者在 vuex module 定义中添加一个namespace: string
字段来导出命名空间名。
例如 sub.js 文件内容:
|
在 store/index.js 中的例子如下:
|
样例说明
在以上的目录结构下,moduleFileKeys
的结果为:
|
这其中任一个文件发生变化,都会在 module 更新结束后,进入给 module.hot.accept
函数传入的回调函数 (deps) => {...}
中,执行我们自定义的更新逻辑。
__webpack_require__
是一个 webpack module 作用域内特有的函数,文档在此。P.S. 这里使用它,而不是 require(moduleId)
,我们都知道源文件中的 require
语句会被 webpack 分析并在生成目标代码时改写,但若入参不是字符串,不能被静态分析出具体的模块,在生成的 bundle 里会被 webpack 转为 __webpack_require__("./src/store sync recursive")(moduleId)
,在我的测试中,__webpack_require__("./src/store sync recursive")
这句的结果是一个 webpackEmptyContext
,调用它会抛出 MODULE_NOT_FOUND 的异常。
接下来关于 moduleDef 判断的代码建立在之前说的预设规则上,可以根据项目实际修改。
P.S. 目前这个方案没有解决两层及以上深度的 module 情况,实际使用中这个似乎也不常见。