了解一下ES module 和 Commonjs

这篇具有很好参考价值的文章主要介绍了了解一下ES module 和 Commonjs。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

最近测试了几个 ES module 和 Commonjs 的例子,理解了之前不太理解的概念,记录一下。要是想多了解的可以去看看阮老师的 Module 那部分。会贴一小部分的代码,不会贴所有验证的代码。

Commonjs require 大概流程

本质上 Commonjs 一直是 node 在使用的规范,虽然其他平台也可以使用。

  • 处理路径,node 有专门的 path 模块和__dirname 等,将路径转成绝对路径,定位目标文件
  • 检查缓存
  • 读取文件代码(fs)
  • 包裹一个函数并执行(自执行函数)
  • 缓存
  • 返回 module.exports
ES module 大概流程

最重要的应该是解析依赖了,ES module 如果都是同步的,会很慢。都说 ES module 是异步的,在不同环境会有不同的结果。其实 ES module 的三个步骤是可以分开异步进行。在浏览器,会使用 HTML 的规范,最后的实例化是同步,在 node 环境,文件都是在本地,同步就显得很容易。

  • 模块解析,入口文件开始,构建 Module Record,然后放置到 Module Map。Module Map 相当于每一个 js 文件,Module Record 相当于里面依赖的每一个 import
  • 获取文件,解析文件,进行 JavaScript 的解析,变量提升等
  • 实例化,执行文件内容
exports 与 module.exports

Commonjs 可以用 exports.xxx 导出,也可以用 module.exports = {}导出,因为整个文件读取之后会包裹到一个自执行函数,差不多是这样:

(function(exports, require, module, filename, dirname){

})(exports, require, module, filename, dirname)

如果直接 exports = {}那么导出是无效的。下面三个例子就可以很好的理解:

function fn(obj){
  obj.num = 2;
};
let obj = {
  num: 1;
};
fn(obj);
console.log(obj);


function fn(obj){
  obj = {num: 2};
};
let obj = {
  num: 1;
};
fn(obj);
console.log(obj);


function fn(obj){
  obj = 2;
};
let obj = 1;
fn(obj);
console.log(obj);

对象是指针的引用,相当于 obj = xxxx,用 obj.xx 赋值其实就是给指针 xxxx 指向的对象赋值,如果 obj = {},相当于 obj 的指针改变了,相当于 obj = xx,所以 exports = {}是无效的。

ES module 是值的引用,Commonjs 是值的拷贝

这块其实挺好实验的,导出一个变量,调用函数改变这个变量再输出,可以得到 Commonjs 的值是不会因为执行了 add 就改变,ES module 就会:

let a = 10;
exports.a = a;
exports.add = () => {
  a++;
};

let a = 10;
export const b = a;
exports.add = () => {
  a++;
};
ES module 是编译时输出,Commonjs 是运行时加载

运行时加载也比较好实验(个人观点这样可以表示是运行时加载):

main.js
let a = require('./a.js');
let b = require('./b.js');

a.js
exports.a = 'a';
let b = require('./b.js');
exports.aa = 'aa';

b.js
let a = require('./a.js');
console.log(a,'in b.js');

这样去执行的时候,b.js 里面的 a 是{a: ‘a’},如果把 exports.aa = ‘aa’;放到 let b = require(‘./b.js’);之前,b.js 里面的 a 是{a: ‘a’, aa: ‘aa’}。

所以 Commonjs 是一边运行一边加载,当 a.js 执行到 let b = require(‘./b.js’);的时候,之前的代码是执行过了,并缓存起来,这时候就会去加载 b.js 并执行。

ES module 是编译时输出

不太确定是否能这样理解:

index.js
import {c} from './c.js';


c.js
import {d} from './d.js';
export let c = 'c';


d.js
import {c} from './c.js';
console.log(c,'in d.js');
export const d = 'd';

得到的结果会报错:Cannot access ‘c’ before initialization,如果 let c 改成 var c,结果是 undefined in d.js。

ES module 会有一个跟 JavaScript 解析一样的过程,先是解析整个 js,做一些变量提升,然后再执行。就是说会先加载所有的文件,并且解析,不会执行,在所有依赖文件加载解析完成,再开始执行。所以我是这样去理解的 ES module 是编译时输出。

