单页面应用SEO和无限滚动AngularJS

4
我们有一个类似Pinterest的站点,并计划将jQuery混乱代码重构为更加结构化的代码。最有可能的两个选择是AngularJS和Backbone+Marionette。该站点由用户生成,主要面向消费(典型的90/9/1规则),用户可以喜欢、收藏和评论帖子。从Feed中,我们打开一个灯箱以查看有关帖子的更多详细信息,包括评论、相关帖子和与Pinterest相似的内容。
我们偶尔使用backbone,并熟悉这个想法,但被样板代码所拖累。我认为Marionette会在很大程度上帮助解决这个问题,但如果长期来看它能帮助改变方向(如Angular),我们也是开放的。
要求:
  • 为了SEO的原因,初始页面必须是静态的。很重要的一点是框架能够从现有内容开始,最好不需要太多的修改。
  • 我们希望所需数据已经在feed中加载完成,以便转换更快。其中一些数据已经存在(标题、描述、照片、喜欢数/书签数、评论数),但还有其他数据需要在详细视图中加载——评论、类似的帖子、谁喜欢这个等等。
  • 在feed或详细lightbox中发生的帖子更改应该在另一个中得到反映,而且工作量应该很小(例如,如果我在feed中喜欢它,那么如果我进入lightbox,我应该看到那个喜欢和新的喜欢计数数字——或者相反)。
  • 我们希望将我们的移动站点(目前使用Sencha Touch)迁移到同一代码库,以便对于共同部分可以实现更接近的功能平衡。

这些要求与我对Angular的关注相关:

1)在通过模板渲染附加页面时,是否可能/有问题使初始页面加载为静态页面。

2) 是否为页面的不同部分使用多个数据源有问题 - 例如主要帖子部分来自嵌入式json数据和“查看更多”在动态源中,而其他详细信息将来自不同的ajax调用。

3) 虽然双向绑定很酷,但由于呈现的项目数量,我担心它可能会对我们产生负面影响。我们需要使用双向绑定的元素数量相对较小。像这样的帖子:

让我对我们的用例感到担忧。我们可以轻松地拥有数百个帖子,每个帖子都有一两打详细信息。 我知道哪些字段/元素不会更改,是否可以“禁用”双向绑定?

在视口之外卸载元素以节省内存是否正常/可能? 这也与移动方向相关,因为内存在那里更加关键。

AngularJS能在我们的使用场景中良好运行/表现吗?有什么技巧/提示可以帮助这里吗?
2个回答

18

有不同的“无限滚动”或所谓的“feed”方法。用户需求和可接受响应负载大小将决定您选择哪种方法。

在性能与易用性之间似乎需要做出妥协。

1. 添加资源

这种方法是传统的向底部追加方法,当用户到达当前滚动高度的底部时,将会发起另一个API调用来“继续堆积”内容。它具有处理跨设备陷阱的最有效解决方案的优点。

正如您提到的那样,这种解决方案的缺点来自于大量的负载在用户粗心地滚动内容时涌入内存。这里没有节流机制。

<div infinite-scroll='getMore()' infinite-scroll-distance='0'>
  <ul>
    <li ng-repeate="item in items">
      {{item}}
    </li>
  </ul>
</div>

var page = 1;
$scope.getMore() = function(){ 
 $scope.items.push(API.returnData(i));
 page++;
}

2. 使用节流方式追加资源

在这里,我们建议用户可以继续在无限追加的动态流中显示更多结果,但是他们必须进行节流或“手动”调用更多数据的请求。相对于用户将要滚动浏览的内容大小,这变得繁琐。

如果每个有效负载返回了大量内容,则用户将不得不较少点击“获取更多”按钮。当然,这是以返回更大有效负载为代价的。

<div>
  <ul>
    <li ng-repeate="item in items">
      {{item}}
    </li>
  </ul>
</div>
<div ng-click='getMore()'>
  Get More!
</div>

var page = 1;
$scope.getMore() = function(){
  $scope.items.push(API.returnData(i));
  page++;
}

3. 虚拟滚动

这是一种最后也是最有趣的无限滚动方式。其思想是仅在浏览器内存中存储一定范围内的渲染结果版本。也就是说,复杂的DOM操作仅作用于您配置中指定的当前范围。然而,这种方法也有自己的缺陷。

最大的问题是跨设备兼容性。

如果您的手持设备具有达到设备宽度的虚拟滚动窗口 --- 它最好小于页面的总高度,因为您将永远无法滚动过此“feed”及其自己的滚动条。您会被“卡住”在页面中间,因为您的滚动将始终作用于虚拟滚动 feed 而不是包含 feed 的实际页面。

