webpack中import实现过程

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.js0.bundle.js,其中index.bundle.js首先插入script中执行,0.bunlde.js是动态引入的,其内容如下,主要包裹了c.js

0.bunlde.js

index.bundle.js是一个立即执行函数,入参如下图,主要包裹着main.js的内容,其中会动态引入0.bundle.js(包裹着c.js这个module):
index.bundle.js

  • 如上图,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方法,如下图:
    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,相当于执行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方法。

你可能感兴趣的:(webpack中import实现过程)