ESM
- https://caniuse.com/es6-module
- Chrome v61+
- https://caniuse.com/es6-module-dynamic-import
- Chrome v63+
- CSSStyleSheet
- 动态构建样式表
- 不支持 @import
caution
- react 没有 esm
import React from "https://esm.sh/[email protected]"
- electron 不支持 esm #21457
tip
- Node 支持 HTTPS impor
- --experimental-loader nodejs/node#36328
- NextJS 支持 - URL Imports
import map
- Chrome v89+
- https://github.com/wicg/import-maps
- https://modern-web.dev/docs/dev-server/plugins/import-maps/
- 目前只支持 web - 不支持 worker
- 可通过脚本动态构建 element
<script type="importmap">
{
"imports": {
"moment": "/node_modules/moment/src/moment.js",
"lodash": "/node_modules/lodash-es/lodash.js"
}
}
</script>
<!-- application/importmap+json -->
<script type="importmap" src="import-map.importmap"></script>
动态构建
<script>
if (HTMLScriptElement.supports && HTMLScriptElement.supports('importmap')) {
console.log('Your browser supports import maps.');
}
const importMap = {
imports: {
moment: '/moment.mjs',
lodash: someFeatureDetection() ? '/lodash.mjs' : '/lodash-legacy-browsers.mjs',
},
};
const im = document.createElement('script');
im.type = 'importmap';
im.textContent = JSON.stringify(importMap);
document.currentScript.after(im);
</script>
<script type="module">
import _ from 'lodash'; // will fetch the right URL for this browser
</script>
importmap 规范
{
"imports": {
// import moment from "moment";
"moment": "/node_modules/moment/src/moment.js",
// 自动映射
// import localeData from "moment/locale/zh-cn.js";
"moment/": "/node_modules/moment/src/",
"lodash": "/node_modules/lodash-es/lodash.js",
// import fp from "lodash/fp.js";
"lodash/": "/node_modules/lodash-es/",
// 无扩展名
// import fp from "lodash/fp"
"lodash/fp": "/node_modules/lodash-es/fp.js",
// remapping - 对 <script> 不生效
"https://www.unpkg.com/vue/dist/vue.runtime.esm.js": "/node_modules/vue/dist/vue.runtime.esm.js",
// 前缀 remmaping
"https://www.unpkg.com/vue/": "/node_modules/vue/",
"/app/helpers.mjs": "/app/helpers/index.mjs",
// 常见处理 hash 场景
"/js/app.mjs": "/js/app-8e0d62a03.mjs"
},
// 限定上下文,修改依赖
"scopes": {
"/scope2/": {
"a": "/a-2.mjs"
},
"/scope2/scope3/": {
"b": "/b-3.mjs"
}
}
}
CDN
cdn | import | github | cjs -> esm |
---|---|---|---|
Skypack | https://cdn.skypack.dev/ | ✅ | |
esm.sh | https://esm.sh/ | esm-dev/esm.sh | ✅ |
jsDelivr | https://cdn.jsdelivr.net/npm/ | ❌ | |
JSPM | https://ga.jspm.io/npm:[email protected]/ | jspm/project | ❌ |
unpkg | https://unpkg.com/${PKG}?module | mjackson/unpkg | ❌ |
- JSPM
- 支持 systemjs
- jsdelivr 提供 esm.run 别名
CDN Test
for i in $(seq 3); do
rm bench-*.txt
for i in $(seq 10); do
curl -o /dev/null -s -w "%{time_total}\n" https://cdn.skypack.dev/[email protected] >> bench-a.txt
curl -o /dev/null -s -w "%{time_total}\n" https://cdn.jsdelivr.net/npm/[email protected]/+esm >> bench-b.txt
curl -o /dev/null -s -w "%{time_total}\n" https://esm.sh/[email protected] >> bench-c.txt
done
for i in bench-*.txt; do
echo $i
awk '{ total += $1; count++ } END { print total/count }' $i
done
done
esm.sh
- 可以 Selfhosted
- 主服务器在 HK
- 基于 esbuild 构建
- cjs -> esm 使用 swc
- 支持 X-Typescript-Types 头 - demo 类型检测
?no-check
禁用
import React from 'https://esm.sh/[email protected]';
// 非模块文件
import 'https://esm.sh/tailwindcss/dist/tailwind.min.css';
// bundle 模式
import { Button } from 'https://esm.sh/antd?bundle';
// 开发模式
import React from 'https://esm.sh/react?dev';
// 依赖控制
import React from 'https://esm.sh/[email protected]';
import useSWR from 'https://esm.sh/[email protected]';
// 别名
import useSWR from 'https://esm.sh/swr?alias=react:preact/compat&[email protected]';
// 目标版本 - 默认基于 header 判断
import React from 'https://esm.sh/react?target=es2020';
// WebWorker
import editorWorker from '/monaco-editor/esm/vs/editor/editor.worker?worker';
const worker = new editorWorker();