JavaScript中使用import 和require打包後實現原理

前言:

以前使用ES6寫代碼,webpack打包後上線,一點問題沒有,也看過打包後的代碼,長的很亂,也沒敢看看咋回事,加載後就是能運行!javascript

今天經過個例子理解一下打包前,和打包後的代碼!html

  1. 建立文件夾,並在裏面建立兩個文件夾,app文件夾和public文件夾,app文件夾用來存放原始數據和咱們將寫的JavaScript模塊,public文件夾用來存放以後供瀏覽器讀取的文件(包括使用webpack打包生成的js文件以及一個index.html文件)。接下來咱們再建立三個文件:
  • index.html --放在public文件夾中;
  • Greeter.js-- 放在app文件夾中;
  • main.js-- 放在app文件夾中;

此時項目結構以下圖所示java



項目結構

咱們在index.html文件中寫入最基礎的html代碼,它在這裏目的在於引入打包後的js文件(這裏咱們先把以後打包後的js文件命名爲bundle.js,以後咱們還會詳細講述)。webpack

<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>sample Project</title>
  </head>
  <body>
    <div id='root'>
    </div>
    <script src="bundle.js"></script>
  </body>
</html>
複製代碼

咱們在Greeter.js中定義一個返回包含問候信息的html元素的函數,並依據CommonJS規範導出這個函數爲一個模塊:web

// Greeter.js
exports.greet= function() {
  var greet = document.createElement('div');
  greet.textContent = "Hi there and greetings!";
  return greet;
};

exports.USER_INFO = "userInfo";
複製代碼

main.js文件中咱們寫入下述代碼,用以把Greeter模塊返回的節點插入頁面。typescript

//main.js 
 let {greeter,USER_INFO} =require('./Greeter.js');
console.log(USER_INFO);
document.querySelector("#root").appendChild(greeter());複製代碼

使用webpack打包後:數組

(function(modules){//=====方法體=========// 
    var installedModules = {};//緩存加載後的模塊
   function __webpack_require__(moduleId) {
        if (installedModules[moduleId]) {
            return installedModules[moduleId].exports;
        }
        var module = installedModules[moduleId] = {
            i: moduleId,
            l: false,
            exports: {}
        };
        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
        module.l = true;
        return module.exports;
    }
    __webpack_require__.m = modules;
    __webpack_require__.c = installedModules;
    __webpack_require__.d = function(exports, name, getter) {
        if (!__webpack_require__.o(exports, name)) {
            Object.defineProperty(exports, name, {
                configurable: false, 
                enumerable: true,
                get: getter 
            });    
        }   
    };
    __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;
    };
    __webpack_require__.o = function(object, property) {
        return Object.prototype.hasOwnProperty.call(object, property);
    };
    __webpack_require__.p = "";
    return __webpack_require__(__webpack_require__.s = 0);
})( 
//============方法參數========//
//這是自執行函數的方法傳參,參數是一個數字包含 main.js模塊,Greeter.js模塊
[
(function(module, exports, __webpack_require__) {
    //這裏是main.js模塊
    let {
        greeter,
        USER_INFO
    } = __webpack_require__(1);
    console.log(USER_INFO);
    document.querySelector("#root").appendChild(greeter());
}),

(function(module, exports) {
    // //這裏是Greeter.js模塊
    exports.greet = function() {
        var greet = document.createElement('div');
        greet.textContent = "Hi there and greetings!";
        return greet;
    };
    exports.USER_INFO = "userInfo";
})
]
);複製代碼

