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.devesm.shskypack.dev
构建方式自研纯 ESM 构建esbuild 转 ESMesbuild + polyfill
importmap 支持✅ 强配套
CJS 兼容方式转标准 ESM,不挂 global、不 polyfillshim process、Buffer 等shim、polyfill 多
多版本隔离✅ scopes + importmapURL 隔离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 样式或者直接写个博客开头、目录、结尾都写好。要不要我帮你整理一下?✨

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值