w

e

b

p

a

c

k

优化背景

webpack优化背景

webpack优化背景

前段时间我很幸运地接了一个古老的项目,webpack的版本还停留在4.0的版本,本来没想优化的,但是由于每次启动需要6分钟,保存一下页面热启动也需要2分钟,直接把我整崩溃了,这种心情 一言难尽我开发了一周,每天大概浪费2个小时在等待页面启动和编译上,我真的崩溃了于是我决定使用一天的时间优化webpack配置开始了我的优化之旅

优化成果

第一次启动 10s热启动 5s 之内第一次打包 20s之内再次打包 10s之内

优化前提

仔细阅读webpack的开发文档注意 很多api是只有webpack5的版本才支持的

优化的最大痛点

webpack 的版本过低(4.0),很多优秀的api和属性甚至是打包编译思想都没有用到

突如其来的一道灵光,换壳,将vue-cli3.6的版本直接升级到vue-cli5.0

业务代码不变,改变项目的整体壳子和vue-clid等配置

可行性分析

业务代码影响分析

都是vue项目并且vue的版本都是用的是vue2.6 的版本,所以业务代码是没有什么风险点的,唯一要改的是配置

配置代码分析

由于vue-cli3.6升级到vue-cli5.0之后,webpack 的优秀属性就都可以使用了

升级vue-cli步骤

使用vue-cli5脚手架搭建一个空壳项目

使用vue-cli5 搭建一个vue的基础项目

替换全局代码

替换业务代码

将src文件夹全部替换将public 文件夹替换

package.json 修改

将vue-cli3.6老项目中的全局配置项和vue-cli5新壳子中的配置项目对比,把vue-cli5中出现过的依赖项直接删除,使用vue-cli5默认的将vue-cli3.6老项目中出现的项目依赖项移捞出来放到新壳子中去删除代码中没有引用的依赖项

由于老项目中的依赖项可能是其他项目搬过来的,所以必定会出现没有用到的依赖项在代码中src文件夹下全局搜索,判断插件是否有使用到,没有用到的直接删除

.babelrc 文件修改

由于vue-cli3.6和vue-cli5.0的babel处理方式不太一样,所以需要修改老的代码就不放了

vue-cli5的.babelrc代码

{

"presets": [

"@vue/cli-plugin-babel/preset"

],

"plugins": [

"equire",

[

"import",

{

"libraryName": "view-design",

"libraryDirectory": "src/components"

}

]

]

}

修改vue.config.js

壳子已经替换完毕,现在开始webpack 的配置

配置 vue.config.js

定义环境变量

const isDev = process.env.NODE_ENV == 'development'

transpileDependencies 关闭

关闭之后,能够提编译速度

transpileDependencies: isDev ? false : true,//转译依赖

默认情况下 babel-loader 会忽略所有 node_modules 中的文件。你可以启用本选项,以避免构建后的代码中出现未转译的第三方依赖。

开启 terser-webpack-plugin 代码压缩

开启参数

minimize 开启压缩

TerserPlugin 配置

parallel 最大并行进程cache 是否开启缓存sourceMap 是否开启 sourceMapterserOptions

compress 压缩选项

compress.drop_console 是否删除console 生产环境删除,开发环境保留

compress.drop_debugger 是否删除debugger 生产环境删除,开发环境保留

output.comments 是否删除comments 生产环境删除,,开发环境保留

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

}

}

}

}

watchOptions 忽略node_modules

config.watchOptions = {

ignored: /node_modules/,//忽略node_modules包文件

aggregateTimeout: 600,//多次修改批量更新

poll: 1000//每秒检查一次变动

}

devtool配置

config.devtool = isDev ? 'source-map' : false//错误信息

开发环境

启用sourcemapdevtool 设置值为 source-map

生产环境

关闭sourcemap注意设置为false,否则无法彻底关闭sourcemap

开启cache缓存

缓存是前端优化的常规手段,webpack 中同样可以缓存分内存和磁盘文件缓存,此处是使用文件磁盘缓存

type 使用 filesystem

