type
status
date
slug
summary
tags
category
titleIcon
password
icon
calloutIcon

定义

模块化是一种代码组织方式,用于提高开发效率,降低维护成本。

模块化标准前的方式演进

基于文件划分的方式

notion image

html

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> </head> <body> <h1>基于文件的模块划分</h1> <script src="module-a.js"></script> <script src="module-b.js"></script> <script> method() console.log(name) name = 'foo' console.log(name) </script> </body> </html>
HTML

javascript

var name = 'module-a' var method = () => { console.log(`method-${name}`) }
JavaScript

javascript

var name = 'module-b' var method = () => { console.log(`method-${name}`) }
JavaScript

javascript

method-module-b module-b foo
JavaScript
  • 一份文件就是一个模块,通过script标签引入使用
  • 对其中变量的调用是对全局成员的调用
弊端
  • 命名冲突
  • 污染全局作用域
  • 无法管理模块依赖关系
  • 模块中所有成员都可以在外部访问修改
  • 完全依靠约定的方式

命名空间方式

html

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> </head> <body> <h1>基于命名空间的模块划分</h1> <script src="module-a.js"></script> <script src="module-b.js"></script> <script> moduleA.method() moduleB.method() console.log(moduleA.name) moduleA.name = 'foo' console.log(moduleA.name) </script> </body> </html>
HTML

javascript

var moduleA = { name: 'module-a', method: function () { console.log(`${this.name}-method`) } }
JavaScript

javascript

var moduleB = { name: 'module-b', method: function () { console.log(`${this.name}-method`) } }
JavaScript

javascript

module-a-method module-b-method module-a foo
JavaScript
  • 每个模块只暴露一个全局对象,所有模块成员挂载到全局对象下
  • 相比于文件的方式将模块包裹在一个全局对象下实现,相当于添加了命名空间
弊端
  • 命名冲突
  • 污染全局作用域
  • 无法管理模块依赖关系
  • 仍没有私有空间,模块中所有成员都可以在外部访问修改

IIFE 立即执行函数方式

  • 模块所有成员放在函数提供的私有作用域中
  • 可以通过挂到全局对象的方式暴露成员
  • 实现了私有成员(模块内部闭包访问) 外部无法直接使用
  • 用IIFE参数作为依赖声明 使每个模块间依赖关系明显

html

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> </head> <body> <h1>基于IIFE的模块划分</h1> <script src="module-a.js"></script> <script src="module-b.js"></script> <script> moduleA.method() console.log(moduleA.name) moduleA.name = 'foo' console.log(moduleA.name) moduleA.method() moduleB.method() </script> <script> dependency = 'React' </script> <script src="module-b.js"></script> <script> moduleB.method() </script> </body> </html>
HTML

javascript

;(function () { var name = 'module-a' function method() { console.log(`method-${name}`) } window.moduleA = { method } })()
JavaScript

javascript

;(function ($) { var name = 'module-b' function method() { console.log(`dependency-${$}`) console.log(`method-${name}`) } window.moduleB = { method } })(dependency)
JavaScript

javascript

Uncaught ReferenceError ReferenceError: dependency is not defined at <anonymous> method-module-a undefined foo method-module-a Uncaught ReferenceError ReferenceError: moduleB is not defined at <anonymous> dependency-React method-module-b
JavaScript

总结

  • 上述<基于文件,基于命名空间,IIFE>的方式均为早期没有工具与规范下的模块化落地实践
  • 最后仍需要手动引入依赖,可能出现忘记引用或未删除不再引用的依赖

模块化标准

CommonJS

  • 一个文件就是一个模块
  • 每个模块具有单独作用域
  • module.exports导出
  • require导入
  • 作为nodejs的默认模块标准,因为node在启动时加载模块,执行时不需要加载,所以是通过同步方式加载模块
但若在浏览器里使用会有大量同步请求

AMD规范 (Asynchronous Module Definition)

  • 通常用在浏览器中用于异步加载模块
NodeJS环境下示例

javascript

// 引入模块加载器 var requirejs = require('requirejs') // 载入模块 requirejs(['./moduleA'], function (module1) { module1.call() })
JavaScript

javascript

// 定义模块 define('moduleA', ['./moduleB'], function ($) { return { call: function () { console.log($) } } })
JavaScript

javascript

define('moduleB', function () { return 'moduleB' })
JavaScript

javascript

