摆脱Vite + Vue 3白屏困扰的终极指南 | 深入探索,轻松应对插图

前言

在项目中全面采用 Vue 3 时,自然而然地选择了与之配套的 Vite 打包工具。在这个过程中,我们开始使用许多新的语法,比如近几年变得流行的可选链语法。虽然这个语法已经被纳入声呐的代码规范中,但是 Vite 在打包时并不会进行过多的兼容处理。

默认情况下,Vite 的兼容目标是支持原生 ES 模块、原生 ES 模块动态导入以及 import.meta 的浏览器。官方预设的兼容目标包括:

  • ES2020
  • Edge 88
  • Firefox 78
  • Chrome 87
  • Safari 14

而我们使用的可选链语法正是 ES2020 版本引入的。因此,Vite 在打包后并不会执行任何兼容处理,这导致一些浏览器版本较低的设备打开网站时会出现白屏的情况。

显然,对于大多数目标用户来说,采用这么新的语法可能有些过于超前。因此,有必要进行一些适当的兼容处理。

教程

只兼容到ES2015(ES6)

由于vue3的特性,最低也只能兼容只是es2015的浏览器,因为再低点没有proxy了,vite官方也提供了方便的配置项:

// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()],
  build: {
    target: "es2015"
  }
})

通过指定打包后的目标版本就行了,vite会将新的语法通过esbuild进行转换,此时我们在支持esm的浏览器上访问时正常的。(注意只有打包后的代码才会有效)

这里我特意大写了 支持ESM的浏览器是有用意的,我们看下打包后的index.html文件。

摆脱Vite + Vue 3白屏困扰的终极指南 | 深入探索,轻松应对插图1

可以看到script标签上使用了type="module"属性,显然即便我们的脚本里面已经兼容到了es2015,但是如果浏览器稍微低一点的版本的,它是支持es2015但是它不支持ESM模块啊。

我们看一下对应的支持统计:

es2015

es6版本最经典的就是proxy的支持

摆脱Vite + Vue 3白屏困扰的终极指南 | 深入探索,轻松应对插图2

ESM模块

摆脱Vite + Vue 3白屏困扰的终极指南 | 深入探索,轻松应对插图3

可以看到和es2015的支持度还是有一些差别的。

那么就进入到第二个问题,如何兼容不支持esm的浏览器

兼容ESM浏览器

官方提供了一个插件:legacy

官方对它的定义是:为打包后的文件提供传统浏览器兼容性支持

其实就是对不支持esm的浏览器的支持,同时还有语法降级处理。

先进行安装:

pnpm i @vitejs/plugin-legacy@4.1.1 terser -D

需要注意的是vite4的legacy插件最高目前是4.1.1,再高的版本只支持vite5了,使用起来会报错。

legacy插件需要依赖terser对代码进行压缩。

使用起来也很简单,我们直接引入插件并启用即可:

// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import legacy from "@vitejs/plugin-legacy";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    legacy()
  ],
  build: {
    target: "es2015"
  }
})

官方定义,在没有明确指定插件配置参数targets时,他会去读取项目中的.browserslistrc文件,我们知道这个文件是一个用于配置支持的浏览器范围的文件,通常会被Babel、Autoprefixer等插件使用。

我个人根据es2015的支持版本书写了一下内容:

Chrome >= 51
Edge >= 15
Safari >= 10
Firefox >= 54
Opera >= 38
iOS >= 10
not ie <= 11
Android >= 5
not IE <= 11

然后我们再去打包项目,可以发现index.html的脚本引入发生了变化。

摆脱Vite + Vue 3白屏困扰的终极指南 | 深入探索,轻松应对插图4

可以看到script发生了变化,除了type="module"还有一个nomodule脚本,nomodule这个属性表示在支持esm的浏览器不运行里面的代码,而不支持esm的浏览器又无法识别type="module",反而会去运行nomodule的script,从而实现了降级区分。

而降级的脚本都会携带一个legacy文本

摆脱Vite + Vue 3白屏困扰的终极指南 | 深入探索,轻松应对插图5

这个就是用于兼容不支持esm模块的浏览器降级方案。

这个方式的好处在较新的浏览器中可以使用新的特性,旧的浏览器使用legacy降级脚本,从而避免了以前打包时一刀切的兼容方式,所有的代码全部降级处理,使得一些新设备没法体现出新语法的性能。