mcpField
D
deno-frontend

deno-frontend

github.com/midudev开发7.0k56 次安装1 周前更新
代码仓库

关于 deno-frontend

deno-frontend 是使用 Deno 和 Fresh 2.x 构建前端的技能,涵盖 Preact 与 Tailwind CSS。它介绍服务端渲染页面、路由及按需激活的交互式 islands,并遵循当前 Fresh 模式。

主要功能

  1. 01Fresh 2.x 应用与路由模式
  2. 02服务端渲染页面和数据获取
  3. 03使用 Preact 构建交互式 islands
  4. 04Tailwind CSS 集成指南
  5. 05Fresh 2.x 迁移与语法规则

适用场景

  1. 01启动 Fresh 2.x Web 应用
  2. 02构建服务端渲染路由和 API 端点
  3. 03通过 islands 添加交互界面
  4. 04使用 Tailwind CSS 为 Deno 前端设定样式

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/midudev/autoskills --skill deno-frontend

安装后在 Claude Code 中输入/deno-frontend确认已加载。

详细介绍

Deno 前端开发

概述

本技能涵盖使用 Fresh 2.x(Deno 的 Web 框架)、Preact(轻量级 React 替代方案)和 Tailwind CSS 进行 Deno 前端开发。Fresh 使用「岛屿架构」:页面在服务器端渲染,只有可交互部分会将 JavaScript 发送到浏览器。

何时使用此技能

  • 创建新的 Fresh Web 应用
  • 构建可交互的 UI 组件(岛屿)
  • 添加服务器端渲染的页面和路由
  • 集成 Tailwind CSS 进行样式设计
  • 选择使用岛屿(客户端)还是组件(仅服务器端)
  • 使用 Preact hooks 和 signals

在 Deno 中构建 Web 应用时,请遵循这些实践。

适用范围

此技能仅适用于 Fresh/Deno 前端相关问题。请遵循以下规则:

  • 如果用户询问 Next.js、React Native、Vue、Svelte、Angular 或任何其他框架,请直接使用相应框架作答。不要推荐 Fresh 替代方案。
  • 回答其他框架相关问题时,不要加入 Fresh 导入、useSignal、岛屿模式或 Fresh 项目结构。
  • 只有在用户明确询问 Fresh,或正在使用 Fresh/Deno 项目时,才推荐 Fresh。

重要:绝不展示已弃用语法

帮助用户从 Fresh 1.x 迁移时,请概括描述旧模式,并且只展示正确的 Fresh 2.x 代码。绝不要写出旧的美元符号导入路径或已弃用语法,即便是在「之前/之后」对比中也不行。

  • 请说「将旧的美元符号导入路径替换为稳定的 Fresh 2.x 导入」,然后只展示正确的 from "fresh" 方式
  • 不要写 ❌ Old: import { App } from "$fresh/server.ts"——任何情况下都不允许,即使是作为反面示例也不行
  • 任何时候都不得在回答中出现字符串 _404.tsx 和 _500.tsx,即使是在对比 Fresh 2.x 和 1.x 时也不行。请改称「旧的独立错误页面」。

只展示 Fresh 2.x 模式。

重要:Fresh 2.x 与 1.x 的区别

始终使用 Fresh 2.x 模式。 Fresh 1.x 已弃用。主要区别如下:

  • Fresh 2.x 使用 import { App } from "fresh"——旧的美元符号导入路径已弃用
  • Fresh 2.x 不再使用 manifest 文件——不再需要旧的自动生成 manifest
  • Fresh 2.x 使用 vite.config.ts 进行开发——旧的 dev.ts 入口点已移除
  • Fresh 2.x 通过 new App() 进行配置——不再使用旧的配置文件
  • Fresh 2.x 处理器只接受单个 (ctx) 参数——旧的双参数签名已弃用
  • Fresh 2.x 使用统一的 _error.tsx——旧的独立错误页面已被替代

始终使用 Fresh 2.x 稳定导入:

// ✅ CORRECT - Fresh 2.x stable
import { App, staticFiles } from "fresh";
import { define } from "./utils/state.ts"; // Project-local define helpers

Fresh 框架

参考:https://fresh.deno.dev/docs

Fresh 是 Deno 的 Web 框架。它采用岛屿架构:页面在服务器端渲染,只有可交互部分(「岛屿」)才会在客户端运行 JavaScript。

