前言

一直以来其实都对模块管理十分模糊,今天打算看下项目中rollup相关的一些内容,顺便决定理清一下esm(es module)和cjs(common js)的相关差异。

首先知道esm和cjs是两个东西,并且他们之间本质上是不支持相互调用的,有的时候我们在项目中能够随便搞都是因为像是webpack,babel或者rollup这种打包工具在背后做了一些转换才能够实现的。

CommonJs

其实就是我们在node中用到的模块啦,
被导入的文件如下:

// utilCommonjs.js
const a = '我从util.js中来的'
const foo = () => {return 1}
exports.c = '我从util.js中来的222' //不会被export出去
module.exports = {
  a
  foo
}
// module.exports = exports 有些地方会看到这个
  • 这里的exports和module.exports理解成:最终导出的其实是module.exports,然而exports这个东西只是作为一个reference方便操作最终导出对象module.exports。
  • 有些地方会在模块导出的最后加入module.exports = exports避免通过引用倒入的内容被遗忘。。反正个人觉得不用exports,只对module.exports操作就好了。

主文件如下

// main.js
var all = require("./utilCommonjs.js")
console.log('最终导出的结果为:')
console.log(all)
// 最终输出的结果为: { a: '我从commonjs1中来的', foo: [Function: foo] }

这里的all就是引入的module.exports这个Object啦。

ES Module

util.js文件如下

 //utilEs.js
export const a = 1;
export const test = () => {
    console.log('test?');
};

var de = 'default';
export default de;
//main.js
import { a, test } from './utilEs.js'
test() // 输出test
console.log(a) //输出1
// 按需导入模块

import * as esmAll from './utilEs.js'
esmAll.test()  // 输出test
// 导入全部模块, 包括default中的内容


import de from './utilEs.js'
console.log(de) //输出deault
// 导入默认模块,es模块需要一个默认导出

在rollup中使用es模块

rollup本身只支持es模块,如果你在你的rollup项目中使用esm的形式,最终生成的bundle.js文件中会将所有模块的内容打包到这一个文件中,比方说上面ES Module小结的main.js生成的bundle.js之后的样子

var a = 1;

var test = function test() {
  console.log('test?');
};

var de = 'default';
test();
console.log(a);
test();
console.log(de); //输出deault

在rollup中使用cjs模块

但是如果你在rollup中使用了commonjs会咋样呢?
比方说我们把上面的main.js改成这样

import { a, test } from './utilEs.js'
test()

import * as esmAll from './utilEs.js'
esmAll.test()

import de from './utilEs.js'
console.log(de)

const commonjs = require('./utilCommonjs.js')
// import * as commonjs from './utilCommonjs.js'
console.log(commonjs.foo())

那么最终生成的bundlejs如下

'use strict';

var test = function test() {
  console.log('test?');
};

var de = 'default';
test();
test();
console.log(de);

var commonjs = require('../commonjs/commonjs1.js'); // import * as commonjs from '../commonjs/commonjs1.js'

console.log(commonjs.foo());

咿? 怎么生成的bundle.js中居然还有个require? 引用外部的文件? 难道bundle.js不是应该没有依赖其他的嘛。。问题出现在哪里。。
后来经过google才得知,rollup本身不支持cjs形式的导入,但是支持cjs形式的导出
这时我们就需要加入两个plugins了,在rollup的配置文件中加入@rollup/plugin-commonjs@rollup/plugin-node-resolve之后,再将我们的main.js中的const commonjs = require('./utilCommonjs.js')改成import * as commonjs from '../commonjs/utilCommonjs.js',但是我们在utilCommonjs文件中的导出方式不变,还是按照cjs的方式导出,得到的bundle.js如下:

'use strict';

var test = function test() {
  console.log('test?');
};

var de = 'default'; // exports = 'dashazi'

var a = '我从commonjs1中来的';

var foo = function foo() {
  return 1;
};

var a_1 = '我从util.js中来的222'; // exports最为

var commonjs1 = {
  a: a,
  foo: foo
};
commonjs1.a = a_1;
test();
test();
console.log(de);
console.log(commonjs1.foo());

Finally,生成的bundle.js可以直接用啦。(那两个插件的目的就是为了让用户能够使用node_module中的一些cjs而设计的,但是引入的方式一定要用esm)此外如果你加入babel之后会做些兼容,输出的bundlejs会有些不同。

References: https://juejin.cn/post/6844904126195695624