ES module 和 Commonjs 循环引用的区别

这点其实挺重要的,ES module 和 Commonjs 都是通过缓存来解决循环引用的问题,不会造成死循环。Commonjs 是运行时加载,在解析到 require 的时候,会先检查缓存,如果没有,会先进行缓存再继续往下执行:

main.js
require('./a.js');

a.js
let b = require('./b.js');
exports.a = 'a';
console.log('a.js', b);

b.js
let a = require('./a.js');
console.log('b.js', a);
exports.b = 'b';

result:
b.js {}
a.js { b: 'b' }

大概流程:

  • main.js require(‘./a.js’); 检查缓存,没有 a.js,执行 a.js
  • a.js,检查缓存,没有,缓存 a.js。执行 let b = require(‘./b.js’);,检查缓存,没有 b.js,执行 b.js
  • b.js,检查缓存,没有 b.js,缓存 b.js。执行 let a = require(‘./a.js’);,检查缓存,有 a.js,获取缓存,打印获取的缓存,b.js 缓存加上 b: ‘b’
  • 回到 a.js,a.js 缓存加上 a: ‘a’,打印

所以 Commonjs 多次引入和循环引入的解决方案,是先缓存,再根据执行的内容新增缓存的内容,而且只会执行一次。

ES module 解决多次引入和循环引入也是依赖缓存,但是缓存的机制不一样。ES module 是值的引用和编译时输出,ES module 导出的是内存地址的索引:

index.js
import {c} from './c.js';

c.js
import {d} from './d.js';
console.log(d,'in c.js');
export var c = 'c';

d.js
import {c} from './c.js';
console.log(c,'in d.js');
export const d = 'd';

result
undefined in d.js
d in c.js

当解析到 d.js 的 import {c} from ‘./c.js’;,会去 module map 检查是否有 c moduel record,有,建立模块指向。当依赖解析完成之后,代码也解析完成了,最后实例化运行代码,所以 d.js 执行的时候 c 是 undefined。

ES module 动态引入 import()

Commonjs 的 require 可以是动态的,也不一定要放在顶层,ES module 的 import 就必须放在最顶层。动态加载在实际应用场景是必须的,对于性能方面有非常大的提升。最典型的就是路由懒加载,如果不是有动态 import,打包出来的是一个文件,首次加载会非常慢。还有是一些条件语句决定是否加载某些文件,对性能也非常友好。

tree shaking

ES module 可以实现 tree shaking,核心就是 ES module 是编译时输出,新进行编译再执行,编译过程就能确定哪些内容是无用的,Commonjs 就无法实现,只有在执行过程中才知道哪些内容是无用的。

node 执行 ES module

如果文件后缀是.mjs(node 执行的后缀是.cjs),那么 node 会根据 ES module 规范去执行,如果是 js,那么 package.json 里面要新增"type": “module”,否则会报错:

Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
SyntaxError: Cannot use import statement outside a module

也可以配置 exports 做兼容,exports 优先级高于 main:

"exports": {
    "import": "./src/index.js",
    "require": "./src/index.cjs"
  }
require 寻找引入的顺序

先看是否是内置包,如果是直接返回;看是否是相对路径,是就处理成可识别绝对路径,如果找不到就报错;不是内置包没有相对路径,从当前目录开始寻找 node_modules,找不到依次往上的目录寻找 node_modules,直到根目录,还找不到就报错。会先以文件名找,再依次是.js、.json、.node。

欢迎关注订阅号 coding个人笔记文章来源地址https://www.toymoban.com/news/detail-532644.html

