webpack
搭建任意的前端架构环境(v4
、v5
都要会使用)webpack
工作流程、底层原理,以及与vite
、gulp
、rollup
等工具的差异与优劣webpack
构建优化技巧(运行速度、代码质量)webpack
可以对任意的文件进行编译打包。webpack
是运行到node
环境中的,支持CommonJS
模块化语法webpack
它是基于配置文件的(定制化特点强)v4
版本,最近新出了v5
版本webpack-cli
webpack
相关命令webpack
webpack
的核心代码包括webpack API
和核心插件所在的包。build
打包功能dist
目录,添加文件hash
值,开启编译进度条、开启代码压缩src
中的js
代码编译成浏览器能够普遍兼容的ES5
代码jsx
语法eslint
,实现对代码的检测CommonJs
模块module.exports={}
module.exports=function(){return{}}
module.exports=()=>{return {}}
module.exports=()=>({})
module.exports=()=>({
entry:{//入口配置(建议在配置入口文件时使用绝对路径)
app:path.resolve(__dirname,'../src/main.js')
},
output:{//出口配置
filename:'js/[]'
}
})
loader
都是一个函数JS
代码)module.exports=function(source){
//使用任何第三方js模版do something
//如果一个loader不用与webpack规则的最后一个loader,不要抛出js代码直接return 一个值就可以了
//不是最后一个loader
return result
//es6模块化
return `export default ${JSON.stringify(result)}`
//CommonJs
return `module.exports=${JSON.stringify(result)}`
}
plugin
本质上都是一个class
类prototype
上定义一个apply
方法
webpack
事件钩子webpack
内部实例数据webpack
提供的回调// 一个 JavaScript 类
class MyExampleWebpackPlugin {
// 在插件函数的 prototype 上定义一个 apply 方法,以 compiler 为参数。
apply(compiler) {
// 指定一个挂载到 webpack 自身的事件钩子。
compiler.hooks.emit.tap(
'MyExampleWebpackPlugin',
(compilation, callback) => {
console.log('这是一个示例插件!');
console.log(
'这里表示了资源的单次构建的 `compilation` 对象:',
compilation
);
// 用 webpack 提供的插件 API 处理构建过程
compilation.addModule(/* ... */);
callback();
}
);
}
}
开发环境优化标准:运行速度尽量快
devtool:inline-source-map
devtool
会减低运行速度关闭watch
监听代码依赖图的变化
v5
中使用hot:true
实现热更新开启memory
缓存
在使用loader
和plugin
时,使用exclude
、inculde
巧用resolve
属性
npm run link
时候,设置symlinks:false
提升解析速度使用thread-loader
使用cache-loader
使用speed-measure-webpack-plugin
webpack
构建速度生产环境优化标准:提高打包后代码的质量
devtool:source-map
bundle
生成.map
文件chunks
拆分
前端代码使用()=>import
动态导入技术
@babel/plugin-syntax-dynamic-import
optimization.splitChunks
或split-chunks-plugin
entry
入口中对多个chunk
中重复代码进行抽离entry:{
vendor:['react'],
app:{
dependOn:'vendor',
import:path.resolve(__dirname,'../foo/main.js')
}
}
bundle
分析技术
webpack-bundle-analyzer
对代码依赖图进行人工分析使用Tree Shaking
技术
src
中的“死代码”(未使用的代码)移除掉,以节省打包后代码的体积package.json
中添加sideEffects:false
,该功能只对mode:production
起作用抽离css
并压缩
mini-css-extract-plugin
抽离css
css-minimizer-webpack-plugin
terser
压缩
terser-webpack-plugin
集成terser
高性能压缩babel-loader
使用babel
加载es6
代码并将其转换为es5
html-loader
将HTML
导出为字符串,需要传入静态资源的引用路径markdown-loader
将markdown
编译为HTML
syle-loader
将模块导出的内容作为样式添加到DOM
中sass-loader
加载·并编译sass/scss
文件为css
文件vue-loader
编译vue
文件语法是CommonJS
语法
意义:协同开发保证代码规范和一致性
使用的插件配置eslint
v4
中,使用eslint-loader
v5
中,使用eslint-webpack-plugin
不同的项目,我们需要安装不同的ESLint
检测器
EsLint
的配置文件有六种,.eslintrc.js
优先级最高
解决ESLint
报错或警告的问题
ESLint
的规则名称并修改它的检测级别ESLint
注释包裹代码,忽略代码的检测webpack
配置文件将ESLint
注释掉
//eslint-disable-line
/*eslint-disable*//*eslint-enable*/
/*eslint-disable no-var*/ /* eslint-enable no-var*/
.eslintigore
文件(推荐)配置文件
module.exports={
//配置解析器
"parserOptions": {
"ecmaVersion": 6,//默认设置为3,5可以使用6,7,8,9也可以指定年号(设置ES版本)
"sourceType": "module",//设置代码类型,默认是“script”如果使用ESMmodule,设置为"module"
"ecmaFeatures": {//配额外的语言特性
"jsx": true,//使用"jsx"
"impliedStrict":true//启用全局strict mode(要求ecmaVersion是5或更高)
}
},
//默认使用esprima作为解析器,可以指定为其他解析器(1.必须为一个node模块2.必须符合parser interface)
"parser":"esprima",
"rules": {
"semi": "error"
},
"plugins":["a-plugin"],//配置插件
"processor":"a-plugin/a-processor"//指定配置处理器(从另一种文件提取或在预处理中转换为JavaScript代码)
,
"overrides":[//为特定类型的文件指定处理器
{
"files":["*.md"],
"processor":"a-plugin/markdown"
}
],
"env":{//配置预定义全局变量,环境
"browser":true,
"node":true
}
}
配置预设
javaScript
语法版本。预设不一定能够编译所有的小语法@babel/core
是babel
编译器的核心代码,最新版本v7
@babel/preset-env
是一个babel
预设,用于编译ES6+
语法@babel/preset-react
是一个babel
预设,用于编译jsx
语法@babel/preset-typescript
,是一个babel
预设,用于编译ts
语法配置插件
babel
的配置文件
babel.config.js
用于全项目范围的配置.babelrc.json
用于package
级别的配置配置文件
module.exports={
presets:[//配置预设
['@babel/preset-env',{}]
],
plugins:[//配置插件,用于弥补预设不能编译的小语法问题
["@babel/plugin-proposal-decorators",{"legacy":true}],
["@babel/plugin-proposal-class-properties",{}]
],
env:{//基于环境配置babel
development:{
plugins:[...]
},
production:{
plugins:[...]
}
}
}
Loader
和Plugin
的区别
loader
本质就是一个函数用于对对应的资源进行转换,因为webpack
只认识JavaScript
plugin
就是插件(具有apply
实例方法的类),是基于事件流的框架Tapable
的webpack
的功能,原理:在webpack
运行的生命周期中会广播出许多事件,plugin
可以监听这些事件,并通过webpack
提供的api
改变输出结果loader
在module.rules
中配置,plugin
在plugins
数组中单独配置,每一项都是plugin
的一个实例Webpack
构建流程
webpack
的运行流程是一个串行的过程,具体流程如下:
初始化参数
shell
语句中的参数得到最终参数开始编译
Compiler
对象run
方法开始执行编译确定入口
entry
找出所有的入口文件编译模块
loader
对模块进行翻译完成模块编译
loader
翻译后得到最终翻译内容,以及它们的依赖关系输出资源
chunk
文件chunk
文件加入到输出列表中输出完成
提高效率的插件
webpack-merge
:提取公共配置,减少重复配置代码webpack-dashboard
:可以更友好的展示相关打包信息size-plugin
:监视资源体积变化,尽早发现问题source map
source map
是将编译、打包、压缩后的代码 映射回源代码的过程,打包压缩后的代码不具有良好的可读性,调试源码就需要soucre map
map
文件只要不打开开发者工具,浏览器是不会加载的。hidden-source-map
:借助第三方错误监控平台Sentry
使用nosources-source-map
:只会显示具体行数以及查看源码的错误栈,安全性比sourcemap
高sourcemap
:通过nginx
设置将.map
文件只对白名单开发(公司内网)模块打包原理
Webpack
实际上为每个模块创造了一个可以导出和导入的环境,本质上并没有修改 代码的执行逻辑,代码执行顺序与模块加载顺序也完全一致。文件监听原理
当发现源码发生变化时,自动构建出新的输出文件
开启监听模式
webpack
命令时,带上--watch
参数webpack.config.js
中设置watch:true
缺点:每次需要手动刷新浏览器
原理:轮询判断文件的最后编辑时间是否变化,如果某个文件发生了变化,并不会立刻告诉监听者,而是先缓存起来,等 aggregateTimeout
后再执行。
module.export = { // 默认false,也就是不开启
watch: true,
// 只有开启监听模式时,watchOptions才有意义
watchOptions: {
// 默认为空,不监听的文件或者文件夹,支持正则匹配
ignored: /node_modules/,
// 监听到变化发生后会等300ms再去执行,默认300ms
aggregateTimeout:300,
// 判断文件是否发生变化是通过不停询问系统指定文件有没有变化实现的,默认每秒问1000次
poll:1000
}
}
热更新原理
HMR
,这个机制可以做到不用刷新浏览器就可以将旧模块更新为新模块webpack dev serve(WDS)
与浏览器之间维护了一个websocket
长连接
WDS
会向浏览器推送更新,并带上构建时的hash
,让客户端与上一次资源进行对比WDS
发起Ajax
请求来获取更改内容(文件列表、hash
),这样客户端再借助这些信息继续向WDS
发起jsonp
请求获取该chunk
的增量更新HotModulePlugin
来完成,提供了相关API
以供开发者根据自身场景进行处理。像react-hot-loader
和vue-loader
都是借助这些API
实现的HMR
bundle
体积进行监控和分析
VSCode
插件Import Cost
帮助我们对引入模块的大小进行实时监控webpack-bundle-analyzer
生成bundle
的模块组成图bundlesize
工具包可以进行自动化资源体积监控。文件指纹
文件指纹指的是打包后输出的文件名的后缀
hash
:只要项目有变化,整个项目的hash
值就会改变chunkhash
:和webpack
打包的chunk
有关,不同的entry
会出不同的chunkhash
contenthash
:根据文件内容来定义hash
,文件内容不变,则contenthash
不变CSS
的文件指纹
图片的文件指纹
保证各个loader
按照预想方式工作
优化 Webpack
的构建速度
代码分割
编写loader
编写Plugin
Babel
原理