js中的模塊化

来源:https://www.cnblogs.com/Yellow-ice/archive/2019/04/01/10639029.html
-Advertisement-
Play Games

前陣子一直忙著找實習,發現已經有一段時間沒寫博客了,面試很多時候會被問到模塊化,今天就讓我們一起來總結下把 一、什麼是模塊化 在js出現的時候,js一般只是用來實現一些簡單的交互,後來js開始得到重視,用來實現越來越複雜的功能,而為了維護的方便,我們也把不同功能的js抽取出來當做一個js文件,但是當 ...


  前陣子一直忙著找實習,發現已經有一段時間沒寫博客了,面試很多時候會被問到模塊化,今天就讓我們一起來總結下把

 

一、什麼是模塊化

  在js出現的時候,js一般只是用來實現一些簡單的交互,後來js開始得到重視,用來實現越來越複雜的功能,而為了維護的方便,我們也把不同功能的js抽取出來當做一個js文件,但是當項目變的複雜的時候,一個html頁面可能需要載入好多個js文件,而這個時候就會出現各種命名衝突,如果js也可以像java一樣,把不同功能的文件放在不同的package中,需要引用某個函數或功能的時候,import下相關的包,這樣可以很好的解決命名衝突等各種問題,但是js中沒有模塊的概念,又怎麼實現模塊化呢

  模塊化開發是一種管理方式,是一種生產方式,一種解決問題的方案,一個模塊就是實現特定功能的文件,有了模塊,我們就可以更方便地使用別人的代碼,想要什麼功能,就載入什麼模塊,但是模塊開發需要遵循一定的規範,否則就都亂套了,因此,才有了後來大家熟悉的AMD規範,CMD規範

  接下來,我們就一起學習下AMD,CMD和es6中的模塊化吧

 

二、AMD

  AMD 即Asynchronous Module Definition,中文名是“非同步模塊定義”的意思,它採用非同步方式載入模塊,模塊的載入不影響它後面語句的運行,所有依賴這個模塊的語句,都定義在一個回調函數中,等到載入完成之後,這個回調函數才會運行

  一般來說,AMD是 RequireJS 在推廣過程中對模塊定義的規範化的產出,因為平時在開發中比較常用的是require.js進行模塊的定義和載入,一般是使用define來定義模塊,使用require來載入模塊

1、定義模塊

  AMD規範只定義了一個函數define,它是全局變數,我們可以用它來定義一個模塊

define(id?, dependencies?, factory);

  其中,id是定義中模塊的名字,這個參數是可選的,如果沒有提供該參數,模塊的名字應該預設為模塊載入器請求的指定腳本的名字,如果提供了該參數,模塊名必須是“頂級”的和絕對的

  dependencies是定義的模塊中所依賴模塊的數組,依賴模塊必鬚根據模塊的工廠方法優先順序執行,並且執行的結果應該按照依賴數組中的位置順序以參數的形式傳入(定義中模塊的)工廠方法中

  factory是模塊初始化要執行的函數或對象,如果為函數,它應該只被執行一次,如果是對象,此對象應該為模塊的輸出值

  下麵來看一個定義模塊的例子

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

2、載入模塊

  require.js中採用require()語句載入模塊,在定義好了模塊後,我們可以使用require進行模塊的載入

require([module], callback);

  require要傳入兩個參數,第一個參數[module],是一個數組,裡面的成員就是要載入的模塊,第二個參數callback,則是載入成功之後的回調函數

  下麵我們來看一個例子

require([increment'], function (increment) {
    increment.add(1);
});

  上面的代碼中,比如我們現在已經定義了一個模塊,名字為increment,裡面有一個add方法,我們現在需要用到裡面的方法,只要像上面一樣將模塊載入進來,然後調用方法就可以了

3、requirejs使用例子

  在使用require.js時,可以通過define()定義模塊,這時候裡面的模塊的方法和變數外部是無法訪問到的,只有通過return,然後再載入這個模塊,才可以進行訪問

define('math',['jquery'], function ($) {//引入jQuery模塊
    return {
        add: function(x,y){
            return x + y;
        }
    };
});

  上面的代碼定義了一個math模塊,返回了一個add方法,要使用這個模塊的方法,我們需要向下麵這樣進行訪問

require(['jquery','math'], function ($,math) {
    console.log(math.add(10,100));//110
});

  通過require,我們載入了math模塊,這樣就可以使用math模塊裡面的add方法了

 

三、CMD

  CMD 即Common Module Definition通用模塊定義,CMD規範是國內發展出來的,同時,CMD是在SeaaJS推廣的過程中形成的,CMD和AMD要解決的都是同個問題,在使用上也都很像,只不過兩者在模塊定義方式和模塊載入時機上有所不同 1、定義模塊   在 CMD 規範中,一個模塊就是一個文件,通過define()進行定義
define(factory);

  define接受factory參數,factory可以是一個函數,也可以是一個對象或字元串

  factory為對象、字元串時,表示模塊的介面就是該對象、字元串,比如可以如下定義一個 JSON 數據模塊

