《vite技术揭秘、还原与实战》第4节--加载index.html

前言

在上一节,我们创建了一个http服务器,但是当你进行访问的时候,会发现被拒绝,这是因为我们还没有对对应的请求做处理

本节我们将优先对index.html文件进行加载处理,它是整个预构建的入口点,非常重要

源码获取

传送门

更新进度

公众号:更新至第9节

博客:更新至第3节

源码分析

在vite中,通过connect包来为http服务器提供中间件能力

ts 复制代码
// packages/vite/src/node/server/index.ts

const middlewares = connect() as Connect.Server
...
const { createServer } = await import('node:http')
createServer(middlewares)

因此,可以在中间件中去对指定的请求做处理

ts 复制代码
middlewares.use(htmlFallbackMiddleware(root, true));

中间件本身是一个函数,通过参数req和res就能够监听特定请求并做客制化处理后return到客户端

ts 复制代码
// packages/vite/src/node/server/middlewares/htmlFallback.ts

import history from 'connect-history-api-fallback'

export function htmlFallbackMiddleware(
  root: string,
  spaFallback: boolean
): Connect.NextHandleFunction {
  const historyHtmlFallbackMiddleware = history({
    // 打印日志
    logger: createDebugger("vite:html-fallback"),
    // 需要拦截和重写的接口路径
    // 此处意为将'/'路径重定向到'/index.html'
    rewrites: [
      {
        from: //$/,
        to({ parsedUrl, request }: any) {
          const rewritten =
            decodeURIComponent(parsedUrl.pathname) + "index.html";

          if (fs.existsSync(path.join(root, rewritten))) {
            return rewritten;
          }

          return spaFallback ? `/index.html` : request.url;
        },
      },
    ],
  });

  // 使用具名函数,当出现错误时,有利于快速定位
  return function viteHtmlFallbackMiddleware(req, res, next) {
    return historyHtmlFallbackMiddleware(req, res, next);
  };
}

代码实现

首先,在packages\vite\src\node\server文件夹下新建middlewares文件夹,它用来管理所有的中间件,比如后续对proxy的处理、对index.html的分析转换等

在middlewares文件夹下新建htmlFallback.ts,它应该返回一个函数

ts 复制代码
export function htmlFallbackMiddleware(): Connect.NextHandleFunction {
  return function viteHtmlFallbackMiddleware(req, res, next) {};
}

并且将其在_createServer中作为中间件引入

ts 复制代码
async function _createServer(userConfig:UserConfig){
    ...
    const middlewares = connect() as Connect.Server
    ...
    middlewares.use(htmlFallbackMiddleware(//$/))
    ...
}

返回htmlFallback.ts文件,开始处理默认请求,在vite中,是采用的connect-history-api-fallback包来进行处理的,调库本身挺无聊的,而且这里的功能也不复杂,因此在这里我们就自己手动进行实现

如下,我们针对GET请求,匹配请求路径是否是/,然后到用户文件根目录中查找index.html 文件,找到后对其进行读取并返回到客户端,若找不到,则next到下一个中间件`即可

ts 复制代码
export function htmlFallbackMiddleware(
  target: RegExp
): Connect.NextHandleFunction {
  return function viteHtmlFallbackMiddleware(req, res, next) {
    // 对于svite加载资源而言,不存在POST请求
    if (req.method === "GET") {
      // req.url本身就是以'/'开头的
      const intactUrl = `http://127.0.0.1${req.url || "/"}`;
      const url = new URL(intactUrl);
      // target是注册中间件时的入参://$/
      const m = url.pathname.match(target);
      if (m) {
        const rewritten = decodeURIComponent(url.pathname) + "index.html";
        const intacFiletPath = join(process.cwd(), rewritten);
        if (existsSync(intacFiletPath)) {
          req.url = rewritten;
          res.statusCode = 200;
          res.setHeader("Content-Type", "text/html");
          // 将index.html文件内容作为响应返回
          res.end(readFileSync(intacFiletPath, "utf-8"));
        }
      }
    }
    return next();
  };
}

调试

启动playground/dev下的示例,打开浏览器,index.html可以被正常渲染

总结

本节,针对默认的/请求,将其转换为/index.html并读取和返回对应的文件内容,这样一来,浏览器就能够正常加载并解析html文件,并且在遇到src或link属性时发起相应的请求

相关推荐
雨季mo浅忆1 天前
记录Node.js安全漏洞及其内置Brotli解码器问题
node.js·安全性测试
tommyjiatong1 天前
资安底座连环雷:从 PHP2Shell 到 Node2Shell
node.js·php
SEO_juper1 天前
Bash 交叉验证 robots.txt 和 sitemap:两个文件自相矛盾,页面永远收录不了
开发语言·python·node.js·json·bash
SEO_juper1 天前
Go 站点地图生成器:从首页爬全站,一键生成 sitemap.xml
xml·开发语言·人工智能·golang·node.js·seo·外贸独立站
Moshow郑锴2 天前
Ansible 批量部署 Node.js 服务:ZIP 直解与共享目录分发两种方案对比
node.js·ansible
少年的范儿2 天前
Windows 安装 Node.js 教程:nvm 版本管理 + pnpm 安装配置
windows·npm·node.js
peach2 天前
同样是装依赖,为什么 npm 长成 node_modules,Maven 长成 .m2
npm·node.js·maven
FungLeo2 天前
成为全栈·Go 后端篇·重写之前先锁基线:契约、Node 版本与行为清单
golang·node.js·go·openapi·契约测试·成为全栈
FungLeo3 天前
成为全栈·Go 后端篇·契约一致性与差分测试:两套实现如何互相校验
golang·node.js·go·合约测试·差分测试·成为全栈·api兼容