moduleB
JavaScript
浏览器中异步原理:加载模块会创建script标签请求脚本执行模块代码
弊端
  • 模块代码相对复杂
  • JS文件请求次数多

CMD

  • 通过sea.js作模块加载器

ES Modules (ES6/2015后)

  • 浏览器环境的默认,语言标准层面的模块化
  • 通过script标签里type=”module”以ES Modules标准执行其中JS代码
  • ES Modules自动采用严格模式,忽略”use strict”(非严格模式this打印全局对象,严格模式下为undefined)
  • 每个ESM模块都是单独私有作用域
  • 通过CORS请求获取外部JS资源(CORS不支持文件形式访问 需要http server方式)
  • script标签延迟执行脚本,相当于defer效果(script默认立即执行,等待脚本加载完毕后继续渲染页面)

html

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> </head> <body> <!-- 1.严格模式 --> <script type="module"> console.log(this) </script> <!-- 2.单独作用域 --> <script type="module"> var foo = 'esther' console.log(foo) </script> <!-- 3.CORS请求 --> <script type="module"> console.log(foo) </script> <script type="module" src="./call.js"></script> <!-- 4.延迟执行 --> <!-- <script> alert('Message1') </script> <h1>先弹出后显示</h1> --> <script type="module"> alert('Message2') </script> <h1>先显示后弹出</h1> </body> </html>
HTML

javascript

Access to script at 'ESModules/call.js' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, isolated-app, chrome-extension, chrome, https, chrome-untrusted. call.js:1 Failed to load resource: net::ERR_FAILED undefined esther Uncaught ReferenceError: foo is not defined at (anonymous)
JavaScript
notion image
notion image
notion image
导入导出
  • 一般导入导出

javascript

let name = 'bbc' setTimeout(() => { name = 'alpha' }, 1000) export { name }
JavaScript

javascript

import { name } from './a.mjs' console.log(name) setTimeout(() => console.log(name), 1500)
JavaScript
  • 修饰成员声明导出与重命名

javascript

export var name = 'name' function call() { console.log('call') } class A {} export { call, A as B }
JavaScript

javascript

import { B, call } from './export.js' console.log(B) console.log(call)
JavaScript

javascript

[class A] [Function: call]
JavaScript
  • 默认导出与引入
default关键字不能当变量使用
可以将某一变量当做默认导出
需要重命名default
导入时直接import变量名方式接收默认导出成员

typescript

import a from './exportD.js' // 等价于 // import { default as a} from './exportD.js' console.log(a)
TypeScript

javascript

export default class A { } // 等价于 // export { A as default}
JavaScript
并非导出对象(简写)与解构语法
notion image
export default接才是对象
notion image
验证
notion image
notion image
表达式错误
notion image
导出的是引用
notion image

javascript

let name = 'bbc' setTimeout(() => { name = 'alpha' }, 1000) module.exports = name // module.exports = { name }
JavaScript
commonjs后接导出对象,不是括号语法

javascript

const a = require('./a.cjs') console.log(a) setTimeout(() => console.log(a), 1500)
JavaScript
require里接路径,不为名称

javascript

let name = 'bbc' setTimeout(() => { name = 'alpha' }, 1000) module.exports = { name }
JavaScript

javascript

const { name } = require('./a.cjs') console.log(name) setTimeout(() => console.log(name), 1500)
JavaScript

javascript

bbc bbc
JavaScript

javascript

let name = 'bbc' setTimeout(() => { name = 'alpha' }, 1000) export { name }
JavaScript

javascript

import { name } from './a.mjs' console.log(name) setTimeout(() => console.log(name), 1500)
JavaScript

javascript

bbc alpha
JavaScript
commonjs
CommonJS modules export values, while ES6 modules export immutable bindings
notion image
import路径需要完整名称 commonjs可省
index.js不可省 commonjs可
notion image
文件路径名称使用打包工具打包模块时可省略扩展名 | index.js
如果省略/会认为是第三方模块 网页中引用可省 需要.开头 与commonjs一致
notion image
可绝对路径/开头 与 url引用
notion image
仅执行 不导入成员
notion image
提取全部成员 as放到一个对象中
每个成员都是对象一个属性
notion image
动态的路径 import 路径变量不可行
notion image
有时需要条件满足后再导入模块 也不能import
需要在最顶层 不能嵌套在其他作用域里
notion image
需要动态导入模块的机制
import函数
返回promise
notion image
同时导出模块默认成员与命名成员
notion image
 
