背景:今天使用 require 加载图片的时候,发现 require 引入的资源可以直接使用了,不需要取 default 属性了,之前 webpack4 的时候还需要取一下 default 属性,后面看了下是 webpack内部做出了改变
webpack5
// index.tsx
const HeaderImg = require("../../img/headerImg.png");
console.log(HeaderImg);
// 结果 -> /static/media/headerImg.[hash].png -> 这里直接输出对应的图片 路径
webpack4
// index.tsx
const HeaderImg = require("../../img/headerImg.png");
console.log(HeaderImg);
// 结果 -> { default: "/static/media/headerImg/[hash].png", __esModule: true } -> 这里直接输出对应的图片 路径
- webpack4、webpack5 使用
require
加载图片的差异,在于 webpack4 使用图片需要加一个 default 属性,不然没办法访问到图片的 path - webpack4 打包的结果有一个
__esModule
这个可以先不管,这个属性表示index.tsx
这个模块是一个 esm 规范的 js文件 - 通过打包结果差异可以看到,当我们使用 webpack5 并且使用 require 加载图片 可以直接使用,不需要在加一个 default 属性进行访问
打包结果(webpack5)
打包结果(webpack4)
- 可以看到 webpack4 打包之后,针对图片的导出还是使用 在
__webpack_exports__["default"]
上面进行导出。 当我们需要访问该图片的时候 需要加一个 default 属性 才能访问成功 - webpack5 直接在
module.exports
上进行导出
两个 webpack版本 内部发生了什么差异 (下面展示 webpack 大改加载和运行原理)
// webpack 打包核心的加载逻辑
function __webpack_require__(moduleId) {
if (installedModules[moduleId]) {
return installedModules[moduleId].exports;
}
var module = (installedModules[moduleId] = {
i: moduleId,
l: false,
exports: {},
hot: hotCreateModule(moduleId),
parents:
((hotCurrentParentsTemp = hotCurrentParents),
(hotCurrentParents = []),
hotCurrentParentsTemp),
children: [],
});
modules[moduleId].call(
module.exports,
module,
module.exports,
hotCreateRequire(moduleId)
);
module.l = true;
return module.exports;
}
// webpack 加载 模块
// 核心的加载 通过 取到 模块的相对位置 做为 对象的 key
// 模块的内容 做为 函数的内容
// 最终通过 module.export 暴露出去
__webpack_require__({
"./src/index1.js": function () {
var img = __webpack_require__("./src/img/headerImg.png");
console.log(img);
},
"./src/img/headerImg.png": (module, exports, __webpack_require__) => {
module.exports =
__webpack_require__.p + "static/media/headerImg.a862f4874931910aad4b.png";
},
});
分析加载过程(webpack5)
- webpack 打包结果就是 实现webpack自己的 commonjs规范, 并且在过程中抹平 esm & commonjs 差异性。
- 核心的加载函数就是
__webpack_require__
, 我们写的import
、require
最终都被编译为__webpack_require__
, 最终形成一个自执行闭包函数。 - 加载逻辑: 本质上就是从打包好的所有的静态资源对象里面不停的进行加载(这里静态资源对象就是 ->
{ './src/index1.js': function(){}, ./src/index2.js': function(){} }
类似这种对象, 本质上我们所有的模块都是这种格式[模块相对路径]: function(){ [模块业务逻辑] }
, 最终多个模块组成一个大的对象,被不停的进行加载 ) - 每次使用 webpack_require 加载一个模块,都会判断有没有缓存
- 没有缓存,最终导出需要 被 加载模块的
module.export
引用 - 这里在 初始化进行了图片的加载,在打包好的 [bundle].js 里面 拿到了 图片相对项目的路径
./src/img/headerImg.png
当作 moduleId 传入__webpack_require__
函数, 最终导出 module.exports 的引用,在这里其实就是__webpack_require__.p + "static/media/headerImg.[hash].png"
; - 相当于我们直接把 图片对应的 地址暴露出去了
分析加载过程(webpack4)
- 核心的加载函数与 webpack5 没有本质上差异,关键的差异在于图片模块的导出, webpack5 图片的导出直接是
module.export = [图片的path]
, webpack4 的导出是module.export.default = [图片的path]
, 两者之间差了一个 default 属性。 - 所以当我们使用 webpack4 并且使用 require 加载图片,需要加一个 default 属性才能访问到真实的图片 path
- 注意,当我们使用 import 导入图片,webpack4 与 webpack5 使用上没有任何差异。但是内部打包导出逻辑也不太一样,
- 当使用 import 导入图片,webpack4 会在引用的地方自动取 导出对象的 default 属性 (见下图, 代码里面写的是
console.log(HeaderImg)
, webpack编译会自动取default
属性) - 当使用 import 导入图片,webpack5 导出对象 依然是
module.export = [图片的path]
, 当图片代码被加载时直接 导出了 module.export 的值