使用 ASP.NET Core 2 的服务器端渲染 Vue
Server Side Rendering Vue with ASP.NET Core 2
我正在尝试了解在使用 aspnet core 时使用 vuejs 进行服务器端渲染的用法和限制。
我用了this starter kit for aspnet core and vuejs to setup a simple vue site, which is running based on the code here: 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 and/or aspnet core 的特定 ssr?
还是我只是漏掉了一步?
编辑:更多信息
我查看了源代码,我认为这是预呈现该部分的方法:https://github.com/aspnet/JavaScriptServices/blob/dev/src/Microsoft.AspNetCore.SpaServices/Prerendering/PrerenderTagHelper.cs
行
output.Content.SetHtmlContent(result.Html);
result.Html 的值为空。但是,当我手动编辑此值以放置测试值时,它也不会呈现到输出 html,并且应用程序 div 标签仍然是空的...
如果我在用预期输出填充 result.Html 值时做错了什么,那是一回事,我将不胜感激,特别是因为输出 html 出现可以找到,因为它在紧随其后的脚本中...
但是,即使我要填充它,它也似乎被跳过了,正如我手动更改值所证明的那样。这是代码中的错误还是我做错了什么,或者两者都有?
正如您正确注意到的那样,对于您的项目,标签助手中的 result.Html
为空。因此该行不能是生成输出的位置。由于预呈现脚本的 HTML 输出也不包含 script
标记,因此很明显必须生成该标记。唯一可能执行此操作的其他行是 PrerenderTagHelper
:
中的以下行
output.PostElement.SetHtmlContent($"<script>{globalsScript}</script>");
这将符合观察到的输出,所以我们应该弄清楚 globalsScript
来自哪里。
如果你看PrerenderTagHelper
implementation, you can see that it will call Prerenderer.RenderToString
which returns a RenderToStringResult
。此结果对象在调用您的 Node 脚本后从 JSON 反序列化。
所以这里有两个感兴趣的属性:Html
和 Globals
。前者负责包含最终在标签助手内部呈现的 HTML 输出。后者是一个 JSON 对象,包含应为客户端设置的附加全局变量。这些是将在 script
标记内呈现的内容。
如果您查看项目中呈现的 HTML,您可以看到有两个全局变量:window.html
和 window.__INITIAL_STATE__
。所以这两个设置在你的代码中的某个地方,虽然 html
不应该是全局的。
罪魁祸首是 renderOnServer.js
文件:
vue_renderer.renderToString(context, (err, _html) => {
if (err) { reject(err.message) }
resolve({
globals: {
html: _html,
__INITIAL_STATE__: context.state
}
})
})
如您所见,这将解析仅包含具有 html
和 __INITIAL_STATE__
属性的 globals
对象的结果。这就是在 script
标签内呈现的内容。
但是你想要做的是让 html
不是 globals
的一部分而是在上面的层上,这样它就被反序列化到 RenderToStringResult.Html
属性:
resolve({
html: _html,
globals: {
__INITIAL_STATE__: context.state
}
})
如果您这样做,您的项目将正确执行服务器端渲染,而不需要 JavaScript 初始视图。
我正在尝试了解在使用 aspnet core 时使用 vuejs 进行服务器端渲染的用法和限制。
我用了this starter kit for aspnet core and vuejs to setup a simple vue site, which is running based on the code here: 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 and/or aspnet core 的特定 ssr?
还是我只是漏掉了一步?
编辑:更多信息
我查看了源代码,我认为这是预呈现该部分的方法:https://github.com/aspnet/JavaScriptServices/blob/dev/src/Microsoft.AspNetCore.SpaServices/Prerendering/PrerenderTagHelper.cs
行
output.Content.SetHtmlContent(result.Html);
result.Html 的值为空。但是,当我手动编辑此值以放置测试值时,它也不会呈现到输出 html,并且应用程序 div 标签仍然是空的...
如果我在用预期输出填充 result.Html 值时做错了什么,那是一回事,我将不胜感激,特别是因为输出 html 出现可以找到,因为它在紧随其后的脚本中...
但是,即使我要填充它,它也似乎被跳过了,正如我手动更改值所证明的那样。这是代码中的错误还是我做错了什么,或者两者都有?
正如您正确注意到的那样,对于您的项目,标签助手中的 result.Html
为空。因此该行不能是生成输出的位置。由于预呈现脚本的 HTML 输出也不包含 script
标记,因此很明显必须生成该标记。唯一可能执行此操作的其他行是 PrerenderTagHelper
:
output.PostElement.SetHtmlContent($"<script>{globalsScript}</script>");
这将符合观察到的输出,所以我们应该弄清楚 globalsScript
来自哪里。
如果你看PrerenderTagHelper
implementation, you can see that it will call Prerenderer.RenderToString
which returns a RenderToStringResult
。此结果对象在调用您的 Node 脚本后从 JSON 反序列化。
所以这里有两个感兴趣的属性:Html
和 Globals
。前者负责包含最终在标签助手内部呈现的 HTML 输出。后者是一个 JSON 对象,包含应为客户端设置的附加全局变量。这些是将在 script
标记内呈现的内容。
如果您查看项目中呈现的 HTML,您可以看到有两个全局变量:window.html
和 window.__INITIAL_STATE__
。所以这两个设置在你的代码中的某个地方,虽然 html
不应该是全局的。
罪魁祸首是 renderOnServer.js
文件:
vue_renderer.renderToString(context, (err, _html) => {
if (err) { reject(err.message) }
resolve({
globals: {
html: _html,
__INITIAL_STATE__: context.state
}
})
})
如您所见,这将解析仅包含具有 html
和 __INITIAL_STATE__
属性的 globals
对象的结果。这就是在 script
标签内呈现的内容。
但是你想要做的是让 html
不是 globals
的一部分而是在上面的层上,这样它就被反序列化到 RenderToStringResult.Html
属性:
resolve({
html: _html,
globals: {
__INITIAL_STATE__: context.state
}
})
如果您这样做,您的项目将正确执行服务器端渲染,而不需要 JavaScript 初始视图。