模块化module/CommonJS/Browserify/AMD/RequireJS/CMD/SeaJS/ES6_Babel
程序员文章站
2022-06-14 08:18:07
...
模块化module/CommonJS/Browserify/AMD/RequireJS/CMD/SeaJS/ES6_Babel
1. 模块化module
将一个复杂的程序依据一定的规则(规范)封装成几个块(文件), 并进行组合在一起
块的内部数据/实现是私有的, 只是向外部暴露一些接口(方法)与外部其它模块通信
-
一个模块的组成
- 数据--->内部的属性
- 操作数据的行为--->内部的函数
-
模块化
- 编码时是按照模块一个一个编码的, 整个项目就是一个模块化的项目
-
模块化的进化过程
-
全局function模式 :
- 编码: 全局变量/函数
- 问题: 污染全局命名空间, 容易引起命名冲突/数据不安全
-
namespace模式 :
- 编码: 将数据/行为封装到对象中
- 解决: 命名冲突(减少了全局变量)
- 问题: 数据不安全(外部可以直接修改模块内部的数据)
-
IIFE模式/增强
- IIFE : 立即调用函数表达式--->匿名函数自调用
- 编码: 将数据和行为封装到一个函数内部, 通过给window添加属性来向外暴露接口
- 引入依赖: 通过函数形参来引入依赖模块
(function(window, module2){ var data = 'atguigu.com' function foo() { module2.xxx() console.log('foo()'+data) } function bar() { console.log('bar()'+data) } window.module = {foo} })(window, module2)
-
-
模块化规范
- <u>CommonJS</u>
- Node.js : 服务器端
- Browserify : 浏览器端 也称为js的打包工具
- 基本语法:
- 定义暴露模块 : exports
引入模块 : requireexports.xxx = value module.exports = value
var module = require('模块名/模块相对路径')
- 定义暴露模块 : exports
- 引入模块发生在什么时候?
- Node : 运行时, 动态同步引入
- Browserify : 在运行前对模块进行编译/转译/打包的处理(已经将依赖的模块包含进来了),
运行的是打包生成的js, 运行时不存在需要再从远程引入依赖模块
- <u>AMD : 浏览器端</u>
- require.js
- 基本语法
- 定义暴露模块: define([依赖模块名], function(){return 模块对象})
- 引入模块: require(['模块1', '模块2', '模块3'], function(m1, m2){//使用模块对象})
- 配置:
require.config({ //基本路径 baseUrl : 'js/', //标识名称与路径的映射 paths : { '模块1' : 'modules/模块1', '模块2' : 'modules/模块2', 'angular' : 'libs/angular', 'angular-messages' : 'libs/angular-messages' }, //非AMD的模块 shim : { 'angular' : { exports : 'angular' }, 'angular-messages' : { exports : 'angular-messages', deps : ['angular'] } } })
- <u>CMD : 浏览器端</u>
- sea.js
- 基本语法
- 定义暴露模块:
define(function(require, module, exports){ 通过require引入依赖模块 通过module/exports来暴露模块 exports.xxx = value })
- 使用模块seajs.use(['模块1', '模块2'])
- 定义暴露模块:
- <u>ES6</u>
- ES6内置了模块化的实现
- 基本语法
-
定义暴露模块 : export
- 暴露一个对象:
export default 对象
- 暴露多个:
export var xxx = value1 export let yyy = value2 var xxx = value1 let yyy = value2 export {xxx, yyy}
- 暴露一个对象:
-
引入使用模块 : import
- default模块:
import xxx from '模块路径/模块名'
- 其它模块
import {xxx, yyy} from '模块路径/模块名' import * as module1 from '模块路径/模块名'
- default模块:
-
- 问题: 所有浏览器还不能直接识别ES6模块化的语法
- 解决:
- 使用Babel将ES6--->ES5(使用了CommonJS) ----浏览器还不能直接支行
- 使用Browserify--->打包处理----浏览器可以运行
- <u>CommonJS</u>
1.1 全局function模式: 将不同的功能封装成不同的全局函数
- 问题: Global被污染了, 很容易引起命名冲突
- module1.js
function fn1() { console.log('fn1()'); } function fn2() { console.log('fn2()'); }
- test1.html
<body> <script type='text/javascript' src='module1.js'></script> <script type='text/javascript'> fn1(); fn2(); </script> </body> </html>
1.2 namespace模式: 简单对象封装
- 作用: 减少了全局变量
- 问题: 不安全(数据不是私有的, 外部可以直接修改)
- module1.js
var obj = { fn1: function () { console.log('fn1()'); }, fn2: function () { console.log('fn2()'); } }
- test2.html
<script type='text/javascript' src='module1.js'></script> <script type='text/javascript'> obj.fn1(); obj.fn2(); </script>
1.3 IIFE模式: 匿名函数自调用(闭包)
- immediately-invoked function expression(立即调用函数表达式)
- 作用: 数据是私有的, 外部只能通过暴露的方法操作
- module3.js
(function (window) { var msg = 'atguigu'; function fn1() { console.log('fn1()' + msg); } function fn2() { console.log('fn2()' + msg); } window.fn1 = fn1; window.fn2 = fn2; })(window)
- test3.html
<script type='text/javascript' src='module3.js'></script> <script type='text/javascript'> fn1(); fn2(); </script>
1.4 IIFE模式增强 : 引入依赖
- 这就是现代模块实现的基石
- 引入jquery到项目中
- module4.js
(function (window, $) { //数据 let data = 'atguigu.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>
1.5 页面加载多个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文件
- 问题:
- 请求过多
- 依赖模糊
- 难以维护
- 这些问题可以通过现代模块化编码和项目构建来解决
2. CommonJS-Node____Node.js模块化
-
下载安装node.js
-
创建项目结构
|-modules
|-module1.js
|-module2.js
|-module3.js
|-main.js
-
模块化编码
-
module1.js
const prefix = 'http://loacalhost:3000/'; const newsUrl = prefix + 'news'; const commentsUrl = prefix + 'comments'; /* 暴露模块中的内容,exports */ exports.newsUrl = newsUrl; exports.commentsUrl = commentsUrl;
-
module2.js
/* 引入依赖模块 require 用户自己定义的模块(自定义模块): 引入路径必须以 ./ 或者 ../ 开头 第三方模块或者nodejs内置的模块: 引入路径直接以模块名引入 查找模块的规则: 路径以 ./ 或者 ../开头的话,直接沿着路径去找 路径如果直接是文件名,首先node中核心模块中找,如果找不到,去当前目录下node_modules找,层层遍历查找到根目录下 会自动补全文件后缀名: .node .js .json */ const m1 = require('./module1'); function getNews(url) { console.log('发送请求,请求地址:' + url); return 'newsData'; } function getComments(url) { console.log('发送请求,请求地址:' + url); return 'commnetsData'; } const newsData = getNews(m1.newsUrl); const commnetsData = getComments(m1.commentsUrl); /* 使用module.exports暴露模块 */ module.exports = { newsData, commnetsData }
-
module3.js
function showData(data) { console.log(data); } module.exports = showData;
-
main.js
const m2 = require('./modules/module2'); const showData = require('./modules/module3'); showData(m2.newsData); showData(m2.commnetsData);
-
通过node运行index.js
- 命令: node index.js
- 工具: 右键-->运行
3. CommonJS-BrowserifyBrowserify模块化
-
创建项目结构
|-build //打包生成文件的目录
|-src //源码所在的目录
|-module1.js
|-module2.js
|-module3.js
|-main.js //应用主源文件
|-index.html
-
下载browserify
- 全局: npm install browserify -g
-
定义模块代码
- 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()') }
- man.js (应用的主js)
//引用模块 let module1 = require('./module1') let module2 = require('./module2') let module3 = require('./module3') //使用模块 module1.foo() module2() module3.foo() module3.bar()
- 打包处理js:
- browserify src/main.js -o build/bundle.js
- 页面使用引入:
<script type="text/javascript" src="./build/bundle.js"></script>
4. AMD-RequireJS
-
下载require.js, 并引入
- 官网: http://www.requirejs.cn/
- github : https://github.com/requirejs/requirejs
- 将require.js导入项目: js/libs/require.js
-
创建项目结构
|-libs
|-require.js
|-modules
|-alerter.js
|-dataServer.js
|-app.js
|-index.html
-
定义require.js的模块代码
- dataServer.js
/* 定义没有依赖的模块 */ define(function () { console.log('dataServer模块被加载了'); var msg = 'atguigu.com'; function dataServer() { return msg.toUpperCase(); } //暴露模块内容 return dataServer; })
- alerter.js
/* 定义有依赖的模块 define(['模块1', '模块2'], function (m1, m2) {}) */ define(['dataServer'], function (dataServer) { console.log('alerter模块被加载了'); var msg = dataServer(); function alerter() { alert(msg); } //向外暴露模块 return alerter; })
-
应用主(入口): app.js
/*
主模块:
define() 定义没有依赖/有依赖的模块
require() 定义异步的模块,和define用法一致
requirejs() 定义配置,定义主模块
*/
//配置模块的路径
require.config({
baseUrl: './', //所有模块的公共路径
paths: {
dataServer: 'modules/dataServer', // 模块名称(一定要与引入的模块名称一一对应): 模块的路径
alerter: 'modules/alerter', //一定不能写文件的后缀名,它会自动补全
jquery: 'libs/jquery-1.10.1' //库/框架自己实现模块化的功能,定义了暴露模块的名称
}
})
//主模块
require(['jquery'], function ($) {
$('#btn').click(function () {
//异步加载你需要的模块,会缓存你加载的模块
require(['alerter'], function (alerter) {
alerter();
})
})
$('body').css('background', 'deeppink');
})
console.log('1111111111111');
-
页面使用模块:
<script data-main="app.js" src="./libs/require.js"></script>
-
使用第三方基于require.js的框架(jquery)
- 将jquery的库文件导入到项目:
- ./libs/jquery-1.10.1.js
- 在app.js中配置jquery路径
paths: { jquery: 'libs/jquery-1.10.1' //库/框架自己实现模块化的功能,定义了暴露模块的名称 }
- 在app.js中使用jquery
require(['jquery'], function ($) { $('#btn').click(function () { //异步加载你需要的模块,会缓存你加载的模块 require(['alerter'], function (alerter) { alerter(); }) }) $('body').css('background', 'deeppink'); })
5. CMD-SeaJS
-
下载sea.js, 并引入
- 官网: http://seajs.org/
- github : https://github.com/seajs/seajs
- 将sea.js导入项目: libs/sea.js
-
创建项目结构
|-libs
|-sea.js
|-modules
|-alerter.js
|-dataServer.js
|-app.js
|-index.html
-
定义sea.js的模块代码
-
dataServer.js
/* 定义没有依赖的模块 */ define(function (require, exports, module) { var msg = 'atguigu.com'; function dataServer() { return msg.toUpperCase(); } //暴露模块 exports.dataServer = dataServer; })
-
alerter.js
/* 定义有依赖的模块 */ define(function (require, exports, module) { //引入依赖的模块(同步) var m1 = require('./dataServer'); //定义自己的数据/函数 function alerter() { alert(m1.dataServer()); } //暴露出去 module.exports = alerter; })
-
app.js : 主(入口)模块
/* 定义主模块 */ define(function (require, exports, module) { //异步引入依赖 require.async(['./alerter'], function (alerter) { alerter(); }) console.log('**************'); })
-
index.html:
<!--
使用seajs:
1. 引入sea.js库
2. 如何定义导出模块 :
define()
exports
module.exports
3. 如何依赖模块:
require()
4. 如何使用模块:
seajs.use()
-->
<script type="text/javascript" src="./libs/sea.js"></script>
<script type="text/javascript">
seajs.use('./modules/app.js');
</script>
6. ES6_Babel_Browserify
-
定义package.json文件
-
安装babel-cli, babel-preset-es2015和browserify
- npm install babel-cli browserify -g
- npm install babel-preset-es2015 --save-dev
- preset 预设(将es6转换成es5的所有插件打包)
-
定义.babelrc文件
{
"presets": ["es2015"]
}
-
编码
- 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]
- src/module2.js 统一暴露
let data = 'module2 data' function fun1() { console.log('module2 fun1() ' + data); } function fun2() { console.log('module2 fun2() ' + data); } export {fun1, fun2}
- src/module3.js
export default { name: 'Tom', setName: function (name) { this.name = name } }
- src/app.js
import * as m1 from './module1'; 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);
-
编译
- 使用Babel将ES6编译为ES5代码(但包含CommonJS语法) : babel src -d build
- 使用Browserify编译js : browserify build/app.js -o dist/bundle.js
-
页面中引入测试
<script type="text/javascript" src="lib/bundle.js"></script>
-
引入第三方模块(jQuery)
1). 下载jQuery模块:
* npm install [email protected] --save
2). 在app.js中引入并使用
import $ from 'jquery'
$('body').css('background', 'red')
-
在package.json中加
- npm run build
"scripts": { "build": "babel src -d build && browserify build/app.js -o dist/bundle.js" },
推荐阅读
-
前端模块化小总结—commonJs,AMD,CMD, ES6 的Module
-
JavaScript里面的模块化4种方式(CommonJS,AMD,CMD,es6Module)
-
模块化module/CommonJS/Browserify/AMD/RequireJS/CMD/SeaJS/ES6_Babel
-
前端模块化:AMD, CMD, CommonJS, ES6 Module
-
前端模块化详解(CommonJS、AMD、CMD、ES Module)
-
前端模块化,AMD,CMD,ES6 Module,CommonJS,ES6 模块与CommonJS 模块的差异
-
JavaScript模块化编程 - CommonJS, AMD ,CMD和 RequireJS之间的关系
-
JS模块化规范(AMD/CMD/CommonJS/UMD/ES6 Module)
-
JavaScript模块化CommonJS/AMD/CMD/UMD/ES6Module的区别
-
前端模块化( CommonJS、AMD、CMD、UMD、ES Module) 详解