在没有 bundling/transpiling 第一方源的情况下从 npm 本地导入 ES 模块依赖项
Natively import ES module dependencies from npm without bundling/transpiling first-party source
背景
我正在尝试创建一个“buildless”JavaScript 应用程序,其中我不需要 watch
任务 运行 transpile JSX,重新每次保存任何源文件时都捆绑代码等。
仅使用第一方代码就可以正常工作,但是当我尝试 import
来自 npm 的依赖项时,我卡住了。
目标
我想实现这样的工作流程:
npm install foo
(假设它是一个 ES 模块,而不是 CommonJS)
- 编辑
source/index.js
并添加 import { bar } from 'foo'
npm run build
。某些东西(webpack、rollup、自定义脚本等)运行s,并将 foo
及其依赖项捆绑到 ./build/vendor.js
(没有来自 source/
的任何内容)。
- 编辑
index.html
以添加 <script src="build/vendor.js" type="module"...
- 我可以在我的浏览器中重新加载
source/index.js
,bar
将可用。我不必 运行 npm run build
直到下一次我 add/remove 依赖。
我已经让 webpack 将依赖项拆分到一个单独的文件中,但是要在无构建上下文中从该文件 import
,我必须 import { bar } from './build/vendor.js
。那时 webpack 将不再捆绑 bar
,因为它不是相对导入。
我也尝试过Snowpack,这在概念上更接近我想要的,但我仍然无法配置它来实现上述工作流程。
我可以编写一个简单的脚本来将文件从 node_modules
复制到 build/
,但我想使用捆绑软件来进行 tree shaking 等。很难找到不过,支持此工作流程的东西。
我知道如何使用 Import Maps 和 Snowpack 来做到这一点。
高级说明
我用了Import Maps to translate bare module specifiers like import { v4 } from 'uuid'
into a URL. They're currently just a drafted standard, but are supported in Chrome behind an experimental flag, and have a shim.
有了它,您可以在代码中使用裸 import
语句,以便打包器理解它们并可以正确工作,进行 tree-shaking 等。但是,当浏览器解析导入时,它''ll see it as import { v4 } from 'http://example.org/vendor/uuid.js'
, 并像普通 ES 模块一样下载它。
设置完成后,您可以使用任何捆绑器来安装软件包,但需要将其配置为构建单独的捆绑包,而不是将所有软件包组合成一个。 Snowpack does a really good job at this, because it's designed for an unbundled development workflow. It uses esbuild 底层,比 Webpack 快 10 倍,因为它避免了不必要地重新构建未更改的包。它仍然会做 tree-shaking 等
实施 - 最小示例
index.html
<!doctype html>
<!-- either use "defer" or load this polyfill after the scripts below-->
<script defer src="es-module-shims.js"></script>
<script type="importmap-shim">
{
"imports": {
"uuid": "https://example.org/build/uuid.js"
}
}
</script>
<script type="module-shim">
import { v4 } from "uuid";
console.log(v4);
</script>
snowpack.config.js
module.exports = {
packageOptions: {
source: 'remote',
},
};
packageOptions.source = remote
告诉 Snowpack 自己处理依赖关系,而不是期望 npm 来做。
运行 npx snowpack add {module slug - e.g., 'uuid'}
在snowpack.deps.json
文件中注册一个依赖,安装在build
文件夹中
package.json
"scripts": {
"build": "snowpack build"
}
每当 add/remove/update 依赖项时调用此脚本。不需要 watch
脚本。
实施 - 完整示例
查看 iandunn/no-build-tools-no-problems/f1bb3052
。以下是相关行的直接链接:
snowpack.config.js
snowpack.deps.json
package.json
core.php
输出垫片
plugin.php
- 输出导入图
passphrase-generator.js
- 导入模块。 (他们在这个例子中被注释掉了,出于这个答案范围之外的原因,只需取消注释它们,运行 bundle
脚本,他们就会工作)。
如果您愿意使用在线服务,Skypack CDN 似乎很适合。例如,我想使用 sample-player NPM 模块,并且我选择在我的项目中使用无捆绑工作流程,仅使用 ES6 模块,因为我的目标是嵌入式 Chromium 最新版本,因此无需担心遗留浏览器支持,所以我需要做的就是:
import SamplePlayer from "https://cdn.skypack.dev/sample-player@^0.5.5";
// init() once the page has finished loading.
window.onload = init;
function init() {
console.log('hello sampler', SamplePlayer)
}
在我的 html:
<script src="./src/sampler/sampler.js" type="module"></script>
当然你也可以在上面url查看CDN生成的JS文件,下载它指向的生成的一体化js文件,也可以离线使用如果需要的话。
背景
我正在尝试创建一个“buildless”JavaScript 应用程序,其中我不需要 watch
任务 运行 transpile JSX,重新每次保存任何源文件时都捆绑代码等。
仅使用第一方代码就可以正常工作,但是当我尝试 import
来自 npm 的依赖项时,我卡住了。
目标
我想实现这样的工作流程:
npm install foo
(假设它是一个 ES 模块,而不是 CommonJS)- 编辑
source/index.js
并添加import { bar } from 'foo'
npm run build
。某些东西(webpack、rollup、自定义脚本等)运行s,并将foo
及其依赖项捆绑到./build/vendor.js
(没有来自source/
的任何内容)。- 编辑
index.html
以添加<script src="build/vendor.js" type="module"...
- 我可以在我的浏览器中重新加载
source/index.js
,bar
将可用。我不必 运行npm run build
直到下一次我 add/remove 依赖。
我已经让 webpack 将依赖项拆分到一个单独的文件中,但是要在无构建上下文中从该文件 import
,我必须 import { bar } from './build/vendor.js
。那时 webpack 将不再捆绑 bar
,因为它不是相对导入。
我也尝试过Snowpack,这在概念上更接近我想要的,但我仍然无法配置它来实现上述工作流程。
我可以编写一个简单的脚本来将文件从 node_modules
复制到 build/
,但我想使用捆绑软件来进行 tree shaking 等。很难找到不过,支持此工作流程的东西。
我知道如何使用 Import Maps 和 Snowpack 来做到这一点。
高级说明
我用了Import Maps to translate bare module specifiers like import { v4 } from 'uuid'
into a URL. They're currently just a drafted standard, but are supported in Chrome behind an experimental flag, and have a shim.
有了它,您可以在代码中使用裸 import
语句,以便打包器理解它们并可以正确工作,进行 tree-shaking 等。但是,当浏览器解析导入时,它''ll see it as import { v4 } from 'http://example.org/vendor/uuid.js'
, 并像普通 ES 模块一样下载它。
设置完成后,您可以使用任何捆绑器来安装软件包,但需要将其配置为构建单独的捆绑包,而不是将所有软件包组合成一个。 Snowpack does a really good job at this, because it's designed for an unbundled development workflow. It uses esbuild 底层,比 Webpack 快 10 倍,因为它避免了不必要地重新构建未更改的包。它仍然会做 tree-shaking 等
实施 - 最小示例
index.html
<!doctype html>
<!-- either use "defer" or load this polyfill after the scripts below-->
<script defer src="es-module-shims.js"></script>
<script type="importmap-shim">
{
"imports": {
"uuid": "https://example.org/build/uuid.js"
}
}
</script>
<script type="module-shim">
import { v4 } from "uuid";
console.log(v4);
</script>
snowpack.config.js
module.exports = {
packageOptions: {
source: 'remote',
},
};
packageOptions.source = remote
告诉 Snowpack 自己处理依赖关系,而不是期望 npm 来做。
运行 npx snowpack add {module slug - e.g., 'uuid'}
在snowpack.deps.json
文件中注册一个依赖,安装在build
文件夹中
package.json
"scripts": {
"build": "snowpack build"
}
每当 add/remove/update 依赖项时调用此脚本。不需要 watch
脚本。
实施 - 完整示例
查看 iandunn/no-build-tools-no-problems/f1bb3052
。以下是相关行的直接链接:
snowpack.config.js
snowpack.deps.json
package.json
core.php
输出垫片plugin.php
- 输出导入图passphrase-generator.js
- 导入模块。 (他们在这个例子中被注释掉了,出于这个答案范围之外的原因,只需取消注释它们,运行bundle
脚本,他们就会工作)。
如果您愿意使用在线服务,Skypack CDN 似乎很适合。例如,我想使用 sample-player NPM 模块,并且我选择在我的项目中使用无捆绑工作流程,仅使用 ES6 模块,因为我的目标是嵌入式 Chromium 最新版本,因此无需担心遗留浏览器支持,所以我需要做的就是:
import SamplePlayer from "https://cdn.skypack.dev/sample-player@^0.5.5";
// init() once the page has finished loading.
window.onload = init;
function init() {
console.log('hello sampler', SamplePlayer)
}
在我的 html:
<script src="./src/sampler/sampler.js" type="module"></script>
当然你也可以在上面url查看CDN生成的JS文件,下载它指向的生成的一体化js文件,也可以离线使用如果需要的话。