Vite学习笔记05 - vite处理各种静态资源

3,397 阅读5分钟

图片加载

图片是项目中最经常使用到的静态资源之一,本身就包括了非常多的格式,比如png,jpeg,webp、avif、gif。当然,也包括经常用作图标的svg。这一部分我们主要讨论的是如何加载图片,也就是如何让图片在页面中正常展示。

1. 使用场景

在日常项目开发中,我们会碰到三种加载图片的场景:

(1)在HTML或者JSX中,通过img标签来加载图片,比如:

<img src="../../assets/a.png"></img>

(2)在 CSS 中通过 background 属性加载图片,如:

background: url('../../assets/b.png') norepeat;

(3)在 JavaScript 中,通过脚本的方式动态指定图片的src属性,如:

document.getElementById('hero-img').src = '../../assets/c.png'

当然,大家一般还会有别名路径的需求,比如地址前缀直接换成@assets,这样就不用开发人员手动寻址,降低开发时的心智负担。

2. 在vite中进行使用

接下来让我们在目前的脚手架项目来进行实际的编码,你可以在 Vite 的配置文件中配置一下别名,方便后续的图片引入:

pnpm install @types/node

vite.config.ts

import path from 'path';
import {defineConfig} from 'vite'

export default defineConfig({
    resolve: {
        alias: {
            "@assets": path.join(__dirname,"src/assets")
        }
    }
})

main.ts

import './style.css'
import typescriptLogo from '@assets/typescript.svg'
import { setupCounter } from './counter'

document.querySelector<HTMLDivElement>('#app')!.innerHTML = `
  <div>
    <a href="https://vitejs.dev" target="_blank">
      <img src="/vite.svg" class="logo" alt="Vite logo" />
    </a>
    <a href="https://www.typescriptlang.org/" target="_blank">
      <img src="${typescriptLogo}" class="logo vanilla" alt="TypeScript logo" />
    </a>
    <h1>Vite + TypeScript</h1>
    <div class="card">
      <button id="counter" type="button"></button>
    </div>
    <p class="read-the-docs">
      Click on the Vite and TypeScript logos to learn more
    </p>
  </div>
`

setupCounter(document.querySelector<HTMLButtonElement>('#counter')!)

这样我们还是能够正常的在页面中正常的访问src/assets下面的图片资源

图片.png

2. 加载SVG图片

刚才我们成功地在 Vite 中实现了图片的加载,上述这些加载的方式对于 svg 格式来说依然是适用的。不过,我们通常也希望能将 svg 当做一个组件来引入,这样我们可以很方便地修改 svg 的各种属性,而且比 img 标签的引入方式更加优雅。

SVG 组件加载在不同的前端框架中的实现不太相同,社区中也已经了有了对应的插件支持:

现在让我们在 React 脚手架项目中安装对应的依赖:

pnpm i vite-plugin-svgr -D

然后需要在 vite 配置文件添加这个插件:

// vite.config.ts
import svgr from 'vite-plugin-svgr';

{
  plugins: [
    // 其它插件省略
    svgr()
  ]
}

随后注意要在 tsconfig.json 添加如下配置,否则会有类型错误:

{
  "compilerOptions": {
    // 省略其它配置
    "types": ["vite-plugin-svgr/client"]
  }
}

接下来让我们在项目中使用 svg 组件:

import { ReactComponent as ReactLogo } from '@assets/icons/logo.svg';

export function Header() {
  return (
    // 其他组件内容省略
     <ReactLogo />
  )
}

回到浏览器中,你可以看到 svg 已经成功渲染:

图片.png

JSON加载

Vite 中已经内置了对于 JSON 文件的解析,底层使用@rollup/pluginutilsdataToEsm 方法将 JSON 对象转换为一个包含各种具名导出的 ES 模块,使用姿势如下:

import { version } from '../../../package.json';

不过你也可以在配置文件禁用按名导入的方式:

// vite.config.ts

{
  json: {
    stringify: true
  }
}

