The author of this article is Wang Wenjian, front-end development engineer of 360 Qi Dance Company

preface

Hi, everyone!

Some time ago when I was building a project with Vue3, I saw Vite coming out at the same time. I still chose to build the project with Webpack just because it was a new packaging tool or an upgraded version of VUE-CLI. Recently watched the video of Yu Yuxi’s talk at VueConf: “Vue3 Ecological Progress and Plan” feels that it really solves some pain points of front-end engineering at the present stage, and can also feel yuxi’s emphasis on Vite and determination to promote it vigorously. Combined with the huge user base of Vue itself, Vite is indeed likely to become the breakthrough of the next generation of front-end construction tools.

This article will discuss the background of Vite’s emergence, the pain points solved, the implementation of core features, the meaning of its existence and the expected future. Vite itself is not complicated. The official Chinese document is very clear and concise. I suggest you read it carefully before using it.


The outline

  • background
  • What is a Vite?
  • Basic usage
  • Realize the principle of
  • Source code analysis
  • Advantages and Disadvantages
  • Contrast with traditional build tools
  • compatibility
  • In the future

background

The background starts with the history of two concepts closely related to Vite: the modular standard for JavaScript and the front-end build tool.

Coexistence of modular standards

Why are there multiple co-existing modular standards for JavaScript? Because js at the beginning of the design is not the concept of modularity, with complexity to improve the front-end business, modular increasingly brought to the attention of the developers and community have begun to emerge a variety of modular solution, they learn from each other, also disputed, form multiple factions, starting from the CommonJS, to the end of the formal launch ES Modules ES6 specification, All of this debate is history, and ES Modules become an important infrastructure for the front end.

  • CommonJS: currently used mainly for Node.js ([email protected] now supports direct use of ES Module)
  • AMD:require.jsIt is not recommended to use the market stock
  • CMD:sea.jsThe nearest implementation, market stock is not recommended to use
  • ES Module: ES language specification, standard, trend, future

Interested in the development history of modular can see “front-end modular development that point in history” @ Yubo, and the core of Vite is to rely on the browser to achieve the ES Module specification.

Build tools in development

In recent years, front-end engineering has developed rapidly, and various building tools have emerged in an endless stream. Currently, Webpack still dominates, and NPM downloads reach more than 20 million times per week. Here are some of the build tools that developers are familiar with by the NPM release timeline.

Current engineering pain points

Now commonly used build tools such as Webpack, mainly by grabbing, compiling and building the entire application code (also known as the packaging process), to produce a compiled and optimized production environment code that is compatible with various browsers. The process is much the same in the development environment, where the entire application build is packaged and then the packaged code is delivered to the Dev Server.

The birth of Webpack and other construction tools has brought great convenience to front-end development. However, with the complexity of front-end business, js code quantity increases exponentially, and the packaging and construction time becomes longer and longer, and dev Server (development server) performance hits a bottleneck:

  • Slow service startup: Dev Server startup times can reach tens of seconds or even minutes in large projects.

  • Slow HMR hot update: Even if HMR mode is adopted, its hot update speed will decrease significantly with the increase of application scale, reaching the performance bottleneck and leaving little room for optimization.

The slow development environment greatly reduces the happiness of developers in the above contextViteCame into being.


What is a Vite?

Based on ESbuild and Rollup, rely on the browser’s own ESM compilation capabilities, to achieve the ultimate development experience of the next generation of build tools!

concept

Here are some basic concepts that will be mentioned frequently:

  • Dependencies: refers to the parts of development that do not change (NPM packages, UI component libraries), which are pre-built by ESBuild.
  • Source code: non-JS code (.jsx,.css,.vue, etc.) that the browser cannot execute directly. Vite only converts the source code when the browser requests it to provide the ESM source code.

The development environment

  • Use browser nativeES ModuleCompilation ability, skipping time-consuming compilation, directly to the browser development environment source code,dev serverOnly light service is provided.
  • The browser executes the ESMimportLook todev serverThat initiated the moduleajaxRequest, the server does a simple processing of the source code back to the browser.
  • ViteThe HMR is executed on native ESM. When editing a file, Vite only needs to precisely deactivate the edited module so that HMR is always updated quickly, regardless of the size of the application.
  • useesbuildHandle project dependencies,esbuildWrite using GO, more than averagenode.jsWrite compilers that are orders of magnitude faster.

The production environment

  • integrationRollupPackage the production environment code, relying on its mature and stable ecosystem and a more concise plug-in mechanism.

Comparison of Processing flow

Webpack works by first packaging the entire application and then providing the packaged code to the Dev Server to start development.

ViteDirect source to the browser, implementationdev serverSeconds open, the browser display page needs to be related to the module, and then todev serverAfter initiating the request and simple processing by the server, the module will be returned to the browser to achieve the true meaning of on-demand loading.


Basic usage

Create a Vite project

$ npm create vite@latest
Copy the code

Select a template

Vite has six built-in templates and their corresponding TS versions, which can be used in most front-end development scenarios. You can click on the templates in the table below to try them out online in StackBlitz, and there are many more community maintenance templates available.

