使用importmap实现在线代码编辑

更新于 阅读 14

在ES module中可以通过import来引入模块,通过相对或者绝对路径加载.

import lodash from "https://esm.sh/lodash";

可当使用webpack等打包工具时明知需要使用 import lodash from "lodash"的方式就能加载对应的文件,这些工具通过nodejs在构建时映射到特定的文件,就能解决模块自动加问题。

所以在浏览器中要只要解决了映射问题,就可以使用相同的方式引入模块。

importmap

<script>中指定type为importmap,再通过JSON对象指定所有模块的映射,

<script type="importmap"> { "imports": { "lodash": "https://esm.sh/lodash", } } </script> <script type="module"> import lodash from 'lodash'; console.log(lodash.sortBy([3, 2, 1])) </script>

映射的值必须是./..//或者绝对URL。同时imports中的包并不一定会加载,只有在其他文件中使用了的包才会加载,未被其他脚本使用的包不会加载。

scopes

imports指定的映射是全局的,当某些文件需要老版本的lodash时, 通过scopes来指定。这里指定/v1/路径下的文件使用3.0.0版本。

<script type="importmap"> { "imports": { "lodash": "https://esm.sh/lodash", }, "scopes": { "/v1/": { "lodash": "https://esm.sh/lodash@3.0.0", } } } </script>

importmap兼容性

目前主流的浏览器除了IE都支持,所以可以放心使用。

如何用importmap实现代码加载

通过上面的介绍,要实现一个简易的在线代码编辑,同时能引入各种资源包就比较容易了。

如下react代码,通过import引入reactreact-dom,由于没有提前加载,代码会直接报错

import React from "react"; import ReactDOM from "react-dom"; const App = () => { return <div>hello</div> } ReactDOM.render(<App />, document.getElementById("app"));

所以使用importmap提前导入即可。

<script type="importmap"> { "imports": { "react": "https://esm.sh/react", "react-dom": "https://esm.sh/react-dom" } } </script>

但是如果要使用其他的模块,比如lodash,那就需要提前手动导入,所以局限性很大。

在线编辑react,由于使用jsx语法,所以使用了@babel/standalone,在线转换jsx。由于babel会先将代码生成ast,所以可以编写一个plugin来收集代码中的import数据。 将自定义collectImportPlugin注册到babel上,编译时收集依赖。

import * as Babel from "@babel/standalone"; let scriptImports = []; export const collectImportPlugin = () => { return { visitor: { ImportDeclaration(path) { scriptImports.push(path.node.source.value); }, }, }; }; Babel.registerPlugin("collectImportPlugin", collectImportPlugin);

上面的插件收集的结果为["react", "react-dom"],如果同时依赖lodash,结果就是["react", "react-dom", "lodash"]

因为https://esm.sh上我们日常使用的包都能获取到,所以这里直接从https://esm.sh加载数据。拼接后就是上面的importmap代码:

<script type="importmap"> { "imports": { "react": "https://esm.sh/react", "react-dom": "https://esm.sh/react-dom", "lodash": "https://esm.sh/lodash" } } </script>

预览效果: