【译】Vite2.0新特性

2,273 阅读10分钟

来源: vitejs.dev/guide/featu… Features章节

本质上来说,Vite和静态文件服务器在开发使用上并没有太大差别。然而Vite在原生ESM imports的基础上做了很多的提升,提供了多种多样的典型见于基于bundler打包设置的特性。

NPM依赖的解析和预打包

原生ES imports不支持纯模块引入,例如我们经常在 webpack-based 项目中使用的方法:

import  { someMethod } from 'my-dep' // 官方例子
import React, { useState } from 'react' // 个人例子

这种引入方式不加转义直接运行会在浏览器中报错。Vite会在所有文件中检测这类纯模块引入,然后做以下处理:

  1. 预打包(Pre-bundle) 这些纯模块以提高页面加载速度,还要把 CommonJS/UMD 标准定义的模块转为ESM标准。预打包是由 esbuild 完成的,这使得Vite的冷启动时间明显快于任何基于JS的打包工具。
  2. 把引入模块的可用路径重写,改成类似于/node_modules/.vite/my-dep.js?v=f3sf2ebd,让浏览器能够正确的引入这些纯模块。

依赖将被强缓存

Vite把依赖的请求通过HTTP header缓存起来,如果你想本地的 编辑/debug 某一依赖,操作如

模块热替换(Hot Module Replacement)

