Original statement: This article was published on the public account: Qianduansuohua, welcome to follow

preface

Uvu mentioned a vite tool during the vUE 3.0 beta broadcast, and tweeted that there would be no more Webpack. Sean, one of the core developers of WebPack, replied with a funny reply, so let’s take a look at the magic of Vite.

What is a Vite?

github:github.com/vitejs/vite

Vite is a native ESM-driven Web development build tool. Developed based on browser native ES Imports in development environment and packaged based on Rollup in production environment.

It mainly has the following characteristics:

  • Quick cold start
  • Instant module hot update
  • True on-demand compilation

Let’s cut the crap and get straight to a trial run.

$ npm init vite-app <project-name>
$ cd <project-name>
$ npm install
$ npm run dev
Copy the code

Let’s take a look at the generated code, because Vite tries to mirror as much of the default configuration in VUE-CLI as possible, so it doesn’t look too different from the vue-CLI generated code.

├ ─ ─ index. The HTML ├ ─ ─ package. The json ├ ─ ─ public │ └ ─ ─ the favicon. Ico └ ─ ─ the SRC ├ ─ ─ App. Vue ├ ─ ─ assets │ └ ─ ─ logo. The PNG ├ ─ ─ ├─ ├─ class.org.txtCopy the code

So let’s look at the entries index.html and main.js


      
<html lang="en">
<head>
  <meta charset="UTF-8">
  <link rel="icon" href="/favicon.ico" />
  <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
  <title>Vite App</title>
</head>
<body>
  <div id="app"></div>
  <script type="module" src="/src/main.js"></script>
</body>
</html>// main.js // just reference the latest vue3 syntax, Import {createApp} from 'vue' import App from './ app.vue 'import './index.css' createApp(App).mount('# App ')Copy the code

The main difference, I found, was the addition of this

<script type="module" src="/src/main.js"></script>
Copy the code

So let’s see what is this?

The principle of

ESM

Script Module is the realization of ES module in the browser, which is supported by the mainstream browsers

The most important feature is to import and export modules in the browser by using export and import, and set type=”module” in the script tag.

<script type="module">
  import{createApp} from './main.js';
  createApp();
</script>
Copy the code

The browser recognizes the

// main.js
export function createApp(){
    console.log('create app! ');
};
Copy the code

At this point, we should be able to understand some of the features vite claims.

  • Packaging tools like Webpack use glue code to assemble modules in order to load them in the browser. For example, Webpack uses map to store module ids and paths, and uses the webpack_require method to get module exports. Vite takes advantage of the browser’s native support for modular imports, bypassing module assembly and eliminating the need to generate bundles, so cold starts are fast
  • The packaging tool packs modules into bundles in advance, but the packaging process is static — regardless of whether a module’s code is executed, the module is packed into the bundle. The downside of this is that the bundle gets bigger and bigger as the project gets bigger and bigger. The ESM is inherently loaded on demand, and only loads on demand when an import is required

If you wonder what vite does, let’s just use ESM in the browser.

Vite run

Provide the web server

In the code base we just generated, instead of starting the project with NPM run dev, open the index.html directly from the browser and see the following error

Using ES Modules in the browser uses HTTP requests to fetch modules, so one of vite’s tasks is to start a Web server to proxy these modules. Vite borrows koA to start a service

export function createServer(config: ServerConfig) :Server {
  // ...
  const app = new Koa<State, Context>()
  const server = resolveServer(config, app.callback())
  
  // ...
  const listen = server.listen.bind(server)
  server.listen = (async (. args:any[]) = > {if (optimizeDeps.auto ! = =false) {
      await require('.. /optimizer').optimizeDeps(config)}return listen(. args)}) as any
  
  return server
}
Copy the code

Module parse

Let’s create a static service locally and open index.html again

Vue, “/”, “./”, or “.. Import paths starting with /” are valid.

import vue from 'vue'
Copy the code

Import XXX from ‘XXX’, import XXX from ‘XXX’, import XXX from ‘XXX’, import XXX from ‘XXX’, import XXX from ‘XXX’ Tools such as Webpack will help us find the specific path of this module for packaging. But the browser doesn’t know you have node_modules in your project, so it can only find modules in relative or absolute paths.

