Webpack
文章目录
一、Webpack的基本功能
- 代码转换:TypeScript编译成JavaScript、SCSS编译成CSS等等
- 文件优化:压缩JavaScript、CSS、html代码,压缩合并图片等
- 代码分割:提取多个页面的公共代码、提取首屏不需要执行部分的代码让其异步加载
- 模块合并:在采用模块化的项目有很多模块和文件,需要构建功能把模块分类合并成一个文件
- 自动刷新:监听本地源代码的变化,自动构建,刷新浏览器
- 代码校验:在代码被提交到仓库前需要检测代码是否符合规范,以及单元测试是否通过
- 自动发布:更新完代码后,自动构建出线上发布代码并传输给发布系统。
二、Webpack的核心概念
- Entry:入口,Webpack执行构建的第一步将从Entry开始,可抽象成输入。告诉Webpack要使用哪个模块作为构建项目的起点,默认为./src/index.js
- output:出口,告诉Webpack在哪里输出它打包好的代码以及如何命名,默认为./dist
- Module:模块,在Webpack里一切皆模块,一个模块对应着一个文件。Webpack会从配置的Entry开始递归找出所有依赖的模块。
- Chunk:代码块,一个Chunk由多个模块组合而成,用于代码合并与分割。
- Loader:模块转换器,用于把模块原内容按照需求转换成新内容。
- Plugin:扩展插件,在Webpack构建流程中的特定时机会广播出对应的事件,插件可以监听这些事件的发生,在特定时机做对应的事情。
三、Webpack常用的Loader
- css-loader读取合并CSS文件
- style-loader把CSS内容注入到JavaScript里
- sass-loader解析sass文件(安装sass-loader,node-sass)
- postcss-loader自动添加浏览器兼容前缀(postcss.config配置)
- url-loader将文件转换为base64 URI。
- vue-loader处理vue文件。
四、Webpack常见的Plugins
- HtmlWbpackPlugin自动在打包结束后生成html文件,并引入bundle.js
- cleanwebPackPlugin打包自动删除上次打包文件
- define-plugin:定义环境变量 (Webpack4 之后指定 mode 会自动配置)
- ignore-plugin:忽略部分文件
- html-webpack-plugin:简化 HTML 文件创建 (依赖于 html-loader)
- web-webpack-plugin:可方便地为单页应用输出 HTML,比 html-webpack-plugin 好用
- uglifyjs-webpack-plugin:不支持 ES6 压缩 (Webpack4 以前)
- terser-webpack-plugin: 支持压缩 ES6 (Webpack4)
- webpack-parallel-uglify-plugin: 多进程执行代码压缩,提升构建速度
- mini-css-extract-plugin: 分离样式文件,CSS 提取为独立文件,支持按需加载 (替代extract-text-webpack-plugin)
- serviceworker-webpack-plugin:为网页应用增加离线缓存功能
- clean-webpack-plugin: 目录清理
- ModuleConcatenationPlugin: 开启 Scope Hoisting
- speed-measure-webpack-plugin: 可以看到每个 Loader 和 Plugin 执行耗时 (整个打包耗时、每个 Plugin 和 Loader 耗时)
- webpack-bundle-analyzer: 可视化 Webpack 输出文件的体积 (业务组件、依赖第三方模块)
下面通过clean-webpack-plugin来看一下插件的使用方法。首先,需要安装clean-webpack-plugin插件。
- npm install --save-dev clean-webpack-plugin
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
module.exports = {
...
plugins: [
...,
new CleanWebpackPlugin(),
...
]
}
五、Loader和Plugin的区别,以及如何自定义Loader和Plugin
5.1 区别
- Loader本质就是一个函数,在该函数中对接收到的内容进行转换,返回转换后的结果。因为 Webpack 只认识 JavaScript,所以 Loader 就成了翻译官,对其他类型的资源进行转译的预处理工作。
- Plugin就是插件,基于事件流框架Tapable,插件可以扩展 Webpack 的功能,在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。
- Loader 运行在打包文件之前,Loader在 module.rules 中配置,作为模块的解析规则,类型为数组。每一项都是一个 Object,内部包含了 test(类型文件)、loader、options (参数)等属性
- Loader 运行在打包文件之前,Loader在 module.rules 中配置,作为模块的解析规则,类型为数组。每一项都是一个 Object,内部包含了 test(类型文件)、loader、options (参数)等属性
5.2 自定义Loader
- 我们知道,Loader本质上来说就是一个函数,函数中的 this 作为上下文会被 webpack 填充,因此我们不能将 loader设为一个箭头函数。该函数接受一个参数,为 webpack 传递给 loader 的文件源内容。
- 同时,函数中 this 是由 webpack 提供的对象,能够获取当前 loader 所需要的各种信息。函数中有异步操作或同步操作,异步操作通过 this.callback 返回,返回值要求为 string 或者 Buffer,如下。
// 导出一个函数,source为webpack传递给loader的文件源内容
module.exports = function(source) {
const content = doSomeThing2JsString(source);
// 如果 loader 配置了 options 对象,那么this.query将指向 options
const options = this.query;
// 可以用作解析其他模块路径的上下文
console.log('this.context');
/*
* this.callback 参数:
* error:Error | null,当 loader 出错时向外抛出一个 error
* content:String | Buffer,经过 loader 编译后需要导出的内容
* sourceMap:为方便调试生成的编译后内容的 source map
* ast:本次编译生成的 AST 静态语法树,之后执行的 loader 可以直接使用这个 AST,进而省去重复生成 AST 的过程
*/
this.callback(null, content); // 异步
return content; // 同步
}
5.3 自定义Plugin
- Webpack的Plugin是基于事件流框架Tapable,由于webpack基于发布订阅模式,在运行的生命周期中会广播出许多事件,插件通过监听这些事件,就可以在特定的阶段执行自己的插件任务。
- 同时,Webpack编译会创建两个核心对象:compiler和compilation。
- compiler:包含了 Webpack 环境的所有的配置信息,包括 options,loader 和 plugin,和 webpack 整个生命周期相关的钩子.
- compilation:作为 Plugin 内置事件回调函数的参数,包含了当前的模块资源、编译生成资源、变化的文件以及被跟踪依赖的状态信息。当检测到一个文件变化,一次新的 Compilation 将被创建。
如果需要自定义Plugin,也需要遵循一定的规范: - 插件必须是一个函数或者是一个包含 apply 方法的对象,这样才能访问compiler实例 - 传给每个插件的 compiler 和 compilation 对象都是同一个引用,因此不建议修改 - 异步的事件需要在插件处理完任务时调用回调函数通知 Webpack 进入下一个流程,不然会卡住
下面是一个自定Plugin的模板:
class MyPlugin {
// Webpack 会调用 MyPlugin 实例的 apply 方法给插件实例传入 compiler 对象
apply (compiler) {
// 找到合适的事件钩子,实现自己的插件功能
compiler.hooks.emit.tap('MyPlugin', compilation => {
// compilation: 当前打包构建流程的上下文
console.log(compilation);
// do something...
})
}
}
在 emit 事件被触发后,代表源文件的转换和组装已经完成,可以读取到最终将输出的资源、代码块、模块及其依赖,并且可以修改输出资源的内容。
六、Webpack 热更新
6.1 热更新
- Webpack的热更新又称热替换(Hot Module Replacement),缩写为HMR。这个机制可以做到不用刷新浏览器而将新变更的模块替换掉旧的模块。
- HMR的核心就是客户端从服务端拉去更新后的文件,准确的说是 chunk diff (chunk 需要更新的部分),实际上 WDS 与浏览器之间维护了一个Websocket,当本地资源发生变化时,WDS 会向浏览器推送更新,并带上构建时的 hash,让客户端与上一次资源进行对比。客户端对比出差异后会向 WDS 发起Ajax请求来获取更改内容(文件列表、hash),这样客户端就可以再借助这些信息继续向 WDS 发起jsonp请求获取该chunk的增量更新。
在Webpack中配置开启热模块也非常的简单,只需要添加如下代码即可。
const webpack = require('webpack')
module.exports = {
// ...
devServer: {
// 开启 HMR 特性
hot: true
// hotOnly: true
}
}
需要说明的是,实现热更新还需要去指定哪些模块发生更新时进行HRM,因为默认情况下,HRM只对css文件有效。
if(module.hot){
module.hot.accept('./util.js',()=>{
console.log("util.js更新了")
})
}
6.2 实现原理
首先,我们来看一张图:
- 上面图中涉及了很多不同的概念,如下: - Webpack Compile:将 JS 源代码编译成 bundle.js - HMR Server:用来将热更新的文件输出给 HMR Runtime - Bundle Server:静态资源文件服务器,提供文件访问路径 - HMR Runtime:socket服务器,会被注入到浏览器,更新文件的变化 - bundle.js:构建输出的文件 - 在HMR Runtime 和 HMR Server之间建立 websocket,即图上4号线,用于实时更新文件变化
- 整个流程,我们可以将它分为两个阶段:启动阶段和更新阶段。
- 当某一个文件或者模块发生变化时,webpack 监听到文件变化对文件重新编译打包,编译生成唯一的hash值,这个hash 值用来作为下一次热更新的标识。然后,根据变化的内容生成两个补丁文件:manifest(包含了 hash 和 chundId ,用来说明变化的内容)和 chunk.js 模块。
- 由于socket服务器在HMR Runtime 和 HMR Server之间建立 websocket链接,当文件发生改动的时候,服务端会向浏览器推送一条消息,消息包含文件改动后生成的hash值,如下图的h属性,作为下一次热更细的标识。
- 在浏览器接受到这条消息之前,浏览器已经在上一次 socket 消息中已经记住了此时的 hash 标识,这时候我们会创建一个 ajax 去服务端请求获取到变化内容的 manifest 文件。mainfest文件包含重新build生成的hash值,以及变化的模块,对应上图的c属性。浏览器根据 manifest 文件获取模块变化的内容,从而触发render流程,实现局部模块更新。
6.3 总结
- ** 通过前面的分析,总结Webpack热模块的步骤如下: - 通过webpack-dev-server创建两个服务器:提供静态资源的服务(express)和Socket服务 - express server 负责直接提供静态资源的服务(打包后的资源直接被浏览器请求和解析) - socket server 是一个 websocket 的长连接,双方可以通信 - 当 socket server 监听到对应的模块发生变化时,会生成两个文件.json(manifest文件)和.js文件(update chunk) - 通过长连接,socket server 可以直接将这两个文件主动发送给客户端(浏览器) - 浏览器拿到两个新的文件后,通过HMR runtime机制,加载这两个文件,并且针对修改的模块进行更新**
七、Webpack Proxy工作原理
7.1 代理
- 在项目开发中不可避免会遇到跨越问题,Webpack中的Proxy就是解决前端跨域的方法之一。所谓代理,指的是在接收客户端发送的请求后转发给其他服务器的行为,webpack中提供服务器的工具为webpack-dev-server。
7.1.1 webpack-dev-server
- webpack-dev-server是 webpack 官方推出的一款开发工具,将自动编译和自动刷新浏览器等一系列对开发友好的功能全部集成在了一起。同时,为了提高开发者日常的开发效率,只适用在开发阶段。在webpack配置对象属性中配置代理的代码如下:
// ./webpack.config.js
const path = require('path')
module.exports = {
// ...
devServer: {
contentBase: path.join(__dirname, 'dist'),
compress: true,
port: 9000,
proxy: {
'/api': {
target: 'https://api.github.com'
}
}
// ...
}
}
- 其中,devServetr里面proxy则是关于代理的配置,该属性为对象的形式,对象中每一个属性就是一个代理的规则匹配。
- 属性的名称是需要被代理的请求路径前缀,一般为了辨别都会设置前缀为 /api,值为对应的代理匹配规则,对应如下: - target:表示的是代理到的目标地址。 - pathRewrite:默认情况下,我们的 /api-hy 也会被写入到URL中,如果希望删除,可以使用pathRewrite。 - secure:默认情况下不接收转发到https的服务器上,如果希望支持,可以设置为false。 - changeOrigin:它表示是否更新代理后请求的 headers 中host地址。
7.2 原理
proxy工作原理实质上是利用http-proxy-middleware 这个http代理中间件,实现请求转发给其他服务器。比如下面的例子:
const express = require('express');
const proxy = require('http-proxy-middleware');
const app = express();
app.use('/api', proxy({target: 'http://www.example.org', changeOrigin: true}));
app.listen(3000);
// http://localhost:3000/api/foo/bar -> http://www.example.org/api/foo/bar
- 在上面的例子中,本地地址为http://localhost:3000,该浏览器发送一个前缀带有/api标识的请求到服务端获取数据,但响应这个请求的服务器只是将请求转发到另一台服务器中。
7.3 跨域
- 在开发阶段, webpack-dev-server 会启动一个本地开发服务器,所以我们的应用在开发阶段是独立运行在 localhost 的一个端口上,而后端服务又是运行在另外一个地址上。所以在开发阶段中,由于浏览器同源策略的原因,当本地访问后端就会出现跨域请求的问题。
- 解决这种问题时,只需要设置webpack proxy代理即可。当本地发送请求的时候,代理服务器响应该请求,并将请求转发到目标服务器,目标服务器响应数据后再将数据返回给代理服务器,最终再由代理服务器将数据响应给本地,原理图如下:
- 在代理服务器传递数据给本地浏览器的过程中,两者同源,并不存在跨域行为,这时候浏览器就能正常接收数据。
- 注意:服务器与服务器之间请求数据并不会存在跨域行为,跨域行为是浏览器安全策略限制
八、如何借助Webpack来优化性能
- 作为一个项目的打包构建工具,在完成项目开发后经常需要利用Webpack对前端项目进行性能优化,常见的优化手段有如下几个方面:
- JS代码压缩
- CSS代码压缩
- Html文件代码压缩
- 文件大小压缩
- 图片压缩
- Tree Shaking
- 代码分离
- 内联 chunk
8.1 JS代码压缩
- terser是一个JavaScript的解释、绞肉机、压缩机的工具集,可以帮助我们压缩、丑化我们的代码,让bundle更小。在production模式下,webpack 默认就是使用 TerserPlugin 来处理我们的代码的。如果想要自定义配置它,配置方法如下。
const TerserPlugin = require('terser-webpack-plugin')
module.exports = {
...
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
parallel: true // 电脑cpu核数-1
})
]
}
}
- TerserPlugin常用的属性如下: - extractComments:默认值为true,表示会将注释抽取到一个单独的文件中,开发阶段,我们可设置为 false ,不保留注释 - parallel:使用多进程并发运行提高构建的速度,默认值是true,并发运行的默认数量: os.cpus().length - 1 - terserOptions:设置我们的terser相关的配置: compress:设置压缩相关的选项,mangle:设置丑化相关的选项,可以直接设置为true mangle:设置丑化相关的选项,可以直接设置为true toplevel:底层变量是否进行转换 keep_classnames:保留类的名称 keep_fnames:保留函数的名称
8.2 CSS代码压缩
- CSS压缩通常用于去除无用的空格等,不过因为很难去修改选择器、属性的名称、值等,所以我们可以使用另外一个插件:css-minimizer-webpack-plugin。配置如下:
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
module.exports = {
// ...
optimization: {
minimize: true,
minimizer: [
new CssMinimizerPlugin({
parallel: true
})
]
}
}
8.3 Html文件代码压缩
- 使用HtmlWebpackPlugin插件来生成HTML的模板时候,可以通过配置属性minify进行html优化,配置如下。
module.exports = {
...
plugin:[
new HtmlwebpackPlugin({
...
minify:{
minifyCSS:false, // 是否压缩css
collapseWhitespace:false, // 是否折叠空格
removeComments:true // 是否移除注释
}
})
]
}
8.4 文件大小压缩
- 对文件的大小进行压缩,可以有效减少http传输过程中宽带的损耗,文件压缩需要用到 compression-webpack-plugin插件,配置如下
new ComepressionPlugin({
test:/\.(css|js)$/, // 哪些文件需要压缩
threshold:500, // 设置文件多大开始压缩
minRatio:0.7, // 至少压缩的比例
algorithm:"gzip", // 采用的压缩算法
})
8.5 图片压缩
- 如果我们对bundle包进行分析,会发现图片等多媒体文件的大小是远远要比 js、css 文件要大的,所以图片压缩在打包方面也是很重要的。配置可以参考如下的方式:
module: {
rules: [
{
test: /\.(png|jpg|gif)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name]_[hash].[ext]',
outputPath: 'images/',
}
},
{
loader: 'image-webpack-loader',
options: {
// 压缩 jpeg 的配置
mozjpeg: {
progressive: true,
quality: 65
},
// 使用 imagemin**-optipng 压缩 png,enable: false 为关闭
optipng: {
enabled: false,
},
// 使用 imagemin-pngquant 压缩 png
pngquant: {
quality: '65-90',
speed: 4
},
// 压缩 gif 的配置
gifsicle: {
interlaced: false,
},
// 开启 webp,会把 jpg 和 png 图片压缩为 webp 格式
webp: {
quality: 75
}
}
}
]
},
]
}
8.6 Tree Shaking
- Tree Shaking 是一个术语,在计算机中表示消除死代码,依赖于ES Module的静态语法分析。在webpack实现Trss shaking有两种不同的方案: - usedExports:通过标记某些函数是否被使用,之后通过Terser来进行优化的 - sideEffects:跳过整个模块/文件,直接查看该文件是否有副作用
- usedExports的配置方法很简单,只需要将usedExports设为true即可,如下。
module.exports = {
...
optimization:{
usedExports
}
}
- 而sideEffects则用于告知webpack compiler在编译时哪些模块有副作用,配置方法是在package.json中设置sideEffects属性。如果sideEffects设置为false,就是告知webpack可以安全的删除未用到的exports,如果有些文件需要保留,可以设置为数组的形式。
"sideEffecis":[ "./src/util/format.js", "*.css" // 所有的css文件]
8.7 代码分离
- 默认情况下,所有的JavaScript代码(业务代码、第三方依赖、暂时没有用到的模块)在首页全部都加载,就会影响首页的加载速度。如果可以分出出更小的bundle,以及控制资源加载优先级,从而优化加载性能。
- 代码分离可以通过splitChunksPlugin来实现,该插件webpack已经默认安装和集成,只需要配置即可。
module.exports = {
...
optimization:{
splitChunks:{
chunks:"all"
}
}}
- splitChunks有如下几个属性: - Chunks:对同步代码还是异步代码进行处理 - minSize: 拆分包的大小, 至少为minSize,如何包的大小不超过minSize,这个包不会拆分 - maxSize: 将大于maxSize的包,拆分为不小于minSize的包 - minChunks:被引入的次数,默认是1
8.8 内联chunk
- 可以通过InlineChunkHtmlPlugin插件将一些chunk的模块内联到html,如runtime的代码(对模块进行解析、加载、模块信息相关的代码),代码量并不大但是必须加载的,比如:
const InlineChunkHtmlPlugin = require('react-dev-utils/InlineChunkHtmlPlugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
... plugin:[
new InlineChunkHtmlPlugin(HtmlWebpackPlugin,[/runtime.+\.js/]}
- 总结一下,Webpack对前端性能的优化,主要是通过文件体积大小入手,主要的措施有分包、减少Http请求次数等。
九、提高Webpack的构建速度
- 随着功能和业务代码越来越多,相应的 Webpack 的构建时间也会越来越久,构建的效率也会越来越低,那如何提升Webpack 构建速度,是前端工程化的重要一环。常用的手段有如下一些: - 优化 loader 配置 - 合理使用 resolve.extensions - 优化 resolve.modules - 优化 resolve.alias - 使用 DLLPlugin 插件 - 使用 cache-loader - terser 启动多线程 - 合理使用 sourceMap
9.1 优化 Loader 配置
- 在使用Loader时,可以通过配置include、exclude、test属性来匹配文件,通过include、exclude来规定匹配应用的loader。例如,下面是ES6 项目中配置 babel-loader 的例子:
module.exports = {
module: {
rules: [
{
// 如果项目源码中只有 js 文件就不要写成 /\.jsx?$/,提升正则表达式性能
test: /\.js$/,
// babel-loader 支持缓存转换出的结果,通过 cacheDirectory 选项开启
use: ['babel-loader?cacheDirectory'],
// 只对项目根目录下的 src 目录中的文件采用 babel-loader
include: path.resolve(__dirname, 'src'),
},
]
},
};
9.2 合理resolve.extensions
- 在开发中,我们会有各种各样的模块依赖,这些模块可能来自第三方库,也可能是自己编写的, resolve可以帮助Webpack从每个 require/import 语句中,找到需要引入到合适的模块代码。
- 具体来说,通过resolve.extensions是解析到文件时自动添加拓展名,默认情况如下:
module.exports = {
...
extensions:[".warm",".mjs",".js",".json"]
}
- 当我们引入文件的时候,若没有文件后缀名,则会根据数组内的值依次查找。所以,处理配置的时候,不要随便把所有后缀都写在里面。
9.3 优化 resolve.modules
- resolve.modules 用于配置 webpack 去哪些目录下寻找第三方模块,默认值为[‘node_modules’]。所以,在项目构建时,可以通过指明存放第三方模块的绝对路径来减少寻找的时间。
module.exports = {
resolve: {
modules: [path.resolve(__dirname, 'node_modules')] // __dirname 表示当前工作目录
},
};
9.4 优化 resolve.alias
- alias给一些常用的路径起一个别名,特别当我们的项目目录结构比较深的时候,一个文件的路径可能是./…/…/的形式,通过配置alias以减少查找过程。
module.exports = {
...
resolve:{
alias:{
"@":path.resolve(__dirname,'./src')
}
}
}
9.5 使用 DLL Plugin 插件
- DLL全称是动态链接库,是为软件在winodw种实现共享函数库的一种实现方式,而Webpack也内置了DLL的功能,为的就是可以共享,不经常改变的代码,抽成一个共享的库。使用步骤分成两部分: - 打包一个 DLL 库 - 引入 DLL 库
打包一个 DLL 库
- Webpack内置了一个DllPlugin可以帮助我们打包一个DLL的库文件,如下。
module.exports = {
...
plugins:[
new webpack.DllPlugin({
name:'dll_[name]',
path:path.resolve(__dirname,"./dll/[name].mainfest.json")
})
]
}
引入 DLL 库
- 首先,使用 webpack 自带的 DllReferencePlugin 插件对 mainfest.json 映射文件进行分析,获取要使用的DLL库。然后,再通过AddAssetHtmlPlugin插件,将我们打包的DLL库引入到Html模块中。
module.exports = {
...
new webpack.DllReferencePlugin({
context:path.resolve(__dirname,"./dll/dll_react.js"),
mainfest:path.resolve(__dirname,"./dll/react.mainfest.json")
}),
new AddAssetHtmlPlugin({
outputPath:"./auto",
filepath:path.resolve(__dirname,"./dll/dll_react.js")
})
}
9.6 合理使用使用 cache-loader
- 在一些性能开销较大的 loader 之前添加 cache-loader,以将结果缓存到磁盘里,显著提升二次构建速度。比如:
module.exports = {
module: {
rules: [
{
test: /\.ext$/,
use: ['cache-loader', ...loaders],
include: path.resolve('src'),
},
],
},
};
- 需要说明的是,保存和读取这些缓存文件会有一些时间开销,所以请只对性能开销较大的 loader 使用此 loader。
9.7 开启多线程
- 开启多进程并行运行可以提高构建速度,配置如下:
module.exports = {
optimization: {
minimizer: [
new TerserPlugin({
parallel: true, //开启多线程
}),
],
},
};
十、模块管理工具(扩展)
10.1 Vite
- Vite是Vue的作者尤雨溪开发的Web开发构建工具,它是一个基于浏览器原生ES模块导入的开发服务器,在开发环境下,利用浏览器去解析import,在服务器端按需编译返回,完全跳过了打包这个概念,服务器随启随用。同时不仅对Vue文件提供了支持,还支持热更新,而且热更新的速度不会随着模块增多而变慢。
- Vite具有以下特点: - 快速的冷启动 - 即时热模块更新(HMR,Hot Module Replacement) - 真正按需编译
- Vite由两部分组成: - 一个开发服务器,它基于 原生 ES 模块 提供了丰富的内建功能,如速度快到惊人的 [模块热更新HMR。 - 一套构建指令,它使用 Rollup打包你的代码,并且它是预配置的,可以输出用于生产环境的优化过的静态资源。
- Vite在开发阶段可以直接启动开发服务器,不需要进行打包操作,也就意味着不需要分析模块的依赖、不需要编译,因此启动速度非常快。当浏览器请求某个模块的时候,根据需要对模块的内容进行编译,大大缩短了编译时间
- 在热模块HMR方面,当修改一个模块的时候,仅需让浏览器重新请求该模块即可,无须像Webpack那样需要把该模块的相关依赖模块全部编译一次,因此效率也更高。