Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

What is 全面ESM 🤩 #826

Closed
xiaoxian521 opened this issue Dec 12, 2023 · 0 comments
Closed

What is 全面ESM 🤩 #826

xiaoxian521 opened this issue Dec 12, 2023 · 0 comments

Comments

@xiaoxian521
Copy link
Member

xiaoxian521 commented Dec 12, 2023

2023-12-12 随着最后一个 升级stylelint v16并遵循esm语法pr合并vue-pure-admin全面迈入ECMAScript模块(ESM)时代,这能很好地适应前端发展潮流,不论从平台角度还是用户角度,它都为后续开发、升级、维护做了较好的铺垫!


在前端和Node.js开发中,ECMAScript模块(ESM)和CommonJS(CJS)是最为常见和广泛使用的两种模块系统


先简单介绍一下CJS

CommonJS(CJS)是一种在Node.js中广泛使用的模块规范。它被设计用于服务器端JavaScript环境,但也被用于一些客户端(浏览器)的工具和库中。CommonJS的主要目标是创建一个模块生态系统,使得开发者可以编写在不同环境下可重用的模块。

关键特征

  1. 同步加载:

    • CommonJS模块通常是同步加载的,这意味着模块在被require时会立即加载和执行。这在服务器端是可行的,因为文件通常都是本地的,但在浏览器中可能会导致性能问题。
  2. 模块导出:

    • 每个CommonJS模块都有一个module对象,该对象有一个exports属性。模块可以通过添加属性到exports对象或者直接将module.exports赋值为一个值(如函数、对象或类)来导出内容。
  3. 模块导入:

    • 使用require函数来导入其他模块。require函数接受一个模块标识符(通常是文件路径)作为参数,并返回该模块导出的内容。
  4. 模块作用域:

    • CommonJS模块中的变量、函数等默认都是私有的,除非它们被明确地导出。

基本语法

导出模块

导出一个函数:

// myModule.js
function myFunction() {
  console.log('Hello from myFunction');
}

module.exports = myFunction;

导出一个对象:

// myModule.js
module.exports = {
  myFunction: function() {
    console.log('Hello from myFunction');
  },
  anotherFunction: function() {
    console.log('Hello from anotherFunction');
  }
};

或者使用exports作为module.exports的快捷方式:

// myModule.js
exports.myFunction = function() {
  console.log('Hello from myFunction');
};

exports.anotherFunction = function() {
  console.log('Hello from anotherFunction');
};

导入模块

导入整个模块:

// main.js
const myModule = require('./myModule.js');

myModule.myFunction(); // 输出: Hello from myFunction
myModule.anotherFunction(); // 输出: Hello from anotherFunction

导入特定的函数(如果模块导出了一个对象):

// main.js
const { myFunction, anotherFunction } = require('./myModule.js');

myFunction(); // 输出: Hello from myFunction
anotherFunction(); // 输出: Hello from anotherFunction

使用场景

  • CommonJS最初是为服务器端JavaScript设计的,特别是Node.js环境。
  • Node.js的内置模块(如 fshttppath 等)都遵循CommonJS规范。
  • 许多早期的npm包都是使用CommonJS格式编写的。

再简单介绍一下ESM

ECMAScript模块(ESM)是JavaScript的官方模块系统,由ECMAScript(ES6)规范定义。ESM提供了一种在JavaScript文件之间导入和导出值(如变量、函数、类等)的标准方法。它旨在取代非标准的模块系统,如 CommonJS(CJS),并成为跨环境(浏览器和服务器端)的统一模块解决方案。

关键特征

  1. 静态结构:

    • ESM的导入和导出语句是静态的,正常情况下它们必须出现在模块的顶层作用域,并且不能被条件语句或函数包围。
  2. 导出和导入语法:

    • ESM使用export关键字来导出模块的公共接口,使用import关键字来导入其他模块的导出。
  3. 异步和按需加载:

    • ESM支持异步模块加载,允许按需动态导入模块。
  4. 模块解析:

    • ESM的模块解析遵循URL的规则,可以使用相对路径、绝对路径或URL
  5. 浏览器和Node.js支持:

    • ESM在现代浏览器中得到了原生支持,无需编译或打包。
    • Node.js从版本12开始提供了对ESM的实验性支持,并在后续版本中逐步完善。

基本语法

导出模块

命名导出:

// myModule.js
export function myFunction() {
  console.log('Hello from myFunction');
}

export const myVariable = 42;

默认导出(每个模块只能有一个默认导出):

// myModule.js
export default function() {
  console.log('Hello from default export');
}

导入模块

导入命名导出:

// main.js
import { myFunction, myVariable } from './myModule.js';

myFunction(); // 输出: Hello from myFunction
console.log(myVariable); // 输出: 42

导入默认导出:

// main.js
import myDefaultFunction from './myModule.js';

myDefaultFunction(); // 输出: Hello from default export

导入整个模块作为命名空间对象:

// main.js
import * as myModule from './myModule.js';

myModule.myFunction(); // 输出: Hello from myFunction
console.log(myModule.myVariable); // 输出: 42

动态导入(返回一个 Promise):

// main.js
import('./myModule.js').then((myModule) => {
  myModule.myFunction();
});

使用场景

  • ESM被设计用于在浏览器和服务器端(如Node.js)中作为统一的模块系统。
  • 它是开发现代JavaScript应用程序的推荐方式,特别是单页应用程序(SPA)和服务器端应用程序。
  • 许多现代前端框架和工具链都采用或支持ESM

终于来到全面ESM