到了这里,关于了解一下ES module 和 Commonjs的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • CommonJS 和 ES6 module

    本文主要自己觉得要记录的点记录下来,不耽误大家时间,会持续更新。 Module对象 CommonJS加载一次之后会缓存文件 加载过程 CommonJS返回的是对象引用之后,在A地方修改后,会影响到B地址的加载结果。 CommonJS  模块同步加载并执行模块文件,ES6 模块提前加载并执行模块文件,

    2024年02月12日
    浏览(34)
  • 记录一下,C#运行nodejs调用js文件提示报错:Error: node:internal/modules/cjs/loader:1080

    个人记录一下,C#运行nodejs调用js文件提示报错: 报错提示信息: Error: node:internal/modules/cjs/loader:1080 throw err;   ^  Error: Cannot find module \\\'F:鎴戠殑....................” .....................下面还有很多报错内容 还有英文提示模块未找到的提示。 我另一个文件运行没报错,运行正常有

    2024年02月11日
    浏览(156)
  • 深入理解CommonJS和ES Module? 优缺点?什么时候用?

    在webpack中,我们可以使用多种模块化方式,如CommonJS和ES Module。 CommonJS是什么? CommonJS是一种模块化规范,它是Node.js采用的模块化规范,它的主要特点是同步加载模块,模块输出的是一个值的拷贝,而不是引用。CommonJS的优点是简单易用,可以在服务器端和客户端使用,缺点

    2024年02月03日
    浏览(42)
  • Node.js开发、CommondJS 、ES-Module模块化设计

    目录  Node.js是什么 基础使用 Node的REPL 全局变量  模块化设计 CommondJS规范  基础使用exports和module.exports require  CommondJS优缺点 AMD和CMD规范 ES_Module  基本使用方法 导出 导入  结合使用 默认导出 ES Module解析流程  Node与浏览器的对比  在浏览器中,HTML与CSS交给Blink处理,如果其

    2023年04月21日
    浏览(44)
  • CommonJS 和 ES6 Module:一场模块规范的对决(上)

    🤍 前端开发工程师(主业)、技术博主(副业)、已过CET6 🍨 阿珊和她的猫_CSDN个人主页 🕠 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 🍚 蓝桥云课签约作者、已在蓝桥云课上架的前后端实战课程《Vue.js 和 Egg.js 开发企业级健康管理项目》、《带你从入

    2024年01月23日
    浏览(41)
  • JavaScript:模块化【CommonJS与ES6】

    在 JavaScript 编程中,随着项目的复杂性增加,代码的组织和管理变得至关重要。模块化是一种强大的编程概念,它允许我们将代码划分为独立的模块,提高了可维护性和可扩展性。本文将详细介绍 CommonJS 和 ES6 模块,帮助你理解它们的特点和用法。 1. CommonJS 模块化 CommonJS 是

    2024年02月13日
    浏览(54)
  • 前端架构师-week3-Node项目如何支持ES Module

    目录 方案一: 通过 webpack 完成 ES Module 资源构建 通过 webpack target 属性支持 Node 内置库 webpack loader 配置 babel-loader 支持低版本 Node  方案二: 通过Node原生支持ES Module Node 支持 ES Module 的两种方法 总结     根目录下创建 webpack.config.js

    2024年02月06日
    浏览(36)
  • 008Node.js模块、自定义模块和CommonJs

    CommonJS API定义很多普通应用程序(主要指非浏览器的应用)使用的API,从而填补了这个空白。它的终极目标是提供一个类似Python,Ruby和Java标 准库。这样的话,开发者可以使用CommonJS API编写应用程序,然后这些应用可以运行在不同的JavaScript解释器和不同的主机环境中。在兼容

    2024年04月13日
    浏览(39)
  • 前端面试:【前端工程化】CommonJS 与 ES6 模块

    嗨,亲爱的前端开发者!在现代Web开发中,模块化是构建可维护和可扩展应用程序的关键。本文将深入探讨两种主要的JavaScript模块系统:CommonJS 和 ES6 模块,以帮助你了解它们的工作原理、用法以及如何选择合适的模块系统。 1. CommonJS: 用途: CommonJS 是一种模块系统,最初

    2024年02月11日
    浏览(55)
  • 前端jd要求:了解一门后端开发语言优先 解决方案之Node.js

    作为前端开发者,了解一门后端开发语言可以为我们提供更多的职业机会和技术优势。在当今的技术领域中,前后端分离的开发模式已经成为主流,前端和后端的协作和沟通变得越来越紧密。因此,作为前端开发者,学习一门后端语言已经成为提高自己技能的重要途径。 以下

    2024年02月12日
    浏览(52)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包