前言
在项目开发的过程中对项目做过一些优化,例如对项目结构、模块划分做了明确的定义。但是,项目打包后的体积却依然有78M
、编译需要30-60S
(电脑都要跑爆炸)、多个庞大模块混杂在一个项目当中。下面就来说说优化的点吧。。
常规优化
体积
从打包后的文件大小来看,图片等资源30+M
、js
部分30+M
,这些文件的大小实在是惊人,所以有了以下的操作:
1、 静态资源迁移oss
明明有oss
静态服务器,前端同学也没有用起来(匪夷所思),然后就把图片、字体等资源迁移到了oss
,然后删除了一部分没有在使用的资源(体力活,大概率不会有人来做了)
2、 webpack-bundle-analyzer
资源处理完了,使用webpack-bundle-analyzer
分析一下js
部分,大量插件被打包编译,占了相当大一部分体积,将部分大且不经常变动插件vue
、vuex
、element-ui
、vue-router
、moment
改用cdn
引入并配置externals
3、 关闭sourceMap
4、部分大型json类文件
从打包结果分析来看,一个js
文件有500+KB
,一看是关于地区的json
数据
这样处理完毕包大概还有个6+m
,没有处理的有echarts
、lodash
等,由于项目中有大量的按需引入,即使配置了externals
也没用,时间也不允许(毕竟抽空做一点是一点),即使如此包也已经缩小了十倍
编译时间
常规编译时间优化,基本都是配置webpack
排除对一些文件的编译,缓存编译、多进程编译等等,插件有hard-source-webpack-plugin
、babel-plugin-dynamic-import-node
、happypack
等等一系列插件,我也都做了尝试,有些许作用,但是我还是低估了项目的骨灰级程度,即使有了缓存、多进程也需要10+s
,但这基本就是常规的编译速度的优化,还有一些小小的建议
1、 避免在入口文件大量引入资源
2、 尽量少的注册全局状态 & 方法
3、 尽量封装、少些无用代码逻辑
按需编译
说完了常规的,一起了解一下按需编译
,项目开发有一些公共模块和无数个独立的依赖公共部分的模块
假设:
公共模块 = main.js + plugins + store + router + 组件
那么:
模块A = 公共模块 + aPlugins + aStore + aRouter + a组件
模块B = 公共模块 + bPlugins + bStore + bRouter + b组件
复制代码
如果开发A模块时可以只编译和A模块相关的内容,那编译速度将会非常的快,以我目前正在优化的项目,编译时间由40+s
缩短到了5s
,相当可观
但从描述上来讲按需编译可以看到多页面
、微前端
的影子,但是成本是非常高的、技术方案相对比较重,同时需要团队给予充分的时间、团队成员也需要一定的技术能力,今天在分享一下按需编译这个方案,目录结构如下
test
├── src
│ ├── main.js
│ ├── register.js
├── modules
│ ├── A
│ │ ├── store.js
│ │ ├── router.js
│ │ ├── index.js
│ ├── B
│ │ ├── store.js
│ │ ├── router.js
│ │ ├── index.js
复制代码
// A - index.js
import Router from './router';
import Store from './store'
export default {
router: Router,
store: Store
};
// B - index.js
import Router from './router';
import Store from './store'
export default {
router: Router,
store: Store
};
复制代码
指定编译模块
webpack-virtual-modules
可以帮助生产虚拟模块,然后根据环境变量通过require.context
动态引入需要的模块
// vue.config.js
// 借助webpack-virtual-modules生成虚拟模块
const VirtualModulesPlugin = require('webpack-virtual-modules');
// 编译模块
const modules = process.env.npm_config_module || '';
// 是否是本地模块编译
const isModule = modules && process.env.NODE_ENV === 'development'
// 返回模块虚拟
let buildModules = [];
if (isModule) {
buildModules = modules.split(',').map((module, index) => `require.context("../modules/${module}", false, /index\.js$/)`);
}
module.exports = {
configureWebpack: {
plugins: [
// 创建虚拟模块
new VirtualModulesPlugin({
'node_modules/dynamic-modules.js': `module.exports = [${buildModules.join(',')}];`
});
]
}
}
复制代码
动态添加信息
// test.js
export default (vue) => {
// 获取虚拟模块数据
let modules = require('dynamic-modules');
// 遍历
for (const curModule of modules) {
curModule.keys().map(key => {
// 解析module
const { router, store } = curModule(key).default;
Object.keys(store).map(key => {
// 动态添加store
vue.$store.registerModule(key, store[key]);
});
// 动态添加router
vue.$router.addRoutes(router);
});
}
}
// 在main.test.js
// mai.js
import test from './test'
const app = new Vue({ ... })
test(app)
复制代码
此时当我们执行npm run serve --module=A
时,只会引入A
模块相关的信息,B
模块则不会进行编译
这个方案更适合一些臃肿项目的优化,时间、人力、技术要求成本均相对较低
优点:成本低,改造快
缺点:针对本地开发
所以,有时间的话,还是进行一下微服务
的尝试
结语
神清气爽
参考:
Webpack externals
「vue模块化按需编译,突破编译瓶颈」实战篇
用 Feature First 的方式管理前端项目复杂度