全面采用ESM的好处

  1. 标准化:

    • ESMECMAScript(JavaScript 的官方规范)的原生模块系统,因此它是标准化的,与社区和未来的发展保持一致。
  2. 更好的模块化:

    • ESM提供了静态模块结构,这意味着可以在编译时分析模块依赖关系,而不是在运行时。
    • 这种静态结构提高了代码的组织性和可维护性。
  3. 树摇(Tree Shaking):

    • 由于ESM的静态结构,构建工具可以更容易地实现树摇,这是一种移除未使用代码的优化技术。
    • 这可以显著减小最终打包文件的体积,提高应用的加载速度和性能。
  4. 更好的性能:

    • 在现代浏览器中,ESM可以被异步加载,这意味着可以实现更有效的代码分割和按需加载。
    • Node.js中的ESM也可以提高性能,因为它允许更好的模块缓存。
  5. 原生浏览器支持:

    • ESM在所有现代浏览器中均得到支持,无需额外的编译步骤就可以直接使用。
    • 这简化了开发流程,并可以直接利用浏览器的模块缓存机制。
  6. 更好的跨环境兼容性:

    • ESM既可以在浏览器中使用,也可以在服务器端(如 Node.js)中使用,这有助于统一前后端的代码模块化策略。
  7. 更方便的代码重用:

    • 使用ESM,开发者可以更容易地编写可在不同环境中重用的模块,这促进了代码共享和开源库的开发。
  8. 顶级 await:

    • ESM支持在模块顶层直接使用await关键字,这使得编写依赖于异步操作的模块变得更加简单。
  9. 更好的工具集成:

    • 许多现代的JavaScript工具和框架都是围绕ESM设计的,这意味着全面采用ESM可以更好地利用这些工具的能力。
  10. 更清晰的依赖管理:

    • ESM强制显式导入和导出,这使得依赖关系更加清晰,也便于开发者理解和追踪模块之间的联系。
  11. 更容易的静态分析:

    • 由于ESM的静态结构,静态分析工具(如 linters 和 type checkers)可以更准确地分析代码,帮助发现潜在的错误和问题。
  12. 有助于TypeScript的解析和编译:

    • TypeScript遵循ES模块的解析算法,这意味着它可以直接利用ESM的静态导入和导出语句来解析模块依赖。采用纯 ESM有助于TypeScript编译器更准确地分析依赖关系,并确保模块解析的结果与运行时环境保持一致。ESM促进了明确的导出和导入语句,这使得TypeScript能够更精确地进行类型检查和推断。TypeScript可以利用ESM的导入导出语句来提供更准确的代码提示和自动化重构

全面采用ESM的要求(也就是如何判断一个项目是不是采用纯esm编写)

  1. 使用importexport语句:

    • 使用import语句来导入模块。
    • 使用export语句来导出模块。
    • 不会出现module.exportsexportsrequire语法。
  2. 文件扩展名:

    • Node.js中,确保你的JavaScript文件使用.mjs.mts扩展名或在package.json中指定"type": "module"以明确它们是 ES模块,不会出现.cjs.cts扩展名。比如 vue-pure-admin 就明确指出项目使用esm模块编写,这也对node.js以及别的工具提供更快模块解析的条件
    • 在浏览器中,通常使用.js扩展名,并确保服务器正确设置 MIME 类型为application/javascript
  3. 第三方模块:

    • package.json文件中的dependenciesdevDependencies依赖项支持ESM
  4. 构建和打包工具:

    • 使用支持ESM的工具链,比如rollupesbuildvite等,本项目使用的vite5也已经 废弃 CJS Node API

常见问题解答

问:全面esm,好家伙,搞得这么严,如果需要用cjs语法咋办?
答:随着vite5 废弃 CJS Node API 并将在 vite6 移除,平台也开始全面迁移代码到esm模块。如果想使用cjs的话,文件后缀名用.cjs.cts即可


总结肯定少不了啊

esm更快、更潮、更简单

esm易维护、高兼容、更nb😝

当然它这么牛,肯定要照顾一下它咯,所以平台新开了个 esmjs组织 专门产出纯esm库。当然如果你有想法,比如想开发或迁移一个库将其支持为纯esm语法,欢迎加入!

前端的更新迭代确实太快了,究其原因,还是它太灵活太弱语言了(不是黑它啊🥹),经常被一些后端语言欺负 😮,动不动就写个工具来替代,心累。所以后端语言还是很有必要学习的,推荐学习rust,后面也会慢慢成为趋势吧(至少在一些性能工具方面)。有时候不想写总结不想写文章就是因为前端不论从库还是工具来说更换太快,甚至不能用年来计算😓。来学rusthttps://github.com/esmjs/geo/issues/1#issuecomment-1831374680

后面感觉可以用 bun 代替nodejs了,它使用 zig 编写,可以大大减少项目启动时间和内存使用量。好嘛,又一个新语言是吧 🥹,听说它的学习成本不算高,先打个Mark

这样看来未来使用bun结合vite即将推出的rust版本的Rollup叫做Rolldown一起使用,在开发体验和构建速度上有很大的提升啊

@xiaoxian521 xiaoxian521 pinned this issue Dec 12, 2023
@pure-admin pure-admin locked and limited conversation to collaborators Dec 15, 2023
@xiaoxian521 xiaoxian521 unpinned this issue Mar 19, 2024
@xiaoxian521 xiaoxian521 pinned this issue Mar 23, 2024
@xiaoxian521 xiaoxian521 unpinned this issue May 13, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant