談Vite在Electron環境下吃花捲拉饅頭的現象

liulun發表於2020-12-09

緣起

在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物件的內容和實際的內容是有出入的。

相關文章