什么是模块?javascript
一个模块的组成css
<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>
发生问题:html
function fun1(){ //... } function fun2(){ //... } //上面的函数fun1,fun2组成了一个模块,使用的时候直接调用某个函数就好了。
缺点:java
var module1 = new Object({ count : 0, fun1 : function (){ //... }, fun2 : function (){ //... } }); //这个里面的fun1和fun2都封装在一个赌侠宁里,能够经过对象.方法的形式进行调用; module1.fun1();
优势:node
缺点:jquery
var module1 = (function(){ var count = 0; var fun1 = function(){ //... } var fun2 = function(){ //... } //将想要暴露的内容放置到一个对象中,经过return返回到全局做用域。 return{ fun1:fun1, fun2:fun2 } })() //这样的话只能在全局做用域中读到fun1和fun2,可是读不到变量count,也修改不了了。 //问题:当前这个模块依赖另外一个模块怎么办?
var module1 = (function (mod){ mod.fun3 = function () { //... }; return mod; })(module1); //为module1模块添加了一个新方法fun3(),而后返回新的module1模块。 //引入jquery到项目中; var Module = (function($){ var _$body = $("body"); // we can use jQuery now! var foo = function(){ console.log(_$body); // 特权方法 } // Revelation Pattern return { foo: foo } })(jQuery) Module.foo();
概述git
特色es6
基本语法:github
exports.xxx = value // 经过module.exports指定暴露的对象value module.exports = value
var module = require('模块相对路径')
引入模块发生在何时?express
Modules/1.0规范包含内容:
exports = {Obj}
,暴露的API须做为此对象的属性。exports本质是引入了module.exports的对象。不能直接将exports变量指向一个值,由于这样等于切断了exports与module.exports的联系。exports=module.exports={Obj}
//结构以下 |-modules |-module1.js//待引入模块1 |-module2.js//待引入模块2 |-module3.js//待引入模块3 |-app.js//主模块 |-package.json { "name": "commonjsnode", "version": "1.0.0" }
3.下载第三方模块:举例express
npm i express --save
// module1 // 使用module.exports = value向外暴露一个对象 module.exports = { name: 'this is module1', foo(){ console.log('module1 foo()'); } } // module2 // 使用module.exports = value向外暴露一个函数 module.exports = function () { console.log('module2()'); } // module3 // 使用exports.xxx = value向外暴露一个对象 exports.foo = function () { console.log('module3 foo()'); }; exports.bar = function () { console.log('module3 bar()'); }; exports.name = 'this is module3' //app.js文件 var uniq = require('uniq'); //引用模块 let module1 = require('./modules/module1'); let module2 = require('./modules/module2'); let module3 = require('./modules/module3'); //使用模块 module1.foo(); module2(); module3.foo(); module3.bar(); module3.name;
5.经过node运行app.js
步骤
|-js |-dist //打包生成文件的目录 |-src //源码所在的目录 |-module1.js |-module2.js |-module3.js |-app.js //应用主源文件 |-index.html //浏览器上的页面 |-package.json { "name": "browserify-test", "version": "1.0.0" }
下载browserify
browserify js/src/app.js -o js/dist/bundle.js
<script type="text/javascript" src="js/dist/bundle.js"></script>
define([依赖模块名], function(){return 模块对象})
require(['模块1', '模块2', '模块3'], function(m1, m2){//使用模块对象})
define(function (require, exports, module) { var reqModule = require("./someModule"); requModule.test(); exports.asplode = function () { //someing } });
AMD也采用require()语句加载模块,可是不一样于CommonJS,它要求两个参数:
require([module], callback);
优势
下载require.js, 并引入
js/libs/require.js
|-js |-libs |-require.js // 引入的require.js |-modules |-alerter.js |-dataService.js |-main.js |-index.html
定义require.js的模块代码
具体来讲,就是模块必须采用特定的define()函数来定义;
define(['myLib'], function(myLib){ function foo(){ myLib.doSomething(); } // 暴露模块 return {foo : foo}; }); //当require()函数加载上面这个模块的时候,就会先加载myLib.js文件。
- 若是这个模块还依赖其余模块,那么define()函数的第一个参数,必须是一个数组,指明该模块的依赖性; ``` // dataService.js define(function () { let msg = 'this is dataService' function getMsg() { return msg.toUpperCase() } return {getMsg} }) // alerter.js define(['dataService', 'jquery'], function (dataService, $) { let name = 'Tom2' function showMsg() { $('body').css('background', 'gray') alert(dataService.getMsg() + ', ' + name) } return {showMsg} }) ```
应用主(入口)js: main.js
(function () { //配置 require.config({ //基本路径 baseUrl: "js/", //模块标识名与模块路径映射 paths: { "alerter": "modules/alerter",//此处不能写成alerter.js,会报错 "dataService": "modules/dataService", } }) //引入使用模块 require( ['alerter'], function(alerter) { alerter.showMsg() }) })()
<script data-main="js/main" src="js/libs/require.js"></script>
具体来讲,每一个模块要定义:
支持的配置项:
baseUrl :全部模块的查找根路径。
paths:path映射那些不直接放置于baseUrl下的模块名。
js/libs/jquery-1.10.1.js
paths: { 'jquery': 'libs/jquery-1.10.1' }
define(['dataService', 'jquery'], function (dataService, \$) { var name = 'xfzhang' function showMsg() { $('body').css({background : 'red'}) alert(name + ' '+dataService.getMsg()) } return {showMsg} })
将angular.js导入项目:js/libs/angular.js
// main.js中配置 (function () { //配置 require.config({ //基本路径 baseUrl: "js/", //模块标识名与模块路径映射 paths: { //第三方库做为模块 'jquery' : './libs/jquery-1.10.1', 'angular' : './libs/angular', //自定义模块 "alerter": "./modules/alerter", "dataService": "./modules/dataService" }, /* 配置不兼容AMD的模块 exports : 指定与相对应的模块名对应的模块对象 */ shim: { 'angular' : { exports : 'angular' } } }) //引入使用模块 require( ['alerter', 'angular'], function(alerter, angular) { alerter.showMsg() console.log(angular); }) })()
基本语法
定义暴露模块:
// 没有依赖的模块 define(function(require, module, exports){ let value = 'xxx'; //经过require引入依赖模块 //经过module.exports/exports来暴露模块 exports.xxx = value module.exports = value }) // 有依赖的模块 define(function(require, exports, module){ //引入依赖模块(同步) var module2 = require('./module2') //引入依赖模块(异步) require.async('./module3', function (m3) { ...... }) //暴露模块 exports.xxx = value })
下载sea.js, 并引入
js/libs/sea.js
define() exports module.exports
require()
seajs.use()
|-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 = 'this is module1' //内部函数 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) }) })
define(function (require) { var m1 = require('./module1') var m4 = require('./module4') m1.show() m4.show() })
<script type="text/javascript" src="js/libs/sea.js"></script> <script type="text/javascript"> seajs.use('./js/modules/main') </script>
es6 中新增了两个命令 export 和 import ;
// math.js export const add = function (a, b) { return a + b } export const subtract = function (a, b) { return a - b }
// main.js import { add, subtract } from './math.js' add(1, 2) substract(3, 2)
定义暴露模块 : 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 '模块路径/模块名'
暴露多个:
export const obj = {test1: ''} export const test = '' export class Test { constuctor() { } } // 或者,直接在暴露的地方定义导出函数或者变量 export let foo = ()=>{console.log('fnFoo');return "foo"},bar="stringBar"
let a=1 let b=2 let c=3 export { a,b,c }
// test.js let a = 1 let b = 2 let c = 3 export { a as test, b, c }; import { test, b, c } from './test.js' // 改变命名后只能写 as 后的命名
// test.js let a = 1 let b = 2 let c = 3 export { a as test, b, c }; // lib.js引入test.js的内容 export * from './test.js' // 引入 import {test,b,c} from './lib.js'
暴露一个对象,默认暴露
// test.js export default function () { console.log('hello world') } //引入 import say from './test.js' // 这里能够指定任意变量名 say() // hello world
import $ from 'jQuery' // 加载jQuery 库 import _ from 'lodash' // 加载 lodash import moment from 'moment' // 加载 moment
// main.js import { add, subtract } from './test' // 对于export default 导出的 import say from './test'
import {add as sum, subtract} from './test' sum (1, 2)
// math.js export const add = function (a, b) { return a + b } export const subtract = function (a, b) { return a - b } //引入 import * as math from './test.js' math.add(1, 2) math.subtract(1, 2)
解决:
{ "name" : "es6-babel-browserify", "version" : "1.0.0" }
npm install babel-cli browserify -g npm install babel-preset-es2015 --save-dev
{ "presets": ["es2015"] }
// js/src/module1.js export function foo() { console.log('module1 foo()'); }; export let bar = function () { console.log('module1 bar()'); }; export const DATA_ARR = [1, 3, 5, 1]; // js/src/module2.js let data = 'module2 data'; function fun1() { console.log('module2 fun1() ' + data); }; function fun2() { console.log('module2 fun2() ' + data); }; export {fun1, fun2}; // js/src/module3.js export default { name: 'Tom', setName: function (name) { this.name = name } } // js/src/app.js import {foo, bar} from './module1' import {DATA_ARR} from './module1' import {fun1, fun2} from './module2' import person from './module3' import $ from 'jquery' //引入完毕 $('body').css('background', 'red') foo() bar() console.log(DATA_ARR); fun1() fun2() person.setName('JACK') console.log(person.name);
编译
<script type="text/javascript" src="js/lib/bundle.js"></script>
引入第三方模块(jQuery)
npm install jquery@1 --save
- 2). 在app.js中引入并使用 ``` import $ from 'jquery' $('body').css('background', 'red') ```
模块化方案 | 优势 | 缺点 |
---|---|---|
commonJS | 复用性强; 使用简单; 实现简单; |
有很多能够拿来即用的模块,生态不错; 同步加载不适合浏览器,浏览器的请求都是异步加载; 不能并行加载多个模块。 |
AMD | 异步加载适合浏览器 | 可并行加载多个模块; 模块定义方式不优雅,不符合标准模块化 |
ES6 | 可静态分析,提早编译 | 面向将来的标准; 浏览器原生兼容性差,因此通常都编译成ES5; 目前能够拿来即用的模块少,生态差 |
对于依赖的模块,AMD 是提早执行,CMD 是延迟执行。
CMD 推崇依赖就近,AMD 推崇依赖前置。
// CMD define(function(require, exports, module) { var a = require('./a'); a.doSomething() // 此处略去 100 行 var b = require('./b') // 依赖能够就近书写 b.doSomething() // ... }) // AMD 默认推荐的是 define(['./a', './b'], function(a, b) { // 依赖必须一开始就写好 a.doSomething() // 此处略去 100 行 b.doSomething() ...})
参考: 使用 AMD、CommonJS 及 ES Harmony 编写模块化的 JavaScript