【重学webpack系列——webpack5.0】
1-15节主要讲webpack的使用,当然,建议结合
《webpack学完这些就够了》
一起学习。
从16节开始,专攻webpack原理,只有深入原理,才能学到webpack设计的精髓,从而将技术点运用到实际项目中。
可以
点击上方专栏订阅
哦。
以下是本节正文:
import()
对于webpack来说,是一个天然的分割点,也就是说,webpack碰到import()会对import()进行解析。怎么解析的,过程主要是:
面试题:webpack解析import()的原理/webpack懒加载原理/webpack异步加载原理
:
首先,
webpack
遇到
import方法
时,会将其当成一个代码分割点,也就是说碰到
import方法
了,那么就去解析
import方法
。然后,
import
引用的文件,
webpack
会将其编译成一个
jsonp
,也就是一个自执行函数,然后函数内部是引用的文件的内容,因为到时候是通过
jsonp
的方法去加载的。具体就是,
import
引用文件,会先调用
require.ensure
方法(打包的结果来看叫
require.e
),这个方法主要是构造一个
promise
,会将
resolve
,
reject
和
promise
放到一个数组中,将
promise
放到一个队列中。然后,调用
require.load
(打包结果来看叫
require.l
)方法,这个方法主要是创建一个
jsonp
,也就是创建一个
script
标签,标签的
url
就是文件加载地址,然后塞到
document.head
中,一塞进去,就会加载该文件了。加载完,就去执行这段
jsonp
,主要就是把
moduleId
和
module
内容存到modules数组中,然后再去走
webpack
内置的
require
。
webpack
内置的
require
,主要是先判断缓存,这个
moduleId
是否缓存过了,如果缓存过了,就直接返回。如果没有缓存,再继续往下走,也就是加载
module
内容,然后最终内容会挂在都
module,exports
上,返回
module.exports
就返回了引用文件的最终执行结果。
- 入口文件
import(/* webpackChunkName: "title"*/'./title').then(result => {
console.log(result);
})
- 依赖文件
module.exports = "title"
- webpack编译结果及分析(缩减过后)
var modules = {}
var cache = {}
function require(moduleId) {
var cachedModule = cache[moduleId];
if (cachedModule) {
return cachedModule.exports; // 最终返回的都是module.exports
}
var module= cache[moduleId] = {
exports: {}
}
modules[moduleId](module, module.exports, require);
return module.exports;
}
// 定义查找代码块的方法
require.find = {};
// 通过JSONP一部加载指定的代码块
require.ensure = (chunkId) => {
let promises = [];
require.find.jsonp(chunkId, promises); // 在jsonp中会创建一个promise,并且添加到promises数组中
return Promise.all(promises);
}
require.publicPath = ''; // 资源文件的访问路径,默认是空字符串,值从webpack.config.js的output的publicPath中去找
require.unionFileName = (chunkId) => { // 统一文件名
return "" + chunkId + ".js"; // title.js
}
require.load = (url) => {
let script = document.createElement('script');
script.src = url;
document.head.appendChild(script); //一旦append之后,浏览器会立刻请求脚本
}
// 已经安装或者加载好的或者加载中的chunk
var installedChunks = {
"main": 0, // key为“main”表示主入口文件,0表示ok,也就是已经加载就绪
// "title": [resolve, reject, promise]
}
require.find.jsonp = (chunkId, promises) => {
var installedChunkData;
let promise = new Promise((resolve, reject) => {
installedChunkData = installedChunks[chunkId] = [resolve, reject];
/*
这个相当于installedChunks值变成了
{
"main": 0,
"title": [resolve, reject, promise]
}
*/
})
installedChunkData[2] = promise;
promises.push(promise);
var url = require.publicPath + require.unionFileName(chunkId); //title.js
require.load(url); // 常见script,然后塞入document.head,然后会自动加载该文件
}
var webpackJsonpCallback = ([chunkIds, moreModules]) => {
var resolves = [];
for (let i = 0; i < chunkIds.length; i++) {
let chunkId = chunkIds[i];
resolves.push(installedChunks[chunkId][0]);//把chunk对应的promise的resolve方法添加到resolves数组里去
installedChunks[chunkId][0] = 0; // 表示已经添加完成
}
for (let moduleId in moreModules) {
modules[moduleId] = moreModules[moduleId];
}
while(resolves.length){
resolves.shift()(); // 让promise的resolve执行,让promise成功
}
}
// 由于打包好了依赖文件title.js是被
var chunkLoadingGlobal = self["webpackChunk_1_webpack_bundle"] = self["webpackChunk_1_webpack_bundle"] || [];
// 重写数组的push方法
chunkLoadingGlobal.push = webpackJsonpCallback;
require.ensure("title") // 先加载代码块,这里的参数"title"就是魔法字符串中的webpackChunkName的值title
.then(require.bind(require, "./src/title.js")) // require模块
.then(result => { // 获取结果
console.log(result);
})