项目插件使用举例(自定义 Next.js 插件:静态资源域名替换)
在自研的 Next.js 同构框架里,常常需要把构建后的 HTML 中的静态资源域名替换为目标环境对应的 CDN 域名。一个常见做法是:
在 compiler 的 before-emit / emit 阶段执行 tap 钩子,从 compilation 中获取到 html 内容,匹配到静态资源域名,使用网站域名将其进行替换。
来源整理自:vue3js.cn 面试官系列
简化示例
class ResourceReplacePlugin {
constructor(opts) {
this.opts = opts;
this.resEnvMap = { staging: "//cdn-staging.example.com/", prod: "//cdn.example.com/" };
this.envMap = { development: "staging", production: "prod" };
}
apply(nfes) {
nfes.server.beforeGenStream.tapAsync("track", (ctx, callback) => {
const resDomain = this.resEnvMap[this.envMap[this.opts.appConfig.Env]];
const _list = this.opts.i18n.getDomainList() || [];
if (_list.length === 0) return callback();
const exp = new RegExp(`//(${_list.join("|")})/`, "ig");
ctx.req.__Cover_HTML__ = ctx.html.replace(exp, resDomain);
callback();
});
nfes.server.afterGenStream.tap("track", (ctx) => {
const resDomain = this.resEnvMap[this.envMap[this.opts.appConfig.Env]];
const _list = this.opts.i18n.getDomainList() || [];
if (_list.length === 0) return;
const exp = new RegExp(`//(${_list.join("|")})/`, "ig");
ctx.req.__Cover_HTML_AFTER__ = ctx.html.replace(exp, resDomain);
});
}
}
module.exports = ResourceReplacePlugin;
补充
补充:在现代 Next.js 中如何用更标准的方式做"出域资源替换"。
Next.js(>= 13 App Router)本身没有暴露官方 plugin API(不像 webpack 有 compiler.hooks),但可以通过以下两种方式做类似的资源替换:
middleware.ts改写 HTML 输出:
// middleware.ts
import { NextResponse } from "next/server";
const CDN_HOSTS = ["static.dev.example.com", "img.dev.example.com"];
const TARGET = process.env.NEXT_PUBLIC_CDN_HOST!;
export function middleware(req: Request) {
const url = new URL(req.url);
if (CDN_HOSTS.some(h => url.hostname === h)) {
return NextResponse.redirect(new URL(url.pathname + url.search, `https://${TARGET}`));
}
return NextResponse.next();
}
- 通过自定义 Server(
server.js)拦截响应:
const next = require("next");
const { createServer } = require("node:http");
const app = next({ dev: false });
app.prepare().then(() => {
createServer((req, res) => {
const handle = app.getRequestHandler();
handle(req, res).then(() => {
// 如果需要更细粒度改写,可以再次 chunk 替换 res
});
}).listen(3000);
});
实用建议:
- 优先使用
middleware.ts+NextResponse.rewrite,不必引入自定义 server,能继续享受 edge / serverless 部署能力。 - 如果一定要在 HTML 生成阶段改写,建议封装成"构建脚本"在
next build之后运行,避免侵入框架内部。



