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)

  1. 下载 require.js, 并引入

  2. 创建项目结构

    • js

      • libs

        • require.js

      • modules

        • alerter.js

        • dataService.js

      • main.js

    • index.html

  3. 定义require.js的模块代码

    • dataService.js

    • alerter.js

  4. 应用主(入口) js:main.js

  5. 页面使用模块:index.html

  6. 使用第三方基于 require.js 的框架(jquery)

    • 将 jquery 的库文件导入到项目:

      • js/libs/jquery-1.10.1.js

    • 在 main.js 中配置 jquery 路径

    • 在 alerter.js 中使用 jquery

  7. 使用第三方不基于 require.js 的框架(angular/angular-messages)

    • 将 angular.js 和 angular-messages.js 导入项目

      • js/libs/angular.js

      • js/libs/angular-messages.js

    • 在 main.js 中配置

    • 页面:

5、CMD规范

  1. 下载 sea.js, 并引入

  2. 创建项目结构

  3. 定义sea.js的模块代码

    • module1.js

    • module2.js

    • module3.js

    • module4.js

    • main.js: 主(入口)模块

  4. index.html:

6、ES6-Babel-Browserify 使用

  1. 定义 package.json 文件

  2. 安装 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的所有插件打包)

  3. 定义 .babelrc 文件(babel run control)

  4. 编码

    • js/src/module1.js

    • js/src/module2.js

    • js/src/module3.js

    • js/src/app.js

  5. 编译

    • 使用 Babel 将 ES6 编译为 ES5 代码(但包含CommonJS语法):babel js/src -d js/lib

    • 使用 Browserify 编译 js:browserify js/lib/app.js -o js/lib/bundle.js

  6. 页面中引入测试

  7. 引入第三方模块(jQuery)

    1. 下载jQuery模块:

      • npm install jquery@1 --save // @后的是版本,1代表1.x.x中的最新版本

    2. 在app.js中引入并使用

最后更新于