文章目录
vue项目中,我们经常会引入路由模块,api模块,vuex模块,还有一些公用组件模块。
每次都手动输入模块和路径,自然难免心生厌恶,只是个名字不一样,每次都得重新写一堆东西。
于是自动化注册和自动化引入,就显得非常重要了。
但是,其实所谓的自动化注册,核心思路就是通过require.context()函数拿到文件夹结构,然后处理数据,生成我们需要的对象或数组,再注册上。
1、自动化导入api模块。
假设我们有一个apiModules文件夹,里面存放我们的各个板块的api地址,如果不使用自动化导出,那么一定是这样的结构,一条条的引入,然后统一导出。
使用自动化导入之后,则是这样的:
可复制代码如下:
// https://webpack.js.org/guides/dependency-management/#requirecontext
// require.context(publicPath, true, /\.js$/), 三个参数分别为:
// 其组件目录的相对路径,是否查询其子目录,匹配基础组件文件名的正则表达式
const modulesFiles = require.context('./apiModules', true, /\.js$/)
// you do not need `import app from './modules/app'`
// it will auto require all vuex module from modules file
const apiModules = modulesFiles.keys().reduce((modules, modulePath) => {
// set './app.js' => 'app'
const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')
const value = modulesFiles(modulePath)
modules[moduleName] = value.default
return modules
}, {})
// console.log('auto modules', apiModules)
export default apiModules
2、自动化导入注册vuex中的modules模块。
这个其实和上面的导入api的操作一样,就不放代码了,上图就行。
3、利用vuex的plugins,实现自动同步vuex的数据到本地存储,或者从本地存储中恢复数据到vuex中。
此操作可以避免vuex刷新数据丢失,也方便查看本地存储中的数据。
但是注意安全措施,也可以做个加密处理再使用。
/**
* 1、自动化恢复session中的数据到store,
* 2、自动同步vuex中的数据到session
* @param {*} store
*/
const sessionStoragePlugin = store => {
// 自动化恢复session中的数据到store
const sessionState = {}
Object.keys(window.sessionStorage).forEach(sessionName => {
const obj = {}
obj[sessionName] = JSON.parse(window.sessionStorage.getItem(sessionName))
Object.assign(sessionState, obj)
})
console.log('sessionState', sessionState)
store.replaceState(Object.assign({}, store.state, sessionState))
// 自动同步vuex中的数据到session
store.subscribe((mutation, state) => {
console.log('mutation', mutation)
console.log('state', state)
Object.keys(state).forEach((stateName, index) => {
window.sessionStorage.setItem(
stateName,
JSON.stringify(state[stateName])
)
})
console.log('mutation mutation', mutation)
console.log('state state', state)
})
}
export default [sessionStoragePlugin]
4.1、自动化注册路由。
如果是一些简单的路由结构,例如没有子层级children,那么同上一样,能够实现自动注册。这里就不赘述了。
但是实际情况却是,我们经常会遇到带有children的情况,而且还有动态路由的情况,所以这里我就不展开了,因为要考虑的情况太多 ,之前也尝试过写一些这方面的自动化,但是要考虑的东西太多。而且路由文件夹必须按照规格书写,限制太大。另外,非常的不灵活,如我们想针对某些特定路由添加一些meta信息什么的,那就哦豁了。
所以这里不建议路由使用自动化,而是按部就班的分模块引入,分模块管理,如果某一些模块的内容符合自动化注册的条件,再考虑针对该模块进行自动化。
这里只展示一下外面index的自动化模块管理,而具体到每个模块的路由实际上还需要手动书写。
—更新—————————————————————————————————————
4.2 自动化注册路由2,自动化注册子路由。
本来是不想这样做的,但是自己手动写相同的代码只是名称不一样,实在是难受。因此还是写了一个自动化注册子路由的简单示例,不涉及到继续嵌套子路由,所以也比较简单。
const VrView = () =>
import(
/* webpackChunkName: "VrView" */ '@/pages/vrView/vrView'
)
const modulesFiles = require.context('@/pages/vrView', true, /(?<!View)(\.vue$)/)
const childRoutes = modulesFiles.keys().reduce((modules, modulePath) => {
const value = modulesFiles(modulePath)
const childConponent = value.default
const path = modulePath.replace(/^\.\/(\w+)\.vue$/g, '$1')
const name = path.replace(/^./, path[0].toUpperCase())
const childRoute = {
path: path,
name: name,
component: childConponent
}
return [...modules, childRoute]
}, [])
// reimburse/xxx
export default [
{
path: '/vrView',
name: 'VrView',
component: VrView,
children: childRoutes
}
]
路由文件夹结构为:
这里剔除了xxxView.vue结尾的组件。
当然了,如果写到极致xxxView文件也可以省掉,以后再说吧。
但是这样还是有个问题,我记得这样处理的结果是非按需加载,还需要研究一下如何变成按需加载才行。
打包后一看,果然尼玛非按需加载的。
—更新————————————————————————————————————
4.3 自动化注册路由3,require.context实现自动化的同时,使用require.ensure完成按需加载和分割代码。
终于,还是找到了解决方案,不要直接使用default导出的模块来作为component,而是要使用require.ensure或者require来实现按需加载。
关键代码如下:
const VrView = () =>
import(
/* webpackChunkName: "VrView" */ '@/pages/vrView/vrView'
)
// const VrViewTest = () =>
// import(
// /* webpackChunkName: "VrViewTest" */ '@/pages/vrView/vrViewTest'
// )
const modulesFiles = require.context('@/pages/vrView', false, /(?<!View)(\.vue$)/)
const childRoutes = modulesFiles.keys().reduce((modules, modulePath) => {
const value = modulesFiles(modulePath)
const childConponent = value.default
const path = modulePath.replace(/^\.\/(\w+)\.vue$/g, '$1')
const name = path.replace(/^./, path[0].toUpperCase())
const aliasChildConponent = childConponent.__file.replace('src/', '')
const childRoute = {
path: path,
name: name,
// component: childConponent // 此种方式无法按需加载和分割代码。
// component: resolve => require([`@/${ aliasChildConponent }`], resolve) // 可以按需加载,但是无法命名chunk。
component: resolve => require.ensure([], () => resolve(require(`@/${ aliasChildConponent }`)), `vrView`)
}
return [...modules, childRoute]
}, [])
打包查看结果。
确实分包了。
但是查看分析图,发现业务代码是分包了,但是第三方库的分包机制却十分混乱,调试了很久依然无果。。。
5、自动化导出和注册基础模块。
require.context()函数非常好用,但是一定请不要滥用。
下例中,就是我实际遇到的项目中的情况。
也许当初写代码的人只是为了偷个懒,一次性导出所有的公用组件。
但是结果就是后续维护的人不断往里面添加组件,各种组件,管它公不公用,是否有必要统一导出,导致components文件夹非常庞大。
正常情况下,components文件夹大点也无所谓,其他页面需要就引入,问题不大。
但是这样写了自动导出之后,其他页面是这样来引入所需组件的。
明明只是为了使用其中一个按钮,结果引入了整个componens文件夹下的全部组件对象。
更恶心的是,当后续开发人员想知道这个组件的路径来源,对组件进行维护时,会一脸懵逼,因为他必须打开components文件夹,一个个的去找。
对了,还不能同名。
。。。
那么正确的做法是什么样的呢 ?
1、首先明确,只有非常基础的,使用频率很高的组件,才建议使用自动导出。例如可以在components文件夹下新建一个base文件夹,然后创建一个index.js来自动导出base文件夹中的所有基础组件。
2、针对这些自动导出的组件,可以进行全局注册,当然,还是建议慎用全局。