为什么浏览器端webpack打包后的产物,可以使用node的process变量呢

396 阅读2分钟

为什么浏览器端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_ENVprocess.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