Vite is an opinionated web dev build tool that serves your code via native ES Module imports during dev and bundles it with Rollup for production.
- Lightning fast cold server start
- Instant hot module replacement (HMR)
- True on-demand compilation
- More details in How and Why
Still experimental, but we intend to make it suitable for production.
$ npx create-vite-app <project-name>
$ cd <project-name>
$ npm install
$ npm run dev
If using Yarn:
$ yarn create vite-app <project-name>
$ cd <project-name>
$ yarn
$ yarn dev
Although Vite is primarily designed to work with Vue 3, it can actually support other frameworks as well. For example, try
npx create-vite-app
with--template react
or--template preact
.
Vite requires native ES module imports during development. The production build also relies on dynamic imports for code-splitting (which can be polyfilled).
Vite assumes you are targeting modern browsers and therefore does not perform any compatibility-oriented code transforms by default. Technically, you can add autoprefixer
yourself using a PostCSS config file, or add necessary polyfills and post-processing steps to make your code work in legacy browsers - however that is not Vite's concern by design.
- Bare Module Resolving
- Hot Module Replacement
- TypeScript
- CSS / JSON Importing
- Asset URL Handling
- PostCSS
- CSS Modules
- CSS Pre-processors
- JSX
- Production Build
Vite tries to mirror the default configuration in vue-cli as much as possible. If you've used vue-cli
or other webpack-based boilerplates before, you should feel right at home. That said, do expect things to be different here and there.
Native ES imports doesn't support bare module imports like
import { createApp } from 'vue'
The above will throw an error by default. Vite detects such bare module imports in all served .js
files and rewrites them with special paths like /@modules/vue
. Under these special paths, Vite performs module resolution to locate the correct files from your installed dependencies.
Note that vue
has special treatment - if it isn't installed in the project locally, Vite will fallback to the version from its own dependencies. If you have Vite installed globally, this makes it possible to quickly prototype with Vue without installing anything locally.
-
*.vue
files come with HMR out of the box. -
For
*.js
files, a simple HMR API is provided:import { foo } from './foo.js' import { hot } from 'vite/hmr' foo() // this code will be stripped out when building if (__DEV__) { hot.accept('./foo.js', (newFoo) => { // the callback receives the updated './foo.js' module newFoo.foo() }) // Can also accept an array of dep modules: hot.accept(['./foo.js', './bar.js'], ([newFooModule, newBarModule]) => { // the callback receives the updated mdoules in an Array }) }
Modules can also be self-accepting:
import { hot } from 'vite/hmr' export const count = 1 // this code will be stripped out when building if (__DEV__) { hot.accept((newModule) => { console.log('updated: count is now ', newModule.count) }) }
A self-accepting module, or a module that expects to be accepted by others can use
hot.dispose
to cleanup any persistent side effects created by its updated copy:import { hot } from 'vite/hmr' function setupSideEffect() {} function cleanupSideEffect() {} setupSideEffect() if (__DEV__) { hot.dispose(cleanupSideEffect) }
Note that Vite's HMR does not actually swap the originally imported module: if an accepting module re-exports imports from a dep, then it is responsible for updating those re-exports (and these exports must be using
let
). In addition, importers up the chain from the accepting module will not be notified of the change.This simplified HMR implementation is sufficient for most dev use cases, while allowing us to skip the expensive work of generating proxy modules.
Vite supports importing .ts
files and <script lang="ts">
in Vue SFCs out of the box.
Vite only performs transpilation on .ts
files and does NOT perform type checking. It assumes type checking is taken care of by your IDE and build process (you can run tsc --noEmit
in the build script).
Vite uses esbuild to transpile TypeScript into JavaScript which is about 20~30x faster than vanilla tsc
, and HMR updates can reflect in the browser in under 50ms.
Note that because esbuild
only performs transpilation without type information, it doesn't support certain features like const enum and implicit type-only imports. You must set "isolatedModules": true
in your tsconfig.json
under compilerOptions
so that TS will warn you against the features that do not work with isolated transpilation.
You can directly import .css
and .json
files from JavaScript (including <script>
tags of *.vue
files, of course).
-
.json
files export their content as an object that is the default export. -
.css
files do not export anything unless it ends with.module.css
(See CSS Modules below). Importing them leads to the side effect of them being injected to the page during dev, and being included in the finalstyle.css
of the production build.
Both CSS and JSON imports also support Hot Module Replacement.
You can reference static assets in your *.vue
templates, styles and plain .css
files either using absolute public paths (based on project root) or relative paths (based on your file system). The latter is similar to the behavior you are used to if you have used vue-cli
or webpack's file-loader
.
All referenced assets, including those using absolute paths, will be copied to the dist folder with a hashed file name in the production build. Never-referenced assets will not be copied. Similar to vue-cli
, image assets smaller than 4kb will be base64 inlined.
The exception is the public
directory - assets placed in this directory will be copied to the dist directory as-is. It can be used to provide assets that are never referenced in your code - e.g. robots.txt
.
All static path references, including absolute paths and those starting with /public
, should be based on your working directory structure. If you are deploying your project under a nested public path, simply specify --base=/your/public/path/
and all asset paths will be rewritten accordingly.
For dynamic path references, there are two options:
-
You can get the resolved public path of a static asset file by importing it from JavaScript. e.g.
import path from './foo.png'
will give you its resolved public path as a string. -
If you need to concatenate paths on the fly, you can use the globally injected
__BASE__
variable with will be the public base path.
Vite automatically applies your PostCSS config to all styles in *.vue
files and imported plain .css
files. Just install necessary plugins and add a postcss.config.js
in your project root.
Note that you do not need to configure PostCSS if you want to use CSS Modules: it works out of the box. Inside *.vue
components you can use <style module>
, and for plain .css
files, you need to name CSS modules files as *.module.css
which allows you to import the naming hash from it.
Because Vite targets modern browsers only, it is recommended to use native CSS variables with PostCSS plugins that implement CSSWG drafts (e.g. postcss-nesting) and author plain, future-standards-compliant CSS. That said, if you insist on using a CSS pre-processor, you can install the corresponding pre-processor and just use it:
yarn add -D sass
<style lang="scss">
/* use scss */
</style>
Note importing CSS / preprocessor files from .js
files, and HMR from imported pre-processor files are currently not supported, but can be in the future.
.jsx
and .tsx
files are also supported. JSX transpilation is also handled via esbuild
. Note that there is currently no auto-HMR support for any JSX-based usage.
The default JSX configuration works out of the box with Vue 3:
import { createApp } from 'vue'
function App() {
return <Child>{() => 'bar'}</Child>
}
function Child(_, { slots }) {
return <div onClick={console.log('hello')}>{slots.default()}</div>
}
createApp(App).mount('#app')
Currently this is auto-importing a jsx
compatible function that converts esbuild-produced JSX calls into Vue 3 compatible vnode calls, which is sub-optimal. Vue 3 will eventually provide a custom JSX transform that can take advantage of Vue 3's runtime fast paths.
There are two other presets provided: react
and preact
. You can specify the preset by running Vite with --jsx react
or --jsx preact
. For the Preact preset, h
is also auto injected so you don't need to manually import it.
Because React doesn't ship ES module builds, you either need to use es-react, or pre-bundle React into a ES module with Snowpack. Easiest way to get it running is:
import { React, ReactDOM } from 'https://unpkg.com/es-react'
ReactDOM.render(<h1>Hello, what!</h1>, document.getElementById('app'))
If you need a custom JSX pragma, JSX can also be customized via --jsx-factory
and --jsx-fragment
flags from the CLI or jsx: { factory, fragment }
from the API. For example, you can run vite --jsx-factory=h
to use h
for JSX element creation calls.
Vite does utilize bundling for production builds, because native ES module imports result in waterfall network requests that are simply too punishing for page load time in production.
You can run vite build
to bundle the app.
Internally, we use a highly opinionated Rollup config to generate the build. The build is configurable by passing on most options to Rollup - and most non-rollup string/boolean options have mapping flags in the CLI (see build/index.ts for full details).
You can create a vite.config.js
or vite.config.ts
file in your project. Vite will automatically use it if one is found in the current working directory. You can also explicitly specify a config file via vite --config my-config.js
.
In addition to options mapped from CLI flags, it also supports alias
, transforms
, and plugins (which is a subset of the config interface). For now, see config.ts for full details before more thorough documentation is available.
You can customize the server using the API. The server can accept plugins which have access to the internal Koa app instance:
const { createServer } = require('vite')
const myPlugin = ({
root, // project root directory, absolute path
app, // Koa app instance
server, // raw http server instance
watcher // chokidar file watcher instance
}) => {
app.use(async (ctx, next) => {
// You can do pre-processing here - this will be the raw incoming requests
// before vite touches it.
if (ctx.path.endsWith('.scss')) {
// Note vue <style lang="xxx"> are supported by
// default as long as the corresponding pre-processor is installed, so this
// only applies to <link ref="stylesheet" href="*.scss"> or js imports like
// `import '*.scss'`.
console.log('pre processing: ', ctx.url)
ctx.type = 'css'
ctx.body = 'body { border: 1px solid red }'
}
// ...wait for vite to do built-in transforms
await next()
// Post processing before the content is served. Note this includes parts
// compiled from `*.vue` files, where <template> and <script> are served as
// `application/javascript` and <style> are served as `text/css`.
if (ctx.response.is('js')) {
console.log('post processing: ', ctx.url)
console.log(ctx.body) // can be string or Readable stream
}
})
}
createServer({
plugins: [myPlugin]
}).listen(3000)