日本电影一区二区_日本va欧美va精品发布_日本黄h兄妹h动漫一区二区三区_日本欧美黄色

「前端」一篇詳細(xì)且全面的“前端模塊化方案”總結(jié)分享(含代碼)

隨著前端功能越來越復(fù)雜,前端代碼日益膨脹,為了減少維護(hù)成本,提高代碼的可復(fù)用性,前端模塊化勢在必行。歡迎閱讀~

當(dāng)所有.js文件都在一個html中引入,造成以下不良影響:

  1. 請求過多。首先我們要依賴多個模塊,那樣就會發(fā)送多個請求,導(dǎo)致請求過多
  2. 依賴模糊。我們不知道他們的具體依賴關(guān)系是什么,也就是說很容易因為不了解他們之間的依賴關(guān)系導(dǎo)致加載先后順序出錯。
  3. 難以維護(hù)。以上兩種原因就導(dǎo)致了很難維護(hù),很可能出現(xiàn)牽一發(fā)而動全身的情況導(dǎo)致項目出現(xiàn)嚴(yán)重的問題。

一、模塊的概念

webpack 中是這樣定義的:

在模塊化編程中,開發(fā)者將程序分解成離散功能塊(discrete chunks of functionality),并稱之為模塊。 每個模塊具有比完整程序更小的接觸面,使得校驗、調(diào)試、測試輕而易舉。 精心編寫的模塊提供了可靠的抽象和封裝界限,使得應(yīng)用程序中每個模塊都具有條理清楚的設(shè)計和明確的目的。

模塊應(yīng)該是職責(zé)單一、相互獨立、低耦合的、高度內(nèi)聚且可替換的離散功能塊。

二、模塊化的概念

模塊化是一種處理復(fù)雜系統(tǒng)分解成為更好的可管理模塊的方式,它可以把系統(tǒng)代碼劃分為一系列職責(zé)單一,高度解耦且可替換的模塊,系統(tǒng)中某一部分的變化將如何影響其它部分就會變得顯而易見,系統(tǒng)的可維護(hù)性更加簡單易得。

模塊化是一種分治的思想,通過分解復(fù)雜系統(tǒng)為獨立的模塊實現(xiàn)細(xì)粒度的精細(xì)控制,對于復(fù)雜系統(tǒng)的維護(hù)和管理十分有益。模塊化也是組件化的基石,是構(gòu)成現(xiàn)在色彩斑斕的前端世界的前提條件。

三、為什么需要模塊化

前端開發(fā)和其他開發(fā)工作的主要區(qū)別,首先是前端是基于多語言、多層次的編碼和組織工作,其次前端產(chǎn)品的交付是基于瀏覽器,這些資源是通過增量加載的方式運行到瀏覽器端,如何在開發(fā)環(huán)境組織好這些碎片化的代碼和資源,并且保證他們在瀏覽器端快速、優(yōu)雅的加載和更新,就需要一個模塊化系統(tǒng)。

四、模塊化的好處

  1. 可維護(hù)性。 因為模塊是獨立的,一個設(shè)計良好的模塊會讓外面的代碼對自己的依賴越少越好,這樣自己就可以獨立去更新和改進(jìn)。
  2. 命名空間。 在 JavaScript 里面,如果一個變量在最頂級的函數(shù)之外聲明,它就直接變成全局可用。因此,常常不小心出現(xiàn)命名沖突的情況。使用模塊化開發(fā)來封裝變量,可以避免污染全局環(huán)境。
  3. 重用代碼。 我們有時候會喜歡從之前寫過的項目中拷貝代碼到新的項目,這沒有問題,但是更好的方法是,通過模塊引用的方式,來避免重復(fù)的代碼庫。我們可以在更新了模塊之后,讓引用了該模塊的所有項目都同步更新,還能指定版本號,避免 API 變更帶來的麻煩。

五、模塊化簡史

5.1 最簡單粗暴的方式

