前言
在 5 分钟带你疾速读懂 vite 打包过程,源码剖析一文中,咱们介绍了 vite 打包的代码转化是通过 esbuild 来实现的。而 esbuild 是不反对 ie11 的。所以 vite 的 GitHub 上常常会呈现一些 issue 问如何反对 ie11,例如上面这个 issue:
在这个 issue 下,vite 的几位 contributor 别离讲了几种反对 ie11 的解法:
undefin 大佬:我没有这样测过,仿佛你须要在 rollupOptions
中增加 babel/polyfill
和增加 umd
格局。
在你的 vite.config.js
中增加以下代码,须要留神的是你要装置 core-js
、@babel/core
、@babel/preset-env
、@babel/runtime
。
import babel from '@rollup/plugin-babel';
export default {
rollupInputOptions: {
plugins: [
babel({
presets: [[
"@babel/preset-env",
{
"corejs": 2,
"useBuiltIns": "usage",
"targets": {"ie": "11"}
}
]]
})
],
}
}
[aleclarson]() 大佬:我做了一个插件 [vite-plugin-legacy]() 用来简略地反对旧的(浏览器)遗留。请期待 #874 合并!
其实相比拟这两位大佬的解法,大体上的思路是一样的,都是围绕 babel
来实现代码的转化、贴片操作。然而,如果较起真来,还是 aleclarson 大佬的 vite-plugin-legacy 比拟 贴心。并且,下面提及的 PR 当初曾经合并了,这代表着咱们曾经能够应用 vite-plugin-legacy 插件了!
须要留神的是,vite-plugin-legacy 的次要实现是基于
configureBuild
Hook 实现的,而configureBuild
Hook 是在 vite 的1.0.0-rc.8
版本以及之后才有的。
那么,进入明天的正题,我将带大家一起观赏一番 configureBuild
的外部现象(源码)~
configureBuild Hook 介绍
configureBuild
Hook 将会在构建性能进行任何操作之前调用。初始化构建配置时,它会被深度拷贝,便于 configureBuild
Hook 能够更轻松地对其进行检查和变异(而不须要查看所有未定义的中央)。默认会将 Rollup 的输出(input)选项裸露给 configureBuild
,使得 vite 的插件(plugin)能更好地操纵构建过程,并且,意味着更弱小的 vite 插件将成为可能。
vite 打包的大抵过程如下:
通过图例,咱们能够简略理解到
configureBuild
Hook 会在打包的最初阶段 生成文件到硬盘 时被调用。
应用 configureBuild Hook 自定义插件
首先,咱们来看一个应用 configureBuild
Hook 的插件示例:
定义插件 myPlugin.ts:
export default (): Plugin => ({configureBuild(viteConfig) {
...
return async build => {....}
}
})
这里,咱们定义了一个箭头函数,它会返回 configureBuild
函数,也就是 configureBuild
Hook,它会在打包的开始被调用,并且会传入两个参数 config
和 builds
(在前面,我会一一介绍它们)。而 configureBuild
调用会返回一个 async
函数,它会在打包的完结调用。
配置 vite.config.ts:
import myPlugin from "./myPlugin"
import type {UserConfig} from "vite"
const config: UserConfig = {
plugins: [myPlugin()
]
}
export default config
两个步骤,咱们就实现了一个的应用 configuredHook
Plugin 的简略示例。尽管,晓得了怎么定义应用 configureBuild
Hook 的 Plugin,然而大家可能会对 configureBuild
的参数 build
和 config
是什么、以及如何利用抱有疑难。
那么,接下来,咱们带着这些疑难从源码的角度深刻理解一下 configureBuild
Hook~
从源码角度意识 configureBuild Hook
configureBuild
Hook 会在 build
构建办法的开始被调用,首先会创立一个 builds
数组用于保留每个 configuredBuild
Hook 操作后果的 bundle
,并且 vite 默认打包生成的 bundle
也会放到 builds
中。
// src/node/build/index build()
const builds: Build[] = []
须要留神的是,并不是
configuredBuild
Hook 必须要返回bundle
,也能够不返回。
而后,会对用户传入 config
的 options
深度拷贝并填充默认值,这是为了保障后续逻辑的失常进行,而不须要解决不存在某个选项的逻辑,相似于当初 webpack 4 减少了配置默认值一样。
const config = prepareConfig(options)
深度拷贝并填充默认值的实现很简略,首先通过 klona npm 模块来实现对 config
的 options
的深度拷贝,而后通过解构赋值的形式设置默认值。
import klona from "klona/json";
function prepareConfig(config: Partial<BuildConfig>): BuildConfig {
const {alias = {},
assetsDir = '_assets',
assetsInclude = isStaticAsset,
assetsInlineLimit = 4096,
...
} = klona(config)
return {
...config,
alias,
assetsDir,
assetsInclude,
assetsInlineLimit,
base,
...
}
}
接下来,会从 config
中获取到 configureBuild
,因为同时可能会存在多个应用了 configureBuild
Hook
的 plugin
,所以这里取到的 configuredBuild
会是一个数组。而后,再通过 map
办法遍历 configureBuild
数组并调用每一个 configureBuild
Hook,将用户的 config
、builds
裸露給它。
const postBuildHooks = toArray(config.configureBuild)
.map((configureBuild) => configureBuild(config, builds))
.filter(Boolean) as PostBuildHook[]
能够看到,这里 vite 只是将
builds
数组裸露给了configureBuild
Hook,咱们能够选择性地操作builds
。
尽管 configBuild
Hook 呈现的目标是为了让用户能更多地操作 bundle
的打包过程,但这并不意味着它的优先级(Priority)是最高的。vite 默认打包生成的 bundle 依然会优先于它被解决,即默认的 bundle 会被搁置到 builds
数组的开始。
const rollup = require('rollup').rollup as typeof Rollup
// 默认打包生成的 bundle
const bundle = rollup({...});
builds.unshift({
id: "index",
bundle
});
到这里 builds
数组就构建好了,接下来就是遍历它,进行 bundle
的写操作(即输入到硬盘上),因为 vite 应用的是 Rollup 实现文件的打包,所以这里调用的是 bundle.write
来将文件输入到硬盘上。
for (const build of builds) {
const bundle = await build.bundle;
const {output} = await bundle[write ? 'write' : 'generate']({
dir: resolvedAssetsPath,
format: 'es',
sourcemap,
entryFileNames: `[name].[hash].js`,
chunkFileNames: `[name].[hash].js`,
assetFileNames: `[name].[hash].[ext]`,
...config.rollupOutputOptions
});
build.html = await renderIndex(output);
build.assets = output
await postBuildHooks.reduce((queue, hook) => queue.then(() => hook(build as any)),
Promise.resolve();)
}
默认状况下,write
是为 true
,即会调用 bundle.write
,将代码写入到硬盘上,这也是为什么咱们的打包后的后果会输入到 dist 目录的起因。在每次遍历 builds
的最初,vite 会调用 postBuildHooks
来应用在开始时调用 configureBuild
Hook 返回的函数(通常会是一个 Promise
),因为 postBuildHooks
是一个 Promise
数组,所以这里应用了 reduce 办法来保障 Hook 的调用程序。
并且,我想大家应该也留神到,当咱们设置 write
为 false
的时候,此时调用的是 bundle.generate
,输入的只是 code
和 sourcemap
,如果这样,咱们就须要通过 configureBuild
Hook 进行 后续的文件写的操作!而且,咱们还能够在 Hook 中进行一些代码转化操作,例如应用 bable-core
的 transform()
,这样一来就很轻松地实现了对 ie11 的反对。
写在最初
正如 configureBuild
Hook 的介绍一样,它的呈现会给 vite 带来更多弱小的插件。例如,咱们能够在 configureBuild
Hook 中批改 index.html
中的 <script/>
,加载 polyfill.io 提供的垫片文件地址,从而实现对旧浏览器的兼容,这也是 aleclarson 大佬的 vite-plugin-legacy 的实现思路。最初,如果文中存在表白谬误或不当的中央,欢送各位同学提 Issue。
❤️ 爱心三连击
通过浏览,如果你感觉有播种的话,能够爱心三连击!!!