架构
Fresh 是一个服务端优先的 Web 框架。页面在服务端渲染,只有交互部分(岛屿)才会将 JavaScript 发送到浏览器。本页面将解释请求如何在框架中流动。
请求生命周期
核心概念
服务端优先渲染
每个页面在发送到浏览器之前都会在服务端完全渲染为 HTML。这意味着:
- 页面立即可见 - 没有空白加载屏幕
- 搜索引擎能看到完整内容
- 页面在禁用 JavaScript 时也能工作
岛屿架构
Fresh 使用 岛屿架构。只有 islands/ 目录中的组件才会在浏览器中进行 hydration。其他所有内容都是静态 HTML,永远不会在客户端运行 JavaScript。
这意味着一个只有一个交互式按钮的页面只需要发送该按钮的 JavaScript,而不是整个页面的 JavaScript。
中间件链
中间件按注册顺序执行,包装处理器。每个中间件调用 ctx.next() 将控制权传递给下一个中间件(或处理器)。这创建了一个洋葱模式,中间件可以在请求上(ctx.next() 之前)和响应上(ctx.next() 之后)执行操作:
ts
app.use(async (ctx) => {
// 之前:在进入时运行
console.log("请求:", ctx.url.pathname);
const response = await ctx.next();
// 之后:在出去时运行
console.log("状态:", response.status);
return response;
});作用域中间件只对匹配特定路径前缀的请求运行。将路径模式作为第一个参数传递给 app.use():
ts
app.use("/admin/*", async (ctx) => {
// 只对 /admin/* 路由运行
const user = ctx.state.user;
if (!user?.isAdmin) return new Response("禁止访问", { status: 403 });
return ctx.next();
});全局中间件对每个请求都运行;作用域中间件让你可以将认证或日志等逻辑应用到部分路由。
布局继承
布局 包装页面组件,并从父目录继承。routes/blog/post.tsx 的页面会从以下布局继承:
routes/_layout.tsx(根布局)routes/blog/_layout.tsx(章节布局)
布局从外向内嵌套:根布局在最外层,每个更深的布局更接近页面,最内层布局直接包装页面组件。应用包装器(_app.tsx)包装所有内容。
构建和部署
Fresh 使用 Vite 来打包生产环境的岛屿 JavaScript。deno task build 命令:
- 发现所有岛屿及其依赖
- 使用代码分割打包客户端 JavaScript
- 生成服务端入口点(
_fresh/server.js) - 对资源进行哈希处理以实现缓存破坏
在生产环境中,_fresh/server.js 提供预构建的资源。在开发环境中,Vite 提供热模块替换以获得即时反馈。