查看“JS模块:ESM 与 CJM”的源代码
←
JS模块:ESM 与 CJM
跳到导航
跳到搜索
因为以下原因,您没有权限编辑本页:
您请求的操作仅限属于该用户组的用户执行:
用户
您可以查看和复制此页面的源代码。
[[category:JavaScript]] == 关于<ref>参考: # [https://wangdoc.com/es6/module-loader 网道:ES6 教程:《Module 的加载实现》] # [https://zhuanlan.zhihu.com/p/113009496 《前端科普系列-CommonJS:不是前端却革命了前端》] </ref> == {| class="wikitable" |+ ESM 与 CJM |- ! !! ESM !! CJM |- | 应用场景 | * 用于:浏览器、node.js | * 用于:node.js |- | 加载方式 | * 静态加载:'''编译时'''输出'''模块接口'''<sup>[[#“编译时加载”与“运行时加载” | “编译时加载”与“运行时加载”]]</sup> * '''异步'''加载 | * 动态加载:'''运行时'''输出'''模块对象'''<sup>[[#“编译时加载”与“运行时加载” | “编译时加载”与“运行时加载”]]</sup> * '''同步'''加载 |- | 模块机制 | * 导出内容:'''各接口''',多方式(单接口、多接口、对象) * 导出方式:'''引用'''<sup>[[#“值引用”与“值拷贝” | “值引用”与“值拷贝”]]</sup> * this 指向:undefined | * 导出内容:'''对象“module.exports”,各接口作为其属性''' * 导出方式:'''拷贝'''<sup>[[#“值引用”与“值拷贝” | “值引用”与“值拷贝”]]</sup> * this 指向:当前模块 |- | 循环引用<sup>[[#循环引用 | 循环引用]]</sup> | * 不会出现循环引用 | * 应该避免循环引用 |} === “编译时加载”与“运行时加载” === “编译时加载”:编译时就确定模块的依赖关系,以及导入和导出的接口 “运行时加载”:运行时才确定模块的依赖关系,以及导入和导出的对象 CommonJS 示例: : <syntaxhighlight lang="JavaScript" highlight=""> // 代码合法 const name = getName() console.log(name) import { getName } from 'module.js' </syntaxhighlight> :* 由于“运行时加载”,'''import 将会被提升至代码头部''',先于 getName() 执行,所以代码是合法的。 ES Module 示例: : <syntaxhighlight lang="JavaScript" highlight=""> // 代码报错:import 中不能使用表达式 import { 'n' + 'ame' } from 'module.js' // 代码报错:import 中不能使用变量 let module = 'module.js' import { name } from module </syntaxhighlight> :* 由于“编译时加载”,'''import 中不能使用变量、表达式'''。 === “值引用”与“值拷贝” === “值引用”:被导出的绑定值依然可以在本地进行修改 “值拷贝”:一旦值被导出,模块内部的变化就不再影响该值 CommonJS 示例: # a.js: #: <syntaxhighlight lang="JavaScript" highlight=""> var name = 'morrain' var age = 17 exports.name = name exports.age = age exports.setAge = function(a){ age = a } </syntaxhighlight> # b.js: #: <syntaxhighlight lang="JavaScript" highlight=""> var a = require('a.js') console.log(a.age) // 输出:18 a.setAge(19) console.log(a.age) // 输出:18 </syntaxhighlight> ES Module 示例: # a.js: #: <syntaxhighlight lang="JavaScript" highlight=""> var name = 'morrain' var age = 17 const setAge = a => age = a export { name, age, setAge } </syntaxhighlight> # b.js: #: <syntaxhighlight lang="JavaScript" highlight=""> import * as a from 'a.js' console.log(a.age) // 输出:18 a.setAge(19) console.log(a.age) // 输出:19 </syntaxhighlight> == 循环引用 == CommonJS:模块在返回时可能尚未完成执行,不会出现无限循环。 ES Module: —— 静态导入:模块在初始化完成之前不能访问 —— 应该避免“循环引用”。 —— 动态导入:由于是异步执行,不会出现无限循环,但可能不能得到正确的内容 —— 应该避免“循环引用”。 === CommonJS 示例: === # a.js: #: <syntaxhighlight lang="JavaScript" highlight=""> console.log('a starting'); exports.done = false; const b = require('./b.js'); console.log('in a, b.done = %j', b.done); exports.done = true; console.log('a done'); </syntaxhighlight> # b.js: #: <syntaxhighlight lang="JavaScript" highlight=""> console.log('b starting'); exports.done = false; const a = require('./a.js'); console.log('in b, a.done = %j', a.done); exports.done = true; console.log('b done'); </syntaxhighlight> # main.js: #: <syntaxhighlight lang="JavaScript" highlight=""> console.log('main starting'); const a = require('./a.js'); const b = require('./b.js'); console.log('in main, a.done = %j, b.done = %j', a.done, b.done); </syntaxhighlight> # 输出: #: <syntaxhighlight lang="JavaScript" highlight=""> $ node main.js main starting a starting b starting in b, a.done = false b done in a, b.done = true a done in main, a.done = true, b.done = true </syntaxhighlight> === ES Module “静态导入”示例: === # a.mjs: #: <syntaxhighlight lang="JavaScript" highlight=""> import b from './b.mjs' let done = false; console.log('a starting'); console.log('in a, b.done = %j', b.done); done = true; console.log('a done'); export default done </syntaxhighlight> # b.mjs: #: <syntaxhighlight lang="JavaScript" highlight=""> import a from './a.mjs' let done = false; console.log('b starting'); console.log('in b, a.done = %j', a.done); done = true; console.log('b done'); export default done </syntaxhighlight> # main.mjs: #: <syntaxhighlight lang="JavaScript" highlight=""> import a from './a.mjs' import b from './b.mjs' console.log('main starting'); console.log('in main, a.done = %j, b.done = %j', a.done, b.done); </syntaxhighlight> # 输出: #: <syntaxhighlight lang="JavaScript" highlight=""> PS D:\Documents\VSCode\helloWorld> node "d:\Documents\VSCode\helloWorld\main.mjs" b starting file:///d:/Documents/VSCode/helloWorld/b.mjs:5 console.log('in b, a.done = %j', a.done); ^ ReferenceError: Cannot access 'a' before initialization at file:///d:/Documents/VSCode/helloWorld/b.mjs:5:34 at ModuleJob.run (node:internal/modules/esm/module_job:194:25) Node.js v18.15.0 </syntaxhighlight> === ES Module “动态导入”示例: === # a.mjs: #: <syntaxhighlight lang="JavaScript" highlight=""> let done = false; console.log('a starting'); async function load() { let b = await import('./b.mjs'); console.log('in a, b.done = %j', b.done); } load() done = true; console.log('a done'); export default done </syntaxhighlight> # b.mjs: #: <syntaxhighlight lang="JavaScript" highlight=""> let done = false; console.log('b starting'); async function load() { let a = await import('./a.mjs'); console.log('in b, a.done = %j', a.done); } load() done = true; console.log('b done'); export default done </syntaxhighlight> # main.mjs: #: <syntaxhighlight lang="JavaScript" highlight=""> console.log('main starting'); async function load() { let a = await import('./a.mjs'); let b = await import('./b.mjs'); console.log('in main, a.done = %j, b.done = %j', a.done, b.done); } load() </syntaxhighlight> # 输出: #: <syntaxhighlight lang="JavaScript" highlight=""> PS D:\Documents\VSCode\helloWorld> node "d:\Documents\VSCode\helloWorld\main.mjs" main starting a starting a done b starting b done in a, b.done = undefined in main, a.done = undefined, b.done = undefined in b, a.done = undefined </syntaxhighlight> == ES Module:循环引用的处理 == <big>💡</big> 利用<span style="color: blue; font-size: 120%">'''“函数提升”'''</span>解决循环引用 <code><span style="color: red">ReferenceError: Cannot access 'a' before initialization</span></code> 的问题; 示例: # a.mjs: #: <syntaxhighlight lang="JavaScript" highlight=""> import * as b from './b.mjs' console.log('a starting'); console.log(`in a, b.msg1 = ${b.msg1}, b.msg2 = ${b.msg2}, print:${b.print()}`); // var 提升:只会提升声明,不会提升其初始化 var msg1 = 'msg1-a' var msg2 = function(){ return 'msg2-a'; }() // 函数提升 function print(){ return 'hello from a !' } console.log('a done'); export {msg1, msg2, print} </syntaxhighlight> # b.mjs: #: <syntaxhighlight lang="JavaScript" highlight=""> import * as a from './a.mjs' console.log('b starting'); console.log(`in b, a.msg1 = ${a.msg1}, a.msg2 = ${a.msg2}, print:${a.print()}`); // var 提升:只会提升声明,不会提升其初始化 var msg1 = 'msg1-b' var msg2 = function(){ return 'msg2-b'; }() // 函数提升 function print(){ return 'hello from b !' } console.log('b done'); export {msg1, msg2, print} </syntaxhighlight> # 运行 a.mjs 输出: #: <syntaxhighlight lang="JavaScript" highlight=""> PS D:\Documents\VSCode\demo> node "d:\Documents\VSCode\demo\a.mjs" b starting in b, a.msg1 = undefined, a.msg2 = undefined, print:hello from a ! b done a starting in a, b.msg1 = msg1-b, b.msg2 = msg2-b, print:hello from b ! a done </syntaxhighlight> <big>📜</big> '''“变量提升”只能使不报错,但不能得到有效内容''' —— <span style="color: blue">只会提升“声明”,不会提升“初始化”</span> —— 所以,上面变量为 <code><span style="color: green">'''undefined'''</span></code> == 在 node.js 中<ref>参考:[https://nodejs.cn/api-v18/packages.html Node.js文档:《module/package 包模块》]</ref> == Node.js 有两个模块系统:CommonJS 模块和 ECMAScript 模块。 Node.js 会将以下视为 ES Module: # 扩展名为 <code>'''.mjs'''</code> 的文件。 # 最近的父“'''package.json'''”文件中“'''type'''”字段值为“'''module'''”时,扩展名为 <code>'''.js'''</code> 的文件。 # 字符串作为参数传入 <code>'''--eval'''</code>,或通过“'''STDIN'''”管道传输到 node,带有标志 <code>'''--input-type=module'''</code>。 Node.js 会将以下视为 CommonJS: # 扩展名为 <code>'''.cjs'''</code> 的文件。 # 最近的父“'''package.json'''”文件中“'''type'''”字段值为“'''commonjs'''”时,扩展名为 <code>'''.js'''</code> 的文件。 # 字符串作为参数传入 <code>'''--eval'''</code> 或 <code>'''--print'''</code>,或通过“'''STDIN'''”管道传输到 node,带有标志 <code>'''--input-type=commonjs'''</code>。 包作者应该设置“type”字段 ! 应避免同时使用 ES Module 和 CommonJS ! == 参考 == <references/>
返回至“
JS模块:ESM 与 CJM
”。
导航菜单
个人工具
登录
命名空间
页面
讨论
大陆简体
已展开
已折叠
查看
阅读
查看源代码
查看历史
更多
已展开
已折叠
搜索
导航
首页
最近更改
随机页面
MediaWiki帮助
笔记
服务器
数据库
后端
前端
工具
《To do list》
日常
阅读
电影
摄影
其他
Software
Windows
WIKIOE
所有分类
所有页面
侧边栏
站点日志
工具
链入页面
相关更改
特殊页面
页面信息