define({ "foo": "bar" });

       也可以通過字元串定義模板模塊

define('I am a template. My name is {{name}}.');

     factory為函數時,表示是模塊的構造方法,執行該構造方法,可以得到模塊向外提供的介面,factory方法在執行時,預設會傳入三個參數:require,exports和 module

define(function(require, exports, module) {

  // 模塊代碼

});

  其中,require用來載入其它模塊,而exports可以用來實現向外提供模塊介面

define(function(require, exports) {

  // 對外提供 foo 屬性
  exports.foo = 'bar';

  // 對外提供 doSomething 方法
  exports.doSomething = function() {};

});

  module是一個對象,上面存儲了與當前模塊相關聯的一些屬性和方法,傳給factory構造方法的exports參數是module.exports對象的一個引用,只通過exports參數來提供介面,有時無法滿足開發者的所有需求,比如當模塊的介面是某個類的實例時,需要通過module.exports來實現

define(function(require, exports, module) {

  // exports 是 module.exports 的一個引用
  console.log(module.exports === exports); // true

  // 重新給 module.exports 賦值
  module.exports = new SomeClass();

  // exports 不再等於 module.exports
  console.log(module.exports === exports); // false

});

  說了這麼多,相信大家可能有點亂,來個簡單的例子,我們看看使用AMD和CMD定義的模塊的寫法

// CMD
define(function(require, exports, module) {
  var a = require('./a')
  a.doSomething()
  // 此處略去 100 行
  var b = require('./b') // 依賴可以就近書寫
  b.doSomething()
  // ... 
})

// AMD 預設推薦的是
define(['./a', './b'], function(a, b) { // 依賴必須一開始就寫好
  a.doSomething()
  // 此處略去 100 行
  b.doSomething()
  ...
}) 

  在上面的代碼中,相信大家很容易可以看出區別吧,AMD和CMD都是通過define()定義模塊,AMD需要把依賴的模塊先寫出來,可以通過return暴露介面,CMD在定義模塊需要傳入require,exports和module這幾個參數,要載入某個模塊時,使用require進行載入,要暴露介面時,可以通過exports,module.exports和return

2、載入模塊

  在前面定義模塊時,我們說過,當factory為函數時,require會作為預設參數傳遞進去,而require可以實現模塊的載入

  require是一個方法,接受模塊標識作為唯一參數,用來獲取其他模塊提供的介面

define(function(require, exports) {

  // 獲取模塊 a 的介面
  var a = require('./a');

  // 調用模塊 a 的方法
  a.doSomething();

});
   從上面定義模塊和載入模塊的方式上,我們也可以看出AMD和CMD主要有下麵幾個不同:       (1)AMD是RequireJS在推廣過程中對模塊定義的規範化產出,CMD是SeaJS在推廣過程中對模塊定義的規範化產出       (2)對於依賴的模塊,AMD是提前執行,CMD是延遲執行       (3)對於依賴的模塊,AMD推崇依賴前置,CMD推崇依賴就近 3、seajs使用例子   因為CMD是SeaJS在推廣過程中對模塊定義的規範化產出,因此一般在實際開發中,我們都是通過SeaJS進行模塊的定義和載入   下麵是一個簡單的例子
// 定義模塊  myModule.js
define(function(require, exports, module) {
  var $ = require('jquery.js')
  $('div').addClass('active');
  exports.data = 1;
});

// 載入模塊
seajs.use(['myModule.js'], function(my){
    var star= my.data;
    console.log(star);  //1
});

  上面的代碼中定義了myModule.js模塊,因為該模塊依賴於jquery.js,因此在需要使用該模塊時可以使用require進行模塊的載入,然後通過exports暴露出介面,通過SeaJS的use方法我們可以載入該模塊,並且使用該模塊暴露出的介面

 

四、es6中的模塊化

  在es6沒有出來之前,社區制定了一些模塊載入方案,最主要的有 CommonJS 和 AMD 兩種,前者用於伺服器,後者用於瀏覽器,ES6 在語言標準的層面上,實現了模塊功能,而且實現得相當簡單,完全可以取代 CommonJS 和 AMD 規範,成為瀏覽器和伺服器通用的模塊解決方案

  es6中的模塊化有一個比較大的特點,就是實現儘量的靜態化,比如說在CommonJS中我們要載入fs中的幾個方法,需要這樣寫

// CommonJS模塊
let { stat, exists, readFile } = require('fs');

// 等同於
let _fs = require('fs');
let stat = _fs.stat;
let exists = _fs.exists;
let readfile = _fs.readfile;

  上面的代碼其實是載入了fs中的所有方法,生成一個對象,再從這個對象上讀取方法,這種載入其實叫做運行時載入,也就是只有運行時才能得到這個對象,不能實現在編譯時實現靜態優化

  ES6 模塊不是對象,而是通過export命令顯式指定輸出的代碼,再通過import命令輸入

