最近社区出现了一个 Pure ESM package 的讨论,作为没有经历过前端模块化方案百花齐放年代的前端,对 CJS 这种 “方言” 解决方案不爽很久了。因此我毫不犹豫的将自己的 npm 包模块方案切换成了 ESM。
在迁移的过程中遇到过很多问题。接下来,我把自己遇到的一些问题,记录下来。
注意时间:本文写于 2022.02.25。
Node 部分
Node 版本
node 版本必须要大于 12
修改 package.json
- package.json 中有一个名为
type的字段,其默认值是commonjs,我们需要将其修改为module。 - 替换
"main": "index.js"为"exports": "./index.js"{"name": "test","version": "1.0.0","main": "./index.js","description": "test","type": "module","keywords": [],"author": "meakle","license": "MIT"}
动态引入
如果使用 CommonJS 作为模块方案,那么你可以在代码中这样进行引入
const func = function() {// 动态的引入文件const obj = require('.....')console.log(obj)}func()
如果我们使用 ESM 规范的语法引入,将会报错
const func = function() {// const obj = require('....')import { obj } from '....'// An import declaration can only be used at the top level of a module.console.log(obj)}func()

此时必须要动态的引入模块:
const func = async function() {const { obj } = await import('...')console.log(obj)}func()
__filename 与 __dirname 失效
如下图所示,无法在 ESM 中使用 __fileename 或者 __dirname
我们可以通过 [import.meta.url](https://nodejs.org/api/esm.html#importmetaurl) 获取到 URL 对象,再通过 [fileURLToPath](https://nodejs.org/api/url.html#urlfileurltopathurl) 方法将 URL 转换为路径。
import { fileURLToPath } from 'url'import { dirname } from 'path'const __filename = fileURLToPath(import.meta.url)const __dirname = dirname(__filename)
平常使用的时候,可以提前封装成一个函数。
路径需要写全
使用 require 引入路径时,路径可以简写,而使用 import 引入时,必须写全
import x from '.'; // 不行import x from './index.js'; // 行
更多
关于 CJS 和 ESM 中的区别可以查阅官方文档:
👉 https://nodejs.org/api/esm.html#differences-between-es-modules-and-commonjs
Typescript 部分
TS 版本
截止目前,若需要 TS 支持 ESM,必须使用 TS 的 nightly build 版本。
关于 nightly build 参考:https://www.typescriptlang.org/docs/handbook/nightly-builds.html
tsconfig 设置
设置 module 和 moduleResolution 字段
{"compilerOptions": {"module": "nodenext","moduleResolution": "nodenext"}}
路径要写全
需要写成 import "./foo.js" 而不能写成 import "./foo" 。
注意:即使实际引入的文件是 ts 文件,但在 import 语句中引入的文件后缀依然是
.js而不是.ts
某些全局变量无法使用
某些类似全局的值,如 require() 和 process 不能直接使用
我试了下, process 还是能用的,这里是 ts 文档里面提到的
更多
更多信息可以查阅 TS 文档: https://www.typescriptlang.org/docs/handbook/esm-node.html
最后
对于 ESM,我个人认为是大势所趋,虽然就目前来看依然不够成熟,还需要手动的配置才能使用,但是我相信不久的将来可以省去这些配置的功夫,前端的模块化方案也能趋于统一。
