• 首页
  • vue
  • TypeScript
  • JavaScript
  • scss
  • css3
  • html5
  • php
  • MySQL
  • redis
  • jQuery
  • vite + vue3 + ts 安装 @vitejs/plugin-legacy 兼容旧版浏览器

    安装浏览器兼容插件@vitejs/plugin-legacy

    npm install @vitejs/plugin-legacy -D
    

    必须安装Terser压缩器,因为插件Legacy,使用Terser进行压缩。

    npm install terser -D
    

    打开vite.config.ts,配置插件。

    import { fileURLToPath, URL } from "node:url";
    import { defineConfig } from "vite";
    import vue from "@vitejs/plugin-vue";
    import legacy from "@vitejs/plugin-legacy";
    
    import AutoImport from "unplugin-auto-import/vite";
    import Components from "unplugin-vue-components/vite";
    import { ElementPlusResolver } from "unplugin-vue-components/resolvers";
    import Icons from "unplugin-icons/vite";
    import IconsResolver from "unplugin-icons/resolver";
    
    import path from "path";
    const pathSrc = path.resolve(__dirname, "src");
    
    export default defineConfig({
      plugins: [
        vue(),
        legacy({
          targets: ["defaults", "not IE 11"],
        }),
        AutoImport({
          resolvers: [
            ElementPlusResolver(),
            IconsResolver({
              prefix: "Icon",
            }),
          ],
          dts: path.resolve(pathSrc, "auto-imports.d.ts"),
        }),
        Components({
          resolvers: [
            ElementPlusResolver(),
            IconsResolver({
              enabledCollections: ["ep"],
            }),
          ],
          dts: path.resolve(pathSrc, "components.d.ts"),
        }),
        Icons({
          autoInstall: true,
        }),
      ],
      build: {
        target: "modules", // 默认 "modules"
      },
      resolve: {
        alias: {
          "@": fileURLToPath(new URL("./src", import.meta.url)),
        },
      },
    });
    

    build.target选项是构建生产环境下代码的设置,此值应该与兼容legacy.target兼容。


    build.target

    • 类型:string|string[]
    • 默认:modules
    • 作用:浏览器兼容性
    build.target 取值范围
    modules设置最终构建的浏览器兼容目标。默认值是一个 Vite 特有的值:modules,这是指支持原生 ES 模块的浏览器。
    esnext自 es2015 以后,所有 es 的版本,2022年6月将要发布的标准。即假设有原生动态导入支持,并且将会转译得尽可能小:如果 build.minify 选项为terseresnext将会强制降级为es2019。其他情况下将完全不会执行转译。
    自定义版本自定义目标也可以是一个 ES 版本(例如:es2015)、一个浏览器版本(例如:chrome58)或是多个目标组成的一个数组。最低:es2015

    转换过程将会由 esbuild 执行,并且此值应该是一个合法的 esbuild 目标选项。注意:如果代码包含不能被 esbuild 安全地编译的特性,那么构建将会失败。


    build.minify

    • 类型:boolean|terser|esbuild
    • 默认:esbuild

    设置为false可以禁用最小化压缩,或是用来指定使用哪种压缩器。默认为esbuild,它比terser快 20-40 倍,压缩率只差 1%-2%。

    注意,在 lib 模式下使用'es'时,build.minify 选项将失效。

    当设置为terser时,必须先安装Terser



    ECMAScript 2015

    ECMAScript 2015(又称为 es6),在 2015 年发布,有重大意义的里程碑。发展到现在的版本:es2015、es2016、es2017、es2018、es2019、es2020。对于浏览器实现支持ES6+ JavaScript 语法的浏览器,被称呼为现代浏览器。不支持的,称呼为传统旧版浏览器。

    es6 支持状况


    • chrome 51+,全部支持。2016 年开始支持。
    • Safari 10+,全部支持。2016 年开始支持。
    • Opera 38+,全部支持。2016 年开始支持。
    • firefox 54+,全部支持。2017 年开始支持。
    • ie 6 ~ 10,都不支持。ie 11 支持部分语法。
    • edage 15+,全部支持。2017 年开始支持。
    • QQ Browser 10.4+,全部支持。2020 年开始支持。
    • Android Browser 5+,全部支持。它是 2015 年开始支持。
    • Safari on iOS 10+,全部支持。它是 2016 年开始支持。

    ES6+ JavaScript 语法的支持率,在移动端,支持广泛,在 pc 端,略逊色。


    浏览器内核

    浏览器内核(Rendering Engine),是指浏览器最核心的部分,负责对网页语法的解释(对HTML、XML、图像、JavaScript、CSS 等等)并渲染(显示)网页。所以,通常所谓的浏览器内核也就是浏览器所采用的渲染引擎,渲染引擎决定了浏览器如何显示网页的内容以及页面的格式信息。不同的浏览器内核对网页编写语法的解释也有不同,因此同一网页在不同的内核的浏览器里的渲染(显示)效果也可能不同,这也是网页编写者需要在不同内核的浏览器中测试网页显示效果的原因。

    Webkit苹果公司研发内核,也是苹果的 Safari 浏览器使用的内核。Webkit 开放源代码。
    Chromium谷歌 Google 公司开发主导。Chromium 内核是基于开源 webkit 内核,是 Webkit 内核的分支之一。Chromium 开放源代码。2020 年之前 Chrome 浏览器,采用此内核,2020 年之后,Edge 浏览器,采用 Chromium 内核。而谷歌 Chrome 浏览器改用 Blink 内核。
    Blink由 Google 和 Opera Software 开发,在 Webkit 的基础上演变而来,应用于 Chromium 项目上,是 Webkit 内核的分支之一。现在 Chrome 浏览器、Opera 浏览器,都使用Blink内核
    Trident微软 IE 浏览器内核,并沿用到 IE11。Trident 实际上是一款开放的内核,有许多采用 IE 内核而非IE的浏览器(壳浏览器)涌现。
    GeckoMozilla 基金会的火狐浏览器 Mozilla Firefox 内核。
    Presto欧朋 Opera 浏览器内核。现在已废弃。Opera 浏览器改用 Blink 内核。



    vite 运行需要浏览器基准

    使用 vite 构建 vue3 项目,其构建的生产环境,需要目标浏览器支持现代 JavaScript 语法:原生 ES 模块(es2015)、原生 ESM 动态导入(es2020)以及import.meta(es2020)。即浏览器需要支持type="module" script 标签、export导出、import静态导入、import()动态导入、import.meta元属性。

    浏览器版本要求:

    • Chrome >=87
    • Firefox >=78
    • Safari >=13
    • Edge >=88

    若构建的项目,绝大部分客户能满足上述要求,只有非常很少部分用户不能满足条件,那么需要@vitejs/plugin-legacy插件来做兼容,它会自动生成兼容性(legacy)chunk 以及相应的 ES 语言功能的polyfill


    支持原生 ESM script 标签


    支持原生 ESM 动态导入 import()


    import.meta 元属性



    vite 使用create-vite搭建的项目:

    • vue、vue-ts
    • react、react-ts
    • vanilla、vanilla-ts
    • preact、preact-ts
    • lit、lit-ts
    • svelte、svelte-ts


    vue3 兼容性

    vue2 项目,浏览器兼容,最低 ie8,vue3项目,不兼任何版本 ie。vite + vue3 搭建的项目,除去上面要求的 vite 现代浏览器标准之外,vue3 要求浏览器必须支持Proxy。Element Plus 也放弃了对 ie 支持,并且要求浏览器支持ES2018ResizeObserver

    Proxy 兼容性

    Proxy 兼容性,浏览器支持状况



    移动 APP 应用中,vue3 支持率

    IOS:9.3 是 2016 年发布,现在市场上 10.0 以上;安卓 5.0 是 2015 年发布的,现在市场上也都在 5.0 以上。所以客户端对 vue3,支持率约 99%,不支持约 1%。对于不支持的,使用@vitejs/plugin-legacy做兼容性处理。


    ESM 模块化

    历史上,JavaScript 一直没有模块(module)体系,无法将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来。其他语言都有这项功能,比如 Ruby 的require、Python 的import,甚至就连 CSS 都有@import,但是 JavaScript 任何这方面的支持都没有,这对开发大型的、复杂的项目形成了巨大障碍。开发社区制定了一些模块加载方案:用于服务器nodejs环境,有CommonJS;用于浏览器环境,有AMD等。

    SystemJS诞生于 2015 年,那个时候 ES Module 还未成为标准,在浏览器端只能通过 requirejs、seajs 等方案实现模块加载,随着 npm 在前端界的流行,一个项目中可能存在多种模块规范,所以我认为 SystemJS 最初诞生的目的是为了做一个通用的模块加载器,在浏览器端实现对 CommonJS、AMD、UMD 等各种模块的加载。后来,随着 ES6 的普及,越来越多的浏览器开始支持 ES Module,SystemJS 能够跑在旧版本的浏览器当中。


    es2015(ES6)增加了模块化的功能(ESM),支持 nodejs 环境和浏览器环境。ESM(模块功能):

    • export:用于规定模块的对外接口。
    • import:用于输入其他模块提供的功能。
    • module:用于浏览器加载。在 HTML 中加载使用传统的 JavaScript 脚本,加载ES6模块,就得在 script 标签上增加type="module"属性
    <script type="module">
      import foo from '../foo.js';
      import bar from '../bar.js'
      
      foo(bar);
      //... to do
    </script>
    



    @vitejs/plugin-legacy

    在不支持ESM模块化的旧版浏览器中,vite 官方插件@vitejs/plugin-legacy来实现。默认情况下,其所做的事:

    • legacy chunk:使用@babel/preset-env,把开发项目中,用es6+语法书写的代码,转变为相应es5语法的兼容性代码(legacy chunk),仍然支持代码拆分。使之能在SystemJS模块化功能下,运行。兼容版的legacy chunk只会在不支持原生 ESM 的浏览器中进行按需加载。
    • polyfill chunk:生成polyfill代码块。运行于目标浏览器,所需要polyfill;以及 SystemJS 运行时,所需polyfillpolyfill:术语。一块代码(通常是 Web 上的 JavaScript),用来为旧浏览器提供它没有原生支持的较新的功能。是针对目标浏览器而实现的模拟环境代码。
    • script nomodule:将<script nomodule>标记,注入生成的 HTML 中,以便仅在不支持原生 ESM 的浏览器中,有条件地加载polyfill chunk和legacy chunk。
    • import.meta.env.LEGACY:注入import.meta.env.LEGACYenv变量,在旧版生产版本中,其值为true,在所有其他情况下,其值为false



    那些版本较低的浏览器不支持 ES6+的语法和新 API,而@babel/preset-env只转换新的 JavaScript 句法,不转换新的 API,比如 Proxy、Symbol、Promise等全局对象,以及一些定义在全局对象上的方法都不会转码。


    配置选项

    targets

    设置目标浏览器兼容的范围。

    • 类型:string|string[]|{[key: string]: string}
    • 默认值:defaults

    如果显式设置,则将其传递给@babel/preset-env。该选项与Browserslist 兼容。默认值:defaults,是 Browserslist 推荐的值。


    Browserslist 在不同前端工具之间共享目标浏览器和 Node.js 版本的配置(不区分大小写)。
    defaultsBrowserslist 的默认浏览器(> 0.5%,last 2 versions,Firefox ESR,not dead)。
    dead没有官方支持或更新的浏览器 24 个月。现在它是IE 11, IE_Mob 11, BlackBerry 10, BlackBerry 7, Samsung 4 和 OperaMobile 12.1,所有版本的 Baidu 浏览器。
    not ie <= 8从以前的查询中排除 IE 8 及更低版本。
    按使用统计
    最新版本
    • last 2 versions:每个浏览器的最后 2 个版本。
    • last 2 Chrome versions: Chrome 浏览器的最后 2 个版本。
    • last 2 major versionslast 2 iOS major versions:最近 2 个主要版本的所有次要/补丁版本。
    浏览器版本
    • iOS 7:直接iOS浏览器版本7。
    • Firefox > 20: Firefox 20 之后的版本。还可以使用>=<<=。它也适用于 Node.js。
    • ie 6-8:选择包含范围的版本。
    • Firefox ESR:最新的 Firefox 扩展支持版本。
    • PhantomJS 2.1PhantomJS 1.9:选择类似于 PhantomJS 运行时的 Safari 版本。
    Node.js 版本
    • node 10and node 10.4:选择最新的 Node.js10.x.x 或10.4.x发布。
    • last 2 node versions:选择 2 个最新的 Node.js 版本。
    • last 2 node major versions:选择 2 个最新的主要版本 Node.js 版本。
    • current node: Browserslist 现在使用的 Node.js 版本。
    • maintained node versions:所有 Node.js 版本,仍然由 Node.js 基金会维护。
    extends browserslist-config-mycompany从 browserslist-config-mycompanynpm 包中获取查询。
    supports es6-module支持特定功能的浏览器。es6-module 这是 Can I Use 页面 feat 的 URL 中的参数。可以在 caniuse-lite/data/features 找到所有可用功能的列表。
    browserslist configBrowserslist 配置中定义的浏览器。在差异服务中很有用,可以修改用户的配置,例如 browserslist config and supports es6-module.
    since 2015or last 2 years自 2015 年以来发布的所有版本(也since 2015-03since 2015-03-10)。
    unreleased versionsunreleased Chrome versionsalpha 和 beta 版本。


    在项目根目录中,查询选择了哪些浏览器。

    npx browserslist
    



    polyfills

    • 类型:string|string[]
    • 默认值:true

    默认情况下,根据目标浏览器范围和最终包中的实际使用情况(通过@babel/preset-env检测到useBuiltIns:'usage')生成一个 polyfills 块。

    设置为字符串列表,以显式控制,要包含的 polyfill。有关详细信息,请参阅Polyfill说明符。

    设置为false,避免生成polyfill并自行处理(仍会生成带有语法转换的遗留块)。


    additionalLegacyPolyfills

    • 类型:string[]

    将自定义导入方式添加到legacypolyfill块中。由于基于使用的polyfill检测,仅涵盖 ES 语言功能,因此可能需要使用此选项,手动指定额外的DOM APIpolyfill

    注意:如果现代和legacy块都需要额外的polyfill,可以简单地在应用程序源代码中导入它们。


    ignoreBrowserslistConfig

    • 类型:boolean
    • 默认值:false

    @babel/preset-env自动检测browserslist 配置源package.json中 browserslist 字段,或者.browserslistrc文件中的配置。设置为false,忽略这些来源。


    modernPolyfills

    • 类型:boolean|string[]
    • 默认值:false

    默认为false,表示禁用此选项。

    若启用此选项,将为现代构建(针对具有原生 ESM 支持的浏览器),生成一个单独的polyfill块。

    请注意,不建议使用该true值(使用自动检测),因为core-js@3它支持的所有前沿特性在polyfill包含中非常具有侵略性。即使针对原生 ESM 支持,它也会注入 15kb 的 polyfill!

    设置为字符串列表,以显式控制要包含的polyfill。有关详细信息,请参阅Polyfill说明符。

    如果您不完全依赖最前沿的运行时特性,那么避免在现代构建中完全使用polyfill并不难。或者,考虑使用像 Polyfill.io 这样的按需服务,仅根据实际浏览器用户代理注入必要的polyfill(大多数现代浏览器不需要任何东西!)。


    renderLegacyChunks

    • 类型:boolean
    • 默认值:true

    设置为false,禁用旧版块。这仅在您使用 modernPolyfills 时才有用,它本质上,允许您使用此插件将 polyfills 注入到现代构建中:

    import legacy from '@vitejs/plugin-legacy'
    
    export default {
      plugins: [
        legacy({
          modernPolyfills: [
            /* ... */
          ],
          renderLegacyChunks: false
        })
      ]
    }
    


    externalSystemJS

    • 类型:boolean
    • 默认值:false

    默认为false。启用此选项,将在 polyfills-legacy 块内部,排除systemjs/dist/s.min.js


    import()动态导入

    在支持原生 ESM 但不支持动态导入的浏览器中(例如 Legacy Edge),legacy chunk,提供了一种方法,使支持原生import()动态导入方法。此功能通过,在SystemJs运行时,注入检查加载legacy bundle,来工作。有以下缺点:

    • 在所有 ESM 浏览器中,Modern bundle 总被加载。
    • 在没有动态导入的情况下,在浏览器中 Modern bundle 抛出 SyntaxError。


    Polyfill

    polyfill配置选项polyfillsmodernPolyfills,可以是下面任意一种:

    import legacy from '@vitejs/plugin-legacy'
    
    export default {
      plugins: [
        legacy({
          polyfills: ['es.promise.finally', 'es/map', 'es/set'],
          modernPolyfills: ['es.promise.finally']
        })
      ]
    }
    


    Content-Security-Policy

    在 Safari 10.1 nomodule 修复、SystemJS 初始化和动态导入中,需要内置 scripts legacy。如果您有严格的 HTTP Content-Security-Policy(CSP)策略要求,则需要将相应的哈希添加到您的script-src列表中:

    • sha256-MS6/3FCg4WjP9gwgaBGwLpRCY6fZBgwmhVCdrPrNf3E=
    • sha256-tQjf8gvb2ROOMapIxFvFAYBeUJ0v1HCbOcSmDNXGtDo=
    • sha256-BoFUHKsYhJ9tbsHugtNQCmnkBbZ11pcW6kZguu+T+EU=
    • sha256-A18HC3jLpyEc9B8oyxq/NBFCyFBJFSsRLt0gmT9kft8=

    这些值(不带 sha256-前缀)也可以通过以下方式检索

    const { cspHashes } = require('@vitejs/plugin-legacy')
    

    当使用regenerator-runtimepolyfill时,它会尝试使用globalThis对象来注册自己。如果globalThis不可用(它相当新且未被广泛支持,包括 IE 11),它会尝试执行Function(...)违反 CSP 的动态调用。为了避免动态eval,在没有globalThis时,考虑把core-js/proposals/global-this,添加到additionalLegacyPolyfills来定义它。