功能
为打包后的文件提供传统浏览器兼容性支持,实现对现代构建和传统构建的控制,提高项目的兼容性和稳定性。
- 能够实现将现代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
})
总结
无论是传统构建还是现代构建,都各有优缺点,大致如下:
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。