这样会将 JSON 的内容解析为export default JSON.parse("xxx"),这样会失去按名导出的能力,不过在 JSON 数据量比较大的时候,可以优化解析性能。

Web Worker 脚本

Vite 中使用 Web Worker 也非常简单,我们可以在新建Header/example.js文件:

const start = () => {
  let count = 0;
  setInterval(() => {
    // 给主线程传值
    postMessage(++count);
  }, 2000);
};

start();

然后在 Header 组件中引入,引入的时候注意加上?worker后缀,相当于告诉 Vite 这是一个 Web Worker 脚本文件:

import Worker from './example.js?worker';
// 1. 初始化 Worker 实例
const worker = new Worker();
// 2. 主线程监听 worker 的信息
worker.addEventListener('message', (e) => {
  console.log(e);
});

WASM脚本

Vite 对于 .wasm 文件也提供了开箱即用的支持,我们拿一个斐波拉契的 .wasm 文件

export function fib(n) {
  var a = 0,
    b = 1;
  if (n > 0) {
    while (--n) {
      let t = a + b;
      a = b;
      b = t;
    }
    return b;
  }
  return a;
}

让我们在组件中导入fib.wasm文件:

// Header/index.tsx
import init from './fib.wasm';

type FibFunc = (num: number) => number;

init({}).then((exports) => {
  const fibFunc = exports.fib as FibFunc;
  console.log('Fib result:', fibFunc(10));
});

Vite 会对.wasm文件的内容进行封装,默认导出为 init 函数,这个函数返回一个 Promise,因此我们可以在其 then 方法中拿到其导出的成员——fib方法。

回到浏览器,我们可以查看到计算结果,说明 .wasm 文件已经被成功执行:

图片.png

其他静态资源

除了上述的一些资源格式,Vite 也对下面几类格式提供了内置的支持:

  • 媒体类文件,包括mp4webmoggmp3wavflacaac
  • 字体类文件。包括woffwoff2eotttfotf
  • 文本类。包括webmanifestpdftxt

也就是说,你可以在 Vite 将这些类型的文件当做一个 ES 模块来导入使用。如果你的项目中还存在其它格式的静态资源,你可以通过assetsInclude配置让 Vite 来支持加载:

// vite.config.ts

{
  assetsInclude: ['.gltf']
}

特殊资源后缀

Vite 中引入静态资源时,也支持在路径最后加上一些特殊的 query 后缀,包括:

  • ?url: 表示获取资源的路径,这在只想获取文件路径而不是内容的场景将会很有用。
  • ?raw: 表示获取资源的字符串内容,如果你只想拿到资源的原始内容,可以使用这个后缀。
  • ?inline: 表示资源强制内联,而不是打包成单独的文件。

Vite中文件资源内联与单文件

在 Vite 中,所有的静态资源都有两种构建方式,一种是打包成一个单文件,另一种是通过 base64 编码的格式内嵌到代码中。

这两种方案到底应该如何来选择呢?

对于比较小的资源,适合内联到代码中,一方面对代码体积的影响很小,另一方面可以减少不必要的网络请求,优化网络性能。而对于比较大的资源,就推荐单独打包成一个文件,而不是内联了,否则可能导致上 MB 的 base64 字符串内嵌到代码中,导致代码体积瞬间庞大,页面加载性能直线下降。

Vite 中内置的优化方案是下面这样的:

  • 如果静态资源体积 >= 4KB,则提取成单独的文件
  • 如果静态资源体积 < 4KB,则作为 base64 格式的字符串内联

上述的4 KB即为提取成单文件的临界值,当然,这个临界值你可以通过build.assetsInlineLimit自行配置,如下代码所示:

// vite.config.ts
{
  build: {
    // 8 KB
    assetsInlineLimit: 8 * 1024
  }
}

svg 格式的文件不受这个临时值的影响,始终会打包成单独的文件,因为它和普通格式的图片不一样,需要动态设置一些属性