【前端工程化】webpack5+vue3+ts+代码规范构建企业级前端项目(三)
目录
- 前言
- 构建耗时分析
- 开启持久化存储缓存
- 开启多线程loader
- 配置alias别名
- 缩小loader作用范围
- 精确使用loader
- 缩小模块搜索范围
- devtool模式配
- 其他优化配置
- 总结
1. 前言
从2020年10月10日,webpack 升级至 5 版本到现在已经快三年,webpack5版本优化了很多原有的功能比如tree-shakin优化,也新增了很多新特性,具体变动可以看这篇文章阔别两年,webpack 5 正式发布了!。
本文是专栏【前端工程化】webpack5+vue3+ts+代码规范构建企业级前端项目系列第三篇,会详细讲解优化构建速度:构建耗时分析,持久化缓存,多线程loader,devtool,loader作用范围等优化配置。
本系列文章将使用最新的webpack5一步一步从零搭建一个完整的vue3+ts开发和打包环境,配置完善构建速度和构建结果的优化配置,以及配置完善的代码规范和git提交规范。完整代码已上传到webpack5-vue3-ts。
全系列概览
全系列文章:
- 《基础功能配置:webpack5配置vue3+ts基础环境,实现dev开发和打包构建》。
- 《进阶功能配置:环境变量,支持css,less,图片和媒体资源,css3前缀,babel兼容》。
- 《优化构建速度:构建耗时分析,持久化缓存,多线程loader,devtool,loader作用范围》。
- 优化化构建结果:构建结果分析,抽离css文件,压缩css,js文件,hash合理配置,代码分割,tree-shaking清理css和js,打包生成gzip。
- 代码格式规范:editorconfig统一编辑器配置,prettier自动格式化代码,stylelint规范样式和保存自动修复,代码提交自动格式化css和js代码等。
- 代码语法规范:eslint检测js代码语法,style-lint检测样式代码语法,使用tsc检测类型报错,lint-staged按需检测代码等。
- git提交规范:代码提交时husky检测代码语法规范,代码提交时husky检测commit备注规范,commitizen配置commit辅助信息等。
2. 构建耗时分析
当进行优化的时候,肯定要先知道时间都花费在哪些步骤上了,而speed-measure-webpack-plugin插件可以帮我们做到,安装依赖:
npm i speed-measure-webpack-plugin -D
使用的时候为了不影响到正常的开发/打包模式,我们选择新建一个配置文件,新增webpack构建分析配置文件build/webpack.analy.js
// build/webpack.analy.js
const prodConfig = require('./webpack.prod.js') // 引入打包配置
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin'); // 引入webpack打包速度分析插件
const smp = new SpeedMeasurePlugin(); // 实例化分析插件
const { merge } = require('webpack-merge') // 引入合并webpack配置方法
// 使用smp.wrap方法,把生产环境配置传进去,由于后面可能会加分析配置,所以先留出合并空位
module.exports = smp.wrap(merge(prodConfig, {
}))
修改package.json添加启动webpack打包分析脚本命令,在scripts新增:
{
// ...
"scripts": {
// ...
"build:analy": "cross-env NODE_ENV=production BASE_ENV=production webpack -c build/webpack.analy.js"
}
// ...
}
执行npm run build:analy命令
可以在图中看到各plugin和loader的耗时时间,现在因为项目内容比较少,所以耗时都比较少,在真正的项目中可以通过这个来分析打包时间花费在什么地方,然后来针对性的优化。
speed-measure-webpack-plugin插件在webpack5+vue3中使用会报错,相关issues,但不影响看实际效果。
3. 开启持久化存储缓存
在webpack5之前做缓存是使用babel-loader缓存解决js的解析结果,cache-loader缓存css等资源的解析结果,还有模块缓存插件hard-source-webpack-plugin,配置好缓存后第二次打包,通过对文件做哈希对比来验证文件前后是否一致,如果一致则采用上一次的缓存,可以极大地节省时间。
webpack5 较于 webpack4,新增了持久化缓存、改进缓存算法等优化,通过配置 webpack 持久化缓存,来缓存生成的 webpack 模块和 chunk,改善下一次打包的构建速度,可提速 90% 左右,配置也简单,修改build/webpack.base.js
// build/webpack.base.js
// ...
module.exports = {
// ...
cache: {
type: 'filesystem', // 使用文件缓存
},
}
当前文章代码的测试结果
模式 | 第一次耗时 | 第二次耗时 |
---|---|---|
启动开发模式 | 2869毫秒 | 687毫秒 |
启动打包模式 | 5455毫秒 | 552毫秒 |
通过开启webpack5持久化存储缓存,再次打包的时间提升了90%。
缓存的存储位置在node_modules/.cache/webpack,里面又区分了development和production缓存:
4. 开启多线程loader
webpack的loader默认在单线程执行,现代电脑一般都有多核cpu,可以借助多核cpu开启多线程loader解析,可以极大地提升loader解析的速度,thread-loader就是用来开启多进程解析loader的,安装依赖
npm i thread-loader -D
使用时,需将此 loader 放置在其他 loader 之前。放置在此 loader 之后的 loader 会在一个独立的 worker 池中运行。
修改build/webpack.base.js
// build/webpack.base.js
module.exports = {
// ...
module: {
rules: [
{
test: /\.vue/,
use: ['thread-loader', 'vue-loader']
},
{
test: /\.ts$/,
use: ['thread-loader', 'babel-loader']
}
]
}
}
由于thread-loader不支持抽离css插件MiniCssExtractPlugin.loader(下面会讲),所以这里只配置了多进程解析js,开启多线程也是需要启动时间,大约500ms左右,所以适合规模比较大的项目。
5. 配置alias别名
webpack支持设置别名alias,设置别名可以让后续引用的地方减少路径的复杂度。
修改build/webpack.base.js
module.export = {
// ...
resolve: {
// ...
alias: {
'@': path.join(__dirname, '../src')
}
}
}
修改tsconfig.json,添加baseUrl和paths
{
"compilerOptions": {
// ...
"baseUrl": ".",
"paths": {
"@/*": [
"src/*"
]
}
}
}
配置修改完成后,在项目中使用 @/xxx.xx,就会指向项目中src/xxx.xx,在js/ts文件和vue文件中都可以用。
src/App.vue可以修改为
<template>
<img :src="smallImg" alt="小于10kb的图片" />
<img :src="bigImg" alt="大于于10kb的图片" />
<!-- 小图片背景容器 -->
<div className='smallImg'></div>
<!-- 大图片背景容器 -->
<div className='bigImg'></div>
</template>
<script setup lang="ts">
import smallImg from '@/assets/imgs/5kb.png'
import bigImg from '@/assets/imgs/22kb.png'
import '@/app.css'
import '@/app.less'
</script>
<style scoped>
</style>
src/app.less可以修改为
// app.less
#root {
.smallImg {
width: 69px;
height: 75px;
background: url('@/assets/imgs/5kb.png') no-repeat;
}
.bigImg {
width: 232px;
height: 154px;
background: url('@/assets/imgs/22kb.png') no-repeat;
}
}
6. 缩小loader作用范围
一般第三库都是已经处理好的,不需要再次使用loader去解析,可以按照实际情况合理配置loader的作用范围,来减少不必要的loader解析,节省时间,通过使用 include和exclude 两个配置项,可以实现这个功能,常见的例如:
- include:只解析该选项配置的模块
- exclude:不解该选项配置的模块,优先级更高
修改build/webpack.base.js
// build/webpack.base.js
const path = require('path')
module.exports = {
// ...
module: {
rules: [
{
include: [path.resolve(__dirname, '../src')], 只对项目src文件的vue进行loader解析
test: /\.vue$/,
use: ['thread-loader', 'vue-loader']
},
{
include: [path.resolve(__dirname, '../src')], 只对项目src文件的ts进行loader解析
test: /\.ts/,
use: ['thread-loader', 'babel-loader']
},
]
}
}
其他loader也是相同的配置方式,如果除src文件外也还有需要解析的,就把对应的目录地址加上就可以了,比如需要引入antd的css,可以把antd的文件目录路径添加解析css规则到include里面。
7. 精确使用loader
loader在webpack构建过程中使用的位置是在webpack构建模块依赖关系引入新文件时,会根据文件后缀来倒序遍历rules数组,如果文件后缀和test正则匹配到了,就会使用该rule中配置的loader依次对文件源代码进行处理,最终拿到处理后的sourceCode结果,可以通过避免使用无用的loader解析来提升构建速度,比如使用less-loader解析css文件。
可以拆分上面配置的less和css, 避免让less-loader再去解析css文件
// build/webpack.base.js
// ...
module.exports = {
module: {
// ...
rules: [
// ...
{
test: /\.css$/, //匹配所有的 css 文件
include: [path.resolve(__dirname, '../src')],
use: [
'style-loader',
'css-loader',
'postcss-loader'
]
},
{
test: /\.less$/, //匹配所有的 less 文件
include: [path.resolve(__dirname, '../src')],
use: [
'style-loader',
'css-loader',
'postcss-loader',
'less-loader'
]
},
]
}
}
8. 缩小模块搜索范围
node里面模块有三种
- node核心模块
- node_modules模块
- 自定义文件模块
使用require和import引入模块时如果有准确的相对或者绝对路径,就会去按路径查询,如果引入的模块没有路径,会优先查询node核心模块,如果没有找到会去当前目录下node_modules中寻找,如果没有找到会查从父级文件夹查找node_modules,一直查到系统node全局模块。
这样会有两个问题,一个是当前项目没有安装某个依赖,但是上一级目录下node_modules或者全局模块有安装,就也会引入成功,但是部署到服务器时可能就会找不到造成报错,另一个问题就是一级一级查询比较消耗时间。可以告诉webpack搜索目录范围,来规避这两个问题。
修改build/webpack.base.js
// build/webpack.base.js
const path = require('path')
module.exports = {
// ...
resolve: {
// ...
// 如果用的是pnpm 就暂时不要配置这个,会有幽灵依赖的问题,访问不到很多模块。
// 查找第三方模块只在本项目的node_modules中查找
modules: [path.resolve(__dirname, '../node_modules')],
},
}
9. devtool配置
开发过程中或者打包后的代码都是webpack处理后的代码,如果进行调试肯定希望看到源代码,而不是编译后的代码, source map就是用来做源码映射的,不同的映射模式会明显影响到构建和重新构建的速度, devtool选项就是webpack提供的选择源码映射方式的配置。
devtool的命名规则为 ^(inline-|hidden-|eval-)?(nosources-)?(cheap-(module-)?)?source-map$
关键字 | 描述 |
---|---|
inline | 代码内通过 dataUrl 形式引入 SourceMap |
hidden | 生成 SourceMap 文件,但不使用 |
eval | eval(...) 形式执行代码,通过 dataUrl 形式引入 SourceMap |
nosources | 不生成 SourceMap |
cheap | 只需要定位到行信息,不需要列信息 |
module | 展示源代码中的错误位置 |
开发环境推荐:eval-cheap-module-source-map
- 本地开发首次打包慢点没关系,因为 eval 缓存的原因, 热更新会很快
- 开发中,我们每行代码不会写的太长,只需要定位到行就行,所以加上 cheap
- 我们希望能够找到源代码的错误,而不是打包后的,所以需要加上 module
修改build/webpack.dev.js
// build/webpack.dev.js
module.exports = {
// ...
devtool: 'eval-cheap-module-source-map'
}
打包环境推荐:none(就是不配置devtool选项了,不是配置devtool: 'none')
// build/webpack.prod.js
module.exports = {
// ...
// devtool: '', // 不用配置devtool此项
}
- none话调试只能看到编译后的代码,也不会泄露源代码,打包速度也会比较快。
- 只是不方便线上排查问题, 但一般都可以根据报错信息在本地环境很快找出问题所在。
- 如果想排查线上问题时可以映射源码,推荐用cheap-module-source-map,只展示行信息,单独存在map文件,可以在编译速度和排查问题上做一个很好的均衡。
10. 其他优化配置
除了上面的配置外,webpack还提供了其他的一些优化方式,本次搭建没有使用到,所以只简单罗列下:
- externals: 外包拓展,打包时会忽略配置的依赖,会从上下文中寻找对应变量。
- module.noParse: 匹配到设置的模块,将不进行依赖解析,适合jquery,boostrap这类不依赖外部模块的包
- ignorePlugin: 可以使用正则忽略一部分文件,常在使用多语言的包时可以把非中文语言包过滤掉
总结
到这里一些项目优化构建速度就已经配置好了,下一篇会讲解优化构建结果的配置,通过一系列配置来优化项目构建结果,通过配置来减少体积大小,提升代码质量,提高整体的项目体验。
文章系列:
- 《基础功能配置:webpack5配置vue3+ts基础环境,实现dev开发和打包构建》。
- 《进阶功能配置:环境变量,支持css,less,图片和媒体资源,css3前缀,babel兼容》。
- 《优化构建速度:构建耗时分析,持久化缓存,多线程loader,devtool,loader作用范围》。
- 优化化构建结果:构建结果分析,抽离css文件,压缩css,js文件,hash合理配置,代码分割,tree-shaking清理css和js,打包生成gzip。
- 代码格式规范:editorconfig统一编辑器配置,prettier自动格式化代码,stylelint规范样式和保存自动修复,代码提交自动格式化css和js代码等。
- 代码语法规范:eslint检测js代码语法,style-lint检测样式代码语法,使用tsc检测类型报错,lint-staged按需检测代码等。
- git提交规范:代码提交时husky检测代码语法规范,代码提交时husky检测commit备注规范,commitizen配置commit辅助信息等。
参考
转载自:https://juejin.cn/post/7248242792283029560