JSPM一个前端神器,配合esbuild wasm可以在页面打包任何想要在浏览器端运行的依赖包
需求背景
有时候想打包一些库,直接能在浏览器端运行,如果用rollup这种工具打包,很多node端的依赖无法解决。比如像postcss严重依赖node path fs 等等模块,但你想在浏览器执行css解析想运行postcss。那么jspm出现了,根据在它网站添加你想要运行的库,会自动生成importmap.有了这个我们就可以在浏览器esm模式引用导入了.
接下来正是利用jspm的优势,直接用esbuild 利用fetch从jspm导入依赖。就直接能打包了。下面详细讲绍写的插件
如:
ESBuild Wasm
完成打包任务依赖几下:
- jspm生成的importmap
- esbuild-wasm
- es-module-shims 的importmap解析器
- esbuild 自定义esmPlugin
执行流程就是
- 配置esbuld config
- 添加esmPlugin插件 传入entryCode 和importmap(jspm生成的)
- 执行esbuild.build方法就ok.最后把contents 转换成blob 下载
esbuild wasm版本是可以在浏览器端运行的,我之前通过这个写了一个指向em.sh网站的前端无安装快速打包工具.
function EsmPlugin({ entryCode, importmap }) { return { name: 'esm-bundle', /** * @params {import('../../scripts/backstage/esbuild/lib/browser.d.ts').PluginBuild} build */ setup: (build) => { // 这个createImportMapResolver解析是来自:es-module-shims const resolver = esmResolver.createImportMapResolver(importmap) build.onResolve({ filter: /.*/ }, args => { const { path, importer, namespace, resolveDir, kind, pluginData } = args if (kind === 'entry-point') { return { namespace: 'local-module', pluginData: { content: entryCode }, path } } const realPath = resolver(path,importer) return { path: realPath, pluginData, namespace:'http-module' } }) // 加载远程 build.onLoad({ filter: /.*/, namespace: 'http-module' }, async (args) => { const { path, namespace, suffix, pluginData } = args const content=await fetch(path).then(res=>res.clone()).then(res=>res.text()).catch(()=>'// 错误') return { contents:content } }) // 加载 build.onLoad({ filter: /.*/, namespace: 'local-module' }, (args) => { const { path, namespace, suffix, pluginData } = args return { contents: pluginData.content } }) } } } function initializeBuild() { esbuild.build({ entryPoints: { main: 'index.js' }, target: 'es2015', format: 'iife', bundle: true, platform: "browser", define: { }, plugins: [EsmPlugin({ entryCode: ` export * from 'postcss' `, importmap: { "imports": { "postcss": "https://ga.jspm.io/npm:postcss@8.5.3/lib/dev.postcss.mjs" }, "scopes": { "https://ga.jspm.io/": { "buffer": "https://ga.jspm.io/npm:@jspm/core@2.1.0/nodelibs/browser/buffer.js", "fs": "https://ga.jspm.io/npm:@jspm/core@2.1.0/nodelibs/browser/fs.js", "nanoid/non-secure": "https://ga.jspm.io/npm:nanoid@3.3.11/non-secure/index.js", "path": "https://ga.jspm.io/npm:@jspm/core@2.1.0/nodelibs/browser/path.js", "picocolors": "https://ga.jspm.io/npm:picocolors@1.1.1/picocolors.browser.js", "process": "https://ga.jspm.io/npm:@jspm/core@2.1.0/nodelibs/browser/process.js", "source-map-js": "https://ga.jspm.io/npm:source-map-js@1.2.1/source-map.js", "url": "https://ga.jspm.io/npm:@jspm/core@2.1.0/nodelibs/browser/url.js" }, "https://ga.jspm.io/npm:postcss@8.5.3/lib/dev.no-work-result.js": { "nanoid/non-secure": "https://ga.jspm.io/npm:nanoid@3.3.11/non-secure/index.cjs" }, "https://ga.jspm.io/npm:postcss@8.5.3/lib/dev.parse.js": { "nanoid/non-secure": "https://ga.jspm.io/npm:nanoid@3.3.11/non-secure/index.cjs" }, "https://ga.jspm.io/npm:postcss@8.5.3/lib/dev.postcss.js": { "nanoid/non-secure": "https://ga.jspm.io/npm:nanoid@3.3.11/non-secure/index.cjs" }, "https://ga.jspm.io/npm:postcss@8.5.3/lib/dev.processor.js": { "nanoid/non-secure": "https://ga.jspm.io/npm:nanoid@3.3.11/non-secure/index.cjs" }, "https://ga.jspm.io/npm:postcss@8.5.3/lib/fromJSON.js": { "nanoid/non-secure": "https://ga.jspm.io/npm:nanoid@3.3.11/non-secure/index.cjs" }, "https://ga.jspm.io/npm:postcss@8.5.3/lib/input.js": { "nanoid/non-secure": "https://ga.jspm.io/npm:nanoid@3.3.11/non-secure/index.cjs" }, "https://ga.jspm.io/npm:postcss@8.5.3/lib/map-generator.js": { "nanoid/non-secure": "https://ga.jspm.io/npm:nanoid@3.3.11/non-secure/index.cjs" } } } })], external: [], globalName: "MyLib", outdir: "./dist" }).then((result) => { console.log('complete', result) const outputFiles=result.outputFiles if(outputFiles.length>0){ downLoadFile(outputFiles[0].path,new Blob([ outputFiles[0].contents ],{ type:'text/js' })) } //return result.outputFiles.length ? result.outputFiles[0].text : '' }) }
📦 JSPM 全面解析 — 下一代浏览器原生模块化解决方案
🎯 什么是 JSPM?
JSPM,全称 JavaScript Package Manager,最初是一个类似于 NPM + Webpack 的前端模块化打包工具。
随着 Web 标准的发展,它逐渐转型为基于 ESM(ECMAScript Modules) 和 importmap 的现代浏览器模块加载解决方案,让前端项目可以:
- 摆脱打包器
- 不依赖 node_modules
- 直接用 CDN 加载标准 ESM
- 支持多版本、按作用域映射依赖
📌 简单说:它让你像后端一样用原生模块,像 Deno 一样不用打包,像微前端一样可以自由控制版本。
🌐 JSPM 核心组成
组件 作用 jspm.dev 免费 ESM CDN,托管 npm 包的 ESM 版本 SystemJS 模块加载器,兼容 ESM、CJS、AMD(早期核心) importmap.dev 在线 importmap 配置和托管服务 JSPM Generator 动态生成 importmap 的工具库 📦 jspm.dev — 现代浏览器 ESM CDN
这是现在 JSPM 生态的核心:
- 每个 npm 包都托管为纯标准 ESM
- 支持 importmap,方便统一版本和路径映射
- 不挂载 global,不 polyfill Node API,100% 原生 ESM
示例:
import React from "https://jspm.dev/react@18" import _ from "https://jspm.dev/lodash@4.17.21"
特点:
- 📖 URL 唯一即版本唯一(支持 semver 范围)
- 🚀 无打包,直接上生产
- 🔒 CDN 静态缓存,长期稳定
📖 importmap — 依赖映射核心
JSPM 主打和浏览器 importmap 配合,解决模块裸导入的问题。
{ "imports": { "vue": "https://jspm.dev/vue@3", "lodash": "https://jspm.dev/lodash@4.17.21" }, "scopes": { "https://example.com/admin/": { "vue": "https://jspm.dev/vue@2.7" } } }
- imports 👉 全局依赖映射
- scopes 👉 某路径下专属依赖版本(多版本共存)
🛠️ JSPM Generator
一个 NPM 包 + 在线服务,可以:
- 自动生成 importmap
- 解析 npm 依赖树
- 输出版本锁定的 CDN 地址
👉 https://generator.jspm.io/
示例:
import { install } from '@jspm/generator' const generator = new install() await generator.install('vue') console.log(generator.importMap)
🎨 JSPM 能做什么?
场景 优势 无打包部署 静态 HTML + importmap,零构建上线 微前端多版本依赖 scopes 多版本隔离,互不干扰 线上 demo / 原型开发 直接 CDN 引入,热更快 Deno / Bun 跨平台项目 同样走 ESM CDN,统一 URL 化 向后兼容改造旧项目 不动 webpack,静态页面分阶段升级 📊 和其它 ESM CDN 对比
特性 jspm.dev esm.sh skypack.dev 构建方式 自研纯 ESM 构建 esbuild 转 ESM esbuild + polyfill importmap 支持 ✅ 强配套 ❌ ❌ CJS 兼容方式 转标准 ESM,不挂 global、不 polyfill shim process、Buffer 等 shim、polyfill 多 多版本隔离 ✅ scopes + importmap URL 隔离 URL 隔离 微前端友好度 ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐ 👉 最适合 无打包 + 微前端 + 纯 ESM 场景的,就是 jspm.dev。
📚 JSPM 和前端未来
JSPM 其实是浏览器原生模块化标准化的先锋实践者,它做的事情,正是:
- 用 URL 做依赖
- 用 importmap 映射版本
- 用 ESM 标准化所有库
- 不用 Node 环境
未来 Deno、Bun、浏览器本地项目都会往这方向靠近。
JSPM 正是Vite / Deno / Bun 模块化生态里的 CDN 版排头兵。
✅ 总结
JSPM 是… 意义 ESM CDN 纯浏览器端 CDN,托管 npm ESM 版本 importmap 配套方案 控制依赖版本、作用域、URL 映射 微前端好搭档 scopes 多版本共存 打包器替代 无需 node_modules 和 Webpack/Vite 未来标准推动者 向标准 ESM + URL 化过渡 如果你需要,我可以帮你把这份内容排成更漂亮的 markdown、Notion 样式或者直接写个博客开头、目录、结尾都写好。要不要我帮你整理一下?✨