ESModule 和 commonjs module 混用

在一些项目中,使用 webpack 打包,难免会有 ESModule 和 commonjs 混用的情况,当然,全部用 ESModule 是最好的,符合标准。但是,在不得已或偷懒情况下必须混用时,要注意混用时的一些模块逻辑。

babel 会将 ESModule 导入进行处理,也就是说 import 的目标文件是 module.exports 导出的 commonjs 文件是可以的。但是前提是,必须先用 babel 进行编译。我们用 webpack babel-loader 的时候,为了获得 tree shaking 的效果,所以会关闭 modules 选项,这就导致在 webpack 进行打包时,babel 不会编译 ESModule,也就不会优化 import 逻辑,因此,这种情况下 import 的目标文件是 module.exports 时,运行时会报错,因为 webpack 认为 module 是不可写对象,你不能重写 module.exports,但你可以重写 exports。

这个逻辑是对的,webpack 按照标准进行了处理,并且是严格处理,这就要求模块输出者,必须输出 exports.xxx 这样的接口,从而可以通过 webpack 实现与 ESModule 完全对接。但是,很遗憾,由于历史原因,我们不可能把我们项目中全部的 module.exports 全部重新写过,而且对于 module.exports = function 的情况根本无解。

好消息是,我们也是有办法的,ESModule 和 commonjs 是可以混用的。关键就在 require 和 import 的区别。在 webpack 里面,目标文件究竟遵循 ESModule 还是 commonjs,完全由导入语句 require/import 来决定。例如,你的 a.js 是 ESModule 导出模块,但是你在外面用 require 导入,那么它可以很自然的被使用,可以说无缝对接。但是,如果你的 a.js 是 commonjs 导出模块,而外面是 import 进行导入,那就必须格外小心,module.exports 的导出方式不可以,但是 exports.xxx 的方式可以使用,当然,使用 export 导出是最优选择。

最终的结论是,不要用 import 去导入原来的 module.exports 的模块,而是要用 require,这在 webpack 中,你可以把这种操作当作日常操作,或者必须使用 require 的唯一特殊情况。