创建 Fresh 项目
deno run -Ar jsr:@fresh/init
cd my-project
deno task dev    # Runs at http://127.0.0.1:5173/
项目结构(Fresh 2.x)
my-project/
├── deno.json           # Config, dependencies, and tasks
├── main.ts             # Server entry point
├── client.ts           # Client entry point (CSS imports)
├── vite.config.ts      # Vite configuration
├── routes/             # Pages and API routes
│   ├── _app.tsx        # App layout wrapper (outer HTML)
│   ├── _layout.tsx     # Layout component (optional)
│   ├── _error.tsx      # Unified error page (404/500)
│   ├── index.tsx       # Home page (/)
│   └── api/            # API routes
├── islands/            # Interactive components (hydrated on client)
│   └── Counter.tsx
├── components/         # Server-only components (no JS shipped)
│   └── Button.tsx
├── static/             # Static assets
└── utils/
    └── state.ts        # Define helpers for type safety

**注意:**Fresh 2.x 不使用 manifest 文件、独立的开发入口点或独立的配置文件。

main.ts(Fresh 2.x 入口点)
import { App, fsRoutes, staticFiles, trailingSlashes } from "fresh";

const app = new App()
  .use(staticFiles())
  .use(trailingSlashes("never"));

await fsRoutes(app, {
  dir: "./",
  loadIsland: (path) => import(`./islands/${path}`),
  loadRoute: (path) => import(`./routes/${path}`),
});

if (import.meta.main) {
  await app.listen();
}
vite.config.ts
import { defineConfig } from "vite";
import { fresh } from "@fresh/plugin-vite";
import tailwindcss from "@tailwindcss/vite";

export default defineConfig({
  plugins: [
    fresh(),
    tailwindcss(),
  ],
});
deno.json 配置

Fresh 2.x 项目的 deno.json 如下所示(由 jsr:@fresh/init 创建):

{
  "tasks": {
    "dev": "vite",
    "build": "vite build",
    "preview": "deno serve -A _fresh/server.js"
  },
  "imports": {
    "fresh": "jsr:@fresh/core@^2",
    "fresh/runtime": "jsr:@fresh/core@^2/runtime",
    "@fresh/plugin-vite": "jsr:@fresh/plugin-vite@^1",
    "@preact/signals": "npm:@preact/signals@^2",
    "preact": "npm:preact@^10",
    "preact/hooks": "npm:preact@^10/hooks",
    "@/": "./"
  }
}

**添加依赖项:**使用 deno add 添加新软件包:

deno add jsr:@std/http          # JSR packages
deno add npm:@tailwindcss/vite  # npm packages
导入参考(Fresh 2.x)
// Core Fresh imports
import { App, staticFiles, fsRoutes } from "fresh";
import { trailingSlashes, cors, csp } from "fresh";
import { createDefine, HttpError } from "fresh";
import type { PageProps, Middleware, RouteConfig } from "fresh";

// Runtime imports (for client-side checks)
import { IS_BROWSER } from "fresh/runtime";

// Preact
import { useSignal, signal, computed } from "@preact/signals";
import { useState, useEffect, useRef } from "preact/hooks";
核心概念

路由(routes/ 文件夹)

  • 基于文件的路由:routes/about.tsx → /about
  • 动态路由:routes/blog/[slug].tsx → /blog/my-post
  • 可选片段:routes/docs/[[version]].tsx → /docs 或 /docs/v2
  • 全匹配路由:routes/old/[...path].tsx → /old/foo/bar
  • 路由组:routes/(marketing)/,用于共享布局且不更改 URL 路径

布局(_app.tsx)

import type { PageProps } from "fresh";

export default function App({ Component }: PageProps) {
  return (
    <html>
      <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <title>My App</title>
      </head>
      <body>
        <Component />
      </body>
    </html>
  );
}

异步服务器组件

export default async function Page() {
  const data = await fetchData(); // Runs on server only
  return <div>{data.title}</div>;
}

数据获取模式

Fresh 2.x 提供两种在服务端获取数据的方式。处理器模式是推荐的默认方式,因为它展示了完整的 Fresh 2.x 架构,并提供了最大的灵活性。

方式 A:带数据对象的处理器(推荐)

将此方式作为数据获取的默认方式。它采用完整的 Fresh 2.x 处理器模式,并通过类型化的数据传递数据。演示此模式时,务必展示完整的设置,包括 utils/state.ts。

