Commonjs
, AMD
, CMD
, ES6模塊化
。我的理解,ES6模塊化纔是主流。因此,咱們發現學習或創建模塊就是抓住兩點:如何引入模塊?如何暴露模塊?javascript
編碼時是按照模塊一個一個編碼的, 整個項目就是一個模塊化的項目css
module1.js
(定義一個模塊1)html
//數據
let data1 = 'module one data'
//操做數據的函數
function foo() {
console.log(`foo() ${data1}`)
}
function bar() {
console.log(`bar() ${data1}`)
}
複製代碼
module2.js
(定義一個模塊2)前端
let data2 = 'module two data';
function foo() { //與模塊1中的函數衝突了
console.log(`foo() ${data2}`)
}
複製代碼
test.html
(去使用定義好的模塊1和模塊2)java
//同步引入,若函數衝突,則後面覆蓋前面
<script type="text/javascript" src="module1.js"></script>
<script type="text/javascript" src="module2.js"></script>
<script type="text/javascript">
foo() //foo() module two data
bar() //bar() module one data
</script>
複製代碼
說明:node
data1
data2
都是全局變量)module1.js
(定義一個模塊1)jquery
let moduleOne = {
data: 'module one data',
foo() {
console.log(`foo() ${this.data}`)
},
bar() {
console.log(`bar() ${this.data}`)
}
}
複製代碼
module2.js
(定義一個模塊2)git
let moduleTwo = {
data: 'module two data',
foo() {
console.log(`foo() ${this.data}`)
},
bar() {
console.log(`bar() ${this.data}`)
}
}
複製代碼
test.html
(去使用定義好的模塊1和模塊2)es6
<script type="text/javascript" src="module1.js"></script>
<script type="text/javascript" src="module2.js"></script>
<script type="text/javascript">
moduleOne.foo() //foo() module one data
moduleOne.bar() //bar() module one data
moduleTwo.foo() //foo() module two data
moduleTwo.bar() //bar() module two data
moduleOne.data = 'update data' //能直接修改模塊內部的數據
moduleOne.foo() //foo() update data
</script>
複製代碼
說明:github
data
都不是全局變量了,而是對象的某一個屬性 )module1.js
(定義一個模塊1)
(function (window) {
//數據
let data = 'IIFE module data'
//操做數據的函數
function foo() { //用於暴露的函數
console.log(`foo() ${data}`)
}
function bar() {//用於暴露的函數
console.log(`bar() ${data}`)
otherFun() //內部調用
}
function otherFun() { //內部私有的函數
console.log('privateFunction go otherFun()')
}
//暴露foo函數和bar函數
window.moduleOne = {foo, bar}
})(window)
複製代碼
test.html
(去使用定義好的模塊1)
<script type="text/javascript" src="module1.js"></script>
<script type="text/javascript">
moduleOne.foo() //foo() IIFE module data
moduleOne.bar() //bar() IIFE module data privateFunction go otherFun()
//moduleOne.otherFun() //報錯,moduleOne.otherFun is not a function
console.log(moduleOne.data) //undefined 由於我暴露的moduleOne對象中無data
moduleOne.data = 'xxxx' //不是修改的模塊內部的data,而是在moduleOne新增data屬性
moduleOne.foo() //驗證內部的data沒有改變 仍是會輸出 foo() IIFE module data
</script>
複製代碼
說明:
引入jquery
到項目中
module1.js
(定義一個模塊1)
(function (window,$) {
//數據
let data = 'IIFE Strong module data'
//操做數據的函數
function foo() { //用於暴露的函數
console.log(`foo() ${data}`)
$('body').css('background', 'red')
}
function bar() {//用於暴露的函數
console.log(`bar() ${data}`)
otherFun() //內部調用
}
function otherFun() { //內部私有的函數
console.log('privateFunction go otherFun()')
}
//暴露foo函數和bar函數
window.moduleOne = {foo, bar}
})(window,jQuery)
複製代碼
test.html
(去使用定義好的模塊1)
<!--引入的js必須有必定順序-->
<script type="text/javascript" src="jquery-1.10.1.js"></script>
<script type="text/javascript" src="module1.js"></script>
<script type="text/javascript">
moduleOne.foo() //foo() IIFE Strong module data 並且頁面背景會變色
</script>
複製代碼
說明:
IIFE模式加強 : 引入依賴
這就是現代模塊實現的基石。其實很像了,有引入和暴露兩個方面。
存在的問題:一個頁面須要引入多個JS的問題
<script type="text/javascript" src="module1.js"></script>
<script type="text/javascript" src="module2.js"></script>
<script type="text/javascript" src="module3.js"></script>
<script type="text/javascript" src="module4.js"></script>
複製代碼
請求過多:一個script
標籤就是一次請求
依賴模糊:看不出來誰依賴着誰?哪些模塊是有依賴關係的,很難看出來。
難以維護:內部依賴關係混亂也就難以維護啦
CommonJS
是服務器端模塊的規範,Node.js
就是採用了這個規範。但目前也可用於瀏覽器端,須要使用 Browserify
進行提早編譯打包。
CommonJS
模塊化的引入方式使用require
; 暴露的方式使用module.exports
或exports
。
CommonJS基於服務器端
下載安裝node.js
建立項目結構
|-modules
|-module1.js
|-module2.js
|-module3.js
|-index.js
複製代碼
模塊化編碼 module1.js
(定義一個模塊1)
定義一個沒有依賴的模塊,此模塊用來定義配置常量
const newsUrl = 'http://localhost:3000/news';
const commentsUrl = 'http://localhost:3000/comments';
//經過exports暴露出去
exports.newsUrl = newsUrl;
exports.commentsUrl = commentsUrl;
複製代碼
module2.js
(定義一個模塊2)
定義一個有依賴的模塊(這個模塊2又依賴模塊1,故須要引入模塊1),用來模擬發送請求獲取數據的一個模塊
//引入依賴
const m1 = require('./module1');
//定義發送請求的方法
function getNews(url) {
console.log('發送請求獲取數據,請求地址爲:' + url);
return 'newsData';
}
function getComments(url) {
console.log('發送請求獲取數據,請求地址爲:' + url);
return 'commentsData';
}
const newsData = getNews(m1.newsUrl);
const commentsData = getComments(m1.commentsUrl);
//經過module.exports暴露模塊
module.exports = {
newsData,
commentsData
}
複製代碼
module3.js
(定義一個模塊3)
定義一個模塊,用來顯示用戶數據
//定義顯示內容的方法
function showData(data) {
console.log('要顯示的信息:' + data);
}
//經過module.exports暴露模塊
module.exports = showData;
複製代碼
index.js
(主模塊,用來啓動整個項目)
須要引入全部須要啓動的模塊
const m2 = require('./modules/module2');
const showData = require('./modules/module3');
showData(m2.newsData);
showData(m2.commentsData)
複製代碼
結果輸出:
發送請求獲取數據,請求地址爲:http://localhost:3000/news
發送請求獲取數據,請求地址爲:http://localhost:3000/comments
要顯示的信息:newsData
要顯示的信息:commentsData
複製代碼
經過node
運行index.js
執行命令: node index.js
CommonJS基於瀏覽器端
建立項目結構
|-dist //打包生成文件的目錄
|-src //源碼所在的目錄
|-module1.js
|-module2.js
|-module3.js
|-index.js //應用主源文件(只需打包主模塊)
|-index.html //引入dist裏面的打包好的js文件,[須要在html文件中引入就是基於瀏覽器端咯]
複製代碼
下載browserify
全局安裝下載: npm install browserify -g
定義模塊代碼 module1.js
定義一個沒有依賴的模塊,此模塊用來定義配置常量
//定義配置常量
const newsUrl = 'http://localhost:3000/news';
const commentsUrl = 'http://localhost:3000/comments';
//暴露出去
exports.newsUrl = newsUrl;
exports.commentsUrl = commentsUrl;
複製代碼
module2.js
定義一個有依賴的模塊(依賴模塊1),用來模擬發送請求獲取數據的一個模塊
//引入依賴
const m1 = require('./module1');
//定義發送請求的方法
function getNews(url) {
console.log('發送請求獲取數據,請求地址爲:' + url);
return 'newsData';
}
function getComments(url) {
console.log('發送請求獲取數據,請求地址爲:' + url);
return 'commentsData';
}
const newsData = getNews(m1.newsUrl);
const commentsData = getComments(m1.commentsUrl);
//暴露模塊
module.exports = {
newsData,
commentsData
}
複製代碼
module3.js
定義一個模塊,用來顯示用戶數據
//定義顯示內容的方法
function showData(data) {
console.log('要顯示的信息:' + data);
}
//暴露模塊
module.exports = showData;
複製代碼
index.js
(應用的主模塊JS)
主模塊,用來啓動整個項目。須要引入全部須要啓動的模塊。
const m2 = require('./module2');
const showData = require('./module3');
showData(m2.newsData);
showData(m2.commentsData);
複製代碼
打包處理index.js
執行命令:browserify src/index.js -o dist/bundle.js
src/index.js
表示就是src
目錄下的index
主模塊
-o
表示 outfile
dist/bundle.js
表示打包處理結果生成到dist/bundle.js
在主頁面index.html
中使用引入:
直接引入主模塊就能夠了,由於主模塊上就有各類依賴,他會自動去解析打包處理。
<script type="text/javascript" src="dist/bundle.js"></script>
複製代碼
結果輸出:
發送請求獲取數據,請求地址爲:http://localhost:3000/news
發送請求獲取數據,請求地址爲:http://localhost:3000/comments
要顯示的信息:newsData
要顯示的信息:commentsData
複製代碼
若是直接引用未打包處理的index.js
則會報錯:
引入方式:<script src="src/index.js"></script>
報錯信息爲:Uncaught ReferenceError: require is not defined-->
複製代碼
咱們如今是基於瀏覽器端的使用。只有在node
環境下才能夠直接使用未打包的index.js
引入,由於在node
環境下有exports
,modular
,require
這些全局方法。node
函數中是這樣的:function (exports, require, module, filename, dirname) {}
,因此咱們引入一個browserify
就會自動配置好這些參數。
完全說明白module.exports
和exports
的區別:
在nodejs
中,module
是一個全局變量,相似於在瀏覽器端的window
也是一個全局變量同樣的道理。
module.exports
初始的時候置爲{}
, exports
也指向這個空對象。
內部的代碼實現是:
var module = {
id: 'xxxx', // 我總得知道怎麼去找到他吧
exports: {}, // exports 就是個空對象
}
var exports = module.exports; //exports是對module.exports的引用
//也就是exports如今指向的內存地址和module.exports指向的內存地址是同樣的
複製代碼
上面的代碼能夠看出咱們日常使用的exports
是對module.exports
的一個引用,二者都是指向同一個對象。
用一句話來講明就是,模塊的require
(引入)能看到的只有module.exports
這個對象,它是看不到exports
對象的,而咱們在編寫模塊時用到的exports
對象實際上只是對module.exports
的引用。(exports = module.exports
)。
咱們可使用exports.a = ‘xxx’
或 exports.b = function () {}
添加方法或屬性,本質上它也添加在module.exports
所指向的對象身上。可是你不能直接exports = { a: 'xxx'}
這樣子的意義就是將exports
從新指向新的對象!它和module.exports
就不是指向同一個對象,也就這二者已經失去了關係,而nodejs
中require
(引入)能看到的是module.exports
指向的對象。
module.exports
。
function foo() {
console.log('foo');
}
function bar() {
console.log('bar');
}
複製代碼
想要將這兩個函數暴露出去,能夠直接使用exports
exports.foo = foo;
exports.bar = bar;
複製代碼
也能夠對module.exports
賦值
module.exports = {
foo: foo,
bar: bar
}
複製代碼
可是不能直接對exports
賦值
// 錯誤
exports = {
foo: foo,
bar: bar
}
複製代碼
由於這樣作僅僅改變了exports
的引用,而不改變module.exports
。 好,劇終。這個問題講明白了吧。
總結CommonJS
特色:同步加載,有緩存
用法:(抓住引入和暴露)
exports
module.exports
require(路徑參數)
./
或者 ../
開頭主要是在服務器端使用的,可是也能在瀏覽器端運行,須要藉助browserify
進行編譯。
CommonJS
規範加載模塊是同步的,也就是說,只有加載完成,才能執行後面的操做。因爲Node.js
主要用於服務器編程,模塊文件通常都已經存在於本地硬盤,因此加載起來比較快,因此同步加載沒有問題。可是若是是瀏覽器端,同步加載很容易阻塞,這時候AMD規範就出來了。AMD規範則是非同步加載模塊,容許指定回調函數。故瀏覽器端通常會使用AMD規範。
AMD 是 RequireJS 在推廣過程當中對模塊定義的規範化產出 。
下載require.js
, 並引入
官網: http://www.requirejs.cn/
github : https://github.com/requirejs/requirejs
將require.js
導入項目: js/libs/require.js
建立項目結構
|-libs
|-require.js
|-modules
|-alerter.js
|-dataService.js
|-main.js
|-index.html
複製代碼
定義require.js
的模塊代碼
dataService.js
(定義一個無依賴的模塊)
define(function () {
let msg = 'hello world lyuya';
function dataServer() {
return msg.toUpperCase();
}
//暴露這個模塊
return dataServer;
});
複製代碼
alerter.js
(定義一個有依賴的模塊)
定義方法:define(['模塊1', '模塊2', '模塊3'], function (m1, m2,m3) {})
注意先後一一對應
//必定要注意一一對應,前面有,後面必定要有,別忘記後面的傳參
define(['dataServer'],function (dataServer) {
let msg = dataServer();
function alerter() {
alert(msg);
}
return alerter;
});
複製代碼
應用主(入口):main.js
(主模塊)
//配置模塊的路徑
requirejs.config({
baseUrl:'./', //配置全部引入模塊的公共路徑(基本路徑)
//模塊標識名與模塊路徑映射
paths : {
// 模塊名稱(必定要與引入的模塊名稱一一對應): 模塊的路徑
dataServer: 'modular/dataServer',
//必定不能寫文件的後綴名,它會自動補全
alerter: 'modular/alerter',
//庫/框架本身實現模塊化的功能,定義了暴露模塊的名稱
jquery: 'libs/jquery-1.10.1'
}
})
//主模塊,下面requirejs能夠用require代替,require是異步可緩存的
requirejs(['alerter','jquery'],function (alerter,$) {
alerter();
$('body').css('background','pink')
});
複製代碼
在頁面index.html
中使用模塊
<!--src引入requirejs模塊去用這個模塊解析主模塊-->
<script data-main="./main" src="./libs/require.js"></script>
複製代碼
總結requireJS
特色:異步加載,有緩存
用法:(抓住引入和暴露)
return
define(['模塊名'], function (模塊暴露內容) {})
require(['模塊名'], function (模塊暴露內容) {})
require
定義異步模塊requirejs.config({}) 配置使用的模塊路徑
requirejs(['模塊名'], function (模塊暴露內容) {})
<script data-main='app.js' src='require.js'></script>
AMD(通用模塊定義)主要是在瀏覽器使用的。
CMD是根據CommonJS和AMD基礎上提出的。
CMD(通用模塊定義)和AMD(異步模塊定)是比較類似的。
RequireJS 遵循的是 AMD(異步模塊定義)規範,SeaJS 遵循的是 CMD (通用模塊定義)規範。
seaJS
是國人阿里創建的,表明着海納百川之意。
下載sea.js
, 並引入
官網: http://seajs.org/
github : https://github.com/seajs/seajs
將sea.js
導入項目: libs/sea.js
建立項目結構
|-libs
|-sea.js
|-modules
|-module1.js
|-module2.js
|-module3.js
|-module4.js
|-main.js
|-index.html
複製代碼
定義sea.js
的模塊代碼
module1.js
define(function (require, exports, module) {
/* require: 引入依賴模塊 exports: 暴露模塊 module: 暴露模塊 */
const msg = 'moduleone';
function getMsg() {
console.log('module1 getMsg() ' + msg);
return msg;
}
//暴露模塊
module.exports = getMsg;
})
複製代碼
module2.js
define(function (require, exports, module) {
exports.msg1 = 'lyuya';
exports.msg2 = 'hello';
})
複製代碼
module3.js
define(function (require, exports, module) {
//同步引入模塊
const getMsg = require('./module1');
let msg = getMsg();
msg = msg.toUpperCase();
module.exports = {
msg
}
})
複製代碼
module4.js
//異步引入模塊
require.async('./module2', function (m2) {
console.log(m2.msg1, m2.msg2);
})
console.log('module4執行了~~~');
})
複製代碼
main.js
:主(入口)模塊
define(function (require) {
const m3 = require('./module3');
require('./module4');
console.log(m3.msg);
})
複製代碼
index.html:
<script type="text/javascript" src="libs/sea.js"></script>
<script type="text/javascript"> seajs.use('./modules/main') </script>
複製代碼
結果輸出:
module1 getMsg() moduleone =====module1.js:12
module4執行了~~~ =====module4.js:9
MODULEONE =====main.js:9
lyuya hello =====module4.js:7
複製代碼
總結seaJS
特色:異步加載,有緩存
用法:
定義模塊
define(function (require, exports, module) {})
引入模塊
同步加載require()
異步加載require.async(['模塊名'], function (模塊暴露內容) {})
暴露模塊
exports
module.exports
html文件引入script標籤
<script src='sea.js'></script>
<script>seajs.use('app.js')</script>
seajs
和requirejs
同樣主要在瀏覽器中使用。其實這兩個通常都不多使用。用的比較多的是commonjs
和立刻要介紹的es6模塊化
。
ES6模塊化的出現,給前端更大的方便。旨在成爲瀏覽器和服務器通用的模塊解決方案,但仍是主要專門針對瀏覽器端。其模塊功能主要由兩個命令構成:export
和import
。如今不少項目都在使用ES6模塊化規範。
定義package.json
文件
安裝babel-cli
, babel-preset-es2015
和browserify
npm install babel-cli browserify -g
npm install babel-preset-es2015 --save-dev
preset 預設(將es6
轉換成es5
的全部插件打包)
定義.babelrc
文件
{
"presets": ["es2015"]
}
複製代碼
編碼
module1.js
分別暴露 後面須要完整的定義(變量或函數定義)
export function foo() {
console.log('module1 foo()');
}
export function bar() {
console.log('module1 bar()');
}
export const DATA_ARR = [1, 3, 5, 1]
複製代碼
module2.js
統一暴露 暴露的是一個對象,要暴露的數據添加爲對象的屬性/方法
let data = 'module2 data'
function fun1() {
console.log('module2 fun1() ' + data);
}
function fun2() {
console.log('module2 fun2() ' + data);
}
export {fun1, fun2}
複製代碼
module3.js
靜默暴露 只能暴露一個內容,默認暴露的本質:定義了default
變量,將後面的值賦值給default
變量,暴露出去
export default {
name: 'Tom',
setName: function (name) {
this.name = name
}
}
複製代碼
app.js
主模塊用import
引入模塊
import {foo, bar} from './module1'
import {DATA_ARR} from './module1'
import {fun1, fun2} from './module2'
import person from './module3'
import $ from 'jquery' //引入第三方jQuery模塊 npm install jquery@1 --save
$('body').css('background', 'red')
foo()
bar()
console.log(DATA_ARR);
fun1()
fun2()
person.setName('JACK')
console.log(person.name);
複製代碼
輸出結果:
module1 foo()
module1 bar()
[1, 3, 5, 1]
module2 fun1()
module2 fun2()
JACK
複製代碼
編譯
使用Babel將ES6編譯爲ES5代碼(但包含CommonJS
語法):babel src -d build
使用Browserify
編譯js:browserify build/app.js -o dist/bundle.js
在頁面index.html
中引入測試
<script type="text/javascript" src="lib/bundle.js"></script>
總結ES6
特色:動態引入(按需加載),沒有緩存
用法:(抓住引入和暴露)
import
import {模塊暴露的內容} from '模塊路徑';
或 import * as m1 from './module1'
import 模塊暴露的內容 from '模塊路徑'
export
主要是用在瀏覽器,服務器端也使用。可是如今瀏覽器和服務器均不支持ES6的模塊化語法,因此要藉助工具來編譯運行
babel
將ES6 - ES5 (ES6的模塊化語法 編譯成commonjs
)browserify
將commonjs
語法編譯成能讓瀏覽器識別的語法前端模塊化開發那點歷史
Javascript模塊化編程@阮一峯
知乎專欄 | AMD和CMD的區別
既然說到模塊化,其實我更想說說模塊化與組件化。這兩個概念在前端領域已經十分廣泛。
先有模塊化後有組件化。組件化是創建在模塊化思想上的一次演進,一個變種。因此,咱們會在軟件工程體系中看過一句話:模塊化是組件化的基石。
組件化和模塊化的思想都是分而治之的思想。但仍是有細小的區分,他們的側重點有所不一樣。
組件化更加傾向於UI層面上,是一個能夠獨立展現內容的「積木」,好比一個頁面的頭部組件,包含結構HTML、樣式CSS、邏輯JS、以及靜態資源圖片組合一個集合體。一個頁面是由衆多組件組成的,就像由衆多「積木」搭成的「城堡」同樣; 模塊化更加傾向於功能或者數據的封裝,通常由幾個組件或1個組件構成的帶有必定功能的集合體;
引用一下@張雲龍「👈大神」對組件化的理解:
就如上圖的這個title
組件,包含告終構HTML、樣式CSS、邏輯JS、以及靜態資源圖片,每每組件的組成就是以上四個方面。這個
header
文件夾咱們能夠拿到其餘項目中使用,它具備能夠獨立展現內容的特色。
結合前面提到的模塊化開發,整個前端項目能夠劃分爲這麼幾種開發概念:
那麼它們之間的關係以下圖所示,一個 應用由多個下圖的 頁面組成。一個 頁面由多個 組件組合。組件中可依賴JS模塊。 因此,前端開發如今不只僅只是別人說的「畫畫頁面實現點效果」的職位,它是實現軟件的圖形用戶界面(Graphical User Interface,簡稱GUI),是一名軟件工程師。如今前端開發都是基於模塊化和組件化的開發,能夠說算是工程化的項目了。從單頁面(SPA)的應用就能夠看出JavaScript大大改善了Web應用的用戶體驗。從谷歌提出PWA(Progressive Web Apps)就能夠看出前端在領域的成長。不只僅如此,多終端也已經成爲時下以及將來的一個必然趨勢,移動端、PC端、觸摸屏、智能設備、物聯網等等,相信前端在跨端的領域下確定會有更好的解決方案。
可是,若是從整個軟件工程來看,咱們就會意識到一個慘痛的事實:前端工程師在整個系統工程中的地位過低了。前端是處於系統軟件的上游(用戶入口),所以沒有其餘系統會來調取前端系統的服務。然後端它在軟件開發中處於下游,後端一方面要爲前端提供接口服務,一方面要向中後臺以及數據層索取服務,對接層次會更多,地位也就更高了。由此致使,感受每次需求評估前端每每是最後一道坎,由於上游依託下游,就只能是下游先行了,總體上就會感受前端對業務的參與度過低了。
甚至,2019了。如今仍是有不少團隊會把前端開發歸類爲產品或設計崗位底下,嗯,我很差說什麼,唉···。
你在的公司前端的組織架構是腫麼樣吶??? 🙋🙋
前端將來必定不會差,就像在人工智能和大數據領域下,不止於前端,前端徹底能夠融合和細化下去。
引用一位螞蟻夥伴的話來講:前兩年的前端主要矛盾是日益爆發的前端新技術同前端程序猿學不動之間的矛盾,而如今主要矛盾發生了變化,變成了前端日益增加的工程地位訴求同前端工程侷限性之間的矛盾。(這人考研政治絕對高分!)
在這樣新的矛盾下,咱們就要化被動爲主動,改接受爲影響。
好啦,好好學習吧,作一個π
型人。打鐵還需自身硬。Confidence~🙌
今天是2019年3月8日,農曆二月二(龍擡頭),星期五,陰陰天氣。我在深圳祝福各位女同胞唷節日快樂永遠美麗,祝福男同胞單身幸福~biubiu💖💗💙💚💛💜💝
去泡個澡,今晚早點休息,明天還要去北京大學深圳醫院。
此文檔做者:呂涯 CSDN主頁:https://blog.csdn.net/LY_code 掘金主頁:https://juejin.im/user/5b220d93e51d4558e03cb948 如有錯誤,及時提出,一塊兒學習,共同進步。謝謝。 😝😝😝