// ES6模塊
import { stat, exists, readFile } from 'fs';

  上面代碼的實質是從fs模塊載入 3 個方法,其他方法不載入,這種載入稱為“編譯時載入”或者靜態載入,即 ES6 可以在編譯時就完成模塊載入,效率要比 CommonJS 模塊的載入方式高,當然,這也導致了沒法引用 ES6 模塊本身,因為它不是對象

1、export

  模塊功能主要由兩個命令構成:export和import,export命令用於規定模塊的對外介面,import命令用於輸入其他模塊提供的功能

  一般來說,一個模塊就是一個獨立的文件,該文件內部的所有變數,外部無法獲取,如果你希望外部能夠讀取模塊內部的某個變數,就必須使用export關鍵字輸出該變數

// profile.js
export var firstName = 'Michael';
export var lastName = 'Jackson';
export var year = 1958;

  如果要輸出函數,可以像下麵這樣定義

function v1() { ... }
function v2() { ... }

export {
  v1 as streamV1,
  v2 as streamV2,
  v2 as streamLatestVersion
};

  上面的代碼中,我們使用了as對函數的對外介面進行了重命名

2、import

  使用export命令定義了模塊的對外介面以後,其他 JS 文件就可以通過import命令載入這個模塊

// main.js
import {firstName, lastName, year} from './profile.js';

function setName(element) {
  element.textContent = firstName + ' ' + lastName;
}

  import命令接受一對大括弧,裡面指定要從其他模塊導入的變數名。大括弧裡面的變數名,必須與被導入模塊(profile.js)對外介面的名稱相同

  我們也可以對載入的模塊進行重命名

import { lastName as surname } from './profile.js';

  除了指定載入某個輸出值,還可以使用整體載入,即用星號(*)指定一個對象,所有輸出值都載入在這個對象上面

  下麵是一個circle.js文件,它輸出兩個方法area和circumference

// circle.js

export function area(radius) {
  return Math.PI * radius * radius;
}

export function circumference(radius) {
  return 2 * Math.PI * radius;
}

  整體載入的寫法如下

import * as circle from './circle';

console.log('圓面積:' + circle.area(4));
console.log('圓周長:' + circle.circumference(14));

  這裡有一個地方需要註意,模塊整體載入所在的那個對象(上例是circle),應該是可以靜態分析的,所以不允許運行時改變,下麵的寫法都是不允許的

import * as circle from './circle';

// 下麵兩行都是不允許的
circle.foo = 'hello';
circle.area = function () {};

  關於import其實還有很多用法,具體的大家可以查看相關的文檔

 

  今天就先介紹到這裡,其實還有commonjs,還沒有進行介紹,如果大家感興趣,可以查看相關的用法呢

 

 

 

 

 

 

 

 

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 目錄:zxing->encoding->EncodingHandler類 中修改 createQRCode方法 ...
  • 寫在前面 用Python加上一些數據分析,來證明《海王》好看。 《海王》一部電影帶你重溫《馴龍高手》《變形金剛》《星球大戰》《星河戰隊》《鐵血戰士》《安德的游戲》《異形》可能還借鑒了對手的《鋼鐵俠》與《黑豹》劇情,再稍稍帶一點《大魚海棠》的味道,配上一丟丟溫子仁式恐怖片套路,優秀的商業片,應該是DC ...
  • 粘貼自Christian.Cao 博客園地址 : https://www.cnblogs.com/QQ862668193/p/6893797.html 輸入框景背景透明:<input style="background:transparent;border:1px solid #ffffff"> 鼠 ...
  • 註 : 本文章按照菜鳥教程 Flex佈局語法教程為原型稍加修改,以方便自己學習. 菜鳥教程地址:http://www.runoob.com/w3cnote/flex-grammar.html 2009年,W3C提出了一種新的方案—-Flex佈局,可以簡便、完整、響應式地實現各種頁面佈局。目前,它已經 ...
  • 2019年4月1日,我註冊了博客園的賬戶,開始記錄我的代碼日常。 學習web前端,是我之前沒有考慮過的事情。在大學渾渾噩噩度過了四年後,終於意識到就業的危機,剛入學的時候聽到學長學姐們說畢業即失業,感覺離自己很遙遠,但臨近畢業我卻親身體會到了。可能是習慣了高中老師們的督促,初入大學校園卻迷失了自我, ...
  • 使用eclipse打開,若用瀏覽器,且出現亂碼,將格式<meta charset="UTF-8">改為<meta charset="GB2316">規格顯示 博客.html(增加了即使驗證的功能) 1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset ...
  • 變數的聲明 在JavaScript程式中,使用一個變數之前應當先聲明,變數時使用關鍵字var來聲明的,如下所示:var num;var sum; 也可以寫成var num,sum,avg;如果只是聲明變數而沒有給變數賦值,預設的值是undefined 可以將變數的初始賦值和變數聲明合寫在一起如下所示 ...
  • JavaScript中創建數組有兩種方式 (一)使用 Array 構造函數: (二)使用數組字面量表示法: 數組的方法有數組原型方法,也有從object對象繼承來的方法,這裡我們只介紹數組的原型方法,數組原型方法主要有以下這些: join()push()和pop()shift() 和 unshift ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...