Web sites are turning into Web Apps.
Code complexity(複雜度) grows as the site gets bigger.
Highly decoupled(解耦) JS files/modules is wanted.
Deployment(部署) wants optimized(優化) code in few HTTP calls.
避免命名衝突(減小命名空間污染)
更好的分離,按需加載
更高複用性
高可維護性
請求過多
依賴模糊
難以維護
每一個文件均可當作一個模塊
在服務器端: 模塊的加載是運行時同步加載的。
在瀏覽器端: 模塊須要提早編譯打包處理。
基本語法:
module.exports = value
exports.xxx = value
require(xxx)
實現:
安裝Node.js
建立項目結構
|-modules |-module1.js |-module2.js |-module3.js |-app.js |-package.json { "name": "commonJS-node", "version": "1.0.0" }
npm install uniq --save
module1.js
module.exports = { foo() { console.log('module1 foo()'); } };
module2.js
module.exports = function() { console.log('module2()'); };
module3.js
exports.foo = function() { console.log('module3 foo()'); }; exports.bar = function() { console.log('module3 bar()'); };
app.js
/* * 1. 定義暴露模塊: * module.exports = value; * exports.xxx = value; * 2. 引入模塊: * var module = require(模塊名或模塊路徑); */ // 引用模塊 let fs = require('fs'); // fs是nodejs中內置的文件系統模塊 let uniq = require('uniq'); // 下載的第三方模塊,功能是數組排序去重 let module1 = require('./modules/module1'); // 自定義的module let module2 = require('./modules/module2'); let module3 = require('./modules/module3'); // 使用模塊 module1.foo(); module2(); console.log(uniq([1, 3, 2, 4, 2])); fs.readFile('app.js', function(error, data){ console.log(data.toString()); })
經過node運行app.js: node app.js
因爲瀏覽器端不具有node那樣的環境,不能識別require
等方法,因此瀏覽器端的模塊化須要藉助Browserify
工具來完成打包,以便瀏覽器識別。
建立項目結構
|-js |-dist // 打包生成文件的目錄 |-src // 源碼所在的目錄 |-module1.js |-module2.js |-module3.js |-app.js // 應用主源文件 |-index.html |-package.json { "name": "browserify-test", "version": "1.0.0" }
下載browserify
npm install browserify -g
// 全局環境安裝npm install browserify --save-dev
// 只是幫助咱們編譯打包文件,在開發環境(-dev)下安裝便可,未來生產環境並不須要安裝完後,package.json中會變成:
{ "name": "browserify-test", "version": "1.0.0" "devDependencies": {"browserify": 版本號}, // 開發環境下的依賴包 "dependencies": {"uniq": 版本號} // 全局依賴包 }
定義模塊代碼
打包處理js源文件:
browserify js/src/app.js -o js/dist/build.js
上面的命令執行完畢後,生成了打包後的文件,就能夠在html頁面中引入了:
<script type="text/javascript" src="js/dist/build.js"></script>
Asynchronous Module Definition(異步模塊定義)
專門用於瀏覽器端,模塊的加載是異步的。
語法:
定義暴露模塊
定義沒有依賴的模塊
define(function(){ return 模塊 })
定義有依賴的模塊
define(['module1','module2'], function(m1, m2){ return 模塊 })
引入使用模塊
require(['module1','module2'], function(m1, m2){ //顯式聲明依賴注入 使用m1/m2 })
實現: Require.js
在沒有使用AMD規範(require.js)的時候,咱們經過多個script標籤來按照依賴順序依次引入js文件,
這樣不但增長了HTTP請求數,更增長了維護的難度,容易出錯。
經過模塊加載器require.js
來加載js模塊:
下載Require.JS,官網: http://www.requirejs.cn/
建立項目結構
|-js |-libs |-require.js |-modules |-module1.js |-module2.js |-main.js |-index.html
定義模塊代碼
module1.js
define(function(){ let msg = 'hello'; function getMsg(){ return msg.toUpperCase(); } return {getMsg}; });
module2.js
define(['module1', 'jquery'], function(m1, $){ let name = "module2"; function showMsg(){ $('body').css('background', "red"); alert(m1.getMsg() + ',' + name); } return {showMsg}; });
編寫應用主入口: main.js
(function () { // 配置 require.config({ //基本路徑 baseUrl: "js/", //模塊標識名與模塊路徑映射 paths: { "module1": "./modules/module1", // 內部會給路徑自動加上.js擴展名 "module2": "./modules/module2", } }); // 引入使用模塊 require(['module2'], function(module2){ module2.showMsg(); }) })()
頁面使用模塊
<script data-main="js/main.js" src="js/libs/require.js"></script>
require.js 在加載的時候會檢查 data-main
屬性:
能夠在data-main指向的腳本中設置模板加載 選項,而後加載第一個應用模塊。
注意:你在main.js中所設置的腳本是異步加載的。因此若是你在頁面中配置了其它JS加載,則不能保證它們所依賴的JS已經加載成功。
例如:
<script data-main="scripts/main" src="scripts/require.js"></script> <script src="scripts/other.js"></script>
// contents of main.js: require.config({ paths: { foo: 'libs/foo-1.1.3' } });
// contents of other.js: // This code might be called before the require.config() in main.js // has executed. When that happens, require.js will attempt to // load 'scripts/foo.js' instead of 'scripts/libs/foo-1.1.3.js' require( ['foo'], function( foo ) { });
使用第三方基於require.js的框架(jquery)
jQuery支持AMD規範,在源碼的最後幾行,define("jquery", [], function(){return jQuery;});
這說明jQuery暴露了一個模塊接口,而且標識名爲jquery。
注意在引入的時候,寫jquery
而不是jQuery
。
將jQuery庫文件導入到項目的libs目錄中,而後在main.js中配置jquery路徑:
path: { 'jquery': './libs/jquery-1.10.1' }
接下來就可使用在module中了。
define(['module1', 'jquery'], function (module1, $) { var name = 'Tom'; function showMsg() { $('body').css({background : 'red'}) alert(name + ' ' + module1.getMsg()) } return {showMsg} })
使用第三方不基於require.js的框架(angular)
將angular.js/angular-messages.js導入項目目錄,而後在paths中添加angular路徑。
爲了配置不兼容AMD的模塊,須要在require.config中多添加:
shim: { 'angular': { exports: 'angular' }, `angular-messages`: { exports: 'angular-message', deps: ['angular'] } }
專門應用於瀏覽器端,模塊的加載是異步的。
實現: sea.js
,github: https://github.com/seajs/seajs
模塊使用時纔會加載執行。
語法:
定義暴露模塊
定義沒有依賴的模塊
define(function(require, exports, module){ exports.xxx = value; module.exports = value; })
定義有依賴的模塊
define(function(require, exports, module){ //引入依賴模塊(同步) var module2 = require('./module2'); //引入依賴模塊(異步),注意異步的function會在主線程執行完畢再執行,所以輸出順序可能變化 require.async('./module3', function(m3){ }) //暴露模塊 exports.xxx = value; })
引入使用模塊
define(function(require){ var m1 = require('./module1'); var m4 = require('./module4'); m1.show(); m4.show(); })
CMD規範,定義模塊相似AMD,暴露模塊相似Commonjs。
使用方法:
下載sea.js並引入到libs。
建立項目結構
|-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) { var data = "hello"; function show(){ console.log('module1 show()' + data); } // 向外暴露 exports.show = show; })
主入口模塊: main.js
define(function(require){ var m1 = require('./module1'); m1.show(); })
在index頁面引入
<script type="text/javascript" src="js/libs/sea.js"></script> <script type="text/javascript"> seajs.use('./js/modules/main.js') </script>
ES6中內置了js模塊化的實現。
語法:
定義暴露模塊: export
暴露一個對象(默認暴露):
export default 對象
能夠暴露任意數據類型,暴露什麼就接收到什麼。
暴露多個對象(常規暴露):
// 分別暴露 export var xxx = value1; export let yyy = value2; // 統一暴露 var xxx = value1; let yyy = value2; export {xxx, yyy}
統一暴露或者分別暴露,在引入的時候必須用對象解構賦值的形式。
引入使用模塊: import
默認暴露的模塊:
import xxx from '模塊路徑/模塊名'
其餘模塊
import {xxx, yyy} from '模塊路徑/模塊名' import * as module1 from '模塊路徑/模塊名'
問題:
一些瀏覽器還不能直接識別ES6模塊化的的語法。
須要使用Babel
來將ES6轉換爲ES5,但因爲內部還使用了CommonJS
,因此瀏覽器仍然不能直接執行。
接着再次使用Browserify
來將文件打包處理,最終引入頁面,瀏覽器能夠直接運行。
js轉換及打包方法:
定義package.json文件
{ "name": "es6_babel_browserify-test", "version": "1.0.0" }
安裝babel-cli, babel-preset-es2015和browserify
npm install babel-cli browserify -g
npm install babel-preset-es2015 --save-dev
定義.babelrc
配置文件(babel在執行以前會先讀取該文件)
{ "presets": ["es2015"] // 該命令決定了babel要去執行的任務,"es2015"表示ES6語法轉換 }
編寫模塊
js/src/module1.js
something...
js/src/app.js
import {fun1, fun2} from './module1'; import $ from 'jquery'; $('body').css('background', 'red'); fun1(); fun2();
編譯打包
使用Babel編譯爲ES5語法(包含CommonJS):
babel js/src -d js/build
(能夠自動生成新目錄build)
使用Browserify打包js:
browserify js/build/app.js -o js/dist/bundle.js
(不能自動生成dist目錄)
頁面中引入
<script type="text/javascript" src="js/build/bundle.js"></script>
引入第三方模塊(jQuery)
下載jQuery模塊:
npm install jquery@1 --save
(模塊後加@表明下載相應版本號下的最新版本)
在app.js中引入使用:
import $ from 'jquery'
注意:
當改變了模塊中的代碼後,須要從新轉換(Babel)、編譯打包(Browserify),再引入頁面。