好程式設計師web前端教程分享js中的模組化二

好程式設計師IT發表於2019-10-25

好程式設計師web前端教程接下來降為大家繼續分享 js中的模組化 知識

4.迴圈依賴

就是a依賴b,b依賴a,對於不同的規範也有不同的結果。

4.1CommonJS

對於node,每一個模組的exports={done:false}表示一個模組有沒有載入完畢,經過一系列的載入最後全部都會變為true。 同步,從上到下,只輸出已經執行的那部分程式碼 首先,我們寫兩個js用node跑一下:

//a.js

console.log('a.js')

var b = require('./b.js')

console.log(1)

//b.js

console.log('b.js')

var a = require('./a.js')

console.log(2)

 

//根據他的特點,require一個檔案的時候,馬上執行內部的程式碼,所以相當於

console.log('a.js')

console.log('b.js')

console.log(2)

console.log(1)

//輸出是a.js、b.js、2、1

複製程式碼

加上export的時候:

//a.js

module.exports = {val:1}

var b = require('./b.js')

console.log(b.val)

module.exports = {val:2}

b.val = 3

console.log(b)

 

//b.js

module.exports = {val:1}

var a = require('./a.js')

console.log(a.val)

module.exports = {val:2}

a.val = 3

console.log(a)

 

//1.在a.js暴露出去一個物件module.exports = {val:1}

//2.require了b,來到b,執行b指令碼

//3.b的第一行,把{val:1}暴露出去,引入剛剛a暴露的{val:1},列印a.val的結果肯定是1

//4.重新暴露一次,是{val:2},然後做了一件多餘的事情,改a.val為3(反正是複製過的了怎麼改都不會影響a.js),毫無疑問列印出{ val: 3 }

//5.回到a,繼續第三行,列印b.val,因為b暴露的值是2,列印2

//6.繼續再做一件無意義的事情,列印{ val: 3 }

複製程式碼

解決辦法:程式碼合理拆分

4.2ES6模組

ES6模組是輸出值的引用,是動態引用,等到要用的時候才用,因此可以完美實現相互依賴,在相互依賴的a.mjs和b.mjs,執行a的時候,當發現import馬上進入b並執行b的程式碼。當在b發現了a的時候,已經知道從a輸入了介面來到b的,不會回到a。但是在使用的過程中需要注意,變數的順序。

如果是單純的暴露一個基本資料型別,當然會報錯not defined。 因為函式宣告會變數提升,所以我們可以改成函式宣告(不能用函式表示式)

//a.mjs

import b from './b'

console.log(b())

function a(){return 'a'}

export default a

//b.mjs

import a from './a'

console.log(a())

function b(){return 'b'}

export default b

複製程式碼

4.3 require

我們一般使用的時候,都是依賴注入,如果是有迴圈依賴,那麼可以直接利用require解決

define('a',['b'],function(b){

    //dosomething

});

define('b',['a'],function(a){

    //dosomething

});

//為了解決迴圈依賴,在迴圈依賴發生的時候,引入require:

define('a',['b','require'],function(b,require){

    //dosomething

    require('b')

});

複製程式碼

4.4 sea

迴圈依賴,一般就是這樣

//a.js

define(function(require, exports, module){

    var b = require('./b.js');

    //......

});

//b.js

define(function(require, exports, module){

    var a = require('./a.js');

    //......

});

複製程式碼

而實際上,並沒有問題,因為sea自己解決了這個問題: 一個模組有幾種狀態:

'FETCHING': 模組正在下載中 'FETCHED': 模組已下載 'SAVED': 模組資訊已儲存 'READY': 模組的依賴項都已下載,等待編譯 'COMPILING':模組正在編譯中 'COMPILED': 模組已編譯

步驟:

·  1.模組a下載並且下載完成FETCHED

·  2.編譯a模組(執行回撥函式)

·  3.遇到了依賴b,b和自身沒有迴圈依賴,a變成SAVED

·  4.模組b下載並且下載完成FETCHED

·  5.b遇到了依賴a,a是SAVED,和自身有迴圈依賴,b變成READY,編譯完成後變成COMPILED

·  6.繼續回到a,執行剩下的程式碼,如果有其他依賴繼續重複上面步驟,如果所有的依賴都是READY,a變成READY

·  7.繼續編譯,當a回撥函式部分所有的程式碼執行完畢,a變成COMPILED

對於所有的模組相互依賴的通用的辦法,將相互依賴的部分抽取出來,放在一箇中介軟體,利用釋出訂閱模式解決

5.webpack是如何處理模組化的