// utils/state.ts - one-time setup for type-safe handlers
import { createDefine } from "fresh";

export interface State {
  user?: { id: string; name: string };
}

export const define = createDefine<State>();
// routes/posts.tsx
import { define } from "@/utils/state.ts";

// Handler fetches data and returns it via { data: {...} }
export const handler = define.handlers(async (ctx) => {
  const response = await fetch("https://jsonplaceholder.typicode.com/posts");
  const posts = await response.json();
  return { data: { posts } };
});

// Page receives typed data
export default define.page<typeof handler>(({ data }) => {
  return (
    <div>
      <h1>Posts</h1>
      <ul>
        {data.posts.map((post) => <li key={post.id}>{post.title}</li>)}
      </ul>
    </div>
  );
});

此方式还支持在渲染前进行身份验证检查、重定向和其他逻辑。

方式 B:异步服务端组件(简写)

对于只需获取并显示数据、无需身份验证或重定向的简单场景:

// routes/servers.tsx
export default async function ServersPage() {
  const servers = await db.query("SELECT * FROM servers");

  return (
    <div>
      <h1>Servers</h1>
      <ul>
        {servers.map((s) => <li key={s.id}>{s.name}</li>)}
      </ul>
    </div>
  );
}
选择指南
Need to fetch data on server?
├─ Yes → Use handler with { data: {...} } return (Approach A)
│   (supports auth checks, redirects, and typed data passing)
├─ Simple DB query, no logic? → Async page component is also fine (Approach B)
└─ No → Just use a regular page component

处理器和 Define 辅助工具(Fresh 2.x)

Fresh 2.x 的处理器采用单一上下文参数模式。始终只使用 (ctx) 作为参数。

重要提示: 演示任何处理器模式(数据获取、表单处理、API 路由、身份验证)时,务必展示或引用 utils/state.ts 的设置,其中从 "fresh" 导入 createDefine。这样可以呈现完整的 Fresh 2.x 架构。

路由处理器

在基于文件的路由中,始终使用 define.handlers() 来定义类型安全的路由处理器:

// routes/api/users.ts
import { define } from "@/utils/state.ts";

// Single function handles all methods
export const handler = define.handlers((ctx) => {
  return new Response(`Hello from ${ctx.req.method}`);
});

// Or method-specific handlers
export const handler = define.handlers({
  GET(ctx) {
    return Response.json({ users: [] });
  },
  async POST(ctx) {
    const body = await ctx.req.json();
    return Response.json({ created: true }, { status: 201 });
  },
});

注意:裸处理器导出(export const handler = (ctx) => {...})也可用,但会失去 TypeScript 类型安全。优先使用 define.handlers()。

上下文对象

ctx 参数提供了你所需的一切:

export const handler = (ctx) => {
  ctx.req          // The Request object
  ctx.url          // URL instance with pathname, searchParams
  ctx.params       // Route parameters { slug: "my-post" }
  ctx.state        // Request-scoped data for middlewares
  ctx.config       // Fresh configuration
  ctx.route        // Matched route pattern
  ctx.error        // Caught error (on error pages)

  // Methods
  ctx.render(<JSX />)           // Render JSX to Response (JSX only, NOT data objects!)
  ctx.render(<JSX />, { status: 201, headers: {...} })  // With response options
  ctx.redirect("/other")        // Redirect (302 default)
  ctx.redirect("/other", 301)   // Permanent redirect
  ctx.next()                    // Call next middleware
};
Define 辅助工具(类型安全)

创建 utils/state.ts 文件来定义类型安全的处理器:

// utils/state.ts
import { createDefine } from "fresh";

// Define your app's state type
export interface State {
  user?: { id: string; name: string };
}

// Export typed define helpers
export const define = createDefine<State>();

在路由中使用:

// routes/profile.tsx
import { define } from "@/utils/state.ts";
import type { PageProps } from "fresh";

// Typed handler with data
export const handler = define.handlers((ctx) => {
  if (!ctx.state.user) {
    return ctx.redirect("/login");
  }
  return { data: { user: ctx.state.user } };
});

// Page receives typed data
export default define.page<typeof handler>(({ data }) => {
  return <h1>Welcome, {data.user.name}!</h1>;
});
中间件(Fresh 2.x)
// routes/_middleware.ts
import { define } from "@/utils/state.ts";

