好程式設計師web前端教程分享js中的模組化二
好程式設計師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/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 好程式設計師web前端教程分享js中的模組化一程式設計師Web前端JS
- 好程式設計師web前端教程分享js閉包程式設計師Web前端JS
- 好程式設計師web前端教程分享js模板模式程式設計師Web前端JS模式
- 好程式設計師web前端教程之前端模組化開發程式設計師Web前端
- 好程式設計師web前端教程分享js reduce方法使用教程程式設計師Web前端JS
- 好程式設計師web前端教程分享前端javascript練習題二程式設計師Web前端JavaScript
- 好程式設計師web前端教程分享前端 javascript 練習題二程式設計師Web前端JavaScript
- 好程式設計師web前端教程分享JS基礎知識程式設計師Web前端JS
- 好程式設計師web前端教程分享CSS技巧!程式設計師Web前端CSS
- 好程式設計師web前端教程分享web中CSS絕對定位程式設計師Web前端CSS
- 好程式設計師web前端教程分享Vue.js面試題程式設計師Web前端Vue.js面試題
- 好程式設計師web前端分享web測試之Js中的變數程式設計師Web前端JS變數
- 好程式設計師web前端教程分享web前端基礎知識程式設計師Web前端
- 好程式設計師web前端教程分享js檔案引用編碼方式程式設計師Web前端JS
- 好程式設計師web前端培訓分享CSS定位的教程程式設計師Web前端CSS
- 好程式設計師分享Web前端效能最佳化程式設計師Web前端
- 好程式設計師web前端分享Nodejs學習筆記之Stream模組程式設計師Web前端NodeJS筆記
- 好程式設計師web前端教程分享javascript 練習題程式設計師Web前端JavaScript
- 好程式設計師web前端教程分享JavaScript面試題程式設計師Web前端JavaScript面試題
- 好程式設計師web前端教程分享JavaScript簡寫方法程式設計師Web前端JavaScript
- 好程式設計師Web前端教程分享JavaScript開發技巧程式設計師Web前端JavaScript
- 好程式設計師Web前端教程分享Vue學習心得程式設計師Web前端Vue
- 好程式設計師web前端教程之Node.Js流程程式設計師Web前端Node.js
- 好程式設計師web前端分享js剪下板Clipboard.js 使用程式設計師Web前端JS
- 好程式設計師web前端教程:字串程式設計師Web前端字串
- 好程式設計師web前端分享:如何理解web語義化?程式設計師Web前端
- 好程式設計師web前端教程分享前端javascript練習題三程式設計師Web前端JavaScript
- 好程式設計師web前端教程分享web前端入門基礎知識程式設計師Web前端
- 好程式設計師Web前端分享前端CSS篇程式設計師Web前端CSS
- 好程式設計師web前端分享JS引擎的執行機制程式設計師Web前端JS
- 好程式設計師web前端分享js實現實戰案例程式設計師Web前端JS
- 好程式設計師web前端教程分享JavaScript的執行機制!程式設計師Web前端JavaScript
- 好程式設計師web前端分享css初始化程式碼程式設計師Web前端CSS
- 好程式設計師web前端教程分享JavaScript Math(算數)物件程式設計師Web前端JavaScript物件
- 好程式設計師web前端學習教程之Node Js流程程式設計師Web前端JS
- 好程式設計師web前端教程分享JavaScript學習筆記之Event事件二程式設計師Web前端JavaScript筆記事件
- 好程式設計師web前端教程分享前端javascript練習題之promise程式設計師Web前端JavaScriptPromise
- 好程式設計師web前端分享如何理解JS的單執行緒程式設計師Web前端JS執行緒