Rendering React.js clientside webapp with PhantomJS

4.2k views Asked by At

A friend has asked me to capture a client-side rendered website built with React.js, preferably using PhantomJS. I'm using a simple rendering script as follows:

var system = require('system'),
fs = require('fs'),
page = new WebPage(),
url = system.args[1],
output = system.args[2],
result;

page.open(url, function (status) {
if (status !== 'success') {
  console.log('FAILED to load the url');
  phantom.exit();
} else {
  result = page.evaluate(function(){
      var html, doc;

      html = document.querySelector('html');

      return html.outerHTML;
  });

  if(output){

    var rendered = fs.open(output,'w');
    rendered.write(result);
    rendered.flush();
    rendered.close();

  }else{

    console.log(result);

  }
}
phantom.exit();
});

The url is http://azertyjobs.tk

I consistently get an error

ReferenceError: Can't find variable: Promise

http://azertyjobs.tk/build/bundle.js:34
http://azertyjobs.tk/build/bundle.js:1 in t
...

Ok so I figured out that ES6 Promises aren't natively supported by PhantomJS yet, so I tried various extra packages like the following https://www.npmjs.com/package/es6-promise and initiated the variable as such:

var Promise = require('es6-promise').Promise

However this still produces the same error, although Promise is now a function. The output of the webpage is also still as good as empty (obviously..)

Now I'm pretty oldschool, so this whole client-side rendering stuff is kind of beyond me (in every aspect), but maybe someone has a solution. I've tried using a waiting script too, but that brought absolutely nothing. Am I going about this completely wrong? Is this even possible to do?

Much appreciated!

Ludwig

3

There are 3 answers

3
Vaviloff On BEST ANSWER

I've tried the polyfill you linked and it didn't work, changed for core.js and was able to make a screenshot. You need to inject the polyfill before the page is opened:

page.onInitialized = function() {
    if(page.injectJs('core.js')){
        console.log("Polyfill loaded");
    }    
}

page.open(url, function (status) {

    setTimeout(function(){
        page.render('output.jpg');
        phantom.exit();
    }, 3000);

});
1
Ryan Wheale On

What you need to understand is that there are several parts of a page loading. First there is the HTML - the same thing you see when you "view source" on a web page. Next there are images and scripts and other resources loaded. Then the scripts are executed, which may or may not result in more content being loaded and possible modifications to the HTML.

What you must do then is figure out a way to determine when the page is actually "loaded" as the user sees it. PhantomJS provides a paradigm for you to waitFor content to load. Read through their example and see if you can figure out a method which works for you. Take special note of where they put phantom.exit(); as you want to make sure that happens at the very end. Good luck.

1
Codebling On

Where (how) are you trying to initialise Promise? You'll need to create it as a property of window, or use es6-promise as a global polyfill, like this require('es6-promise').polyfill(); or this require('es6-promise/auto'); (from the readme).

Also, what do you mean by "capture"? How If you're trying to scrape data, you may have better luck using X-ray. It supports Phantom, Nightmare and other drivers.

Keep in mind also that React can also be server rendered. React is like templating, but with live data bindings. It's not as complicated as you're making it out to be.