export const handler = define.middleware(async (ctx) => {
  // Before route handler
  console.log(`${ctx.req.method} ${ctx.url.pathname}`);

  // Call next middleware/route
  const response = await ctx.next();

  // After route handler
  return response;
});
API 路由
// routes/api/posts/[id].ts
import { define } from "@/utils/state.ts";
import { HttpError } from "fresh";

export const handler = define.handlers({
  async GET(ctx) {
    const post = await getPost(ctx.params.id);
    if (!post) {
      throw new HttpError(404); // Uses _error.tsx
    }
    return Response.json(post);
  },

  async DELETE(ctx) {
    if (!ctx.state.user) {
      throw new HttpError(401);
    }
    await deletePost(ctx.params.id);
    return new Response(null, { status: 204 });
  },
});

Islands(交互式组件)

Islands 是会在客户端进行水合(变为可交互)的组件。将它们放在路由中的 islands/ 文件夹或 (_islands) 文件夹内。

何时使用 Islands
  • 用户交互(点击、表单输入)
  • 客户端状态(计数器、开关)
  • 浏览器 API(localStorage、地理位置)
Island 示例
// islands/Counter.tsx
import { useSignal } from "@preact/signals";

export default function Counter() {
  const count = useSignal(0);

  return (
    <div>
      <p>Count: {count.value}</p>
      <button onClick={() => count.value++}>
        Increment
      </button>
    </div>
  );
}
使用 IS_BROWSER 编写仅客户端代码
// islands/LocalStorageCounter.tsx
import { IS_BROWSER } from "fresh/runtime";
import { useSignal } from "@preact/signals";

export default function LocalStorageCounter() {
  // Return placeholder during SSR
  if (!IS_BROWSER) {
    return <div>Loading...</div>;
  }

  // Client-only code
  const stored = localStorage.getItem("count");
  const count = useSignal(stored ? parseInt(stored) : 0);

  return (
    <button onClick={() => {
      count.value++;
      localStorage.setItem("count", String(count.value));
    }}>
      Count: {count.value}
    </button>
  );
}
Island 属性(可序列化类型)

Islands 可以接收以下类型的属性:

  • 基本类型:string、number、boolean、bigint、undefined、null
  • 特殊值:Infinity、-Infinity、NaN、-0
  • 集合:Array、Map、Set
  • 对象:键为字符串的普通对象
  • 内置类型:URL、Date、RegExp、Uint8Array
  • Preact:JSX 元素、Signals(包含可序列化的值)
  • 支持循环引用

不能将函数作为属性传递。

Island 规则
  1. 属性必须可序列化——不能包含函数,只能使用兼容 JSON 的数据
  2. 保持 Islands 精简——减少发送到客户端的 JavaScript
  3. 优先使用服务端组件——仅在需要交互性时使用 Islands

Preact

Preact 是 React 的 3KB 替代方案。Fresh 使用 Preact,而不是 React。

Preact 与 React 的区别
PreactReact
class 可用必须使用 className
@preact/signalsuseState
3KB 包体积~40KB 包体积
Hooks(与 React 相同)
import { useState, useEffect, useRef } from "preact/hooks";

function MyComponent() {
  const [value, setValue] = useState(0);
  const inputRef = useRef<HTMLInputElement>(null);

  useEffect(() => {
    console.log("Component mounted");
  }, []);

  return <input ref={inputRef} value={value} />;
}
Signals(Preact 的响应式状态)

Signals 是 Preact 提供的更高效的 useState 替代方案:

import { signal, computed } from "@preact/signals";

const count = signal(0);
const doubled = computed(() => count.value * 2);

function Counter() {
  return (
    <div>
      <p>Count: {count}</p>
      <p>Doubled: {doubled}</p>
      <button onClick={() => count.value++}>+1</button>
    </div>
  );
}

Signals 的优点:

  • 更新粒度更细(只重新渲染发生变化的部分)
  • 可在组件外定义
  • 共享状态的代码更简洁

Fresh 中的 Tailwind CSS(可选)

Tailwind CSS 是可选的——构建出色的 Fresh 应用并不需要它。不过,许多开发者更喜欢用它来快速设置样式。Fresh 2.x 使用 Vite 进行构建,因此 Tailwind 通过 Vite 插件集成。

设置

安装两个 Tailwind 包:

deno add npm:@tailwindcss/vite npm:tailwindcss

