Cannot pass module functions to Page

2019-03-01 00:00发布

问题:

I have a module called util with the methods getMutedColor and some others. getMutedColor relies on another called rand in the same module.

page.includeJs('https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.10/d3.min.js', function() {
    var util = require('./util');
    var svg = page.evaluate(pageContext.pageExec, data, meta, util);
    /** ... **/
}

I can call util.getMutedColor() just fine within this scope but in my pageContext.pageExec function, util.getMutedColor no longer exists. The util parameter is still an object, but I cannot call any of the exported methods:

TypeError: 'undefined' is not a function (evaluating 'util.getMutedColor()')

Is there a way to pass a self-contained module to a page?

回答1:

No, it is not really possible. As seen in the docs:

Note: The arguments and the return value to the evaluate function must be a simple primitive object. The rule of thumb: if it can be serialized via JSON, then it is fine.

The function that you pass to evaluate must be self-contained and the data that you pass cannot contain functions or objects that are created with new. Those are stripped.

This means that you must copy the util object completely into the pageExec function.

If util is too big to copy or if you use multiple functions of util, you can try different things:

  1. Serialize the functions and pass them as strings to the evaluate callback where you would instantiate them with new Function(string) and bind them to the util object.

    var funcNames = [];
    for(var prop in util) {
        if (util.hasOwnProperty(prop) && typeof util[prop] === "function") {
            funcNames.push(prop);
            util[prop] = util[prop].toString();
        }
    }
    util.__funcNames = funcNames; // make it a property of util
    page.evaluate(pageContext.pageExec, data, meta, util);
    

    and inside of pageContext.pageExec:

    util.__funcNames.forEach(function(funcName){
        util[funcName] = new Function(util[funcName]);
    });
    
  2. Have only a single pageContext function which contains everything and you can use a switch case with a parameter that is passed every time to evaluate to select the proper function inside of the single pageContext function.

  3. Use page.injectJs to inject the utils. It would be necessary to namespace the utils in by checking if window is present with something like that:

    var util = {...};
    if (window) {
        window.util = util
    } else if(module) {
        module.exports = util;
    }
    

    Probably there are better scripts out there to do this depending on the environment.



标签: phantomjs