【问题标题】:Open RequireJS Application with PhantomJS使用 PhantomJS 打开 RequireJS 应用程序
【发布时间】:2014-12-15 19:43:03
【问题描述】:

我正在尝试加载一个使用大量异步代码执行的单页应用程序,其中涉及 RequireJS 和 jQuery 延迟。应用程序在浏览器中按预期加载,但在 PhantomJS 中不加载。

例如,我花了一些时间试图弄清楚如何使以下 sn-p 工作:

# index.html
<body>
  <script>
    require.config({
      base: '.',
      paths: {
        main: 'main'
      }
    })

    require(['main'], function() {
       window.myglobal = {
           something: 'foo'
       }
    });
  </script>
</body>

# phantomjs
page.evaluateAsync(function() {
     console.log(window.myglobal.something); // Should print out 'foo'.
}, 100);

我认为将evaluateAsync 与必须通过反复试验确定的固定超时一起使用并不令人满意。也许有人可以提出更好的模式。

【问题讨论】:

    标签: javascript asynchronous requirejs phantomjs


    【解决方案1】:

    evaluateAsync 的文档并没有说太多,所以我会从表面上看它,并假设它只是异步执行代码,对可能已经加载或可能没有加载的内容没有任何进一步的限制。 (source code 也不表示任何进一步的约束。)

    我看到的问题是您有两个异步函数,它们可以按任意顺序执行。当require(['main'], ...) 被调用时,这告诉RequireJS 开始加载模块,但你不知道模块什么时候真正加载。同样,当您执行page.evaluateAsync 时,您是在告诉 PhantomJS 异步执行一段代码。所以它会执行,但你不知道什么时候。

    因此可能会发生以下情况:

    1. 模块完成加载:window.myglobal 已设置。

    2. 调用console.log,输出正确的值。

    或者:

    1. console.log 被调用,但失败。

    2. 模块完成加载:window.myglobal 已设置。

    设置延迟console.log 执行的超时将使第一种情况更可能发生,但它不保证它。

    你可以做的是像这样改变你的 HTML:

    <body>
      <script>
        require.config({
          base: '.',
          paths: {
            main: 'main'
          }
        })
    
        define('init', ['main'], function () {
           window.myglobal = {
               something: 'foo'
           };
        });
    
        require(['init']);
      </script>
    </body>
    

    您的 PhantomJS 脚本:

    page.evaluateAsync(function() {
         require(['init'], function () {
             console.log(window.myglobal.something); // Should print out 'foo'.
         });
    });
    

    它的作用是定义一个名为init 的模块。 (这是一种罕见的情况,用define 明确命名您的模块是可以的。通常您只需使用依赖项列表启动define 调用。)然后当调用evaluateAsync 时,它会请求init 模块,这保证了对window.myglobal 的分配将在console.log 运行之前发生。

    也可以使用 Promise 来获得所需的结果,但我更喜欢展示仅使用 RequireJS 的解决方案。

    【讨论】:

    • 感谢您的回答!我熟悉requirejs,但对phantomjs 很陌生。我猜您的解决方案不适用于该设置,如果 phantomjs 旨在打开应用程序的 url 并且没有对 js 文件的本地访问权限。
    • 您传递给evaluateAsync 的函数在浏览器上下文中执行。不需要“本地访问 js 文件”。
    【解决方案2】:

    PhantomJS 是一个无头浏览器,用于各种东西。其中很大一部分是网站的测试/自动化。这意味着您通常没有机会更改站点代码。大多数时候没有必要,例如在这种情况下。

    您只需要等到页面脚本/DOM 处于您想要进一步处理的状态。这通常使用 PhantomJS 的 examples 中的 waitFor 来完成。

    在您的情况下,您可以将waitFor 定义添加到脚本的开头并等待window.myglobal 被定义:

    page.open(url, function(){
        waitFor(function check(){
            return page.evaluate(function(){
                return !!window.myglobal;
            });
        }, function then(){
            // do something useful
        }, 10000); // upper bound on acceptable wait timeout
    });
    

    check 是一个定期调用以检查是否满足特定条件的函数。所以逻辑是,只要满足条件,您就可以做一些有用的事情,包括使用来自then 回调的page.evaluate 在页面上做一些事情。

    还有一些方法可以不等待特定变量/DOM 节点,而是等待网络活动的一般结束,如 answer

    【讨论】:

    • 第一段是为了区分这个答案和路易斯的答案。
    • 我假设// do something useful也应该在page.evaluate的回调中
    • 不一定。 check 是一个定期调用以检查是否满足特定条件的函数。所以逻辑是,只要满足条件,您就可以做一些有用的事情,包括使用page.evaluate 在页面上做一些事情。您当然可以在 check 函数中做一些有用的事情,但这完全取决于您的用例。
    • 是的!我只是添加了全局变量仅在page.evaluate 的范围内定义的注释
    猜你喜欢
    • 2013-10-27
    • 2020-08-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-18
    • 1970-01-01
    • 1970-01-01
    • 2022-12-21
    相关资源
    最近更新 更多