为什么浏览器端webpack打包后的产物,可以使用node的process变量呢。
此前这个问题一直困扰这着我,知道慢慢深入webpack的源码,慢慢的也看出了一些门道。
举个例子。
// a.js
console.log(process.env, process.env.NODE_ENV, process.env.NODE_ENV1)
// 打印 {} development development2
我们可以看到
1、process这个值是存在的,但是window.process里面并没有这个属性,
2、process.env打印出来只有一个空{}, 而且它的原型链下面也没有我们需要的属性(NODE_ENV、NODE_ENV1)
// webpack.config.js
plugins: [
new EnvironmentPlugin({
NODE_ENV: 'development',
NODE_ENV1: 'development2',
}),
]
这是我们webpack配置的一个片段,EnvironmentPlugin(也就是webpack.EnvironmentPlugin)这个插件的作用呢就是向process.env这个对象里面追加NODE_ENV、NODE_ENV1,
但是我们在浏览器端直接打印process.env里面为什么没有呢。
让我们来看一下webpack打包后的产物。
// bundle.js
(function (process) {
console.log(process.env, "development", "development1");
}.call(this, __webpack_require__(/*! ./../node_modules/process/browser.js */ "./node_modules/process/browser.js")))
这是截取后的片段
我们发现console.log(process.env, process.env.NODE_ENV, process.env.NODE_ENV1)这句话里的process.env.NODE_ENV 和 process.env.NODE_ENV1直接被打印出来了。而process.env却没有。
事实上参数里的process是__webpack_require__("./node_modules/process/browser.js")返回的结果,
他是webpack内部实现的node process的浏览器支持的版本。
而process.env.NODE_ENV 和 process.env.NODE_ENV1通过webpack内部EnvironmentPlugin插件下面的DefinePlugin插件下面的ƒunction toCode方法在编译阶段就将值给读出来了
const toCode = (code, parser) => {
if (code === null) {
return "null";
}
if (code === undefined) {
return "undefined";
}
if (code instanceof RuntimeValue) {
return toCode(code.exec(parser), parser);
}
if (code instanceof RegExp && code.toString) {
return code.toString();
}
if (typeof code === "function" && code.toString) {
return "(" + code.toString() + ")";
}
if (typeof code === "object") {
return stringifyObj(code, parser);
}
return code + "";
};
感兴趣的也可以看看"./node_modules/process/browser.js"里面的内容
(function (module, exports) {
var process = module.exports = {};
var cachedSetTimeout;
var cachedClearTimeout;
function defaultSetTimout() {
throw new Error('setTimeout has not been defined');
}
function defaultClearTimeout() {
throw new Error('clearTimeout has not been defined');
}
(function () {
try {
if (typeof setTimeout === 'function') {
cachedSetTimeout = setTimeout;
} else {
cachedSetTimeout = defaultSetTimout;
}
} catch (e) {
cachedSetTimeout = defaultSetTimout;
}
try {
if (typeof clearTimeout === 'function') {
cachedClearTimeout = clearTimeout;
} else {
cachedClearTimeout = defaultClearTimeout;
}
} catch (e) {
cachedClearTimeout = defaultClearTimeout;
}
}())
function runTimeout(fun) {
if (cachedSetTimeout === setTimeout) {
//normal enviroments in sane situations
return setTimeout(fun, 0);
}
if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
cachedSetTimeout = setTimeout;
return setTimeout(fun, 0);
}
try {
return cachedSetTimeout(fun, 0);
} catch (e) {
try {
return cachedSetTimeout.call(null, fun, 0);
} catch (e) {
return cachedSetTimeout.call(this, fun, 0);
}
}
}
function runClearTimeout(marker) {
if (cachedClearTimeout === clearTimeout) {
//normal enviroments in sane situations
return clearTimeout(marker);
}
if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
cachedClearTimeout = clearTimeout;
return clearTimeout(marker);
}
try {
return cachedClearTimeout(marker);
} catch (e) {
try {
return cachedClearTimeout.call(null, marker);
} catch (e) {
return cachedClearTimeout.call(this, marker);
}
}
}
var queue = [];
var draining = false;
var currentQueue;
var queueIndex = -1;
function cleanUpNextTick() {
if (!draining || !currentQueue) {
return;
}
draining = false;
if (currentQueue.length) {
queue = currentQueue.concat(queue);
} else {
queueIndex = -1;
}
if (queue.length) {
drainQueue();
}
}
function drainQueue() {
if (draining) {
return;
}
var timeout = runTimeout(cleanUpNextTick);
draining = true;
var len = queue.length;
while (len) {
currentQueue = queue;
queue = [];
while (++queueIndex < len) {
if (currentQueue) {
currentQueue[queueIndex].run();
}
}
queueIndex = -1;
len = queue.length;
}
currentQueue = null;
draining = false;
runClearTimeout(timeout);
}
process.nextTick = function (fun) {
var args = new Array(arguments.length - 1);
if (arguments.length > 1) {
for (var i = 1; i < arguments.length; i++) {
args[i - 1] = arguments[i];
}
}
queue.push(new Item(fun, args));
if (queue.length === 1 && !draining) {
runTimeout(drainQueue);
}
};
function Item(fun, array) {
this.fun = fun;
this.array = array;
}
Item.prototype.run = function () {
this.fun.apply(null, this.array);
};
process.title = 'browser';
process.browser = true;
process.env = {};
process.argv = [];
process.version = '';
process.versions = {};
function noop() { }
process.on = noop;
process.addListener = noop;
process.once = noop;
process.off = noop;
process.removeListener = noop;
process.removeAllListeners = noop;
process.emit = noop;
process.prependListener = noop;
process.prependOnceListener = noop;
process.listeners = function (name) { return [] }
process.binding = function (name) {
throw new Error('process.binding is not supported');
};
process.cwd = function () { return '/' };
process.chdir = function (dir) {
throw new Error('process.chdir is not supported');
};
process.umask = function () { return 0; };
//# sourceURL=webpack:///./node_modules/process/browser.js?
})
以上测试采取webpack版本4.46.0