webpack buildHttp 实验特性:构建期拉取远程 ESM 依赖并用 lockfile 锁定内容
本文以仓库中 examples/build-http 官方示例为主体,完整讲解 webpack 的 experiments.buildHttp 实验特性:如何在编译时直接从 https 地址(如 ESM CDN)导入模块、如何通过白名单策略 allowedUris 限制可信来源、以及 webpack.lock 锁文件如何以 sha512 integrity 值锁定远程资源内容。读完后你可以理解从示例配置到 HttpUriPlugin 底层实现(integrity 校验、lockfile 缓存、frozen/upgrade 语义)的完整调用链,并掌握该特性在不同 mode 下的构建产物差异。
一、特性定位:把"远程 URL 模块"变成构建期确定的依赖
webpack 早已支持非文件 URL(如 data: URI 由 DataUriPlugin 处理),而 buildHttp 实验特性将这一能力扩展到 http(s) 资源:当模块请求是一个 https 地址且命中白名单策略时,webpack 会在构建期发起真实网络请求拉取远端模块内容,将其作为普通模块参与解析、代码生成、压缩与优化,而不是在运行时才去加载。
这一点从示例的构建输出中可以直观验证:unoptimized 模式下统计信息显示 modules by path https:// 30 KiB,各 CDN 模块(https://jspm.dev/ 16.1 KiB 12 modules、https://cdn.esm.sh/、https://cdn.skypack.dev/ 等)全部被展开并列出 exports 信息,最终产物只有一个 82.3 KiB 的 output.js;production 模式下这些远端模块与本地入口一起被 scope hoisting 合并进 ./example.js + 25 modules 并压缩到 12.4 KiB。远程代码在构建期被完整"内化"进了 bundle。
源码层面的入口在 lib/WebpackOptionsApply.js:
if (options.experiments.buildHttp) {
const HttpUriPlugin = require("./schemes/HttpUriPlugin");
const httpOptions = options.experiments.buildHttp;
new HttpUriPlugin(httpOptions).apply(compiler);
}
即:只要开启 experiments.buildHttp,webpack 就会向 compiler 应用 lib/schemes/HttpUriPlugin.js。
二、官方示例:从四个 ESM CDN 同时导入同一个库
示例的入口文件 examples/build-http/example.js 演示了最典型的用法——直接 import 一个 https 地址:
import pMap1 from "https://cdn.skypack.dev/p-map";
import pMap2 from "https://cdn.esm.sh/p-map";
import pMap3 from "https://jspm.dev/p-map";
import pMap4 from "https://unpkg.com/p-map-series?module"; // unpkg doesn't support p-map :(
console.log(pMap1);
console.log(pMap2);
console.log(pMap3);
console.log(pMap4);
对应配置 examples/build-http/webpack.config.js:
"use strict";
/** @type {import("webpack").Configuration} */
const config = {
// enable debug logging to see network requests!
// stats: {
// loggingDebug: /HttpUriPlugin/
// },
experiments: {
buildHttp: [
"https://cdn.esm.sh/",
"https://cdn.skypack.dev/",
"https://jspm.dev/",
/^https:\/\/unpkg\.com\/.+\?module$/
]
}
};
module.exports = config;
两个值得注意的细节:
buildHttp直接写数组即可。在 lib/config/normalization.js 中,规范化逻辑会把数组形式包装成对象:Array.isArray(options) ? { allowedUris: options } : options。也就是说上面这份简写等价于buildHttp: { allowedUris: [...] },数组内容即白名单策略allowedUris。allowedUris的每一项既可以是 URI 前缀字符串,也可以是 RegExp:示例中三条 CDN 域名用前缀匹配,而 unpkg 因为需要?module查询参数才返回 ESM,所以用了正则/^https:\/\/unpkg\.com\/.+\?module$/精确约束。请求的 URL 不匹配任何一项时,HttpUriPlugin会直接报doesn't match the allowedUris policy错误(见 lib/schemes/HttpUriPlugin.js 中的 policy 校验与重定向后再校验逻辑)。配置注释还提示了调试手段:用stats.loggingDebug: /HttpUriPlugin/打开 debug 日志即可观察实际发出的网络请求。
完整配置项参考(来自 JSON Schema)
buildHttp 的完整对象形式定义在 schemas/WebpackOptions.json 的 HttpUriOptions 中:
| 配置项 | 类型 | 说明 |
|---|---|---|
allowedUris |
字符串/正则数组(必填) | 允许构建的 URI 列表,支持 URI 前缀字符串或正则 |
lockfileLocation |
绝对路径字符串 | 锁文件位置,默认为 webpack.lock |
cacheLocation |
绝对路径字符串或 false |
锁文件条目的资源内容缓存位置(示例中为 webpack.lock.data/);传 false 可禁用内容存储 |
frozen |
布尔 | 置为 true 后,任何会导致锁文件或资源内容变更的操作都会直接报错 |
upgrade |
布尔 | 置为 true 后,会重新拉取既有锁文件条目的资源,内容变化时升级锁条目 |
proxy |
字符串 | 指定 HTTP 请求使用的代理服务器 |
其中默认值由 lib/config/defaults.js 给出:
if (typeof experiments.buildHttp === "object") {
D(experiments.buildHttp, "frozen", production);
D(experiments.buildHttp, "upgrade", false);
}
frozen 的默认值绑定 production——production 模式下默认冻结锁文件,development 模式默认允许更新;upgrade 默认关闭。这一默认策略解释了示例中两种构建行为(下一节)。
三、构建产物对比:unoptimized vs production
examples/build-http 目录下的 README 由 template.md 模板生成,_{{stdout}}_ 与 _{{production:stdout}}_ 占位符分别注入两次构建的统计输出。两段输出的差异恰好说明了远程模块"内化"后享受的全部常规优化:
Unoptimized(development 默认产物)
asset output.js 82.3 KiB [emitted] (name: main)
runtime modules 614 bytes 3 modules
modules by path https:// 30 KiB
modules by path https://jspm.dev/ 16.1 KiB 12 modules
modules by path https://cdn.esm.sh/ 6.15 KiB
https://cdn.esm.sh/p-map 173 bytes [built] [code generated]
[exports: default, pMapSkip]
[used exports unknown]
harmony side effect evaluation https://cdn.esm.sh/p-map ./example.js 2:0-45
harmony import specifier https://cdn.esm.sh/p-map ./example.js 6:12-17
https://cdn.esm.sh/v53/p-map@5.1.0/es2015/p-map.js 1.18 KiB [built] [code generated]
...
https://unpkg.com/p-map-series?module 263 bytes [built] [code generated]
[exports: default]
./example.js 314 bytes [built] [code generated]
entry ./example.js main
可以看到:
- 每个远端模块都是独立 Module,带有自己的 exports(
[exports: default, pMapSkip])与依赖关系记录(harmony side effect evaluation、harmony import specifier等依赖条目); - CDN 返回的"重定向/别名 URL"也会被展开成独立模块:例如
https://cdn.esm.sh/p-map只是个入口,真正的实现是https://cdn.esm.sh/v53/p-map@5.1.0/es2015/p-map.js,jspm.dev 的 12 个模块甚至包含npm:@jspm/core@2.0.0-beta.11/nodelibs/os、nodelibs/process这类 polyfill 子模块。这印证了构建期递归解析:远端模块内部再 import 其他 URL 时,同样按allowedUris策略继续拉取; - 三个 CDN 对同一
p-map的依赖树各自独立(重复的aggregate-error、clean-stack等以不同 URL 各存一份)。
Production 模式
asset output.js 12.4 KiB [emitted] [minimized] (name: main)
orphan modules 30 KiB [orphan] 26 modules
./example.js + 25 modules 30.2 KiB [built] [code generated]
[no exports]
[no exports used]
entry ./example.js main
example.js + 25 modules 表示远端模块与入口模块被 scope hoisting 合并成一个模块组,再经过压缩,82.3 KiB 降到 12.4 KiB。也就是说,远程 ESM 依赖与普通本地模块享有完全相同的优化待遇,代价是构建期必须联网且受 allowedUris 白名单约束。
四、webpack.lock 锁文件:integrity 校验与确定性构建
buildHttp 的另一个核心设计是锁文件。示例目录中随附的 examples/build-http/webpack.lock 展示了真实结构:
{
"https://cdn.esm.sh/p-map": {
"integrity": "sha512-TfztRxlC5elIRa7x3oz4bfhtxJr5hIhoa+bliQkroNj8haEMPp1mv/eAsfzBt032G1oK6JT6y3135FP0vRh13Q==",
"contentType": "application/javascript; charset=utf-8"
},
"https://cdn.esm.sh/v53/p-map@5.1.0/es2015/p-map.js": { "integrity": "sha512-...", "contentType": "application/javascript" },
...
"https://unpkg.com/p-map-series?module": {
"resolved": "https://unpkg.com/p-map-series@3.0.0/index.js?module",
"integrity": "sha512-...",
"contentType": "application/javascript; charset=utf-8"
},
"version": 1
}
结合 lib/schemes/HttpUriPlugin.js 的实现,可以确认以下机制:
- integrity 计算:
computeIntegrity对拉取内容做sha512-<base64>摘要,verifyIntegrity用于比对(特殊值"ignore"可跳过校验)。每条锁条目记录integrity与contentType;发生重定向时还会记录resolved最终地址(如 unpkg 的p-map-series?module被解析为具体的index.js?module)。 - 锁文件旁路缓存:示例中的
webpack.lock.data/目录存放锁条目的资源内容快照。从源码的错误提示可确认该缓存受 integrity 保护——"The lockfile cache is protected by integrity checks, so any external modification will lead to a corrupted lockfile cache",默认排除规则为**/*webpack.lock.data/**(即默认不作为普通模块参与构建)。若不需要离线复现,可用cacheLocation: false禁用内容存储。 - frozen 语义:frozen 模式下,"URL 无锁条目"、"锁条目内容已变化"、"内容缺失"等任何会改动锁文件的场景都会转为错误(源码中有对应的
has no lockfile entry and lockfile is frozen、has an outdated lockfile entry, but lockfile is frozen等错误分支)。由于 production 模式默认frozen: true,CI 构建中第二次 production 构建若 CDN 内容发生变化将直接失败,从而保证产物可复现。 - upgrade 语义:默认关闭;显式开启后会重新抓取既有锁条目的资源,内容变化时升级锁条目,用于受控地跟进远端更新。
- 重定向也受策略约束:重定向目标若不匹配
allowedUris,会报doesn't match the allowedUris policy after redirect错误,防止白名单被重定向绕过。
五、实践要点与边界
- 适用前提:该特性标记在
experiments下,属于实验性 API;构建期需要网络访问(可通过proxy走代理),且远程内容必须能解析为 webpack 支持的模块形态(示例均为 ESM)。 - 安全边界:
allowedUris是硬性白名单,建议按"域名前缀 + 必要时正则"收敛可信 CDN 范围;不匹配的 URL 会立即失败而非放行。 - 确定性保障:本地/开发场景下默认可更新锁文件;交付与 CI 场景依赖 production 默认的 frozen 行为,配合 integrity 校验锁定远端内容,避免 CDN 内容变化导致线上 bundle 漂移。
- 调试手段:配置中注释掉的
stats: { loggingDebug: /HttpUriPlugin/ }是观察每次网络请求、锁文件读写路径的官方调试入口。 - 该示例的 README 输出本身也是"活"的:由 examples/build-http/template.md 在 examples 构建流程中注入真实
stdout生成,统计中的模块 URL、大小与版本(如p-map@5.1.0)均对应当次实际拉取的远端内容。
总结:experiments.buildHttp 用"白名单 + 构建期拉取 + sha512 锁文件"三件套,把 ESM CDN 这类运行时远程依赖转成了构建期完全可控的普通模块——既有 scope hoisting、压缩等全部本地模块优化,又通过 webpack.lock 保留了 supply chain 层面的内容锁定能力。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00