JS模块化

背景

js自身简略的页面设计:页面动画 + 表单提交
并无模块化 or 命名空间的概念

JS模块化需要日益增长

幼年期:无模块化

  1. 开始须要在页面中减少一些不同的js:动画、表单、格式化
  2. 多种js文件被分在不同的文件中
  3. 不同的文件又被同一个模板饮用
// 过后是被认可的:文件拆散是最根底的<script src="jquery.js"> </script><script src="main.js"> </script><script src="dep1.js"> </script><script src="dep2.js"> </script>

问题:

  • 净化全局作用域 => 不利于大型项目的开发以及多人团队的共建

成长期:模块化的雏形 - IIFE(立刻函数、语法侧的优化)

作用域的把控

例子:

// 定义一个全局变量let count = 0// 代码块1const increase = () => ++count// 代码块2const reset = () => {    count = 0}increase()reset()

利用函数的块级作用域

(() => {    let conut = 0    // ...})

仅定义了一个函数,并没有执行,如果立刻执行

(() => {    let conut = 0    // ...})()

初步实现了一个最简略的模块(通过立刻执行函数)

尝试去定一个最简略的模块

const iifeModule = (() => {    let count = 0    // 代码块1    const increase = () => ++count// 代码块2    const reset = () => {        count = 0    }    return {        // 代码块1         increase: () => ++count,        // 代码块2        reset: () => {            count = 0        }    }})()iifeModule.increase()iifeModule.reset()

诘问:如果有额定依赖,如何优化iife? - 参数的调用

优化1:依赖其余模块的IIFE
const iifeModule = ((dependencyModule1, dependencyModule2) => {    let count = 0    return {        // 代码块1         increase: () => ++count,        // 代码块2        reset: () => {            count = 0        }    }})(dependencyModule1, dependencyModule2)iifeModule.increase()iifeModule.reset()

问:理解jquery的依赖解决以及模块加载计划吗?/ 理解传统IIFE是如何解决多方依赖的问题吗?
答:IIFE + 传参调配

实际上,jquery 等框架其实利用了 revealing module(揭示模式) 的写法

const iifeModule = ((dependencyModule1, dependencyModule2) => {    let count = 0    // 代码块1    const increase = () => ++count    // 代码块2    const reset = () => {        count = 0    }    // 仅裸露接口,不裸露办法    return {        increase,        reset    }   })(dependencyModule1, dependencyModule2)iifeModule.increase()iifeModule.reset()

成熟期

CJS - CommonJS

由node制订的一套计划
特色:
  • 通过 module + exports 去对外裸露借口
  • 通过 require 来调用其余模块

模块组织形式
main.js 文件中

// 援用const dependencyModule1 = require(dependencyModule1)const dependencyModule2 = require(dependencyModule2)// 解决let count = 0// 代码块1const increase = () => ++count// 代码块2const reset = () => {    count = 0}// 做一些跟引入依赖相干事宜……// 裸露接口exports.increase = increaseexports.reset = resetmodule.exports = {    increase,    reset}

模块应用形式

const { increase, reset } = require(main.js)increase()reset()

可能被问到的货色

理论执行解决
转化成立即执行函数
CJS 和 IIFE 的联合

// 须要穿参(function () {    const dependencyModule1 = require(dependencyModule1)    const dependencyModule2 = require(dependencyModule2)        // 业务逻辑……}).call(thisValue, exports, require, module)

CJS 长处:
CommonJS 率先在服务端实现了,从框架层面解决依赖、全局变量净化的问题

CJS 毛病:
次要针对了服务端的解决方案,都是同步引入。对于异步拉取依赖整合没有那么敌对。

新的问题 —— 异步依赖

AMD标准

通过异步加载 + 容许定制回调函数
经典实现框架:require.js

新增定义形式:

// 通过deifine来定义一个模块,而后require进行加载/** * id: 模块名 * [depends]:依赖模块 * callback:工厂办法 */define(id, [depends], callback)require([module], callback)

模块定义形式:

define('amdModule', ['dependencyModule1', 'dependencyModule2'], (dependencyModule1,dependencyModule1) => {    // 解决    let count = 0    // 代码块1    const increase = () => ++count    // 代码块2    const reset = () => {        count = 0    }        return {        increase, reset    }})

引入模块:

require(['amdModule'], amdModule => {    amdModule.increase()})

问:如果在amdModule中想兼容已有代码,怎么办?

define('amdModule', [], require => {    // 援用    const dependencyModule1 = require(dependencyModule1)    const dependencyModule2 = require(dependencyModule2)// 解决    let count = 0// 代码块1    const increase = () => ++count// 代码块2    const reset = () => {        count = 0    }// 做一些跟引入依赖相干事宜……// 裸露接口    exports.increase = increase    exports.reset = reset    module.exports = {        increase,        reset    }})

问:AMD中应用revealing

define('amdModule', [], (require, exports, module) => {    // 援用    const dependencyModule1 = require(dependencyModule1)    const dependencyModule2 = require(dependencyModule2)    // 解决    let count = 0    // 代码块1    const increase = () => ++count    // 代码块2    const reset = () => {        count = 0    }    // 做一些跟引入依赖相干事宜……    // 裸露接口    exports.increase = increase    exports.reset = reset})define(require => {    const otherModule = require('amdModule')    otherModule.increase()    otherModule.reset()})

问:兼容 AMD & CommonJS?答:UMD
UMD的呈现

(define('amdModule', [], (require, exports, module) => {    // 援用    const dependencyModule1 = require(dependencyModule1)    const dependencyModule2 = require(dependencyModule2)    // 解决    let count = 0    // 代码块1    const increase = () => ++count    // 代码块2    const reset = () => {        count = 0    }    // 做一些跟引入依赖相干事宜……    // 裸露接口    exports.increase = increase    exports.reset = reset}))(    //  指标是一次性辨别 CommonJS or AMD    typeof module === "object"    && module.exports    && typeof define !== "function"     ? // 是 CommonJS    factory => module.exports = factory(require, exports, module)         : // 是AMD    define)
  • 长处:适宜在浏览器中加载异步模块,能够并行加载多个模块
  • 毛病:会有引入老本,不能按需加载

CMD标准

按需加载
次要利用的框架 sea.js
define('module', [], (require, exports, module) => {    let $ = require('jquery');    // jquery 相干的代码        let dependencyModule1 = require('dependencyModule1')    //  dependencyModule1 相干逻辑})
  • 长处:按需加载,依赖就近
  • 毛病:依赖打包,加载逻辑存在每个模块中,扩充模块体积

ES6模块化

走向新时代

新增定义:
引入关键字 —— import
导出关键字 —— export

模块引入、导出和定义
esModule.js

// 引入import dependencyModule1 from 'dependencyModule1'import dependencyModule2 from 'dependencyModule2'// 实现let count = 0export const increase = () => ++countconst reset = () => {    count = 0}// 导出export default {    reset}

模板引入的中央:

<script type="module" src="esModule.js"></script>

node中:

import { reset } from 'esModule.js'reset()import esModule from 'esModule.js'esModule.reset()
  • 长处(重要性):通过一种最对立的状态整合js模块化
  • 毛病(局限性):实质上还是运行了依赖剖析

解决模块化的新思路 —— 前端工程化

背景

基本问题 — 运行时进行依赖剖析

前端的模块化解决计划依赖于运行时剖析

解决方案:线下执行
grunt、gulp、webpack

<!doctype html>    <script src="main.js"></script>    <script>    //    给构建工具一个标示位        require.config(__FRAME_CONFIG__)    </script>    <script>    require(['a', 'e'], () => {        // 业务逻辑    })    </script></html>
define('a', () => {    let b = require('b');    let c = require('c');        exports.run = () => {    //   run    }})

工程化实现

step1: 扫描依赖关系表:

{    a: ['b', 'c']    b: ['d']}

step2: 从新生成依赖数据模板

<!doctype html>    <script src="main.js"></script>    <script>        //  构建工具生成数据        require.config({            "deps": {                a: ['b', 'c'],                b: ['d'],                e: []            }        })    </script>    <script>    require(['a', 'e'], () => {        // 业务逻辑    })    </script></html>

step3: 执行工具,采纳模块化计划解决模块化解决依赖

define('a', ['b', 'c'], () => {    // 执行代码    exports.run = () => {        //   run    }})
长处:
  1. 构建时生成配置,运行时执行
  2. 最终转化执行解决依赖
  3. 能够扩大

齐全体:webpack为外围的工程化 + MVVM框架组件化 + 设计模式

总结:

  1. 无模块化 -> IIFE => 从无到有的过程
  2. IIFE -> CommonJS => 从代码层到框架层
  3. CommonJS -> AMD => 解决了异步问题
  4. AMD -> CMD => 解决了按需加载的问题