这篇文章将使用 deno 的 web 框架 Fresh,一个简单的 Web 应用 Link Maker,一个用于将链接转换成卡片样式的预览效果。
这个项目也放在了 fresh 的 Showcase,感兴趣的可以查看一番。
什么是 fresh?
fresh 自称是下一代 web 开发框架(这句话怎么这么熟悉?),是一个基于 Deno 的 Web 框架。它提供了许多用于构建 Web 应用程序和 API 的工具和功能。Fresh 框架特别强调简单性和灵活性,并着重于提供最佳的性能和开发体验。它支持 TypeScript,并且不需要任何配置或构建步骤。这些特性使得 Fresh 框架成为构建高效和现代 Web 应用程序的理想选择。
Fresh 的前端渲染层由 Preact 完成,包括 Islands 架构的实现也是基于 Preact。如果你想在 Fresh 中使用其他主流前端框架,目前来说有点无能为力。
创建 fresh 项目
deno 提供了非常友好的创建 fresh 项目的命令,运行:
deno run -A -r https://fresh.deno.dev my-project
cd my-project
deno task start
根据你的喜好进行配置,如下
此时会创建如下文件
my-project
├── components # 组件
│ └── Button.tsx # 按钮组件
├── deno.json # deno配置文件
├── dev.ts #
├── fresh.gen.ts #
├── import_map.json # 依赖导入映射
├── islands # 群岛(组件群岛)
│ └── Counter.tsx
├── main.ts # 入口文件
├── routes # 路由
│ ├── [name].tsx
│ ├── api
│ │ └── joke.ts
│ └── index.tsx
├── static # 静态资源
│ ├── favicon.ico
│ └── logo.svg
└── twind.config.ts # twind配置文件
介绍几个文件:
dev.ts
: 项目开发模式的匹配文件,假设你需要区分生产环境和开发环境,就可以通过 dev.ts,prod.ts 命令来指明入口main.ts
: 入口文件,会用于链接 Deno Deploy。fresh.gen.ts
: 这个清单文件会基于routes/
和islands/
文件夹自动生成。包含项目的 route 和 island 信息。import_map.json
: 这是用于管理项目的依赖项的导入映射。这允许轻松地导入和更新依赖项。
其中最主要的两个目录,这里会细说。
routes
routes/
: 存放项目中的所有路由。文件即路由,每个文件的名称对应于访问该页的路径。注:此文件夹中的代码永远不会直接发送到客户端.
其中 routes/api 通常存放一些 api 接口,这这里你完全可以将其当做一个 deno 的服务端,可以做后端能做的事情,通常来说就是提供一个可请求的 api 接口。
而其他文件就相当于一个可访问的页面组件,同样是文件路由系统,也可以在这里进行 SSR、中间件操作。
islands
Islands/
: 群岛,Fresh 中我并未看到对这一词的解释,你可以到 astro 群岛 看看新的 Web 架构模式,主要作用就是用于存放交互式组件(服务端组件),可以在客户端和服务端运行。有点类似与 next.js 的服务端组件,同样有两种状态(服务端,浏览器端)。
这一部分会有点难理解,你只要知道 IsLands 存放的组件有两种状态(服务端,浏览器端),下文称服务端组件,不同于 components 下的组件,服务端组件有一些优势,例如说
- 可以直接访问服务端相关资源
- 避免了不必要的客户端和服务端之间的交互,因此性能更快
- 允许一些类库可以直接运行在服务端,因此减小了客 户端包文件的大小
想要真正理解服务端组件,就不得不将其与 SSR 拿来对比了。
SSR 通常是将数据通过服务端的前端框架渲染成 HTML,直接将 HTML 返回给客户端就可以省去 xhr/fetch 请求的过程,只需要首次请求就能得到数据。此时页面交互,数据更新与传统的前端应用没有任何区别,通俗点说 SSR 就是省去 xhr/fetch 请求的过程。
而服务端组件会在服务端完成渲染,然后通过自定义的协议发送到客户端。前端应用会将新的 UI 整体(服务端组件)的合并到客户端 UI 树里面(也有叫 hydration 水合),此过程不会对客户端其他状态产生影响,还能达到保持客户端状态的目的,极大的增强了用户体验。
如果你仔细查看控制面板的网络请求输出,可以看到服务器端组件是可以请求的。(这里用的后面实战的截图作为展示)
不过既然服务端组件也有很多限制,就比如说服务端状态下,是无法使用 Web 相关 Api 的,数据传输(通过 props)是有前提的,要 JSON 可序列化,也就是说只能传递基本类型、基本对象、数组,像 Date,自定义类,函数等复制对象是无法传递的。
实战
项目还是相对比较简单的,将链接转化为一个卡片样式的预览效果(包含链接的标题,图片,描述)。
核心代码在 routes\api\link.ts
下,将会生成 /api/link
接口,例如访问 https://link-maker.deno.dev/api/link?q=https://kuizuo.cn 你就可以得到如下 json 数据
{
"title": "愧怍的小站",
"description": "Blog",
"image": "https://kuizuo.cn/img/logo.png",
"url": "https://kuizuo.cn"
}
原理就是通过 fetch 请求目标 url,通常来说得到的是一个 html 页面,这时使用 deno-dom 解析成 Dom 对象,通过 css 选择器选取所要的数据,并整合返回给调用方。
有了这个接口,剩下的前端工作就相对比较轻松了,主要也就是细节话的问题。