JS模块化 - 学习笔记

目录

背景

幼年期:并无模块化

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

作用域的把控

成熟期:CJS - Common.js

AMD规范

CMD规范

ES6模块化

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

背景

工程化实现


背景

js本身简单的页面射你:页面动画 + 表单提交,当初并无模块化 or 命名空间的概念。

 JS的模块化需求日益增长

幼年期:并无模块化

  1. 开始需要在页面中增加一些不同的js:动画、表单、格式化
  2. 各种js文件被分在不同的文件中
  3. 不同的文件又被同一个模版引用
  <script src="jquery.js"></script>
  <script src="main.js"></script>
  <script src="dep1.js"></script>
  //……

认可:

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

问题出现:

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

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

作用域的把控

例子:

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

  increase();
  reset();

利用函数块级作用域

(() => { 
    let count = 0;
    // ……
})

仅定义一个函数,如果立即执行

(() => {
    let count = 0;
    // ……
}();

初步实现一个最最最简单的模块

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

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

iifeModule.increase();
iifeModule.reset();

追问:有额外依赖时,如何优化IIFE相关代码

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

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

        面试1: 了解早期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();

成熟期:CJS - Common.js

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
}

模块使用方式

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

  increase();
  reset();

可能被问到的问题

实际执行处理

  (function (thisValue, exports, require, module) {
    const dependencyModule1 = require(./dependencyModule1);
    const dependencyModule2 = require(./dependencyModule2);

    // 业务逻辑……
  }).call(thisValue, exports, require, module);

优点:CommonJS率先在服务端实现了,从框架层解决依赖、全局变量污染的问题。

缺点:主要针对了服务端的解决方案,对于异步拉取依赖的处理整合的不是那么的友好。

新的问题 —— 异步依赖

AMD规范

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

新增定义方式

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

模块定义方式

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

    return {
      increase, reset
    }
  })

引入模块:

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

面试题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
    }
  })

面试题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();
  })

面试题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
  )

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

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

CMD规范

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

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

    let dependencyModule1 = require('./dependecyModule1');
    // dependencyModule1相关逻辑
  })

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

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

面试题5: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
  }

模版引入的地方

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

node中

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

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

面试题6:动态模块

考察:export、promise

ES11原生解决方案

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

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

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

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

背景

根本问题 —— 运行时进行依赖分析

前端的模块化处理方案依赖于运行时分析

解决方案:线下执行

grunt gulp webpack

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

    export.run = () {
      // run
    }
  })

工程化实现

step1:扫描依赖关系表:

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

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'], () => {
    // 执行代码
    export.run = () => {}
  })

优点:

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

清梦-

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

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

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

打赏作者

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

抵扣说明:

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

余额充值