跳至主要内容
Deno 2 终于来了 🎉️
了解更多
Fresh lemon

Fresh 1.3 – 简化的路由组件和其他改进

距离我们发布 Fresh 1.2 仅仅过去了一个月,我们就已经带着另一个版本回来了!我们计划在未来每月发布 Fresh 的新次要版本。

本次发布周期包含了来自社区的大量出色的 PR,这真是太棒了!文档得到了扩展和改进,许多错误得到修复,新功能得到添加。感谢所有为本次发布做出贡献的人。但是,闲话少说,让我们看看我们在 Fresh 中所做的所有改进。

异步路由组件

我们收到了很多反馈,指出将数据从路由处理程序传递到页面的组件需要一些烦人的样板代码。为了确保类型安全,您总是需要为组件的 props 创建一个接口,将其作为泛型传递给 Handlers 类型,并在组件定义中使用它。这需要很多步骤!

// previous Fresh versions
interface Data {
  foo: number;
}

export const handler: Handlers<Data> = {
  async GET(req, ctx) {
    const value = await loadFooValue();
    ctx.render({ foo: value });
  },
};

export default function MyPage(props: PageProps<Data>) {
  return <p>foo is: {props.data.foo}</p>;
}

由于 GET 处理程序与它将要渲染的组件经常紧密耦合,因此一个显而易见的问题是,为什么我们不直接将两者合并在一起?而这正是我们所做的。在 Fresh 1.3 中,我们可以显著简化之前的代码段。

export default async function MyPage(req: Request, ctx: RouteContext) {
  const value = await loadFooValue();
  return <p>foo is: {value}</p>;
}

由于处理程序和组件都在同一个函数中,因此无需声明中间接口来传递两者之间的数据;您可以直接传递数据。

但不用担心,您无需重写所有路由。事实上,我们也不喜欢自己重写代码。新方法不过是一个额外的选项,可以让简单的路由更容易编写。使用异步路由不是必需的。

当您响应其他 HTTP 动词(如 POST)时,您可能仍然需要一个处理程序。

export const handler: Handlers<{}> = {
  POST(req) {
    // ... do something here
  },
};

export default async function MyPage(req: Request, ctx: RouteContext) {
  const value = await loadFooValue();
  return <p>foo is: {value}</p>;
}

此外,现有的通过 ctx.render() 和单独的组件函数从处理程序渲染路由的方式将继续起作用,并且可以独立测试渲染和数据检索机制。

从插件添加路由和/或中间件

插件在扩展 Fresh 的内置功能方面非常强大。在 Fresh 1.3 中,它们可以注入虚拟路由和中间件。这对于添加开发特定路由或管理面板的插件特别有用。

function myPlugin() {
  return {
    name: "my-plugin",
    middlewares: [
      {
        middleware: { handler: () => new Response("Hello!") },
        path: "/hello",
      },
    ],
    routes: [
      {
        path: "/admin/hello",
        component: () => <p>Hello from /admin/hello</p>,
      },
    ],
  };
}

感谢 Reed von Redwitziccee0 的贡献。

500 错误模板回退

尽管我们程序员尽力考虑到所有情况,但往往仍然存在一些意想不到的情况会导致错误发生。我们简化了错误处理,Fresh 现在将自动渲染 _500.tsx 模板作为回退,当路由处理程序中抛出错误时。

export const handler = (req: Request, ctx: HandlerContext): Response => {
  // Fresh will catch this errors and render the 500 error template
  throw new Error("Catch me if you can");
};

感谢 Kamil Ogórek 的添加。

错误边界

虽然比处理程序中的错误少见,但在渲染过程中也可能会抛出错误。为此,我们添加了对错误边界的基本支持。当 Preact 检测到一个类组件具有 componentDidCatch() 方法或静态 getDerivedStateFromError 方法时,该组件将被视为错误边界。当在渲染过程中抛出错误时,这些组件可以捕获错误并渲染回退 UI。

class ErrorBoundary extends Component {
  state = { error: null };

  static getDerivedStateFromError(error) {
    return { error };
  }

  render() {
    return this.state.error
      ? this.props.fallback(this.state.error)
      : this.props.children;
  }
}

// Usage:
<ErrorBoundary fallback={(error) => <p>Error happened: {error.message}</p>}>
  <SomeComponentThatThrows />
</ErrorBoundary>;

这在您处理高度动态的数据并希望 渲染回退 UI 而不是渲染 500 错误页面时很有用。虽然 Fresh 1.3 中的更改为捕获渲染错误奠定了基础,但我们已经在思考如何让 API 更好地融入 Fresh 本身。

在同一个文件中导出多个岛屿

在之前的 Fresh 版本中,每个岛屿都应该位于自己的文件中,并通过 default 导出。每个岛屿文件都被视为独立的入口点,并以单独的 JavaScript 文件的形式提供给浏览器。这个限制已经被移除,现在您可以在一个文件中导出任意多个岛屿。

// ./islands/MyIsland.tsx
// Export multiple islands in Fresh 1.3
export default function SayHello() {
  // ...island code here
}

export function OtherIsland() {
  // ...island code here
}

export function AndAnotherIsland() {
  // ...island code here
}

将岛屿分组在一个文件中可以减少网站必须发出的请求数量,甚至可以使某些网站更快!需要注意的是,虽然将所有岛屿放在同一个文件中很诱人,但这种模式可能会产生相反的效果。一个好的经验法则是,将一起使用并显示在同一页面上的岛屿分组在一起可以获得最佳性能。

感谢 Reed von Redwitz 的贡献。

Fresh 代码风格检查规则

提供反馈的最佳方式是在编辑器中直接进行。我们开始追踪一些常见的意外错误,比如将处理程序导出命名为 handlers 而不是 handler,并将这些错误集成到我们的代码风格检查工具中。每当我们检测到错误时,您将在编辑器中看到波浪线。

screenshot of lint

Fresh 向用户展示的错误信息往往晦涩难懂,这常常让人困惑,尤其是在配置错误或出现严重错误时。我们针对最常见的错误,重新编写了错误信息,使其更加易于理解。尽管我们认为已经覆盖了很大一部分,但可能还有更多可以改进的地方。因此,如果您遇到任何让您感到困惑的错误信息,请 联系我们

新的代码风格检查规则在新的 Fresh 项目中可以开箱即用。要将它们与现有项目一起使用,只需将以下内容添加到您的 deno.json 文件中即可。

{
  "lint": {
    "rules": {
      "tags": ["fresh", "recommended"]
    }
  }
}

支持 Deno.serve

随着最近的 Deno 1.35.0 版本发布,Deno.serve API 标记为稳定。我们在 Fresh 中也做出了相应的改进,从 1.3 版本开始,当可用时,我们将使用 Deno.serve。这个新 API 不仅更快,而且比 std/http 中以前的 serve API 更简单。

感谢 Lino Le Van 的贡献。

更多提高生活质量的改进

我们发布了一系列小的改进,例如可以从每个处理程序或中间件访问路由参数。BigInt 值现在可以作为 props 传递给岛屿。入门模板现在看起来好多了,还有更多改进!

Screenshot