接下来是可靠性。如果用户手动从低索引拖动滚动条到极高索引,您将强制浏览器非常快地运行这些指令,在测试中,这导致我的浏览器崩溃。这可以通过隐藏滚动条来解决,但当然用户也可以通过非常快速地滚动来调用相同的情况。

这里是演示

来源

"出于SEO的原因,初始页面必须是静态的。重要的是框架能够从现有内容开始,最好不需要太多的修改。"

所以你的意思是在提供内容之前,希望页面在服务器端进行预渲染?这种方法在2000年代初期效果很好,但现在大多数人都在向单页应用程序风格转变。有很好的理由:

  • 您发送给用户的初始种子作为引导来获取API数据,使您的服务器工作量大大减少。

  • 延迟加载资源和异步网络服务调用使感知的加载时间比传统的“先在服务器上渲染所有内容,然后将其返回给用户”的方法快得多。

  • 通过使用页面预渲染/缓存引擎来保留您的SEO,该引擎位于Web服务器的前面,仅对Web爬虫响应您的“完全渲染版本”。这个概念在这里很好地解释了。

我们希望在feed中已经加载lightbox所需的数据,以便过渡更快。一些数据已经存在(标题、描述、照片、喜欢数/书签数、评论数),但还有其他数据需要加载到详细视图中-评论、类似的帖子、谁喜欢这个等等。
如果您的初始负载不包含每个“feed id”的子数据点,并且需要使用其他API请求来加载它们到您的lightbox中-那么您正在做正确的事情。这是完全合法的用例。您将为单个API调用争论50-100ms,这对您的最终用户来说是无法感知的延迟。如果您绝对需要将附加负载与您的feed一起发送,则不会获得太多好处。
在feed或详细lightbox中发生的帖子更改应该很容易地反映在另一个中(例如,如果我从feed中喜欢它,如果我去lightbox,我应该看到那个喜欢和新的喜欢计数器数字-或相反)。
你在这里混合使用技术 --- "赞"按钮是对Facebook的API调用。是否将这些更改传播到同一页上其他Facebook“赞”按钮的实例取决于Facebook处理方式,我相信快速谷歌可以帮助你解决问题。
然而特定于您网站的数据 --- 有几种不同的用例:
- 假设我更改了lightbox中的标题,并且还希望更改传播到当前显示的Feed中。如果您的“保存编辑操作”POST到服务器,则成功回调可以触发使用websocket更新新值。此更改将传播到不仅是您的屏幕,而且其他人的屏幕。 - 您还可以谈论双向数据绑定(AngularJS非常擅长此项功能)。通过双向数据绑定,您从Web服务获取的“模型”或数据可以绑定到视图中的多个位置。这样,当您编辑页面的某一部分与共享同一模型时,其他部分将实时更新。这发生在任何HTTP请求之前,因此是完全不同的用例。

我们希望将我们的移动站点(目前使用Sencha Touch)迁移到同一代码库,以便于共享通用部分,从而使移动站点和主站点之间更加接近特性平衡。

您应该真正关注现代响应式CSS框架,例如BootstrapFoundation。使用响应式Web设计的重点在于您只需要构建一次网站即可适应所有不同的屏幕尺寸。

如果您谈论功能模块化,AngularJS是最好的选择。其想法是您可以将网站组件导出为可用于另一个项目的模块。这也可以包括视图。如果您使用响应式框架构建了视图,那么猜猜——您现在可以在任何地方使用它。

1)在通过模板渲染其他页面时,初始页面加载是否可能存在问题?

如上所述,最好摆脱这种方法。如果您绝对需要它,模板引擎并不关心您的有效负载是在服务器端还是客户端呈现的。链接到部分页面将同样可访问。

2) 对于页面的不同部分使用多个数据源是否有问题 - 例如,主要帖子部分来自嵌入式JSON数据和来自动态加载的“查看更多” ,而附加详细信息将来自不同的ajax调用。

再次强调,这正是该行业正在发展的方向。您将通过使用初始静态引导程序来获取所有外部API数据来节省“感知”和“实际”加载时间 --- 这也将使您的开发周期更快,因为您正在分离完全独立的关注点。您的API不应关心您的视图,您的视图也不应关心您的API。思路是当您将它们分解成较小的部分时,您的API和前端代码都可以变得模块化/可重用。

3) 虽然双向绑定很酷,但由于渲染的项目数量,我担心在我们的情况下可能会产生负面影响。我们需要进行双向绑定的元素数量相对较少。

我还将结合您在下面留下的评论来回答这个问题:

