javascript
javascript中的模块系统
文章目錄
- 簡介
- CommonJS和Nodejs
- AMD異步模塊加載
- CMD
- ES modules和現(xiàn)代瀏覽器
- 在HTML中使用module和要注意的問題
簡介
在很久以前,js只是簡單的作為瀏覽器的交互操作而存在,一般都是非常短小的腳本,所以都是獨立存在的。
但是隨著現(xiàn)代瀏覽器的發(fā)展,特別是nodejs的出現(xiàn),js可以做的事情變得越來越多也越來越復(fù)雜。于是我們就需要模塊系統(tǒng)來組織不同用途的腳本,進行邏輯的區(qū)分和引用。
今天將會給大家介紹一下js中的模塊系統(tǒng)。
CommonJS和Nodejs
CommonJS是由Mozilla公司在2009年1月份提出來了。沒錯,就是那個firfox的公司。
最初的名字叫做ServerJS,在2009年8月的時候為了表示這個標準的通用性,改名為CommonJS。
CommonJS最主要的應(yīng)用就是服務(wù)端的nodejs了。瀏覽器端是不直接支持CommonJS的,如果要在瀏覽器端使用,則需要進行轉(zhuǎn)換。
CommonJS使用require()來引入模塊,使用module.exports來導(dǎo)出模塊。
我們看一個CommonJS的例子:
require("module"); require("../file.js"); exports.doStuff = function() {}; module.exports = someValue;注意,CommonJS是同步加載的。
AMD異步模塊加載
AMD的全稱是Asynchronous Module Definition 。它提供了一個異步加載模塊的模式。
AMD是RequireJS在推廣過程中對模塊定義的規(guī)范化產(chǎn)出。
異步加載的好處就是可以在需要使用模塊的時候再進行加載,從而減少了一次性全部加載的時間,尤其是在瀏覽器端,可以提升用戶的體驗。
看下AMD加載模塊的定義:
define(id?, dependencies?, factory);AMD是通過define來定義和加載依賴模塊的。
其中id表示要定義的模塊的名字,dependencies表示這個模塊的依賴模塊,factory是一個函數(shù),用來初始化模塊或者對象。
我們看一個例子:
define("alpha", ["require", "exports", "beta"], function (require, exports, beta) {exports.verb = function() {return beta.verb();//Or:return require("beta").verb();}});這個例子中,我們定義了一個alpha模塊,這個模塊需要依賴"require", “exports”, "beta"三個模塊。
并且在factory中導(dǎo)出了beta模塊的verb方法。
define中id和dependencies都不是必須的:
//無iddefine(["alpha"], function (alpha) {return {verb: function(){return alpha.verb() + 2;}};});//無依賴define({add: function(x, y){return x + y;}});甚至我們可以在AMD中使用CommonJS:
define(function (require, exports, module) {var a = require('a'),b = require('b');exports.action = function () {};});定義完之后,AMD使用require來加載模塊:
require([dependencies], function(){});第一個參數(shù)是依賴模塊,第二個參數(shù)是回調(diào)函數(shù),會在前面的依賴模塊都加載完畢之后進行調(diào)用。加載的模塊會以參數(shù)形式傳入該函數(shù),從而在回調(diào)函數(shù)內(nèi)部就可以使用這些模塊。
require(["module", "../file"], function(module, file) { /* ... */ });require加載模塊是異步加載的,但是后面的回調(diào)函數(shù)只會在所有的模塊都加載完畢之后才運行。
CMD
CMD是SeaJS在推廣過程中對模塊定義的規(guī)范化產(chǎn)出。它的全稱是Common Module Definition。
CMD也是使用define來定義模塊的,CMD推崇一個文件作為一個模塊:
define(id?, deps?, factory)看起來和AMD的define很類似,都有id,依賴模塊和factory。
這里的factory是一個函數(shù),帶有三個參數(shù),function(require, exports, module)
我們可以在factory中通過require來加載需要使用的模塊,通過exports來導(dǎo)出對外暴露的模塊,module表示的是當前模塊。
我們看一個例子:
// 定義模塊 myModule.js define(function(require, exports, module) {var $ = require('jquery.js')$('div').addClass('active'); });// 加載模塊 seajs.use(['myModule.js'], function(my){});所以總結(jié)下AMD和CMD的區(qū)別就是,AMD前置要加載的依賴模塊,在定義模塊的時候就要聲明其依賴的模塊。
而CMD加載完某個依賴模塊后并不執(zhí)行,只是下載而已,只有在用到的時候才使用require進行執(zhí)行。
ES modules和現(xiàn)代瀏覽器
ES6和現(xiàn)代瀏覽器對模塊化的支持是通過import和export來實現(xiàn)的。
首先看下import和export在瀏覽器中支持的情況:
首先我們看下怎么使用export導(dǎo)出要暴露的變量或者方法:
export const name = 'square';export function draw(ctx, length, x, y, color) {ctx.fillStyle = color;ctx.fillRect(x, y, length, length);return {length: length,x: x,y: y,color: color}; }基本上,我們可以使用export導(dǎo)出var, let, const變量或者function甚至class。前提是這些變量或者函數(shù)處于top-level。
更簡單的辦法就是將所有要export的放在一行表示:
export { name, draw, reportArea, reportPerimeter };export實際上有兩種方式,named和default。上面的例子中的export是named格式,因為都有自己的名字。
下面看下怎么使用export導(dǎo)出默認的值:
// export feature declared earlier as default export { myFunction as default };// export individual features as default export default function () { ... } export default class { .. }named可以導(dǎo)出多個對象,而default只可以導(dǎo)出一個對象。
導(dǎo)出之后,我們就可以使用import來導(dǎo)入了:
import { name, draw, reportArea, reportPerimeter } from './modules/square.js';如果導(dǎo)出的時候選擇的是default,那么我們在import的時候可以使用任何名字:
// file test.js let k; export default k = 12;// some other file import m from './test'; // 因為導(dǎo)出的是default,所以這里我們可以使用import m來引入 console.log(m); // will log 12我們可以在一個module中使用import和export從不同的模塊中導(dǎo)入,然后在同一個模塊中導(dǎo)出,這樣第三方程序只需要導(dǎo)入這一個模塊即可。
export { default as function1,function2 } from 'bar.js';上面的export from 等價于:
import { default as function1,function2 } from 'bar.js'; export { function1, function2 };上面的例子中,我們需要分別import function1 function2才能夠使用,實際上,我們可以使用下面的方式將所有的import作為Module對象的屬性:
import * as Module from './modules/module.js';Module.function1() Module.function2()然后function1,function2就變成了Module的屬性,直接使用即可。
在HTML中使用module和要注意的問題
怎么在HTML中引入module呢?我們有兩種方式,第一種是使用src選項:
<script type="module" src="main.js"></script>第二種直接把module的內(nèi)容放到script標簽中。
<script type="module">/* JavaScript module code here */ </script>注意,兩種script標簽的類型都是module。
在使用script來加載module的時候,默認就是defer的,所以不需要顯示加上defer屬性。
如果你在測試的時候使用file:// 來加載本地文件的話,因為JS模塊安全性的要求,很有可能得到一個CORS錯誤。
最后,import() 還可以作為函數(shù)使用,來動態(tài)加載模塊:
squareBtn.addEventListener('click', () => {import('./modules/square.js').then((Module) => {let square1 = new Module.Square(myCanvas.ctx, myCanvas.listId, 50, 50, 100, 'blue');square1.draw();square1.reportArea();square1.reportPerimeter();}) });本文作者:flydean程序那些事
本文鏈接:http://www.flydean.com/js-modules/
本文來源:flydean的博客
歡迎關(guān)注我的公眾號:「程序那些事」最通俗的解讀,最深刻的干貨,最簡潔的教程,眾多你不知道的小技巧等你來發(fā)現(xiàn)!
總結(jié)
以上是生活随笔為你收集整理的javascript中的模块系统的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: javascript中的内置对象和数据结
- 下一篇: javascript中的内存管理