首页 > 解决方案 > 使用 ASP.NET Core 2 的服务器端渲染 Vue

问题描述

我试图了解使用 aspnet 核心时使用 vuejs 进行服务器端渲染的用法和限制。

我使用这个用于 aspnet 核心和 vuejs 的入门工具包来设置一个简单的 vue 站点,该站点基于此处的代码运行:https ://github.com/selaromdotnet/aspnet-vue-ssr-test/tree/master

然后我修改了项目以更新 aspnet-prerendering 并添加了 vue-server-renderer,编译了一个大杂烩来拼凑这个更新:https ://github.com/selaromdotnet/aspnet-vue-ssr-test/tree/ ssr

如果我运行这个项目,该站点似乎可以正常加载,并且如果我关闭浏览器中的 javascript,我可以看到服务器端渲染似乎已执行并填充了 html 结果:

在此处输入图像描述

然而,由于 JavaScript 被禁用,内容并没有被移动到 dom 中,因为它看起来正在尝试......

我对服务器端渲染的理解是,它将完全填充 html 并向用户提供完整的页面,这样即使 JS 被禁用,他们至少也能够看到该页面(特别是用于 SEO 目的)。我不正确吗?

现在我相信现代搜索引擎会执行这样的简单脚本来获取内容,但是如果禁用 js,我仍然不希望呈现空白页面......

这是服务器端渲染的限制,还是特别是带有 vue 和/或 aspnet 核心的 ssr?

还是我只是在某处错过了一步?

编辑:更多信息

我查看了源代码,我认为这是在此处预呈现该部分的方法:https ://github.com/aspnet/JavaScriptServices/blob/dev/src/Microsoft.AspNetCore.SpaServices/Prerendering/PrerenderTagHelper.cs

线

output.Content.SetHtmlContent(result.Html); 

result.Html 的值为空。但是,当我手动编辑此值以放置测试值时,它也不会呈现到输出 html,并且 app div 标签仍然为空...

如果我在使用预期输出填充 result.Html 值时做错了什么,那是一回事,我会很感激这样做的一些帮助,特别是因为似乎找到了输出 html,因为它在脚本中立即跟随...

但是,即使我要填充它,它似乎也被跳过了,正如我手动更改值所证明的那样。这是代码中的错误还是我做错了什么,或者两者兼而有之?

标签: vue.jsvuejs2asp.net-core-2.0server-side-renderingasp-net-core-spa-services

解决方案


正如您正确注意到的那样,对于您的项目,result.Html标签助手内部为空。因此该行不能是生成输出的位置。由于您的预渲染脚本的 HTML 输出也不包含script标签,因此很明显必须生成该标签。唯一可以做到这一点的其他行是来自以下的PrerenderTagHelper

output.PostElement.SetHtmlContent($"<script>{globalsScript}</script>");

这将适合观察到的输出,所以我们应该弄清楚它的globalsScript来源。

如果您查看PrerenderTagHelper实现,您会发现它将调用Prerenderer.RenderToString返回 a RenderToStringResult。此结果对象在调用您的 Node 脚本后从 JSON 反序列化。

所以这里有两个感兴趣的属性:HtmlGlobals。前者负责包含最终在标签助手中呈现的 HTML 输出。后者是一个 JSON 对象,其中包含应为客户端设置的附加全局变量。这些将在该script标签内呈现。

如果您查看项目中呈现的 HTML,您会看到有两个全局变量:window.htmlwindow.__INITIAL_STATE__. 因此,这两个设置在您的代码中的某个位置,尽管html不应该是全局的。

罪魁祸首是renderOnServer.js文件:

vue_renderer.renderToString(context, (err, _html) => {
    if (err) { reject(err.message) }
    resolve({
        globals: {
            html: _html,
            __INITIAL_STATE__: context.state
        }
    })
})

如您所见,这将解析仅包含具有和属性的globals对象的结果。这就是在标签内部呈现的内容。html__INITIAL_STATE__script

但是你想要做的不是html作为上层的一部分globals而是在上面的层上,以便它被反序列化到RenderToStringResult.Html属性中:

resolve({
    html: _html,
    globals: {
        __INITIAL_STATE__: context.state
    }
})

如果您这样做,您的项目将正确执行服务器端渲染,而初始视图不需要 JavaScript。


推荐阅读