笔记4—js模块化

31 阅读2分钟

js模块化发展脉络

无模块化→初具雏形→成熟→新时代

幼年期:无模块化

文件分离是最基础的模块化

  • 小问:script标签的两个参数

    1. 默认:解析到标签,立即pending,下载并执行
    2. defer:解析到标签,开始异步下载,继续解析完成后,再开始执行
    3. async:解析到标签,开始异步下载,下载完成后立即执行,并阻塞解析;执行完成后,再继续解析
  • 面试抛问题方向:

    1. 浏览器渲染原理
    2. 同步异步原理
    3. 模块化加载原理

问题:

  • 污染全局作用域→不利于大型项目的开发及多人团队共建

成长期:模块化雏形—IIFE(立即调用函数表达式)

🌰:最简单的模块— 模块 + 外部

// iifemodule
const module = (() => {
    let count = 0;
    return {
        increase: () => ++count;
        reset: () => {
            count = 0;
        }
    }
})();

module.increase();
module.reset();
  • 面试追问1:有额外依赖时,如何优化处理IIFE

    答:依赖其他的IIFE

    const iifeModule = ((dependecyModule1, dependecyModule2) => {
      let count = 0;
      // dependecyModule……
      return {
          increase: () => ++count;
          reset: () => {
              count = 0;
          }
      }
    })(dependecyModule1, dependecyModule2);// 调用时,将依赖作为参数传入
    
  • 面试追问2:了解早期jQuery依赖处理/模块加载的方案么? / 了解传统IIFE是如何解决多方依赖的问题么?

    答:IIFE + 传参调配

    const iifeModule = ((dependencyModule1, dependencyModule2) => {
        let count = 0;
        const increase = () => ++count;
        const reset = val => {
            count = 0;
            // dependencyModule
        }
    
        return {
            increase,
            reset
        }
    })(dependencyModule1, dependencyModule2);
    iifeModule.increse(1);
    iifeModule.reset();
    // 返回的是能力 = 使用方传参 + 本身逻辑能力 + 依赖能里
    // $('').attr('title', 'new');
    
  • 面试抛问题方向:

    1. 深入模块化的实现
    2. 转向框架(jquery、vue、react)模块化实现的细节,框架的特征原理
    3. 设计模式—模块化的设计模式

成熟期

CJS—CommonJS

  • node.js 指定的标准

  • 特征:

    1. 通过 module + export 对外暴露接口
    2. 通过 require 调用其他模块
// 引入部分
const dependecyModule1 = require(./dependecyModule1);
const dependecyModule2 = require(./dependecyModule2);

// 核心逻辑
let count = 0;
const increase = () => ++count;
const reset = val => {
    // …… 
}

// 暴露接口部分
export.increase = increase;
export.reset = reset;

module.exports = {
    increase,
    reset
}
const { increase, reset } = require('dep.js');

increase();
reset();
  • 优点:CJS从服务侧解决依赖全局污染的问题
  • 缺憾:只针对服务端
  • 新的问题:异步依赖

AMD

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

新增定义方式:

// 通过define来定义一个模块,然后用require去加载
define(id, [depends], callback);
require([module], callback);
define('amdModule', ['dependecyModule1', 'dependecyModule2'], (dependecyModule1,
dependecyModule2) => {
    // 业务逻辑
    let count = 0;
    const increase = () => ++count;
    const reset = val => {
        count = 0;
        // dependecyModule1……
    }
})
require(['amdModule'], amdModule => {
    amdModule.increase();
})
  • 面试追问1:如何对已有代码做兼容

    1. 增加定义阶段
    2. 返回作为回调内部的return
    define('amdModule', [], require => {
        // 引入部分
        const dependecyModule1 = require(./dependecyModule1);
        const dependecyModule2 = require(./dependecyModule2);
    
        // 核心逻辑
        let count = 0;
        const increase = () => ++count;
        const reset = val => {
            // …… 
        }
    
        // 暴露接口部分
        export.increase = increase;
        export.reset = reset;
    
        return {
            increase,
            reset
        }
    })
    
  • 面试追问2:兼容判断AMDCJS

UMD 出现

(define('amdModule', ['dependencyModule1'], require => {
    // 核心逻辑
    let count = 0;
    const increase = () => ++count;
    const reset = val => {
        // …… 
    }

    // 暴露接口部分
    export.increase = increase;
    export.reset = reset;

    return {
        increase,
        reset
    }
}))(
    // 目标: 一次性去区分CJS和AMD
    // 1. CJS factory
    // 2. module module.exports
    // 3. define
    typeof module === 'Object'
    && module.exports
    && typeof define !== "function"
        ? // 是CJS
            factory => module.exports => factory(require, exports, module)
        : // 是AMD
            define
)
  • 优点:解决浏览器的异步动态依赖
  • 缺点:增加引入成本,没有按需加载

CMD规范

  • 按需加载
  • 主要应用框架:sea.js
// 依赖就近
define('module', (require, exports, module) => {
    let $ = require('jquery');
    // ……………………

    let depends1 = require('./dependencyModule1');
})
  • 优点:按需加载,依赖就近

  • 缺点:

    1. 依赖于打包
    2. 扩大了模块内的体积
  • 区别:按需加载,依赖就近

踏入新时代

ESM

  • 新增定义:

    1. 引入关键字:import
    2. 导出关键字:export
// 引入区域
import dependencyModule1 from './dependencyModule1.js';

// 逻辑处理区域
// …… 

export default {
    increase, reset
}

---------------------------

export {increase, reset};

import {increase, reset};
  • 面试追问1:ESM动态模块

考察: export promise

ES11 原生解决方案

import('./esModule.js').then(dynamicEsModule => {
    dynamicEsModule.increase();
})
  • 优点:通过一种最统一的形态整合了所有js的模块化