allowCollectingMemory 收集在反序列化期间分配的未使用的内存,仅当 cache.type 设置为 ‘filesystem’ 时生效。这需要将数据复制到更小的缓冲区中,并有性能成本。 cacheDirectory 文件缓存的目录

指定为档期目录下的 .temp_cache 文件夹下

它下面分development 和 production 文件夹

config.cache = {

type: 'filesystem',

allowCollectingMemory: true,

cacheDirectory: path.resolve(__dirname, '.temp_cache'),

}

注意 建议定期删除 temp_cache 文件夹,以免占用过多磁盘空间

小伙伴担忧

是否会卡顿

缓存太多是否会导致,内存爆掉甚至于卡顿我在此明确地告诉你,不会,原因很简单

这个缓存是基于磁盘的,不是内存

建议一周删除一次缓存文件

output 配置

config.output = {

clean: true, // 在生成文件之前清空 output 目录

compareBeforeEmit: false,// 当在磁盘中已经存在有相同内容的文件时,webpack 将不会写入输出文件。

filename: '[name].[contenthash].bundle.js',// wenpack打包后的文件名

chunkFilename: 'js/[name].[contenthash].bundle.js',// 异步加载的模块

path: path.join(__dirname, 'testProject'),

publicPath: isDev ? '/' : '/testProject/',

}

备注 chainWebpack 和 configureWebpack 区别

官方介绍

chainWebpack 是一个函数,会接收一个基于 webpack-chain 的 ChainableConfig 实例。允许对内部的 webpack 配置进行更细粒度的修改。configureWebpack 如果这个值是一个对象,则会通过 webpack-merge 合并到最终的配置中

通俗区别

chainWebpack用于修改,会和默认配置项合并,追加某个属性值configureWebpack用于合并,向原有配置项追加配置,直接添加整个配置项所以,只修改某个属性使用 chainWebpack,添加配置项使用configureWebpack

webpack辅助工具

可帮助我我们更好的优化代码

webpack-bundle-analyzer

代码分析工具,可分析打包之后的文件

speed-measure-webpack-plugin

打包的速度分析,和时间分析插件

config.plugins.push(new BundleAnalyzerPlugin())

config.plugins.push(new WebpackBar({ name: 'PC', color: '#07c160' }))

const {defineConfig} = require('@vue/cli-service')

const TerserPlugin = require('terser-webpack-plugin');

const path = require('path')

const webpack = require('webpack');

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 == 'development'

const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");

const WebpackBar = require('webpackbar');

module.exports = defineConfig({

// productionSourceMap: false, // 关闭生产环境的 source map

publicPath: isDev ? '/' : '/testProject/',

outputDir: 'testProject',

lintOnSave: false,

transpileDependencies: isDev ? false : true,//转译依赖

chainWebpack: config => {

config.plugin('speed-measure-webpack-plugin').use(SpeedMeasurePlugin).end();

if (!isDev) {

config.plugins.delete('prefetch');

// 移除 preload 插件

config.plugins.delete('preload');

}

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, 'testProject'),

publicPath: isDev ? '/' : '/testProject/',

// 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.resolve.alias =

// 设置路径别名,设置后需保持jsconfig.json内一致

{

'@': resolve('src'),

'_c': resolve('src/components')

}

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)

console.log(config.module)

}

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'),

}

}

})

优化成果

初次 npm run dev 运行速度

所有的缓存文件都清除,运行时间大概在36秒

基于缓存 npm run dev 运行速度

只需要 4.7秒

初次 npm run build 打包速度

基于缓存 npm run build 打包速度

9.64秒

项目体量

src文件数量和大小

770个文件,10MB

项目依赖大小

781MB

打包后文件 大小

仅有 9.8MB

个人总结

webpack 本质上也是js,我们不会配置,可能只是不太熟悉,不要有恐惧心理先看仔细阅读文档,重点看他的优化方案,整合下即可结合到项目中

致谢

感谢webpack官方文档提供的文档说明感谢我的项目组给了我挑战自己的机会感谢我的导师给予我的帮助

感谢您百忙之中抽时间阅读我写的博客,谢谢您的肯定,也希望对您能有所帮助如果您有更好的见解请在评论区留言或者私聊我,期待与您的交流

精彩链接

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。