JS模塊化的前世今生

在JavaScript發(fā)展初期前端就是為了實(shí)現(xiàn)簡(jiǎn)單的頁面交互邏輯矾策,寥寥數(shù)語即可磷账;如今CPU见间、瀏覽器性能得到了極大的提升蛙奖,很多頁面邏輯遷移到了客戶端(表單驗(yàn)證等)挟伙,隨著web2.0時(shí)代的到來故爵,Ajax技術(shù)得到廣泛應(yīng)用,jQuery等前端庫層出不窮陵珍,前端代碼日益膨脹粹污,此時(shí)在JS方面就會(huì)考慮使用模塊化規(guī)范去管理拂玻。

1.模塊化的理解

1.1.什么是模塊?

  • 將一個(gè)復(fù)雜的程序依據(jù)一定的規(guī)則(規(guī)范)封裝成幾個(gè)塊(文件), 并進(jìn)行組合在一起
  • 塊的內(nèi)部數(shù)據(jù)與實(shí)現(xiàn)是私有的, 只是向外部暴露一些接口(方法)與外部其它模塊通信

1.2.模塊化的進(jìn)化過程

  • 全局function模式 : 將不同的功能封裝成不同的全局函數(shù)
    • 編碼: 將不同的功能封裝成不同的全局函數(shù)
    • 問題: 污染全局命名空間, 容易引起命名沖突或數(shù)據(jù)不安全地粪,而且模塊成員之間看不出直接關(guān)系
function m1(){
  //...
}
function m2(){
  //...
}
  • namespace模式 : 簡(jiǎn)單對(duì)象封裝
    • 作用: 減少了全局變量取募,解決命名沖突
    • 問題: 數(shù)據(jù)不安全(外部可以直接修改模塊內(nèi)部的數(shù)據(jù))
let myModule = {
  data: 'www.baidu.com',
  foo() {
    console.log(`foo() ${this.data}`)
  },
  bar() {
    console.log(`bar() ${this.data}`)
  }
}
myModule.data = 'other data' //能直接修改模塊內(nèi)部的數(shù)據(jù)
myModule.foo() // foo() other data

這樣的寫法會(huì)暴露所有模塊成員,內(nèi)部狀態(tài)可以被外部改寫蟆技。

  • IIFE模式:匿名函數(shù)自調(diào)用(閉包)
    • 作用: 數(shù)據(jù)是私有的, 外部只能通過暴露的方法操作
    • 編碼: 將數(shù)據(jù)和行為封裝到一個(gè)函數(shù)內(nèi)部, 通過給window添加屬性來向外暴露接口
    • 問題: 如果當(dāng)前這個(gè)模塊依賴另一個(gè)模塊怎么辦?
// index.html文件
<script type="text/javascript" src="module.js"></script>
<script type="text/javascript">
    myModule.foo()
    myModule.bar()
    console.log(myModule.data) //undefined 不能訪問模塊內(nèi)部數(shù)據(jù)
    myModule.data = 'xxxx' //不是修改的模塊內(nèi)部的data
    myModule.foo() //沒有改變
</script>
// module.js文件
(function(window) {
  let data = 'www.baidu.com'
  //操作數(shù)據(jù)的函數(shù)
  function foo() {
    //用于暴露函數(shù)
    console.log(`foo() ${data}`)
  }
  function bar() {
    //用于暴露函數(shù)
    console.log(`bar() ${data}`)
    otherFun() //內(nèi)部調(diào)用
  }
  function otherFun() {
    //內(nèi)部私有的函數(shù)
    console.log('otherFun()')
  }
  //暴露行為
  window.myModule = { foo, bar } //ES6寫法
})(window)
  • IIFE模式增強(qiáng) : 引入依賴

這就是現(xiàn)代模塊實(shí)現(xiàn)的基石

// module.js文件
(function(window, $) {
  let data = 'www.baidu.com'
  //操作數(shù)據(jù)的函數(shù)
  function foo() {
    //用于暴露有函數(shù)
    console.log(`foo() ${data}`)
    $('body').css('background', 'red')
  }
  function bar() {
    //用于暴露有函數(shù)
    console.log(`bar() ${data}`)
    otherFun() //內(nèi)部調(diào)用
  }
  function otherFun() {
    //內(nèi)部私有的函數(shù)
    console.log('otherFun()')
  }
  //暴露行為
  window.myModule = { foo, bar }
})(window, jQuery)
 // index.html文件
  <!-- 引入的js必須有一定順序 -->
  <script type="text/javascript" src="jquery-1.10.1.js"></script>
  <script type="text/javascript" src="module.js"></script>
  <script type="text/javascript">
    myModule.foo()
  </script>

