Study: https://github.com/zelixag/toyvite

This is the first day of my participation in the Gwen Challenge.More article challenges

1. Vite concept

  • Vite is a lighter, faster Web application development tool for modern browsers
  • It is implemented based on the ECMAScript standard native module system (ES Modules)

Vite is a solution to the webpack development phase using webpack-dev-server cold start time is too long. Webpack HMR is slow to respond to hot updates, leading to poor development experience.

A project built using Vite is a normal vue3 application, with few special features and fewer configuration files and dependencies than a VUE-CLI project. Vite creates projects that rely on extraordinary simplicity for development. Only Vite and @vue/ Compiler-SFC.

  • Vite: command line tool that makes us wait to implement later
  • @vue/ Compiler-sFC is used to compile the single file component at the end of the. Vue in our project. Vue2 uses vuE-template-compiler

Vite currently only supports vuE3, but you can also select a different template to support other frameworks when creating it.

2. Basic usage

Sons command

  • Vite Serve: a development Web server for development, you don’t need to compile all the code files to start up, it is very fast, we look at the following:

When you run Vite Serve, you don’t need to package it. You run a Web server directly. When the browser makes a request to the server, for example, when a file is assembled, the server side compiles a single file component, and then returns the compiled result to the browser. Module processing is requested to the server side for processing.

Review vuE-CLI-service serve and look at the following figure:

When running vue-CLI-service serve, webpack will be used internally to package all modules first. If there are too many modules, the packaging speed will be very slow. The packing results will be stored in memory, and then the web server developed will be started. The browser requests the server, and the server packs up the stored memory and returns the result to the browser. Webpack will pre-compile and pack all modules in the server memory. No matter whether modules are executed or called, they will be pre-compiled and packed into the bundle. As the project gets bigger and bigger, the packaged bundle will get bigger and bigger, and the packaging speed will be slower and slower.

Vite borrows from modern browsers’ native ESModule support, omitting module packaging for files that need to be compiled, such as single-file files, sass style modules, etc. Another mode used by Vite is just-in-time compilation, which means that a file is compiled only when the browser requests it. The advantage of this just-in-time compilation is that it builds on demand and is faster.

- Vite HMR - immediately compiles the current modified file - Webpack HMR - will automatically rewrite the build with this file as the entry, and all involved dependencies will be loadedCopy the code

As you can imagine, Vite HMR performance will be better

  • Vite build:
    • Rollup: Is the package Rollup used, or is the file compiled ahead of time and packed together
    • Dynamic Import cutting is implemented in Vite using the Dynamic Import native Dynamic import feature, so the packaging results are still supported by modern browsers
    • Polyfill: However, the dynamic import feature is still handled by Polyfill

The emergence of Vite raises a question worth pondering

Think: Pack or not pack

  • There are two reasons to use Webpack packaging:
    • Browser environments do not support modularity Most browsers support esModule
    • Fragmented module files can create a lot of HTTP requests http2 can help us solve, can take links

ESModule support

If you also need to be compatible with IE, you can’t build your project using Vite

Out of the box

  • TypeScript – Built-in support
  • Less/SASS/Stylus/PostCSS – Built-in support (requires separate installation of corresponding compiler)
  • Support JSX
  • Support WebAssembly

.

Vite features

  • The server compiles and returns who is required for a quick cold start
  • Module hot updates faster who updates requests who, the server compiles and returns
  • By compiling on demand, the server compiles and returns files that are not used by the compilation
  • Avoid various loaders and plugin configurations out of the box

Vite core functions

  • Static Web server
    • The static server intercepts part of the request
  • Compile single-file components
    • Intercepts other modules that the browser does not recognize and handles
  • HMR

Next we will implement a command line tool that can start a static Web server. Vite uses koA internally to start a static Web server. We will also use KOA to start a static Web server. Create a Node project

Initialize package.json file and install KOA and KoA-send

