乐趣区

关于前端:vite-不支持-ie-11configureBuild-Hook-帮你定制-bundle-打包过程

前言


在 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,它会在打包的开始被调用,并且会传入两个参数 configbuilds(在前面,我会一一介绍它们)。而 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 的参数 buildconfig 是什么、以及如何利用抱有疑难。

那么,接下来,咱们带着这些疑难从源码的角度深刻理解一下 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,也能够不返回。

而后,会对用户传入 configoptions 深度拷贝并填充默认值,这是为了保障后续逻辑的失常进行,而不须要解决不存在某个选项的逻辑,相似于当初 webpack 4 减少了配置默认值一样。

const config = prepareConfig(options)

深度拷贝并填充默认值的实现很简略,首先通过 klona npm 模块来实现对 configoptions 的深度拷贝,而后通过解构赋值的形式设置默认值。

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,将用户的 configbuilds 裸露給它。

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 的调用程序。

并且,我想大家应该也留神到,当咱们设置 writefalse 的时候,此时调用的是 bundle.generate,输入的只是 codesourcemap,如果这样,咱们就须要通过 configureBuild Hook 进行 后续的文件写的操作!而且,咱们还能够在 Hook 中进行一些代码转化操作,例如应用 bable-coretransform(),这样一来就很轻松地实现了对 ie11 的反对。

写在最初

正如 configureBuild Hook 的介绍一样,它的呈现会给 vite 带来更多弱小的插件。例如,咱们能够在 configureBuild Hook 中批改 index.html 中的 <script/>,加载 polyfill.io 提供的垫片文件地址,从而实现对旧浏览器的兼容,这也是 aleclarson 大佬的 vite-plugin-legacy 的实现思路。最初,如果文中存在表白谬误或不当的中央,欢送各位同学提 Issue。

❤️ 爱心三连击

通过浏览,如果你感觉有播种的话,能够爱心三连击!!!

退出移动版