《vite技术揭秘、还原与实战》第4节--加载index.htm
2024-01-07 本文已影响0人
习惯水文的前端苏
前言
在上一节,我们创建了一个http
服务器,但是当你进行访问的时候,会发现被拒绝,这是因为我们还没有对对应的请求做处理
本节我们将优先对index.html
文件进行加载处理,它是整个预构建的入口点,非常重要
源码获取
更新进度
公众号:更新至第9
节
博客:更新至第4
节
源码分析
在vite
中,通过connect
包来为http
服务器提供中间件能力
// packages/vite/src/node/server/index.ts
const middlewares = connect() as Connect.Server
...
const { createServer } = await import('node:http')
createServer(middlewares)
因此,可以在中间件中去对指定的请求做处理
middlewares.use(htmlFallbackMiddleware(root, true));
中间件本身是一个函数,通过参数req
和res
就能够监听特定请求并做客制化处理后return
到客户端
// 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
,它应该返回一个函数
export function htmlFallbackMiddleware(): Connect.NextHandleFunction {
return function viteHtmlFallbackMiddleware(req, res, next) {};
}
并且将其在_createServer
中作为中间件引入
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
到下一个中间件`即可
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
属性时发起相应的请求