notion image
notion image
直接导出导入成员
后续也就不再能使用这些成员
notion image
notion image
如果default导出需要index重命名,不然会作为index的default导出
notion image
notion image
notion image
notion image
es6 to es5 或 polyfill
使用unpkg获取js代码内容放入浏览器中使用
notion image
babel浏览器运行版本
es module loader 读出代码交给babel转换
notion image
 
notion image
支持esmodules的会执行两次
nomodule里只会在不支持es6 module的浏览器里工作
notion image
notion image
polyfill动态解析 效率差
应提前编译
notion image
原生使用esmodules
notion image
notion image

javascript

import { camelCase } from 'lodash' ^^^^^^^^^ SyntaxError: Named export 'camelCase' not found. The requested module 'lodash' is a CommonJS module, which may not support all module.exports as named exports. CommonJS modules can always be imported via the default export, for example using: import pkg from 'lodash'; const { camelCase } = pkg;
JavaScript
如果没有命名导出,可以先默认导出然后解构
notion image
notion image
notion image
notion image
原生不行
notion image
notion image
notion image
全局成员commonjs内置
notion image
esmodule里全都不能使用
都是commonjs在将模块包装成函数后通过参数提供进来的成员 esmodule中就没有这些提供了
 
notion image
替代方案
import export 对应前三
import.meta.url file协议的 文件url地址
notion image
notion image
notion image
包装成的函数的形参 伪全局对象
 
 

NodeJS中使用ES Modules

  • 文件名命名为mjs
  • 在package.json中设置”type”: “module”
notion image
 
notion image
notion image
notion image
preset-env包含所有新特性 插件集合
 
notion image
node_modules下.bin里有执行文件
notion image
notion image
preset一组插件
notion image
 
 

javascript

export const user = { abc: 1 }
JavaScript

javascript

import { user } from './a.mjs' // import * as user from './a.mjs' console.log(user) // { abc: 1 }
JavaScript

javascript

// import { user } from './a.mjs' import * as user from './a.mjs' console.log(user) // [Module: null prototype] { user: { abc: 1 } }
JavaScript
 
Bing&Google搜索引擎收录前端面试题整理
Loading...
CamelliaV
CamelliaV
Java;CV;ACGN
最新发布
单例模式的四种写法
2025-4-24
体验MCP
2025-4-24
MetingJS使用自定义音乐源-CF+Huggingface部署
2025-4-2
博客访问站点测速分析与对比
2025-3-26
前端模块化
2025-3-16
Voxel2Mesh相关论文精读与代码复现
2025-3-15
公告
计划:
  • LLM相关
  • 支付业务 & 双token无感刷新
  • (线程池计算优惠方案)天机学堂Day09-Day12复盘-优惠劵业务
  • (业务复盘,技术汇总)天机学堂完结复盘
  • hot 100
 
2024-2025CamelliaV.

CamelliaV | Java;CV;ACGN


  1. 1 给予你的爱 Xi YuaN/Digital Vengeance/唢清
  2. 2 スペルビア帝国/夜 平松建治
  3. 3 Imagination QQHHh
  4. 4 virtues QQHHh
  5. 5 Tricolor (short ver.) Digital Vengeance/44
  6. 6 港口夜 - 四周年 月代彩
  7. 7 神よ、その黄昏よ 金﨑猛
  8. 8 絆炎 (English Ver) Katherine Eames
  9. 9 ラストエンゲージ~祈りの呪文 馬場泰久
  10. 10 an evening calm fripSide
  11. 11 フレスベルグの少女~風花雪月~ Caro
  12. 12 Answer 北原春希/小木曽雪菜
  13. 13 Kiss Kiss Kiss BENI
  14. 14 远航高歌 染音若蔡/阿南
  15. 15 Sentimental Blue Trident
  16. 16 目指す先にあるもの Falcom Sound Team J.D.K.
  17. 17 Night City r e l/Artemis Delta
  18. 18 Gimme×Gimme P*Light/Giga/初音ミク/鏡音リン
  19. 19 桃幻浪漫 Airots/Active Planets & AUGUST
  20. 20 DESIRE 美郷あき
  21. 21 镜花堂(feat.芬璃尔) 幻塔手游/Rux
  22. 22 she was sitting under the osmanthus tree 梶浦由記
给予你的爱 - Xi YuaN/Digital Vengeance/唢清
00:00 / 03:59