谈Vite在Electron环境下吃花卷拉馒头的现象

缘起

在Electron的渲染进程中(也就是页面代码中),

我们常常使用process.env来携带一些环境变量,

比如HTTP服务地址的基质,本地静态资源的路径等

这样做主要有两个目的

一个是方便开发者写多个配置环境变量的文件,区分生产环境、测试环境和开发环境

另一个是主进程和渲染进程共享一套环境变量,全局任何一个地方都随取随用,非常方便

正因为如此,一般的编译工具都不会动用户的process对象

但Vite不一样,Vite的作者认为Vite只是给Web(运行在浏览器中的)产品提供服务的,

所以编译时把用户的process对象吃掉没关系,甚至可以拉一个完全不一样的东西给开发者都没影响

因为Web前端开发者用不到这个对象

哎,尤雨溪完全忽略了Electron开发者的感受

现象

 用Vite创建一个Vue3项目,在入口文件中输出这两个对象

console.log(process)
console.log(process.env)

然后用Vite编译,Electron打包编译的文件,安装并启动Electron,打开调试器,

process对象的输出如下(注意process下env属性是正常的):

process.env对象的输出如下:

为什么会出现这种现象呢?我们打开Vite编译后的文件,找到目标位置,发现代码被转化成了这个样子:

  console.log(process);
  console.log({NODE_ENV: "production"});

process还是老样子,但process.env被直接转成了一个对象字面量  

原理

想来Vite这么做可能的原因是:

在process.env下加属性是Node.js开发者最常用的区分生产环境和开发环境的方案了

但浏览器环境下根本就没有process对象,那怎么办呢?

就直接粗暴的改写了开发者的代码吧

把process.env转码成了{NODE_ENV: “production”}

于是,就是现在我们看到的结果

 翻翻Vite的代码,确实找到了如下逻辑(这是最新的代码,以前我看到的跟这还不一样):

      createReplacePlugin(
        (id) =>
          !/\?vue&type=template/.test(id) &&
          // also exclude css and static assets for performance
          !isCSSRequest(id) &&
          !resolver.isAssetRequest(id),
        {
          ...defaultDefines,
          ...userDefineReplacements,
          ...userEnvReplacements,
          ...builtInEnvReplacements,
          'import.meta.env.': `({}).`,
          'import.meta.env': JSON.stringify({
            ...userClientEnv,
            ...builtInClientEnv
          }),
          'process.env.NODE_ENV': JSON.stringify(resolvedMode),
          'process.env.': `({}).`,
          'process.env': JSON.stringify({ NODE_ENV: resolvedMode }),
          'import.meta.hot': `false`
        },
        !!sourcemap
      ),

就是这段代码转写了我们业务代码中的process.env

元凶找到,就有相应的解决方案了

方案

最老的版本的Vite,只能这样做才可以

eval(['process',"env"].join('.'))

当前版本的Vite,这样写也可以的:

process["env"]

官方推荐使用的方式

import.meta.env

但我不推荐这样用,这种写法拿到的env对象的内容和实际的内容是有出入的。