简体   繁体   English

从 npm 本地导入 ES 模块依赖,无需捆绑/转译第一方源

[英]Natively import ES module dependencies from npm without bundling/transpiling first-party source

Background背景

I'm trying to create a " buildless " JavaScript app, one where I don't need a watch task running to transpile JSX, re-bundle code, etc every time I save any source file.我正在尝试创建一个“无构建”的JavaScript应用程序,每次我保存任何源文件时,我都不需要运行watch任务来转换 JSX、重新捆绑代码等。

It works fine with just first-party code, but I'm stuck when I try to import dependencies from npm.它仅适用于第一方代码,但当我尝试从 npm import依赖项时,我被卡住了。

Goal目标

I want to achieve this kind of workflow:我想实现这种工作流程:

  1. npm install foo (assume it's an ES module, not CommonJS) npm install foo (假设它是一个 ES 模块,而不是 CommonJS)
  2. Edit source/index.js and add import { bar } from 'foo'编辑source/index.js并添加import { bar } from 'foo'
  3. npm run build . npm run build Something (webpack, rollup, a custom script, whatever) runs, and bundles foo and its dependencies into ./build/vendor.js (without anything from source/ ).某些东西(webpack、汇总、自定义脚本等)运行,并将foo及其依赖项捆绑到./build/vendor.js (没有来自source/的任何内容)。
  4. Edit index.html to add <script src="build/vendor.js" type="module"...编辑index.html添加<script src="build/vendor.js" type="module"...
  5. I can reload source/index.js in my browser, and bar will be available.我可以在我的浏览器中重新加载source/index.js ,并且bar将可用。 I won't have to run npm run build until the next time I add/remove a dependency.在下次添加/删除依赖项之前,我不必运行npm run build

I've gotten webpack to split dependencies into a separate file, but to import from that file in a buildless context, I'd have to import { bar } from './build/vendor.js .我已经得到 webpack 将依赖项拆分到一个单独的文件中,但是要在无构建上下文中从该文件import ,我必须import { bar } from './build/vendor.js At that point webpack will no longer bundle bar , since it's not a relative import.那时 webpack 将不再捆绑bar ,因为它不是相对导入。

I've also tried Snowpack , which is closer to what I want conceptually, but I still couldn't configure it to achieve the above workflow.我也尝试过Snowpack ,这在概念上更接近我想要的,但我仍然无法配置它来实现上述工作流程。

I could just write a simple script to copy files from node_modules to build/ , but I'd like to use a bundled in order to get tree shaking, etc. It's hard to find something that supports this workflow, though.我可以只写一个简单的脚本来将文件从node_modules复制到build/ ,但我想使用 bundled 来进行 tree shaking 等。但是很难找到支持这个工作流程的东西。

I figured out how to do this, using Import Maps and Snowpack.我想出了如何做到这一点,使用 Import Maps 和 Snowpack。

High-Level Explanation高层解释

I used Import Maps to translate bare module specifiers like import { v4 } from 'uuid' into a URL.我使用Import Maps将诸如import { v4 } from 'uuid'类的裸模块说明符转换为 URL。 They're currently just a drafted standard , but are supported in Chrome behind an experimental flag, and have a shim .它们目前只是一个起草的标准,但在 Chrome 中支持在实验标志后面,并且有一个 shim

With that, you can use bare import statements in your code, so that a bundler understands them and can work correctly, do tree-shaking, etc. When the browser parses the import, though, it'll see it as import { v4 } from 'http://example.org/vendor/uuid.js' , and download it like a normal ES module.有了它,您可以在代码中使用裸import语句,以便捆绑器理解它们并可以正常工作,进行摇树等操作。但是,当浏览器解析导入时,它会将其视为import { v4 } from 'http://example.org/vendor/uuid.js' ,然后像普通的 ES 模块一样下载它。

Once those are setup, you can use any bundler to install the packages, but it needs to be configured to build individual bundles, instead of combining all packages into one.设置好这些之后,您可以使用任何捆绑器来安装包,但需要对其进行配置以构建单独的包,而不是将所有包合并为一个。 Snowpack does a really good job at this, because it's designed for an unbundled development workflow. Snowpack在这方面做得非常好,因为它是为非捆绑式开发工作流设计的。 It uses esbuild under the hood, which is 10x faster than Webpack, because it avoids unnecessarily re-building packages that haven't changed.它在后台使用esbuild ,比 Webpack 快 10 倍,因为它避免了不必要地重新构建未更改的包。 It still does tree-shaking, etc.它仍然会摇树等。

Implementation - Minimal Example实施 - 最小示例

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 tells Snowpack to handle dependencies itself, rather than expecting npm to do it. packageOptions.source = remote告诉 Snowpack 自己处理依赖项,而不是期望 npm 来做。 Run npx snowpack add {module slug - eg, 'uuid'} to register a dependency in the snowpack.deps.json file, and install it in the build folder.运行npx snowpack add {module slug - eg, 'uuid'}snowpack.deps.json文件中注册依赖项,并将其安装在build文件夹中。

package.json

"scripts": {
    "build":  "snowpack build"
}

Call this script whenever you add/remove/update dependencies.每当您添加/删除/更新依赖项时调用此脚本。 There's no need for a watch script.不需要watch脚本。

Implementation - Full Example实施 - 完整示例

Check out iandunn/no-build-tools-no-problems/f1bb3052 .查看iandunn/no-build-tools-no-problems/f1bb3052 Here's direct links to the the relevant lines:以下是相关行的直接链接:

If you are willing to use an online service, the Skypack CDN seems to work nicely for this.如果您愿意使用在线服务,Skypack CDN 似乎很适合。 For instance I wanted to use the sample-player NPM module and I've chosen to use a bundle-less workflow for my project using only ES6 modules as I'm targeting embedded Chromium latest version so don't need to worry about legacy browser support, so all I needed to do was:例如,我想使用 sample-player NPM 模块,并且我选择在我的项目中使用 bundle-less 工作流程,仅使用 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)
}

and in my html:在我的 html 中:

  <script src="./src/sampler/sampler.js" type="module"></script>

And of course you could just look inside the JS file the CDN generates at the above url and download the generated all-in-one js file it points to, in order to use it offline as well if needed.当然你也可以在上面的url查看CDN生成的JS文件,下载它指向的生成的一体化js文件,需要时也可以离线使用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM