標籤:檔案路徑 ext 屬性 css 檔案系統 async jquery 命名 .config
JavaScript模組化規範詳解目錄
為什麼要模組化
模組化的好處
頁面引入載入script存在的問題
模組化規範
為什麼要模組化?
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.
模組化的好處
避免命名衝突(減少命名空間汙染)
更好的分離,按需載入
更高複用性
高可維護性
頁面引入載入script存在的問題:
模組化規範
CommonJS
每個檔案都可當做一個模組
在伺服器端: 模組的載入是運行時同步載入的。
在瀏覽器端: 模組需要提前編譯打包處理。
基本文法:
- 暴露模組:
module.exports = value
exports.xxx = value
- 引入模組:
require(xxx)
- 第三方模組: xxx為模組名
- 自訂模組: xxx為模組檔案路徑
實現:
- 伺服器端實現: Node.js
- 瀏覽器端實現: Browserify,也稱為CommonJS的瀏覽器端的打包工具。
Node.js模組化過程
安裝Node.js
建立項目結構
|-modules |-module1.js |-module2.js |-module3.js|-app.js|-package.json { "name": "commonJS-node", "version": "1.0.0" }
- 下載第三方模組
- 模組化編碼
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‘); // 自訂的modulelet module1 = require(‘./modules/module1‘);let module1 = require(‘./modules/module1‘);// 使用模組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
Browserify模組化過程
由於瀏覽器端不具備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>
AMD
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‘] }}
CMD
專門應用於瀏覽器端,模組的載入是非同步。
實現: 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模組化規範
ES6中內建了js模組化的實現。
文法:
定義暴露模組: export
暴露一個對象(預設暴露):
export default 對象
可以暴露任意資料類型,暴露什麼就接收到什麼。
暴露多個對象(常規暴露):
// 分別暴露export var xxx = value1;export let yyy = value2;// 統一暴露var xxx = value1;let yyy = value2;export {xxx, yyy}
統一暴露或者分別暴露,在引入的時候必須用對象解構賦值的形式。
引入使用模組: import
問題:
一些瀏覽器還不能直接識別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文法轉換}
編寫模組
編譯打包
使用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 [email protected] --save
(模組後加@代表下載相應版本號碼下的最新版本)
在app.js中引入使用:
import $ from ‘jquery‘
注意:
當改變了模組中的代碼後,需要重新轉換(Babel)、編譯打包(Browserify),再引入頁面。
JavaScript模組化規範詳解