回顾
先来个简单回顾:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>测试内联代码导入导出</title>
<script>
// 预编译带 text/babel 的script标签,处理 import
function preCompileInlineModuleScript() {
const srcMap = {}; // 存储所有内联模块的blob src地址
[...document.querySelectorAll('script[type="text/module"]')]
.map((script, idx) => {
// 如果未通过 data-module 指定模块名,生成类似 module0 的默认名称
const moduleName = script.dataset?.module || `module${idx}`;
const url = URL.createObjectURL(new Blob([script.textContent], { type: 'text/javascript' }));
srcMap[moduleName] = url;
return script;
})
.forEach(script => {
const { src, textContent, dataset } = script;
if (!textContent) {
return;
}
// 自动检索类似 `import Demo from 'demo'` 这样的代码
const newContent = textContent.replace(/(?<=(import|export) .+['"])(.+)(?=['"][;\n])/g, (m, _, src) => srcMap[src] || src);
const ele = document.createElement('script');
ele.textContent = newContent;
ele.type = 'module';
script.parentNode.insertBefore(ele, script); // 依次插在原script前面
script.remove();
});
}
document.addEventListener("DOMContentLoaded", preCompileInlineModuleScript);
</script>
</head>
<body>
测试内联代码导入导出
<script type="text/module" data-module="demo1">
// export default const a = 1; // 语法错误,为什么不能这么写?因为default可以看成是`const default`的语法糖
// 正确写法1
// const a = 1;
// export default a;
// 正确写法2
// export default 1;
// 导出函数
// export default function () {}
// 导出类,注意一个模块只能有一个默认导出
// export default class {}
export default {
a: 1,
b: 2,
}
</script>
<script type="text/module">
import demo, * as all from 'demo1';
// import { a } from 'demo1'; // 报错
console.log(demo); // 输出 {a: 1, b: 2}
console.log(all); // 输出 {default: {a: 1, b: 2}}
</script>
<script type="text/module" data-module="demo2">
export default 'hello';
export const a = 1;
// 语法错误,export 导出的大括号不是一个对象,只是一个特殊的语法
// export {
// b: 2,
// c: 3,
// };
// 正确写法
const b = 2;
const c = 3;
export { b, c };
</script>
<script type="text/module">
import demo, * as all from 'demo2';
import { a, b, c } from 'demo2';
console.log(demo); // 输出 'hello'
console.log(all); // 输出 {default: 'hello', a: 1, b: 2, c: 2}
console.log(a, b, c); // 输出 1 2 3
</script>
</body>
</html>
特殊地方
先有一个a.js:
// a.mjs
export default 'hello';
export const a = 1;
const b = 2;
const c = 3;
export { b, c };
然后有一个b.js,这个js什么都不做,只是做一层转发透传:
// b.mjs
export * from './a.mjs';
// c.mjs
import * as a from './a.mjs';
import * as b from './b.mjs';
console.log('a', a); // a正常:{ a: 1, b: 2, c: 3, default: 'hello' }
console.log('b', b);// b不正常,少了default { a: 1, b: 2, c: 3 }
这一点很奇怪,和我们预期不一致。实际上在导入的时候*
是包含default
的,可是导出的时候default
却被漏掉了。export * from 'a'
等价于export {a, b, c} from 'a'
,不等价于export {a, b, c, default} from 'a'
// b.mjs
export * from './a.mjs';
export {a, b, c} from './a.mjs'; // 二者完全等价
如果想要在b中实现完全透传,正确写法:
// b.mjs
export * from './a.mjs';
export { default } from './a.mjs'; // default必须显示导出
// c.mjs
import * as b from './b.mjs';
console.log(b); // { a: 1, b: 2, c: 3, default: 'hello' }
怎么理解这件事情呢?default
是一个特殊的关键字,是不能作为变量名的,但是可以作为对象的key值,我们在利用import * as xx
导入的时候由于xx是一个对象,所以可以正常导入,但是在导出的时候由于default太特殊所以被过滤掉了。
const default = 1; // 不合法,default不能做变量名
export { default };
import { default } from 'a'; // 同样不合法
// 但是将import和export结合的时候却可以
export { default } from 'a';
// 或者这样写
import a from 'a';
export default a;