vite简介、原理及简单实现

文章目录

vite简介:

vite基于es modules 实现,面向现代浏览器的更轻、快的Web工具
解决webpack 开发阶段 devServer 冷启动时间过长,HMR 热更新反应速度慢的问题;

1、快速冷启动(对比vue-cli):

1、使用vite创建的项目就是一个普通的v3应用,相比于vue-cli创建的项目也少了很多配置文件和依赖;
vite项目依赖:Vite、@vue/compiler-sfc
vite serve; vite build
2、vite是直接启动服务器,请求之后将文件开始编译传递给服务器(即时编译,按需速度会更快,开发阶段无需存储);
3、vue-cli是先打包好bundle存储,请求之后发送bundle给服务器解析,因此打包bundle的过程受项目大小和依赖的影响;

2、模块热更新 HMR:

vite HMR:立即编译当前所修改的文件
webpack HMR:会自动以这个文件为入口重写build一次,所有涉及到的依赖都会被加载一遍

3、按需编译 build

vite build:

生产环境下使用rollup打包,还是会提前打包编译到一起;
动态导入Dynamic import,只支持现代浏览器,可用Polyfill辅助;

使用webpack打包原因(打包起因):

浏览器环境不支持模块化; — 现代浏览器对es module的支持
零散的模块文件产生大量的http请求; — http2 多路复用

4、开箱即用:

ts(内置),less/sass/stylus/postcss - 内置需单独安装,JSX,Web Assembly

vite特性总结:

快速冷启动、模块热更新、按需编译、开箱即用(避免loader、plugin配置)

vite原理

核心功能分析:静态Web服务器、编译单文件组件(拦截浏览器不识别的模块并处理)、HMR

1、静态服务器

基于 koa、koa-send

2、处理模块处理的路径,如 from ‘vue’ -> from ‘/@modules/vue.js’

ctx.type === ‘application/javascript’

contents.replace(/(from\s+['"])(?![\.\/])/g, '$1/@modules/')
<!-- /?!/ 表示不匹配的 $1 分组匹配到的内容 -->

3、加载第三方模块,如 /@modules/vue.js

校验路由,调整ctx.path为实际地址:

const pkg = require(pkgPath)
ctx.path = path.join('/node_modules', moduleName, pkg.module)
// pkg.module 即pkg的package.json的入口文件的值

4、处理单文件模块

单文件组件编译成一个选项对象,返回给浏览器;
把模板编译成render函数(ctx.requery.type===‘template’)

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

浏览器下找不到process - /shared;开发环境下process.env.xx -> 对应值;如NODE_ENV -> development

vite初步实现:

#!/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) // null 标识流写完了
  return stream
}

// 3. 加载第三方模块
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 即pkg的package.json的入口文件的值,也是一个路径;如 vue对应 "module": "dist/vue.runtime.esm-bundler.js"
  }
  await next()
})

// 1. 静态文件服务器
app.use(async (ctx, next) => {
  await send(ctx, ctx.path, { root: process.cwd(), index: 'index.html' })
  await next()
})

// 4. 处理单文件组件
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 }) // 组件中的模板
      code = templateRender.code // code即render函数
    }
    ctx.type = 'application/javascript'
    ctx.body = stringToStream(code)
  }
  await next()
})

// 2. 修改第三方模块的路径
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')
上一篇:vue3-vite相对路径处理


下一篇:React 17 + Vite + ECharts 实现疫情数据可视化「06 完成疫情地图绘制」