webpack加载逻辑细分

一、分类

  • 当前文件模块类型:esModule / commonJs
  • 加载模块方式:import / import() / require() / require(表达式)

1、基本逻辑

// 导入一个模块
__webpack_require__(模块id)

// => 会执行下面代码
// Execute the module function
__webpack_modules__[moduleId](module, module.exports, __webpack_require__);

return module.exports;

// 说明:
// 1、执行通用加载函数:__webpack_require__
// 2、如果该模块没有加载过,则执行该模块,然后导出该模块的exports
复制代码

2、当前文件模块类型是commonJs

// 在webpack会转化成:

// 当前模块:
module.exports = ...
复制代码

3、当前文件模块类型是esModule

// 源码
export const a = {
    x:1,
    y:[1,2]
}
export default 13;

=> 在webpack会转化成:
/* 1 */
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "a": () => (/* binding */ a),
/* harmony export */   "default": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
const a = {
    x:1,
    y:[1,2]
}
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (13);

/***/ })

// 分析:
// 1、会执行__webpack_require__.r:把exports标记__esModule:true
// 2、会执行 __webpack_require__.d。对导出的项,做getter处理,没有seter,也就是导出的模块中不能修改导出项
复制代码

4、导入模块方式为import

// 案例一:import + 解析
import {a} from './'
console.log(a)
// => 在webpack中会转化为
var _ref1_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
console.log(_ref1_js__WEBPACK_IMPORTED_MODULE_0__.a)
复制代码
// 案例二:import + default
import de from './ref1.js'
console.log(de)
// => 在webpack中会转化为
var _ref1_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
console.log(_ref1_js__WEBPACK_IMPORTED_MODULE_0__.default)
复制代码
// 总结:
// 也就是都是通过__webpack_require__(模块id)来加载模块,返回export{}对象
// 如果是{}形式解析,则为importObj.xx形式
// 如果是导入default,则为importObj.default形式
复制代码

5、导入模块方式为import()

// 案例一、如果该模块已经不是已经在modules中了。则
import ref1 from './ref1.js'
import('./ref1.js').then((res)=>{
    console.log('import',res)
})
console.log(ref1)

=> 在webpack会转化为:

__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _ref1_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
// const ref = require('./ref.json')

Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, 1)).then((res)=>{
    console.log('import',res)
})
console.log(_ref1_js__WEBPACK_IMPORTED_MODULE_0__.default)

// 因为已经有了ref1模块,所以这边没有使用异步再去加载该模块
// 因为import()是动态,是Promise。所以这边模拟了这个过程
// 补充:其实通过前面已经知道,导出的是{a:default:}类似结构,所以,如果需要{a,default} = res来获取到处项
复制代码
// 案例二、纯粹异步加载
import('./ref1.js').then((res)=>{
    console.log('import',res)
})

=> 在webpack会转化为:
__webpack_require__.e(/* import() */ 1).then(__webpack_require__.bind(__webpack_require__, 1)).then((res)=>{
    console.log('import',res)
})

// __webpack_require__.e 是异步加载模块
// 相当于amd类似
复制代码

6、导入模式为import()表达式

// 和下面的require()表达式是一个效果
// 注意:都需要提供一些上下文给系统,不然它也茫然啊,不知道要生成什么map
// 如:require(window.path)你来生成map嘛,最起码要(`./${path}`)类似这种提供一些信息
复制代码

7、导入模式为require()

// 源码:
const ref = require('./ref.json')

=> 在webpack会转化为:
const ref = __webpack_require__(1)
复制代码

8、导入模式为require(表达式):模块上下文(content)

// 源码:
const ref = require('./ref.json')
=>webpack转化后
const ref = __webpack_require__(1)(`./${name}`)

// 具体下面有补充
复制代码

官方文档:webpack.docschina.org/guides/depe…

一、带表达式的require理解

// 案例
const name = "ref.json"
const ref = require(`./${name}`)
复制代码
构建后代码
// module:0
const name = "ref.json"
const ref = __webpack_require__(1)(`./${name}`)

