手写一个简化版的Vite

702 阅读1分钟

手写一个简化版的Vite需要实现以下几个核心功能:

  1. 开发服务器
  2. 模块解析和加载
  3. 热更新(HMR)
  4. 构建打包

以下是一个基本的Vite实现示例:

const Koa = require('koa');
const fs = require('fs').promises;
const path = require('path');
const { parse } = require('es-module-lexer');
const MagicString = require('magic-string');

// 创建Koa应用
const app = new Koa();

// 中间件处理请求
app.use(async (ctx) => {
  const { url, query } = ctx.request;
  
  // 处理HTML
  if (url === '/') {
    ctx.type = 'text/html';
    let html = await fs.readFile('./index.html', 'utf-8');
    // 注入HMR客户端脚本
    html = html.replace(
      '<head>',
      `<head>
        <script type="module">
          import { handleHMRUpdate } from '/@vite/client'
          window.handleHMRUpdate = handleHMRUpdate
        </script>`
    );
    ctx.body = html;
  }
  
  // 处理JavaScript模块
  else if (url.endsWith('.js')) {
    const filePath = path.join(__dirname, url);
    ctx.type = 'application/javascript';
    const contents = await fs.readFile(filePath, 'utf-8');
    // 解析导入语句
    const [imports] = parse(contents);
    const ms = new MagicString(contents);
    
    // 重写导入路径
    imports.forEach(({ s: start, e: end, d: dynamicIndex }) => {
      const id = contents.substring(start, end);
      if (/^[^\/\.]/.test(id)) {
        ms.overwrite(start, end, `/@modules/${id}`);
      }
    });
    
    ctx.body = ms.toString();
  }
  
  // 处理CSS
  else if (url.endsWith('.css')) {
    const filePath = path.join(__dirname, url);
    const contents = await fs.readFile(filePath, 'utf-8');
    ctx.type = 'application/javascript';
    ctx.body = `
      const style = document.createElement('style')
      style.textContent = ${JSON.stringify(contents)}
      document.head.appendChild(style)
    `;
  }
  
  // 处理第三方模块
  else if (url.startsWith('/@modules/')) {
    const moduleName = url.replace('/@modules/', '');
    const pkgPath = path.join(__dirname, 'node_modules', moduleName, 'package.json');
    const pkg = require(pkgPath);
    const modulePath = path.join(__dirname, 'node_modules', moduleName, pkg.module);
    const moduleContent = await fs.readFile(modulePath, 'utf-8');
    ctx.type = 'application/javascript';
    ctx.body = moduleContent;
  }
});

// 启动服务器
app.listen(3000, () => {
  console.log('Vite dev server running at http://localhost:3000');
});

这个简化版的Vite实现了以下功能:

  1. 提供一个基本的开发服务器
  2. 处理HTML、JavaScript和CSS文件
  3. 解析并重写JavaScript模块的导入语句
  4. 支持加载第三方模块
  5. 注入简单的HMR客户端脚本