Fresh 1.6:Tailwind CSS 插件、简化的类型定义等等
在本轮迭代中,我们致力于实现一流的 Tailwind CSS 支持,并扩展了插件 API。除此之外,我们还花时间对各种开发人员体验进行了改进。
记住:您可以通过运行以下命令启动新的 Fresh 项目
deno run -Ar https://fresh.deno.dev
或者通过在项目文件夹中运行以下命令更新现有项目
deno run -Ar https://fresh.deno.dev/update
。
以下是 Fresh 1.6 中新增功能的概述。继续阅读以了解所有详细信息,以及您对 Fresh 的下一个迭代的期待。
Fresh 1.6 一览
- 🌊 一流的 Tailwind CSS 插件 我们正在从 Twind 迁移到 Tailwind CSS。它具有更好的编辑器集成,并将 CSS 生成从渲染路径中移出。
- 📨 带有表单的局部组件 局部组件现在支持 Form 元素。
- 🧯 错误页面的局部组件 局部组件现在可以用来渲染错误页面。
- 🕵️♀️ 可关闭的错误叠加层 开发过程中显示的自定义错误叠加层现在可以关闭,以便您可以看到项目的 500 页面。
- ⚡️ 改进的岛屿捆绑策略 岛屿现在以更智能的方式捆绑在一起,这会导致更少的生成文件,并允许您将相关的岛屿分组在一起。
- ⚔️ 减少清单合并冲突 清单文件中的标识符现在是从文件名派生的,而不是递增的数字。 这极大地减少了合并冲突的可能性。
-
📦 支持预生成资产 Fresh 在遇到
_fresh/static
中的相同文件时,会优先提供生成的资产。 -
🧩 插件 API 增强 插件 API 现在支持从插件添加岛屿,添加
<link>
元素,并且有一个新的configResolved
钩子。 - 🏁 更快的路由匹配 路由匹配性能得到了提升,尤其是在项目拥有大量静态文件的情况下。
-
🛣️ 从子路径提供服务 Fresh 项目现在可以在子路径下提供服务,例如
https://example.com/foo/bar
,而不是域名的根路径。 - 🏆 简化的类型定义 不同的上下文类型的数量已从 6 个减少到 2 个,现在只有一个 props 类型,而不是四个。
一流的 Tailwind CSS 插件
这是一个期待已久的更新,Fresh 现在附带了合适的 Tailwind CSS 插件。它使用与 Node 中相同的 npm 包。这意味着我们将 Twind 插件置于维护模式,并将其标记为已弃用。
Tailwind CSS 比 Twind 具有许多优势,其中最重要的一个优势是 Tailwind CSS 处于积极维护状态,并支持更完善的编辑器体验。切换到 Tailwind CSS 也会为您带来一些不错的性能优势,因为它只需要在部署时生成 CSS 一次,而 Twind 则是在每次请求时动态生成 CSS。
要从 Twind 切换到 Tailwind CSS,请按照我们文档中的本指南进行操作。请注意,此插件需要提前构建才能为您的项目设置。
感谢Jason Gardner 为初始的 tailwindcss 插件集成提供了原型!
带有表单的局部组件
我们已经扩展了局部组件的功能,使其能够与 <form>
元素一起使用。类似于现有的针对 <a>
元素的局部组件支持,Fresh 在提交者的父元素具有“真值” f-client-nav
属性时,将选择使用局部组件处理表单。即使提交按钮位于封闭表单元素之外,这也适用。
<form id="foo">
<Partial name="slot-1">
<input type="text" value={value} name="name" />
</Partial>
</form>
<button
type="submit"
form="foo"
formaction="/form"
f-partial="/form"
formmethod="POST"
>
submit
</button>
错误页面的局部组件
使用 Fresh 1.6,您现在也可以对错误页面使用局部组件。以前,当响应状态码不是 ok 时,局部导航会出错。此限制已解除,我们只检查响应内容类型是否为 HTML。
在处理代码的这一部分时,我们还添加了对局部导航自动跟随重定向的支持。
可关闭的错误叠加层
在 Fresh 1.5 中,我们在开发过程中添加了一个错误叠加层,它显示有关错误发生位置的详细信息。问题是,它覆盖了用户的 _500.tsx
错误页面。我们修复了这个问题,现在错误叠加层是一个可以关闭的真正的错误叠加层。
改进的岛屿捆绑策略
在浏览器中,我们发现我们可以改进提供岛屿 JavaScript 代码的方式。以前,每个岛屿组件都会被移动到一个单独的包中,这会导致生成许多小于 1kb 的 .js
文件。新的策略遵循原始文件,让您对如何最好地为您的项目捆绑岛屿有更多控制权。通过尊重原始文件,您可以将相关的岛屿分组在一起并将它们包含在同一个包中。
减少清单合并冲突
虽然我们希望从长远来看消除清单,但我们认为可以改进当前频繁遇到的痛点,即在添加新路由或重命名路由时发生合并冲突。以前,标识符由 $<number>
组成,其中数字部分仅仅是递增的。新方法将文件名转换为有效的 JavaScript 标识符,并且只有在找到具有相同名称的现有标识符时才会附加一个数字。
感谢Reed von Redwitz 将此功能完善!
支持预生成资产
在许多情况下,您可能希望为部署生成资产文件。这些可能包括文件优化或生成 CSS(例如,用于 Tailwind CSS 插件)。其工作原理是,Fresh 会优先提供驻留在 <project>/_fresh/static
中的静态文件,而不是默认的 <project>/static
目录。
插件 API 增强
插件 API 是我们一直想要改进但没有时间进行的领域。随着此次发布,我们腾出了专门的时间来扩展它,加入了一些用户一直在要求的新功能。
来自插件的岛屿
其中最令人兴奋的功能之一是能够从插件添加岛屿。通过指定岛屿文件的路径,Fresh 将以与将它们放置在 islands/
目录中相同的方式处理它们。
import { Plugin } from "$fresh/server.ts";
import * as path from "https://deno.land/[email protected]/path/mod.ts";
const __dirname = path.dirname(path.fromFileUrl(import.meta.url));
export default function myIslandPlugin(): Plugin {
return {
name: "my-island-plugin",
islands: {
baseLocation: import.meta.url,
paths: [
"./plugin-islands/MyIsland.tsx",
"./plugin-islands/OtherPluginIsland.tsx",
],
},
};
}
感谢 Reed von Redwitz 帮助我们理清了所有细节。
从插件添加链接元素
插件现在可以添加 <link>
-elements 来添加额外的样式表或类似内容。
import { Plugin } from "$fresh/server.ts";
function MyPlugin(): Plugin {
return {
name: "link-inject",
render(ctx) {
ctx.render();
return {
links: [{ rel: "stylesheet", href: "styles.css" }],
};
},
};
}
感谢 Adam Gregory 为实现此功能而做出的努力!
新的 configResolved 钩子
通常情况下,您的插件需要根据 Fresh 配置应用不同的逻辑。我们已经将配置传递给 onBuildStart(config)
,但对于插件 API 的其他部分来说,这太晚了。因此,我们添加了一个新的 configResolved
钩子,类似于 vite
,它允许您获取完全解析的 Fresh 配置。
import { Plugin, ResolvedFreshConfig } from "$fresh/server.ts";
function MyPlugin(): Plugin {
let config: ResolvedFreshConfig;
return {
name: "my-cool-plugin",
configResolved(resolvedConfig) {
config = resolvedConfig;
},
};
}
更快的路由匹配
我们查看的另一个旨在让 Fresh 更快的领域是我们的路由器。我们注意到,在处理和匹配路由的方式上存在一些低效,尤其是在项目有许多静态文件时。我们添加了另一个优化,它检测路由是否没有动态部分,并将回退到简单的字符串比较。
总的来说,在应用所有优化之后,我们看到在 deno.com 上的路由匹配时间提高了 4-10 倍。
从子路径提供服务
并非每个项目都在根域地址上提供服务,用户的一个普遍需求是能够从子路径提供 Fresh。例如,它不应该托管在 https://example.com/
上,而应该在 https://example.com/foo/bar
上提供。现在,使用新的 router.basePath
配置选项可以实现这一点。
// fresh.config.ts
import { defineConfig } from "$fresh/server.ts";
export default defineConfig({
router: {
basePath: "/foo/bar",
},
});
再次感谢 Reed von Redwitz 将其推向完成!
简化的类型
我一直觉得我们可以在用户界面 API 的类型方面做得更好。例如,看看各种中间件和路由上下文类型。
MiddlewareHandlerContext
AppContext
ErrorHandlerContext
HandlerContext
LayoutContext
UnknownHandlerContext
需要了解很多类型。通过最近的代码清理和内部简化,我们意识到它们大多数都是相同的类型。因此,我们将上下文类型的数量减少到只有两个。
FreshContext
RouteContext
(用于异步路由/布局/应用程序包装器)
将来,我们甚至可能将其进一步减少到只有一个。请注意,我们仍然保留旧类型作为新类型的别名,以避免造成重大更改。
在简化核心的过程中,我们注意到我们的道具类型也同样复杂,比应该的复杂得多。
ErrorPageProps
AppProps
UnknownPageProps
LayoutProps
这四种类型已简化为单一类型
PageProps
我们对目前的简化感到非常满意,因为它减轻了使用 Fresh 的心理负担。
除了类型简化之外,我们还确保 Fresh 上下文对象在任何地方都可用。它现在也被传递给中间件和路由。此外,我们现在在上下文中传递完全解析的 Fresh config
,这样您就可以根据它应用不同的逻辑。
未来展望
与每次发布一样,我希望能够在这个版本中包含更多内容。事实是,这些功能需要更多时间来打磨。好奇的 Deno 阅读者可能已经注意到最近 Deno 版本说明中的 新的“预编译”JSX 转换。这是我在这个周期中花费了相当多时间的东西,也是我想要尽快带到 Fresh 的东西。它主要是对 JSX 转译方式的性能改进,将使大多数 Fresh 站点提速 2-4 倍。
我与 Bartek 一起完成的另一件事是 Deno 的 HMR。最初的原型也已在 Deno 1.38 中落地,但还需要做更多工作才能将其连接到 Fresh 中,并使其运行顺畅。
当然,还有更多工作要做,围绕着 Partial!我真的很想有一个合适的方法来指定待处理状态,进行错误处理,并公开功能,以便您可以在事件监听器中调用它。我不知道这最终会是什么样子,但这是我在 Fresh 中真正想要找到一个好的答案的东西。
由于圣诞节期间的假期,12 月的周期将比平时短一些。我们很可能会跳过一个功能发布,只在年底再发布另一个补丁版本。可以在 12 月迭代计划 中看到当前的迭代计划。