JS 组织代码方式演变——模块化
JS 组织代码方式演变——模块化
看别人的代码的时候,有些人写的代码一团糟,可是有些人的代码进行合理的分块,看起来很容易阅读理解。
1、是什么
什么是模块?
将一个复杂的程序依据一定的规则(规范)封装成几个块(文件), 并进行组合在一起
块的内部数据/实现是私有的, 只是向外部暴露一些接口(方法)与外部其它模块通信
一个模块的组成
数据--->内部的属性
操作数据的行为--->内部的函数
模块化
编码时是按照模块一个一个编码的, 整个项目就是一个模块化的项目
原则
把大程序分割成一个个模块
把大模块分割成一个个小模块
优点
增强代码的可阅读性、可维护性
模块化是现代前端工程化的开端
2、模块化的进化过程
代码之间会相互依赖(A 需要 B 中的变量/函数),应该怎样对代码进行组织呢
(1)通过注释进行组织
优点:极简
缺点
有些功能可能忘了加注释 → 专人进行代码审查(Code Review)
JS 文件会越来越大 → 分成多个 JS 文件
(2)用函数组织代码
编码:全局变量/函数
问题:污染全局命名空间, 容易引起命名冲突/数据不安全
缺点
全局函数名太多了 → 可以使用命名空间
调用关系复杂,难以调整顺序 → 立即执行函数
JS 文件会越来越大 → 拆分成多个 JS 文件
(3)用命名空间(namespace) 组织代码
编码:将数据/行为封装到对象中,使用的时候
对象名.方法
解决:命名冲突(减少了全局变量)
问题:数据不安全(外部可以直接修改模块内部的数据)
优点:占用的全局变量少,只有一个
app
缺点
名字越来越长 → 无解
依赖关系不清晰 → 立即执行函数
(4)立即执行函数
IIFE:立即调用函数表达式--->匿名函数自调用
编码:将数据和行为封装到一个函数内部, 通过给 window 添加属性来向外暴露接口
优点
可以有名字,也可以没有名字
可以声明依赖
可以选择导出内容
缺点:无法处理循环依赖 → 无解
(5)CommonJS
Node.js:服务器端
基本语法:
定义暴露模块:exports
引入模块:require
每个文件都可以当作一个模块
引入模块发生在什么时候?
Node:运行时, 动态同步引入
优点
用文件当名字,不用全局变量
可以声明依赖
可以选择导出内容
可以循环依赖
缺点:不支持异步/浏览器环境 → AMD
(6)AMD (异步模块定义)
浏览器端,模块的加载是异步的
require.js
基本语法
定义暴露模块:
define([依赖模块名], function(){return 模块对象})
引入模块:
require(['模块1', '模块2', '模块3'], function(m1, m2){//使用模块对象})
优点
用字符串当名字,不用全局变量
可以声明依赖
可以选择导出内容
可以循环依赖
支持异步
缺点
对同步的支持不如 CommonJS → Node 用 CJS,前端用 ES Modules
没有写入 ECMAScript 文档 → ESM
(7)ES Modules(ES 模块)
ES 6 内置了模块化的实现
<script type="module" src="index.js"></script>
基本语法
定义暴露模块:
export
暴露一个对象:
暴露多个:
引入使用模块:
import
default 模块:
其它模块
问题:有些浏览器还不能直接识别 ES 6 模块化的语法
优点
该支持的都支持
支持静态分析(tree-shaking)
支持按需加载(
import()
,是个 promise)
缺点
不支持拼接字符串 → 无解
不支持模糊加载 → 使用代码生成技术
不兼容 Node 的 CJS → 使用
.mjs
后缀
3、模块化演示
1、CommonJS 基于服务器端(Node.js)
下载安装 Node.js
创建目录结构
Modules
module1.js
module2.js
module3.js
app.js
package.json(命令创建
npm init
,再输入包名)
下载第三方模块
npm install uniq --save
package.json
模块化编码
module1.js
module2.js
module3.js
app.js
通过 node 运行 app.js——
node app.js
2、CommonJS 基于浏览器端应用(Browserify)
创建项目结构
js
dist //打包生成文件的目录
src //源码所在的目录
module1.js
module2.js
module3.js
app.js //应用主源文件
index.html
package.json
下载 browserify
全局:
npm install browserify -g
局部:
npm install browserify --save-dev
下载第三方模块
npm install uniq --save
package.json
定义模块代码
module1.js
module2.js
module3.js
app.js
打包处理 js:
browserify js/src/app.js -o js/dist/bundle.js
页面使用引入
index.html
3、AMD 规范
(1)NoAMD
项目结构
js
alerter.js
dataService.js
app.js
test1.html
文件内容
dataService.js
alerter.js
app.js
test1.html
(2)AMD(require.js)
下载 require.js, 并引入
将 require.js 导入项目: js/libs/require.js
创建项目结构
js
libs
require.js
modules
alerter.js
dataService.js
main.js
index.html
定义require.js的模块代码
dataService.js
alerter.js
应用主(入口) js:main.js
页面使用模块:index.html
使用第三方基于 require.js 的框架(jquery)
将 jquery 的库文件导入到项目:
js/libs/jquery-1.10.1.js
在 main.js 中配置 jquery 路径
在 alerter.js 中使用 jquery
使用第三方不基于 require.js 的框架(angular/angular-messages)
将 angular.js 和 angular-messages.js 导入项目
js/libs/angular.js
js/libs/angular-messages.js
在 main.js 中配置
页面:
5、CMD规范
下载 sea.js, 并引入
GitHub : https://github.com/seajs/seajs
将 sea.js 导入项目: js/libs/sea.js
创建项目结构
定义sea.js的模块代码
module1.js
module2.js
module3.js
module4.js
main.js: 主(入口)模块
index.html:
6、ES6-Babel-Browserify 使用
定义 package.json 文件
安装 babel-cli, babel-preset-es2015 和 browserify // cli——command line interface
npm install babel-cli browserify -g
npm install babel-preset-es2015 --save-dev
preset 预设(将es6转换成es5的所有插件打包)
定义
.babelrc
文件(babel run control)编码
js/src/module1.js
js/src/module2.js
js/src/module3.js
js/src/app.js
编译
使用 Babel 将 ES6 编译为 ES5 代码(但包含CommonJS语法):
babel js/src -d js/lib
使用 Browserify 编译 js:
browserify js/lib/app.js -o js/lib/bundle.js
页面中引入测试
引入第三方模块(jQuery)
下载jQuery模块:
npm install jquery@1 --save
// @后的是版本,1代表1.x.x
中的最新版本
在app.js中引入并使用
最后更新于