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

Fresh 1.5:部分页面、客户端导航等等

今天,我们很高兴地宣布 Fresh 的 1.5 版本发布,这是一个快速、原生 Deno 的框架,用于构建全栈 Web 应用程序。

此版本包含一种全新的客户端导航方法,我们将其称为 **部分页面**。使用 HTML 属性,您可以配置 Fresh 应用程序以使用服务器端渲染的标记替换已加载页面的 HTML,而无需重新加载页面。这种导航风格使您的应用程序感觉更加响应,并且可以防止在页面切换时丢失客户端岛组件中的状态。

除了部分页面之外,Fresh 1.5 还包含许多改进和错误修复,旨在支持复杂的 UI 开发模式。

准备好试用了吗?您可以通过运行以下命令来启动一个新的 Fresh 项目

deno run -Ar https://fresh.deno.dev

您也可以通过在项目文件夹中运行以下命令来更新现有项目

deno run -Ar https://fresh.deno.dev/update .

以下是 1.5 中新增功能的概述。继续阅读以了解所有细节,以及对 Fresh 的 下一次迭代 的期望。

Fresh 1.5 概述

使用部分页面的客户端导航

传统上,客户端导航是 Web 开发中难以处理的一件事。它通常需要大量的样板代码和新的数据加载策略。对于 Fresh,我们希望以一种不需要开发人员改变构建网站和应用程序的方式来解决这个问题。

使用 Fresh 1.5,我们发布了一个名为部分页面的新概念,它通过融合客户端和服务器端导航,使 Fresh 应用程序感觉更加类似应用程序。部分页面的理念是,您可以标记页面中在导航时会发生更改的区域,而 Fresh 只会更新这些区域。最棒的是:岛状态保持不变。

请注意,Fresh 文档页面上的搜索栏岛在页面之间导航时不再闪烁。

为了使这项工作,我们只需要更改代码中的两件事

  1. 向容器元素添加 f-client-nav 属性,使该节点下面的所有链接都选择加入客户端导航和部分页面
  2. 使用 <Partial name="content">...</Partial> 组件将主要内容区域包装起来

就这样!我们现在在我们的文档中拥有了完整的客户端导航!可以在 此 PR 中看到完整的更改。

- import { asset, Head } from "$fresh/runtime.ts";
+ import { asset, Head, Partial } from "$fresh/runtime.ts";

  // ...snip

  export default function DocsPage(props: PageProps<Data>) {
    return (
-     <div class="flex flex-col min-h-screen">
+     <div class="flex flex-col min-h-screen" f-client-nav>
-       <Content page={props.page} />
+       <Partial name="docs-main">
+         <Content page={props.page} />
+       </Partial>
      </div>
    )
  }

在幕后,Fresh 会获取新页面,并只从 HTML 响应中提取相关内容。

细粒度部分页面修改

我们可以通过使用 f-partial 属性选择加入更细粒度的部分页面修改,来进一步优化这种模式。

- <a href="/docs/routes">Routes</a>
+ <a href="/docs/routes" f-partial="/partials/docs/routes">Routes</a>

单击链接时,我们将按预期导航到 /docs/routes,但会从 /partials/docs/routes 而不是 /docs/routes 获取新内容。在我们的例子中,这可以是一个简化的 HTML 页面,它只返回主要内容,并绕过直接在服务器上渲染外部文档。

export default function DocRoute() {
  return (
    <Partial name="content">
      {/* Render only the markdown content here */}
    </Partial>
  );
}

在一个 HTTP 响应中应用多个部分页面

Fresh 中部分页面一个很酷的方面是,一个响应可以返回任意多个部分页面。这样,您就可以在一个 HTTP 响应中更新页面上多个无关的区域。例如,在线商店就是一个使用此功能很有用的场景。

export default function AddToCart() {
  return (
    <>
      <Partial name="cart-items" mode="append">
        {/* Render the new cart item here */}
      </Partial>
      <Partial name="total-price">
        <p>Total: {totalPrice}</p>
      </Partial>
    </>
  );
}

指定替换模式

