js模块化

模块化

一、模块化的理解

1.什么是模块化?

  • 将一个复杂的程序依据一定的规则(规范)封装成几个块(文件), 并进行组合在一起

  • 块的内部数据/实现是私有的, 只是向外部暴露一些接口(方法)与外部其它模块通信

2.一个模块的组成

  • 数据—>内部的属性

  • 操作数据的行为—>内部的函数

3.模块化

  • 编码时是按照模块一个一个编码的, 整个项目就是一个模块化的项目

二、JS模块发展背景

1. 幼年期(也就是无模块化)

  1. 开始需要在页面中增加一些不同的js:动画、表单、格式化
  2. 多种js文件被分在不同的文件中
  3. 不同的文件又被同一个模板引用(文件分离是最基础的模块化第一步)
    <script src="jquery.js"></script>
    <script src="main.js"></script>
    <script src="dep1.js"></script>
    //…
  1. 出现的问题:污染全局作用域 => 不利于大型项目的开发以及多人团队的共建

2. 成长期(模块化的雏形 - IIFE(语法侧的优化) )

IIFE( 立即调用函数表达式)是一个在定义时就会立即执行的JavaScript函数

  1. 作用域的把控
    // 定义一个全局变量
    let count = 0;
    // 代码块1
    const increase = () => ++count;
    // 代码块2
    const reset = () => {
      count = 0;
    }
    increase();
    reset();
  1. 利用函数块级作用域
    (()=>{
        let count = 0;
        //...
      })
  1. 仅定义了一个函数 如果立即执行,就在函数后加(),表示立即执行
    (() => {
        let count = 0;
        // ……
      }();
  • 以上步骤,仅是初步实现了一个最最简单的模块.

    下面,尝试去定义一个最简单的模块

    const iifeModule = (() =>{
      let count = 0;
      return {
        increase: () => ++count;
        reset: () => {
          count = 0;
        }
      }
    })();
    iifeModule.increase();
    iifeModule.reset();
  1. 相关问题
    ​ 1)有额外依赖时,如何优化IIFE相关代码?
    ​ 答:依赖其他模块的IIFE
    const iifeModule = ((dependencyModule1, dependencyModule2) => { 			//dependencyModule即为其他模块
      let count = 0;
      return {
        increase: () => ++count;
        reset: () => {
          count = 0;
        }
      }
    })(dependencyModule1, dependencyModule2);
    iifeModule.increase();
iifeModule.reset();

​ 2)了解早期jquery的依赖处理以及模块加载方案吗?/ 了解传统IIFE是如何解决多方依赖的问题
答:IIFE加传参调配,实际上JQuery框架其实应用了revealing(揭露)的写法

    const iifeModule = ((dependencyModule1, dependencyModule2) => {
      let count = 0;
      const increase = () => ++count;
      const reset = () => {
        count = 0;
      }
      return {
        increase, reset
      }
    })(dependencyModule1, dependencyModule2);
    iifeModule.increase();
    iifeModule.reset();

3. 成熟期( Commonjs(CJS) )

  • 由node.js制定

特征:1.通过 module + exports 去对外暴露接口
2.通过 require 来调用其他模块

  • 模块组织方式:
    ​1).main.js文件
// 引入部分
const dependencyModule1 = require(./dependencyModule1);
const dependencyModule2 = require(./dependencyModule2);
// 处理部分
let count = 0;
const increase = () => ++count;
const reset = () => {
  count = 0;
}
// 做一些跟引入依赖相关事宜……
// 暴露接口部分
exports.increase = increase;
exports.reset = reset;
module.exports = {
  increase, reset
}

​ 2).模块使用方式( require 调用即可 )

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

4. Commenjs优缺点

  • 优点:CommonJS率先在服务端实现了,从框架层面解决依赖、全局变量污染的问题
  • 缺点:主要针对了服务端的解决方案。对于异步拉取依赖的处理整合不是那么的友好
    那么在此产生了新的问题 ----- 异步依赖 ,就需要AMD来解决

三、AMD规范

​ 方法:通过异步加载 和 允许制定回调函数

​ 经典实现框架: require.js

1. 新增定义方式

  • 通过 define 来定义一个模块,然后 require 进行加载
    //params: 模块名,依赖模块,工厂方法  
    define(id, [depends], callback);
    require([module], callback);

2. 模块定义方式

    define('amdModule', ['dependencyModule1', 'dependencyModule2'], (dependencyModule1, dependencyModule2) => {
      // 业务逻辑
      // 处理部分
      let count = 0;
      const increase = () => ++count;
      const reset = () => {
        count = 0;
      }
      return {
        increase, reset
      }
    })

3. 引入模块

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