// 也就是这里变成()()结构
// 也就是加载模块1,然后传变量来决定最终是加载哪个模块
// 即:模块1是一个多个模块的一个映射
复制代码
// module:1
// 一个预期符合该表达式的模块映射
var map = {
	"./": 2,
	"./index": 2,
	"./index.js": 2,
	"./index1": 5,
	"./index1.js": 5,
	"./ref": 4,
	"./ref.json": 4,
	"./requireTest": 0,
	"./requireTest.js": 0
};
//...
function webpackContext(req) {
	// ...
	return __webpack_require__(id);
}
// 导出的不是一个模块,而是一个函数
module.exports = webpackContext;
复制代码

即:require如果是表达式,则编译时返回的是函数,然后运行时映射到真正的模块。

二、require.content的使用

  • 理解:对比带表达式的require是系统判断生成的上下文,这里是我们来指定解析上下文
  • 这个是webpack提供的api
// 上面是上下文的概念理解,这里贴上这部分完整代码
var map = {
	"./": 2,
	"./index": 2,
	"./index.js": 2,
	"./index1": 5,
	"./index1.js": 5,
	"./ref": 4,
	"./ref.json": 4,
	"./requireTest": 0,
	"./requireTest.js": 0
};


function webpackContext(req) {
	var id = webpackContextResolve(req);
	return __webpack_require__(id);
}
function webpackContextResolve(req) {
	if(!__webpack_require__.o(map, req)) {
		var e = new Error("Cannot find module '" + req + "'");
		e.code = 'MODULE_NOT_FOUND';
		throw e;
	}
	return map[req];
}
webpackContext.keys = function webpackContextKeys() {
	return Object.keys(map);
};
webpackContext.resolve = webpackContextResolve;
module.exports = webpackContext;
webpackContext.id = 1;
复制代码
  • 返回的是函数,而不是模块;
// webpackContext
webpackContext('./ref') // 加载ref模块
webpackContext.resolve('./ref') // 输出./ref路径匹配到的模块id
webpackContext.keys() // 输出map的keys
webpackContext.id   // 输出当前模块的模块id
复制代码

image.png

总结

  • 模块编译:如果是esModule会执行下面
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "a": () => (/* binding */ a),
/* harmony export */   "default": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
const a = {
    x:1,
    y:[1,2]
}
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (13);
复制代码
  • 模块编译:如果是commonJs不会有额外处理
  • 我们来观察esModule和commmonJs的区别
1、esModule是对导出项的引用
即:
import {a} from './ref1.js'
console.log(a)
===>
var _ref1_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
console.log(_ref1_js__WEBPACK_IMPORTED_MODULE_0__.a)

// 拿a来分析
模块中的a => 模块中导出:对exports设置a且设置getter:()=>(a) => 使用:importObj.a
// 1、设置getter:让其处于编译时候建立联系
// 2、没有setter:让其只读
// 3、模块中的a ==getter==> 使用中的a:导入项的引用

2、commonJs是对导出项的浅拷贝
const ref = require('./ref1.js')
console.log(ref.a)
=>
const ref = __webpack_require__(1)
console.log(ref.a)
// 1、导出对象{} ===> ref:也就是到处对象是导入对象的引用


整体理解:importrequire的升级版:import是导出对象的项的引用(对比require是不是要更细致),require是导出对象的引用
// import是require的升级版
1require是导出对象的引用,import是导出对象的项的引用
2import通过getter实现编译时建立联系,而require是运行时建立联系(import会变量提升,在编译阶段就有完整的关系图,于是在使用导出对象使用时,使用getter完全ok。而require只是运行时加载,也就是编辑阶段没有完整的关系图,进而会出现循环引用问题)
3import通过设置getter没有设置setter,进而控制该导出项不能修改,保证了原有模块的稳健。而require是导出对象的引用,而没有其它约束,进而具有破坏性,需要人为注意好不去破坏原有模块。
复制代码
  • 导入模块逻辑-静态
__webpack_require__
复制代码
  • 导入模块逻辑-动态:表达式
import(表达式)
require(表达式)

// 知识点:编译上下文content
// import()异步
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享