zoukankan      html  css  js  c++  java
  • webpack4.X源码解析之esModule打包分析

    入口文件index.js采用esModule方式导入模块文件,非入口文件index1.js分别采用CommonJS和esmodule规范进行导出。

    首先,init之后创建一个简单的webpack基本的配置,在src目录下创建两个js文件(一个主入口文件和一个非主入口文件)和一个html文件,package.json,webpack.config.js公共部分代码如下:(除JS之外的部分)

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="utf-8">
            <title></title>
        </head>
        <body>
        </body>
    </html>
    {
      "name": "mywebpack",
      "version": "1.0.0",
      "main": "index.js",
      "license": "MIT",
      "devDependencies": {
        "html-webpack-plugin": "4.5.0",
        "webpack": "4.44.2",
        "webpack-cli": "3.3.12"
      }
    }
    const path = require('path')
    const HtmlWebpackPlugin = require('html-webpack-plugin')
    
    module.exports = {
      devtool: 'none',
      mode: 'development',
      entry: './src/index.js',
      output: {
        filename: 'built.js',
        path: path.resolve('dist')
      },
      plugins: [
        new HtmlWebpackPlugin({
          template: './src/index.html'
        })
      ]
    }

    运行yarn webpack打包之后生成的built.js文件

    1 打包之后的js文件是一个匿名函数自调用和一个当前函数调用时传入的一个对象

    2 这个对象暂且称之为模块定义,它是一个键值对

    3 这个对象的键名是当前加载模块的文件名和某个目录的拼接

    4 这个键的值就是一个函数,和node.js模块加载有一些类似,会将被加载模块中的内容包裹在一个函数中

    5 这个函数在将来某个时间点上会被调用,同时接收到一定的参数,利用这些参数可以实现模块的加载操作

    CommonJS导出

    //采用esmodule导入
    import name from './index1.js'
    console.log('我是入口js文件')
    console.log(name)
    //index1.js 采用CommonJS导出
    module.exports="我不是入口文件"
     (function(modules) { // webpackBootstrap
         // 定义对象用于将来缓存被加载过的模块
         var installedModules = {};
         // The require function
         function __webpack_require__(moduleId) {
             //判断当前缓存中是否存在要被加载的模块内容,如果存在则直接返回
             if(installedModules[moduleId]) {
                 return installedModules[moduleId].exports;
             }
              //如果当前缓存中不存在则需要我们自己定义{} 执行被导入的模块内容加载
             var module = installedModules[moduleId] = {
                 i: moduleId,
                 l: false,
                 exports: {}
             };
             //调用当前 moduleId 对应的函数,然后完成内容的加载
             modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
             // 当上述的方法调用完成之后,我们就可以修改 l 的值用于表示当前模块内容已经加载完成了
             module.l = true;
             // 加载工作完成之后,要将拿回来的内容返回至调用的位置
             return module.exports;
         }
         // 将模块定义保存一份,通过m属性挂载到自定义方法
         __webpack_require__.m = modules;
         // 保留缓存
         __webpack_require__.c = installedModules;
         //d方法 定义 d 方法用于在对象的身上添加指定的属性,同时给该属性提供一个 getter
         __webpack_require__.d = function(exports, name, getter) {
            //定义 o 方法用于判断对象的身上是否存在指定的属性
             if(!__webpack_require__.o(exports, name)) {
                //添加a属性getter方法 并且可枚举 
                 Object.defineProperty(exports, name, { enumerable: true, get: getter });
             }
         };
        //O方法 定义 o 方法用于判断对象的身上是否存在指定的属性
        __webpack_require__.o = function(object, property) {
            return Object.prototype.hasOwnProperty.call(object, property); 
        };
         // 定义 r 方法用于标识当前模块是 es6 类型
         __webpack_require__.r = function(exports) {
             if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
                 Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
             }
             Object.defineProperty(exports, '__esModule', { value: true });
         };
         // create a fake namespace object
         // mode & 1: value is a module id, require it
         // mode & 2: merge all properties of value into the ns
         // mode & 4: return value when already ns object
         // mode & 8|1: behave like require
         __webpack_require__.t = function(value, mode) {
             if(mode & 1) value = __webpack_require__(value);
             if(mode & 8) return value;
             if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
             var ns = Object.create(null);
             __webpack_require__.r(ns);
             Object.defineProperty(ns, 'default', { enumerable: true, value: value });
             if(mode & 2 && typeof value != 'string') 
          for(var key in value) __webpack_require__.d(ns, key,
          function(key) { return value[key]; }.bind(null, key)); return ns; }; //n方法 用于设置具体的 getter __webpack_require__.n = function(module) { //module里面是下面传递过来的字符串'我不是入口文件' var getter = module && module.__esModule ? function getDefault() { return module['default']; } : //没有__esModule所以 return module function getModuleExports() { return module; }; //调用d方法 把getter传入 getter就是上文中的getModuleExports函数 __webpack_require__.d(getter, 'a', getter); //返回getter方法 内部添加了一个a属性,值为'我不是入口文件' return getter; }; // 用于保存资源访问路径 __webpack_require__.p = ""; // 调用 __webpack_require__ 方法执行模块导入与加载操作 return __webpack_require__(__webpack_require__.s = "./src/index.js"); }) /************************************************************************/ ({ "./src/index.js": (function(module, __webpack_exports__, __webpack_require__) { "use strict"; //由于是esModule导入的,所以添加标记表明为esmodule __webpack_require__.r(__webpack_exports__); //webpack自定义的名字,调用了webpack内部的__webpack_require__方法 var _index1_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index1.js */ "./src/index1.js"); //把index1.js中的module.exports导出的“我不是入口文件”传递给n方法 var _index1_js__WEBPACK_IMPORTED_MODULE_0___default = __webpack_require__.n(_index1_js__WEBPACK_IMPORTED_MODULE_0__); console.log('我是入口js文件') //默认打印的为挂载的default的a(get)方法 console.log(_index1_js__WEBPACK_IMPORTED_MODULE_0___default.a) }), "./src/index1.js": (function(module, exports) { //由于是commonJS规范导出,所以没有其他的操作 module.exports="我不是入口文件" }) });

    针对于上述的代码主入口文件采用esmodule导入先用r方法标记,调用__webpack_require__方法,把index1.js中的"我不是入口文件",传递给n方法,在内部判断index1.js是否为esmodule规范导出,如果为cmj模式导出则var 

    getter=function getModuleExports() { return module; },之后传递getter到d方法,在d方法内部添加a属性getter方法 并且可枚举,在n方法内部return getter,返回getter方法 内部添加了一个a属性,值为'我不是入口文件'。

    esModule导出

    //采用esmodule导入
    import index1default,{name} from './index1.js'
    console.log('我是入口js文件')
    console.log(index1default,'----->',name)
    //index1.js 采用esModule导出
    export default "我不是入口文件"
    export const name="张三"
     (function(modules) { // webpackBootstrap
         // The module cache
         var installedModules = {};
         // The require function
         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;
         }
         // expose the modules object (__webpack_modules__)
         __webpack_require__.m = modules;
    
         // expose the module cache
         __webpack_require__.c = installedModules;
    
         // define getter function for harmony exports
         __webpack_require__.d = function(exports, name, getter) {
             if(!__webpack_require__.o(exports, name)) {
                 Object.defineProperty(exports, name, { enumerable: true, get: getter });
             }
         };
    
         // define __esModule on exports
         __webpack_require__.r = function(exports) {
             if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
                 Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
             }
             Object.defineProperty(exports, '__esModule', { value: true });
         };
    
         // create a fake namespace object
         // mode & 1: value is a module id, require it
         // mode & 2: merge all properties of value into the ns
         // mode & 4: return value when already ns object
         // mode & 8|1: behave like require
         __webpack_require__.t = function(value, mode) {
             if(mode & 1) value = __webpack_require__(value);
             if(mode & 8) return value;
             if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
             var ns = Object.create(null);
             __webpack_require__.r(ns);
             Object.defineProperty(ns, 'default', { enumerable: true, value: value });
             if(mode & 2 && typeof value != 'string') 
            for(var key in value) __webpack_require__.d(ns, key, function(key) {
                return value[key]; }.bind(null, key)
            );
             return ns;
         };
    
         // getDefaultExport function for compatibility with non-harmony modules
         __webpack_require__.n = function(module) {
             var getter = module && module.__esModule ?
                 function getDefault() { return module['default']; } :
                 function getModuleExports() { return module; };
                 __webpack_require__.d(getter, 'a', getter);
                return getter;
         };
         // Object.prototype.hasOwnProperty.call
         __webpack_require__.o = function(object, property) {
            return Object.prototype.hasOwnProperty.call(object, property); 
        };
         // __webpack_public_path__
         __webpack_require__.p = "";
         // Load entry module and return exports
         return __webpack_require__(__webpack_require__.s = "./src/index.js");
     })
    /************************************************************************/
     ({
     "./src/index.js":
     (function(module, __webpack_exports__, __webpack_require__) {
    "use strict";
    //添加r方法标记为node_modules打包 __webpack_require__.r(__webpack_exports__);
    //调用webpack调用的方法__webpack_require__,传递参数./src/index1.js
    var _index1_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index1.js */ "./src/index1.js"); //采用esmodule导入 console.log('我是入口js文件') console.log( _index1_js__WEBPACK_IMPORTED_MODULE_0__["default"], '----->', _index1_js__WEBPACK_IMPORTED_MODULE_0__["name"]) }), "./src/index1.js": (function(module, __webpack_exports__, __webpack_require__) { "use strict"; //r方法标记为esmodule __webpack_require__.r(__webpack_exports__); //通过d方法挂载name属性值暂时为undefined,当代码运行到下面的const name="张三"时 name的值为张三 __webpack_require__.d(__webpack_exports__, "name", function() { return name; });
    //赋值一个default属性值为我不是入口文件 __webpack_exports__[
    "default"] = ("我不是入口文件");
    //赋值name属性值为张三 const name
    ="张三" }) });

    针对于上述的代码与commonjs规范导出不同的是,index1.js文件导出用的esmoudle规范,首先通过r方法标记esmodule,然后通过d方法挂载name属性导出后面的name值为张三。

    /*************************************************************************/

    心若计较,处处都是怨言;心若放宽,时时都是晴天。

    ---感谢阅读,o(* ̄︶ ̄*)o开心每一天!
  • 相关阅读:
    (转载)SAPI 包含sphelper.h编译错误解决方案
    C++11标准的智能指针、野指针、内存泄露的理解(日后还会补充,先浅谈自己的理解)
    504. Base 7(LeetCode)
    242. Valid Anagram(LeetCode)
    169. Majority Element(LeetCode)
    100. Same Tree(LeetCode)
    171. Excel Sheet Column Number(LeetCode)
    168. Excel Sheet Column Title(LeetCode)
    122.Best Time to Buy and Sell Stock II(LeetCode)
    404. Sum of Left Leaves(LeetCode)
  • 原文地址:https://www.cnblogs.com/websiteblogs/p/14409719.html
Copyright © 2011-2022 走看看