上例子通過jquery方法將頁面的背景顏色改成紅色玩敏,所以必須先引入jQuery庫,就把這個(gè)庫當(dāng)作參數(shù)傳入质礼。這樣做除了保證模塊的獨(dú)立性旺聚,還使得模塊之間的依賴關(guān)系變得明顯

1.3.模塊化的好處

  • 避免命名沖突(減少命名空間污染)
  • 更好的分離, 按需加載
  • 更高復(fù)用性
  • 高可維護(hù)性

1.4.引入多個(gè)<script>后出現(xiàn)出現(xiàn)問題

  • 請(qǐng)求過多

首先我們要依賴多個(gè)模塊眶蕉,那樣就會(huì)發(fā)送多個(gè)請(qǐng)求砰粹,導(dǎo)致請(qǐng)求過多

  • 依賴模糊

我們不知道他們的具體依賴關(guān)系是什么,也就是說很容易因?yàn)椴涣私馑麄冎g的依賴關(guān)系導(dǎo)致加載先后順序出錯(cuò)造挽。

  • 難以維護(hù)

以上兩種原因就導(dǎo)致了很難維護(hù)伸眶,很可能出現(xiàn)牽一發(fā)而動(dòng)全身的情況導(dǎo)致項(xiàng)目出現(xiàn)嚴(yán)重的問題。
模塊化固然有多個(gè)好處刽宪,然而一個(gè)頁面需要引入多個(gè)js文件,就會(huì)出現(xiàn)以上這些問題界酒。而這些問題可以通過模塊化規(guī)范來解決圣拄,下面介紹開發(fā)中最流行的commonjs, AMD, ES6, CMD規(guī)范。

2.模塊化規(guī)范

2.1.CommonJS

(1)概述

Node 應(yīng)用由模塊組成毁欣,采用 CommonJS 模塊規(guī)范庇谆。每個(gè)文件就是一個(gè)模塊,有自己的作用域凭疮。在一個(gè)文件里面定義的變量饭耳、函數(shù)、類执解,都是私有的寞肖,對(duì)其他文件不可見。在服務(wù)器端,模塊的加載是運(yùn)行時(shí)同步加載的新蟆;在瀏覽器端觅赊,模塊需要提前編譯打包處理。

(2)特點(diǎn)

  • 所有代碼都運(yùn)行在模塊作用域琼稻,不會(huì)污染全局作用域吮螺。
  • 模塊可以多次加載,但是只會(huì)在第一次加載時(shí)運(yùn)行一次帕翻,然后運(yùn)行結(jié)果就被緩存了鸠补,以后再加載,就直接讀取緩存結(jié)果嘀掸。要想讓模塊再次運(yùn)行紫岩,必須清除緩存。
  • 模塊加載的順序横殴,按照其在代碼中出現(xiàn)的順序被因。

(3)基本語法

  • 暴露模塊:module.exports = valueexports.xxx = value
  • 引入模塊:require(xxx),如果是第三方模塊,xxx為模塊名衫仑;如果是自定義模塊梨与,xxx為模塊文件路徑

此處我們有個(gè)疑問:CommonJS暴露的模塊到底是什么? CommonJS規(guī)范規(guī)定,每個(gè)模塊內(nèi)部文狱,module變量代表當(dāng)前模塊粥鞋。這個(gè)變量是一個(gè)對(duì)象,它的exports屬性(即module.exports)是對(duì)外的接口瞄崇。加載某個(gè)模塊呻粹,其實(shí)是加載該模塊的module.exports屬性

// example.js
var x = 5;
var addX = function (value) {
  return value + x;
};
module.exports.x = x;
module.exports.addX = addX;

上面代碼通過module.exports輸出變量x和函數(shù)addX苏研。

var example = require('./example.js');//如果參數(shù)字符串以“./”開頭等浊,則表示加載的是一個(gè)位于相對(duì)路徑
console.log(example.x); // 5
console.log(example.addX(1)); // 6

