Vite 是一个面向现代浏览器的更轻、更快的 Web 应用开发工具,它基于 ECMAScript 标准原生模块系统(ES Modules)实现。它启动应用不需要像 webpack 那样先把整个项目编译打包一次,它是按需编译的,只有浏览器请求到模块的时候,才编译这个模块。热更新也是只重新编译当前修改的模块,webpack 会以这个模块为入口,把依赖也重新打包一次。
Vite 手写实现
#!/usr/bin/env nodeconst 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/vueif (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 codeif (!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 = __renderexport 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')
