一、背景
目前AI云平台很多前端项目启动和编译都比较缓慢,不少项目每次启动都要1分钟以上,每次修改代码保存编译后也需要好几十秒,
严重影响开发效率,而且有些子系统首次打开也比较缓慢,急需优化解决;
二、分析
启动和编译比较缓慢主要是因为脚手架过老,很多项目的前端脚手架都是四五年前的老版本,比如都还是webpack4版本的脚手架,
webpack每次启动会重新生成一下编译文件,这部分编译文件重复生成,也会造成启动缓慢,页面打开缓慢的主要原因是包的体积
比较大,没有进行压缩,删除无用的代码,也没有按需去加载;
三、解决方案
1、添加缓存
2、脚手架升级 webpack4 -> webpack5
3、开启摇树优化
4、代码压缩(开启gzip压缩)
四、webpack5对比webpack4的优点
- 更高的Node.js版本要求:相比Webpack 4,Webpack 5对Node.js的版本要求更高,通常建议使用10.13以上版本,这有助于移除大量历史遗留代码。
- 性能优化:Webpack 5引入了持久化缓存,可以更快地重新构建应用程序,提高开发效率。
- 更精确的代码分割:Webpack 5可以更精确地分割代码,有助于减少页面加载时间。
- WebAssembly和Asset Modules支持:Webpack 5支持使用WebAssembly和Asset Modules来处理文件,使得构建更加灵活。
- 现代化的资源模块类型:Webpack 5支持对ES modules和JSON modules进行构建,使得支持现代浏览器的特性更加容易。
- 移除的插件和功能:为了提高构建性能和可维护性,Webpack 5移除了一些过时的插件和功能,例如UglifyJS和CommonsChunkPlugin
五、具体配置 (vue.config.js)
js
const {defineConfig} = require ('@vue/cli-service');
const TerserPlugin = require ('terser-webpack-plugin');
const path = require ('path');
const webpack = require ('webpack');
const CompressionPlugin = require ('compression-webpack-plugin');
const CopyPlugin = require ('copy-webpack-plugin');
const BundleAnalyzerPlugin = require ('webpack-bundle-analyzer')
.BundleAnalyzerPlugin;
const resolve = dir => path.join (__dirname, dir);
// const packageName = require ('./package.json').name;
const isDev = process.env.NODE_ENV == 'local';
const SpeedMeasurePlugin = require ('speed-measure-webpack-plugin');
const WebpackBar = require ('webpackbar');
module.exports = defineConfig ({
productionSourceMap: isDev ? true : false, // 关闭生产环境的 source map
publicPath: '/deeplearn/',
// outputDir: 'deeplearn',
lintOnSave: false,
transpileDependencies: isDev ? false : true, //转译依赖
chainWebpack: config => {
if (!isDev) {
config.plugins.delete ('prefetch');
// 移除 preload 插件
config.plugins.delete ('preload');
}
config.plugin ('html').tap (args => {
args[0].title = '学习平台';
return args;
});
// 兼容部分IE和位置iview BUG
config.module
.rule ('view-design')
.test (/view-design.src.*?js$/)
.use ('babel-loader')
.loader ('babel-loader')
.end ()
.rule ('icons')
.test (/\.svg$/)
.include.add (resolve ('src/assets/svg'))
.end ()
.use ('svg-sprite-loader')
.loader ('svg-sprite-loader')
.options ({
symbolId: 'icon-[name]',
})
.end ();
config.module
.rule ('md')
.test (/\.md/)
.use ('vue-loader')
.loader ('vue-loader')
.end ()
.use ('vue-markdown-loader')
.loader ('vue-markdown-loader/lib/markdown-compiler')
.options ({raw: true});
config
.plugin ('speed-measure-webpack-plugin')
.use (SpeedMeasurePlugin)
.end ();
},
configureWebpack: config => {
if (!isDev) {
// config.entry = './src/main.js';
// config.output = {
// clean: true, // 在生成文件之前清空 output 目录
// compareBeforeEmit: false, // 当在磁盘中已经存在有相同内容的文件时,webpack 将不会写入输出文件。
// filename: '[name].[contenthash].bundle.js', // wenpack打包后的文件名
// chunkFilename: 'js/[name].[contenthash].bundle.js', // 异步加载的模块
// path: path.join (__dirname, 'deeplearn'),
// publicPath: isDev ? '/' : '/deeplearn/',
// qiankun接入配置
// library: `${packageName}-[name]`,
// libraryTarget: 'umd', // 把微应用打包成 umd 库格式
// chunkLoadingGlobal: `webpackJsonp_${packageName}`, //webpack5 output.jsonpFunction 更名为 output.chunkLoadingGlobal
// };
// config.plugins.push (new BundleAnalyzerPlugin ());
config.plugins.push (new WebpackBar ({name: 'PC', color: '#07c160'}));
config.plugins.push (
new CompressionPlugin ({
filename: '[path].gz[query]',
minRatio: 0.8, // 最小压缩比率,官方默认0.8
algorithm: 'gzip', // 默认压缩是gzip
test: /\.(js|css)$/, // 匹配文件名
threshold: 10240, // 对超过10k的数据压缩
deleteOriginalAssets: false, // 不删除源文件
})
);
}
config.plugins.push (
new webpack.ProvidePlugin ({
_: 'lodash',
introJs: ['intro.js'],
})
);
config.plugins.push (
new CopyPlugin ({
patterns: [{from: 'src/assets/static', to: 'static'}],
options: {
concurrency: 10,
},
})
);
config.resolve.alias =
// 设置路径别名,设置后需保持jsconfig.json内一致
{
'@': resolve ('src'),
_c: resolve ('src/components'),
};
config.resolve.extensions = ['.js', '.json', '.vue'];
let minimizeConfig = {
minimize: true,
minimizer: [
new TerserPlugin ({
parallel: 4,
cache: true,
sourceMap: false,
terserOptions: {
compress: {
drop_console: isDev ? false : true,
drop_debugger: isDev ? false : true,
},
output: {
comments: false,
},
},
}),
],
concatenateModules: false, // 公共代码整合,生产环境下被启用
};
config.optimization = {
// runtimeChunk: true,
usedExports: isDev ? false : true, //开启要数优化 tree shaking
sideEffects: false,
splitChunks: {
chunks: 'all',
minSize: 20000,
minRemainingSize: 0,
minChunks: 1,
maxAsyncRequests: 30,
maxInitialRequests: 30,
enforceSizeThreshold: 50000,
cacheGroups: {
//公用模块抽离
common: {
chunks: 'initial',
minSize: 0, //大于0个字节
minChunks: 2, //抽离公共代码时,这个代码块最小被引用的次数
},
//第三方库抽离
vendor: {
priority: 1, //权重
test: /node_modules/,
chunks: 'initial',
minSize: 0, //大于0个字节
minChunks: 2, //在分割之前,这个代码块最小应该被引用的次数
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true,
},
},
},
};
if (!isDev) {
config.optimization = Object.assign (config.optimization, minimizeConfig);
}
config.watchOptions = {
ignored: /node_modules/, //忽略node_modules包文件
aggregateTimeout: 600, //多次修改批量更新
poll: 1000, //每秒检查一次变动
};
config.devtool = isDev ? 'source-map' : false; //错误信息
config.cache = {
type: 'filesystem',
allowCollectingMemory: true,
cacheDirectory: path.resolve (__dirname, '.temp_cache'),
};
},
css: {
extract: isDev ? false : true,
sourceMap: isDev ? true : false, // 设置为 true 之后可能会影响构建的性能。
loaderOptions: {
less: {
javascriptEnabled: true,
},
},
},
pluginOptions: {
'style-resources-loader': {
preProcessor: 'less',
patterns: [
path.resolve (__dirname, './src/assets/it-uiue/css/theme.less'),
],
},
},
devServer: {
// compress: true, //影响启动速度 启用 gzip compression
port: 8080, //端口号
// hot: true,//热模块替换影响启动速度
// liveReload: true,//当监听到文件变化时 dev-server 将会重新加载或刷新页面
// open: true,
headers: {
'Access-Control-Allow-Origin': '*',
},
client: {
overlay: false,
},
// proxy: {
// '/proxyApi': {
// target: 'http://172.30.209.77:30016/',
// // target: 'http://10.1.197.99:8080/',
// // target: 'http://172.30.209.77:30016/',
// // pathRewrite: {
// // '^/proxyApi': ''
// // },
// // target: 'http://127.0.0.1:8080/pitaya/api/v1',
// changeOrigin: true,
// ws: true,
// },
// },
watchFiles: {
paths: ['src/**/*'],
options: {
usePolling: false,
},
},
},
});
六、效果对比
1、优化前构建速度
2、优化后构建速度
七、问题记录
1、webpack裸的配置升级为vue-cli5.0时,很多插件配置要带过来比如 CopyPlugin、和ProvidePlugin,以防项目出现引用,出现资源引用不到的问题;
2、v-show用法与内联display:flex会有冲突,导致v-show会失效,不能隐藏dom元素,改为通过class类名的方式就可以了;
3、iview组件库,Button默认的外边距会消失,需要额外加上;