使用ASP.NET Core 2的服务器端渲染Vue

时间:2018-06-07 16:32:32

标签: vue.js vuejs2 asp.net-core-2.0 serverside-rendering asp-net-core-spa-services

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

我使用this starter kit for aspnet core and 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结果:

enter image description here

但是,由于禁用了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似乎是发现,因为它紧跟在脚本之后......

然而,即使我填充它,它似乎被跳过,我手动更改值就是证明了这一点。这是代码中的错误还是我做错了,或者两者都错了?

1 个答案:

答案 0 :(得分:2)

正如您所注意到的,对于您的项目,标记帮助程序中的result.Html为空。因此该行不能是生成输出的位置。由于预呈现脚本的HTML输出也不包含script标记,因此很明显必须生成某些内容。唯一可以执行此操作的其他行是PrerenderTagHelper

中的以下内容
output.PostElement.SetHtmlContent($"<script>{globalsScript}</script>");

这符合观察到的输出,因此我们应该找出globalsScript的来源。

如果您查看PrerenderTagHelper实施,可以看到它会调用Prerenderer.RenderToString并返回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
        }
    })
})

如您所见,这将解析仅包含globalshtml属性的__INITIAL_STATE__对象的结果。这是在script标记内部呈现的内容。

但您要做的是将html作为globals的一部分,而是放在上面的图层上,以便将其反序列化到RenderToStringResult.Html属性中:

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

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