js 模組化規範(commonjs、AMD、ES6、CMD)
開發中最流行的 commonjs、AMD、ES6、CMD 規範。
參考資料:
https://mp.weixin.qq.com/s/MPEhWlS9KiIc9I6Of5GpOQ
http://www.ruanyifeng.com/blog/2015/05/commonjs-in-browser.html
http://foio.github.io/requireJS/
https://github.com/ZhiCYue/requireJs-analysis/blob/master/require.js
學習筆記 + 原理分析
理解原理後,網上再蒐集相關資料時,會對一些概念有更好的認識,比如:延時執行、立即執行、執行時、編譯時等等。
再比如:“AMD是提前執行,CMD是延遲執行。” ,“CMD 推崇依賴就近,AMD 推崇依賴前置” 等等。
commonjs
Node 應用由模組組成,採用 CommonJS 模組規範。每個檔案就是一個模組,有自己的作用域。在一個檔案裡面定義的變數、函式、類,都是私有的,對其他檔案不可見。在伺服器端,模組的載入是執行時同步載入的;在瀏覽器端,模組需要提前編譯打包處理。
特點:
- 所有程式碼都執行在模組作用域,不會汙染全域性作用域。
- 模組可以多次載入,但是隻會在第一次載入時執行一次,然後執行結果就被快取了,以後再載入,就直接讀取快取結果。要想讓模組再次執行,必須清除快取。
- 模組載入的順序,按照其在程式碼中出現的順序。
基本語法:
- 暴露模組:module.exports = value 或 exports.xxx = value;
- 引入模組:require(xxx), 如果是第三方模組,xxx 為模組名;如果是自定義模組,xxx 為模組檔案路徑。
執行
- 服務端:node app.js
- 瀏覽器:藉助 Browserify。參考: browserify js/src/app.js -o js/dist/bundle.js
記錄點 1 CommonJS 模組的載入機制是,輸入的是被輸出的值的拷貝。也就是說,一旦輸出一個值,模組內部的變化就影響不到這個值。
// lib.js
var counter = 3;
function incCounter() {
counter++;
}
module.exports = {
counter: counter,
incCounter: incCounter,
};
// main.js
var counter = require('./lib').counter;
var incCounter = require('./lib').incCounter;
console.log(counter); // 3
incCounter();
console.log(counter); // 3
記錄點 2 瀏覽器用不支援 CommonJS 格式。要想讓瀏覽器用上這些模組,必須轉換格式。
瀏覽器不相容CommonJS的根本原因,在於缺少四個Node.js環境的變數。
module
exports
require
global
只要能夠提供這四個變數,瀏覽器就能載入 CommonJS 模組。
下面是一個簡單的示例。
var module = {
exports: {}
};
(function(module, exports) {
exports.multiply = function (n) { return n * 1000 };
}(module, module.exports))
var f = module.exports.multiply;
f(5) // 5000
上面程式碼向一個立即執行函式提供 module 和 exports 兩個外部變數,模組就放在這個立即執行函式裡面。模組的輸出值放在 module.exports 之中,這樣就實現了模組的載入。
Browserify 的實現
請看一個例子,main.js 模組載入 foo.js 模組。
// foo.js
module.exports = function(x) {
console.log(x);
};
// main.js
var foo = require("./foo");
foo("Hi");
使用下面的命令,就能將main.js轉為瀏覽器可用的格式。
$ browserify main.js > compiled.js
browser 轉換後的compiled.js 原始碼:
(function () {
function r(e, n, t) {
function o(i, f) {
if (!n[i]) {
if (!e[i]) {
var c = "function" == typeof require && require;
if (!f && c) return c(i, !0);
if (u) return u(i, !0);
var a = new Error("Cannot find module '" + i + "'");
throw a.code = "MODULE_NOT_FOUND", a
}
var p = n[i] = { exports: {} };
e[i][0].call(p.exports, function (r) {
var n = e[i][1][r];
return o(n || r)
}, p, p.exports, r, e, n, t)
}
return n[i].exports
}
for (var u = "function" == typeof require && require, i = 0; i < t.length; i++) o(t[i]);
return o
}
return r
})()({
1: [function (require, module, exports) {
// foo.js
module.exports = function (x) {
console.log(x);
};
}, {}], 2: [function (require, module, exports) {
// main.js
var foo = require("./foo.js");
foo("Hi");
}, { "./foo.js": 1 }]
}, {}, [2]);
compiled.js 在html檔案中引入,瀏覽器開啟,即可看到控制檯輸出 Hi。
重構後的compiled.js 程式碼如下:(方便理解)
/** 模組一 */
var module1 = function (require, module, exports) {
// foo.js
module.exports = function (x) {
console.log(x);
};
}
/** 模組二 */
var module2 = function (require, module, exports) {
// main.js
var foo = require("./foo.js");
foo("Hi");
}
/** 將模組放置陣列中,並儲存對應依賴 */
var rObject = {
1: [ module1, {} ],
2: [ module2, { "./foo.js": 1 } ]
}
/** 相同依賴的進行快取 */
var cObject = {};
/** 定義入口js 模組索引 */
var mArr = [ 2 ];
/**
* run 函式
* 注:commonjs 的核心
* @param {Object} relation
* @param {Object} cache
* @param {*} t
*/
function run(relation, cache, t) {
function schedule(i, flag) {
if (!cache[i]) {
if (!relation[i]) {
// 如果require 存在,並且型別為function
var _require = "function" == typeof require && require;
// 執行require 方法,執行依賴的js 指令碼
if (!flag && _require) return _require(i, !0);
if (_out_require) return _out_require(i, !0);
// 丟擲異常
var err = new Error("Cannot find module '" + i + "'");
err.code = "MODULE_NOT_FOUND";
throw err;
}
// 為每個模組定義一個快取物件
var _module = cache[i] = { exports: {} };
relation[i][0].call(_module.exports, function (path) {
// 獲取path 指令碼在relation 中的序號
var num = relation[i][1][path];
return schedule(num || path)
}, _module, _module.exports, run, relation, cache, t)
}
return cache[i].exports;
}
for (var _out_require = "function" == typeof require && require, i = 0; i < t.length; i++) {
schedule(t[i]);
}
return schedule;
}
// 執行
run(rObject, cObject, mArr);
至此browserify 的原理一目瞭然。
但是,browserify 不能在瀏覽器中使用,但按照上述解析後的程式碼的方式編寫模組,則可以在瀏覽器端使用commonjs。其他方式的瀏覽器端commonjs,可以學習阮老師的 tiny-browser-require .
node 實現
node中的require() 方法原始碼解讀:http://www.ruanyifeng.com/blog/2015/05/require.html
AMD
CommonJS 規範載入模組是同步的,也就是說,只有載入完成,才能執行後面的操作。AMD 規範則是非同步載入模組,允許指定回撥函式。
由於 Node.js 主要用於伺服器程式設計,模組檔案一般都已經存在於本地硬碟,所以載入起來比較快,不用考慮非同步載入的方式,所以 CommonJS 規範比較適用。但是,如果是瀏覽器環境,要從伺服器端載入模組,這時就必須採用非同步模式,因此瀏覽器端一般採用 AMD 規範。此外 AMD 規範比 CommonJS 規範在瀏覽器端實現要來著早。
基本語法
定義暴露模組:
// 定義沒有依賴的模組
define(function(){
return 模組
})
// 定義有依賴的模組
define(['module1', 'module2'], function(m1, m2){
return 模組
})
引入使用模組:
require(['module1', 'module2'], function(m1, m2){
使用 m1/m2
})
主要的js 庫:require.js
官網: http://www.requirejs.cn/
github : https://github.com/requirejs/requirejs
使用方法
在 index.html 引入
< script data-main="js/main" src="js/libs/require.js">< /script>
比較
如果不使用amd 的方式,使用通常的閉包方式引入js,如果js指令碼多並且相互依賴複雜時,程式碼可能會是這樣子:
// index.html 檔案
<div><h1>Modular Demo 1: 未使用 AMD(require.js)</h1></div>
<script type="text/javascript" src="js/modules/dataService.js"></script>
<script type="text/javascript" src="js/modules/alerter.js"></script>
// ... 更多的指令碼引入
<script type="text/javascript" src="js/main.js"></script>
弊端:引入順序要要求嚴格,js請求次數多。
原始碼解析
原始碼:https://cdn.bootcss.com/require.js/2.3.6/require.js
相關資料:http://foio.github.io/requireJS/
分析require原始碼前,先了解一下指令碼的非同步載入相關:
並行的下載指令碼
(1) XHR eval
在瀏覽器原理中知道,瀏覽器解析script標籤的js指令碼是同步的,即會阻塞dom的渲染過程。如以下程式碼:
// a.js
alert(document.getElementById('div'))
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<title>Document</title>
</head>
<body>
<!-- 方式 1 -->
<script>
var XMLHttpReq = new XMLHttpRequest();
XMLHttpReq.onreadystatechange = function(){
if (XMLHttpReq.readyState == 4) {
if (XMLHttpReq.status == 200) {
var text = XMLHttpReq.responseText;
eval(text)
}
}
};
XMLHttpReq.open("get", 'a.js', true);
XMLHttpReq.send();
</script>
<!-- 方式 2 -->
<!-- <script src='a.js'></script> -->
<div id="div">Hello.</div>
</body>
</html>
通過靜態服務訪問index.html, 可知方式1 是非同步方式,在請求a.js 指令碼時不阻塞dom的渲染,模擬請求延時長一些,則可以拿到div 元素;方式 2 是同步方式,a.js指令碼在執行時,頁面是還沒有id為"div"元素的,因此alert的內容始終為null。
xhr eval方式缺點: 不支援跨域請求。
(2) script dom element
我們也可以直接在瀏覽器中插入script dom節點。如下程式碼:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Document</title>
</head>
<body>
<!-- 方式 1 -->
<!-- 方式 2 -->
<!-- 方式 3 -->
<script>
var scriptElem = document.createElement('script');
scriptElem.src = 'http://127.0.0.1:9006/a.js';
document.getElementsByTagName('head')[0].appendChild(scriptElem);
</script>
<div id="div">Hello.</div>
</body>
</html>
啟動本機靜態服務訪問index.html, 地址:http://localhost:9006/index.html 可以看到結果,指令碼的載入執行不阻塞dom的渲染(由於頁面的div比較簡單瀏覽器渲染很快,a.js指令碼中的alert 測試時都能夠獲取到渲染後的div,而a.js是在載入完後就開始執行的)
優點:支援跨域請求。
缺點:需要工程師自己在程式碼層面實現執行順序的控制。
記錄點 3 requir.js 就是通過script dom element 的方式實現的。
(3) document write script tag
這種方式是使用document.write 方法:
document.write("<script type='text/javascript' src='A.js'></script>");
優點:
scritp Tag可以保證多個指令碼並行載入。
可以保證指令碼按文件中出現的順序執行。
缺點:
會阻塞其他資源並行下載。
(4) defer和async屬性
async="async"不會阻塞其他資源,但是無法保證指令碼的執行順序。defer="defer"阻塞其他資源的載入,並且可以保證指令碼的執行順序,但是要到頁面解析完成後才開始執行指令碼。
指令碼的執行順序
當外部指令碼按常規方式載入時,它會阻塞行內指令碼的執行,可以保證順序。但是指令碼通過上述的幾種方式非同步載入時,就無法保證行內指令碼和非同步指令碼之間的順序。
保證行內指令碼和外部指令碼的執行順序:
(1) onlode事件
新增script dom節點時,監聽載入事件:
//行內函式
function callback(){
Console.log(‘calllback’);
}
//非同步載入函式
function loadScript(url, callback){
var script = document.createElement ("script")
script.type = "text/javascript";
if (script.readyState){ //IE
script.onreadystatechange = function(){
if (script.readyState == "loaded" || script.readyState == "complete"){
script.onreadystatechange = null;
callback();
}
};
} else { //Others
script.onload = function(){
callback();
};
}
script.src = url;
document.getElementsByTagName("head")[0].appendChild(script);
}
//控制行內指令碼和外部指令碼的執行順序
loadScript('a.js',callback);
(2) 定時器
通過定時檢查外部指令碼是否載入完成(即檢查對應變數是否存在):
<script src="MyJs.js"></script>
<script>
function callback(){
}
function checkMyJs(){
if(undefined===typeof(MyJs)){
setTimeout(checkMyJs, 300)
}else{
callback();
}
}
</script>
特點:
onload 和定時器的方式缺點:多個指令碼的執行順序不好控制。
多個外部指令碼之間的執行順序
(1) 同域指令碼
對於同域中的多個外部指令碼,可以使用XHR的方式載入指令碼,並通過一個佇列來控制指令碼的執行順序。
// a.js
console.log('a');
// b.js
console.log('b');
// c.js
console.log('c');
<!DOCTYPE html>
<html lang="en">
<head>
<title>Document</title>
</head>
<body>
<script>
var ScriptLoader = ScriptLoader || {}
ScriptLoader.Script = {
//指令碼佇列
queueScripts: [],
loadScriptXhrInjection: function (url, onload, bOrder) {
var iQ = ScriptLoader.Script.queueScripts.length;
if (bOrder) {
var qScript = { response: null, onload: onload, done: false };
ScriptLoader.Script.queueScripts[iQ] = qScript;
}
var xhrObj = ScriptLoader.Script.getXHROject();
xhrObj.onreadystatechange = function () {
if (xhrObj.readyState == 4) {
//有順序要求的指令碼需要新增的佇列,按新增順序執行
if (bOrder) {
//有順序要求的指令碼需要設定載入和執行狀態
ScriptLoader.Script.queueScripts[iQ].response = xhrObj.responseText;
//執行指令碼佇列
ScriptLoader
相關推薦
js 模組化規範(commonjs、AMD、ES6、CMD)
開發中最流行的 commonjs、AMD、ES6、CMD 規範。
參考資料: https://mp.weixin.qq.com/s/MPEhWlS9KiIc9I6Of5GpOQ http://www.ruanyifeng.com/blog/2015/05/commonjs-in-
再談 JS中的模組規範(CommonJS,AMD,CMD)來自玉伯的seajs分析
隨著網際網路的飛速發展,前端開發越來越複雜。本文將從實際專案中遇到的問題出發,講述模組化能解決哪些問題,以及如何使用 Sea.js 進行前端的模組化開發。惱人的命名衝突我們從一個簡單的習慣出發。我做專案時,常常會將一些通用的、底層的功能抽象出來,獨立成一個個函式,比如funct
JS模組化規範:AMD/CMD/CommonJS
一、模組化規範的由來
隨著網頁的複雜化,javascript程式碼也相應變得龐大起來,程式碼之間的分工合作就尤為重要了,這時候有了模組,我們就可以只關注自己業務的核心邏輯,其他想要實現的功能直接載入他人的模組便足夠了。考慮到模組的統一,便有了模組規範化。接下來
淺析JS中的模塊規範(CommonJS,AMD,CMD) http://www.2cto.com/kf/201411/348276.html
cpu 重要 mat 只有一個 targe () actor cti 最重要的 如果你聽過js模塊化這個東西,那麽你就應該聽過或CommonJS或AMD甚至是CMD這些規範咯,我也聽過,但之前也真的是聽聽而已。
現在就看看吧,這些規範到底是啥東西,幹嘛的。
理解JS中的模塊規範(CommonJS,AMD,CMD)
site 繼續 arr 包管理器 color sea 文件依賴 避免 說我 隨著互聯網的飛速發展,前端開發越來越復雜。本文將從實際項目中遇到的問題出發,講述模塊化能解決哪些問題,以及如何使用 Sea.js 進行前端的模塊化開發。
惱人的命名沖突
我們從一個簡單的習慣
前端模組化IIFE,commonjs,AMD,UMD,ES6 Module規範超詳細講解
[TOC]
## 為什麼前端需要模組化
在沒有模組化的時候,多個指令碼引入頁面,會造成諸多問題,比如:
- 多人協同開發的時候,系統中可能會引入很多js指令碼,這些js會定義諸多全域性變數,這時候很容易出現變數名覆蓋的問題
```html
一覽js模組化:從CommonJS到ES6
本文由雲+社群發表
模組化是指把一個複雜的系統分解到一個一個的模組。
模組化開發的優點:
(1)程式碼複用,讓我們更方便地進行程式碼管理、同時也便於後面程式碼的修改和維護。
(2)一個單獨的檔案就是一個模組,是一個單獨的作用域,只向外暴露特定的變數和函式。這樣可以避免汙染全域性變數,減少變數
JS 模組化規範
在我們最初寫程式碼的時候,引入JS檔案用script標籤來引入,並且在引入多個JS檔案時,當前檔案所依賴的JS檔案必須放在前面。也就存在一個順序的問題,而且這是由開發者去判斷和把控的。而現在前端專案越來越複雜,難免會出現很多很多script標籤引入JS,這無論對
Js模組化規範理解
export const obj = {
test1: ''
}
export const test = ''
exrpot class Test {
constuctor() {
}
}
或者是下邊的寫法
var name = "name"
var age = "age"
e
ES6中的模組化規範(一)
注意:ES6 的模組自動採用嚴格模式,不管你有沒有在模組頭部加上"use strict";。模組功能主要由兩個命令構成:export和import。export命令用於規定模組的對外介面,import命令用於輸入其他模組提供的功能。export命令一個模組就是一個獨立的檔案。
JS模組化程式設計之AMD規範(一)
隨著網站逐漸變成"網際網路應用程式",嵌入網頁的Javascript程式碼越來越龐大,越來越複雜。
網頁越來越像桌面程式,需要一個團隊分工協作、進度管理、單元測試等等......開發者不得不使用軟體工程的方法,管理網頁的業務邏輯。
JavaScript模組化程式設
詳解AMD、CommonJS和UMD模組化規範
開發的時候,我們經常會把某些功能封裝成可複用的模組。模組封裝了功能,並且對外暴露一個API。隨著Node.js的誕生和發展,JavaScript可以在服務端執行,同時客戶端應用也越來越流行,JavaScript界產生了對優秀和健壯模組系統的需求。在Java
JS模組化程式設計之AMD模型實現原理(Requirejs瀏覽器模型,nodejs伺服器模型)
官方引數手冊:https://requirejs.org/docs/api.html
/**
* require使用指南!
* 第一步:建立一個符合Require CMD模組化的標準目錄結構,去官方檢視!
* 第二步:在html檔案中指定主js檔
模組化規範之commenJS、AMD、CMD、ES6
commenJS(Node.js基於commenJS規範編寫)
理解
每個檔案都可以當作一個模組
在伺服器端(Node):模組的載入是在執行時同步載入的
在瀏覽器端(Browserify):模組需要提前編譯打包處理
基本語法
暴露模組(暴露出
js模組化程式設計之徹底弄懂CommonJS和AMD/CMD!
先回答我:為什麼模組很重要?
答:因為有了模組,我們就可以更方便地使用別人的程式碼,想要什麼功能,就載入什麼模組。但是,這樣做有一個前提,那就是大家必須以同樣的方式編寫模組,否則你有你的寫法,我有我的寫法,豈不是亂了套!
於是下面三個模組規範出來了,這篇文章也出來了(拼出來的 {捂臉笑})。
&
require-js-模組化 CMD AMD
模組化的標準有了模組,我們就可以更方便地使用別人的程式碼,想要什麼功能,就載入什麼模組。這樣做有一個前提,那就是大家必須以同樣的方式編寫模組,否則你有你的寫法,我有我的寫法,豈不是亂了套!
CommonJS:是一個模組化的標準,Node.js在使用的模組化標準。適用與後端開發的標準。AMD(As
JS模組化(一):CommonJS
模組化的規範: 目前有四種: 1.CommonJS &nbs
JS模組化開發規範
JS模組化開發規範,以下介紹三種
commonJS規範(Nodejs模組系統遵循此規範,適用於服務端)
1、 規範定義
CommonJS規範規定,一個檔案就是一個模組,用module變數代表當前模組。 Node在其內部提供一個Module的構建函式。所有模組都是Module的例項
2、 module.ex
[前端]關於JS模組化/AMD/CMD/UMD及CSS的BEM
工作上接觸到的模組化都是比較主流的ESM和commonJS
CSS上通用的是BEM
這裡主要是總結給自己看的
ESM(ES6 Module)
一個檔案一個模組
基本是webpack結合vue最常用的東西了
引入import,暴露用export
import re
js模組化,AMD與CMD的區別
最近在研究cmd和amd,在網上看到一篇不錯的文章,整理下看看。
在JavaScript發展初期就是為了實現簡單的頁面互動邏輯,寥寥數語即可;如今CPU、瀏覽器效能得到了極大的提升,很多頁面邏輯遷移到了客戶端(表單驗證等),隨著web2.0時代的到來,Ajax技術得到