官术网_书友最值得收藏!

Specifying a path for external scripts

In this recipe, we will introduce the libraryPath property on the phantom object and discuss how to use it to control the source of scripts that are loaded in the runtime via injectJs.

Getting ready

To run this recipe, we will need at least one injectable script and the script that we want to run.

The script in this recipe is available in the downloadable code repository as recipe03.js under chapter02. If we run the provided example script, we must change to the root directory for the book's sample code.

How to do it…

Consider the following script:

console.log('Initial libraryPath: ' + phantom.libraryPath);

phantom.libraryPath = phantom.libraryPath.replace(/chapter02$/,
  'lib');

console.log('Updated libraryPath: ' + phantom.libraryPath);

var isInjected = phantom.injectJs('hemingway.js');

if (isInjected) {
  console.log('Script was successfully injected.');
  console.log('Give me some Fibonacci numbers! ' +
    fibonacci(Math.round(Math.random() * 10) + 1));

  phantom.exit();
} else {
 console.log('Failed to inject script.');
 phantom.exit(1);
}

Given the preceding code block, enter the following at the command line:

phantomjs chapter02/recipe03.js

Our output should look like the following:

Initial libraryPath: /Users/robf/phantomjs-cookbook/chapter02
Updated libraryPath: /Users/robf/phantomjs-cookbook/lib
Script was successfully injected.
Give me some Fibonacci numbers! 0,1,1,2,3,5,8,13,21,34

How it works…

The injectJs method on the phantom object can take a script from the filesystem and inject it into the current execution context. It works by loading the specified file (relative to the current libraryPath), interpreting it, and applying the interpreted script (global variables and all) to the current context; this operates in much the same way as JavaScript is imported onto a web page via a script tag. We should use injectJs to import scripts that do not conform to the CommonJS module proposal.

Tip

The CommonJS module proposal is a specification for loading code that minimizes pollution of the global scope or namespace in a JavaScript program by providing an exports object (within the module) where we can attach our public properties and methods, and a global require method that we can use to import those modules. For more information about the CommonJS module proposal, see http://wiki.commonjs.org/wiki/Modules.

Our preceding example script performs the following actions:

  1. We write the current base path for library scripts to the console using the phantom.libraryPath property.
  2. We update the default libraryPath (the script's working directory) to be our intended target directory. Then, we write that to the console as well.
  3. We import a script using phantom.injectJs, passing (in our case) only the filename to the method. The method returns true if the script imports successfully and false if it does not; this Boolean value is assigned to our variable, isInjected.
  4. If the script imports successfully, we call the fibonacci function that was imported from the script and write its results to the console. Then, we exit from PhantomJS. Otherwise, we exit PhantomJS with an error message.

There's more…

As mentioned in the preceding section, a call to phantom.injectJs is functionally equivalent to a script tag on a web page—code is read from the target, passed through the interpreter, and applied to the execution context. The libraryPath property plays an important role here because it provides the path on the filesystem that will be used to resolve any relative paths requested through phantom.injectJs.

Injectable scripts can be stored in any readable location on the filesystem. The key requirement of injectJs is that it can resolve the provided path and interpret the target as a syntactically correct JavaScript file.

phantom.libraryPath

The libraryPath property on the phantom object is a simple one—it is a string that holds the value for the absolute path that will be used to resolve scripts that are injected into the execution context. The property on the phantom object is read/write, so we can update it at any time during our script. However, it must be an absolute path. We can change libraryPath to be a relative path, but methods that depend on it (for example, injectJs) will fail. We can update libraryPath through a simple assignment, for example:

phantom.libraryPath = '/path/to/libraries';

phantom.injectJs

The injectJs method on the phantom object is one way that we can import external scripts into our current execution context. As mentioned in the discussion of our example script, calls to phantom.injectJs take a string as an argument, and that string should be a reference to a file on the filesystem, either as a filename, a relative path, or an absolute path. The script path argument is consumed as follows:

  • If PhantomJS can interpret the path as absolute, it will attempt to retrieve the script from that location on the filesystem.
  • If PhantomJS cannot interpret the path as absolute, it will attempt to resolve that script as relative to the specified libraryPath.

The injectJs method itself provides feedback about the loading operation in the form of its return value: true if the script was successfully injected, and false if it failed for any reason. If successfully injected, the interpreted contents of the script are applied to the outer space (and not within any webpage objects) of the PhantomJS execution context.

phantom.injectJs versus require

It is important to consider the contents of any script before importing it with injectJs. Just as scripts imported into a web page can easily "pollute" the global scope, so can scripts imported using injectJs. Put another way, if we have a variable named foo in our PhantomJS script, and then we use injectJs to import a script that also has a variable named foo, they will collide, and the most recently added value for foo will take precedence.

Compare injectJs with the global require function that assumes the target files to be CommonJS modules. Although scripts imported with injectJs have the potential to pollute the current execution context and clobber variables or function names, we have a lot more freedom to write these scripts in whatever style we choose. Contrast this with the require function, which expects to resolve an exports object with the exposed methods and properties. The require method is a safer solution, but it comes at the cost of flexibility, and it cannot take advantage of phantom.libraryPath; meanwhile, injectJs can consume scripts that otherwise target more platforms but carry more risks associated with the PhantomJS global scope. It's up to us to consider those trade-offs when designing our library scripts and how we will import them.

See also

  • The Reading a file from PhantomJS recipe
  • The Loading custom modules in PhantomJS recipe
主站蜘蛛池模板: 会昌县| 云龙县| 屯门区| 姚安县| 工布江达县| 黑河市| 广平县| 沧州市| 会同县| 长寿区| 内黄县| 卢湾区| 永德县| 定襄县| 罗甸县| 赤峰市| 罗源县| 广州市| 莆田市| 桂阳县| 儋州市| 垣曲县| 洪湖市| 沅陵县| 潞西市| 中宁县| 同心县| 杨浦区| 阳城县| 台湾省| 宜州市| 南阳市| 上虞市| 防城港市| 益阳市| 津市市| 昌乐县| 锡林浩特市| 拉萨市| 偃师市| 福清市|