webpack相关
webpack的理解,解决了什么问题?
webpack
是一个用于现代JavaScript
应用程序的静态模块打包工具
webpack可以解决一下几个问题:
- 需要通过模块化的方式来开发
- 使用一些高级特性来提高开发效率,比如通过ES6+、TS开发脚本逻辑,scss、less等CSS扩展语言来编写css
- 监听文件的变化来并且反映到浏览器上,提高开发的效率(热更新)
- 开发完毕后需要对代码进行压缩、合并以及其他相关优化
webpack的构建流程
webpack的运行流程是个串行的过程,它的工作流程就是将各个插件串联起来
从启动到结束会依次执行一下三个步骤:
初始化流程:从配置文件或shell语句中读取或合并参数,并初始化需要使用的插件和配置插件等执行环境所需要的参数
编辑构建流程:从Entry出发,针对每个Module串行调用对应的Loader去翻译文件内容,再找到该Module依赖的Module,递归地进行编译处理
初始化后会调用Compiler的run来真正启动webpack编译构建流程:
- complie 开始编译(构建一个compilation 对象,执行模块创建、依赖收集、分块、打包等主要任务)
- make 从入口点文件分析模块及其依赖的模块,创建这些模块对象
- build-module 构建模块(调用配置的loaders,模块转换完后,使用acorn解析输出AST树)
- seal 封装构建结果
- emit 把各个chunk输出到结果文件
输出流程:对编译后的 Module 组合成 Chunk,把 Chunk 转换成文件,输出到文件系统
webpack 中常见的Loader
loader用于对模块的源码进行转换,在import或者加载模块时预处理文件。
像一些css、sass、png等文件,webpack则无能为力,这个时候就需要配置对应的loader进行文件内容的解析
配置loader的三种方式:
配置方式(推荐),在webpack.config.js中指定loader
在module.rules属性中配置,rules是一个数组,所以可以配置多个loader,每个配置是一个对象,test属性表示匹配规则,一般是正则表达式,use属性来调用对应的loader
内联方式:在每个import语句中显式指定loader
cli方式:在shell命令中指定它们
特性:
- loader可以同步也可以异步
- loader运行在node.js中,并且可以执行任何操作
- 除了常见的通过
package.json
的main
来将一个 npm 模块导出为 loader,还可以在 module.rules 中使用loader
字段直接引用一个模块 - 插件(plugin)可以为 loader 带来更多特性
- loader 能够产生额外的任意文件
常见的loader:
css-loader
分析css模块之间的关系,合并成一个css。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19npm install --save-dev css-loader // sh
rules: [
...,
{
test: /\.css$/,
use: {
loader: "css-loader",
options: {
// 启用/禁用 url() 处理
url: true,
// 启用/禁用 @import 处理
import: true,
// 启用/禁用 Sourcemap
sourceMap: false
}
}
}
]如果只通过css-loader加载文件,这时候页面代码设置的样式并没有生效。
原因在于,
css-loader
只是负责将.css
文件进行一个解析,而并不会将解析后的css
插入到页面中如果我们希望再完成插入
style
的操作,那么我们还需要另外一个loader
,就是style-loader
style-loader
把css-loader生成的内容,用style标签挂载到页面的head中
1
2
3
4
5
6
7
8
9npm install --save-dev style-loader // sh
rules: [
...,
{
test: /\.css$/,
use: ["style-loader", "css-loader"]
}
]
less-loader/sass-loader
开发中,我们也常常会使用
less
、sass
、stylus
预处理器编写css
样式,使开发效率提高raw-loader
在
webpack
中通过import
方式导入文件内容,该loader
并不是内置的,所以首先要安装file-loader
把识别出的资源模块,移动到指定的输出⽬目录,并且返回这个资源在输出目录的地址(字符串)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21npm install --save-dev file-loader // sh
rules: [
...,
{
test: /\.(png|jpe?g|gif)$/,
use: {
loader: "file-loader",
options: {
// placeholder 占位符 [name] 源资源模块的名称
// [ext] 源资源模块的后缀
name: "[name]_[hash].[ext]",
//打包后的存放位置
outputPath: "./images",
// 打包后文件的 url
publicPath: './images',
}
}
}
]
url-loader
可以处理理
file-loader
所有的事情,但是遇到图片格式的模块,可以选择性的把图片转成base64
格式的字符串,并打包到js
中,对小体积的图片比较合适,大图片不合适。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22npm install --save-dev url-loader // sh
rules: [
...,
{
test: /\.(png|jpe?g|gif)$/,
use: {
loader: "url-loader",
options: {
// placeholder 占位符 [name] 源资源模块的名称
// [ext] 源资源模块的后缀
name: "[name]_[hash].[ext]",
//打包后的存放位置
outputPath: "./images"
// 打包后文件的 url
publicPath: './images',
// 小于 100 字节转成 base64 格式
limit: 100
}
}
}
]
webpack 中常见的plugin, 解决了什么问题?
plugin
赋予其各种灵活的功能,运行在 webpack
的不同阶段(钩子 / 生命周期),贯穿了webpack
整个编译周期,目的在于解决loader
无法实现的其他事
特性:
本质是一个具有apply方法的js对象,apply方法会被webpack compiler调用,并且在整个编译生命周期都可以访问到compiler对象
1 |
|
编译生命周期钩子,如下:
- entry-option: 初始化option
- run
- compile: 真正开始编译,在创建compilation对象之前
- Compilation:生成了compilation对象
- make:从entry开始递归分析依赖,准备对每个模块进行build
- after-compile: 编译build过程结束
- emit: 在将内存中 assets 内容写到磁盘文件夹之前
- after-emit: 在将内存中 assets 内容写到磁盘文件夹之后
- done: 完成所有的编译过程
- failed:编译失败的时候
常见的plugin
html-webpack-plugin
在打包结束后,⾃动生成⼀个
html
⽂文件,并把打包生成的js
模块引⼊到该html
中1
2
3
4
5
6
7
8
9
10
11
12// webpack.config.js
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
...
plugins: [
new HtmlWebpackPlugin({
title: "My App",
filename: "app.html",
template: "./src/html/index.html"
})
]
};clean-webpack-plugin(删除(清理)构建目录)
mini-css-extract-plugin(提取css到一个独立的文件中)
DefinePlugin
允许在编译时创建配置的全局对象,是一个
webpack
内置的插件,不需要安装1
2
3
4
5
6
7
8
9
10const { DefinePlugun } = require('webpack')
module.exports = {
...
plugins:[
new DefinePlugin({
BASE_URL:'"./"'
})
]
}这时候编译
template
模块的时候,就能通过下述形式获取全局对象1
<link rel="icon" href="<%= BASE_URL%>favicon.ico>"
copy-webpack-plugin
复制文件或目录到执行区域,如
vue
的打包过程中,如果我们将一些文件放到public
的目录下,那么这个目录会被复制到dist
文件夹中1
2
3
4
5
6
7
8
9
10
11
12new CopyWebpackPlugin({
parrerns:[
{
from:"public",
globOptions:{
ignore:[
'**/index.html'
]
}
}
]
})复制的规则在
patterns
属性中设置:from:设置从哪一个源中开始复制
to:复制到的位置,可以省略,会默认复制到打包的目录下
globOptions:设置一些额外的选项,其中可以编写需要忽略的文件
说说Loader和plugin的区别?编写Loader和plugin的思路?
概念:
- loader是文件加载器,能够加载资源文件,并对这些文件进行一些处理,诸如编译、压缩等,最终一起打包到指定的文件中
- plugin 赋予了 webpack 各种灵活的功能,例如打包优化、资源管理、环境变量注入等,目的是解决 loader 无法实现的其他事
运行时机的区别:
- loader是运行在打包文件之前
- plugin在整个编译周期都起作用
在webpack运行的生命周期中会广播出许多事件,plugin可以监听这些事件,在合适的时机通过webpack提供的api改变输出结果。
对于loader实质是一个转化器,将A文件进行编译转化为B文件,操作的是文件。
编写loader:
loader本质是一个函数,函数中的 this
作为上下文会被 webpack
填充,因此我们不能将 loader
设为一个箭头函数,
函数接受一个参数,为 webpack
传递给 loader
的文件源内容,函数中 this
是由 webpack
提供的对象,能够获取当前 loader
所需要的各种信息,函数中有异步操作或同步操作,异步操作通过 this.callback
返回,返回值要求为 string
或者 Buffer
1 |
|
一般在编写loader
的过程中,保持功能单一,避免做多种功能
编写plugin:
由于webpack基于发布订阅模式,在运行的生命周期中会广播出许多事件,插件通过监听这些事件,就可以在特定的阶段执行自己的插件任务
webpack
编译会创建两个核心对象:
compiler:包含了 webpack 环境的所有的配置信息,包括 options,loader 和 plugin,和 webpack 整个生命周期相关的钩子
compilation:作为 plugin 内置事件回调函数的参数,包含了当前的模块资源、编译生成资源、变化的文件以及被跟踪依赖的状态信息。当检测到一个文件变化,一次新的 Compilation 将被创建
如果自己要实现
plugin
,也需要遵循一定的规范:- 插件必须是一个函数或者是一个包含
apply
方法的对象,这样才能访问compiler
实例 - 传给每个插件的
compiler
和compilation
对象都是同一个引用,因此不建议修改 - 异步的事件需要在插件处理完任务时调用回调函数通知
Webpack
进入下一个流程,不然会卡住
- 插件必须是一个函数或者是一个包含
1 |
|
在 emit
事件发生时,代表源文件的转换和组装已经完成,可以读取到最终将输出的资源、代码块、模块及其依赖,并且可以修改输出资源的内容
webpack的热更新是如何做到的?
总结:
- 通过webpack-dev-server创建两个服务器:提供静态资源服务(express)和Socket服务
- express server 负责直接提供静态资源服务(打包后的资源直接被浏览器请求和解析)
- socket server是一个websocket的长链接,可双向进行通信
- 当socket server 监听到对应模块发生变化时,会生产两个文件,.json(manifest) 、.js文件(update chunk)
- 通过长链接,socket server可直接将这两个文件主动发送给客户端(浏览器)
- 浏览器拿到两个新的文件后,通过HMR runtime机制,加载这两个文件,并且针对修改的模块进行更新
webpack proxy 的工作原理,为什么能解决跨域?
Webpack proxy 是webpack提供的代理服务,可接收客户端请求进行转发到对应的服务器,解决前端开发模式下跨域的问题
通过服务器工具webpack-dev-server实现,只适用在开发阶段
关于配置方面,在webpack
配置对象属性中通过devServer
属性提供,如下:
1 |
|
devServetr
里面proxy
则是关于代理的配置,该属性为对象的形式,对象中每一个属性就是一个代理的规则匹配
属性的名称是需要被代理的请求路径前缀,一般为了辨别都会设置前缀为/api
,值为对应的代理匹配规则,对应如下:
pathRewrite:默认情况下,我们的 /api-hy 也会被写入到URL中,如果希望删除,可以使用pathRewrite
- secure:默认情况下不接收转发到https的服务器上,如果希望支持,可以设置为false
- changeOrigin:它表示是否更新代理后请求的 headers 中host地址
工作原理:
proxy的工作原理实质上是利用http-proxy-middleware 这个http代理中间件,实现请求转发给其他服务器
在开发阶段,本地地址为http://localhost:3000
,该浏览器发送一个前缀带有/api
标识的请求到服务端获取数据,但响应这个请求的服务器只是将请求转发到另一台服务器中
1 |
|
跨域:
本地开发时候,通过webpack-dev-server启动了一个服务独立运行在localhost的一个端口上,而后端又是运行在另外一台服务器中,由于浏览器的同源策略的原因,因此本地访问会出现跨域的问题。
通过设置webpack proxy
实现代理请求后,相当于浏览器与服务端中添加一个代理者
当本地发送请求的时候,代理服务器响应该请求,并将请求转发到目标服务器,目标服务器响应数据后再将数据返回给代理服务器,最终再由代理服务器将数据响应给本地。
服务器与服务器之间请求数据并不会存在跨域行为,跨域行为是浏览器安全策略限制
如何借助webpack来优化前端性能?
手段:
JS代码压缩(terser-webpack-plugin)
terser
是一个JavaScript
的解释、绞肉机、压缩机的工具集,可以帮助我们压缩、丑化我们的代码,让bundle
更小1
2
3
4
5
6
7
8
9
10
11
12const TerserPlugin = require('terser-webpack-plugin')
module.exports = {
...
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
parallel: true // 电脑cpu核数-1
})
]
}
}- extractComments:默认值为true,表示会将注释抽取到一个单独的文件中,开发阶段,我们可设置为 false ,不保留注释
- parallel:使用多进程并发运行提高构建的速度,默认值是true,并发运行的默认数量: os.cpus().length - 1
- terserOptions:设置我们的terser相关的配置:
- compress:设置压缩相关的选项,mangle:设置丑化相关的选项,可以直接设置为true
- mangle:设置丑化相关的选项,可以直接设置为true
- toplevel:底层变量是否进行转换
- keep_classnames:保留类的名称
- keep_fnames:保留函数的名称
css代码压缩(css-minimizer-webpack-plugin)
css压缩通常是去除无用的空格等
HTML代码压缩(HtmlWebpackPlugin)
使用
HtmlWebpackPlugin
插件来生成HTML
的模板时候,通过配置属性minify
进行html
优化1
2
3
4
5
6
7
8
9
10
11
12
13module.exports = {
...
plugin:[
new HtmlwebpackPlugin({
...
minify:{
minifyCSS:false, // 是否压缩css
collapseWhitespace:false, // 是否折叠空格
removeComments:true // 是否移除注释
}
})
]
}
文件大小压缩(compression-webpack-plugin)
对文件的大小进行压缩,减少
http
传输过程中宽带的损耗1
2
3
4
5
6new ComepressionPlugin({
test:/\.(css|js)$/, // 哪些文件需要压缩
threshold:500, // 设置文件多大开始压缩
minRatio:0.7, // 至少压缩的比例
algorithm:"gzip", // 采用的压缩算法
})
图片压缩(file-loader、image-webpack-loader)
一般来说在打包之后,一些图片文件的大小是远远要比
js
或者css
文件要来的大,所以图片压缩较为重要1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43module: {
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
}
}
}
]
},
]
}
tree sharking
Tree Shaking
是一个术语,在计算机中表示消除死代码,依赖于ES Module
的静态语法分析(不执行任何的代码,可以明确知道模块的依赖关系)两种方案:
- usedExports: 通过标记某些函数是否被使用,之后通过Terser来进行优化的
- sideEffects:跳过整个模块/文件,直接查看该文件是否有副作用
usedExports:
配置方法也很简单,只需要将
usedExports
设为true
1
2
3
4
5
6module.exports = {
...
optimization:{
usedExports
}
}使用之后,没被用上的代码在
webpack
打包中会加入unused harmony export mul
注释,用来告知Terser
在优化时,可以删除掉这段代码sideEffects:
sideEffects
用于告知webpack compiler
哪些模块时有副作用,配置方法是在package.json
中设置sideEffects
属性如果
sideEffects
设置为false,就是告知webpack
可以安全的删除未用到的exports
1
2
3
4"sideEffecis":[
"./src/util/format.js",
"*.css" // 所有的css文件
]css tree shaking
css
进行tree shaking
优化可以安装PurgeCss
插件1
2
3
4
5
6
7
8
9
10
11
12
13
14const PurgeCssPlugin = require('purgecss-webpack-plugin')
module.exports = {
...
plugins:[
new PurgeCssPlugin({
path:glob.sync(`${path.resolve('./src')}/**/*`), {nodir:true}// src里面的所有文件
satelist:function(){
return {
standard:["html"]
}
}
})
]
}- paths:表示要检测哪些目录下的内容需要被分析,配合使用glob
- 默认情况下,Purgecss会将我们的html标签的样式移除掉,如果我们希望保留,可以添加一个safelist的属性
代码分离
将代码分离到不同的
bundle
中,之后我们可以按需加载,或者并行加载这些文件默认情况下,所有的
JavaScript
代码(业务代码、第三方依赖、暂时没有用到的模块)在首页全部都加载,就会影响首页的加载速度代码分离可以分出出更小的
bundle
,以及控制资源加载优先级,提供代码的加载性能这里通过
splitChunksPlugin
来实现,该插件webpack
已经默认安装和集成,只需要配置即可默认配置中,chunks仅仅针对于异步(async)请求,我们可以设置为initial或者all
1
2
3
4
5optimization:{
splitChunks:{
chunks:"all"
}
}splitChunks
主要属性有如下:- Chunks,对同步代码还是异步代码进行处理
- minSize: 拆分包的大小, 至少为minSize,如何包的大小不超过minSize,这个包不会拆分
- maxSize: 将大于maxSize的包,拆分为不小于minSize的包
- minChunks:被引入的次数,默认是1
内联chunk
可以通过
InlineChunkHtmlPlugin
插件将一些chunk
的模块内联到html
,如runtime
的代码(对模块进行解析、加载、模块信息相关的代码),代码量并不大,但是必须加载的
关于webpack
对前端性能的优化,可以通过文件体积大小入手,其次还可通过分包的形式、减少http请求次数等方式,实现对前端性能的优化
如何提高webpack的构建速度
优化手段:
优化 loader 配置
在使用
loader
时,可以通过配置include
、exclude
、test
属性来匹配文件,接触include
、exclude
规定哪些匹配应用loader
1
2
3
4
5
6
7
8
9
10
11
12
13
14module.exports = {
module: {
rules: [
{
// 如果项目源码中只有 js 文件就不要写成 /\.jsx?$/,提升正则表达式性能
test: /\.js$/,
// babel-loader 支持缓存转换出的结果,通过 cacheDirectory 选项开启
use: ['babel-loader?cacheDirectory'],
// 只对项目根目录下的 src 目录中的文件采用 babel-loader
include: path.resolve(__dirname, 'src'),
},
]
},
};
合理使用 resolve.extensions
在开发中我们会有各种各样的模块依赖,这些模块可能来自于自己编写的代码,也可能来自第三方库,
resolve
可以帮助webpack
从每个require/import
语句中,找到需要引入到合适的模块代码通过
resolve.extensions
是解析到文件时自动添加拓展名,默认情况如下1
2
3
4module.exports = {
...
extensions:[".warm",".mjs",".js",".json"]
}当我们引入文件的时候,若没有文件后缀名,则会根据数组内的值依次查找
当我们配置的时候,则不要随便把所有后缀都写在里面,这会调用多次文件的查找,这样就会减慢打包速度
优化 resolve.modules
resolve.modules
用于配置webpack
去哪些目录下寻找第三方模块。默认值为['node_modules']
,所以默认会从node_modules
中查找文件 当安装的第三方模块都放在项目根目录下的./node_modules
目录下时,所以可以指明存放第三方模块的绝对路径,以减少寻找,配置如下:1
2
3
4
5
6
7module.exports = {
resolve: {
// 使用绝对路径指明第三方模块存放的位置,以减少搜索步骤
// 其中 __dirname 表示当前工作目录,也就是项目根目录
modules: [path.resolve(__dirname, 'node_modules')]
},
};
优化 resolve.alias
alias
给一些常用的路径起一个别名,特别当我们的项目目录结构比较深的时候,一个文件的路径可能是./../../
的形式通过配置
alias
以减少查找过程1
2
3
4
5
6
7
8
9module.exports = {
...
resolve:{
alias:{
"@":path.resolve(__dirname,'./src')
}
}
}
#
使用 DLLPlugin 插件
DLL
全称是 动态链接库,是为软件在winodw种实现共享函数库的一种实现方式,而Webpack也内置了DLL的功能,为的就是可以共享,不经常改变的代码,抽成一个共享的库。这个库在之后的编译过程中,会被引入到其他项目的代码中使用步骤分成两部分:
- 打包一个 DLL 库
- 引入 DLL 库
1
2
3
4
5
6
7
8
9
10// 打包一个 DLL 库,webpack内置了一个DllPlugin可以帮助我们打包一个DLL的库文件
module.exports = {
...
plugins:[
new webpack.DllPlugin({
name:'dll_[name]',
path:path.resolve(__dirname,"./dll/[name].manifest.json")
})
]
}1
2
3
4
5
6
7
8
9
10
11
12
13// 使用 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")
})
}使用 cache-loader
在一些性能开销较大的
loader
之前添加cache-loader
,以将结果缓存到磁盘里,显著提升二次构建速度保存和读取这些缓存文件会有一些时间开销,所以请只对性能开销较大的
loader
使用此loader
1
2
3
4
5
6
7
8
9
10
11module.exports = {
module: {
rules: [
{
test: /\.ext$/,
use: ['cache-loader', ...loaders],
include: path.resolve('src'),
},
],
},
};
terser 启动多线程
1
2
3
4
5
6
7
8
9
10//使用多进程并行运行来提高构建速度
module.exports = {
optimization: {
minimizer: [
new TerserPlugin({
parallel: true,
}),
],
},
};
- 合理使用 sourceMap(打包生成
sourceMap
的时候,如果信息越详细,打包速度就会越慢)
总结: 可以看到,优化webpack
构建的方式有很多,主要可以从优化搜索时间、缩小文件搜索范围、减少不必要的编译等方面入手
与webpack类似的打包工具有哪些?区别?
Rollup
Rollup
是一款ES Modules
打包器,从作用上来看,Rollup
与Webpack
非常类似。不过相比于Webpack
,Rollup
要小巧的多Rollup
的优点:- 代码更简洁、效率更高
- 默认支持 Tree-shaking
但缺点也十分明显,加载其他类型的资源文件或者支持导入
CommonJS
模块,又或是编译ES
新特性,这些额外的需求Rollup
需要使用插件去完成综合来看,
rollup
并不适合开发应用使用,因为需要使用第三方模块,而目前第三方模块大多数使用CommonJs
方式导出成员,并且rollup
不支持HMR
,使开发效率降低但是在用于打包
JavaScript
库时,rollup
比webpack
更有优势,因为其打包出来的代码更小、更快,其存在的缺点可以忽略Parcel
Parcel ,是一款完全零配置的前端打包器,它提供了 “傻瓜式” 的使用体验,只需了解简单的命令,就能构建前端应用程序
执行命令后,
Parcel
不仅打包了应用,同时也启动了一个开发服务器,跟webpack Dev Server
一样跟
webpack
类似,也支持模块热替换,但用法更简单同时,
Parcel
有个十分好用的功能:支持自动安装依赖,像webpack
开发阶段突然使用安装某个第三方依赖,必然会终止dev server
然后安装再启动。而Parcel
则免了这繁琐的工作流程同时,
Parcel
能够零配置加载其他类型的资源文件,无须像webpack
那样配置对应的loader
Snowpack
Snowpack,是一种闪电般快速的前端构建工具,专为现代
Web
设计,较复杂的打包工具(如Webpack
或Parcel
)的替代方案,利用JavaScript
的本机模块系统,避免不必要的工作并保持流畅的开发体验开发阶段,每次保存单个文件时,
Webpack
和Parcel
都需要重新构建和重新打包应用程序的整个bundle
。而Snowpack
为你的应用程序每个文件构建一次,就可以永久缓存,文件更改时,Snowpack
会重新构建该单个文件Vite
vite ,是一种新型前端构建工具,能够显著提升前端开发体验
它主要由两部分组成:
- 一个开发服务器,它基于 原生 ES 模块 提供了丰富的内建功能,如速度快到惊人的 模块热更新HMR
- 一套构建指令,它使用 Rollup打包你的代码,并且它是预配置的,可以输出用于生产环境的优化过的静态资源
其作用类似
webpack
+webpack-dev-server
,其特点如下:- 快速的冷启动
- 即时的模块热更新
- 真正的按需编译
vite
会直接启动开发服务器,不需要进行打包操作,也就意味着不需要分析模块的依赖、不需要编译,因此启动速度非常快利用现代浏览器支持
ES Module
的特性,当浏览器请求某个模块的时候,再根据需要对模块的内容进行编译,这种方式大大缩短了编译时间在热模块
HMR
方面,当修改一个模块的时候,仅需让浏览器重新请求该模块即可,无须像webpack
那样需要把该模块的相关依赖模块全部编译一次,效率更高
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!