项目很大,得忍一下
背景
常和我们的客户端厮混,也经常陪他们发版,每次发版编译打包都可以在那边玩一局游戏了。一边幸灾乐祸,一边庆幸h5编译还好挺快的,直到我们的项目也发展成了*山,巨石项目。由于线上要给用户查看历史的推广活动,所以很多老的业务项目都还是留在项目中,导致我们的router层爆炸,打包速度直线下降,开发过程中,开了hmr稍微有点改动也要等个几秒钟,恨不得立刻重启一个新项目。但是现实告诉你,忍住,别吐,后面还有更多的业务活动加进来。那么怎么解决这个问题呢,这个时候mp的思路是个不错的选择。
关键点
打包慢,本质原因是依赖庞大,组件过多。开发过程中,我们开新的业务组件时,往往和其他业务组件是隔离的,那么我们打包的时候是不是可以把那些不相干的业务组件隔离出去,当然可以。打包工具,从入口开始进行扫描,单页面的模块引入基本都是借助router,所以,关键的是如果我们能够控制router的数量,其实就能够控制编译和打包规模了。
问题
router在vue项目中我们常用的是全家桶的库vue-router,vue-router最多提供了懒加载,动态引入功能并不支持。有小伙伴说router的引入路径可不可以动态传入,我只能说小伙子你很机智,但是vue-router并不支持动态的引入路径。因此我们换个思路,就是在入口的位置控制router的规模,通过不同规模的router实例来实现router的动态引入。当然这需要我们对router库进行一定改造,使其变的灵活易用
一般的router
通常的router如下:
// router.js
/*global require*/
const Vue = require('vue')
const Router = require('vue-router')
Vue.use(Router)
const routes = [
{
path: '/routermap',
component: (resolve) => require(['../containers/map.vue'], resolve),
name: 'routermap',
desc: '路由列表'
},
{
path: '/',
component: (resolve) => require(['../containers/index.vue'], resolve)
},
{
path: '*',
component: (resolve) => require(['../containers/nofound.vue'], resolve),
name: 'defaultPage',
desc: '默认页'
}
]
const router = new Router({
mode: 'history',
routes
})
router.afterEach((to, from) => {
///
})
export default router
// 引入 entry.js
import router from './router.js'
router.beforeEach((to, from, next) => {
///
next()
})
router.afterEach(function(to, from) {
///
})
new Vue({
el: '#app',
template: '<App/>',
router,
})
我们可以不断的往routes数组中添加新的router item来添加新的业务组件,这也是我们的项目不断变大的根本,这样既不好维护,也会导致后面的编译效率
易于维护和管理的router
其实好的管理和维护本质就是分门别类,把类似功能的放在一起,而不是一锅粥都放在一起,这样基本就能解决追踪维护的功能,对应router管理其实也不是很复杂,多建几个文件夹就行如下:
对应routes/index.js代码如下:
import testRouter from './test.js'
const routes = [
{
path: '/map',
component: (resolve) => require(['../containers/map.vue'], resolve),
name: 'map',
desc: '路由列表'
},
{
path: '/',
component: (resolve) => require(['../containers/index.vue'], resolve)
},
...testRouter,
// 可以扩展其他router
{
path: '*',
component: (resolve) => require(['../containers/nofound.vue'], resolve),
name: 'defaultPage',
desc: '默认页'
}
]
// test.js
/**
* 测试相关页面路由映射
*/
/*global require*/
export default [
{
path: '/test/tools',
name: 'testTools',
component: resolve => require(['@test/tools/index.vue'], resolve),
desc: '测试工具'
}
]
我们通过把router分为几个类别的js,然后在通过router item的数组展开合并,就做到了分门别类,虽然看似简单,但是可以把管理和维护效果提升几个数量级。
支持mp的router
虽然上面支持了易于管理和维护,但是实际上我们如果只是单一入口的话,导出的还是一个巨大的router。那么如何支持多入口呢,其实也不用想的过于复杂,我们让类似test.js的router文件既支持router item的数组导出,也支持类似routes/index.js一样的router实例导出即可。所谓既能分也能合才是最灵活的,这里我们可以利用工厂模式做一个factory.js,如下:
/**
* app 内的页面路由映射
*/
/*global require*/
const Vue = require('vue')
const Router = require('vue-router')
Vue.use(Router)
const RouterFactory = (routes) => {
return new Router({
mode: 'history',
routes: [
{
path: '/map',
component: (resolve) => require(['../containers/map.vue'], resolve),
name: 'map',
desc: '路由列表'
},
{
path: '/',
component: (resolve) => require(['../containers/index.vue'], resolve)
},
...routes,
{
path: '*',
component: (resolve) => require(['../containers/nofound.vue'], resolve),
name: 'defaultPage',
desc: '默认页'
}
]
})
}
export default RouterFactory
这个factory.js产出的router实例和routes/index.js一模一样所以我们只需组装一下test.js即可,如下:
/*global require*/
import RouterFactory from './factory'
export const testRouter = [
{
path: '/test/tools',
name: 'testTools',
component: resolve => require(['@test/tools/index.vue'], resolve),
desc: '测试工具'
}
]
export default RouterFactory(developRouter)
// routes/index.js的引入变化一下即可
import testRouter from './test.js'
// 修改为=》
import { testRouter } from './test.js'
那么我们的入口该如何修改呢?也很简单:
// testEntry.js
import router from './routes/test.js'
router.beforeEach((to, from, next) => {
///
next()
})
router.afterEach(function(to, from) {
///
})
new Vue({
el: '#app',
template: '<App/>',
router,
})
我们建立了一个新的入口文件 testEntry.js 这个入口只引入了test相关的模块组件
如何灵活的和编译命令做配合呢
根据上面,我们进行mp改造的基础已经做好,关于如何多入口编译webpack或者其他打包里面都是基础知识,这里就不多赘述。这里主要聊一下如何灵活的配合命令做编译和部署。
既然router我们都可以分为不同的文件,编译文件我们同样可以拆分为不同的文件,这也使得我们的命令可以灵活多变,这里我们以webpack做为示例:
根据上图示例 我们的webpack的配置文件仅仅改动了entry,我们稍微改造一下build.js,使其能够接受不同的编译命令:
// build.js
let page = 'all'
if (process.argv[2]) {
page = process.argv[2]
}
let configMap = {
'all': require('./webpack.prod.conf'),
'app': require('./webpack.app.conf')
}
let webpackConfig = configMap[page]
// dev-server.js
let page = 'all'
if (process.argv[2]) {
page = process.argv[2]
}
let configMap = {
'all': require('./webpack.dev.conf'),
'app': require('./webpack.app.dev.conf')
}
let webpackConfig = configMap[page]
对应的脚本配置:
// package.json
"scripts": {
"dev": "node build/dev-server.js",
"build": "node build/build.js",
"build:app": "node build/build.js app",
"dev:app": "node build/dev-server.js app"
},
以上app对应test。最后,我们只需要在命令行执行相应命令,即可实现我们可控的router规模的开发,基本随便来新的需求,咱都可以夜夜做新郎,怎么搞都是飞速。当然部署的话我们也可以单独执行一部分页面的部署命令到单独的域名,换个思路也可以作为一种预发测试的部署方法。
# 整体项目的开发编译
npm run dev
# 单独的app,也即test项目的开发编译
npm run dev:app
# 整体项目的部署
npm run build
# 单独的app,也即test项目的部署
npm run build:app
结语
以上,即如何利用mp思路,提高我们的编译开发效率。时常有人会在提高网页性能的时候说到mp,但mp本质上并不能提高页面的性能,比如白屏优化。而路由中使用懒加载其实才是提高部分网页性能的出力者,关于白屏优化,本篇文章不作展开讨论。
来源:juejin.cn/post/7218866717739696183