Nuxt Bridge 非 SSR 下编译异常及解决建议
Nuxt Bridge 在 ssr:false 或 target:static 下无法编译调试的故障,初步测试从 3.0.0-27437402.494f85a 到 3.0 RC1 都有问题。 我的开发环境是:
- windows 10 / 11
- nodejs v16.14.2
- Nuxt CLI v3.0.0-rc.1-27517945.7e912e7
- Webpack
问题 1: nuxt.config.js 中 ssr:false 时,出现无法找到 /.nuxt/dist/server/server.mjs 文件错误
ERROR Rollup error: Could not load file:///D:/***/.nuxt/dist/server/server.mjs (imported by node_modules/@nuxt/bridge/dist/runtime/nitro/renderer.mjs): ENOENT: no such file or directory, open 'D:\***\.nuxt\dist\server\server.mjs'
分析 node_modules/@nuxt/bridge/dist/runtime/nitro/renderer.mjs 发现,位于 11 行
const getSSRApp = !process.env.NUXT_NO_SSR && cachedImport(() => import("#build/dist/server/server.mjs"));
非 ssr 状态下不引用 #build/dist/server/server.mjs
按 nuxt 的思路,非 ssr 模式下,不会在 .nuxt/dist/server 下生成服务端相关文件,但是系统编译时不管是不是 ssr 模式,Webpack 都会尝试 import 这个文件,一旦没发现这个文件就报错。
所以目前想到的办法就是,不管是不是 ssr 都在 server 下生成 server.mjs
找到 node_modules/@nuxt/bridge/dist/chunks/module.mjs 约 209-306 行
if (name === "server") {
const jsServerEntry = resolve(nuxt.options.buildDir, "dist/server/server.js");
await promises.writeFile(jsServerEntry.replace(/.js$/, ".cjs"), 'module.exports = require("./server.js")', "utf8");
await promises.writeFile(jsServerEntry.replace(/.js$/, ".mjs"), 'export { default } from "./server.cjs"', "utf8");
} else if (name === "client") {
const manifest = await promises.readFile(resolve(nuxt.options.buildDir, "dist/server/client.manifest.json"), "utf8");
await promises.writeFile(resolve(nuxt.options.buildDir, "dist/server/client.manifest.mjs"), "export default " + manifest, "utf8");
}
大致意思是服务端调用时生成服务端文件,可以端调用是生成客户端文件,调试发现,当 ssr:true 时会分别走一次 server 和 client,但是 ssr:false 时,server 就不会调用,这样 .nuxt/dist/server 下没有 server.mjs 自然就出错。
解决办法: 临时解决办法就是 client 调用检测到为 非 ssr 模式时,同时生成 server.mjs 空文件,即:
if (name === "server") {
const jsServerEntry = resolve(nuxt.options.buildDir, "dist/server/server.js");
await promises.writeFile(jsServerEntry.replace(/.js$/, ".cjs"), 'module.exports = require("./server.js")', "utf8");
await promises.writeFile(jsServerEntry.replace(/.js$/, ".mjs"), 'export { default } from "./server.cjs"', "utf8");
} else if (name === "client") {
if (nuxt.options.ssr === false){
const jsServerEntry = resolve(nuxt.options.buildDir, "dist/server/server.js");
await promises.writeFile(jsServerEntry.replace(/.js$/, ".mjs"), 'export default {}', "utf8");
}
const manifest = await promises.readFile(resolve(nuxt.options.buildDir, "dist/server/client.manifest.json"), "utf8");
await promises.writeFile(resolve(nuxt.options.buildDir, "dist/server/client.manifest.mjs"), "export default " + manifest, "utf8");
}
问题 2: target:static 时执行 nuxi generate 错误
FATAL [nitro] Please use nuxt generate for static target 10:10:20
at setupNitroBridge (/D:/***/node_modules/@nuxt/bridge/dist/chunks/module.mjs:150:11)
at /D:/***/node_modules/@nuxt/bridge/dist/chunks/module.mjs:1300:15
at node_modules\hable\dist\hable.js:1:990
at node_modules\hable\dist\hable.js:1:208
at async Nuxt.callHook (node_modules\hable\dist\hable.js:1:959)
at async ModuleContainer.ready (node_modules\@nuxt\core-edge\dist\core.js:49:5)
at async Nuxt._init (node_modules\@nuxt\core-edge\dist\core.js:346:5)
解决办法: 警告信息明确提示使用 nuxt generate 而不是 nuxi ,但是不知为何官方文档标注是使用 nuxi
问题 3: target:static 时执行 nuxt generate 编译失败, ESM 无法加载
FATAL Only file and data URLs are supported by the default ESM loader. On Windows, absolute paths must be valid file:// URLs. Received protocol 'd:'
at new NodeError (node:internal/errors:371:5)
at throwIfUnsupportedURLProtocol (node:internal/modules/esm/resolve:1035:11)
at defaultResolve (node:internal/modules/esm/resolve:1105:3)
at ESMLoader.resolve (node:internal/modules/esm/loader:530:30)
at ESMLoader.getModuleJob (node:internal/modules/esm/loader:251:18)
at ESMLoader.import (node:internal/modules/esm/loader:332:22)
at importModuleDynamically (node:internal/modules/esm/translators:106:35)
at importModuleDynamicallyCallback (node:internal/process/esm_loader:35:14)
at prerender (/D:/***/node_modules/nitropack/dist/chunks/prerender.mjs:2169:39)
at async /D:/***/node_modules/@nuxt/bridge/dist/chunks/module.mjs:348:9
at async Nuxt.callHook (node_modules\hable\dist\hable.js:1:959)
at async Builder.build (node_modules\@nuxt\builder-edge\dist\builder.js:264:5)
at async ensureBuild (node_modules\@nuxt\cli-edge\dist\cli-generate.js:126:3)
at async Object.run (node_modules\@nuxt\cli-edge\dist\cli-generate.js:243:7)
at async NuxtCommand.run (node_modules\@nuxt\cli-edge\dist\cli-index.js:370:7)
这个问题的原因时编译后调用模块使用了绝对地址,如:d:\ ……\xxx.js,而这种地址是无法正常加载的,需要使用 file:// 头,即:file://d:\ …… \xxx.js
解决办法: 打开 /node_modules/nitropack/dist/chunks/prerender.mjs 第 2169 行
找到:const { localFetch } = await import(resolve(nitroRenderer.options.output.serverDir, "index.mjs"));
改为:const { localFetch } = await import('file://' + resolve(nitroRenderer.options.output.serverDir, "index.mjs"));
问题 4:target:static 时执行 nuxt generate 完成无法正常渲染 html 文件
修改问题 3 后,脚本编译成功,服务可以启动,但是无法正常渲染 html 文件,html 内容全部是 500 错误
You can preview this build using node .output/server/index.mjs nitro 10:32:08
Listening on [http://localhost:3000/](http://localhost:3000/) [nuxt] [request error] Invalid module ".cache/nuxt/views/document.template" is not a valid package name imported from D:\***\.output\server\chunks\renderer.mjs
at new NodeError (node:internal/errors:371:5)
at parsePackageName (node:internal/modules/esm/resolve:859:11)
at packageResolve (node:internal/modules/esm/resolve:880:5)
at moduleResolve (node:internal/modules/esm/resolve:978:18)
at defaultResolve (node:internal/modules/esm/resolve:1080:11)
at ESMLoader.resolve (node:internal/modules/esm/loader:530:30)
at ESMLoader.getModuleJob (node:internal/modules/esm/loader:251:18)
at ModuleWrap. (node:internal/modules/esm/module_job:79:40)
at link (node:internal/modules/esm/module_job:78:36)
原因是 .output\server\chunks\renderer.mjs 中引用错误
4: import htmlTemplate from '.cache/nuxt/views/document.template';
260: const getClientManifest = cachedImport(() => import('.cache/nuxt/dist/server/client.manifest'));
.cache/nuxt/views/document.template 与 .cache/nuxt/dist/server/client.manifest 都是无效地址,应该是 .nuxt/view/document.template.mjs 与 .nuxt/dist/server/client.manifest.mjs
解决办法: node_modules/nitropack/dist/chunks/prerender.mjs 生成 .output\server\chunks\renderer.mjs 时,file://***/node_modules/.cache/ 的文件要 replace 为正确路径,且不能将 .mjs 替换 replace 掉。
请原谅我知道了原因,但是没有办法通过修改代码解决,因为有点复杂能力有限,没花时间去研究了。
所以我最终的解决办法就是:降级! 关闭 bridge
打开 nuxt.config.js 添加:bridge: false
Would you be able to provide a reproduction? 🙏
More info
Why do I need to provide a reproduction?
Reproductions make it possible for us to triage and fix issues quickly with a relatively small team. It helps us discover the source of the problem, and also can reveal assumptions you or we might be making.
What will happen?
If you've provided a reproduction, we'll remove the label and try to reproduce the issue. If we can, we'll mark it as a bug and prioritise it based on its severity and how many people we think it might affect.
If needs reproduction labeled issues don't receive any substantial activity (e.g., new comments featuring a reproduction link), we'll close them. That's not because we don't care! At any point, feel free to comment with a reproduction and we'll reopen it.
How can I create a reproduction?
We have a couple of templates for starting with a minimal reproduction:
👉 https://stackblitz.com/github/nuxt/starter/tree/v2-bridge 👉 https://codesandbox.io/p/github/nuxt/starter/v2-bridge-codesandbox
A public GitHub repository is also perfect. 👌
Please ensure that the reproduction is as minimal as possible. See more details in our guide.
You might also find these other articles interesting and/or helpful:
This issue was closed because it was open for 7 days without a reproduction.