require命令用于加載模塊文件。require命令的基本功能是摹蘑,讀入并執(zhí)行一個(gè)JavaScript文件筹燕,然后返回該模塊的exports對(duì)象。如果沒有發(fā)現(xiàn)指定模塊衅鹿,會(huì)報(bào)錯(cuò)撒踪。

(4)模塊的加載機(jī)制

CommonJS模塊的加載機(jī)制是,輸入的是被輸出的值的拷貝大渤。也就是說制妄,一旦輸出一個(gè)值,模塊內(nèi)部的變化就影響不到這個(gè)值泵三。這點(diǎn)與ES6模塊化有重大差異(下文會(huì)介紹)耕捞,請(qǐng)看下面這個(gè)例子:

// lib.js
var counter = 3;
function incCounter() {
  counter++;
}
module.exports = {
  counter: counter,
  incCounter: incCounter,
};

上面代碼輸出內(nèi)部變量counter和改寫這個(gè)變量的內(nèi)部方法incCounter衔掸。

// main.js
var counter = require('./lib').counter;
var incCounter = require('./lib').incCounter;

console.log(counter);  // 3
incCounter();
console.log(counter); // 3

上面代碼說明,counter輸出以后砸脊,lib.js模塊內(nèi)部的變化就影響不到counter了具篇。這是因?yàn)閏ounter是一個(gè)原始類型的值,會(huì)被緩存凌埂。除非寫成一個(gè)函數(shù)驱显,才能得到內(nèi)部變動(dòng)后的值

(5)服務(wù)器端實(shí)現(xiàn)

①下載安裝node.js

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

注意:用npm init 自動(dòng)生成package.json時(shí)瞳抓,package name(包名)不能有中文和大寫

|-modules
  |-module1.js
  |-module2.js
  |-module3.js
|-app.js
|-package.json
  {
    "name": "commonJS-node",
    "version": "1.0.0"
  }

③下載第三方模塊

npm install uniq --save // 用于數(shù)組去重

④定義模塊代碼

//module1.js
module.exports = {
  msg: 'module1',
  foo() {
    console.log(this.msg)
  }
}
//module2.js
module.exports = function() {
  console.log('module2')
}
//module3.js
exports.foo = function() {
  console.log('foo() module3')
}
exports.arr = [1, 2, 3, 3, 2]
// app.js文件
// 引入第三方庫埃疫,應(yīng)該放置在最前面
let uniq = require('uniq')
let module1 = require('./modules/module1')
let module2 = require('./modules/module2')
let module3 = require('./modules/module3')

module1.foo() //module1
module2() //module2
module3.foo() //foo() module3
console.log(uniq(module3.arr)) //[ 1, 2, 3 ]

⑤通過node運(yùn)行app.js

命令行輸入node app.js,運(yùn)行JS文件

(6)瀏覽器端實(shí)現(xiàn)(借助Browserify)

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

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

②下載browserify

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

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

注意:index.html文件要運(yùn)行在瀏覽器上孩哑,需要借助browserify將app.js文件打包編譯栓霜,如果直接在index.html引入app.js就會(huì)報(bào)錯(cuò)!

④打包處理js

根目錄下運(yùn)行browserify js/src/app.js -o js/dist/bundle.js

⑤頁面使用引入

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

2.2.AMD

CommonJS規(guī)范加載模塊是同步的横蜒,也就是說胳蛮,只有加載完成,才能執(zhí)行后面的操作丛晌。AMD規(guī)范則是非同步加載模塊仅炊,允許指定回調(diào)函數(shù)。由于Node.js主要用于服務(wù)器編程澎蛛,模塊文件一般都已經(jīng)存在于本地硬盤抚垄,所以加載起來比較快,不用考慮非同步加載的方式谋逻,所以CommonJS規(guī)范比較適用呆馁。但是,如果是瀏覽器環(huán)境毁兆,要從服務(wù)器端加載模塊浙滤,這時(shí)就必須采用非同步模式,因此瀏覽器端一般采用AMD規(guī)范气堕。此外AMD規(guī)范比CommonJS規(guī)范在瀏覽器端實(shí)現(xiàn)要來著早纺腊。

(1)AMD規(guī)范基本語法

定義暴露模塊:

//定義沒有依賴的模塊
define(function(){
   return 模塊
})
//定義有依賴的模塊
define(['module1', 'module2'], function(m1, m2){
   return 模塊
})

