webpack基本原理可參考webpack系列之一總覽,本文是對import方法的補充钢悲。
示例源碼
示例結(jié)構(gòu)
// package.json
{
"name": "test",
"version": "1.0.0",
"description": "",
"main": "main.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack"
},
"author": "",
"license": "ISC",
"devDependencies": {
"html-webpack-plugin": "^2.30.0",
"webpack": "^3.8.1"
}
}
// webpack.config.js
var path = require('path');
var webpack = require('webpack');
var HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
// mode: 'production',
//配置入口文件点额,入口文件可以以對象的形式配置舔株,也可以以數(shù)組的形式配置,后綴名可以省略
/*
* 對象形式配置入口
* */
entry:{
index:'./main' //'./index.js'
},
/*
* 數(shù)組形式配置入口
* */
//entry: [
// './main.js'
//],
//輸出文件出口
output: {
/*
輸出路徑,在這我們要手動創(chuàng)建一個文件夾还棱,名字可以自己命名载慈,
輸出的文件路徑是相對于本文件的路徑
* */
path: path.resolve(__dirname, './dist'), //輸出路徑
// publicPath: '/dist/',
filename: '[name].bundle.js' //輸出文件名,文件可以自己定義珍手,[name]的意思是與入口文件的文件對應(yīng)办铡,可以不用[name],
},
resolve: {
extensions: ['.js']
},
plugins: [
new HtmlWebpackPlugin({
filename: "index.html",
inject: "body"
})
]
}
// c.js
export default {
key: 'something'
}
// main.js
import('./c').then(test => {
console.log('??? ', test)
})
分析
以上代碼琳要,執(zhí)行npm run build后寡具,輸出的js文件有兩個:index.bundle.js
和0.bundle.js
,其中index.bundle.js
首先插入script中執(zhí)行稚补,0.bunlde.js
是動態(tài)引入的童叠,其內(nèi)容如下,主要包裹了c.js
:
0.bunlde.js
index.bundle.js
是一個立即執(zhí)行函數(shù)课幕,入?yún)⑷缦聢D厦坛,主要包裹著main.js的內(nèi)容,其中會動態(tài)引入0.bundle.js(包裹著c.js這個module):index.bundle.js
- 如上圖乍惊,153行的webpack_require.e會返回
promise
杜秸,其實現(xiàn)如下:
__webpack_require__.e = function requireEnsure(chunkId) {
/******/ var installedChunkData = installedChunks[chunkId];
/******/ if(installedChunkData === 0) {
/******/ return new Promise(function(resolve) { resolve(); });
/******/ }
/******/
/******/ // a Promise means "currently loading".
/******/ if(installedChunkData) {
/******/ return installedChunkData[2];
/******/ }
/******/
/******/ // setup Promise in chunk cache
/******/ var promise = new Promise(function(resolve, reject) {
/******/ installedChunkData = installedChunks[chunkId] = [resolve, reject]; // 此處把resove和reject保存到installedChunkData備用
/******/ });
/******/ installedChunkData[2] = promise;
/******/
/******/ // start chunk loading
/******/ var head = document.getElementsByTagName('head')[0];
/******/ var script = document.createElement('script');
/******/ script.type = "text/javascript";
/******/ script.charset = 'utf-8';
/******/ script.async = true;
/******/ script.timeout = 120000;
/******/
/******/ if (__webpack_require__.nc) {
/******/ script.setAttribute("nonce", __webpack_require__.nc);
/******/ }
/******/ script.src = __webpack_require__.p + "" + chunkId + ".bundle.js";
/******/ var timeout = setTimeout(onScriptComplete, 120000);
/******/ script.onerror = script.onload = onScriptComplete;
/******/ function onScriptComplete() { // 該方法用于檢測動態(tài)加載js后的一些檢測
/******/ // avoid mem leaks in IE.
/******/ script.onerror = script.onload = null;
/******/ clearTimeout(timeout);
/******/ var chunk = installedChunks[chunkId];
/******/ if(chunk !== 0) {
/******/ if(chunk) {
/******/ chunk[1](new Error('Loading chunk ' + chunkId + ' failed.'));
/******/ }
/******/ installedChunks[chunkId] = undefined;
/******/ }
/******/ };
/******/ head.appendChild(script); // 往頁面中動態(tài)加入0.bundle.js
/******/
/******/ return promise; // 返回promise
/******/ };
- 在上面的代碼中,
0.bundle.js
插入script后润绎,運行代碼撬碟,會調(diào)用webpackJsonp方法,如下圖:
0.bundle.js - webpackJsonp
webpackJsonp在index.bundle.js
中定義:
/******/ window["webpackJsonp"] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) {
/******/ // add "moreModules" to the modules object,
/******/ // then flag all "chunkIds" as loaded and fire callback
/******/ var moduleId, chunkId, i = 0, resolves = [], result;
/******/ for(;i < chunkIds.length; i++) {
/******/ chunkId = chunkIds[i];
/******/ if(installedChunks[chunkId]) {
/******/ resolves.push(installedChunks[chunkId][0]);
/******/ }
/******/ installedChunks[chunkId] = 0;
/******/ }
/******/ for(moduleId in moreModules) {
/******/ if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
/******/ modules[moduleId] = moreModules[moduleId];
/******/ }
/******/ }
/******/ if(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules, executeModules);
/******/ while(resolves.length) {
/******/ resolves.shift()(); // 第一個元素是resolve莉撇,相當(dāng)于執(zhí)行resolve()呢蛤,觸發(fā)then方法(詳見下一步)
/******/ }
/******/
/******/ };
其中關(guān)鍵是resolves.shift()()
,resolves.shift()返回的是resolve稼钩,該句相當(dāng)于執(zhí)行resolve()顾稀,觸發(fā)then方法(詳見下一步)
- 執(zhí)行完webpack_require.e(0)之后达罗,進入then方法(上一步的
resolves.shift()()
觸發(fā))坝撑,執(zhí)行webpack_require.bind(null, 1),其中1表示的是0.bundle.js中第二個入?yún)?shù)組的第二個元素(下標(biāo)為1)粮揉,即c.js這個module巡李。webpack_require實現(xiàn)如下:
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
modules[moduleId]表示的就是c.js module,那么modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
相當(dāng)于就是把c.js中export default內(nèi)容賦給module.exports
扶认,執(zhí)行webpack_require侨拦,就相當(dāng)于獲取到了該module.exports
。
- 回到
index.bunlde.js
第153行代碼:
__webpack_require__.e/* import() */(0).then(__webpack_require__.bind(null, 1)).then(test => {
console.log('??? ', test)
})
__webpack_require__.bind(null, 1)
返回的是函數(shù)辐宾,該函數(shù)執(zhí)行后的結(jié)果(即上一步分析的module.exports)會傳給下一個then
方法狱从。