Brief introduction, principle and simple implementation of vite

vite introduction:

vite is a lighter and faster Web tool for modern browsers based on es modules
Solve the problems of too long cold start time of devServer and slow response speed of HMR hot update in webpack development stage;

1. Quick cold start (comparison vue-cli):

1. A project created with vite is a common v3 application, which has fewer configuration files and dependencies than a project created with Vue cli;
Vite project dependencies: vite, @ Vue / compiler SFC
vite serve; vite build
2. vite is to directly start the server, and then start compiling the file and pass it to the server after the request (instant compilation will be faster on demand, and there is no need to store it in the development stage);
3. Vue cli packages the bundle storage first, and then sends the bundle to the server for resolution. Therefore, the process of packaging the bundle is affected by the project size and dependency;

2. Module hot update HMR:

vite HMR: compile the currently modified file immediately
webpack HMR: it will automatically rewrite the build with this file as the entry, and all the dependencies involved will be loaded again

3. Compile build on demand

vite build:

In the production environment, rollup is used for packaging, or it will be packaged and compiled in advance;
Dynamic import, which only supports modern browsers, can be assisted by Polyfill;

Packaging reason using webpack (packaging reason):

The browser environment does not support modularization; -- Modern browser support for es module
Scattered module files generate a large number of http requests; - http2 multiplexing

4. Out of the box:

TS (built-in), less / sass / style / postcss - built-in needs to be installed separately, JSX, Web Assembly

vite feature summary:

Quick cold start, module hot update, on-demand compilation, out of the box (avoid loader and plugin configuration)

vite principle

Core function analysis: static Web server, compiling single file components (intercepting and processing modules not recognized by the browser), HMR

1. Static server

Based on koa and koa send

2. The processing path of the processing module, such as from 'vue' - > from '/ @ modules/vue.js'

ctx.type === 'application/javascript'

contents.replace(/(from\s+['"])(?![\.\/])/g, '$1/@modules/')
<!-- /?!/ Indicates a mismatch $1 Group matched content -->

3. Load third-party modules, such as / @ modules/vue.js

Verify the route and adjust ctx.path to the actual address:

const pkg = require(pkgPath)
ctx.path = path.join('/node_modules', moduleName, pkg.module)
// pkg.module is the value of the pkg package.json entry file

4. Processing sheet file module

The single file component is compiled into an option object and returned to the browser;
Compile the template into render function (ctx.requery.type = = 'template')

compilerSFC = require('@vue/compiler-sfc')
const templateRender = compilerSFC.compileTemplate({ source: descriptor.template.content })
code = templateRender.code

process - /shared cannot be found in the browser; Process.env.xx - > corresponding value in the development environment; Such as node_ ENV -> development

vite preliminary implementation:

#!/usr/bin/env node
const path = require('path')
const { Readable } = require('stream')
const Koa = require('koa')
const send = require('koa-send')
const compilerSFC = require('@vue/compiler-sfc')

const app = new Koa()

const streamToString = stream => new Promise((resolve, reject) => {
  const chunks = []
  stream.on('data', chunk => chunks.push(chunk))
  stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')))
  stream.on('error', reject)
})

const stringToStream = text => {
  const stream = new Readable()
  stream.push(text)
  stream.push(null) // The null identity stream has been written
  return stream
}

// 3. Load the third-party module
app.use(async (ctx, next) => {
  // ctx.path --> /@modules/vue
  if (ctx.path.startsWith('/@modules/')) {
    const moduleName = ctx.path.substr(10)
    const pkgPath = path.join(process.cwd(), 'node_modules', moduleName, 'package.json')
    const pkg = require(pkgPath)
    ctx.path = path.join('/node_modules', moduleName, pkg.module)
    // pkg.module is the value of the pkg package.json entry file, and it is also a path; For example, vue corresponds to "module": "dist/vue.runtime.esm-bundler.js"
  }
  await next()
})

// 1. Static file server
app.use(async (ctx, next) => {
  await send(ctx, ctx.path, { root: process.cwd(), index: 'index.html' })
  await next()
})

// 4. Processing sheet file component
app.use(async (ctx, next) => {
  if (ctx.path.endsWith('.vue')) {
    const contents = await streamToString(ctx.body)
    const { descriptor } = compilerSFC.parse(contents)
    let code
    if (!ctx.query.type) {
      code = descriptor.script.content
      // console.log(code)
      code = code.replace(/export\s+default\s+/g, 'const __script = ')
      code += `
      import { render as __render } from "${ctx.path}?type=template"
      __script.render = __render
      export default __script
      `
    } else if (ctx.query.type === 'template') {
      const templateRender = compilerSFC.compileTemplate({ source: descriptor.template.content }) // Templates in components
      code = templateRender.code // code is the render function
    }
    ctx.type = 'application/javascript'
    ctx.body = stringToStream(code)
  }
  await next()
})

// 2. Modify the path of the third-party module
app.use(async (ctx, next) => {
  if (ctx.type === 'application/javascript') {
    const contents = await streamToString(ctx.body)
    // import vue from 'vue'
    // import App from './App.vue'
    ctx.body = contents
      .replace(/(from\s+['"])(?![\.\/])/g, '$1/@modules/')
      .replace(/process\.env\.NODE_ENV/g, '"development"')
  }
})

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

Keywords: Javascript Vue Vue.js vite

Added by bfinucan on Fri, 22 Oct 2021 11:07:54 +0300