IndexXuan / vue-cli-plugin-vite

Use vite today, with vue-cli.
https://github.com/IndexXuan/vue-cli-plugin-vite
MIT License
391 stars 24 forks source link

vite 无法在 src/main.js 外的文件读取 .env 的环境变量 #48

Closed czs0906 closed 3 years ago

czs0906 commented 3 years ago

参考这个issue,用 vite 做了以下试验:

  1. .env.development 文件添加以下内容:
    VUE_APP_API_FOR_VUE_CLI='/vue-cli/api'
    VUE_APP_API_FOR_VITE='/vite/api'
  2. 修改 src/main.js 文件,内容如下:
    
    import Vue from 'vue'
    import App from './App.vue'

if (process.env.VITE) { console.log(process.env.VUE_APP_API_FOR_VITE) } else { console.log(process.env.VUE_APP_API_FOR_VUE_CLI) }

new Vue({ render: (h) => h(App) }).$mount('#app')

运行结果如下图,符合预期。
![ok](https://user-images.githubusercontent.com/68422863/132711905-85c20eb6-bc7d-478b-a615-080f6e6cd832.png)

但在其它 `js` 文件使用,浏览器会抛出异常: `Uncaught ReferenceError: process is not defined`。步骤如下:

1. 新建 `src/request.js`,内容如下:
``` js
import axios from 'axios'

const request = axios.create({
  baseURL: process.env.VITE
    ? process.env.VUE_APP_API_FOR_VITE
    : process.env.VUE_APP_API_FOR_VUE_CLI
})

export default request
  1. 修改 src/main.js文件,内容如下:
    
    import Vue from 'vue'
    import request from './request'
    import App from './App.vue'

Vue.prototype.$request = request

new Vue({ render: (h) => h(App) }).$mount('#app')


运行结果如下图:
![issue](https://user-images.githubusercontent.com/68422863/132711479-afde2672-7214-4b86-b3f9-d5428e7bcfc2.png)

`vue-cli` 则不会出现此问题
IndexXuan commented 3 years ago

肯定不是只有main 可用,但是我也确实遇到过,怀疑是 vite bug. @vite/client 文件晚于访问 env 就会出现这样情况。可以提供可复现的最小 demo 么.

czs0906 commented 3 years ago

这是我测试的demo vue-cli-vite-demo.zip

screetBloom commented 3 years ago

目前我们的策略是 dev 用 vite,build 还是走 Vue CLI 像这个问题可以先用 typeof 做本地兼容,保证在不改动历史逻辑的前提下先接入 vite 能力

类似👇

typeof process !== 'undefined' ? process.env.VUE_APP_ENV : 'development'

@czs0906

screetBloom commented 3 years ago

详细原因咱们可以一起定位一下,等有结论了可以在这个 issue 中讨论一波

bowencool commented 3 years ago

我的 .env 文件全都不生效。。

IndexXuan commented 3 years ago

@bowencool 这么一句没有上下文的抱怨无助于解决任何问题。我本人也在用这个插件,如果 .env 不生效,我干嘛自己不修复。肯定是又超出我认知的使用方式或其他问题引起的。

bowencool commented 3 years ago

demo.zip 我就执行了 vue add vite

IndexXuan commented 3 years ago

demo.zip 我就执行了 vue add vite

github 时代,为啥不弄个 repo ... 我下载看看你这个,但愿没有木马

IndexXuan commented 3 years ago

demo.zip 我就执行了 vue add vite

image

你这个没有任何问题,vue-cli-plugin-vite 目前不支持设置 modeyarn vite 只会读取 .env & .env.development 文件以及命令行里直接 VUE_APP_XXX=bbb yarn vite 的 env,你把你的 .env.dev 重命名为 .env.development 即可

IndexXuan commented 3 years ago

详细原因咱们可以一起定位一下,等有结论了可以在这个 issue 中讨论一波

@czs0906 @screetBloom 你俩有啥新发现么 ?我这边之前看是 /@vite/client 是用来构建 runtime window.process 对象的,晚于业务代码的话,就会有问题,但是我这边项目没复现,可能是代码依赖关系不同导致的。

bowencool commented 3 years ago

你这个没有任何问题,vue-cli-plugin-vite 目前不支持设置 modeyarn vite 只会读取 .env & .env.development 文件以及命令行里直接 VUE_APP_XXX=bbb yarn vite 的 env,你把你的 .env.dev 重命名为 .env.development 即可

可以跟 @vue/cli 保持一样吗?因为.env.dev 里写的变量有点多,所以我们现在依赖 --mode=dev--mode=online

IndexXuan commented 3 years ago

你这个没有任何问题,vue-cli-plugin-vite 目前不支持设置 modeyarn vite 只会读取 .env & .env.development 文件以及命令行里直接 VUE_APP_XXX=bbb yarn vite 的 env,你把你的 .env.dev 重命名为 .env.development 即可

可以跟 @vue/cli 保持一样吗?因为.env.dev 里写的变量有点多,所以我们现在依赖 --mode=dev--mode=online

支持,但暂时没时间。可以扩展改造下你们自己的 bin/vite,传入 —mode

screetBloom commented 3 years ago

详细原因咱们可以一起定位一下,等有结论了可以在这个 issue 中讨论一波

@czs0906 @screetBloom 你俩有啥新发现么 ?我这边之前看是 /@vite/client 是用来构建 runtime window.process 对象的,晚于业务代码的话,就会有问题,但是我这边项目没复现,可能是代码依赖关系不同导致的。

这个问题目前在我们的场景已经定位并解决,判断是 configureServer 的 res.end 阻塞了后续插件的加载 会尽快提供一个 fix 的pr修复此问题

ZenDay commented 3 years ago

@screetBloom hello,我这边项目也遇到了同样的问题,请问能给出大概的文件和代码行数在哪里吗?我似乎没看到跟 configureServer 相关的内容出现问题🤔。感谢🙏~

ZenDay commented 3 years ago

我这边对比了一下 vite 默认项目和通过这个插件生成的目标 index.html 之间的差别:

vite 生成的 index.html:

<!DOCTYPE html>
<html lang="en">
  <head>
    <script type="module" src="/@vite/client"></script>

    <meta charset="UTF-8" />
    <link rel="icon" href="/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite App</title>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="/src/main.ts"></script>
  </body>
</html>

vue-cli-plugin-vite 生成的 index.html:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="/favicon.ico">
    <title>Home Page</title>
  </head>
  <body>
    <noscript>
      <strong>We're sorry but Home Page doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
    <!-- built files will be auto injected -->
    <script type="module" src="/src/main"></script>
</body>
</html>

可以发现 vite 在 meta 标签前应该会默认插入一个 script 标签用来加载 /@vite/client 文件。而这个文件中包含了把 env 变量挂载到全局的代码。

这个 /@vite/client 文件除了在这里被引入以外,在构建 .vue 或者 .css 文件的时候,也会默认引入,这是两者都有的特性,如 App.vue 构建生成的 js 文件会有:

import { createHotContext as __vite__createHotContext } from "/@vite/client";import.meta.hot = __vite__createHotContext("/src/App.vue");var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __decorateClass = (decorators, target, key, kind) => {
  var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
  for (var i = decorators.length - 1, decorator; i >= 0; i--)
    if (decorator = decorators[i])
      result = (kind ? decorator(target, key, result) : decorator(result)) || result;
  if (kind && result)
    __defProp(target, key, result);
  return result;
};

因此所有在 App.vue 后面才访问的 env 环境变量,是都可以被访问成功的。 对应:

import Vue from 'vue'
import App from './App.vue'

if (process.env.VITE) {
  console.log(process.env.VUE_APP_API_FOR_VITE)
} else {
  console.log(process.env.VUE_APP_API_FOR_VUE_CLI)
}

new Vue({
  render: (h) => h(App)
}).$mount('#app')

但如果在 App.vue 之前就被访问的 env 环境变量,在当前场景下,是无法访问成功的(因为 /@vite/client 还没有被加载)。 对应:

import Vue from 'vue'
import request from './request'
import App from './App.vue'

Vue.prototype.$request = request

new Vue({
  render: (h) => h(App)
}).$mount('#app')

但在 vite 原生场景下,是可以访问成功的。(因为在全局 script 中加载了 /@vite/client

IndexXuan commented 3 years ago

有解法么?大佬们,PR 欢迎

ZenDay commented 3 years ago

个人觉得原因应该是 vite-plugin-html-template 的中间件和 vite 本身的 indexHtml 中间件冲突了,导致一些默认的对于 html 的处理并没有应用过来。 比较简单粗暴的解决方案是直接在 vite-plugin-html-template 中加上 <script type="module" src="/@vite/client"></script>。 更合理的解决方案可能是需要看 vite-plugin-html-template 可以怎么样应用 vite 中 indexHtml 默认的中间件,比较麻烦。。。

IndexXuan commented 3 years ago

@ZenDay 感谢 @screetBloom 帮测试下你们的这样改可以么 ?

screetBloom commented 3 years ago

@ZenDay 感谢 @screetBloom 帮测试下你们的这样改可以么 ?

好的,收到,今天我也会一起看一波

IndexXuan commented 3 years ago

v1.5.0 thanks all.