4.相关问题

4.1 如果在AMDmodule中想兼容已有代码,如何做?
    define('amdModule', [], require => {
      // 引入部分
      const dependencyModule1 = require(./dependencyModule1);
      const dependencyModule2 = require(./dependencyModule2);
      // 处理部分
      let count = 0;
      const increase = () => ++count;
      const reset = () => {
        count = 0;
      }
      // 做一些跟引入依赖相关事宜……
      return {
        increase, reset
      }
    })
4.2 AMD中使用revealing
    define('amdModule', [], (require, export, module) => {
      // 引入部分
      const dependencyModule1 = require(./dependencyModule1);
      const dependencyModule2 = require(./dependencyModule2);
      // 处理部分
      let count = 0;
      const increase = () => ++count;
      const reset = () => {
        count = 0;
      }
      // 做一些跟引入依赖相关事宜……
      export.increase = increase();
      export.reset = reset();
    })
    define('amdModule', [], require => {
      const otherModule = require('amdModule');
      otherModule.increase();
      otherModule.reset();
    })
4.3 兼容AMD&CJS / 如何判断CJS和AMD UMD的出现
    (define('amdModule', [], (require, export, module) => {
        // 引入部分
        const dependencyModule1 = require(./dependencyModule1);
        const dependencyModule2 = require(./dependencyModule2);
        // 处理部分
        let count = 0;
        const increase = () => ++count;
        const reset = () => {
          count = 0;
        }
        // 做一些跟引入依赖相关事宜……
        export.increase = increase();
        export.reset = reset();
      }))(
        // 目标是一次性区分CommonJSorAMD
        typeof module === "object"&& module.exports&& typeof define !== "function"

          ? // 是 CJS
          factory => module.exports = factory(require, exports, module)
          : // 是AMD
          define
      )

5. AMD优缺点

  • 优点:适合在浏览器中加载异步模块,可以并行加载多个模块
  • 缺点:会有引入成本,不能按需加载

四、CMD规范

  • 按需加载

主要应用框架:sea.js

    define('module', (require, exports, module) => {
      let $ = require('jquery');
      // jquery相关逻辑
      let dependencyModule1 = require('./dependecyModule1');
      // dependencyModule1相关逻辑
    })

CMD优缺点

  • 优点:按需加载,依赖就近
  • 缺点:依赖于打包,加载逻辑存在于每个模块中,扩大模块体积

相关问题: AMD & CMD 的区别

​ 答:依赖就近,按需加载

五、ES6模块化

1. 新增定义

​ 引入关键字 ---- import
​ 导出关键字 ---- export

2. 模块引入、导出和定义的地方

    // 引入区域
    import dependencyModule1 from './dependencyModule1.js';
    import dependencyModule2 from './dependencyModule2.js';
    // 实现代码逻辑
    let count = 0;
    export const increase = () => ++count;
    export const reset = () => {
      count = 0;
    }
    // 导出区域
    export default {
      increase, reset
    }

3. 模板引入的地方

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

4. 在node中

    import { increase, reset } from './esModule.mjs';
    increase();
    reset();
    import esModule from './esModule.mjs';
    esModule.increase();
    esModule.reset();

5. 相关问题

​ ES6中的动态模块是哪些?

​ 答: export 和 promise

6. ES11原生解决方案(包含了 promise)

    import('./esModule.js').then(dynamicEsModule => {
      dynamicEsModule.increase();
    })

7. ES6模块化优缺点

  • 优点(重要性):通过一种最统一的形态整合了js的模块化
  • 缺点(局限性):本质上还是运行时的依赖分析

六、解决模块化的新思路 - 前端工程化

1. 背景

根本问题 - 前端的模块化处理方案依赖于运行时分析

2.问题解决方案

线下执行 grunt、gulp、webpack…

  <!doctype html>
    <script src="main.js"></script>
    <script>
      // 给构建工具一个标识位
      require.config(__FRAME_CONFIG__)
    </script>
    <script>
      require(['a', 'e'], () => {
        // 业务处理
      })
    </script>
  </html>

3. 工程化实现(思路)

1.扫描依赖关系表

    {//a b e的全局依赖
      a: ['b', 'c'],
      b: ['d'],
      e: []
    }

2.重新生成依赖数据模板

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

3.执行工具,采用模块化方案解决模块化处理依赖

  define('a', ['b', 'c'], () => {
      // 执行代码
      export.run = () => {}
    })

4. 工程化实现优点

  • 构建时生成配置,运行时执行
  • 最终转化成执行处理依赖
  • 可以拓展

七、最终的完全体

webpack(核心的工程化)+ mvvm框架组件化 + 设计模式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

巨龙王

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值