Vite 概念
- Vite 是一个面向现代浏览器的一个更轻,更快地Web应用开发工具
- 它基于 ECMAScript 标准原生模块系统 (ES Modules)实现
它的出现解决 webpack 在开发阶段 使用webpack-dev-server 冷启动时间过长,webpack HMR热更行反应慢的问题
Vite 项目依赖
- Vite
- @vue/compiler-sfc 编译项目中.vue结尾的单文件组件
基础使用
- vite serve
-
vite build
vite-serve.png
vue-cli-service.png
vite即时编译,只有在请求某个文件时才会去编译与其相关的文件
HMR
- Vite HMR
- 立即编译当前所修改的文件
- Webpack HMR
- 会自动以这个文件为入口重写 build 一次,所有的涉及到的依赖也都会被加载一遍
打包or不打包
- 使用Webpack打包的两个原因
- 浏览器环境并不支持模块化
- 零散的模块文件会产生大量的HTTP请求
开箱即用
- TypeScript -内置支持
- lese/sass/stylus/postcss 内置支持(需要单独安装)
- JSX
- Web Assembly
Vite 特性
- 快速冷启动
- 模块热更新
- 按需编译
- 开箱即用
Vite实现原理-静态Web服务器
核心功能
- 静态 Web 服务器
- 编译单文件组件
- 拦截浏览器不识别的模块,并处理
- HMR
基于Koa实现静态web服务器
package.json 配置bin 入口文件
{
"name": "vite-cli",
"version": "1.0.0",
"description": "",
"main": "index.js",
"bin": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@vue/compiler-sfc": "^3.0.0-rc.10",
"koa": "^2.13.0",
"koa-send": "^5.0.1"
}
}
index.js
#!/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)
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)
}
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
}
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')
网友评论