This brings us to one of vite’s implementation cores – intercepting browser requests for modules and returning the results

Let’s see how vite works, right?

/@module/The prefix

By comparing main.js in the project with main.js in the development environment, it is found that the main.js content has changed

import { createApp } from 'vue'
import App from './App.vue'
import './index.css'

createApp(App).mount('#app')
Copy the code

Turned out to be

import { createApp } from '/@modules/vue.js'
import App from '/src/App.vue'
import '/src/index.css? import'

createApp(App).mount('#app')
Copy the code

In order to solve the problem of import XXX from ‘XXX’, Vite does a uniform handling of the resource path, adding a /@module/ prefix. We are in the SRC/node/server/serverPluginModuleRewrite ts source this koa middleware can see vite did a layer of processing for the import, the process is as follows:

  • Get the request body in the KOA middleware
  • Use es-module-lexer to parse the ast resource to get the contents of the import
  • Check whether the imported resource is an absolute path and consider it as an NPM module
  • Return the processed resource path: “vue” => “/@modules/vue”

support/@module/

In/SRC/node/server/serverPluginModuleResolve ts can be seen in the processing logic is probably

  • Get the request body in the KOA middleware
  • Check whether the path starts with /@module/, if the package name is retrieved
  • Go to node_module and find the library and return the corresponding contents based on package.json

File to compile

What about other files such as Vue, CSS, TS, etc.?

Let’s take a look at the vue file as an example. In Webpack we use vue-loader to compile single-file components. In fact, Vite also intercepts the module request and performs a real-time compilation.

By comparing the app. vue under engineering with the actual loaded app. vue under development environment, it is found that the content has changed

The original App. Vue

<template>
  <img alt="Vue logo" src="./assets/logo.png" />
  <HelloWorld msg=Vue 3.0 + Vite />
</template>

<script>
import HelloWorld from './components/HelloWorld.vue';

export default {
  name: 'App'.components: {
    HelloWorld,
  },
};
</script>
<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>
Copy the code

Turned out to be

import HelloWorld from '/src/components/HelloWorld.vue';

const __script = {
    name: 'App'.components: {
        HelloWorld,
    },
};

import "/src/App.vue? type=style&index=0&t=1592811240845"
import {render as __render} from "/src/App.vue? type=template&t=1592811240845"
__script.render = __render
__script.__hmrId = "/src/App.vue"
__script.__file = "/Users/wang/qdcares/test/vite-demo/src/App.vue"
export default __script
Copy the code

This splits a.vue file into three requests (script, style, and template). The browser receives the response from app. vue that contains the script logic, parses it to the template and style paths. An HTTP request is made again to request the corresponding resource, which Vite intercepts and processes again before returning the corresponding content.

// App.vue? type=style
import { updateStyle } from "/vite/hmr"
const css = "\n#app {\n  font-family: Avenir, Helvetica, Arial, sans-serif;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n  text-align: center;\n  color: #2c3e50;\n  margin-top: 60px;\n}\n"
updateStyle("7ac74a55-0", css)
export default css
Copy the code
// App.vue? type=template
import {createVNode as _createVNode, resolveComponent as _resolveComponent, Fragment as _Fragment, openBlock as _openBlock, createBlock as _createBlock} from "/@modules/vue.js"

const _hoisted_1 = /*#__PURE__*/
_createVNode("img", {
    alt: "Vue logo".src: "/src/assets/logo.png"
}, null.- 1 /* HOISTED */
)

export function render(_ctx, _cache) {
    const _component_HelloWorld = _resolveComponent("HelloWorld")

    return (_openBlock(),
    _createBlock(_Fragment, null, [_hoisted_1, _createVNode(_component_HelloWorld, {
        msg: Vue 3.0 + Vite})].64 /* STABLE_FRAGMENT */))}Copy the code

In fact, after seeing this idea, the processing of other types of files is almost similar logic, depending on the file type requested, different compilation processing.

In fact, Vite does on-demand compilation in real time by intercepting requests on top of load on demand

After the language

Here we actually have a basic understanding of the principle of Vite, although it is not possible to completely replace Webpack under the current ecology, but it is a new solution to explore. In fact, in addition to Vite, there is a similar solution in the community, snowpack, if you are interested to check out.