function fn1(){ // ...}function fn2(){ // ...}

通過 script 標(biāo)簽引入文件,調(diào)用相關(guān)的函數(shù)。這樣需要手動去管理依賴順序,容易造成命名沖突,污染全局,隨著項目的復(fù)雜度增加維護(hù)成本也越來越高。

5.2 用對象來模擬命名空間

var output = { _count: 0, fn1: function(){ // ... }}

這樣可以解決上面的全局污染的問題,有那么點命名空間的意思,但是隨著項目復(fù)雜度增加需要越來越多的這樣的對象需要維護(hù),不說別的,取名字都是個問題。最關(guān)鍵的還是內(nèi)部的屬性還是可以被直接訪問和修改。

5.3 閉包

var module = (function(){ var _count = 0; var fn1 = function (){ // ... } var fn2 = function fn2(){ // ... } return { fn1: fn1, fn2: fn2 }})()module.fn1();module._count; // undefined

這樣就擁有獨立的詞法作用域,內(nèi)存中只會存在一份 copy。這不僅避免了外界訪問此 IIFE 中的變量,而且又不會污染全局作用域,通過 return 暴露出公共接口供外界調(diào)用。這其實就是現(xiàn)代模塊化實現(xiàn)的基礎(chǔ)。

5.4 更多

還有基于閉包實現(xiàn)的松耦合拓展、緊耦合拓展、繼承、子模塊、跨文件共享私有對象、基于 new 構(gòu)造的各種方式,這種方式在現(xiàn)在看來都不再優(yōu)雅。

// 松耦合拓展// 這種方式使得可以在不同的文件中以相同結(jié)構(gòu)共同實現(xiàn)一個功能塊,且不用考慮在引入這些文件時候的順序問題。// 缺點是沒辦法重寫你的一些屬性或者函數(shù),也不能在初始化的時候就是用module的屬性。var module = (function(my){ // ... return my})(module || {})// 緊耦合拓展(沒有傳默認(rèn)參數(shù))// 加載順序不再自由,但是可以重載var module = (function(my){ var old = my.someOldFunc my.someOldFunc = function(){ // 重載方法,依然可通過old調(diào)用舊的方法... } return my})(module)

六、實現(xiàn)模塊化的方案規(guī)范總覽

歷史上,JavaScript 一直沒有模塊(module)體系,無法將一個大程序拆分成互相依賴的小文件,再用簡單的方法拼裝起來。其他語言都有這項功能,比如 Ruby 的require、Python 的import,甚至就連 CSS 都有@import,但是 JavaScript 任何這方面的支持都沒有,這對開發(fā)大型的、復(fù)雜的項目形成了巨大障礙。

在 ES6 之前,社區(qū)制定了一些模塊加載方案,最主要的有 CommonJSAMD 兩種。前者用于服務(wù)器,后者用于瀏覽器。ES6 在語言標(biāo)準(zhǔn)的層面上,實現(xiàn)了模塊功能,而且實現(xiàn)得相當(dāng)簡單,完全可以取代 CommonJS 和 AMD 規(guī)范,成為瀏覽器和服務(wù)器通用的模塊解決方案。

ES6 模塊的設(shè)計思想是盡量的靜態(tài)化,使得編譯時就能確定模塊的依賴關(guān)系,以及輸入和輸出的變量。CommonJS 和 AMD 模塊,都只能在運行時確定這些東西。比如,CommonJS 模塊就是對象,輸入時必須查找對象屬性。

目前實現(xiàn)模塊化的規(guī)范主要有:

  • CommonJS
  • CMD
  • AMD
  • ES6模塊

七、CommonJS()

CommonJS 是以在瀏覽器環(huán)境之外構(gòu)建 JavaScript 生態(tài)系統(tǒng)為目標(biāo)而產(chǎn)生的項目,比如在服務(wù)器和桌面環(huán)境中。

采用同步加載模塊的方式,也就是說只有加載完成,才能執(zhí)行后面的操作。CommonJS 代表:Node 應(yīng)用中的模塊,通俗的說就是你用 npm 安裝的模塊。

它使用 require 引用和加載模塊,exports 定義和導(dǎo)出模塊,module 標(biāo)識模塊。使用 require 時需要去讀取并執(zhí)行該文件,然后返回 exports 導(dǎo)出的內(nèi)容。

//定義模塊 math.js var random=Math.random()*10; function printRandom(){ console.log(random) } function printIntRandom(){ console.log(Math.floor(random)) } //模塊輸出 module.exports={ printRandom:printRandom, printIntRandom:printIntRandom } //加載模塊 math.js var math=require('math') //調(diào)用模塊提供的方法 math.printIntRandom() math.printRandom()

7.1 CommonJS主要用于服務(wù)端的模塊化,不適用于前端模塊化的原因在于:

  • 服務(wù)端加載一個模塊,直接就從硬盤或者內(nèi)存中讀取了,消耗時間可以忽略不計
  • 瀏覽器需要從服務(wù)端下載這個文件,所以說如果用CommonJS的require方式加載模塊,需要等代碼模塊下載完畢,并運行之后才能得到所需要的API。
  • 如果我們在某個代碼模塊里使用CommonJS的方法require了一個模塊,而這個模塊需要通過http請求從服務(wù)器去取,如果網(wǎng)速很慢,而CommonJS又是同步的,所以將阻塞后面代碼的執(zhí)行,從而阻塞瀏覽器渲染頁面,使得頁面出現(xiàn)假死狀態(tài)。

7.2 CommonJS在瀏覽器端實現(xiàn)的步驟:

1. 創(chuàng)建項目結(jié)構(gòu)

|-js |-dist //打包生成文件的目錄 |-src //源碼所在的目錄 |-module1.js |-module2.js |-module3.js |-app.js //應(yīng)用主源文件|-index.html //運行于瀏覽器上|-package.json { "name": "browserify-test", "version": "1.0.0" }

2. 下載browserify

  • 全局: npm install browserify -g
  • 局部: npm install browserify –save-dev

3. 定義模塊代碼(同服務(wù)器端)

注意:index.html文件要運行在瀏覽器上,需要借助browserify將app.js文件打包編譯,如果直接在index.html引入app.js就會報錯!

4. 打包處理js

根目錄下運行browserify js/src/app.js -o js/dist/bundle.js

5. 頁面使用引入

在index.html文件中引入<script type="text/javascript" src="js/dist/bundle.js"></script>

八、AMD(Asynchronous Module Definition)

異步模塊定義,所謂異步是指模塊和模塊的依賴可以被異步加載,他們的加載不會影響它后面語句的運行。有效避免了采用同步加載方式中導(dǎo)致的頁面假死現(xiàn)象。AMD代表:RequireJS。

AMD一開始是CommonJS規(guī)范中的一個草案,全稱是Asynchronous Module Definition,即異步模塊加載機(jī)制。后來由該草案的作者以RequireJS實現(xiàn)了AMD規(guī)范,所以一般說AMD也是指RequireJS。

RequireJS是一個工具庫,主要用于客戶端的模塊管理。它的模塊管理遵守AMD規(guī)范,RequireJS的基本思想是,通過define方法,將代碼定義為模塊;通過require方法,實現(xiàn)代碼的模塊加載

它主要有兩個接口:definerequire。define 是模塊開發(fā)者關(guān)注的方法,而 require 則是模塊使用者關(guān)注的方法。

8.1 define() 函數(shù):

define(id?, dependencies?, factory);//id :可選參數(shù),它指的是模塊的名字。//dependencies:可選參數(shù),定義中模塊所依賴模塊的數(shù)組。//factory:模塊初始化要執(zhí)行的函數(shù)或?qū)ο?/code>

需要注意的是,dependencies有多少個元素,factory就有多少個傳參,位置一一對應(yīng)。
使用栗子:

define("alpha", ["require", "exports", "beta"], function (require, exports, beta) { exports.verb = function() { return beta.verb(); //Or: //return require("beta").verb(); } });

8.2 require() 函數(shù)

require([module], callback);//module:一個數(shù)組,里面的成員就是要加載的模塊.//callback:模塊加載成功之后的回調(diào)函數(shù)。

需要注意的是 ,module 有多少個元素,callback 就有多少個傳參,位置一一對應(yīng)。

require(["a","b","c"],function(a,b,c){ //code here });

8.3 AMD的優(yōu)缺點

AMD 運行時核心思想是「Early Executing」,也就是提前執(zhí)行依賴 AMD 的這個特性有好有壞:

  • 首先,盡早執(zhí)行依賴可以盡早發(fā)現(xiàn)錯誤。
  • 另外,盡早執(zhí)行依賴通??梢詭砀玫挠脩趔w驗,也容易產(chǎn)生浪費。
  • 引用AMD的Javscript庫: 目前,主要有兩個Javascript庫實現(xiàn)了AMD規(guī)范:require.js和curl.js
  • 在瀏覽器環(huán)境中異步加載模塊;并行加載多個模塊;
  • 開發(fā)成本高,代碼的閱讀和書寫比較困難,模塊定義方式的語義不順暢;不符合通用的模塊化思維方式,是一種妥協(xié)的實現(xiàn)。

8.4 AMD在瀏覽器端的實現(xiàn)步驟

1. 下載require.js, 并引入

  • 官網(wǎng): http://www.requirejs.cn/
  • github : https://github.com/requirejs/requirejs

然后將require.js導(dǎo)入項目: js/libs/require.js

2. 創(chuàng)建項目結(jié)構(gòu)

|-js |-libs |-require.js |-modules |-alerter.js |-dataService.js |-main.js|-index.html

3. 定義require.js的模塊代

// dataService.js文件// 定義沒有依賴的模塊define(function() { let Msg = 'www.baidu.com' function getMsg() { return msg.toUpperCase() } return { getMsg } // 暴露模塊});//alerter.js文件// 定義有依賴的模塊define(['dataService'], function(dataService) { let name = 'Tom' function showMsg() { alert(dataService.getMsg() ', ' name) } // 暴露模塊 return { showMsg }});// main.js文件(function() { require.config({ baseUrl: 'js/', //基本路徑 出發(fā)點在根目錄下 paths: { //映射: 模塊標(biāo)識名: 路徑 alerter: './modules/alerter', //此處不能寫成alerter.js,會報錯 dataService: './modules/dataService' } }); require(['alerter'], function(alerter) { alerter.showMsg() });})();// index.html文件<!DOCTYPE html><html> <head> <title>Modular Demo</title> </head> <body> <!-- 引入require.js并指定js主文件的入口 --> <script data-main="js/main" src="js/libs/require.js"></script> </body></html>

4. 頁面引入require.js模塊:

在index.html引入 <script data-main="js/main" src="js/libs/require.js"></script>

此外在項目中如何引入第三方庫?只需在上面代碼的基礎(chǔ)稍作修改:

// alerter.js文件define(['dataService', 'jquery'], function(dataService, $) { let name = 'Tom' function showMsg() { alert(dataService.getMsg() ', ' name) } $('body').css('background', 'green') // 暴露模塊 return { showMsg }});// main.js文件(function() { require.config({ baseUrl: 'js/', //基本路徑 出發(fā)點在根目錄下 paths: { //自定義模塊 alerter: './modules/alerter', //此處不能寫成alerter.js,會報錯 dataService: './modules/dataService', // 第三方庫模塊 jquery: './libs/jquery-1.10.1' //注意:寫成jQuery會報錯 } }) require(['alerter'], function(alerter) { alerter.showMsg() })})()

上例是在alerter.js文件中引入jQuery第三方庫,main.js文件也要有相應(yīng)的路徑配置。 小結(jié):通過兩者的比較,可以得出AMD模塊定義的方法非常清晰,不會污染全局環(huán)境,能夠清楚地顯示依賴關(guān)系。AMD模式可以用于瀏覽器環(huán)境,并且允許非同步加載模塊,也可以根據(jù)需要動態(tài)加載模塊。

九、CMD(Common Module Definition)

CMD是SeaJS在推廣過程中生產(chǎn)的對模塊定義的規(guī)范,在Web瀏覽器端的模塊加載器中,SeaJS與RequireJS并稱,SeaJS作者為阿里的玉伯。

CMD規(guī)范專門用于瀏覽器端,模塊的加載是異步的,模塊使用時才會加載執(zhí)行。CMD規(guī)范整合了CommonJS和AMD規(guī)范的特點。在 Sea.js 中,所有 JavaScript 模塊都遵循 CMD模塊定義規(guī)范。

9.1 CMD語法

定義暴露模塊:

//定義沒有依賴的模塊define(function(require, exports, module){ exports.xxx = value module.exports = value})//定義有依賴的模塊define(function(require, exports, module){ //引入依賴模塊(同步) var module2 = require('./module2') //引入依賴模塊(異步) require.async('./module3', function (m3) { }) //暴露模塊 exports.xxx = value})

引入使用模塊:

define(function (require) { var m1 = require('./module1') var m4 = require('./module4') m1.show() m4.show()})

9.2 CMD的優(yōu)缺點

  • 優(yōu)點:依賴就近,延遲執(zhí)行 可以很容易在 Node.js 中運行;
  • 缺點:依賴 SPM 打包,模塊的加載邏輯偏重;

9.3 sea.js使用步驟

1. 下載sea.js, 并引入

  • 官網(wǎng): seajs.org/
  • github : github.com/seajs/seajs

然后將sea.js導(dǎo)入項目: js/libs/sea.js

2. 創(chuàng)建項目結(jié)構(gòu)

|-js |-libs |-sea.js |-modules |-module1.js |-module2.js |-module3.js |-module4.js |-main.js|-index.html

3. 定義sea.js的模塊代碼

// module1.js文件define(function (require, exports, module) { //內(nèi)部變量數(shù)據(jù) var data = 'atguigu.com' //內(nèi)部函數(shù) function show() { console.log('module1 show() ' data) } //向外暴露 exports.show = show});// module2.js文件define(function (require, exports, module) { module.exports = { msg: 'I Will Back' }});// module3.js文件define(function(require, exports, module) { const API_KEY = 'abc123' exports.API_KEY = API_KEY});// module4.js文件define(function (require, exports, module) { //引入依賴模塊(同步) var module2 = require('./module2') function show() { console.log('module4 show() ' module2.msg) } exports.show = show //引入依賴模塊(異步) require.async('./module3', function (m3) { console.log('異步引入依賴模塊3 ' m3.API_KEY) })});// main.js文件define(function (require) { var m1 = require('./module1') var m4 = require('./module4') m1.show() m4.show()})

4. 在index.html中引入

<script type="text/javascript" src="js/libs/sea.js"></script><script type="text/javascript"> seajs.use('./js/modules/main')</script>

十、ES6模塊化(重點介紹)

ES6模塊的設(shè)計思想,是盡量的靜態(tài)化,使得編譯時就能確定模塊的依賴關(guān)系,以及輸入和輸出的變量。所以說ES6是編譯時加載,不同于CommonJS的運行時加載(實際加載的是一整個對象),ES6模塊不是對象,而是通過export命令顯式指定輸出的代碼,輸入時也采用靜態(tài)命令的形式。

ES6 的模塊自動采用嚴(yán)格模式,不管你有沒有在模塊頭部加上"use strict";。

嚴(yán)格模式主要有以下限制。

  • 變量必須聲明后再使用
  • 函數(shù)的參數(shù)不能有同名屬性,否則報錯
  • 不能使用with語句
  • 不能對只讀屬性賦值,否則報錯
  • 不能使用前綴 0 表示八進(jìn)制數(shù),否則報錯
  • 不能刪除不可刪除的屬性,否則報錯
  • 不能刪除變量delete prop,會報錯,只能刪除屬性delete global[prop]
  • eval不會在它的外層作用域引入變量
  • eval和arguments不能被重新賦值
  • arguments不會自動反映函數(shù)參數(shù)的變化
  • 不能使用arguments.callee
  • 不能使用arguments.caller
  • 禁止this指向全局對象
  • 不能使用fn.caller和fn.arguments獲取函數(shù)調(diào)用的堆棧
  • 增加了保留字(比如protected、static和interface)

其中,尤其需要注意this的限制。ES6 模塊之中,頂層的this指向undefined,即不應(yīng)該在頂層代碼使用this。

10.1 語法

模塊功能主要由兩個命令構(gòu)成:export和import。export命令用于規(guī)定模塊的對外接口,import命令用于輸入其他模塊提供的功能。

10.1.1 export

一個模塊就是一個獨立的文件。該文件內(nèi)部的所有變量,外部無法獲取。如果你希望外部能夠讀取模塊內(nèi)部的某個變量,就必須使用export關(guān)鍵字輸出該變量。下面是一個 JS 文件,里面使用export命令輸出變量。

function cUl(){ let ulEle = document.createElement("ul"); for(let i = 0; i < 5; i ){ let liEle = document.createElement("li"); liEle.innerHTML = "無序列表" i; ulEle.appendChild(liEle); } return ulEle;}let ul = cUl();export {ul};

10.1.2 import

使用export命令定義了模塊的對外接口以后,其他 JS 文件就可以通過import命令加載這個模塊。

import {table} from "../test/test_table.js";import {div} from "../test/test_div.js" ;import {ul} from "../test/test_ul.js" ;export {table, div, ul};

10.2 ES6 模塊與 CommonJS 模塊的差異

它們有兩個重大差異:

1. CommonJS 模塊輸出的是一個值的拷貝,ES6 模塊輸出的是值的引用。

2. CommonJS 模塊是運行時加載,ES6 模塊是編譯時輸出接口。

第二個差異是因為 CommonJS 加載的是一個對象(即module.exports屬性),該對象只有在腳本運行完才會生成。而 ES6 模塊不是對象,它的對外接口只是一種靜態(tài)定義,在代碼靜態(tài)解析階段就會生成。

下面重點解釋第一個差異,我們還是舉上面那個CommonJS模塊的加載機(jī)制例子:

// lib.jsexport let counter = 3;export function incCounter() { counter ;}// main.jsimport { counter, incCounter } from './lib';console.log(counter); // 3incCounter();console.log(counter); // 4

ES6 模塊的運行機(jī)制與 CommonJS 不一樣。ES6 模塊是動態(tài)引用,并且不會緩存值,模塊里面的變量綁定其所在的模塊

文章來源:https://zhuanlan.zhihu.com/p/134070306

相關(guān)新聞

聯(lián)系我們
聯(lián)系我們
公眾號
公眾號
在線咨詢
分享本頁
返回頂部
德令哈市| 双流县| 肃南| 昌平区| 永安市| 海口市| 华安县| 灵寿县| 唐山市| 清苑县| 安国市| 仁怀市| 满洲里市| 黑龙江省| 安徽省| 博爱县| 东兰县| 白河县| 日喀则市| 汾阳市| 扶余县| 明水县| 海宁市| 濮阳市| 绩溪县| 商都县| 博乐市| 苍溪县| 宜兰市| 宁都县| 林西县| 开江县| 莆田市| 淮阳县| 兴安盟| 汶上县| 汉中市| 霍林郭勒市| 南部县| 南皮县| 中牟县|