《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));

中间件本身是一个函数,通过参数reqres就能够监听特定请求并做客制化处理后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文件,并且在遇到srclink属性时发起相应的请求

相关推荐
菠菜7079 小时前
Node十六章(pnpm原理及软硬链接)
前端·javascript·node.js
孟陬10 小时前
如何让 vite 编译含有 Node.js 模块的 TS 文件 ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING
typescript·node.js
孟陬12 小时前
三种解决办法 ReferenceError: __dirname is not defined in ES module scope
node.js
前端的阶梯12 小时前
深入理解流式传输数据(一)
node.js
Mintopia16 小时前
Node.js 中 crypto 模块的实用指南与应用示例
前端·javascript·node.js
曼陀罗16 小时前
【pnpm、npm】各种命令详解
npm·node.js·前端工程化
玲小珑16 小时前
LLMOps开发(二) Memory
langchain·node.js·ai编程
一梦南柯17 小时前
将HAR文件中的请求和响应转换为本地Node.js服务
前端·node.js
jjjzz17 小时前
🚀 NestJS+TypeORM+MySQL+RBAC构建消息中心后台实战
node.js
天蓝色的鱼鱼17 小时前
Vite+Vue高效开发技巧全揭秘
前端·vue.js·vite