It was published in the department column
Introduction to Vite
1.1 What is a Vite?
Vite is a new generation of front-end building tools, which was born when Yu Yu Creek was developing Vue3.0. Similar to Webpack+ webpack-dev-server. It uses the ESM features of the browser to import organizational code, which is compiled and returned on the server side on demand, bypassing the concept of packaging completely and allowing the server to use it on demand. Rollup is used in production as a packaging tool, billed as the next generation front-end build tool.
Vite has the following features:
- Quick cold start:
No Bundle
+esbuild
pre-built
- Instant module hot update: based
ESM
theHMR
And use the browser cache strategy to speed things up
- True on-demand loading: using a browser
ESM
Support to achieve true on-demand loading
1.2 Comparison between Vite and traditional packaging methods
1.2.1 VS Webapck
Webpack is the most used front-end packaging build tool in recent years, and the community is the most complete. The new 5.x version has optimized the construction details, and the packaging speed has been significantly improved in some scenarios. When Webpack starts, it builds a dependency graph of the project modules, and if the code changes somewhere in the project, Webpack repackages the dependencies, which slows down as the project grows.
Vite, in contrast to Webpack, does not have a packaging process, but instead directly starts a development server, devServer. Vite hijacks HTTP requests from the browser and does the corresponding processing on the back end to simply decompose and consolidate the files used in the project and then return them to the browser (the whole process does not package or compile the files). So compilation is fast.
1.2.2 VS SnowPack
Snowpack is the first packaging tool that leverages the browser’s native ESM capabilities. The idea is to reduce or avoid entire bundle packaging. By default, applications are deployed unbundle in both dev and Production environments. But it was built to be a choice for the user, and the overall packaging experience was a bit fragmented.
Vite integrates directly with Rollup, giving users a complete, out-of-the-box solution and extending more advanced functionality thanks to these integrations.
The big difference is that Vite uses Rollup’s built-in configuration when bundles are packed, while Snowpack delegates it to Parcel/ ‘webpack’ via other plug-ins.
2. Pre-knowledge
2.1 the ESM
Before you get to know Vite, you need to know the ESM
ESM is an official standardized module system proposed by JavaScript. Different from CJS, AMD, CMD and so on before, ESM provides a more native and dynamic module loading solution. The most important thing is that it is supported by browsers natively, which means that we can execute import directly in the browser. Dynamically introduce the modules we need, rather than packing them all together.
Currently, ESM modularity supports more than 92% of browsers, and as an ECMA standard, more browsers will support the ECMA specification in the future
When we use module development, we are actually building a module dependency diagram, starting with the entry file when the module loads, and eventually generating the complete module instance diagram.
ESM execution can be divided into three steps:
- Build: Determine where to download the module file, download, and parse all the files into module records
- Instantiation: convert the module record into a module instance, allocate memory space for all modules, and point the module to the corresponding memory address according to the export and import statements.
- Run: Run the code to fill the memory space
As you can see from the above instantiation, the ESM uses a real-time binding pattern, with both exported and imported modules pointing to the same memory address, known as a value reference. CJS uses value copying, that is, all exported values are copied values.
2.2 Esbuild
Vite uses Esbuild to convert.ts, JSX, and.js code files, so let’s look at es-build.
Esbuild is a JavaScript Bundler packaging and compression tool that provides resource packaging capabilities similar to Webpack, Rollup, etc. JavaScript and TypeScript code can be packaged and distributed to run on web pages. But it packs 10 to 100 times faster than other tools.
Currently it supports the following features:
- loader
- The compression
- packaging
Tree shaking
Source map
generate
Esbuild provides four functions: Transform, Build, buildSync, and Service. Interested can refer to the official documentation.
2.3 a Rollup
In a production environment, Vite is packaged using Rollup
Rollup is an ESM-based JavaScript packaging tool. It always produces smaller, faster packages than other packaging tools like Webpack. Because Rollup is based on ESM modules, it is more efficient than the CommonJS module mechanism used by Webpack and Browserify. The beauty of Rollup is the same place, one load at a time. You can Tree Shaking source code (remove code that has been defined but is not being used), and Scope to reduce output file size to improve performance.
Rollup is divided into build stage and output generate stage. The main process is as follows:
- Get the contents of the import file, wrapped into
module
Generate an abstract syntax tree
- Dependency parsing of the entry file abstract syntax tree
- Generate the final code
- Write to target file
If your project (especially the class library) only has JavaScript and no other static resource files, using Webpack is a bit overkill. Because Webpack packages files that are slightly larger, slower, and less readable. Rollup is also a good choice.
If you want to learn more about Rollp, check out the Rollp website.
3 Core Principles
To elaborate:
- When you declare a
script
The label type ismodule
When, if the
<script type= module src= /src/main.js ></script>
Copy the code
- When the browser parses a resource, it initiates one to the current domain name
GET
requestmain.js
file
// main.js
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
Copy the code
- The request to the
main.js
File, will detect internal containsimport
The imported package will againimport
Reference to aHTTP
Request the contents file of the module, as shown inApp.vue
,vue
file
The core principle of Vite is that the browser already supports import of ES6. When encountering import, it will send an HTTP request to load the file. Vite starts a KOA server to intercept these requests, and performs corresponding processing on the back end to decompose and integrate the files used in the project. It is then returned to the browser in ESM format. Instead of packing and compiling files, Vite truly loads on demand, so it runs much faster than the original WebPack compiler!
3.1 EsM-based Dev Server
inVite
Before coming out, traditional packaging tools such asWebpack
Is it to resolve the dependencies, package the build and then start the development server,Dev Server
Must wait for all modules to be built while we modifybundle
A submodule of a module, the wholebundle
The files are repackaged and exported. The larger the application, the longer the startup time.
Vite takes advantage of the browser’s ESM support, and when a module is imported, the browser downloads it. Start the development server first, and then request the file of the corresponding module when the code is executed until the module is loaded, essentially realizing dynamic loading. The gray sections are routes that are not used for the time being, and all of these sections will not participate in the build process. With more and more applications in the project, adding route will not affect the speed of its construction.
3.2 HMR Hot Update based on ESM
At present, all the packaging tools to achieve hot update ideas are similar with minor differences: mainly through WebSocket to create browser and server communication monitoring file changes, when the file is modified, the server sends a message to inform the client to modify the corresponding code, the client corresponding to different files for different operations update.
3.2.1 VS Webpack
Webpack: recompile, request changed module code, client reload
Vite: A module that requests changes and then reloads
Vite uses Chokidar to listen for file system changes and only reload the modules that have changed, precisely invalidating the relevant modules with their adjacent HMR boundary connections, so that HMR updates don’t slow down as the application size increases and Webpack goes through a pack build. So Vite also performs better than Webpack in HMR scenarios.
3.2.2 Core process
The whole Vite hot update process can be broken down into four steps
- To create a
websocket
The service side andclient
File to start the service - through
chokidar
Listening for file changes - When the code changes, the server determines and pushes it to the client
- The client performs updates based on the pushed information
Overall flow chart:
3.2.2.1 Start hot Update: createWebSocketServer
Before Vite dev Server starts, Vite does some preparation work for HMR. For example, create webSocket service, use Chokidar to create a listener object watcher to listen for file changes, etc.
Source location: packages/vite/SRC/node/server/index. The ts
export async function createServer( inlineConfig: InlineConfig = {} ): Promise<ViteDevServer> { .... const ws = createWebSocketServer(httpServer, config, httpsOptions) const { ignored = [], ... watchOptions } = serverConfig.watch || {} const watcher = chokidar.watch(path.resolve(root), { ignored: [ '**/node_modules/**', '**/.git/**', ...(Array.isArray(ignored) ? ignored : [ignored]) ], ignoreInitial: true, ignorePermissionErrors: true, disableGlobbing: true, ... watchOptions }) as FSWatcher .... watcher.on('change', async (file) => { }) watcher.on('add', (file) => { }) watcher.on('unlink', (file) => { }) ... return server }Copy the code
The createWebSocketServer method creates the WebSocket service, performs some error handling, and returns the encapsulated on, OFF, Send, and close methods for the server to push messages and shut down the service.
Source location: packages/vite/SRC/node/server/ws. The ts
export function createWebSocketServer( server: Server | null, config: ResolvedConfig, httpsOptions? : HttpsServerOptions ): WebSocketServer { let wss: WebSocket let httpsServer: Server | undefined undefined = const HMR = isObject / / hot update configuration (config. Server. HMR) && config. The Server HMR const wsServer = (HMR && HMR. Server) | | server / / normal mode if (wsServer) {WSS = new WebSocket ({noServer: True}) wsServer.on('upgrade', (req, socket, head) => {// Listen for websocket messages sent via vite client, If (req. Headers ['sec-websocket-protocol'] === HMR_HEADER) {wss.handleUpgrade(req, socket as socket, head, (ws) => { wss.emit('connection', ws, Req)})})} else {// Middleware mode // vite dev Server in Middleware mode WSS = new WebSocket(websocketServerOptions)} wss.on('connection', (socket) => { ... }) / / error handling WSS) on (' error '(e: error & {code: string}) = > {... }) // Return {on: wss.on.bind(WSS), off: wss.off. Bind (WSS), send(payload: HMRPayload) {... }, close() { ... }}}Copy the code
3.2.2.2 Performing hot Update: moduleGraph+handleHMRUpdate module
The main two operations are moduleGraph.onFileChange to modify the cache of the file and handleHMRUpdate to perform hot updates
Source location: packages/vite/SRC/node/server/index. The ts
watcher.on('change', async (file) => { file = normalizePath(file) if (file.endsWith('/package.json')) { return invalidatePackageData(packageCache, file) } // invalidate module graph cache on file change moduleGraph.onFileChange(file) if (serverConfig.hmr ! == false) { try { await handleHMRUpdate(file, server) } catch (err) { ws.send({ type: 'error', err: prepareError(err) }) } } })Copy the code
3.2.2.2.1 moduleGraph
ModuleGraph is a class defined by Vite to document a module dependency graph for the entire application, in addition to moduleNode.
Source location: packages/vite/SRC/node/server/moduleGraph ts
The moduleGraph consists of a series of maps that map urls, IDS, files, etc., to the ModuleNode, which is the smallest module unit defined in Vite. From these two classes you can build the following module dependency diagram:
The moduleGraph. ‘ ‘onFileChange function is used to clear the transformResult property of the ModuleNode object corresponding to the modified file, invalidating the conversion cache of the previous module. This is Vite’s caching mechanism for hot updates. You can see the introduction on the official website.
Source location: packages/vite/SRC/node/server/moduleGraph ts
onFileChange(file: string): void {
const mods = this.getModulesByFile(file)
if (mods) {
const seen = new Set<ModuleNode>()
mods.forEach((mod) => {
this.invalidateModule(mod, seen)
})
}
}
invalidateModule(mod: ModuleNode, seen: Set<ModuleNode> = new Set()): void {
mod.info = undefined
mod.transformResult = null
mod.ssrTransformResult = null
invalidateSSRModule(mod, seen)
}
Copy the code
3.2.2.2.2 handleHMRUpdate
The handleHMRUpdate module mainly listens for file changes, processing and judgment through WebSocket to send a message to the client to request the new module code.
Source location: packages/vite/packages/vite/SRC/node/server/HMR ts
3.2.2.3 Client: Websocket communication and update processing
Client: When we configure hot update and not SSR, the Vite layer will write HMR related client code in our code when processing HTML, as follows:
When receiving the message pushed by the server, it processes the message according to different message types. For example, (connected
,update
,custom
…). , which is most frequently used in actual development hot updatesupdate
(Dynamic loading hot update module) andfull-reload
(Refresh the entire page) event.
Source location: packages/vite/packages/vite/SRC/client/client. The ts
Core code implementation
3.2.2.4 Optimization: The browser cache policy improves the response speed
At the same time,Vite
Also usedHTTP
Speed up the entire page reload. Set the response header so that the dependent module (dependency module
) for strong caching, while source files through Settings304 Not Modified
Instead, it becomes updatable according to conditions.
If you need to make changes to the dependent code module, you can manually invalidate the cache:
vite --force
Copy the code
Or manually delete cache files in node_modules/. ‘ ‘vite.
3.3 EsBuild-based dependent precompilation optimization
3.3.1 Why is pre-build required?
- support
commonJS
Rely on
- The above mentioned
Vite
Is based on browser native supportESM
The ability to implement, but requires that the user’s code module must beESM
Module, therefore must becommonJs
The files are processed in advance and converted intoESM
Module and cachenode_modules/.vite
- Reduce module and request numbers
In addition to the loDash library, which we often use to import packages into each other in separate files, lodash-es packages can have hundreds of submodules and make hundreds of HTTP requests when import {debounce} from ‘lodash-es’ appears in the code. These requests can clog the network and affect page loading.
Vite transforms ESM dependencies that have many internal modules into a single module to improve subsequent page loading performance.
By pre-building LoDash-es into a module, you only need one HTTP request!
3.3.2 WhyEsbuild
?
To quote a quote from University of Utah: “Fast” is a word
This is the image of the Esbuild home page. The new generation of packaging tools provide the same resource packaging capability as Webpack, Rollup, and Parcel, but it is 10 to 100 times faster and takes 2% to 3% less time than Webpack
- Compile run VS explain run
- Most front-end packaging tools are based on
JavaScript
Realized, you knowJavaScript
It’s an interpreted language, explaining as it goes along. whileEsbuild
Select useGo
Language preparation, the language can be compiled into native code, at the time of compilation will be language into machine language, at the time of startup can be directly executed, inCPU
In an intensive scenario,Go
More performance advantages.
- Multithreading VS single threading
JavaScript
Essentially a single-threaded language, until introducedWebWorker
Before it is possible to use the browser,Node
To achieve multithreaded operation. As far as I’m onWebpack
Source code understanding, its source code is not usedWebWorker
Provides multithreading capabilities. whileGO
Natural multithreading advantage.- The build process is optimized and fully utilized
CPU
resources
3.3.3 Implementation Principle?
After Vite is precompiled, the file is cached in the node_modules/. Vite/folder. Consider the following to determine if you need to re-perform the prebuild.
package.json
In:dependencies
change
- Package manager
lockfile
If you want to force Vite to re-pre-build dependencies, you can either start the development server with –force or just drop the node_modules/.vite/ folder.
3.3.3.1 Core code implementation
- through
createServer
createserver
Object is executed when the server startshttpServer.listen
methods
- In the implementation
createServer
When,Vite
The bottom layer will rewriteserver.listen
Method: First call the plug-in’sbuildStart
To performrunOptimize()
methods
runOptimize()
calloptimizeDeps()
andcreateMissingImporterRegisterFn()
methods
const runOptimize = async () => { if (config.cacheDir) { server._isRunningOptimizer = true try { server._optimizeDepsMetadata = await optimizeDeps( config, config.server.force || server._forceOptimizeOnRestart ) } finally { server._isRunningOptimizer = false } server._registerMissingImport = createMissingImporterRegisterFn(server) } } if (! middlewareMode && httpServer) { let isOptimized = false // overwrite listen to run optimizer before server start const listen = httpServer.listen.bind(httpServer) httpServer.listen = (async (port: number, ... args: any[]) => { if (! isOptimized) { try { await container.buildStart({}) await runOptimize() isOptimized = true } catch (e) { httpServer.emit('error', e) return } } return listen(port, ... args) }) as any } else { await container.buildStart({}) await runOptimize() }Copy the code
optimizeDeps()
It is generated mainly from the configuration filehash
, get the content of the last pre-purchase build (stored_metadata.json
File). Compare if it’s not strong prebuild_metadata.json
Of the filehash
And newly generatedhash
: Returns if yes_metadata.json
File contents, otherwise empty the cache file callEsbuild
The building blocks are saved again_metadata.json
file
3.3.3.2 Overall flow chart
The core code is in packages/ vite/SRC/node /optimizer/index.ts
- Automatic search depends on the main modules:
esbuildScanPlugin
- Pre-build and compile main modules:
esbuildDepPlugin
3.3.3.2 Overall flow chart
The core code is in packages/ vite/SRC/node /optimizer/index.ts
- Automatic search depends on the main modules:
esbuildScanPlugin
- Pre-build and compile main modules:
esbuildDepPlugin
3.4 Rollup-based Plugins
Vite takes its cues from Preact’s WMR, inheriting Vite Plugins from the Rollup ‘ ‘Plugins “ “API with some extensions (such as vite-specific hooks). Vite also provides a powerful plug-in API based on the Rollup plugins mechanism. For plugins that are currently compatible with Vite or built in, see Viet-rollup-plugins
3.4.1 What is a Vite plugin
The Vite plug-in extends Vite’s capabilities by exposing some of the timing of the build packaging process in conjunction with utility functions, allowing users to write custom configuration code that is executed during the packaging process. Such as parsing user-defined file input, translating code before packaging it, or searching.
In practice, Vite only needs to be extended based on the Rollup interface, with some Vite specific hooks and attributes added to ensure compatibility with Rollup.
3.4.2 Vite exclusive hooks
The specific use of each hook can be found here
config
: can be inVite
Modified before parsingVite
Related configuration of. The hook receives the original user configurationconfig
And a variable that describes the configuration environmentenv
configResolved
: parsingVite
Call after configuration, confirm configuration
configureserver
: is used to configure the development serverdev-server
Add custom middleware
transformindexhtml
: Mainly used for conversionindex.html
, the hook receives the currentHTML
String and transformation context
handlehotupdate
: Performs customizationHMR
Updates are available throughws
Sends custom events to the client
3.4.3 Generic hooks
Here are some common generic hooks. The rest of the common hooks can be moved here
-
Called once when the service starts
options
: Get, manipulateRollup
optionsbuildstart
: Start creating
-
Called on each incoming module request
resolveId
: Creates custom validation functions that can be used to locate third-party dependenciesload
: a custom loader can be used to return custom contenttransform
: is called on each incoming module request, primarily to transform a single module
-
Called once when the service is closed
buildend
: is called when the server is shut downcloseBundle
3.4.4 Call sequence of hooks
To quote a picture from village chief @Young:
As you can see from the Vite website, the Vite plugin can use a Enforce attribute (similar to the Webpack loader) to adjust its application order. Enforce can be pre or Post. The parsed plug-ins are sorted in the following order:
Alias
enforce:'pre'
Custom plug-in for
Vite
The core plug-in
- There is no
enforce
Custom plug-in for
Vite
Build plug-ins
enforce:'post'
Custom plug-in for
Vite
Post-build plug-ins
3.4.5 Custom Plug-ins
- Write plug-in code
Export default function myVitePlugin () {const virtualFileId = '@my-vite-plugin' const virtualFileId = '@my-vite-plugin' Name: 'vite-plugin', // hook // config config: (config, env) => ({console.log('config',config) return {}}), // confirm config configResolved: config => ({}), options: Options => ({}), buildStart: options => ({}), transformIndexHtml: (HTML, CTX) => ({return HTML}), // confirm resolveId: (the source, the importer) = > ({}), / / transformation transform: (code, id) = > ({})}}Copy the code
- Introducing plug-ins:
vite.config.js/ts
Referenced in the
// vite.config.js/ts import myVitePlugin from '... ' export default defineConfig{ plugins:[vue(),myVitePlugin()] }Copy the code
4 summarizes
Finally, summarize the pros and cons of Vite:
-
Advantages:
- Fast cold start: adopted
No Bundle
andesbuild
Pre-built, much faster thanWebpack
- Efficient hot update: Based
ESM
Realize and utilizeHTTP
Header to speed up the reload of the entire page and increase caching strategies - True on-demand loading: browser-based
ESM
Support to achieve true on-demand loading
- Fast cold start: adopted
-
disadvantages
- Ecology: For now
Vite
Is less ecological thanWebapck
But I think ecology is just a matter of time. - Production environment due to
esbuild
rightcss
And code segmentation is not friendly to useRollup
package
- Ecology: For now
Viet.js is just emerging in the build packaging scene, but in many cases it is better than existing solutions. Consider a full-fledged Webpack if you have ecological, rich loaders and plugins requirements. In other cases, viet.js is a good choice for packaging build tools.
5 appendix
- The official documentation
- Esbuild website
- Vite plug-in apis
- Rollup official documentation
- Rollup – Next-generation ES6 module bundler – Interview with Rich Harris
- Vite source
- Vite2 plug-in development guide