• js模块化


    js模块化

    一、背景

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

    JS的模块化需求日益增长

    二、模块化的发展

    1.幼年期: 无模块化

    1.开始需要在页面中增加一些不同的js: 动画、表单、格式化等
    2.多种js文件被分在不同的文件中
    3.不同的文件又被同一个模板引用

      <script src="jquery.js"></script>
      <script src="main.js"></script>
      <script src="dep1.js"></script>
      //……
    
    • 1
    • 2
    • 3
    • 4

    认可:

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

    缺点:

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

    2.成长期: IIFE(语法侧的优化)

    IIFE: Immediately Invoked Function Expression,立即调用的函数表达式。也就是 在函数声明的同时立即调用这个函数。

    利用函数的块级作用域,定义一个函数,立即执行,初步实现了一个最最最简单的模块。

    尝试去定义一个最简单的模块:

    const iifeModule = (() => {
      let count = 0;
      return {
        increase: () => ++count;
        reset: () => {
          count = 0;
        }
      }
    })();
    
    iifeModule.increase();
    iifeModule.reset();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    追问1:有额外依赖时,如何优化IIFE相关代码
    答:以参数的形式传参

    优化1:依赖其他模块的IIFE

    const iifeModule = ((dependencyModule1, dependencyModule2) => {
      let count = 0;
      return {
        increase: () => ++count;
        reset: () => {
          count = 0;
        }
      }
    })(dependencyModule1, dependencyModule2);
    
    iifeModule.increase();
    iifeModule.reset();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    追问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();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    3.成熟期

    CJS-Commonjs

    node.js制定
    特征:

    • 通过module+exports区对外暴露接口
    • 通过require来调用其他模块

    模块组织方式:
    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
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    模块使用方式:

      const { increase, reset } = require('./main.js');
    
      increase();
      reset();
    
    • 1
    • 2
    • 3
    • 4

    追问1:实际执行处理

    (function (thisValue, exports, require, module) {
        const dependencyModule1 = require(./dependencyModule1);
        const dependencyModule2 = require(./dependencyModule2);
    
        // 业务逻辑……
      }).call(thisValue, exports, require, module);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    优点:
    CommonJs率先在服务端实现了,从框架层面解决依赖,全局变量污染的问题
    缺点:
    主要针对了服务端的解决方案。对于伊布拉取的处理整合不是那么友好。

    新的问题:异步依赖

    AMD规范

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

    新增定义方式:

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

    模块定义方式

     define('amdModule', ['dependencyModule1', 'dependencyModule2'], (dependencyModule1, dependencyModule2) => {
        // 业务逻辑
        // 处理部分
        let count = 0;
        const increase = () => ++count;
        const reset = () => {
          count = 0;
        }
    
        return {
          increase, reset
        }
      })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    引入模块:

      require(['amdModule'], amdModule => {
        amdModule.increase();
      })
    
    • 1
    • 2
    • 3

    追问2:如果在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
        }
      })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    追问3: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();
      })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    追问4:兼容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
      )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    优点:
    适合在浏览器中加载异步模块,可以并行加载多个模块

    缺点:
    会有引入成本,不能按需加载

    CMD规范

    按需加载
    主要应用的框架 sea.js

      define('module', (require, exports, module) => {
        let $ = require('jquery');
        // jquery相关逻辑
    
        let dependencyModule1 = require('./dependecyModule1');
        // dependencyModule1相关逻辑
      })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    优点:
    按需加载,依赖就近

    依赖于打包,加载逻辑存在于每个模块中,扩大模块体积

    追问5:AMD&CMD区别
    依赖就近,按需加载。
    AMD 推崇依赖前置、提前执行,CMD推崇依赖就近、延迟执行

    ES6模块化 :走向新时代

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

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

     // 引入区域
      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
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    模板引入的地方

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

    node中:

    import { increase, reset } from './esModule.mjs';
      increase();
      reset();
    
      import esModule from './esModule.mjs';
      esModule.increase();
      esModule.reset();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    追问6:动态模块
    考察 export promise
    ES11原生解决方案:

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

    优点(重要性):通过一种最统一的形态整合了js的模块化

    缺点(局限性):本质上还是 运行的依赖分析

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

    完全体 :
    webpack为核心的工程化 + mvvm框架组件化 + 设计模式

  • 相关阅读:
    Unity ECS最新DOTS环境搭建教程
    Server is too busy _解决方法和httpRuntime代码放在web.config哪
    java---中国剩余定理---表达整数的奇怪方式(每日一道算法2022.9.19)
    漏洞复现--时空智友企业流程化管控系统敏感信息泄露(POC)
    计算机毕业设计Java微博系统网站(源码+系统+mysql数据库+Lw文档)
    机器人——力控
    zookeeper 的 Watch 功能三种应用场景
    时刻保持学习状态
    python绑定游戏窗口模拟键盘鼠标操作方法64位32位通用
    在2024年WWDC大会上,苹果宣布了其全新的“Apple Intelligence”AI功能以及ISO18功能
  • 原文地址:https://blog.csdn.net/weixin_44247866/article/details/125446101