客户端HTML MVC呈现与通过NodeJS的服务器端重新发送

时间:2012-08-30 18:26:05

标签: javascript node.js angularjs html-rendering

我们正在寻找我们团队的选项来决定基于Angular的客户端MVC方法和服务器端NodeJS / ExpressJS服务器端呈现方法。

我们的Angular应用程序作为一个index.html下载,并使XHR请求填充页面。由于我们需要预呈现页面,因此当内容更改为服务器上的某个位置时,我们使用PhantomJS保存每个页面的副本。这允许支持SEO。

是否有任何关于全页主干应用程序或角度应用程序的示例,人们可以指出这些应用程序以便我们查看其他人是否正在执行此操作。

或者是我们可以在野外看到的NodeJS服务器端呈现应用程序的示例。

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

4 个答案:

答案 0 :(得分:33)

我主要负责服务器渲染和大多数客户端渲染应用程序。每种类型都有自己的优点和缺点。然而,你必须在一个或另一个之间做出选择的想法是错误的二分法。如果您拥有这些资源,您可以将两者结合起来,以获得两全其美的效果。

我认为纯粹的客户端框架存在4个主要挑战:

  • SEO和分析
  • 缓存
  • 内存
  • 延迟

SEO

因为您使用的是Node.JS,所以只需使用服务器上的客户端框架输出googlebot和company的静态页面,就可以减轻SEO问题。最近,Google为单页面应用程序提供了一个很好的Analytics API,但这比简单地在主模板的末尾添加几行更多。

缓存

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

内存

记忆是我为俯视而付出的代价。在我知道它之前,我意外地制作了一个占用超过200MB RAM的应用程序。我可能会通过优化将其降低到一半,但我怀疑如果我在服务器上全部渲染它,它将花费超过20 MB。

延迟

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

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

答案 1 :(得分:8)

在服务器上使用node.js,原则上您可以使用相同的代码在客户端和服务器上呈现。实现此方法的框架是MeteorDerby,它们还在客户端和服务器之间进行数据模型的透明同步。两者仍然被认为是在阿尔法,但似乎已经很好地工作。

同时,客户端和服务器端呈现都有利有弊:

  • 客户端呈现的缺点是初始页面加载需要很长时间,但是一旦加载了所有资源,用户就可以无需页面无缝地导航网站。您可能希望最小化Ajax调用的数量和/或使用客户端缓存(例如Angular.js控制器中的缓存数据)。
  • 服务器端呈现提供了快速的初始页面加载,并且适用于SEO,但每次用户导航时,整个页面在加载新URL时会变为空白。

所以这一切都取决于你是想要一个快速的初始页面加载,但不希望用户保持这么长时间(然后使用服务器端渲染)或者它不是那么重要的页面加载快速(如在Gmail中),但用户将导航很长时间(然后使用客户端渲染)。

答案 2 :(得分:5)

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

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

更新:当我写完原始答案时,没有像prerendr.io那样存在,但我可以指向你now

答案 3 :(得分:0)

我的解决方案使Google可以对Angular进行抓取。用于aisel.co

  1. https://github.com/localnerve/html-snapshots
  2. 处理的快照
  3. 将规则添加到.htaccess

    RewriteCond %{QUERY_STRING} ^_escaped_fragment_=(.*)$
    RewriteCond %{REQUEST_URI} !^/snapshots/views/ [NC]
    RewriteRule ^(.*)/?$ /snapshots/views/%1 [L]
    
  4. 为快照创建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);
        });
    });
    
  5. 确保所有内容都适用于卷曲,输入终端

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

  6. 不要关于谷歌和其他抓取工具的指令。你的应用程序应该包含元头规则:

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

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