您可能已经注意到,在前面的示例中,我们在 <Partial> 组件上使用了新的 mode="append" 属性。默认模式是始终替换部分页面内容,但使用 mode 属性,您可以指定希望如何将内容集成到活动页面中。我们添加了三种不同的合并模式

  • replace - 替换现有部分页面的内容(默认)
  • prepend - 在现有内容之前插入新内容
  • append - 在现有内容之后插入新内容

就个人而言,我们发现 append 模式在您有显示日志消息或类似列表数据的 UI 时非常有用。请访问我们的 文档,以了解有关部分页面的更多信息。

我们一直想要让样式化活动链接变得更容易。到目前为止,我们所见过的代码库大多数都通过许多组件转发当前 URL,以便能够检查 <a> 元素的 href 属性是否应该以某种方式进行样式化,以显示它是当前页面。

事实是,Fresh 已经知道当前 URL 是什么,并且可以为您自动执行此操作。无需手动传递当前 URL。使用 Fresh 1.5,我们将向链接添加以下两个属性

  • data-current - 添加到与当前路径完全匹配的链接
  • data-ancestor - 添加到与当前 URL 部分匹配的链接

以下是如何使用 CSS 对它们进行样式化的示例

/* Give links pointing to the current page a green color */
a[data-current] {
  color: green;
}

/* Color all ancestor links of the current page the color peachpuff */
a[data-ancestor] {
  color: peachpuff;
}

…以及使用 twind

// Current link
<a href="/foo" class="[data-current]:text-green-600">...</a>

// Ancestor link
<a href="/foo" class="[data-ancestor]:font-bold">...</a>

自定义构建目标

在内部,Fresh 将优化后的前端资产的生成传递给 esbuild,它有一个非常酷的 “目标”功能,它允许您指定要支持的最低浏览器版本。esbuild 会接受该信息,并尝试将指定范围内不支持的较新的 JavaScript 结构转换为这些较旧的引擎支持的结构。由于每个项目都有不同的需求,因此在我们的配置中长时间暴露它是合乎情理的。

// fresh.config.ts
export default defineConfig({
  build: {
    target: ["chrome99", "firefox99", "safari15"],
  },
});

请访问 esbuild 文档,以了解所有可能的 target 值列表。

分析打包文件

在 Fresh 1.5 中,我们公开了 esbuild 的 metafile.json 文件,该文件可用于分析和检查实际发送到浏览器的模块。该文件可以通过运行项目构建任务生成。

构建完成后,您可以在 esbuild 项目本身提供的 https://esbuild.org.cn/analyze/ 上检查元文件。

感谢 Tiago Gimenes 将此功能添加到 Fresh 中!

Bar chart of the modules included in the assets for the fresh website.

错误覆盖层

我们也花了一些时间来改进开发者体验。Fresh 1.5 现在将渲染一个合适的错误覆盖层,并尽力向您展示错误的来源。请放心,这仅在您运行 dev.ts 时可见。

The error overlay in browser shows you an excerpt of where it occured, the stack trace, as well as the error message.

终端中显示的错误将显示错误发生位置的视觉指示。

The terminal prints an excerpt of the surrounding area of where the stack trace points to.

这是一个小细节,但已经证明对我直接在 Fresh 上工作很有用。

其他值得注意的功能

  • 内联脚本会自动获得 nonce 值,这使得 使用内容安全策略 (CSP) 标头 更加方便。
  • Deno KV OAuth 插件已移至 Fresh 存储库,因为它是一个一级支持的插件 - 感谢 Asher GomezMichael Herzner 添加此功能!
  • 插件 API 收到了一个 buildStartbuildEnd 钩子,分别在构建开始和结束时调用。我们认为这两个钩子是实验性的,因为我们将在不久的将来开始在插件 API 中公开更多内容,这些钩子可能会发生变化。

未来展望

随着对部分的支持,我们离发布 视图转换 API 越来越近了。我们也渴望让部分变得更加强大!我们正在考虑的其他领域包括插件 API 的新功能等等。

就像过去几个周期一样,您可以关注 GitHub 上的下一个 迭代计划