hotOnly: true 不会自动刷新,能看到错误 module.hot.accept 写了很多与功能无关的代码 额外代码是冗余的 Defineplugin:注入全局成员的,production默认会启用起来,会注入process.env.NODE_ENV,通过它判断环境。变量需要转换成字符串。
new webpack.DefinePlugin({
API_BASE_URL: 'http://xxx.com' //这是不对的,js中输出没带字符串
API_BASE_URL: '"http://xxx.com"' //这是对的,需要引号变成字符串,输出就是在js中能用的代码
})
TreeShaking deadCode 它不是指某个配置选项,是一组功能搭配使用后的优化效果,在productionn自动启动
optimization: {
usedExports: true, //负责标记哪些是没有的代码
concatenateModules: true, //尽可能的将所有模块合并输出到一个函数中,既提升了运行效率,又减少了代码体积,这个属性又被称为Scope Hoisting,也就是作用于提升,这是webpack3添加的特性,再配合minimize,代码就会减少很多
minimize: true //压缩代码,去除掉没用的代码
}
TreeShaking & Babel,使用Babel会使得TreeShaking失效,TreeShaking前提必须是使用ES Modules,由webpack打包的代码必须使用ESM,如果配置@babel/preset-env,babel会转换es成commonJS,TreeShaking就不会生效。在最新版本的babel-loader中,已经自动关闭了esm转换的插件,支持esm,所以打包后还是esm代码,
use:{
loader: 'babel-loader',
option:{
presets: [['@babel/preset-env',{modules:'commonjs'}]] //这个是强制执行转换成commonjs,这样usedExports就不会生效。
presets: [['@babel/preset-env',{modules:false}]] //这个是关闭转换成commonjs,这样usedExports就会生效。
}
}
副作用:模块执行的时候,除了导出成员是否还做了其他的事情,这个特性,一般用于开发npm模块才会用到,官网当中把sideEffects的介绍和TreeShaking混在一起,所以很多人认为sideEffects和TreeShaking是因果关系,其实跟TreeShaking无关。
如果不启用sideEffects,没用的文件也会被打包到bundle.js中,production环境是默认打开的
optimization: {
sideEffects: true //这是开启这个功能
}
package.json中
sideEffects: false //用来标识代码不是副作用的
没有副作用,也就是没有用到的代码就会被移除掉,不会打包进来。
使用sideEffects的前提,是确保你的代码真的没有副作用,否则webpack打包时会误删掉有副作用的代码。
在申明中,没有执行导出没有副作用,就会被移除掉。
extend.js
//因为add没有导出,尽管在main.js中被引用,开启sideEffects还是会被移除掉
Number.prototype.add = function(a,b){
return a+b
}
main.js
import './src/extend.js'
add(1,2)
package.json中
sideEffects: ['./src/extend.js','./src/gloabal.css']
//用来标识哪些代码是有副作用的
解决办法是 1.关闭副作用
- 在文件前标记哪些文件是有副作用的
所有的代码最终都会被打包到一起,应用非常复杂的话,就需要进行codeSplitting,分包/分割代码,按需加载。 1.多入口打包。 2.动态导入,实现按需加载
entry:{
index: './src/index.js',
album: './src/album.js'
},
output: {
filename: '[name].bundle.js'
}
这样导致2个页面都使用了这2个js文件,但是我们想要的是html只使用想要的js文件,可以在HtmlWebpackPlugin中加入chunks: ['index']。
new HtmlWebpackPlugin({
title: 'multiple entry',
template: './src/index.html',
filename: 'index.html',
chunks: ['index']
}),
new HtmlWebpackPlugin({
title: 'multiple entry',
template: './src/album.html',
filename: 'album.html',
chunks: ['album']
})
Split Chunks提取公共模块,不同入口中肯定会有公共模块
optimization: {
splitChunks: {
chunks:'all' //所有的公共代码
}
}
动态导入的模块会被自动提取到对应的bundle中
if(hash=== '#index'){
import('./index').then((res)=>{
})
} else if(hash=== '#album'){
import('./album').then((res)=>{
})
}
魔法注释,可以改变模块的名称
if(hash=== '#index'){
import(/* webpackChunkName : 'index1'*/ './index').then((res)=>{
})
} else if(hash=== '#album'){
import(/* webpackChunkName : 'index2'*/'./album').then((res)=>{
})
}
提取css中
plugins: [
new MiniCssExtractPlugin()
]
压缩css
plugins: [
new OptimizeCssAssetsWebpackPlugin()
]
官网中OptimizeCssAssetsWebpackPlugin不是配置在plugins中, 配置在plugins中是适用于全局, 配置在minimizer中,只会在minimizer开启状态中使用,建议使用在minimizer中统一控制,没开启压缩就不会压缩,生成环境自动开启。 配置在minimizer中的缺点是:配置了minimizer数组,webpack会认为你要使用自定义的压缩插件,这样会覆盖一些内置的压缩器插件,导致压缩js的功能丢失,所以需要手动再加入对应插件TerserWebpackPlugin
optimization: {
minimizer: [new OptimizeCssAssetsWebpackPlugin(), new TerserWebpackPlugin()]
}
hash:项目级别的,项目中任何一个文件发生改动,整个项目都会发生变化 chunckHash:在打包过程中,同一路的打包hash都是相同的 contentHash:文件级别的,根据输出文件的内容来输出hash,只要是不同的文件就会有不同的hash,精确到文件,是最好的方式,最适合解决缓存
plugins: [
new MiniCssExtractPlugin({
filename:'[name].[contentHash:8].bundle.css' //8指定hash长度
})
]
loader工作原理就是是负责资源文件从输入到输出的转换。loader其实是一种管道的概念,可以依次使用多个loader。 plugin是除了资源模块加载,也是增强webpack自动化方面的能力,loader是专注实现资源模块加载,从而实现整体项目的打包
关于webpack零零散散总结了很长一段时间了,今天才来梳理成文,里面主要包含了如下知识点:
- webpack怎么配置(区分打包环境、启动热更新、动态导入、模块解析等)
- source map包含哪些?开发环境和生产环境怎么选择?
- tree-shaking怎么配置
- 缓存机制设置等
- webpack4和webpack5的区别
- 热更新HRM原理
- webpack构建流程
- webpack打包文件详解
集中其他相关篇幅:
- plugin和loader的区别;
- 怎么写plugin?其原理;
- 怎么写loader?其原理;
- 常用的plugin和loader;
希望对大家有帮助,同时发现错误欢迎指正,谢谢!!!
一、webpack进阶配置
细节可参考官网,以下是一些重要点提取出来 概念 | webpack 中文网 (webpackjs.com)
资源模块(Asset Modules,webpack5新引入的的)
功能:
- 是一种 模块类型,它允许使用资源文件,而无需配置额外loader。
- 资源文件:字体、图片、图标、HTML。。。
- 不用file-loader、url-loader也能加载图片和字体。
webpack4操作:
- raw-loader :将文件导入为字符串
- file-loader: 将文件发送到输出目录
- url-loader:将文件发送到输出目录,或转为Data URI(base64)内联到bundle中
webpack5操作:
- asset/resource:发送一个单独的文件并导出URL(之前通过使用file-loader实现)
- asset/inline:导出一个资源的data URI (之前通过使用url-loader实现)
- asset/source:导出资源的源代码(之前通过使用raw-loader 实现)
- asset:在导出一个data URI 和发送一个单独的文件之间自动选择(url-loader)
Webpack Dev Serve
作用:发布web服务,提高开发效率
webpack4:webpack-dev-server ...
webpack5:webpack server...
webpack4热更新:
hot:true
webpack5热更新:
webpack5新加的
liveReload:true (不能再使用hot)
target: 'web' (热更新只适用于web相关的targets)
proxy配置接口代理
changeOrigin:true
区分打包环境
- 通过环境变量区分
- webpack --env.production
- webpack.config.js中判断env
- 通过配置文件区分
- webpack.dev.conf.js
- webpack.prod.conf.js
- webpack.base.conf.js (公共配置)
webpack-merge 将多个配置合并在一起
命令行中设置环境变量
- webpack4:webpack --env.production
- webpack5:webpack --env production (没有点)
webpack.config.js
- 读取环境变量env.production
- 根据环境变量指定不同的配置
webpack.config.js
module.exports = (env, argv) => {
cosnt config = {mode:'development'}
if(env production){
config.mode = 'production'
...
}
return config
}
提取公共模块
optimization: {
splitChunks: {
chunks:'all'
}
}
动态导入
懒加载:默认不加载,事件触发后才加载。
webpackChunkName: '加载名称'
document.getElementId('btn').onclick = function(){
//import 启动懒加载
//webpackChunkName: 'desc' 指定懒加载的文件名称
//webpackPrefetch: true 启动预加载
import(/*webpackChunkName: 'desc', webpackPrefetch: true */'test').then(()=>{
console.lo('此处才加载了test文件,才能调用它文件中的东西')
})
}
预加载:先等待其他资源加载,浏览器空闲时,再加载 webpackPrefetch: true 缺点:在移动端有兼容性问题
源码映射(source map)
映射模式(devtool的值)
- 不同映射模式的报错定位效果和打包执行速度不同
- webpack4中,一共有13种不同的映射模式
- webpack5中,一共有26种不同的映射模式
webpack5中的命名更新严格
^(inline-|hidden-|eval-)?(nosources-)?(cheap-(module-)?)?source-map$
| 模式 | 描述 |
|---|---|
| cheap | 只定位错误,不定位报错列 |
| hidden | 生成.map文件,但是.js的末尾没有关联.map,报错后需手动关联,然后定位报错 |
| inline | 不生成.map文件,映射以base64-VLQs的形式添加到.js最后 |
| eval | 不生成.map文件,映射信息追加到eval函数的最后,来关联处理前后的对应关系 |
| module | 不但映射工程师自己写的代码,还支持对loader和第三方模块的映射 |
| nosources | 生成.map中不包含sourceContent,定位错误时看不到源码(更安全) |
webpack4的13个常见模式
webpack5 有26种模式虽然更丰富,但是关键词不变,多了几个排列组合, 虽然模式多,但是很多模式现在还没效果,webpack5更新内容多,是为了以后做铺垫的。
如何选择合适的映射模式(这是个人建议,但是不绝对)
- 开发环境:eval-cheap-module-source-map
- 生产环境:none | nosources-source-map
如果想在生成环境也想看到错误定位,也可以在生成环境使用 cheap-module-source-map
这样选择的原因:
- eval的rebuild速度快,因此我们可以在本地环境中增加eval属性。
- 使用eval-source-map会使打包后的文件太大,因此在生产环境中不会使用
tree-shaking
Tree-shaking 较早由 Rich_Harris 的 rollup 实现,后来,webpack2 也引入了tree-shaking 的功能,本质是消除无用的JavaScript代码,(支持CSS消除吗?我觉得不支持,CSS又不是ES Modules规范)。
tree-shaking原理:
将所有代码打包到一个作用域下,然后遍历所有作用域,去除没使用的作用域(webpack原理:遍历所有引入模块,把它们打包成一个文件,在这个过程中,就知道哪些export的模块被使用到)
基于ES6的静态引用,treeshaking通过扫描所有ES6的export,找出被import的内容并添加到最终代码中。
注意点:
- 使用ES Modules规范的模块,才能执行tree-shaking。因为tree-shaking依赖于ES Modules的静态语法分析
- 不能删除立即执行函数,避免使用IFEE
- 如果使用第三方的模块,可以尝试直接从文件路劲引用的方式使用
Tree shaking为什么用ES6:
因为ES6模块的出现,ES6模块依赖关系是确定的,和运行时的状态无关,可以进行可靠的静态分析,这样方便甩掉重复代码,这就是Tree shaking的基础。
如何使用:
- 生产模式:tree-shaking会自动开启
- 开发模式:
- usedExports
- sideEffects
1. usedExports
optimization: {
//标记未被使用的代码,打包后的未使用的代码会带上注释/*unused harmony export xxxx*/
usedExports:true,
//删除unused harmony export xxxx标记的代码
minimize:true,
//terser-webpack-plugin压缩插件:webpack4需要单独安装,webpack5无需安装,但需要引入
minimizer: [new TerserPlugin()]
}
optimization.usedExports (标记没用的代码) ,打包后的未使用的代码会带上注释/*unused harmony export xxxx*/
optimization.minimize:true (删除unused harmony export xxxx标记的代码)
terser-webpack-plugin (去掉项目多余的debuger)
webpack4需要单独安装terser-webpack-plugin (webpack5无需安装,但需要引入)
Tree Shaking与 Source Map存在兼容性问题:
Tree Shaking仅仅支持 devtool: source-map | inline-source-map | hidden-source-map | nosources-source-map;
因为eval模式,将JS输出为字符串 (不是ES module规范),导致Tree Shaking失效
2. sideEffects 副作用
无副作用:如果一个模块单纯的导入导出变量,那它就无副作用
有副作用:如果一个模块还修改其他模块或者全局的一些东西,就有副作用
- 修改全局变量
- 在原型上扩展方法
- css的引入(比如作用于html)
sideEffects的作用:把未使用但无副作用的模块一并删除
对于没有副作用的模块,未使用代码不会被打包(相当于压缩了输出内容)
开启副作用:
optimization: {
sideEffects:true
}
标识代码是否有副作用(在package.json中设置sideEffects):
- true: 所有代码都有副作用
- false: 所有代码都没有副作用(告诉webpack可以安全地删除未用的exports)
- 数组:(告诉webpack哪些模块有副作用,不删除)
//比如 为true
sideEffects:true
//比如 为数组
sideEffects: ['.src/test.js','*.css']
Webpack Tree shaking 深入探究 (juejin.cn)
缓存机制
babel缓存:
- cacheDirectory:true 第二次构建时,会读取之前的缓存
文件资源缓存:
- 如果代码在缓存期内,代码更新后看不到实时效果
- 方案:将代码文件名称,设置为哈希名称,名称发生变化时,就加载最新内容
webpack哈希值:
- hash 每次webpack打包生成的hash值
- chunkhash 不同chunk的hash值不同,同一次打包可能生成不同的chunk
- contenthash 不同内容的hash值不同,同一个chunk中可能有不同的内容
//8代表hash名称位数
[name].[contenthash:8].js
[name].[contenthash:8].css
模块解析resolve
- 配置模块解析的规则
- alias:配置模块加载的路径别名
alias:{'@':resolve('src')}
- extensions:引入模块时,可以省略哪些后缀
extensions:['js','json']
还有其他,可看官网
排除打包externals
- 排除打包依赖,防止对某个依赖项进行打包
- 一般,一些成熟的第三方库,是不需要打包的,比如jquery,可以直接引入CDN
模块联邦
Webpack5 模块联邦让 Webpack 达到了线上 Runtime 的效果,让代码直接在项目间利用 CDN 直接共享,不再需要本地安装 Npm 包、构建再发布。
const HtmlWebpackPlugin = require("html-webpack-plugin");
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
module.exports = {
// other webpack configs...
plugins: [
new ModuleFederationPlugin({
name: "app_one_remote",
remotes: {
app_two: "app_two_remote",
app_three: "app_three_remote"
},
exposes: {
AppContainer: "./src/App"
},
shared: ["react", "react-dom", "react-router-dom"]
}),
new HtmlWebpackPlugin({
template: "./public/index.html",
chunks: ["main"]
})
]
};
调用
import { Search } from "app_two/Search";
ModuleFederationPlugin插件的几个重要参数:
- name当前应用名称,需要全局唯一
- remotes可以将其他项目的name映射到当前项目中
- exposes表示导出的模块,只有在此申明的模块才可以作为远程依赖被使用
- shared是非常重要的参数,可以让远程加载的模块对应依赖改为使用本地项目的React或ReactDOM
二、webpack高阶详解知识
webpack4和webpack5的区别
- 热更新
webpack4:webpack-dev-server ...
webpack5:webpack server...
webpack4热更新:
hot:true
webpack5热更新:
webpack5新加的
liveReload:true (不能再使用hot)
target: 'web' (热更新只适用于web相关的targets)
- source map模式写法不一样
webpack4的13个常见模式
webpack5的26个常见模式
具体查看官网
- sideEffects 使用 usedExports
optimization: {
//标记未被使用的代码,打包后的未使用的代码会带上注释/*unused harmony export xxxx*/
usedExports:true,
//删除unused harmony export xxxx标记的代码
minimize:true,
//terser-webpack-plugin压缩插件:webpack4需要单独安装,webpack5无需安装,但需要引入
minimizer: [new TerserPlugin()]
}
webpack4需要单独安装terser-webpack-plugin (webpack5无需安装,但需要引入使用)
- webpack5内置缓存功能 Webpack5 内置缓存方案探索_追逐丶的博客-CSDN博客_webpack5 缓存
webpack4的缓存方案:cache-loader、dll
webpack5的缓存方案:`
IdleFileCachePlugin:持久化到本地磁盘MemoryCachePlugin:持久化到内存
webpack5的内置缓存方案无论从性能上还是安全性上都要好于cache-loader:
性能上:由于所以被webpack处理的模块都会被缓存,缓存的覆盖率要高的多安全上:由于cache-loader使用了基于mtime的缓存验证机制,导致在CI环境中缓存经常会失效,但是Webpack5改用了基于文件内容etag的缓存验证机制,解决了这个问题。具体使用的Webpack5配置官网已经给出了。
-
webpack5增加了模块联邦
-
关闭url-loader默认的ES Modules规范,强制url-loader使用CommonJS规范进行打包
webpack4中只需要url-loader配置esModule:false
webpack5需要html-loader和url-loader都配置esModule:false
热更新(HRM)原理
Webpack HMR 原理解析 - 知乎 (zhihu.com)
总结主要几点:
- 浏览器端和服务端之间建立一个 websocket 长连接,将 webpack 编译打包的各个阶段的状态信息告知浏览器端
- 通过 JsonpMainTemplate.runtime 向 server 端发送 Ajax 请求,服务端返回一个 json,该 json 包含了所有要更新的模块的 hash 值,获取到更新列表后,该模块
再次通过 jsonp 请求,获取到最新的模块代码
优化 Webpack 的构建速度?
Webpack5 性能优化 - 优化构建速度 - 云+社区 - 腾讯云 (tencent.com)
如何对bundle体积进行监控和分析?
-
VSCode 中有一个插件 import cost 可以帮助我们对引入模块的大小进行实时监测
-
webpack-bundle-analyzer生成 bundle 的模块组成图,显示所占体积
webpack构建流程(打包原理终版)
webpack打包后产生的文件是个立即自执行函数,自执行函数的入参是个数组,这个数组包含了所有的模块,包裹在函数中。
Webpack 实际上为每个模块创造了一个可以导出和导入的环境,本质上并没有修改 代码的执行逻辑,代码执行顺序与模块加载顺序也完全一致。
从webpack配置文件中配置的entry入口文件开始解析文件构建AST语法树,找出每个文件所依赖的文件,递归下去,最终打包成一个文件。
简单webpack打包原理:
- 初始化:启动构建,读取与合并配置参数,加载 Plugin,实例化 Compiler
- 编译:从 Entry 出发,针对每个 Module 串行调用对应的 Loader 去翻译文件的内容,再找到该 Module 依赖的 Module,递归地进行编译处理
- 输出:将编译后的 Module 组合成 Chunk,将 Chunk转换成文件,输出到文件系统中
/**
* 初始化准备工作,获取模块内容,分析模块,收集依赖,通过babel把es6转化为es5形成更AST,递归获取所有依赖
*
* 初始化准备工作,获取模块内容
* 分析模块:将获取到的模块内容 解析成AST语法树( @babel/parser),AST是个引用路劲
* 收集依赖:将用import语句引入的文件路径收集起来。我们将收集起来的路径放到deps里,将file目录路径跟获得的value值拼接成相对路劲放在deps里(@babel/traverse)
* ES6转成ES5(AST): 把获得的ES6的AST转化成ES5,这样获取到代码 (@babel/core @babel/preset-env)
* 递归获取所有依赖:这个对象包括该模块的路径(file),该模块的依赖(deps),该模块转化成es5的代码;
* 并且得处理成:以文件的路径为key,{code,deps}为值的形式存储
* 整合代码:目的就是要生成一个bundle.js文件,也就是打包后的一个文件。其实思路很简单,就是把index.js的内容和它的依赖模块整合起来。然后把代码写到一个新建的js文件。
* 形成可执行的文件:放到立即执行函数里
*
初始化参数:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数
开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译
确定入口:根据配置中的 entry 找出所有的入口文件
编译模块:从入口文件出发,调用所有配置的 Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理
完成模块编译:在经过第4步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系
输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会
输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统
最终表达:
webpack的运行流程是一个串行的过程,从启动到结束会依次执行以下流程:根据配置文件初始化参数,加载所有配置的插件,执行对象的 run 方法开始执行编译;
* 与此同时,根据配置确定该入口文件,从入口文件出发,调用所有配置的 Loader 对模块进行翻译(翻译包括css转化成js、es6转化成es5),并找出该模块依赖的模块,递归处理;
* 再根据输出的源码、文件依赖关系,最终打包成一个文件
*/
webpack打包文件详解
如果该模块是es6,会在导出对象中加入__esModule = true CommonJS加载ES Module
__webpack_require__是引入输入模块,并返回模块的导出对象,最开始是引入入口文件,后面怎么引入其他依赖文件的呢?
是一个立即执行函数,参数是依赖文件
异步加载的chunk最终还是同步加载,它被添加到 modules 对象中,这样就可通过 modules[moduleId].call(module.exports, module, module.exports, webpack_require) 来同步加载chunk,也就是 foo.bundle.js(异步加载的模块) 中第一个 then 执行的内容,传入模块的路径,使用 webpack_require 进行同步加载。
懒加载代码块:原理使用script标签(可以理解为jsonp原理?),返回Promise.all(promises),再在then中使用它
异步就是:只有import的时候,会加载它的内容
ES6模块:会块标识为 ES Module,并且将函数内容定义挂在 default 上
打包最终生成的文件:最终打包出的是一个自执行函数;
自执行函数入参是一个对象modules,其key为打包的模块文件的路径,对应的value为一个函数,其内部为模块文件定义的内容;
自执行函数体返回 __webpack_require__(__webpack_require__.s = "./src/index.js") 这段代码,此处为加载入口模块并返回模块的导出对象。
function __webpack_require__(moduleId) {
}
其中包含缓存机制
var module = installedModules[moduleId] = {
i: moduleId,
l: false,
exports: {}
};
模块间的引用,webpack怎么打包的
- CommonJS 加载 CommonJS
({
"./src/foo.js":
(function(module, exports) {
module.exports = 'foo';
}),
"./src/index.js":
(function(module, exports, __webpack_require__) {
const foo = __webpack_require__("./src/foo.js");
console.log(foo)
})
})
- CommonJS 加载 ES module
({
"./src/foo.js":
(function(module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__); //将传入的对象标识上__esModule=true,即表明该模块为es6模块
__webpack_exports__["default"] = ('foo'); //将模块的内容挂在__webpack_exports__的default属性上
}),
"./src/index.js":
(function(module, exports, __webpack_require__) {
const foo = __webpack_require__("./src/foo.js");
console.log(foo)
})
})
- ES module 加载 ES module
({
"./src/foo.js":
(function(module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
__webpack_exports__["default"] = ('foo');
}),
"./src/index.js":
(function(module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
var _foo_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./src/foo.js");
console.log(_foo_js__WEBPACK_IMPORTED_MODULE_0__["default"])
//_foo_js__WEBPACK_IMPORTED_MODULE_0__用来接收导入的文件,并通过default属性获取到文件的默认导出内容
})
})
webpack_require.n则是用于获取模块的默认导出对象,兼容 CommonJS 和 ES module 两种方式。
- ES module 加载 CommonJS
({
"./src/foo.js":
(function(module, exports) {
module.exports = 'foo';
}),
"./src/index.js":
(function(module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
var _foo_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./src/foo.js");
var _foo_js__WEBPACK_IMPORTED_MODULE_0___default = __webpack_require__.n(_foo_js__WEBPACK_IMPORTED_MODULE_0__);
console.log(_foo_js__WEBPACK_IMPORTED_MODULE_0___default.a)
})
})
当入口文件index.js以es module的方式加载遵循commonjs规范的foo.js时,通过__webpack_require__加载传入的模块,将得到的模块_foo_js__WEBPACK_IMPORTED_MODULE_0__再传入__webpack_require__.n方法获取到该模块的默认导出对象。因为foo.js中的内容是通过export导出,而非export default导出。因此foo被挂在了default的一个a属性上。
异步按需加载
懒加载代码块:原理使用script标签(可以理解为jsonp原理?),返回Promise.all(promises),再在then中使用它
异步就是:只有import的时候,会加载它的内容
/**
* 该对象用于存储已经加载和正在加载中的chunks
* undefined:表示chunk未加载
* null:表示chunk预加载 / 预获取
* Promise:表示chunk正在加载中
* 0: 表示chunk已经加载了
*/
var installedChunks = {
"index": 0, // 默认入口模块已经加载完毕
};
AST
从代码生成AST的关键:词法分析和语法分析
github.com/CodeLittleP…
github.com/jamiebuilds…
我开发过的plugin
- 很多需求涉及到时间,但是因为时间有兼容性,写了plugin统一处理date格式。
- 图片资源路径统一处理:提测后(因为本地图片调试方便,也方便更改),上传本地图片到阿里云,本地图片替换成cdn链接图片,上传完之后进行删除,防止项目文件太多,减少项目总体积。
思路:获取本地文件,取出文件路径,读出文件流,上传到oss,得到链接,然后进行替换
可参考loader和plugin专题 (juejin.cn)
我开发过的loader
- 兼容以前自适应方式,改了之前的计算方式
我的总结
webpack 是一个模块打包工具
根据业务需要需要进行不同的得配置,当然配置好它,可以进行一些优化,比如减少包的大小、提高打包速度、减少代码中重复写代码、分析文件大小 (具体等着面试官来问对吧?)
webpack构建流程最终表达:
webpack的运行流程是一个串行的过程,从启动到结束会依次执行以下流程:根据配置文件初始化参数,加载所有配置的插件,执行对象的 run 方法开始执行编译;
与此同时,根据配置确定该入口文件,从入口文件出发,调用所有配置的 Loader 对模块进行翻译(翻译包括css转化成js、es6转化成es5),并找出该模块依赖的模块,递归处理;
再根据输出的源码、文件依赖关系,最终打包成一个文件。
juejin.im/post/684490… (webpack)
juejin.im/entry/68449… (webpack配置)
www.cnblogs.com/HYZhou2018/… (优化)
juejin.im/post/685457… (源码分析)
juejin.im/post/685457… (源码分析)
www.yuque.com/yijiangxili… (面试总结)
尚硅谷前端Webpack5教程(高级进阶篇)_哔哩哔哩_bilibili
Plugins | webpack 中文网 (webpackjs.com)
juejin.cn/post/713820… 重点看 juejin.cn/post/719663… juejin.cn/post/741061…