JavaScript TypeScript
vanilla vanilla-ts
vue vue-ts
react react-ts
preact preact-ts
lit lit-ts
svelte svelte-ts

Start the

{
  "scripts": {
    "dev": "vite".// Start the development server with aliases: 'vite dev', 'vite serve'
    "build": "vite build".// Build artifacts for production environments
    "preview": "vite preview" // Preview production builds locally}}Copy the code

Realize the principle of

ESbuild compilation

Esbuild is written using Go, which is CPU intensive and provides better performance and faster compilation. Here is a comparison of build speeds from the official website: Server: “It’s over.” Developer: “Good quick, good love!!”

Depend on prebuild

  • Modular compatibility:As noted in the background at the beginning, there are still multiple modular standard codes,ViteVarious other modular specifications in dependencies (CommonJS, UMD) are converted into ESM during the pre-build phase to provide to the browser.
  • Performance optimization:A lot of ESM code in the NPM package, a lotimportRequest may cause network congestion.ViteuseesbuildTo reduce ESM relationships that have a large number of internal modules into a single moduleimportNumber of module requests.

According to the need to load

  • Only when an import request is received does the server compile the corresponding file and return the ESM source code to the browser, achieving true load on demand.

The cache

  • HTTP cache:Make full use ofhttpCaching is optimized, and dependencies (code that does not change) are max-age,immutableStrong cache, the source part uses 304Negotiate the cacheTo improve the page opening speed.
  • File system cache: ViteDuring the pre-build phase, the built dependencies are cached tonode_modules/.viteIn order to improve the speed of pre-build, it will be rebuilt only when relevant configuration changes or manual controls are made.

Rewrite module path

Browser import can only import relative/absolute paths, whereas development code often imports modules in node_module directly using the NPM package name, which needs to be converted to the browser.

  • es-module-lexerScan import syntax
  • magic-stringOverwrite the import path of the module
// Develop the code
import { createApp } from 'vue'

/ / after the transformation
import { createApp } from '/node_modules/vue/dist/vue.js'
Copy the code

Source code analysis

Similar to Webpack-dev-server, Vite also uses WebSocket to establish a connection with the client to achieve hot update. The source code can be basically divided into two parts, the source code location is:

  • vite/packages/vite/src/clientClient (for clients)
  • vite/packages/vite/src/nodeServer (for development server)

Client code will be injected into the client when the service is started, for the client to process WebSocket messages (such as updating a module of the page, refreshing the page); The Server code is the server-side logic that handles the build of the code and requests for page modules.

A brief look at the source code ([email protected]), the core functions are mainly the following methods (the following is the source code interception, some of the logic has been deleted) :

  1. Command line to start the servicenpm run devAfter, the source code is executedcli.ts, the callcreateServerMethod to create an HTTP service that listens on the development server port.
/ / source location vite/packages/vite/SRC/node/cli. Ts
const { createServer } = await import('./server')
try {
    const server = await createServer({
        root,
        base: options.base,
        ...
    })
    if(! server.httpServer) {throw new Error('HTTP server not available')}await server.listen()
}
Copy the code
  1. createServerThe execution of the method does a lot of work, such as integrating configuration items, creating HTTP services (created earlier through KOA), creatingWebSocketServices, file listening to create source code, plug-in execution, Optimize, etc. Marked in the comments below.
/ / source location vite/packages/vite/SRC/node/server/index. The ts
export async function createServer(
    inlineConfig: InlineConfig = {}
) :Promise<ViteDevServer> {
    // Vite configuration integration
    const config = await resolveConfig(inlineConfig, 'serve'.'development')
    const root = config.root
    const serverConfig = config.server

    // Create an HTTP service
    const httpServer = await resolveHttpServer(serverConfig, middlewares, httpsOptions)

    // Create the WS service
    const ws = createWebSocketServer(httpServer, config, httpsOptions)

    // Create the watcher and set the code file listener
    const watcher = chokidar.watch(path.resolve(root), {
        ignored: [
            '**/node_modules/**'.'**/.git/**'. (Array.isArray(ignored) ? ignored : [ignored]) ], ... watchOptions })as FSWatcher

    // Create a server object
    const server: ViteDevServer = {
        config,
        middlewares,
        httpServer,
        watcher,
        ws,
        moduleGraph,
        listen,
        ...
    }

    // The file listens for changes, and webSocket communicates forwards
    watcher.on('change'.async (file) => {
        ...
        handleHMRUpdate()
    })

    // Lots of middleware
    middlewares.use(...)
    
    // optimize
    const runOptimize = async() = > {... }return server
}
Copy the code
  1. Use Chokidar to listen for file changes and bind listening events.
/ / source location vite/packages/vite/SRC/node/server/index. The ts
  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
Copy the code
  1. throughwsTo create aWebSocketService that triggers hot updates and sends messages to clients when it listens for file changes.
/ / source location vite/packages/vite/SRC/node/server/ws. Ts
export function createWebSocketServer(.){
    let wss: WebSocket
    const hmr = isObject(config.server.hmr) && config.server.hmr
    const wsServer = (hmr && hmr.server) || server

    if (wsServer) {
        wss = new WebSocket({ noServer: true })
        wsServer.on('upgrade'.(req, socket, head) = > {
            // Service ready
            if (req.headers['sec-websocket-protocol'] === HMR_HEADER) {
                wss.handleUpgrade(req, socket as Socket, head, (ws) = > {
                    wss.emit('connection', ws, req)
                })
            }
        })
    } else{... }// When the service is ready, you can see the familiar print [vite] Connected on the browser console.
    wss.on('connection'.(socket) = > {
        socket.send(JSON.stringify({ type: 'connected'}))... })/ / fail
    wss.on('error'.(e: Error & { code: string }) = >{... })// Return the ws object
    return {
        on: wss.on.bind(wss),
        off: wss.off.bind(wss),
        // Send information to the client
        // Multiple clients trigger simultaneously
        send(payload: HMRPayload) {
            const stringified = JSON.stringify(payload)
            wss.clients.forEach((client) = > {
                // readyState 1 means the connection is open
                client.send(stringified)
            })
        }
    }
}
Copy the code
  1. Code is injected into the browser when the service is started to handle what the client receivesWebSocketMessages, such as reinitiating module requests and refreshing pages.
/ / source location vite/packages/vite/SRC/client/client. The ts
async function handleMessage(payload: HMRPayload) {
  switch (payload.type) {
    case 'connected':
      console.log(`[vite] connected.`)
      break
    case 'update':
      notifyListeners('vite:beforeUpdate', payload)
      ...
      break
    case 'custom': {
      notifyListeners(payload.event as CustomEventName<any>, payload.data)
      ...
      break
    }
    case 'full-reload':
      notifyListeners('vite:beforeFullReload', payload)
      ...
      break
    case 'prune':
      notifyListeners('vite:beforePrune', payload)
      ...
      break
    case 'error': {
      notifyListeners('vite:error', payload)
      ...
      break
    }
    default: {
      const check: never = payload
      return check
    }
  }
}
Copy the code

advantage

  • Come on! Come on! Very fast!!
  • Highly integrated, out of the box.
  • ESM based rapid hot update, no need to package compilation.
  • Based on theesbuildThe dependence of preprocessing, thanWebpackWait until Node writes a compiler several orders of magnitude faster.
  • Compatible withRollupHuge plug-in mechanism, plug-in development more concise.
  • Not withVueBinding, supportReactOther frameworks, independent build tools.
  • Built-in SSR support.
  • Naturally supports TS.

insufficient

  • VueStill for first priority support, tailor-made compiled plug-ins, rightReactIs less supported thanVuePowerful.
  • Although the 2.0 official version has been released and can be used for official online production, there is little practice in the market at present.
  • Production environment integrationRollupPackaging, inconsistent with the code ultimately executed by the development environment.

Compared with webpack

Since Vite features the ultimate experience of the development environment, and the production environment integrates Rollup, the comparison here is mainly the comparison between Webpack-dev-server and vite-dev-server:

  • For a long time nowWebpackDominated the field of front-end engineering,ViteSince its launch, GitHub star has received a lot of attention and the community has been active. The number of GitHub Stars has increased rapidly and now reaches 37.4K

  • WebpackRich configuration, extremely flexible to use, but expensive to get started,ViteOut of the box configuration is highly integrated
  • WebpackStarting a service requires a slow package build,ViteNo compilation can be opened in seconds
  • WebpackHot updates need to be packaged and built slowly,ViteMillisecond response
  • WebpackMature and stable, rich in resources, a large number of practical cases,VitePractice is less
  • ViteuseesbuildCompile, build faster thanwebpackOrders of magnitude faster

compatibility

  • The default target browser is inscriptNative ESM and native ESM dynamic import are supported on the label
  • You can use the official plug-in@vitejs/plugin-legacyEscape to the traditional version and its counterpartpolyfill

Future exploration

  • The performance of traditional build tools has reached a bottleneck, focusing on the development experienceVite“Are likely to be popular.
  • Mainstream browsers basically support ESM, and ESM will become mainstream.
  • ViteinVue3.0Instead ofvue-cli, as official scaffolding, will greatly increase usage.
  • Vite2.0After the launch, it can be used in actual projectsVite.
  • Use directly if you feel like itViteIt’s too risky, and it isdev serverSlow speed problems need to be solved, you can try to useViteBuild a separate setdev server

The related resources

The official plug-in

In addition to supporting the existing Rollup plug-in system, four of the most critical plug-ins are officially available

  • @vitejs/plugin-vueSupports Vue3 single file components
  • @vitejs/plugin-vue-jsxVue3 JSX support (dedicated Babel conversion plug-in)
  • @vitejs/plugin-reactProvides full React support
  • @vitejs/plugin-legacyTraditional browser compatibility support for packaged files

The UI component library

  • Element UI: Supports vite introduction

A link to the

  • Vite website
  • Vue3 Ecological progress and plan – Yu Yu Creek
  • Vite source code analysis
  • Develop with Vite | Vite quickstart – Anthony Fu DE Vue Beijing party Day 13