引入使用模塊:

require(['module1', 'module2'], function(m1, m2){
   使用m1/m2
})

(2)未使用AMD規(guī)范與使用require.js

通過比較兩者的實(shí)現(xiàn)方法,來說明使用AMD規(guī)范的好處送巡。

  • 未使用AMD規(guī)范
// dataService.js文件
(function (window) {
  let msg = 'www.baidu.com'
  function getMsg() {
    return msg.toUpperCase()
  }
  window.dataService = {getMsg}
})(window)
 // alerter.js文件
(function (window, dataService) {
  let name = 'Tom'
  function showMsg() {
    alert(dataService.getMsg() + ', ' + name)
  }
  window.alerter = {showMsg}
})(window, dataService)
// main.js文件
(function (alerter) {
  alerter.showMsg()
})(alerter)
// 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>

最后得到如下結(jié)果:WWW.BAIDU.COM,Tom

這種方式缺點(diǎn)很明顯:首先會(huì)發(fā)送多個(gè)請(qǐng)求,其次引入的js文件順序不能搞錯(cuò)盒卸,否則會(huì)報(bào)錯(cuò)骗爆!

  • 使用require.js

RequireJS是一個(gè)工具庫,主要用于客戶端的模塊管理蔽介。它的模塊管理遵守AMD規(guī)范摘投,RequireJS的基本思想是煮寡,通過define方法,將代碼定義為模塊犀呼;通過require方法幸撕,實(shí)現(xiàn)代碼的模塊加載
接下來介紹AMD規(guī)范在瀏覽器實(shí)現(xiàn)的步驟:

①下載require.js, 并引入

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

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

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

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

③定義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ā)點(diǎn)在根目錄下
    paths: {
      //映射: 模塊標(biāo)識(shí)名: 路徑
      alerter: './modules/alerter', //此處不能寫成alerter.js,會(huì)報(bào)錯(cuò)
      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>

④頁面引入require.js模塊:

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

此外在項(xiàng)目中如何引入第三方庫外臂?只需在上面代碼的基礎(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ā)點(diǎn)在根目錄下
    paths: {
      //自定義模塊
      alerter: './modules/alerter', //此處不能寫成alerter.js,會(huì)報(bào)錯(cuò)
      dataService: './modules/dataService',
      // 第三方庫模塊
      jquery: './libs/jquery-1.10.1' //注意:寫成jQuery會(huì)報(bào)錯(cuò)
    }
  })
  require(['alerter'], function(alerter) {
    alerter.showMsg()
  })
})()

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

2.3.CMD

CMD規(guī)范專門用于瀏覽器端酌毡,模塊的加載是異步的,模塊使用時(shí)才會(huì)加載執(zhí)行蕾管。CMD規(guī)范整合了CommonJS和AMD規(guī)范的特點(diǎn)枷踏。在 Sea.js 中,所有 JavaScript 模塊都遵循 CMD模塊定義規(guī)范娇掏。

(1)CMD規(guī)范基本語法

定義暴露模塊:

//定義沒有依賴的模塊
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()
})

(2)sea.js簡(jiǎn)單使用教程

①下載sea.js, 并引入

  • 官網(wǎng):<http://seajs.org/>
  • github :<https://github.com/seajs/seajs>

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

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

|-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) {
  //內(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()
})

④在index.html中引入

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

最后得到結(jié)果如下:

Live reload enabled
module1 show() atguigu.com
module4 show() I Wil Back
異步引入依賴模塊3 abc123

2.4.ES6模塊化

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

(1)ES6模塊化語法

export命令用于規(guī)定模塊的對(duì)外接口番电,import命令用于輸入其他模塊提供的功能岗屏。

/** 定義模塊 math.js **/
var basicNum = 0;
var add = function (a, b) {
    return a + b;
};
export { basicNum, add };
/** 引用模塊 **/
import { basicNum, add } from './math';
function test(ele) {
    ele.textContent = add(99 + basicNum);
}

如上例所示,使用import命令的時(shí)候漱办,用戶需要知道所要加載的變量名或函數(shù)名这刷,否則無法加載。為了給用戶提供方便娩井,讓他們不用閱讀文檔就能加載模塊暇屋,就要用到export default命令,為模塊指定默認(rèn)輸出洞辣。

