JS模块化

1. 不得不说的历史

背景

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;// 代码块1const increase = () => ++count;// 代码块2const 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 - 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
}

模块使用方式

  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进行加载/*defineparams: 模块名,依赖模块,工厂方法*/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();}))(// 目标是一次性区分CommonJSorAMDtypeof module === "object"&& module.exports&& typeof define !== "function"? // 是 CJSfactory => module.exports = factory(require, exports, module): // 是AMDdefine)
  • 优点: 适合在浏览器中加载异步模块,可以并行加载多个模块
  • 缺点:会有引入成本,不能按需加载
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>
  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框架组件化 + 设计模式

js模块化:详解与面试相关推荐

  1. 波比-JS模块化详解

    1.什么是模块化 模块化是一种代码管理.组织和通信的模式 2.为什么要用模块化? 随着项目复杂度的提高,项目变得越来越难以维护,js模块化也油然而生,所有的模块都处于全局域下,容易造成命名冲突的问题, ...

  2. JAVA9模块化详解(一)——模块化的定义

    JAVA9模块化详解(一)--模块化的定义 前言 java9已经出来有一段时间了,今天向大家介绍一下java9的一个重要特性--模块化.模块化系统的主要目的如下: 更可靠的配置,通过制定明确的类的依赖 ...

  3. 8 年经验面试官详解 Java 面试秘诀!

    日前,全球知名 TIOBE 编程语言社区最新发布 11 月的编程语言排行榜,根据最新的榜单显示,相比上个月编程语言 Top 5 并没有太大的变化,其中 Java 依旧稳坐榜首,随后分别是 C.Pyth ...

  4. 李洪强iOS经典面试题156 - Runtime详解(面试必备)

    李洪强iOS经典面试题156 - Runtime详解(面试必备)   一.runtime简介 RunTime简称运行时.OC就是运行时机制,也就是在运行时候的一些机制,其中最主要的是消息机制. 对于C ...

  5. Three.js实例详解___旋转的精灵女孩(附完整代码和资源)(一)

    Three.js实例详解___旋转的精灵女孩(附完整代码和资源)(一) 本文目录: 一.[旋转的精灵女孩]案例运行效果 二.Three.js简介 三.Three.js代码正常运行显示条件 (1)不载入 ...

  6. Python爬虫JS解密详解,学会直接破解80%的网站(一)!!!

    文章目录 1.网页查看 2.有道翻译简单实现源码 3.JS解密(详解) 4.python实现JS解密后的完整代码 4.1.实现效果 5.JS解密后完整代码升级版 5.1.实现效果 CSDN独家福利降临 ...

  7. 使用Gin框架集成JWT,源码、详解、面试问题

    使用Gin框架集成JWT,源码.详解.面试问题 一.什么是JWT Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519). ...

  8. 【Java网络编程与IO流】Http协议详解以及面试有关问题

    HTTP协议详解以及面试有关题目 1 HTTP请求 一个HTTP请求报文由请求行.请求头部.空行和请求数据四个部分组成. 1.1 请求行 请求行中有请求方法字段.URL字段和HTTP协议版本3个字段组 ...

  9. Three.js实例详解___旋转的精灵女孩(附完整代码和资源)(三)

    Three.js实例详解___旋转的精灵女孩(附完整代码和资源)(三) 本篇目录: 六.完整构建整个[旋转的精灵女孩]实例 (1).新建.启动webGL工程空间 (2).构建项目的目录层次结构 (2. ...

  10. Three.js实例详解___旋转的精灵女孩(附完整代码和资源)(二)

    Three.js实例详解___旋转的精灵女孩(附完整代码和资源)(二) 本篇目录: 五.实例中所使用的代码语法详细解释 (1).构建一个三维空间场景 (2).选择一个透视投影相机作为观察点 (a).创 ...

最新文章

  1. OpenCV 错误:无法打开摄像头(打开摄像头卡机)
  2. MIT警示“深度学习过度依赖算力”,研究三年算法不如用10倍GPU
  3. ipad连接电脑_Ipad已经停用需要连接iTunes的一种解决方案,
  4. 从零入门 Serverless | 一文搞懂函数计算及其工作原理
  5. 语音识别中强制对齐_语音识别中的标注问题和嵌入式训练
  6. socket只能连接本地mysql_MySQL本地用IP登陆而非socket
  7. 如何在WinForm中发送HTTP请求
  8. 60-140-044-使用-DataSink-使用OutputTag进行Side Output(侧输出)
  9. JSON.Stringify
  10. 安全测试——SQL注入
  11. 内网穿透NPS使用教程
  12. R、冗余分析(RDA)、ggplot2、置信椭圆
  13. 华为牛人在华为工作十年的感悟!
  14. 大学生创业知识(转)
  15. 报错:Unfortunately you can‘t have non-Gradle Java modules and Android-Gradle modules in one project
  16. Cassandra之jdbc-cassandra使用笔记
  17. SQLServer 2000 服务不能启动的解决办法
  18. 三星宣布量产64层V-NAND闪存芯片: 传输速度达1Gbps
  19. 分享几个谷歌(Chrome 内核浏览器)浏览器扩展 crx 下载站
  20. Java中向上转型与向下转型

热门文章

  1. vue 自适应屏幕的宽高度
  2. linux docker 常用命令
  3. Codeforces Round #826 (Div. 3)(D. Masha and a Beautiful Tree)线段树解法
  4. 记录一次nodejs 从高得地图根据地名获取经纬度
  5. TEQC数据处理与质量分析
  6. Python金融量化
  7. 关于浮子流量计的小知识!
  8. 2、maven案例idexXML调模板maven运Tomcat报错org.apache.jasper.JasperException: Unable to compile class forJSP
  9. Office Word 护眼模式设置
  10. bing 高级搜索_如何使用Bing的高级搜索运算符:更好搜索的8条提示