With vite, do I still need webpack?

Posted Jun 26, 20207 min read

Original statement:This article was first published on the public account:front-end trivia(qianduansuohua), welcome to follow

Foreword

Youda mentioned a vite tool in the vue 3.0 beta live broadcast two days ago, and also tweeted that she would never go back to webpack, and also attracted the core developer of webpack Sean Funny reply, let's take a look at the magic of vite.

What is Vite?

github: https://github.com/vitejs/vite

Vite is a web development and construction tool driven by native ESM. Based on the browser's native ES imports development in the development environment, based on Rollup packaging in the production environment.

It mainly has the following characteristics:

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

Without further ado, let's try it out first.

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

Let's take a look at the generated code, because vite tries to mirror the default configuration in vue-cli as much as possible, so we will find that it looks not much different from the code generated by vue-cli.

    index.html
    package.json
    public
      favicon.ico
    src
        App.vue
        assets
          logo.png
        components
          HelloWorld.vue
        index.css
        main.js

Then we look at the entrance index.html and main.js

<!DOCTYPE html>
<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 quote the latest vue3 syntax, the rest is no different
import {createApp} from'vue'
import App from'./App.vue'
import'./index.css'

createApp(App).mount('#app')

Found the main difference is that there is such a thing

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

Then let's take a look at what this is?

principle

ESM

script module is the implementation of the ES module on the browser side, which is currently supported by mainstream browsers

Its biggest feature is to use export and import to import and export modules on the browser side, and set type="module" in the script tag

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

The browser will recognize the <script> element with the addition of type="module", the browser will consider this inline script or external link script as an ECMAScript module, and the browser will import its internal import Initiate an http request to obtain the module content.
In main.js, we use named export to export the createApp function, which can be obtained in the script above

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

In fact, here, we can basically understand several features that vite claims.

  • Packing tools such as webpack will use glue code to assemble each module in order to load each module in the browser. For example, webpack uses map to store the module id and path, and uses the webpack_require method to get the module export, vite Using the browser's native support for modular import, the assembly of modules is omitted, and there is no need to generate bundles, so cold start is very fast
  • The packaging tool will package each module into the bundle in advance, but the packaging process is static-no matter whether the code of a certain module is executed, this module must be packaged into the bundle, such a disadvantage is that as the project gets more and more The bigger the package, the bigger and bigger the bundle. And ESM is born to be loaded on demand, only when imported will be loaded on demand

When I see here, I wonder if what vite actually does. We just use the browser s ESM. It s fine, so let s try it.

Vite running

Provide web server

In the code base we just generated, instead of starting the project through npm run dev, open index.html directly through the browser, you will see the following error

Using ES module in the browser is to use http request to get the module, so one of the tasks of vite is to start a web server to proxy these modules, and vite borrowed 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
}

Module analysis

Let's start a static service locally, then open index.html to see

It probably means that the import path starting with "/", "./", or "../" cannot be found if the module vue is not found.

import vue from'vue'

In other words, the ESM in the browser cannot obtain the content of the imported module. Usually we write code. If it is not a module that refers to a relative path, but a module that refers to node_modules, it is directly import xxx from'xxx' , tools such as Webpack will help us find the specific path of this module for packaging. But the browser does not know that you have node_modules in your project, it can only find modules by relative path or absolute path.

Then this leads to an implementation core of vite-intercept the browser's request for the module and return the processed result**

Let's see how vite is handled?

/@module/ prefix

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

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

createApp(App).mount('#app')

became

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

createApp(App).mount('#app')

In order to solve the problem of import xxx from'xxx' errors, vite does a unified treatment of this resource path, adding a /@module/ prefix.
We can see in the koa middleware of src/node/server/serverPluginModuleRewrite.ts source code that vite has done a layer of import processing. The process is as follows:

  • Get request body in koa middleware
  • Use es-module-lexer to parse the resource ast and get the content of import
  • Determine whether the imported resource is an absolute path, absolutely regarded as an npm module
  • Return the processed resource path:"vue" => "/@modules/vue"

Support /@module/

You can see in /src/node/server/serverPluginModuleResolve.ts that the approximate processing logic is

  • Get request body in koa middleware
  • Determine whether the path starts with /@module/, if it is to remove the package name
  • Go to node_module to find this library and return the corresponding content based on package.json

File compilation

What we mentioned above is the processing of ordinary js modules. How are other files, such as vue, css, ts, etc. handled?

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

By comparing the App.vue under the project with the actual App.vue loaded in the development environment, it is found that the content has changed

Original App.vue

<template>
  <img alt="Vue logo" src="./assets/logo.png" />
  <HelloWorld msg="Hello 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>

became

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

This will split the original .vue file into three requests(corresponding to script, style and template respectively), the browser will first receive the App.vue response containing the script logic, and then parse it into the template and style After the path, it will initiate an HTTP request again to request the corresponding resource. At this time, Vite intercepts it and processes it again, and then returns 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

//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:"Hello Vue 3.0 + Vite"
    })], 64 /* STABLE_FRAGMENT */
   ))
}

In fact, after seeing this idea, the processing of other types of files is almost similar logic, and different compilations are made according to the different file types requested.

Actually, vite implements real-time on-demand compilation by intercepting requests on the basis of on-demand loading

Afterword

So far we have basically understood the principle of vite. Although it is impossible to completely replace webpack in the current ecology, it is after all a kind of exploration of new solutions.
In fact, in addition to vite, there are similar programs in the community snowpack , those who are interested can go to find out.