webpack是目前用的最多的一款模块加载器兼打包工具。
Webpack 本身只能处理 JavaScript 模块,如果要处理其他类型的文件,就需要使用 loader 进行转换。
Loader可以理解为是模块和资源的转换器,它本身是一个函数,接受源文件作为参数,返回转换的结果。
module.loaders
告知 webpack 每一种文件都需要使用什么加载器来处理。
webpack常用配置
entry
:打包入口文件,可以是单个或者多个JS文件,决定了webpack从哪个模块开始生成依赖关系图;output
:输出的目录和文件名,包括path、filename、publicPath等;resolve
:设置webpack如何解析模块依赖;plugins
:插件使用devServer
:提供了一个简单的web服务器和实时重载功能,包含了contentBase、port、proxy等;optimization
:使用splitChunks和runtimeChunk配置代码拆分和运行时代码提取等优化策略externals
:用于配置排除打包的模块;devtool
:配置source-map类型;context
:webpack使用的根目录,string类型必须是绝对路径; -target
:指定Webpack编译的目标环境;performance
:输出文件的性能检查配置;noParse
:不用解析和处理的模块;stats
:控制台输出日志控制。
Loader和Plugins
Loader和Plugin的区别
- 功能不同
- Loader本质是一个js函数,是一个“翻译官”。由于webpack只能解析原生js文件,因此,loader用于将其它类型的文件进行转换;
- Plugin本质就是一个具有apply方法的JS对象,用于扩展、增强webpack的功能。webpack在运行周期中会广播出很多事件,plugin可监听这些事件,在合适时机通webpack提供的API改变输出的结果;
- 用法不同
- loader的配置在
module.rules
下进行,类型为数组,每一项都是一个Object,包含了对于什么类型的文件(test)、使用什么加载(loader)、和使用的参数(options); - Plugin的配置在plugins下进行,类型为数组,每一项都是一个Plugin的实例,参数都通过构造函数传入。
- loader的配置在
常用的Loader和Plugin
-
常用Loader:
- babel-loader:将ES6+转换为ES5;
- css-loader:解析css文件,并处理css依赖关系;
- style-loader:将css代码注入到HTML中;
- file-loader:解析文件路径,将文件复制到输出目录,并返回文件路径;
- url-loader:类似于file-loader,但可以将小于指定大小的文件转换为base64编码的Data URL格式;
- sass-loader/less-loader:将Sass/less文件编译为css文件;
- postcss-loader:自动添加css前缀,优化css代码;
- vue-loader:将Vue单文件组件编译为js代码;
-
常用Plugins:
- HtmlWebpackPlugin:生成HTML文件,并自动将打包后的JS和CSS文件引入到HTML文件中;
- CleanWebpackPlugin:清除输出目录;
- ExtraTextWebpackPlugin/MiniCssExtractPlugin:将css代码提取到单独的CSS文件中;
- DefinePlugin:定义全局变量;
- UglifyJsWebpackPlugin:压缩JS代码;
- HotModuleReplacemenetPlugin:热模块替换,用于在开发环境下实现热更新;
- BundleAnalyzePlugin:打包分析大小和依赖;
webpack的构建流程
webpack构建流程,是一个串行的过程,即将各个插件串联起来,在运行过程中会广播事件,插件只需要监听它所关心的事件,就能加入到这条webpack机制中运作,使得整个系统扩展性更好。
需要了解的可以看下神三元大神的实现一个简单的Webpack文章
- 初始化:读取配置参数,启动webpack,创建Compiler对象并开始解析; 2.初始化Compiler(一个全局单例,负责整个webpack打包的构建流程),定义了很多钩子函数;
- 确定入口:
entry
参数; - 编译模块:根据每个Module和loader配置,递归的进行编译处理;
- Compiler开始编译,执行
run
方法,构建Compilation对象; - Compilation对象是每一次构建的上下文对象,包含了当次构建所需要的所有信息,每次热更新和重新构建,都会重新生成;
build module
完成模块编译,此时loader
开始发挥作用,因为要分清文件的依赖关系,需要通过遍历AST
抽象语法树分析依赖的模块,进而继续循环执行下一个模块的编译解析。
- Compiler开始编译,执行
- 完成模块编译,得到模块编译后之间的依赖关系;
- 输出资源,即多个模块的Chunk转换成的单独的文件输出;
- 输出完成,根据配置,把文件内容写入到文件系统;
webpack的热更新
热更新:即在不刷新页面的前提下,将新代码替换掉旧代码。
热更新原理
- 通过
webpack-dev-serve
创建两个服务器,提供静态资源的服务(node.js)和socket服务; - socket服务是一个websocket的长链接,双方可以通信
- 当某一个文件或者模块发生变化时,webpack监听到变化则对文件重新编译打包,编译生成唯一的
hash
值,作为下一次热更新的标识; - 当文件发生改动,服务端会向浏览器推送一条消息,包含改动的
hash
值 - 创建一个
ajax
去服务端请求获取到变化内容的manifest文件 - 浏览器根据manifest文件获取模块变化的内容,触发render流程,实现局部模块更新
实现原理 webpack的热更新,实际上是webpack-dev-server和浏览器之间维护的一个websocket服务。当本地资源发生改变后,webpack会将先打包生成新的模块代码放入内存中,然后webpack-dev-server向浏览器推送更新,并带上构建时的hash,让客户端和上一次资源进行对比、更新。
webpack和vite的实现原理有何区别
webpack优化怎么做的,怎么做到优化9倍的,怎么定位耗时长的地方和原因的
webpack升级踩坑
vite和webpack的区别
Vite
- 冷启动;
- 即时的模块热更新:利用现代浏览器支持ES Module的特性,不需要分析依赖,在热模块方面,当修改一个模块的时候,仅需让浏览器重新请求该模块即可,无需重新全部编译;
- 按需编译
Webpack
- 对CommonJS、AMD、ES6的语法做了兼容
- 具有强大的Plugin接口,具有很好的灵活性和扩展性
- 社区更丰富
编写webpack的扩展
Webpack是一个强大的模块打包器,允许通过编写插件来扩展功能。Webpack插件是一个具有apply
方法的JS对象,该方法会被webpack compiler调用,并且可以在整个编译生命周期中访问compiler对象。
编写webpack插件步骤示例:
- 创建一个新的JS文件,用于存放插件代码,例:ExPlugin.js;
- 定义插件类,易于组织代码,例:
class ExPlugin {
constructor(options) {
// 初始化插件,接受一些选项
this.options = options;
}
apply(compiler) {
// 在这里编写你的插件逻辑
// compiler 是 webpack 编译器的实例
}
}
-
在
apply
方法中编写插件逻辑;在
apply
方法中,可以使用compiler
对象来访问webpack的编译生命周期钩子,可以在这些钩子中添加时间监听器来执行相关的插件逻辑;
class ExPlugin {
constructor(options) {
// 初始化插件,接受一些选项
this.options = options;
}
apply(compiler) {
// 使用tapAsync方法来添加一个异步钩子监听器
compiler.hooks.run.tapAsync('ExPlugin', (compilation, callback) ={
console.log('webpack编译开始了');
callback();
})
}
}
-
使用插件;
在webpack配置文件中,导入插件使用。
const ExPlugin = require('./ExPlugin');
module.exports = {
//...其它配置
plugins: [
new ExPlugin({ /* 相关传参 */ })
]
}
- 测试插件;
- 发布插件(看需要):npm + 文档;
babel转换的过程
代码分割(Code Splitting)是什么
代码分割,是一种优化技术。允许将一个大的chunk拆分成多个小chunk,从而实现按需加载,减少初始加载时间,提高应用程序的性能。
使用:optimization.splitChunks
配置项来开启代码分割。