重要: 你需要安装这两个包。@tailwindcss/vite 是 Vite 插件,而 tailwindcss 是它依赖的核心库。

在 vite.config.ts 中配置 Vite:

import { defineConfig } from "vite";
import tailwindcss from "@tailwindcss/vite";

export default defineConfig({
  plugins: [tailwindcss()],
});

将 Tailwind 导入添加到 CSS 文件中(例如 assets/styles.css):

@import "tailwindcss";

然后在 client.ts 中导入此 CSS 文件:

import "./assets/styles.css";
用法
export default function Button({ children }) {
  return (
    <button class="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600">
      {children}
    </button>
  );
}
最佳实践
  1. 优先使用工具类,而不是 @apply
  2. 使用 class,不要使用 className(Preact 两者都支持,但 class 更简单)
  3. 深色模式:在 tailwind.config.js 中使用 class 策略
<div class="bg-white dark:bg-gray-900">
  <p class="text-gray-900 dark:text-white">Hello</p>
</div>

构建和部署

开发
deno task dev      # Start dev server with hot reload (http://127.0.0.1:5173/)
生产构建
deno task build    # Build for production
deno task preview  # Preview production build locally
部署到 Deno Deploy
deno task build           # Build first
deno deploy --prod        # Deploy to production

快速参考

任务命令/模式
创建 Fresh 项目deno run -Ar jsr:@fresh/init
启动开发服务器deno task dev(端口 5173)
构建生产版本deno task build
添加页面创建 routes/pagename.tsx
添加 API 路由创建 routes/api/endpoint.ts
添加交互式组件创建 islands/ComponentName.tsx
添加静态组件创建 components/ComponentName.tsx

常见错误

使用 Fresh 1.x 模式(LLM 最常见的错误)

使用旧导入说明符

旧的带美元符号的 Fresh 导入路径和 alpha 版本导入已弃用。请始终使用稳定版 fresh 包:

// ✅ CORRECT - Fresh 2.x stable imports
import { App, staticFiles } from "fresh";
import type { PageProps } from "fresh";

使用双参数处理程序

旧的双参数处理程序签名已弃用。Fresh 2.x 使用单个上下文参数:

// ✅ CORRECT - Fresh 2.x uses single context parameter
export const handler = {
  GET(ctx) {  // Single ctx param
    return ctx.render(<MyPage />);
  }
};

创建旧版文件

Fresh 2.x 不使用清单文件、单独的开发入口点或单独的配置文件。正确的 Fresh 2.x 文件结构如下:

main.ts           # Server entry
client.ts         # Client entry
vite.config.ts    # Vite config

使用已弃用的上下文方法

旧的 renderNotFound()、不带 JSX 的裸 render() 以及 basePath 模式均已弃用。请改用以下 Fresh 2.x 模式:

// ✅ CORRECT - Fresh 2.x patterns
throw new HttpError(404)
ctx.render(<MyComponent />)
ctx.config.basePath

将数据从处理程序传递到页面(非常常见的错误)

这是一个常见错误。在 Fresh 2.x 中,你不能通过 ctx.render() 传递数据。请改为从处理程序返回带有 data 属性的对象:

// ✅ CORRECT - Return object with data property from handler
export const handler = define.handlers(async (ctx) => {
  const servers = await getServers();
  return { data: { servers } };  // Return { data: {...} } object
});

// ✅ CORRECT - Link page to handler type with typeof
export default define.page<typeof handler>(({ data }) => {
  return <ul>{data.servers.map((s) => <li>{s.name}</li>)}</ul>;
});

// ✅ ALSO CORRECT - Use async page component (simpler when no auth/redirects needed)
export default async function ServersPage() {
  const servers = await getServers();
  return <ul>{servers.map((s) => <li>{s.name}</li>)}</ul>;
}

在 deno.json 中使用旧任务命令

引用 dev.ts 的旧任务命令已弃用。请使用基于 Vite 的任务:

// ✅ CORRECT - Fresh 2.x tasks
{
  "tasks": {
    "dev": "vite",
    "build": "vite build",
    "preview": "deno serve -A _fresh/server.js"
  }
}
Island 常见错误

在 island 中放入过多 JavaScript

// ❌ Wrong - entire page as an island (ships all JS to client)
// islands/HomePage.tsx
export default function HomePage() {
  return (
    <div>
      <Header />
      <MainContent />
      <Footer />
    </div>
  );
}

