最近社区出现了一个 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,我个人认为是大势所趋,虽然就目前来看依然不够成熟,还需要手动的配置才能使用,但是我相信不久的将来可以省去这些配置的功夫,前端的模块化方案也能趋于统一。