JavaScript 模块化
JavaScript模块化
模块化进化史
全局 function 模式
- 概念:即把所有的函数全都存储到一个全局对象中
global - 缺点:容易造成命名冲突(比如函数冲突)
- 代码示例
- module1.js
//数据 let data = 'com' //操作数据的函数 function foo() { console.log(`foo() ${data}`) } function bar() { console.log(`bar() ${data}`) } - module2.js
let data2 = 'other data'; function foo() { //与另一个模块中的函数冲突了 console.log(`foo() ${data2}`) } - test.html
<script type="text/javascript" src="module1.js"></script> <script type="text/javascript" src="module2.js"></script> <script type="text/javascript"> let data = "修改后的数据" foo() bar() </script>
- module1.js
namespace 模式
- 概念
- 将函数封装到对象之中
- 缺点
- 不安全(可以直接修改模块内的数据)
- 代码示例
- module1.js
let myModule = { data: 'com', foo() { console.log(`foo() ${this.data}`) }, bar() { console.log(`bar() ${this.data}`) } }
- module2.js
let myModule2 = { data: 'com2222', foo() { console.log(`foo() ${this.data}`) }, bar() { console.log(`bar() ${this.data}`) } } - test2.html
<script type="text/javascript" src="module2.js"></script> <script type="text/javascript" src="module22.js"></script> <script type="text/javascript"> myModule.foo() myModule.bar() myModule2.foo() myModule2.bar() myModule.data = 'other data' //能直接修改模块内部的数据 myModule.foo() </script>
- module1.js
IIEF 模式
- 概念
- 匿名函数自调用(闭包)
- immediately-invoked function expression(立即调用函数表达式)
- 作用
- 数据是私有的, 外部只能通过暴露的方法操作
- 问题: 如果当前这个模块依赖另一个模块怎么办?
- 代码示例
// 定义了一个立即调用函数
(function (window) {
//数据
let data = '.com'
//操作数据的函数
function foo() { //用于暴露有函数
console.log(`foo() ${data}`)
}
function bar() {//用于暴露有函数
console.log(`bar() ${data}`)
otherFun() //内部调用
}
function otherFun() { //内部私有的函数
console.log('otherFun()')
}
//暴露行为
window.myModule = {foo, bar} // 将window对象增加了一个对象,该对象包含 foo 和 bar 方法
})(window)
- [[JavaScript模块化之IIEF模式示例代码]]
IIEF 模式增强
- 概念
- IIFE模式增强 : 引入依赖
- 这就是现代模块实现的基石
- 代码示例
- 引入jquery到项目中
- module4.js
(function (window, $) { //数据 let data = 'com' //操作数据的函数 function foo() { //用于暴露有函数 console.log(`foo() ${data}`) $('body').css('background', 'red') } function bar() {//用于暴露有函数 console.log(`bar() ${data}`) otherFun() //内部调用 } function otherFun() { //内部私有的函数 console.log('otherFun()') } //暴露行为 window.myModule = {foo, bar} })(window, jQuery) - test4.html
<script type="text/javascript" src="jquery-1.10.1.js"></script> <script type="text/javascript" src="module4.js"></script> <script type="text/javascript"> myModule.foo() </script>
- module4.js
页面加载多 js 问题
页面加载多个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> - 说明
- 一个页面需要引入多个js文件
- 问题:
- 请求过多
- 依赖模糊
- 难以维护
- 这些问题可以通过现代模块化编码和项目构建来解决
CommonJS-Node 模块化
-
1、下载安装nodejs
-
2、创建如下结构项目
|-modules |-module1.js |-module2.js |-module3.js |-app.js |-package.json { "name": "commonJS-node", "version": "1.0.0" } -
3、下载第三方模块
npm install uniq --save -
4、模块化编码
- module1.js 暴露一个函数
module.exports = { foo() { console.log('moudle1 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(模块名或模块路径); */ "use strict"; //引用模块 let module1 = require('./modules/module1') let module2 = require('./modules/module2') let module3 = require('./modules/module3') let uniq = require('uniq') let fs = require('fs') //使用模块 module1.foo() module2() module3.foo() module3.bar() console.log(uniq([1, 3, 1, 4, 3]))
- module1.js 暴露一个函数
-
5、通过node运行app.js
* 命令:node app.js
* 工具: 右键–>运行
CommonJS-Browserify 模块化
- 1、创建项目结构
|-js |-dist //打包生成文件的目录 |-src //源码所在的目录 |-module1.js |-module2.js |-module3.js |-app.js //应用主源文件 |-index.html |-package.json { "name": "browserify-test", "version": "1.0.0" } - 2、下载browserify
- 全局:
npm install browserify -g - 局部:
npm install browserify --save-dev
- 全局:
- 3、 定义模块代码
- module1.js
module.exports = { foo() { console.log('moudle1 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 (应用的主js)
//引用模块 let module1 = require('./module1') let module2 = require('./module2') let module3 = require('./module3') let uniq = require('uniq') //使用模块 module1.foo() module2() module3.foo() module3.bar() console.log(uniq([1, 3, 1, 4, 3]))
- module1.js
- 打包处理js:
browserify js/src/app.js -o js/dist/bundle.js
- 页面使用引入:
<script type="text/javascript" src="js/dist/bundle.js"></script>
AMD-RequireJS 模块化
- 1、下载require.js, 并引入
- 官网: http://www.requirejs.cn/
- github : https://github.com/requirejs/requirejs
- 将require.js导入项目: js/libs/require.js
- 2、创建项目结构
|-js |-libs |-require.js // 存放第三方库 |-modules |-alerter.js |-dataService.js |-main.js |-index.html - 3、定义require.js的模块代码
- dataService.js
// 使用define传入了一个匿名函数 define(function () { let msg = 'com' function getMsg() { return msg.toUpperCase() } return {getMsg} }) - alerter.js
// 使用define传入了第三方库和一个匿名函数 define(['dataService', 'jquery'], function (dataService, $) { let name = 'Tom2' function showMsg() { $('body').css('background', 'gray') alert(dataService.getMsg() + ', ' + name) } return {showMsg} })
- dataService.js
- 4、应用主入口:main.js
// 定义一个立即调用函数 (function () { //配置 requirejs.config({ //基本路径 baseUrl: "js/", //模块标识名与模块路径映射 paths: { "alerter": "modules/alerter", "dataService": "modules/dataService", } }) //引入使用模块 requirejs( ['alerter'], function(alerter) { alerter.showMsg() }) })() - 5、页面使用模块:
- index.html
// 参数1指定了入口的配置文件,参数2指定了库位置 <script data-main="js/main" src="js/libs/require.js"></script>
- index.html
- 6、使用第三方基于require.js的框架(jquery)
- 1、将jquery的库文件导入到项目:
js/libs/jquery-1.10.1.js
- 2、在main.js中配置jquery路径
paths: { 'jquery': 'libs/jquery-1.10.1' } - 3、在alerter.js中使用jquery
define(['dataService', 'jquery'], function (dataService, $) { var name = 'xfzhang' function showMsg() { $('body').css({background : 'red'}) alert(name + ' '+dataService.getMsg()) } return {showMsg} })
- 1、将jquery的库文件导入到项目:
- 7、使用第三方不基于require.js的框架(angular)
- 1、将angular.js导入项目
- js/libs/angular.js
- 2、在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); }) })()
- 1、将angular.js导入项目
- [[JavaScript模块化之AMD-RequireJS完整配置示例代码]]
CMD-Sea.js模块化
- 下载sea.js, 并引入
- 官网: http://seajs.org/
- github : https://github.com/seajs/seajs
- 将sea.js导入项目: js/libs/sea.js
- 创建项目结构
|-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 = 'com' //内部函数 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:
<!--
使用seajs:
1. 引入sea.js库
2. 如何定义导出模块 :
define()
exports
module.exports
3. 如何依赖模块:
require()
4. 如何使用模块:
seajs.use()
-->
<script type="text/javascript" src="js/libs/sea.js"></script>
<script type="text/javascript">
seajs.use('./js/modules/main')
</script>
ES6-Babel-Browserify
- 1、 定义package.json文件
{ "name" : "es6-babel-browserify", "version" : "1.0.0" } - 2、安装babel-cli, babel-preset-es2015和browserify
- 关于 babel Babel · Babel (babeljs.io) JavaScript 编译器
- 关于 browserify Browserify 打包工具,将 nodejs 编写的模块转化为浏览器可运行的 JavaScript
npm install babel-cli browserify -g npm install babel-preset-es2015 --save-dev preset 预设(将es6转换成es5的所有插件打包) - 3、定义.babelrc文件
{ "presets": ["es2015"] } - 4、编码
- js/src/module1.js 分别暴露
export function foo() { console.log('module1 foo()'); } export function bar() { 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);
- js/src/module1.js 分别暴露
- 5、编译
- 使用Babel将ES6编译为ES5代码(但包含CommonJS语法) :
babel js/src -d js/lib - 使用Browserify编译js :
browserify js/lib/app.js -o js/lib/bundle.js
- 使用Babel将ES6编译为ES5代码(但包含CommonJS语法) :
- 6、页面中引入测试
<script type="text/javascript" src="js/lib/bundle.js"></script> - 7、引入第三方模块(jQuery)
- 1). 下载jQuery模块:
npm install jquery@1 --save
- 2). 在app.js中引入并使用
import $ from 'jquery' $('body').css('background', 'red')
- 1). 下载jQuery模块:
- [[JavaScript模块化之ES6-Browserify完整示例代码]]