客户端 HTML MVC 渲染与通过 NodeJS 的服务器端渲染

Posted

技术标签:

【中文标题】客户端 HTML MVC 渲染与通过 NodeJS 的服务器端渲染【英文标题】:Client Side HTML MVC Rendering vs Server Side Rending via NodeJS 【发布时间】:2012-08-25 13:18:07 【问题描述】:

我们正在寻找我们团队的选项,以在基于 Angular 的客户端 MVC 方法和服务器端 NodeJS / ExpressJS 服务器端渲染方法之间做出决定。

我们的 Angular 应用程序下载为一个 index.html,并发出 XHR 请求来填充页面。因为我们需要预渲染页面,所以当内容更改到服务器上的某个位置时,我们使用 PhantomJS 保存每个页面的副本。这允许对 SEO 的支持。

是否有任何完整页面主干应用程序或 Angular 应用程序的示例可供人们指出以供我们查看其他人是否正在这样做。

另外还有我们可以在野外看到的 NodeJS 服务器端渲染应用程序的示例。

最后有人对这种架构有意见吗?

【问题讨论】:

您能否解释一下当内容更改为服务器上的某个位置时保存页面是什么意思?您如何允许用户链接到 Web 应用程序的特定“页面”?还是这不适用? 这是一个网站还是网络应用程序? 您好,我建议您阅读这篇关于 MVC 框架的内容丰富的博文。不包括 Express,但它提供了在构建 MVC 应用程序之前需要考虑的大量素材,以及哪些方法适合哪些应用程序。 coding.smashingmagazine.com/2012/07/27/…. 【参考方案1】:

我从事过主要是服务器渲染和主要是客户端渲染的应用程序。每种类型都有自己的优点和缺点。但是,您必须在其中一个或另一个之间进行选择的想法是错误的二分法。如果您有资源,您可以将两者结合起来,以实现两全其美。

我看到纯客户端框架的 4 个主要挑战:

SEO 和分析 缓存 内存 延迟

搜索引擎优化

因为您使用的是 Node.JS,所以可以通过简单地使用服务器上的客户端框架为 googlebot 和公司输出静态页面来缓解 SEO 问题。最近,Google 为单页应用程序制作了一个不错的 Analytics API,但这比简单地在主模板的末尾添加几行要多一些工作。

缓存

缓存是加速任何 Web 应用程序的非常重要的方法。对于少量数据,将数据缓存在客户端的内存或本地存储中会更快,但存储空间非常有限(目前约为 5MB)。加上缓存失效在 localStorage 中很难做到。

内存

记忆是我为忽视而付出的高昂代价。在不知不觉中,我不小心制作了一个占用超过 200MB RAM 的应用程序。我也许可以通过优化将它降低到一半,但我怀疑如果我将它全部呈现在服务器上,它会占用超过 20 MB。

延迟

延迟也很容易被忽略。例如,Drupal 为每个页面运行大约 50 到 100 个 SQL 查询。当数据库服务器就在应用程序服务器旁边时,您不必担心延迟,所有这些查询都可以在不到几百毫秒的时间内执行。您的客户端应用程序通常需要一百毫秒来发出一个 AJAX 请求。这意味着您需要花费大量时间设计服务器端 API 以尽量减少这些往返,但此时服务器已经拥有生成 HTML 所需的所有数据。如果您不小心,拥有一个与正确 RESTful 接口对话的客户端应用程序可能会变得非常缓慢。

37 Signals 最近发布了关于 the hybrid client/server architecture 的博客,他们为新版本的 Basecamp 实施。这种混合方法使用服务器来呈现 HTML,但利用客户端上的 PJAX 之类的东西来摆脱整页刷新。效果真的很快,是我推荐的。

【讨论】:

【参考方案2】:

在服务器上使用 node.js,原则上您可以使用相同的代码在客户端和服务器上进行渲染。实现这种方法的框架是Meteor 和Derby,它们还在客户端和服务器之间进行数据模型的透明同步。虽然两者仍被认为处于 alpha 阶段,但似乎已经很好地工作了。

同时,客户端和服务器端渲染各有利弊:

客户端呈现的缺点是初始页面加载需要很长时间,但是一旦加载了所有资源,用户就可以在没有页面的情况下无缝导航站点。您可能希望尽量减少 Ajax 调用的数量和/或使用客户端缓存(例如,在 Angular.js 控制器中缓存数据)。 服务器端呈现提供了快速的初始页面加载,有利于 SEO,但每次用户导航时,整个页面在加载新 URL 时会出现一秒钟的空白。

因此,这完全取决于您是想要快速的初始页面加载但不要期望用户停留那么久(然后使用服务器端呈现),还是页面加载速度不那么重要(如在 Gmail 中)但用户会浏览很长时间(然后使用客户端渲染)。

【讨论】:

【参考方案3】:

我们目前正在测试这种疯狂的方法: 我们有在客户端上运行的 angularJS 应用程序。当我们检测到 Googlebot 作为代理时,我们会运行 PhantomJS 实例并使用其输出响应爬虫。棘手的部分是知道您的客户端应用程序何时完成加载,以便您可以选择并返回它。如果您在加载客户端 JS 应用程序之前执行此操作,则爬虫将不会获取太多数据,主要是 index.html。

可以在这里找到简单的实现: http://pastebin.com/N3w2iyr8

更新:在我写原始答案的时候,不存在像 prerendr.io 这样的东西,但我可以指出它now。

【讨论】:

我想这是处理爬虫最好的方法,而不是搞乱整个开发过程。【参考方案4】:

我的解决方案是让 Google 可以抓取 Angular 上的应用程序。用于aisel.co

    https://github.com/localnerve/html-snapshots 处理的快照

    将规则添加到您的 .htaccess

    RewriteCond %QUERY_STRING ^_escaped_fragment_=(.*)$
    RewriteCond %REQUEST_URI !^/snapshots/views/ [NC]
    RewriteRule ^(.*)/?$ /snapshots/views/%1 [L]
    

    为快照创建 node.js 脚本,并在终端中运行它:node snapshots.js

    var htmlSnapshots = require('html-snapshots');
        var result = htmlSnapshots.run(
        input: "array",
        source: [
                "http://aisel.dev/#!/",
                "http://aisel.dev/#!/contact/",
                "http://aisel.dev/#!/page/about-aisel"
        ],
        outputDir: "web/snapshots",
        outputDirClean: true,
        selector: ".navbar-header",
        timeout: 10000
    , function(err, snapshotsCompleted) 
        var fs = require('fs');
        fs.rename('web/snapshots/#!', 'web/snapshots/views', function(err) 
            if ( err ) console.log('ERROR: ' + err);
        );
    );
    

    确保一切都可以使用 curl,在终端中输入

    curl http://aisel.dev/\?_escaped_fragment_\=/page/about-aisel/ 这应该显示快照的内容 .../www/aisel.dev/public/web/snapshots/views/page/about-aisel/index.html

不要对 google 和其他爬虫的指令。您的应用应在头部包含元规则:

    <meta name="fragment" content="!">

来自谷歌的完整条款:https://developers.google.com/webmasters/ajax-crawling/docs/specification

【讨论】:

以上是关于客户端 HTML MVC 渲染与通过 NodeJS 的服务器端渲染的主要内容,如果未能解决你的问题,请参考以下文章

通过 json 结果 MVC 渲染 Html 部分视图

通过nodejs 服务器读取HTML文件渲染到页面

nodejs渲染到页面的理解

6张图带你迅速了解前后端分离与服务端渲染模式

nodejs渲染模板

使用mvc7