【问题标题】:How to fix script 404 errors (wrong path) in Angular project prerendered with Ng Universal如何修复使用 Ng Universal 预渲染的 Angular 项目中的脚本 404 错误(错误路径)
【发布时间】:2020-04-22 05:52:17
【问题描述】:

我在 Angular Web 应用中有不同的路线,例如“/about”、“/contact”等。

运行 build:ssr 后,我运行 ng run myproject:prerender

这将完成并在/dist/myproject/browser/* 中创建文件夹。每条路线都有不同的文件夹。

每个文件夹中都有一个 index.html 文件,它是渲染的 HTML 路由。到目前为止一切顺利。

但是,在每个 index.html 呈现的文件中,第一行如下所示:

<html lang="en"><head><script src="runtime-es2015.c9afb3256f2870e161de.js" type="module"></script><script src="runtime-es5.c9afb3256f2870e161de.js" nomodule="" defer=""></script><script src="polyfills-es5.2878b91c9d9c0a1ee890.js" nomodule="" defer=""></script><script src="polyfills-es2015.4cfd8e3e2fd9d10b9e9e.js" type="module"></script><script src="vendor-es2015.429669b901f4df42a057.js" type="module"></script><script src="vendor-es5.429669b901f4df42a057.js" nomodule="" defer=""></script><script src="main-es2015.a676e1a4bb3cc6e8a80c.js" type="module"></script><script src="main-es5.a676e1a4bb3cc6e8a80c.js" nomodule="" defer=""></script>

这看起来很好,直到您意识到脚本 src 是相对的。

因此,当部署时,假设https://myproject.com/about 将从 /about/ 文件夹加载呈现的 index.html。但是在加载脚本的时候,因为src是相对的,所以脚本路径变成了:https://myproject.com/about/runtim-es2015....js

这会返回 404 并使应用程序崩溃,因为该文件夹中不存在脚本。这些脚本位于根目录中。

这有意义吗?

我通过在我的部署管道中编写并包含以下脚本创建了一个临时修复程序,该脚本将正斜杠添加到 src 路径:

const { readdirSync } = require('fs')
const fs = require('fs')

const getDirectories = source =>
    readdirSync(source, { withFileTypes: true })
        .filter(dirent => dirent.isDirectory())
        .map(dirent => dirent.name)

const loop = function (DirectoryString) {

    const Dirs = getDirectories(DirectoryString);

    if (Dirs.length) {

        Dirs.forEach(folderName => {

            const DirectoryStr = './dist/myproject/browser/' + folderName + '/';

            if (fs.existsSync(DirectoryStr)) {

                console.log(DirectoryStr + 'index.html Fixed');

                loop(DirectoryStr);

                fs.readdir(DirectoryStr, (err, files) => {
                    files.forEach(file => {
                        if (file === "index.html") {

                            const fileName = `${DirectoryStr}index.html`;

                            try {
                                fs.readFile(fileName, 'utf8', function (err, data) {
                                    if (err) {
                                        return console.log(err);
                                    } else {
                                        var result = data.replace(/script src\=\"(?!http)([a-zA-Z])/g, 'script src="/$1');
                                        fs.writeFile(fileName, result, 'utf8', function (err) {
                                            if (err) return console.log(err);
                                        });
                                    }
                                });
                            } catch (e) {
                                console.log(e);
                            }
                        }
                    });
                });

            }

        });

    }

}

loop('./dist/myproject/browser/');

这行得通,但是现在,我想要一种在 Angular 中解决此问题的方法。我找不到其他有同样问题的人。有什么建议吗?

更新

在 localhost 开发中有时也会出现此问题。可以这么说,如果路线的深度不止一条,就会发生这种情况。

所以 ng 服务器会自动重新加载 localhost:4200/order/step2(由更新代码触发),应用程序没有加载,我收到错误

GET http://localhost:4200/order/step2/runtime.js net::ERR_ABORTED 404 (Not Found)

这对于开发来说是相当烦人的。这个项目从来不习惯,最近才开始:(

更新 2

另外,我在查看 localhost 呈现页面的源代码时观察到,脚本标签被添加到文档之外。

从顶部开始,这是文档的渲染源:

<script src="runtime.js" type="module"></script><script src="polyfills.js" type="module"></script><script src="styles.js" type="module"></script><script src="vendor.js" type="module"></script><script src="main.js" type="module"></script><!doctype html>
<html lang="en">

<head>
    <meta charset="utf-8">

为什么 Angular 会这样做?

更新 3

我的文档头中有&lt;base href="/"&gt;,但我怀疑由于脚本标签是在基础声明之前和文档之外添加的,所以基础没有被应用。

更新 4

这里是重现该问题的 github 存储库:

https://github.com/EmanH/angular-bug-SO-61358153

【问题讨论】:

  • 尝试在angular.json文件的prerender目标的options部分添加"deployUrl": "/",
  • @David 没用。失败并出现错误Schema validation failed with the following errors: Data path "" should NOT have additional properties(deployUrl).
  • 你在 github 上有一个可以重现问题的小项目吗?
  • 对不起,deployUrl 设置有误,您需要将其添加到 angular.json 文件的build 目标的production 配置中。我试过这个,当我运行ng run project:prerender:production 时,它会将/ 添加到 index.html 中的脚本路径中,如果它应该应用于其他配置,您可以对其他配置执行相同的操作。但是我不确定为什么您的脚本被添加到页面顶部...
  • 如果您查看 dist 文件夹中预渲染的 index.html 文件,它们是否在文件顶部 base 标记之前也有脚本?愚蠢的问题,您是否有一些代码可以创建/移动可能有问题的 dom 元素 (appendChild/renderer/...)?

标签: angular angular-universal


【解决方案1】:

您的仓库中的Index.html 没有结束&lt;/body&gt; 标记。让我们看看 angular-cli 的来源,以了解 Angular 如何添加 &lt;script&gt; 标签。

// Determine script insertion point
let scriptInsertionPoint;
if (bodyElement.__location && bodyElement.__location.endTag) {
  scriptInsertionPoint = bodyElement.__location.endTag.startOffset;
} else {
  // Less accurate fallback
  // parse5 4.x does not provide locations if malformed html is present
  scriptInsertionPoint = params.inputContent.indexOf('</body>');
}

indexSource.insert(scriptInsertionPoint, scriptElements);

它搜索结束&lt;/body&gt;标签的位置!在您的情况下,脚本被插入到 -1 位置。

老答案

请检查这个简单的回购https://github.com/dnmakarov/angular-universal-guide。它已配置 SSR,因此请确保预渲染在那里正常工作。我已经检查过 - 它对我来说很好。

主要问题是&lt;script&gt; 标签放置在index.html 中的位置。我检查了 Angular 的源代码,看起来这个逻辑几个月都没有改变。所以请确保您使用最新版本的angular-cli@nguniversal/*@angular/* 软件包。

如果之后问题仍然存在,请至少共享最小的存储库,以便我可以重现它。我与 Angular Universal 合作过很多次,并且第一次看到这样的问题。谢谢。

【讨论】:

  • 我有一个非常简单的 Github 存储库来复制这个问题:github.com/EmanH/angular-bug-SO-61358153 运行 ng build 并检查创建的 index.html 文件。
  • 啊!多么菜鸟的错误。谢谢。
猜你喜欢
  • 2021-05-20
  • 1970-01-01
  • 2022-01-17
  • 2023-02-08
  • 2021-08-08
  • 2018-10-19
  • 1970-01-01
  • 1970-01-01
  • 2020-01-10
相关资源
最近更新 更多