【问题标题】:ngTransclude fails on PhantomJSngTransclude 在 PhantomJS 上失败
【发布时间】:2015-05-05 18:37:10
【问题描述】:

所以我正在开发一个 AngularJS 网站,我的任务是让它对 Facebook 分享和 SEO 友好。我选择了 PhantomJS 作为一种可能的解决方案,用于评估 Javascript 并输出已执行的 html 代码,目前这只是用信息填充 facebook 元标记。

然而,在启动 phantomJS 并评估页面后,我的 chrome 中出现此错误:

错误:[ngTransclude:orphan] 在模板中非法使用 ngTransclude 指令!没有找到需要嵌入的父指令。元素:<ul ng-transclude=""> http://errors.angularjs.org/1.2.22/ngTransclude/orphan?p0=%3Cul%20ng-transclude%3D%22%22%3E

查看代码后,我只在站点中使用了一次transclude,那是用于生成我的菜单,其中包括一个 ul 和 transcluded li 项。

    app.directive('mvMenu', ['$rootScope', '$location', function ($rootScope, $location) {
return {
    restrict: 'E',
    transclude: true,
    controller: function ($scope, $element, $attrs) {
        ...
    },
    template: '<nav id="nav" role="navigation">' +
                '<div class="block">' +
                '<ul ng-transclude>' +
                '</ul>' +
                '</div>' +
                '</nav>'

}
}]);

我的 li items 也是一个指令,它的代码是这样的:

app.directive('mvMenuItem', ['$location', function ($location) {
return {
    require: '^mvMenu',
    restrict: 'E',
    scope: {
        text: '@text',
        navLink: '@link',
        icon: '@faicon'
    },
    link: function (scope, element, attr, menuCtrl) {
        ...
    },
    template: '<li ng-class="{\'is-active\': isActive(navLink)}">' +
    '<a ng-click="navAndToggleMenu(navLink)"><span class="fa {{icon}}"></span>{{text | translate}}</a>' +
    '</li><!--'
}
}]);

当网站在任何设备上的任何浏览器上使用时,我从未见过此错误。只有在使用 phantomJS 评估页面时才会出现。

这是我用来生成 html 的 phantomJS 代码:

var page = require('webpage').create();
var system = require('system');

var lastReceived = new Date().getTime();
var requestCount = 0;
var responseCount = 0;
var requestIds = [];
var startTime = new Date().getTime();

page.onResourceReceived = function (response) {
if (requestIds.indexOf(response.id) !== -1) {
    lastReceived = new Date().getTime();
    responseCount++;
    requestIds[requestIds.indexOf(response.id)] = null;
}
};
page.onResourceRequested = function (request) {
if (requestIds.indexOf(request.id) === -1) {
    requestIds.push(request.id);
    requestCount++;
}
};

function checkLoaded() {
return page.evaluate(function () {
    return document.all;
}) != null;
}
// Open the page
page.open(system.args[1], function () { });

var checkComplete = function () {
// We don't allow it to take longer than 5 seconds but
// don't return until all requests are finished
if ((new Date().getTime() - lastReceived > 300 && requestCount ===            
responseCount) || new Date().getTime() - startTime > 10000 || checkLoaded()) {
    clearInterval(checkCompleteInterval);
    var result = page.content;
    //result = result.substring(0, 10000);
    console.log(result);
    //console.log(results);
    phantom.exit();
}
}
// Let us check to see if the page is finished rendering
var checkCompleteInterval = setInterval(checkComplete, 300);

此代码取自How to make a SPA SEO crawlable?,唯一的区别在于评估方法中的“return document.all”。

提前致谢!

【问题讨论】:

  • 偶然删除菜单有用吗?
  • 你在哪里使用 mvMenu 指令?
  • @AlexMA 我尝试删除菜单,是的,它执行没有错误。显然我在使用普通浏览器时想要那里的菜单,所以也许有办法只在 phantomjs 中删除菜单?
  • @HankScorpio 我正在使用 ASP.NET 后端,该指令直接在我的 index.cshtml 文件中使用。在尝试注释掉菜单后,我注意到我的角度代码似乎根本没有执行,因为在检查了 phantomjs 在 chrome 中吐出的内容后,我注意到 ng-view 根本没有填充,我的 facebook 元标记也没有.我四处搜索并尝试在我的 apicalls 之后设置 $scope.htmlReady(),但无济于事

标签: angularjs phantomjs


【解决方案1】:

在你运行你的 PhantomJS 脚本之后,你得到的 html 已经被渲染了。当你在普通浏览器中打开它已经渲染后,让 angular 尝试再次渲染它是没有意义的。

以下面的标记为例:

<div ng-repeat="stuff in stuffList">{{stuff}}</div>

这例如被渲染到

<div class="ng-repeat" ng-repeat="stuff in stuffList">Stuff1</div>
<div class="ng-repeat" ng-repeat="stuff in stuffList">Stuff2</div>

如果您在浏览器中打开已经呈现的标记会发生什么? ng-repeat 将再次执行,这将导致以下标记:

<div class="ng-repeat" ng-repeat="stuff in stuffList">Stuff1</div>
<div class="ng-repeat" ng-repeat="stuff in stuffList">Stuff1</div>
<div class="ng-repeat" ng-repeat="stuff in stuffList">Stuff2</div>
<div class="ng-repeat" ng-repeat="stuff in stuffList">Stuff2</div>

这不是你想要的。

您应该在保存标记之前删除所有页面脚本。不再需要 JavaScript。

page.evaluate(function(){
    [].forEach.call(document.querySelectorAll("script"), function(s){
        s.parentNode.removeChild(s)
    });
});
fs.write("content.html", page.content);
phantom.exit();

【讨论】:

  • 感谢您似乎已修复错误。您似乎对 phantomjs 有一些经验,也许您可​​以帮助我解决我注意到的新问题。运行上面的代码后,似乎根本没有执行任何 angularjs 代码。 PhantomJS 在我的 index.cshtml 后面的 MVC 控制器中通过 ASP.NET 执行。我正在使用来自 this link 的代码,它执行 phantomjs.exe 和脚本。有没有我遗漏的步骤?
  • 你的意思是在应用我的修复之后?这就是想法。 JavaScript 不会被执行。当不了解 JS 的机器人出现时,您似乎正在将其用于 SEO。也许我误解了你的问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-21
  • 1970-01-01
  • 2016-02-22
相关资源
最近更新 更多