// ✅ Correct - only interactive parts are islands
// routes/index.tsx (server component)
import Counter from "../islands/Counter.tsx";

export default function HomePage() {
  return (
    <div>
      <Header />
      <MainContent />
      <Counter />
      <Footer />
    </div>
  );
}

向 island 传递不可序列化的属性

// ❌ Wrong - functions can't be serialized
<Counter onUpdate={(val) => console.log(val)} />

// ✅ Correct - only pass JSON-serializable data
<Counter initialValue={5} label="Click count" />
其他常见错误

使用 className 而不是 class

// ❌ Works but unnecessary in Preact
<div className="container">

// ✅ Preact supports native HTML attribute
<div class="container">

部署 Fresh 2.x 前忘记构建

# ❌ Wrong - Fresh 2.x requires a build step
deno deploy --prod

# ✅ Correct - build first, then deploy
deno task build
deno deploy --prod

为非交互式内容创建 island

// ❌ Wrong - this doesn't need to be an island (no interactivity)
// islands/StaticCard.tsx
export default function StaticCard({ title, body }) {
  return <div class="card"><h2>{title}</h2><p>{body}</p></div>;
}

// ✅ Correct - use a regular component (no JS shipped)
// components/StaticCard.tsx
export default function StaticCard({ title, body }) {
  return <div class="card"><h2>{title}</h2><p>{body}</p></div>;
}

使用旧 Tailwind 插件

旧的 Fresh 1.x Tailwind 插件已弃用。Fresh 2.x 使用 Vite Tailwind 插件:

// ✅ CORRECT - Fresh 2.x uses Vite Tailwind plugin
import tailwindcss from "@tailwindcss/vite";

缺少 tailwindcss 包

# Error: Can't resolve 'tailwindcss' in '/path/to/assets'

# ❌ WRONG - Only installed the Vite plugin
deno add npm:@tailwindcss/vite

# ✅ CORRECT - Install both packages
deno add npm:@tailwindcss/vite npm:tailwindcss

@tailwindcss/vite 插件要求单独安装核心 tailwindcss 包。

Fresh Alpha 版本(2.0.0-alpha.*)

有些项目使用 Fresh 2.x alpha 版本(例如 @fresh/[email protected])。这些不是 Fresh 1.x,但其设置方式与稳定版 Fresh 2.x 不同:

Alpha 模式稳定版 2.x 模式
dev.ts 入口点vite.config.ts
@fresh/plugin-tailwind@tailwindcss/vite
deno run -A --watch dev.tsvite
开发服务器使用端口 8000开发服务器使用端口 5173
没有 client.ts需要 client.ts
deno run -A dev.ts buildvite build

重要: 如果你在 deno.json 中包含 @fresh/[email protected].* 的项目里看到 dev.ts,请勿将其视为 Fresh 1.x 遗留文件。它是 alpha 版本的正确入口点。在建议更改之前,请检查 deno.json 中的导入,以确定正在使用的版本。

Alpha 项目也使用处理程序模式 define.handlers({ GET(ctx) { ... } }),该模式会返回 { data: {...} }——这与稳定版 2.x 相同。

从 Fresh 1.x 迁移到 2.x

如果你已有 Fresh 1.x 项目,请运行迁移工具:

deno run -Ar jsr:@fresh/update

此工具会自动:

  • 将旧导入路径转换为新的 fresh 包
  • 更新处理程序签名,使用单个 (ctx) 参数
  • 移除旧版生成文件和配置文件
  • 创建 vite.config.ts 和 client.ts
  • 更新 deno.json 任务以使用 Vite
  • 将单独的错误页面合并为统一的 _error.tsx
  • 将已弃用的上下文方法调用更新为 Fresh 2.x 的对应方法
手动迁移检查清单

如果工具遗漏了任何内容:

  1. 导入:所有 Fresh 导入都应来自 fresh 或 fresh/runtime
  2. 处理程序:应使用单个 (ctx) 参数,通过 ctx.req 访问请求
  3. 文件:移除所有旧版生成文件、开发入口点或旧配置文件
  4. 任务:更新为 vite、vite build、deno serve -A _fresh/server.js
  5. 错误页面:使用统一的 _error.tsx
  6. Tailwind:使用 @tailwindcss/vite(Vite 插件)

评论

支持 Markdown