如何用 AngularJS 影响 HTML 渲染优先级?
How to impact the HTML rendering priorities with AngularJS?
我正在通过 PhantomJS 通过 Selenium 为搜索引擎机器人预呈现我的 HTML 页面,以便他们可以看到完全加载的内容。目前,在 PhantomJS 到达页面后,我正在等待 5 秒,以便确定所有内容都已加载。
与其每次都等待这 5 秒,我考虑的一种解决方案是等到 <body />
标记上的属性 html-ready
设置为 true:
<html ng-app>
<head>...</head>
<body html-ready="{{htmlReady}}">
...
</body>
</html>
.controller("AnyController", function($scope, $rootScope, AnyService) {
$rootScope.htmlReady = false;
AnyService.anyLongAction(function(anyData) {
$scope.anyData = anyData;
$rootScope.htmlReady = true;
});
})
问题是:在完成 任何视图更新(例如显示 anyData
)后,html-ready
属性是否始终设置为真 ?换句话说,有没有可能在一圈中,html-ready
属性是true
,而页面还没有完全加载?如果是,如何处理?
简答
没有。设置 html-ready
后,无法保证您的标记将完全呈现。
长答案
据我所知,无法准确确定模型更改后 Angular 何时完成更新 DOM。一般来说,它发生得非常快,不需要超过几个周期就可以完成,但情况并非总是如此。
正确检测页面何时完成 loading/rendering 实际上是一个相当大的挑战,如果您查看专用工具的源代码,例如 prerender,您会发现它们使用几种不同的检查来尝试 来决定页面是否准备就绪。即便如此,它也不会在 100% 的时间内正常工作(Phantom 可能会崩溃,请求可能需要比平时更长的时间才能完成,等等)。
如果你真的想针对这个问题提出自己的解决方案,我建议你看一下prerender的源代码(或其他类似的项目)以获得一些灵感。
它应该在摘要之后完成,这样它有更多的机会按预期工作。
AnyService.anyLongAction(function(anyData) {
$scope.anyData = anyData;
$timeout(function () {
$rootScope.htmlReady = true;
}, 0, false);
});
但在应用程序方面是没有用的。您必须注意每个地方的变化,Angular 没有提供任何使任务更容易的东西。
幸运的是,您可以从 Angular 中自由抽象并保持简单。
var ignoredElements = [];
ignoredElements = ignoredElements.concat($('.continuously-updating-widget').toArray());
var delay = 200; // add to taste
var timeout;
var ready = function () {
$('body').off('DOMSubtreeModified');
clearTimeout(timeoutLimit);
alert('ready');
};
$('body').on('DOMSubtreeModified', function (e) {
if (ignoredElements.indexOf(e.target) < 0) {
clearTimeout(timeout);
timeout = setTimeout(ready, delay);
}
});
var timeoutLimit = setTimeout(ready, 5000);
尽管它不是生产代码,但如有需要,请随意调整它。
将处理程序放入 throttle wrapper 函数中是个好主意(事件将一直发送垃圾邮件)。如果您在页面上使用可能超过超时延迟的远程请求,最好将此方法与异步服务的几个承诺结合起来,并使用 $q.all
解决它们。不过,总比照顾好每一个指令和服务要好得多。
DOMSubtreeModified
被认为是过时的(它从未被真正承认,MutationObserver
被推荐),但当前版本的FF和Chrome支持它,它应该是对硒没问题。
我正在通过 PhantomJS 通过 Selenium 为搜索引擎机器人预呈现我的 HTML 页面,以便他们可以看到完全加载的内容。目前,在 PhantomJS 到达页面后,我正在等待 5 秒,以便确定所有内容都已加载。
与其每次都等待这 5 秒,我考虑的一种解决方案是等到 <body />
标记上的属性 html-ready
设置为 true:
<html ng-app>
<head>...</head>
<body html-ready="{{htmlReady}}">
...
</body>
</html>
.controller("AnyController", function($scope, $rootScope, AnyService) {
$rootScope.htmlReady = false;
AnyService.anyLongAction(function(anyData) {
$scope.anyData = anyData;
$rootScope.htmlReady = true;
});
})
问题是:在完成 任何视图更新(例如显示 anyData
)后,html-ready
属性是否始终设置为真 ?换句话说,有没有可能在一圈中,html-ready
属性是true
,而页面还没有完全加载?如果是,如何处理?
简答
没有。设置 html-ready
后,无法保证您的标记将完全呈现。
长答案
据我所知,无法准确确定模型更改后 Angular 何时完成更新 DOM。一般来说,它发生得非常快,不需要超过几个周期就可以完成,但情况并非总是如此。
正确检测页面何时完成 loading/rendering 实际上是一个相当大的挑战,如果您查看专用工具的源代码,例如 prerender,您会发现它们使用几种不同的检查来尝试 来决定页面是否准备就绪。即便如此,它也不会在 100% 的时间内正常工作(Phantom 可能会崩溃,请求可能需要比平时更长的时间才能完成,等等)。
如果你真的想针对这个问题提出自己的解决方案,我建议你看一下prerender的源代码(或其他类似的项目)以获得一些灵感。
它应该在摘要之后完成,这样它有更多的机会按预期工作。
AnyService.anyLongAction(function(anyData) {
$scope.anyData = anyData;
$timeout(function () {
$rootScope.htmlReady = true;
}, 0, false);
});
但在应用程序方面是没有用的。您必须注意每个地方的变化,Angular 没有提供任何使任务更容易的东西。
幸运的是,您可以从 Angular 中自由抽象并保持简单。
var ignoredElements = [];
ignoredElements = ignoredElements.concat($('.continuously-updating-widget').toArray());
var delay = 200; // add to taste
var timeout;
var ready = function () {
$('body').off('DOMSubtreeModified');
clearTimeout(timeoutLimit);
alert('ready');
};
$('body').on('DOMSubtreeModified', function (e) {
if (ignoredElements.indexOf(e.target) < 0) {
clearTimeout(timeout);
timeout = setTimeout(ready, delay);
}
});
var timeoutLimit = setTimeout(ready, 5000);
尽管它不是生产代码,但如有需要,请随意调整它。
将处理程序放入 throttle wrapper 函数中是个好主意(事件将一直发送垃圾邮件)。如果您在页面上使用可能超过超时延迟的远程请求,最好将此方法与异步服务的几个承诺结合起来,并使用 $q.all
解决它们。不过,总比照顾好每一个指令和服务要好得多。
DOMSubtreeModified
被认为是过时的(它从未被真正承认,MutationObserver
被推荐),但当前版本的FF和Chrome支持它,它应该是对硒没问题。