Vite基于ESM提供了一个 HMR API。HMR(Hot Module Replacement)是利用API提供即时、精确地更新,这一操作不会导致页面重加载或者应用状态丢失(使用状态管理时 // 个人理解)。Vite为Vue的单文件组件和React的快速刷新提供了第一方HMR集成库支持。也有为Preact提供的官方集成库( @prefresh/vite)。

值得一提的是你不再需要手动配置模块热替换——使用@vitejs/create-app指令创建应用时,这一项功能已经帮你预设好了。

TS支持

Vite支持在项目中开箱即用的使用.ts文件。

Vite仅对.ts文件执行转译,不执行类型检查。Vite假定类型检查由你的IDE和构建过程(你可以在构建脚本中使用tsc --noEmit指令)完成。

Vite使用esbuild对ts进行转译,这一过程比使用vanilla的tsc指令快20-30倍。HRM更新在浏览器中的表现是小于50ms。

值得注意的是,因为esbuild仅执行不带类型信息的转义,因此它不支持某些功能例如:const enum和隐式的type-only导入(TS3.8)。如此一来,你必须在tsconfig.json文件下,compilerOptions属性下配置"isolateModules": true。以便于TS本身提醒你隔离式转译的功能不会生效。

客户端类型

Vite的默认类型是适用于它的Nodejs API。来调整一个Vite应用中客户端代码的环境,需要在你的tsconfig.json文件中加入以下代码

{
    "compilerOptions": {
        "types": ["vite/client"]
    }
}

这会提供以下的类型支持:

  • 静态资源的导入(例如导入一个.svg文件)
  • Vite注入的环境变量的类型import.meta.env
  • HMR API类型import.meta.hot

VUE

Vite提供一流的Vue支持:

JSX

Vite提供开箱即用的.jsx.tsx文件支持。JSX同样由 esbuild 负责转译,默认支持React16特性。React17的esbuild支持请点击这里

Vue的开发者可以使用官方插件@vitejs/plugin-vue-jsx,该插件提供了Vue3特有的特性,包括HMR,全局组件解析,vue指令(directives)和插槽(slots)。

如果不想在React或Vue项目中使用JSX,可以使用esbuild选项配置自定义的可选项jsxFactoryjsxFragment。例如为Preact项目配置如下:

// vite.config.js
export default {
  esbuild: {
    jsxFactory: 'h',
    jsxFragment: 'Fragment'
  }
}

更多细节查看ESBuild文档

你可以使用可选项jsxInject全局注入JSX语法支持来避免在每一个文件中手动声明引入。这一特性为Vite独有配置:

// vite.config.js
export default {
  esbuild: {
    jsxInject: `import React from 'react'`
  }
}

CSS

引入.css文件将通过具有HMR支持的<style>标签将样式表内容注入页面。你还可以将处理后的CSS字符串作为模块的默认导出。

@import内联样式和重构

Vite默认支持通过postcss-import使用@import内联的引入CSS。Vite别名也适用于@import。另外,即使导入的文件位于不同的文件夹下,所有的CSSurl()引用都会自动重构以保证正确性。

@import别名和URL重构同样支持Sass和Less文件。

PostCSS

如果项目包含有效的PostCSS配置(postcss-load-config支持的任何格式,例如postcss.config.js),它将自动应用于所有导入的CSS。

CSS 模块

任何以.module.css结尾的CSS文件都会被当做一个CSS模块文件。导入这样的文件将返回相应的模块对象:

/* example.module.css */
.red {
  color: red;
}
import classes from './example.module.css'
document.getElementById('foo').className = classes.red

可以通过css.modules选项配置CSS模块的行为。

如果将css.modules.localsConvention设置为启用驼峰命名法(设置localsConvention:'camelCaseOnly'),则还可以使用命名导入:

// .apply-color -> applyColor
import { applyColor } from './example.module.css'
document.getElementById('foo').className = applyColor

CSS 预处理器

因为Vite仅面向现代浏览器,我们建议将原生CSS变量与PostCSS插件一起使用。PostCSS插件实行CSSWG草案(例如postcss-nesting)并支持符合未来标准的普通CSS。

也就是说,Vite内置了对.scss, .sass, .less, .styl.stylus文件的支持。因此不用再为这些文件额外安装适配Vite的插件,但还是要安装相应的预处理器本身:

# .scss and .sass
npm install -D sass

# .less
npm install -D less

# .styl and .stylus
npm install -D stylus

如果是使用Vue单文件组件,也会自动启用<style lang="sass">等。

Vite改进了@import解析Sass和Less的方式,因此Vite同样支持别名引入。而且,与根文件位于不同文件路径下被导入的Sass / Less文件,其相对路径url()的引用也将自动重构以确保正确性。

Stylus因其API限制并不支持@import别名和url重构。

你也可以联合使用CSS模块和预处理器。使用方法是在文件拓展名中缀加入.module,例如style.module.scss

静态资源

导入一个静态资源时会返回给使用者一个解析过的公共URL,使用时:

import imgUrl from './img.png'
document.getElementById('hero-img').src = imgUrl

引入时的特殊参数决定使用者以何种方式引入静态资源:

// 显式的作为URL引入
import assetAsURL from './asset.js?url'
// 作为字符串引入
import assetAsString from './shader.glsl?raw'
// 加载Web Workers
import Worker from './worker.js?worker'
// 创建时Web Workers作为base64字符串行内引入
import InlineWorker from './worker.js?worker&inline'

更多细节查看静态资源打包

JSON

JSON文件支持直接引入和命名引入:

// import the entire object
import json from './example.json'
// import a root field as named exports - helps with treeshaking!
import { field } from './example.json'

批量引入(通配引入)(glob import)

Vite支持批量引入模块。方法是只需要通过import.meta.glob函数:

const modules = import.meta.glob('./dir/*.js)`

上述写法实际上是以下写法的变形:

// code produced by vite
const modules = {
  './dir/foo.js': () => import('./dir/foo.js'),
  './dir/bar.js': () => import('./dir/bar.js')
}

接着你可以通过模块modules对象的key来遍历访问相应的模块:

for (const path in modules) {
  modules[path]().then((mod) => {
    console.log(path, mod)
  })
}

匹配的文件默认通过动态引入实现懒加载。项目打包时会拆成多块。如果你想要直接引入所有模块(例如:依赖于这些模块的副作用需要先被应用),则可以使用import.meta.globEager函数:

const modules = import.meta.globEager('./dir/*.js')

上述写法实际上是以下写法的变形:

// code produced by vite
import * as __glob__0_0 from './dir/foo.js'
import * as __glob__0_1 from './dir/bar.js'
const modules = {
  './dir/foo.js': __glob__0_0,
  './dir/bar.js': __glob__0_1
}

值得注意的是:

  • 这只是Vite提供的特性而非web或ES标准
  • 通配模式与import语法使用规则相同,引入路径必须是相对路径(以./开头)或绝对路径(以/开头,被解释为项目根路径)。不支持在依赖中使用通配引入。
  • 通配匹配由fast-glob完成——查看文档通配特性支持

Web Assembly

预编译的.wasm文件可以直接引入。文件的默认导出是一个初始化函数,函数返回一个wasm实例的导出对象的Promise:

import init from './example.wasm'

init().then((exports) => {
  exports.test()
})

这个初始化函数也可以接受一个传给WebAssembly.instantiateimports对象作为第二参数:

init({
  imports: {
    someFunc: () => {
      /* ... */
    }
  }
}).then(() => {
  /* ... */
})

产品打包过程中,小于assetInlineLimit.wasm文件将作为base64字符串内联。否则,它们将作为静态资源直接复制到dist目录并按需获取。

Web Workers

一个web worker脚本可以通过添加引入参数?worker的方式直接引入。默认导出是一个自定义的worker构造函数:

import MyWorker from './worker?worker'

const worker = new MyWorker()

worker脚本也可以直接使用import而不是imnportScripts()引入。但是请注意,开发环境下这种方式依靠浏览器的原生支持且仅在谷歌浏览器下可用。但是生产环境打包后会被编译为通用格式。

默认情况打包下,worker脚本会被分块导出。如果你希望worker作为base64字符串内联,请添加内联参数:

import MyWorker from './worker?worker&inline'

打包优化

下面列出来的特性都是在打包程序中自动应用。除非你想要禁用某些配置,否则不需要精确配置。

动态引入Polyfill

Vite使用ES动态导入作为代码拆分点。生成的代码还将使用动态导入来加载异步块。 但是,原生ESM通过script标签提供的动态导入支持比ESM加载要慢,并且在浏览器支持上两个功能存在差异。Vite会自动注入轻量级的动态导入Polyfill,以缓解这种差异。

如果你只使用具有原生动态导入功能支持的浏览器,则可以通过build.polyfillDynamicImport显式禁用此功能。

CSS代码拆分

Vite会自动提取模块使用到的CSS到一个异步块中,然后为此生成一个分隔文件。加载关联的异步块时,会通过<link>标签自动加载CSS文件,并确保仅在加载CSS之后解析异步块,以避免FOUC的情况。

如果您希望将所有CSS提取到一个文件中,则可以通过将build.cssCodeSplit设置为false来禁用CSS代码拆分。

预载指令生成

Vite自动为入口块文件和构建的HTML中直接引入这些文件的地方生成<link rel =“ modulepreload”>指令。

异步块加载优化

在真实的程序中,Rollup通常会生成"common"块,"common"块中的代码会在其他两三个块中共享。结合动态导入,通常会出现以下情况:

在非优化方案中,当导入异步块A时,浏览器必须先请求并解析A,然后才能确定它还需要公共块C。这将导致额外的网络往返:

Entry ---> A ---> C

Vite会使用预加载步骤自动重写代码拆分的动态导入调用,以便在请求A并行获取C

Entry ---> (A + C)

C可能会进一步导入,这将导致会比未优化的情况下产生更大的网络往返。 Vite的优化将跟踪所有直接导入,以完全消除往返,无论导入深度如何。

初次翻译技术文档,欢迎批评~