问题
I'm trying to run some browser code in Node.js in order to simplify testing (deasync is the reason).
Currently, I use jsdom to parse the only html file. At first, I was trying to make it also load the scripts, which are linked in <script>
tags. But due to some stupid issues with relative file paths I have switched to passing a list of absolute paths to the script files as a parameter to jsdom.env
function.
Now I'm pretty sure that jsdom finds my scripts properly, but I cannot set any global variables in them. Also, console.log
is not working there. I have checked that if I load a script through require
command, then both global variables and console output are working as expected. But with jsdom.env
they are not working.
I use Node.js version 4.2.6 on Windows 7. The following 4 files are used to reproduce the issue (they are located in the same directory):
main.js - started with node.exe:
var jsdom = require("jsdom");
require("./module");
var window = jsdom.env(
'<p><a class="the-link" href="https://github.com/tmpvar/jsdom">jsdom!</a></p>',
[
// "http://code.jquery.com/jquery.js",
"file://" + __dirname + "/myjquery.js",
"file://" + __dirname + "/script.js"
],
undefined,
function(error, window) {
console.log("contents of a.the-link:", window.$("a.the-link").text());
console.log(global.hello, window.hello, global.hi);
}
);
module.js:
console.log("Hi!");
global.hi = 15
script.js:
console.log("Hello!");
global.hello = 126;
myjquery.js is just jQuery downloaded from http://code.jquery.com/jquery.js.
It prints out:
Hi!
contents of a.the-link: jsdom!
undefined undefined 15
So it definitely loads myjquery.js
properly, because it is used to print the first line. Also, it sets a global variable and prints a line in the module, but it does not do it in the script.
Could you please explain why and propose a way to fix it?
回答1:
The hello
global
global
is a NodeJS thing, not a browser thing. In your script.js
, if you want to set global variables in the page (like jQuery does), you would either just declare them at global scope:
var hello = 126;
or assign them as properties on window
:
window.hello = 126;
or (at global scope and if you're not using strict mode) to this
:
this.hello = 126;
If you do any of those in script.js
, window.hello
will be set in your callback.
Seeing console.log
The JSDom page quite clearly says that to see console
output from within the window, you must do something:
// Create and configure a virtual console
var virtualConsole = jsdom.createVirtualConsole();
virtualConsole.on("log", function (message) {
console.log("console.log called ->", message);
});
// Use it in the config object
var window = jsdom.env(
'<p><a class="the-link" href="https://github.com/tmpvar/jsdom">jsdom!</a></p>',
[
// "http://code.jquery.com/jquery.js",
"file://" + __dirname + "/myjquery.js",
"file://" + __dirname + "/script.js"
],
{ virtualConsole: virtualConsole }, // <== Here
function(error, window) {
console.log("contents of a.the-link:", window.$("a.the-link").text());
console.log(window.hello, global.hi);
}
);
Seeing errors
You mentioned that you don't see errors (which would have been useful, since global.hello = 126;
does indeed cause an error);
The JSDom documentation talks about how to listen for errors, too:
virtualConsole.on("jsdomError", function (error) {
console.error(error.stack, error.detail);
});
That just goes after our console
setup above. Now, errors in script.js
and such are reported to the Node console.
Relative paths
You'd mentioned some trouble with relative paths. Just FYI, this worked just fine for me:
var window = jsdom.env(
'<p><a class="the-link" href="https://github.com/tmpvar/jsdom">jsdom!</a></p>',
[
"myjquery.js",
"script.js"
],
{ virtualConsole: virtualConsole },
function(error, window) {
console.log("contents of a.the-link:", window.$("a.the-link").text());
console.log(window.hello, global.hi);
}
);
...as did using "http://code.jquery.com/jquery.js"
directly.
Side note: The JSDom documentation around virtualConsole
shows the configuration object slightly different than I do above. I did this:
var window = jsdom.env(
// ...
{ virtualConsole: virtualConsole },
// ...
);
whereas their docs show this:
var window = jsdom.env(
// ...
{ virtualConsole },
// ...
);
The reason for the difference is that you were using ES5 only in your code, so I translated. They're using an ES2015 (ES6) feature that lets you specify a property using a shorthand if the property name and the name of the variable you're taking it from are the same. So { virtualConsole }
and { virtualConsole: virtualConsole }
are exactly the same thing in ES2015, but the former doesn't work in ES5 and earlier. If you're using the current Node and JSDom, you can use that ES2015 feature.
来源:https://stackoverflow.com/questions/35099863/setting-global-variables-in-a-script-loaded-by-jsdom