webpack基本原理可参考webpack系列之一总览,本文是对import方法的补充。
示例源码
// 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',
//配置入口文件,入口文件可以以对象的形式配置,也可以以数组的形式配置,后缀名可以省略
/*
* 对象形式配置入口
* */
entry:{
index:'./main' //'./index.js'
},
/*
* 数组形式配置入口
* */
//entry: [
// './main.js'
//],
//输出文件出口
output: {
/*
输出路径,在这我们要手动创建一个文件夹,名字可以自己命名,
输出的文件路径是相对于本文件的路径
* */
path: path.resolve(__dirname, './dist'), //输出路径
// publicPath: '/dist/',
filename: '[name].bundle.js' //输出文件名,文件可以自己定义,[name]的意思是与入口文件的文件对应,可以不用[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)
})
分析
以上代码,执行npm run build后,输出的js文件有两个:index.bundle.js
和0.bundle.js
,其中index.bundle.js
首先插入script中执行,0.bunlde.js
是动态引入的,其内容如下,主要包裹了c.js
:
index.bundle.js
是一个立即执行函数,入参如下图,主要包裹着main.js的内容,其中会动态引入0.bundle.js(包裹着c.js这个module):
- 如上图,153行的webpack_require.e会返回
promise
,其实现如下:
__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() { // 该方法用于检测动态加载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); // 往页面中动态加入0.bundle.js
/******/
/******/ return promise; // 返回promise
/******/ };
- 在上面的代码中,
0.bundle.js
插入script后,运行代码,会调用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,相当于执行resolve(),触发then方法(详见下一步)
/******/ }
/******/
/******/ };
其中关键是resolves.shift()()
,resolves.shift()返回的是resolve,该句相当于执行resolve(),触发then方法(详见下一步)
- 执行完webpack_require.e(0)之后,进入then方法(上一步的
resolves.shift()()
触发),执行webpack_require.bind(null, 1),其中1表示的是0.bundle.js中第二个入参数组的第二个元素(下标为1),即c.js这个module。webpack_require实现如下:
/******/ 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__);
相当于就是把c.js中export default内容赋给module.exports
,执行webpack_require,就相当于获取到了该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)
返回的是函数,该函数执行后的结果(即上一步分析的module.exports)会传给下一个then
方法。