// export-default.js
export default function () {
  console.log('foo');
}
// import-default.js
import customName from './export-default';
customName(); // 'foo'

模塊默認(rèn)輸出, 其他模塊加載該模塊時(shí)咐刨,import命令可以為該匿名函數(shù)指定任意名字昙衅。

(2)ES6 模塊與 CommonJS 模塊的差異

它們有兩個(gè)重大差異:

① CommonJS 模塊輸出的是一個(gè)值的拷貝,ES6 模塊輸出的是值的引用定鸟。

② CommonJS 模塊是運(yùn)行時(shí)加載而涉,ES6 模塊是編譯時(shí)輸出接口

第二個(gè)差異是因?yàn)?CommonJS 加載的是一個(gè)對(duì)象(即module.exports屬性)联予,該對(duì)象只有在腳本運(yùn)行完才會(huì)生成啼县。而 ES6 模塊不是對(duì)象,它的對(duì)外接口只是一種靜態(tài)定義躯泰,在代碼靜態(tài)解析階段就會(huì)生成谭羔。

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

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

ES6 模塊的運(yùn)行機(jī)制與 CommonJS 不一樣麦向。ES6 模塊是動(dòng)態(tài)引用瘟裸,并且不會(huì)緩存值,模塊里面的變量綁定其所在的模塊诵竭。

(3) ES6-Babel-Browserify使用教程

簡(jiǎn)單來說就一句話:使用Babel將ES6編譯為ES5代碼话告,使用Browserify編譯打包js

①定義package.json文件

 {
   "name" : "es6-babel-browserify",
   "version" : "1.0.0"
 }

*②安裝babel-cli, babel-preset-es2015和browserify

  • npm install babel-cli browserify -g

  • npm install babel-preset-es2015 --save-dev

  • preset 預(yù)設(shè)(將es6轉(zhuǎn)換成es5的所有插件打包)

③定義.babelrc文件

  {
    "presets": ["es2015"]
  }

④定義模塊代碼

//module1.js文件
// 分別暴露
export function foo() {
  console.log('foo() module1')
}
export function bar() {
  console.log('bar() module1')
}
//module2.js文件
// 統(tǒng)一暴露
function fun1() {
  console.log('fun1() module2')
}
function fun2() {
  console.log('fun2() module2')
}
export { fun1, fun2 }
//module3.js文件
// 默認(rèn)暴露 可以暴露任意數(shù)據(jù)類項(xiàng)卵慰,暴露什么數(shù)據(jù)沙郭,接收到就是什么數(shù)據(jù)
export default () => {
  console.log('默認(rèn)暴露')
}
// app.js文件
import { foo, bar } from './module1'
import { fun1, fun2 } from './module2'
import module3 from './module3'
foo()
bar()
fun1()
fun2()
module3()

⑤ 編譯并在index.html中引入

  • 使用Babel將ES6編譯為ES5代碼(但包含CommonJS語法) : babel js/src -d js/lib
  • 使用Browserify編譯js : browserify js/lib/app.js -o js/lib/bundle.js

然后在index.html文件中引入

<script type="text/javascript" src="js/lib/bundle.js"></script>

最后得到如下結(jié)果:

foo() module1
bar() module1
fun1() module2
fun2() module2
默認(rèn)暴露

此外第三方庫(以jQuery為例)如何引入呢
首先安裝依賴npm install jquery
然后在app.js文件中引入

//app.js文件
import { foo, bar } from './module1'
import { fun1, fun2 } from './module2'
import module3 from './module3'
import $ from 'jquery'

foo()
bar()
fun1()
fun2()
module3()
$('body').css('background', 'green')