感谢您的回答!您能否澄清一下 - 看起来1)和2)只涉及如何实现无限滚动,而不是可能出现的性能问题。似乎3)以类似于最近版本的Sencha Touch的方式解决了这个问题,这可能是一个好的解决方案。
您将遇到的性能问题完全是主观的。我试图在讨论中概述性能考虑因素,例如节流,因为节流可以大大减少服务器承受的压力以及每个新结果集附加到DOM时用户浏览器所需执行的工作量。
无限滚动,经过一段时间后,将耗尽用户浏览器的内存。我可以告诉您的是,这是不可避免的,但只有通过测试,您才能知道具体情况。根据我的经验,我可以告诉您,用户的浏览器可以处理大量的滚动,但是,每个结果集的有效负载大小以及您在所有结果上运行的指令的大小是完全主观的。在我描述的第三个选项中,有一些解决方案仅呈现在范围数据集上,但也有其局限性。
API返回的数据大小不应超过1-2KB,并且查询返回时间应该只需要50-200毫秒。如果未达到这些速度,也许是时候重新评估查询或通过使用子ID查询其他端点来缩小结果集的大小了。请保留HTML标签。

谢谢你的回答!你能否澄清一下——似乎1)和2)只涉及如何实现无限滚动,而不是可能出现的性能问题。看起来3)以类似于Sencha Touch最新版本的方式解决了这个问题,这可能是一个好的解决方案。 - Yehosef
此答案还涉及性能问题。您了解问题中提到的其他问题吗?(1.静态初始页面和2.多个数据源) - Yehosef
@Yehosef,我已经扩展了我的答案,包括你的其他问题。如果你还有任何问题,请告诉我。 - Dan Kanze
关于SEO方面-我阅读了这篇文章,虽然它解释了一种方法,但并不清楚它是否真的对Google有帮助。 我们的SEO专家一直认为向机器人和普通用户提供不同的内容是不可取的。 另一个重要的考虑因素是,我们成为SPA对我们的用户并不是非常重要。 仅仅因为一个网站可以成为SPA,并不意味着它应该成为SPA。例如-yelp.com或instructables.com是否应该编写成SPA? 我们基本上已经有一个工作站点/流程,我们只想找到一种方法来使JavaScript更加有序。 - Yehosef
谢谢。我欣赏那种自然适合的模式,我知道使用它时可能会有性能提升,但是当不需要它时,我也看到了风险(我们曾经使用过pushstate,这并不总是一件轻松的事情 - 也许他们会处理所有细节)。假设我们没有确定采用SPA方法(我们的SEO专家与您的意见不同)-使用Angular会有多大困难? - Yehosef
显示剩余7条评论

0
Dan的回答中仍未解决的主要问题是初始页面加载。我们仍不满意使用客户端方法的方式-对于SEO和初始页面加载仍然存在风险。我们有相当数量的SEO流量,并正在寻找更多-这些人没有缓存地访问我们的网站,我们只有几秒钟时间来吸引他们。
有几个选项可以处理Angular的服务器端-我将在此处尝试收集一些。

https://github.com/ithkuil/angular-on-server https://github.com/ithkuil/angular-on-server/wiki/Running-AngularJS-on-the-server-with-Node.js-and-jsdom

https://github.com/angular/angular.js/issues/2104

随着问题的出现,我们会逐步添加更多内容。


1
如果您的实现团队在为SPA实现SEO解决方案时遇到了麻烦,为什么不尝试像http://www.brombone.com/这样的付费服务,甚至是通过http://www.blitline.com/docs/seo_optimizer设置自己的S3的免费服务呢? - Dan Kanze
请注意,他们的整个宣传口号是“谷歌无法爬取您的Angular网站-但我们可以为您抓取它,以便它们获得纯HTML”。这似乎加强了我们的担忧。引用:“Googlebot能否运行所有JavaScript?答案是否定的。但不要担心,我们为您提供了解决方案。” - Yehosef
当然,这是谷歌提供的相同解决方案...对于SEO,您不需要关心机器人是否可以“运行”您的JavaScript。您只需要确保它可以爬取HTML。此外,无论Web开发人员是否喜欢,谷歌都与W3合作,通过诸如AngularJS和Polymer(用于Web框架的全局API)等技术改革Web标准...换句话说,这就是Web正在走向的方向,我们无能为力。 - Dan Kanze
问题在于谷歌不会花费太多时间/ CPU 运行所有我的 JavaScript,以查看页面应该是什么样子 - 这些服务会向您收取费用。而且不清楚是否因为具有不同的机器人/非机器人流量而受到惩罚 - 我们的 SEO 人员说会有影响,但这可能只是针对关键字堆积的情况 - SPAs 可能没有问题。我相信大多数 SPA 应用程序并不是为了 SEO / 机器人而构建的,因此整个问题对他们来说都是无意义的。对于那些需要 SEO 的人,我怀疑他们是否会运行像 phantomjs 这样的 CPU 昂贵的东西来呈现我的页面。 - Yehosef
1
如果我表达不清楚,你绝对需要预渲染内容。 - Dan Kanze
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接