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 Redwitz 和 iccee0 的贡献。
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 本身。
在同一个文件中导出多个 Islands
在之前的 Fresh 版本中,每个 island 都应位于自己的文件中,并通过 default
导出。每个 island 文件都被视为自己的入口点,并作为单独的 JavaScript 文件发送到浏览器。此限制已被移除,您现在可以在单个文件中导出任意数量的 island。
// ./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
}
将 island 组合到单个文件中可以减少网站发出的请求数量,甚至可以使某些网站更快!请注意,虽然将所有 island 放在同一个文件中很有吸引力,但这种模式可能会产生相反的效果。一个好的经验法则是,将同一页面上一起使用的 island 分组,可以获得最佳性能。
感谢 Reed von Redwitz 的贡献。
Fresh Linting 规则
提供反馈的最佳方式就在编辑器中。我们开始追踪常见的意外错误,例如将处理程序导出命名为 handlers
而不是 handler
,并将其集成到我们的 linter 中。每当我们检测到错误时,您就会在编辑器中看到那些波浪线。
一个常见的困惑来源是 Fresh 在配置错误或完全出错时向用户呈现的通常难以理解的错误消息。我们检查了最常见的错误,并重写了错误消息,使其更具可读性。虽然我们认为已经涵盖了很大一部分,但可能还有更多我们可以做得更好的情况。因此,如果您遇到任何让您感到困惑的错误消息,请联系我们!
新的 linting 规则对于新的 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 传递给 island。入门模板现在看起来好多了,还有更多改进!