{
  "name": "vite-cli"."version": "1.0.0"."main": "index.js"."bin": "index.js"."license": "ISC"."dependencies": {
    "koa": "Tokens ^ 2.13.1"."koa-send": "^ 5.0.1." "}}Copy the code

The main js file

#! /usr/bin/env node

const Koa = require('koa');
const send = require('koa-send');


const app = new Koa()

// 1. Static file server
app.use(async (ctx, next) => {
  // The first parameter context, the current requested path, the current node program running directory, the default page
  await send(ctx, ctx.path, {root: process.cwd(), index: 'index.html'})
  await next()
})

app.listen(3000);
console.log('Server running @ http://localhost:3000')

Copy the code

We will thus start a static server created by koA. After we write the above code, we will globally soft link the Node project using NPM link. Then create a vuE3 project using the official Vite and execute vite-CLI under this project. After each code update, you need to re-execute vite-CLI in this VUe3 project to see the effect of the update

Error: We failed to parse the vue module, we used import to import the module requiring relative paths, ‘/’, ‘./’, ‘.. / ‘

As shown in the figure, we found that the path strength of vue did not have the three relative paths mentioned above, and the browser did not recognize them, resulting in an error. We have to solve this problem. We can see how Vite solves this problem:

We start a vite created project:

  • Found that Vite processed the path to a non-existent path on the server,/@modules/vue.jsThe path doesn’t exist on the server, we didn’t create the @modules folder at all. The server should handle the request, loading our vue module when it has @modules, that’s the idea.

  • Look at the mian. Js request header, which tells the browser that the file returned is javascript;

Now that you’ve started a static Web server, how do you load a third-party module

Loading a third-party Module

Two middleware pieces need to be created:

  • The first requires the path change in import to be changed to/@modules/ The name of the third-party module
  • The second one determines whether the request path contains the request when the request comes in/@modules/ The name of the third-party moduleIf so, go to node_modules to load the corresponding third-party modules
  1. Implement the first middleware, adding @modules to the third-party module path
Const streamToString = stream => new Promise((resolve, reject) => {const chunks = []) On ('data', chunk => chunks.push(chunk)) // Return the stream data to stream.on('end', () => resolve(buffer.concat (chunks).tostring (' utF-8 '))) App.use (async (CTX, next) => {if(ctx.type === 'application/javascript') {// ctx.body is a stream, but we want to convert the stream file to a string. Const contents = await streamToString(ctx.body); const contents = await streamToString(ctx.body); // import vue from 'vue' // import App from './ app.vue '// ctx.body = contents. Replace (/(from\s+['"])(? ! [\.\/])/g, '$1/@modules/') } })Copy the code

Ctx.type === ‘application/javascript ‘, then convert the read content to a string, and replace contents. Replace (/(from\s+[‘”])(? ! \.\/)/g, ‘$1/@modules/’) finally get what we want, $400 doesn’t matter, the next middleware handles 404, our third party module loads successfully

  1. Second middleware, if the request path is/@modules/To start, we request that the path be changed to the file path for node_modules, and then continue to hand it over to the static file middleware
// 3. Load the third-party module
app.use(async (ctx, next) => {
  // ctx.path --> /@modules/vue
  if(ctx.path.startsWith(`/@modules/`)) {
    // Get the module name
    const moduleName = ctx.path.substr(10);
    // We need to get the module entry file. The entry to esModules, which uses the path module to concatenate paths
    // The first parameter is the current project and path, the second parameter is node_modules and the third parameter finds the package.json file path of the module
    const pkgPath = path.join(process.cwd(), 'node_modules', moduleName, 'package.json');
    // Use require to get the module
    const pkg = require(pkgPath)
    // The first parameter is in the node_modules folder, the second parameter is in the third-party module folder, and the third parameter is in the third-party package entry file
    ctx.path = path.join('/node_modules', moduleName, pkg.module)
  }
  await next();
})

Copy the code

Through the above code, the startup project found that the third-party module vue started successfully, but the third-party module browser inside is not loaded, why?

We observed that the browser failed to load app.vue and index.css because the browser did not recognize the module when loading the component, and did not recognize the CSS module when loading the CSS file. So the server also needs to process modules that the browser can’t recognize, and then return files that the browser can recognize after compilation.

Compile single-file components

The single file component module introduced by main.js and the style module are not recognized by the browser. The browser can only process JS modules, so other modules must be processed on the server side. When requesting single file modules, the single file component needs to be compiled into JS modules on the server and then returned to the browser. Now you can open a browser and see how single-file components are handled in Vite.

Vite compiles the single-file component into an object, loads the HelloWord component first, and then creates a component options object that has no template because the template eventually needs to be converted to the Render function and mounted onto the options object

Then I load app.vue again, passing a parameter type=template. This time the request is telling the server to help me bind the template of the single-file component. It then returns a render function. Then mount the Render function onto the component option object created above. The next two options are __hmrId and __file, and finally the component optional object is exported

From this process, we can see that when the browser requests the single-file component, the server will compile the single-file component file and return the corresponding result to the browser.

How single-file components are handled in Vite:

  • The request is sent twice
  • Compile the single file into a component object on the first request
  • The second request compiles the single-file component template, returns a Render function, and mounts the render function onto the previously generated component object

Let’s do this in code:

const stringToStream = text= > {
  const stream = new Readable;
  stream.push(text);
  stream.push(null);
  return stream;
}
// 4. Handle single-file components
app.use(async (ctx, next) => {
  // Process single-file components in two batches
  // First check if it is a single-file component
  if(ctx.path.endsWith('.vue')) {
    const contents = awit streamToString(ctx.body);
    // Returns an object, a single-file component description object
    const {descriptor} = compilerSFC.parse(contents)
    let code
    if(! ctx.query.type ==='template') {
      code = descriptor.script.content;

      code = code.replace(/export\s+default\s/g.'const __script = ')
      code+= `
      import { render as __render } from "${ctx.path}? type=template" __script.render = __render console.log('haha') export default __script `
    }
    ctx.type = 'appliction/javascript'
    ctx.body = stringToStream(code)
  }
  await next()
})

Copy the code

This code should be placed in an intermediate place before loading the third party module, and then loading the third party component after processing

We can see that after processing the above code, the server returns exactly what we want. But the browser still has two errors

Error: the CSS module is not loaded. Error: the CSS module is not loaded. In order not to interfere with the second request, we annotate both the CSS import and img module import in the Vite create Vue3 project first

Process the second request

If the query.type attribute value is template, the second request needs to be processed

// 4. Handle single-file components
app.use(async (ctx, next) => {
  // Process single-file components in two batches
  // First check if it is a single-file component
  if(ctx.path.endsWith('.vue'{...}))else if(ctx.query.type === 'template') {
      The // compilerSFC object has a method that takes an object as an argument and is the content of the compilertemplate
      const templateRender = compilerSFC.compileTemplate({source: descriptor.template.content})
      code = templateRender.code
    }
    ctx.type = 'application/javascript';
    ctx.body = stringToStream(code);
  }
  await next()
})
/ / 2
Copy the code

The browser will still be blank when compilerSFC is used and an error will be reported. Process is used in the Node environment and the server will return this variable without processing it.

Process. Env.NODE_ENV is used to package production environment or development environment, but we do not use packaging tools now. The server must process it before it can return.

So the server will return code that contains process.env.node_env to “development”

// 2. Modify the path of the third-party module
app.use(async (ctx, next) => {
  if(ctx.type === 'application/javascript') {
    The value of ctx.body is a stream, but we want to convert the stream file to a string. In order to change the path of the third party module, and the other positions of this function need to use a method of our cut
    const contents = await streamToString(ctx.body);
    // import vue from 'vue'
    // import App from './ app.vue
    ctx.body = contents
      // The search string is matched at the beginning of any string that does not match pattern. This match is not required for future use. Such as "Windows (? ! 95 NT | | 98 | 2000) "can match" Windows3.1 "in the" Windows ", but it can't match "Windows" in the "Windows".
      // import vue from 'vue' ---> import vue from '/@modules/vue'
      // import db from '.. /db/index' ---> import db from '.. /db/index'
      .replace(/(from\s+['"])(? ! [\.\/])/g.'$1/@modules/')
      .replace(/process\.env\.NODE_ENV/g.'"development"')}})Copy the code

Allow commands in projects created in vitevite-cliThe effect finally came out.

No style is correct, because we have no dealing with CSS module, nor will the CSS modules into js module, so if you want to do can be more single file conversion components, will return to the browser CSS module to compile in should also will be very easy, we know vite about principle, the rest can be easily implemented step by step, I believe, Well the third day of vue3 learning, welcome to view, follow, like, comment!!