3.總結(jié)

  • CommonJS規(guī)范主要用于服務(wù)端編程裳朋,加載模塊是同步的病线,這并不適合在瀏覽器環(huán)境,因?yàn)橥揭馕吨枞虞d鲤嫡,瀏覽器資源是異步加載的送挑,因此有了AMD CMD解決方案。
  • AMD規(guī)范在瀏覽器環(huán)境中異步加載模塊暖眼,而且可以并行加載多個(gè)模塊惕耕。不過,AMD規(guī)范開發(fā)成本高诫肠,代碼的閱讀和書寫比較困難司澎,模塊定義方式的語義不順暢。
  • CMD規(guī)范與AMD規(guī)范很相似栋豫,都用于瀏覽器編程挤安,依賴就近,延遲執(zhí)行丧鸯,可以很容易在Node.js中運(yùn)行蛤铜。不過,依賴SPM 打包,模塊的加載邏輯偏重
  • ES6 在語言標(biāo)準(zhǔn)的層面上昂羡,實(shí)現(xiàn)了模塊功能,而且實(shí)現(xiàn)得相當(dāng)簡(jiǎn)單摔踱,完全可以取代 CommonJS 和 AMD 規(guī)范虐先,成為瀏覽器和服務(wù)器通用的模塊解決方案
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末派敷,一起剝皮案震驚了整個(gè)濱河市蛹批,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌篮愉,老刑警劉巖腐芍,帶你破解...
    沈念sama閱讀 218,451評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異试躏,居然都是意外死亡猪勇,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門颠蕴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來泣刹,“玉大人,你說我怎么就攤上這事犀被∫文” “怎么了?”我有些...
    開封第一講書人閱讀 164,782評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵寡键,是天一觀的道長(zhǎng)掀泳。 經(jīng)常有香客問我,道長(zhǎng)西轩,這世上最難降的妖魔是什么员舵? 我笑而不...
    開封第一講書人閱讀 58,709評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮遭商,結(jié)果婚禮上固灵,老公的妹妹穿的比我還像新娘。我一直安慰自己劫流,他們只是感情好巫玻,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,733評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著祠汇,像睡著了一般仍秤。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上可很,一...
    開封第一講書人閱讀 51,578評(píng)論 1 305
  • 那天诗力,我揣著相機(jī)與錄音,去河邊找鬼我抠。 笑死苇本,一個(gè)胖子當(dāng)著我的面吹牛袜茧,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播瓣窄,決...
    沈念sama閱讀 40,320評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼笛厦,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了俺夕?” 一聲冷哼從身側(cè)響起裳凸,我...
    開封第一講書人閱讀 39,241評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎劝贸,沒想到半個(gè)月后姨谷,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,686評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡映九,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,878評(píng)論 3 336
  • 正文 我和宋清朗相戀三年梦湘,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片件甥。...
    茶點(diǎn)故事閱讀 39,992評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡践叠,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出嚼蚀,到底是詐尸還是另有隱情禁灼,我是刑警寧澤,帶...
    沈念sama閱讀 35,715評(píng)論 5 346
  • 正文 年R本政府宣布轿曙,位于F島的核電站弄捕,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏导帝。R本人自食惡果不足惜守谓,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,336評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望您单。 院中可真熱鬧斋荞,春花似錦、人聲如沸虐秦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽悦陋。三九已至蜈彼,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間俺驶,已是汗流浹背幸逆。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人还绘。 一個(gè)月前我還...
    沈念sama閱讀 48,173評(píng)論 3 370
  • 正文 我出身青樓楚昭,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親拍顷。 傳聞我的和親對(duì)象是個(gè)殘疾皇子哪替,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,947評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 前言在 JavaScript 發(fā)展初期就是為了實(shí)現(xiàn)簡(jiǎn)單的頁面交互邏輯,寥寥數(shù)語即可菇怀;如今 CPU、瀏覽器性能得到了...
    前端一菜鳥閱讀 833評(píng)論 0 9
  • 前言 在JavaScript發(fā)展初期就是為了實(shí)現(xiàn)簡(jiǎn)單的頁面交互邏輯晌块,寥寥數(shù)語即可爱沟;如今CPU、瀏覽器性能得到了極大...
    Aniugel閱讀 1,470評(píng)論 0 0
  • //字符串轉(zhuǎn)變?yōu)閿?shù)組1 NSMutableString str=[[NSMutableStringalloc]in...
    PlatonsDream閱讀 489評(píng)論 0 0
  • 母親坐在床邊幽幽的開口“如果我們走了匆背,你就孤身一人了呼伸,快點(diǎn)找個(gè)對(duì)象結(jié)婚吧~” 我是一個(gè)三十+的中年北漂剩女,過著每...
    古蔥閱讀 17評(píng)論 0 0
  • 匡開草 01 在眾多電器中铃辖,冰箱當(dāng)之無愧是我孩童時(shí)期最期待有的家庭物件。那時(shí)憑空就有這么種錯(cuò)覺:有了冰箱猪叙,那就等同...
    青石路上匡開草閱讀 374評(píng)論 0 0