假設我們定義兩個js:app.js是主入口檔案,a.js、b.js是app依賴檔案,用的是COMMONJS規範 webpack首先會從入口模組app.js開始,根據引入方法require把所有的模組都讀取,然後寫在一個列表上:

var modules = {

  './b.js': generated_b,

  './a.js': generated_a,

  './app.js': generated_app

}

複製程式碼

'generated_'+name是一個IIFE,每個模組的原始碼都在裡面,不會暴露內部的變數。比如對於沒有依賴其他模組的a.js一般是這樣,沒有變化:

function generated_a(module, exports, webpack_require) {

   // ...a的全部程式碼

}

複製程式碼

對於app.js則不一樣了:

function generated_app(module, exports, webpack_require) {

  var a_imported_module = __webpack_require__('./a.js');

  var b_imported_module = __webpack_require__('./b.js');

  a_imported_module['inc']();

  b_imported_module['inc']();

}

複製程式碼

webpack_require 就是require、exports、import這些的具體實現,夠動態地載入模組a、b,並且將結果返回給app

對於webpack_require,大概是這樣的流程

var installedModules = {};//儲存已經載入完成的模組

function webpack_require(moduleId) {

  if (installedModules[moduleId]) {//如果已經載入完成直接返回

    return installedModules[moduleId].exports;

  }

  var module = installedModules[moduleId] = {//如果是第一次載入,則記錄在表上

            i: moduleId,

            l: false,//沒有下載完成

            exports: {}

  };

//在模組清單上面讀取對應的路徑所對應的檔案,將模組函式的呼叫物件繫結為module.exports,並返回

  modules[moduleId].call(module.exports, module, module.exports,__webpack_require__);

  module.l = true;//下載完成

  return module.exports;

}

複製程式碼

對於webpack打包後的檔案,是一個龐大的IIFE,他的內容大概是這樣子:

(function(modules) {

    var installedModules = {};

    function __webpack_require__(moduleId) { /*...*/}

    __webpack_require__.m = modules;//所有的檔案依賴列表

    __webpack_require__.c = installedModules;//已經下載完成的列表

    __webpack_require__.d = function(exports, name, getter) {//定義模組物件的getter函式

        if(!__webpack_require__.o(exports, name)) {

            Object.defineProperty(exports, name, {

                configurable: false,

                enumerable: true,

                get: getter

            });

        }

    };

    __webpack_require__.n = function(module) {//當和ES6模組混用的時候的處理

        var getter = module && module.__esModule ?//如果是ES6模組用module.default

            function getDefault() { return module['default']; } :

            function getModuleExports() { return module; };//是COMMONJS則繼續用module

        __webpack_require__.d(getter, 'a', getter);

        return getter;

    };

    __webpack_require__.o = function(object, property) { //判斷是否有某種屬性(如exports)

return Object.prototype.hasOwnProperty.call(object, property);

};

    __webpack_require__.p = "";//預設路徑為當前

    return __webpack_require__(__webpack_require__.s = 0);//讀取第一個模組

})

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

//IIFE第二個括號部分

([

(function(module, exports, __webpack_require__) {

var a = __webpack_require__(1);

var b = __webpack_require__(2);

//模組app程式碼

}),

 

(function(module, exports, __webpack_require__) {

//模組a程式碼

module.exports = ...

}),

(function(module, exports, __webpack_require__) {

//模組b程式碼

module.exports = ...

})

]);

複製程式碼

如果是ES6模組,處理的方法也不一樣。還是假設我們定義兩個js:app.js是主入口檔案,a.js、b.js是app依賴檔案。

(function(modules) {

//前面這段是一樣的

})

([

(function(module, __webpack_exports__, __webpack_require__) {//入口模組

    Object.defineProperty(__webpack_exports__, "__esModule", { value: true });

    var __WEBPACK_IMPORTED_MODULE_0__m__ = __webpack_require__(1);

    var __WEBPACK_IMPORTED_MODULE_1__m__ = __webpack_require__(2);

    Object(__WEBPACK_IMPORTED_MODULE_0__m__["a"])();//用object包裹著,使得其他模組export的內容即使是基本資料型別,也要讓他變成一個引用型別

    Object(__WEBPACK_IMPORTED_MODULE_1__m__["b"])();

 

}),

(function(module, __webpack_exports__, __webpack_require__) {

__webpack_exports__["a"] = a;//也就是export xxx

//....

}),

(function(module, __webpack_exports__, __webpack_require__) {

__webpack_exports__["b"] = b;

//....

})

]);

 


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69913892/viewspace-2661418/,如需轉載,請註明出處,否則將追究法律責任。

相關文章