关于Webpack一些问题的简单补充和总结
- 1.Webpack的本质
- 2.代码分割(代码分离)的实现方式
- 3.Webpack的打包原理
- 4.Webpack的核心概念
- 5.npm打包时需要注意哪些?
- 6.前端为什么要进行打包和构建?
- 7.怎么配置单页应用?怎么配置多页应用?
- 8.loader vs plugin
- 9.bundle vs chunk vs module
- 10.sourceMap
- 11.babel 相关
- 12.webpack-dev-server vs http服务器
- 13.如何在Webpack中处理不同类型的资源文件,例如图片、CSS和字体文件?
- 14.Webpack的Tree Shaking是什么?如何配置实现代码的无用代码剔除?
- 15.解释一下Webpack的文件指纹(file fingerprint)和缓存(caching)机制。
- 16.Webpack的Resolve模块解析
- 17.Webpack是如何把不同的模块合并到一起的
- 18.Webpack提高效率的插件
- 19.Webpack构建流程
- 20.如何保证各个loader按照预想方式工作
- 21.Webpack事件机制
1.Webpack的本质
Webpack是一个前端模块打包工具。它可以将多个模块按照依赖关系进行静态分析,并生成一个或多个打包后的文件。Webpack的主要功能包括:
- 模块打包
将项目中的所有模块(JavaScript、CSS、图片等)当作一个整体,通过依赖关系将它们打包成一个或多个静态资源文件。 - 依赖管理
Webpack可以分析模块之间的依赖关系,根据配置的入口文件找出所有依赖的模块,并将其整合到打包结果中。 - 文件转换
Webpack本身只能处理JavaScript模块,但通过加载器(Loader)的使用,可以将其他类型的文件(如CSS、LESS、图片等)转换为有效的模块,使其能够被打包到最终的结果中。 - 代码拆分
Webpack支持将代码拆分成多个模块,按需加载,实现按需加载和提升应用性能。 - 插件系统
Webpack提供了丰富的插件系统,可以通过插件实现各种功能的扩展,例如压缩代码、自动生成HTML文件等。 - 开发服务器
Webpack提供了一个开发服务器,可以在本地快速启动一个服务器,支持热模块替换(Hot Module Replacement),使得开发过程更加高效 - 性能优化
Webpack集成了多种性能优化技术,如代码压缩、代码混淆、资源优化(如图片压缩)和缓存利用等,显著减少文件大小,提高加载速度 - 跨浏览器兼容性
通过Babel等加载器,Webpack可以将现代JavaScript代码转换为兼容旧浏览器的格式,解决了跨浏览器兼容性的问题
2.代码分割(代码分离)的实现方式
2.1 使用entry配置手动地分离代码
如果入口 chunk 之间包含一些重复的模块,那些重复模块都会被引入到各个 bundle 中。
entry:{
index:'./src/index.js',
another:'./src/another-module.js'
},
output:{
filename:'[name].bundle.js',
...
},
2.2 提取公用依赖配置dependOn
配置entry提取公用依赖。配置 dependOn 选项,这样可以在多个chunk之间共享模块。
const path = require('path');
module.exports = {
mode: 'development',
entry: {
index: {
import: './src/index.js',
dependOn: 'shared',//添加此配置
},
another: {
import: './src/another-module.js',
dependOn: 'shared',//添加此配置
},
shared: 'lodash',//添加此配置
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
},
};
2.3 SplitChunksPlugin插件
SplitChunksPlugin插件可以将公共的依赖模块提取到已有的入口 chunk 中,或者提取到一个新生成的 chunk。
optimization:{
...
splitChunks:{
chunks:'all'
}
},
2.4 动态导入
其实就是编译器一看到import(文件路径),就会把这个文件视为要拆分的文件,自动会把它单独打一个包。实现代码如下:
// 假设我们有一个moduleA.js文件
// src/modules/moduleA.js
export default '我是module A!';
// 在主文件中,我们可以这样动态引入moduleA.js
// src/index.js
function loadModuleA() {
return import(/* webpackChunkName:'moduleA' */'./mudules/moduleA.js')
.then(moduleA => {
console.log(moduleA.default); // 输出: '我是module A!'
})
.catch(error => {
console.error('Module loading failed:', error);
});
}
// 当你需要的时候调用这个函数
loadModuleA();
2.5 预获取/预加载模块
与prefetch指令相比,preload指令有许多不同之处:
- preload chunk会在父chunk加载时,以并行方式开始加载。prefetch chunk会在父chunk加载结束后开始加载。
- preload chunk具有中等优先级,并立即下载。prefetch chunk在浏览器闲置时下载。
- preload chunk会在父chunk中立即请求,用于当下时刻。prefetch chunk会用于未来的某个时刻。
浏览器支持程度不同。
//src/index.js
//测试预获取
const button = document.createElement('button')
button.textContent = '点击执行加法运算'
button.addEventListener('click', () => {
import(/* webpackChunkName: 'minus', webpackPrefetch: true */
'./modules/minus.js').then(({ minus }) => {
console.log(minus(10, 5))
})
})
document.body.appendChild(button)
//src/index.js
//测试预加载
const button2 = document.createElement('button')
button2.textContent = '点击执行字符串打印'
button2.addEventListener('click', () => {
import(/* webpackChunkName: 'print', webpackPreload: true */
'./modules/print.js').then(({ print }) => {
print()
})
})
document.body.appendChild(button2)
2.6 总结:代码分割的本质
代码分割的本质其实就是在源代码直接上线和打包成唯一脚本main.bundle.js这两种极端方案之间的一种更适合实际场景的中间状态。
- 源代码直接上线:虽然过程可控,但是http请求多,性能开销大。
- 打包成唯一脚本:一把梭完自己爽,服务器压力小,但是页面空白期长,用户体验不好。
3.Webpack的打包原理
Webpack的打包原理主要是基于模块化的思想,将项目中的各个文件(包括JavaScript、CSS、图片等静态资源)视为模块,并根据这些模块之间的依赖关系进行静态分析。然后,Webpack会根据指定的规则将这些模块打包成一个或多个静态资源文件(bundle),以供浏览器加载和执行。
4.Webpack的核心概念
4.1 entry
入口,Webpack 执行构建的第一步将从 Entry 开始,可抽象成输入。告诉webpack要使用哪个模块作为构建项目的起点,默认为./src/index.js:
//单个入口写法
module.exports = {
entry: {
main: './path/to/my/entry/file.js',
},
};
//多个入口写法,分离 app(应用程序) 和 vendor(第三方库) 入口
module.exports = {
entry: {
main: './src/app.js',
vendor: './src/vendor.js',
},
};
4.2 output
出口,告诉webpack在哪里输出它打包好的代码以及如何命名,默认为./dist。
const path = require('path');
module.exports = {
entry: './path/to/my/entry/file.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'my-first-webpack.bundle.js',
},
};
4.3 mode
模式,通过选择 development, production 或 none 之中的一个,来设置 mode 参数,你可以启用 webpack 内置在相应环境下的优化。其默认值为 production。
module.exports = {
mode: 'production',
};
4.4 loader
模块转换器,用于把模块原内容按照需求转换成新内容。
//loader在哪配置
module.exports = {
module: {
rules: [
{ test: /\.css$/, use: 'css-loader' },
{ test: /\.ts$/, use: 'ts-loader' },
],
},
};
4.5 plugin
扩展插件,loader用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。包括:打包优化,资源管理,注入环境变量。
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack'); // 用于访问内置插件
module.exports = {
module: {
rules: [{ test: /\.txt$/, use: 'raw-loader' }],
},
plugins: [new HtmlWebpackPlugin({ template: './src/index.html' })],
};
4.6 devServer
提供了一个简单的web服务器和实时重载功能,可以通过devServer.contentBase、devServer.port、devServer.proxy等进行配置。
4.7 optimization
可以使用optimization.splitChunks和optimization.runtimeChunk配置代码拆分和运行时代码提取等优化策略。
4.8 devtool
配置source-map类型。
4.9 externals
用于配置排除打包的模块,例如,可以将jQuery作为外置扩展,避免将其打包到应用程序中。
4.10 performance
输出文件的性能检查配置。
4.11 浏览器兼容性
Webpack 支持所有符合 ES5 标准 的浏览器(不支持 IE8 及以下版本)。webpack 的 import() 和 require.ensure() 需要 Promise。如果你想要支持旧版本浏览器,在使用这些表达式之前,还需要提前加载polyfill。
4.12 module
模块,在 Webpack 里一切皆模块,一个模块对应着一个文件。Webpack 会从配置的 Entry 开始递归找出所有依赖的模块。
4.13 chunk
代码块,一个 Chunk 由多个模块组合而成,用于代码合并与分割。
5.npm打包时需要注意哪些?
NPM模块需要注意以下问题:
- 要支持
CommonJS模块化规范,所以要求打包后的最后结果也遵守该规则。 npm模块使用者的环境是不确定的,很有可能并不支持ES6,所以打包的最后结果应该是采用ES5编写的。并且如果ES5是经过转换的,请最好连同SourceMap一同上传。npm包大小应该是尽量小(有些仓库会限制包大小)- 发布的模块不能将依赖的模块也一同打包,应该让用户选择性的去自行安装。这样可以避免模块应用者再次打包时出现底层模块被重复打包的情况。
UI组件类的模块应该将依赖的其它资源文件,例如.css文件也需要包含在发布的模块里。
6.前端为什么要进行打包和构建?
代码层面:
- 体积更小(Tree-shaking、压缩、合并),加载更快
- 编译高级语言和语法(TS、ES6、模块化、scss)
- 统一、高效的开发环境
- 统一的构建流程和产出标准
- 集成公司构建规范(提测、上线)
研发流程层面:
- 统一、高效的开发环境
- 统一的构建流程和产出标准
- 集成公司构建规范(提测、上线)
7.怎么配置单页应用?怎么配置多页应用?
7.1 单页应用
单页应用可以理解为webpack的标准模式,直接在entry中指定单页应用的入口即可,这里不再赘述。
7.2 多页应用
多页应用项目的目录结构必须遵守他预设的规范。详细示例内容可见此处。
8.loader vs plugin
8.1 loader
8.1.1 示例
module.exports = {
module: {
rules: [
{
test: /\.css|less$/,
use: [
"style-loader",
"css-loader",
"less-loader",
],
},
],
},
};
8.1.2 机制
webpack默认只能打包js文件,配置里的module.rules数组配置了一组规则,告诉 Webpack 在遇到哪些文件时使用哪些 Loader 去加载和转换打包成js。
8.1.3 注意点
- use属性的值需要是一个由 Loader 名称组成的数组,Loader 的执行顺序是由后到前的。
- 每一个 Loader 都可以通过URL querystring 的方式传入参数,例如
css-loader?minimize中的minimize告诉css-loader要开启 CSS 压缩。
8.1.4 常用loader
vue-loader处理vue文件。babel-loader:将ES6代码转换为ES5,确保兼容性,适用于旧版浏览器ts-loader处理TypeScript文件,将其转换为JavaScript
用途: 用于配置项目typescriptstyle-loader:将CSS代码注入到JavaScript中,通过DOM操作加载CSS。css-loader:处理CSS文件,支持模块化、压缩和文件导入等功能,通常与style-loader一起使用sass-loader解析sass文件(安装sass-loader,node-sass)less-loader解析less文件(安装less-loader,less)postcss-loader自动添加浏览器兼容前缀(postcss.config配置)file-loader:将文件输出到指定文件夹,通过相对URL引用输出文件。url-loader:与file-loader类似,当文件较小时,可以直接将文件内容以base64格式注入到代码中。
8.2 plugin
8.2.1 示例
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports={
entry:'./src/index.js',
output:{
filename:'bundle.js',
path:path.resolve(__dirname,'./dist')
},
mode:'development',
devtool: 'inline-source-map' ,
plugins:[
new HtmlWebpackPlugin()
],
}
8.2.2 机制
Plugin 是用来扩展 Webpack 功能的,通过在构建流程里注入钩子实现,它给 Webpack 带来了很大的灵活性。Webpack 是通过plugins属性来配置需要使用的插件列表的。plugins属性是一个数组,里面的每一项都是插件的一个实例,在实例化一个组件时可以通过构造函数传入这个组件支持的配置属性。
8.2.3 常用plugin
html-webpack-plugin:自动在打包结束后生成html文件,并引入bundle.jsuglifyjs-webpack-plugin:Webpack早期版本的默认JS压缩插件。需要注意的是,uglifyjs-webpack-plugin对ES6的压缩支持可能不是特别好。terser-webpack-plugin:从Webpack 4.0开始,默认使用terser-webpack-plugin作为JS压缩插件。与uglifyjs-webpack-plugin相比,terser-webpack-plugin对ES6的压缩支持更好。同时,可以开启parallel参数,使用多进程进行压缩,加快压缩速度。MiniCssExtractPlugin:该插件将CSS从JavaScript代码中提取出来,生成独立的CSS文件,有利于浏览器的并行加载和缓存管理。这在生产环境中优化页面性能方面非常有用CleanWebpackPlugin:每次打包前清理输出目录,确保输出目录只包含当前构建生成的文件,避免旧文件残留,保持项目结构的清晰compression-webpack-plugin:这个插件用于gzip压缩,启用gzip压缩可以大幅缩减传输资源大小,从而缩短资源下载时间,减少首次白屏时间,提升用户体验。compression-webpack-plugin压缩的主要是webpack打包好的资源文件。
8.3 loader和plugin的对比
8.3.1 功能不同。
- Loader
Webpack 中的 Loader 主要用于处理文件类型的转换和处理,比如将 ES6/ES7 代码转换成 ES5 代码,将 LESS/SASS/CSS 文件转换成浏览器可识别的 CSS 文件等。 - Plugin
Webpack 中的 Plugin 主要用于在打包过程中做一些额外的处理工作,比如文件压缩、代码分离、资源优化、生成 HTML 文件等。
8.3.2 使用方式不同
- Loader
Webpack 中的 Loader 需要在模块的 rules 属性中配置,同时还需要通过 npm 安装相应的 Loader,如 babel-loader、css-loader 等。在配置 Loader 时,需要设置 Loader 的匹配规则和转换规则,使得 Webpack 能够正确地识别和处理需要转换的文件类型。 - Plugin
Webpack 中的 Plugin 需要在配置文件中单独引入,并通过 new 关键字实例化,如 new HtmlWebpackPlugin()、new UglifyJsPlugin() 等。在配置 Plugin 时,需要设置插件的参数和执行顺序,以便插件能够按照开发者的需求进行工作。
8.3.3 作用范围不同
- Loader:
Webpack 中的 Loader 是针对于每个文件进行处理的,每个文件都会经过 Loader 进行转换处理,因此 Loader 的作用范围比较小。 - Plugin
Webpack 中的 Plugin 是针对于整个项目进行处理的,它们能够修改 Webpack 打包的结果、优化打包过程、生成文件等,因此 Plugin 的作用范围比较大。
9.bundle vs chunk vs module
9.1 bundle
Bundle是webpack最终输出的文件,它是由一组已经经过加载和编译的Chunk组成的。换句话说,Chunk经过进一步的处理和优化后,最终会被合并成Bundle。Bundle是可以在浏览器中直接运行的代码。
在前端开发中,Bundle的作用是将多个源文件(如JavaScript、CSS、图像等)合并为单个文件,以便在浏览器中更高效地加载和传输。这有助于减少网络请求次数、提高加载速度、实现更好的缓存和压缩效果。
9.2 chunk
Chunk是webpack打包过程中的中间产物。它代表着一组被合并在一起的Modules。通常情况下,Chunk是由多个Module组成的,webpack会根据一定的规则将这些Module打包成一个Chunk。
Chunk的产生途径包括:entry入口、异步加载模块和代码分割(code splitting)。Chunk是webpack打包过程中的一个阶段,它表示了模块打包的初步结果。
9.3 module
是开发中的单个模块。
9.4 三者关系
- Module(模块) 是webpack中最小的单元,它们是代码的离散功能块。
- Chunk(代码块) 是webpack打包过程中的中间产物,由多个Module组成。
- Bundle(捆绑包) 是webpack最终输出的文件,由一组已经过加载和编译的Chunk组成,可以直接在浏览器中运行。大多数情况下chunk和bundle是一对一的,如果配置了sourcemap时,可能会出现一个chunk对应多个bundle的情况。
10.sourceMap
10.1 示例
module.exports = {
entry: './src/index.js', // 入口文件
output: {
filename: 'main.js', // 输出文件名
path: path.resolve(__dirname, 'dist'), // 输出目录
},
devtool: 'source-map', // 开启 source map
module: {
},
}
10.2 不同前缀的意义
- inline:不生成映射关系文件,打包进main.js
- cheap: 只精确到行,不精确到列,打包速度快;只管业务代码,不管第三方模块
- module:不仅管业务代码,而且管第三方代码
- eval:执行效率最快,性能最好
10.3 最佳实践
-
开发环境:
cheap-module-eval-source-map:本地开发首次打包慢点没关系,因为eval缓存的原因,热更新会很快 // 开发中,我们每行代码不会写的太长,只需定位到行就行,所以加上cheap // 我们希望能够找到源代码的错误,而不是打包后的,所以需要加上module -
线上环境:
cheap-module-source-map或者不设置devtool选项
这里需要补充说明的是,eval-source-map组合使用是指将.map以DataURL的形式引入到打包好的模块中,类似于inline属性的效果,我们在生产中,使用eval-source-map会使打包后的文件太大,因此在生产环境中不会使用eval-source-map。但是因为eval的rebuild速度快,因此我们可以在本地环境中增加eval属性。
10.4 总结
source map是将编译、打包、压缩后的代码映射回源代码的过程。打包压缩后的代码不具备良好的可读性,想要调试源码就需要soucre map。
线上环境一般有三种处理方案:
hidden-source-map:借助第三方错误监控平台 Sentry 使用nosources-source-map:只会显示具体行数以及查看源代码的错误栈。安全性比 sourcemap 高sourcemap:通过 nginx 设置将 .map 文件只对白名单开放(公司内网)
注意:避免在生产中使用inline-和eval-,因为它们会增加bundle体积大小,并降低整体性能。
11.babel 相关
11.1 polyfill 以及 runtime 区别
- babel-polyfill 它是通过向全局对象和内置对象的prototype上添加方法来实现的。比如运行环境中不支持Array.prototype.find 方法,引入polyfill, 我们就可以使用es6方法来编写了,但是缺点就是会造成全局空间污染。
- babel-runtime: 它不会污染全局对象和内置对象的原型。
详细情况可以参考我写的文章https://blog.youkuaiyun.com/fageaaa/article/details/146023275的第五节。
11.2 ES stage 含义
- Stage 0 - 设想(Strawman):只是一个想法,可能有 Babel插件。
- Stage 1 - 建议(Proposal):这是值得跟进的。
- Stage 2 - 草案(Draft):初始规范。
- Stage 3 - 候选(Candidate):完成规范并在浏览器上初步实现。
- Stage 4 - 完成(Finished):将添加到下一个年度版本发布中。
这是babel6的知识,了解了解即可。babel7相对于babel6做了很多改动。详细情况可自己查资料。
11.3 preset–env 作用
- babel-preset-es2015: 可以将es6的代码编译成es5.
- babel-preset-es2016: 可以将es7的代码编译为es6.
- babel-preset-es2017: 可以将es8的代码编译为es7.
- babel-preset-latest: 支持现有所有ECMAScript版本的新特性
11.4 Babel原理
主要分为三个阶段:
- 解析
当Babel接收到源代码时,将会调用一个叫做解析器的工具,用于将源代码转换为抽象语法树(AST)。在这个过程中,解析器会识别代码中的语法结构,并将其转换为对应的节点类型。例如,当解析器遇到一个变量声明语句时,它将会创建一个 “VariableDeclaration” 节点,并将该节点的信息存储在AST中。AST是一个以节点为基础组成的树形结构,每个节点都有相应的类型、属性和子节点等信息。 - 转换
一旦AST被创建,Babel 将遍历整个树形结构,对每个节点进行转换。这些转换可以是插件、预设或手动创建的。转换器会检查AST中的每个节点,然后对其进行相应的修改或替换,以将新语法转换为旧语法。 例如,如果 Babel 遇到一个包含箭头函数的节点,而你已经启用了转换插件,该插件将会将箭头函数转换为其等效的体函数。代码转换后,Babel 将会生成一个新的AST。 - 生成
最后,Babel将基于转换后的AST生成代码文本。在这个步骤中,Babel将遍历转换后的AST,并创建对应的代码字符串,并将这些字符串组合成一个完整的JavaScript文件。如果启用了代码压缩,Babel还可以将生成的代码进行压缩。
总结来说,Babel 的原理就是将JavaScript 源代码转换为抽象语法树(AST),然后对AST进行转换,生成与源代码功能相同但向后兼容的代码。Babel 提供了一个强大的生态系统,使得开发者可以轻松扩展并自定义转换器,实现自己的功能需求。
12.webpack-dev-server vs http服务器
12.1 webpack-dev-server
webpack-dev-server是一个独立的npm模块,同时也是一个服务器插件。在使用webpack-dev-server之前,需要单独安装它作为项目的开发依赖。
Webpack Dev Server插件是一个基于Node.js构建的Web服务器,它可以在本地开发环境中启动一个实时的Web服务器,并且能够自动编译并且刷新浏览器,为前端开发提供了很大的便利。
Webpack Dev Server支持热模块替换(HMR),即在应用程序运行中更新模块而无需刷新整个页面,它还提供了一些其它的特性包括压缩、编译、转换、代码分离等等。Webpack Dev Server通常用来搭建本地开发环境,而部署时则需要使用其它的Web服务器。
12.2 http服务器
http服务器因其模拟后台api接口的能力,更适用于测试环境。
webpack-dev-server使用内存来存储webpack开发环境下的打包文件,并且可以使用模块热更新,比传统的http服务对开发更加有效。
13.如何在Webpack中处理不同类型的资源文件,例如图片、CSS和字体文件?
Webpack提供了各种加载器(Loader)来处理不同类型的资源文件。下面是处理图片、CSS和字体文件的示例:
13.1 图片文件
安装 file-loader 或 url-loader:
npm install file-loader 或 npm install url-loader
在Webpack配置文件中配置加载器:
module: {
rules: [
{
test: /\.(png|jpg|gif)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[hash].[ext]', // 文件指纹配置
outputPath: 'images/', // 输出目录
}
}
]
}
]
}
以上配置将处理以 .png、.jpg 或 .gif 结尾的图片文件,并将它们输出到指定的目录中。
13.2 CSS 文件
安装 style-loader 和 css-loader:
npm install style-loader css-loader
在Webpack配置文件中配置加载器:
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
]
}
以上配置将处理以 .css 结尾的 CSS 文件,并使用 style-loader 和 css-loader 进行加载和处理。
13.3 字体文件
安装 file-loader 或 url-loader:
npm install file-loader 或 npm install url-loader
在Webpack配置文件中配置加载器:
module: {
rules: [
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[hash].[ext]', // 文件指纹配置
outputPath: 'fonts/' // 输出目录
}
}
]
}
]
}
以上配置将处理以 .woff、.woff2、.eot、.ttf 或 .otf 结尾的字体文件,并将它们输出到指定的目录中。
注意,在配置加载器时,你可以根据自己的需求自定义加载器的选项。这些加载器可以在Webpack配置中的 module.rules 中进行定义。
14.Webpack的Tree Shaking是什么?如何配置实现代码的无用代码剔除?
Webpack的Tree Shaking是一个用于删除代码中未被引用的未使用代码的优化技术。
它可以显著减小打包后的文件体积,提高网页加载速度。
要配置Webpack实现代码的无用代码剔除,需要进行以下几个步骤:
- 确保你的代码是使用
ES6模块语法编写的,而不是CommonJS(require)语法。因为Tree Shaking只对ES6模块有效。 - 在
Webpack配置文件中,通过设置mode为production,启用Webpack的生产模式,此时Tree Shaking功能会默认开启。 - 确保你的
package.json文件中的sideEffects字段被正确配置。sideEffects字段告诉Webpack哪些文件是没有副作用的,可以进行Tree Shaking。如果你的应用是纯粹的JavaScript应用,可以将sideEffects设置为"sideEffects": false。如果你的应用使用了一些有副作用的模块(例如CSS文件),你需要将这些模块添加到sideEffects字段中。这样Webpack就不会将这些有副作用的模块进行Tree Shaking。
注意:Tree Shaking只适用于ES6模块语法,并且只能剔除未引用的代码,不能剔除被动态引用的代码(例如通过字符串拼接生成模块路径)。另外,一些特定的代码结构可能会导致Tree Shaking失效,例如使用eval、with等特殊语法。
15.解释一下Webpack的文件指纹(file fingerprint)和缓存(caching)机制。
15.1 文件指纹
15.1.1 文件指纹是什么
Webpack的文件指纹机制是指在打包生成静态资源时,为每个文件生成唯一的标识码。这个标识码通常是通过对文件内容进行hash计算得到的。一旦文件内容发生改变,其文件指纹也会发生改变,从而防止浏览器在缓存过期前使用旧的文件。
15.1.2 文件指纹的常见类型
文件指纹有以下几种常见的类型:
- Hash:每次打包时,Webpack 会给每个输出的文件生成一个 hash 值。只要文件内容发生变化,其 hash 值也会发生变化。
- Chunkhash:根据不同的入口文件进行依赖关系解析后,Webpack 会为每个 chunk 生成一个 hash 值。只有当前 chunk 内容发生变化时,其 hash 值才会发生变化。
- Contenthash:采用文件内容的 hash 值作为文件指纹,只有文件内容发生变化时,其 hash 值才会发生变化。适用于样式文件、图片文件等。
15.1.3 文件指纹使用示例
JS的文件指纹设置:
设置output的filename,用chunkhash。
module.exports = {
entry: {
app: './scr/app.js',
search: './src/search.js'
},
output: {
filename: '[name][chunkhash:8].js',
path:__dirname + '/dist'
}
}
CSS的文件指纹设置:
设置MiniCssExtractPlugin的filename,使用contenthash。
module.exports = {
entry: {
app: './scr/app.js',
search: './src/search.js'
},
output: {
filename: '[name][chunkhash:8].js',
path:__dirname + '/dist'
},
plugins:[
new MiniCssExtractPlugin({
filename: `[name][contenthash:8].css`
})
]
}
图片的文件指纹设置:
设置file-loader的name,使用hash。
占位符名称及含义如下:
- ext 资源后缀名
- name 文件名称
- path 文件的相对路径
- folder 文件所在的文件夹
- contenthash 文件的内容hash,默认是md5生成
- hash 文件内容的hash,默认是md5生成
- emoji 一个随机的指代文件内容的emoj
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename:'bundle.js',
path:path.resolve(__dirname, 'dist')
},
module:{
rules:[{
test:/\.(png|svg|jpg|gif)$/,
use:[{
loader:'file-loader',
options:{
name:'img/[name][hash:8].[ext]'
}
}]
}]
}
}
15.2 缓存机制
15.2.1 缓存机制是什么
缓存机制是指浏览器在加载页面时,会将静态资源(如 JS、CSS、图片等)保存在本地,以便下次加载相同资源时可以直接使用缓存副本,从而提高网页加载速度。缓存机制分为强缓存和协商缓存两种方式。
15.2.2 缓存机制的分类
- 强缓存:通过设置 Response Header 中的 Cache-Control 或 Expires 字段,告诉浏览器静态资源的有效期。在有效期内,浏览器会直接从缓存获取资源,无需向服务器发起请求。
- 协商缓存:通过设置 Response Header 中的 Last-Modified 和 ETag 字段,告诉浏览器静态资源的版本信息。在请求资源时,如果浏览器的缓存仍然有效,则会发送一个请求到服务器,服务器会根据请求中的 If-Modified-Since 和 If-None-Match 字段进行验证,返回 304 状态码,并告诉浏览器可以使用缓存,从而减少数据传输。
关于缓存机制详情可见我写的文章https://blog.youkuaiyun.com/fageaaa/article/details/146066068中的第18节。
16.Webpack的Resolve模块解析
Webpack的Resolve模块解析是用于解析模块路径的配置选项。它可以帮助Webpack正确地确定模块的位置。
- resolve.modules
用于指定模块的搜索路径。当Webpack在解析导入语句时,它会按照指定的顺序依次查找这些路径来确定模块的位置。默认情况下,Webpack会在当前工作目录和node_modules文件夹中查找。 - resolve.alias
用于创建模块的路径别名。通过配置别名,可以让Webpack在导入模块时使用更简短的路径。这对于减少代码中的冗余路径非常有用。 - resolve.extensions
用于指定可以省略的文件扩展名。当导入模块时没有指定文件扩展名时,Webpack会按照指定的顺序依次尝试添加扩展名来解析模块。这样可以让我们在导入模块时省略掉繁琐的扩展名,提高开发效率。
通过合理配置这些选项,我们可以让Webpack更快地找到模块的位置,并且在导入模块时更加方便简洁。
17.Webpack是如何把不同的模块合并到一起的
- 解析入口文件
Webpack从配置的入口文件开始,解析该文件及其依赖的模块。入口文件通常是项目的根文件,如index.js。 - 构建AST(抽象语法树)
Webpack使用解析器(parser)将源代码转换为AST。AST是源代码的树状表现形式,使得Webpack能够理解和分析代码的结构。 - 找出依赖关系
在解析AST的过程中,Webpack会识别出import、require等模块导入语句,并记录下这些依赖关系。这些依赖关系形成了一个依赖图(dependency graph)。 - 加载和转换模块
根据依赖图,Webpack会加载每个依赖模块,并使用配置的loader对模块进行转换。例如,CSS文件会通过css-loader转换为JavaScript模块,图片文件会通过file-loader或url-loader进行处理。 - 打包模块
一旦所有模块都被加载和转换,Webpack会将它们打包到一个或多个bundle中。这个过程包括将模块的代码和依赖关系合并到一起,以及进行代码优化和压缩等操作。 - 输出bundle
最后,Webpack将打包好的bundle输出到配置的输出目录中。输出可以是单个文件,也可以是多个文件(如代码分割后的多个chunk)。
18.Webpack提高效率的插件
- webpack-dashboard:可以更友好的展示相关打包信息。
- webpack-merge:提取公共配置,减少重复配置代码
- speed-measure-webpack-plugin:简称 SMP,分析出 Webpack 打包过程中 Loader 和 Plugin 的耗时,有助于找到构建过程中的性能瓶颈。
- size-plugin:监控资源体积变化,尽早发现问题
- HotModuleReplacementPlugin:模块热替换
19.Webpack构建流程
Webpack 的运行流程是一个串行的过程,从启动到结束会依次执行以下流程:
- 初始化参数:从配置文件和
Shell语句中读取与合并参数,得出最终的参数 - 开始编译:用上一步得到的参数初始化
Compiler对象,加载所有配置的插件,执行对象的run方法开始执行编译 - 确定入口:根据配置中的
entry找出所有的入口文件 - 编译模块:从入口文件出发,调用所有配置的
Loader对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理 - 完成模块编译:在经过第4步使用
Loader翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系 - 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的
Chunk,再把每个Chunk转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会 - 输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统
在以上过程中,Webpack会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,并且插件可以调用Webpack提供的API改变Webpack的运行结果。
简单说
- 初始化:启动构建,读取与合并配置参数,加载
Plugin,实例化Compiler - 编译:从
Entry出发,针对每个Module串行调用对应的Loader去翻译文件的内容,再找到该Module依赖的Module,递归地进行编译处理 - 输出:将编译后的
Module组合成Chunk,将Chunk转换成文件,输出到文件系统中
20.如何保证各个loader按照预想方式工作
可以使用 enforce 强制执行 loader 的作用顺序,pre 代表在所有正常 loader 之前执行,post 是所有 loader 之后执行。(inline 官方不推荐使用)。
- pre: 前置 loader
- normal: 普通 loader
- inline: 内联 loader
- post: 后置 loader
21.Webpack事件机制
Webpack常见的事件有:
- before-run: 在Webpack开始执行构建之前触发,可以用于清理上一次构建的临时文件或状态。
- run: 在Webpack开始执行构建时触发。
- before-compile: 在Webpack开始编译代码之前触发,可以用于添加一些额外的编译配置或预处理代码。
- compile: 在Webpack开始编译代码时触发,可以用于监听编译过程或处理编译错误。
- this-compilation: 在创建新的Compilation对象时触发,Compilation对象代表当前编译过程中的所有状态和信息。
- compilation: 在Webpack编译代码期间触发,可以用于监听编译过程或处理编译错误。
- emit: 在Webpack生成输出文件之前触发,可以用于修改输出文件或生成一些附加文件。
- after-emit: 在Webpack生成输出文件后触发,可以用于清理中间文件或执行一些其他操作。
- done: 在Webpack完成构建时触发,可以用于生成构建报告或通知开发者构建结果。
Webpack的事件机制是基于Tapable实现的,Tapable是Webpack事件机制的核心类,它封装了事件的订阅和发布机制。在Webpack中,Compiler对象和Compilation对象都是Tapable类的实例对象。

750

被折叠的 条评论
为什么被折叠?



