1

功能

为打包后的文件提供传统浏览器兼容性支持,实现对现代构建和传统构建的控制,提高项目的兼容性和稳定性。

  • 能够实现将现代JavaScript代码向传统浏览器兼容的代码转换,实现降级处理
  • 能够通过配置的目标浏览器环境,对现代JavaScript代码进行转换,生成对应目标浏览器的兼容代码
  • 能够通过配置,自主控制是否生成传统构建、现代构建的代码产物

实现原理

  • 通过babel 进行代码转换,根据目标浏览器环境进行自动降级处理
  • 通过相关插件配置,生成 polyfill chunk,确保在传统浏览器能够正常运行
  • 处理现代浏览器和传统浏览器的兼容性,生成对应的构建产物

Enter:viteLegacyPlugin

通过 viteLegacyPlugin 函数初始化插件 @vite/plugin-legacy, 具体分析下都做了哪些工作。

图片

整个入口函数最终导出了legacyConfigPlugin、legacyGenerateBundlePlugin、legacyPostPlugin 三个 Plugin。

  • legacyConfigPlugin:处理配置相关任务
  • legacyGenerateBundlePlugin:用于生成传统浏览器的兼容代码产物
  • legacyPostPlugin:用于构建后阶段处理传统浏览器兼容性相关的任务,构建完成后执行,用于处理后续操作(压缩、优化、额外处理逻辑等)

Plugin1:legacyConfigPlugin

图片

这里的处理非常简单,如果配置了 renderLegacyChunks 不为 false,则对 config.build.target 进行配置,当 modernTargets 有值,则 config.build.target 以外部传入配置的目标范围做现代浏览器的打包构建,否则则采用默认的策略 进行打包构建。

  • 配置 config.build 中的 cssTarget、target
  • 判断采用默认构建目标( 'es2020', 'edge79', 'firefox67', 'chrome64', 'safari12')或者传入的 modernTargets 配置构建目标
  • 通过 define 定义环境变量 import.meta.env.LEGACY 并返回
  • configResolved 中做相应配置提醒

Plugin2:legacyGenerateBundlePlugin

该插件配置了 apply 属性为 build,则在vite构建产物时处理:
图片

  • 不是 legacy 类型则走 modern polyfill 构建
  • renderChunk 处理后的polyfill信息单独构建 polyfill 并合并到原有的 bundle 信息中,在后续的实际产物打包到一起

Plugins:legacyPostPlugin

通过配置 enforce 为 post,来进行构建最后阶段的执行。

configResolved

在该钩子中对产物 chunk 进行 legacy 文件命名 (getLegacyOutputFileName)。

图片

  • 配置targets, 优先取 legacy-plugin 透传的 targets,其次是项目根目录查找,兜底默认预设目标
  • 处理 chunk 文件,变更为[name]-legacy-[hash].js (getLegacyOutputFileName)
  • 创建 legacy rollupOptions.output 配置,在最终 build 阶段进行 transform 和 moduleParsed 处理, 以 system format 被 renderChunk 处理

renderChunk

通过 babel transform 做 AST 分析后,根据 targets 生成的 polyfill 记录信息在 legacyPolyfills,用于后续 bundle 生成。

图片

  • 非 legacy chunk 时,如果配置了 modernPolyfills 为 true, 就通过 detectPolyfills 方法对代码通过 @babel/preset-env 进行检测,最终将所需 polyfill 模块名添加到对应的 modernPolyfills Map中
  • 如果配置了polyfills 不为 false, 则会通过 createBabelPresetEnvOptions 函数根据 targets 创建 @babel/preset-env 配置

transformIndexHtml

主要处理资源引用以及现代浏览器的能力判断

  • 对打包产物在入口html的资源引用进行处理
  • 对现代浏览器能力的判断

这里我们主要来看下是如何判断现代浏览器能力的,核心代码如下:

const detectModernBrowserVarName = '__vite_is_modern_browser'
export const detectModernBrowserDetector = 
  `import.meta.url;import("_").catch(()=>1);(async function*(){})().next()`
export const detectModernBrowserCode =
  `${detectModernBrowserDetector};if(location.protocol!="file:"){window.${detectModernBrowserVarName}=true}`

通过代码片段的执行,利用动态 import 、异步生成器函数和协议检查三个步骤来判断,最终给 __vite_is_modern_browser 赋值,代码执行时则 window.__vite_is_modern_browser 是否为 true 来判断是否支持现代浏览器。

generateBundle

删除非.map文件的 asset 类型 bundle。

出海应用

结构

图片

流程处理

图片

配置

legacy({
  targets: ['android <= 9', 'chrome <= 60', 'safari <=10', 'ios <= 10', 'edge <=13'],
  additionalLegacyPolyfills: ['regenerator-runtime/runtime'],
  renderLegacyChunks: true,
  polyfills: true
})

总结

无论是传统构建还是现代构建,都各有优缺点,大致如下:
image.png


哈啰技术
89 声望51 粉丝

哈啰官方技术号,不定期分享哈啰的相关技术产出。