首先最爲層是包裹着當即執行函數(加粗的內容),參數是一個數組,數組中每一項是對應的模塊,每一個模塊包裹在(function(module, exports, __webpack_require__) {//模塊內容});瀏覽器

函數自執行時定義 緩存

var installedModules = {};//緩存加載後的模塊,再次引入時直接獲取
複製代碼
定義 function __webpack_require__(){}
掛載靜態屬性和方法
    __webpack_require__.m = modules;

    __webpack_require__.c = installedModules;

    __webpack_require__.d = function(exports, name, getter) {
        //...  
    };

    __webpack_require__.n = function(module) {
        //...
   };

    __webpack_require__.o = function(object, property) {
        //...
    };

    __webpack_require__.p = "";

複製代碼

而後執行:bash

return __webpack_require__(__webpack_require__.s = 0);複製代碼

  • 首先判斷緩存 installedModules中沒有,建立

var module = installedModules[moduleId] = {
            i: moduleId,
            l: false,
            exports: {}
        };
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);複製代碼

  • 執行modules[0].call(...),即執行傳入的main.js模塊

    (function(module, exports, __webpack_require__) {
        //這裏是main.js模塊
        let {
            greeter,
            USER_INFO
        } = __webpack_require__(1);
        console.log(USER_INFO);
        document.querySelector("#root").appendChild(greeter());
    })複製代碼

  • 執行時獲取 __webpack_require__(1),即執行傳入的Greeter.js模塊
var module = installedModules[moduleId] = {
            i: moduleId,
            l: false,
            exports: {}
        };
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 複製代碼
  • 執行完Greeter模塊,return 
    exports={
        greet:function() {
            var greet = document.createElement('div');
            greet.textContent = "Hi there and greetings!";
            return greet;
        },
       USER_INFO:"userInfo"
    }複製代碼

接下來將require改成import看看打包後的如何實現

咱們將Greeter.js的信息改成以下

// Greeter.js
export default function() {
  var greet = document.createElement('div');
  greet.textContent = "Hi there and greetings!";
  return greet;
};

export const USER_INFO = "userInfo";
複製代碼

main.js文件中的代碼,修改後

//main.js 
import greet,{USER_INFO} from './Greeter.js';
console.log(USER_INFO);
document.querySelector("#root").appendChild(greet());複製代碼

而後咱們再次打包:

(function(modules) {
   var installedModules = {};
   function __webpack_require__(moduleId) {
       if (installedModules[moduleId]) {
           return installedModules[moduleId].exports;
       }
       var module = installedModules[moduleId] = {
           i: moduleId,
           l: false,
           exports: {}
       };
       modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
       module.l = true;
       return module.exports;
   }
   __webpack_require__.m = modules;
   __webpack_require__.c = installedModules;
   __webpack_require__.d = function(exports, name, getter) {
       if (!__webpack_require__.o(exports, name)) {
           Object.defineProperty(exports, name, {
               configurable: false,
               enumerable: true,
               get: getter
           });
       }
   };

   __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;
   };

   __webpack_require__.o = function(object, property) {
       return Object.prototype.hasOwnProperty.call(object, property);
   };

   __webpack_require__.p = "";
  return __webpack_require__(__webpack_require__.s = 0);
})(

[
(function(module, __webpack_exports__, __webpack_require__) {
   "use strict";
   Object.defineProperty(__webpack_exports__, "__esModule", {
       value: true
   });
   var __WEBPACK_IMPORTED_MODULE_0__Greeter_js__ = __webpack_require__(1);
   //main.js
   console.log(__WEBPACK_IMPORTED_MODULE_0__Greeter_js__["a"]);
   document.querySelector("#root").appendChild(Object(__WEBPACK_IMPORTED_MODULE_0__Greeter_js__["b"])());
}),

(function(module, __webpack_exports__, __webpack_require__) {

   "use strict";
   __webpack_exports__["b"] = (function() {
       var greet = document.createElement('div');
       greet.textContent = "Hi there and greetings!";
       return greet;
   });;

   const USER_INFO = "userInfo";
   __webpack_exports__["a"] = USER_INFO;
})]

);複製代碼

能夠發現執行過程和上面require同樣,先 __webpack_require__(__webpack_require__.s = 0)

即執行main.js模塊:

function(module, __webpack_exports__, __webpack_require__) {
   "use strict";
   Object.defineProperty(__webpack_exports__, "__esModule", {
       value: true
   });
   var __WEBPACK_IMPORTED_MODULE_0__Greeter_js__ = __webpack_require__(1);
   //main.js
   console.log(__WEBPACK_IMPORTED_MODULE_0__Greeter_js__["a"]);
   document.querySelector("#root").appendChild(Object(__WEBPACK_IMPORTED_MODULE_0__Greeter_js__["b"])());
}複製代碼

區別:

與require不一樣的是新增__esModule屬性,代表是js export定義的模塊,引入方式也是不一樣的

require引入的直接是整個module.exports對象,每次都是先總體引入對象,定義變量獲取裏面的屬性

import 直接獲取__WEBPACK_IMPORTED_MODULE_0__Greeter_js__["a"]屬性值

相同點:

編譯前:都是先將沒給模塊頭尾包裝,進行做用域隔離,經過傳入module,module.exports,require作爲參數調用獲取模塊,並緩存模塊

(function(module, __webpack_exports__, __webpack_require__) {
    //模塊代碼
 })複製代碼
相關文章
相關標籤/搜索