Astro
Astro 是一款灵活的元框架,它可以适应各种工具和集成,使您能够利用众多生态系统。
它还允许您使用您喜欢的 UI 框架(或根本不使用框架)编写组件,并在构建时将页面渲染为静态 HTML,或者使用服务器端渲染 (SSR) 在服务器上动态渲染。
这将生成快速、对 SEO 友好的输出,可以部署到任何静态托管环境或服务器。
有关集成的更多详细信息以及查看源代码,请查看 GitHub 上的 QwikDev Astro 集成。
Astro 而不是 Qwik City
将 Astro 与 Qwik 集成时,请注意 Qwik City API 与 Astro 不兼容。
Astro 是一款元框架,它提供自己的 API 和功能集来处理这些问题。这些包括
- 路由
- 页面
- 布局
- 数据获取
- 服务器端渲染 (SSR)
因此,将 Qwik 与 Astro 集成时,应使用 Astro 的 API 和功能,而不是 Qwik City 的 API。这将确保您的 Qwik 组件在 Astro 环境中正常工作。有关更多信息,请参阅 Astro 文档。
@qwikdev/astro 💜
此集成利用了 可恢复性 在 Astro 中的强大功能,使用 Qwik 组件。
安装
有两种方法可以添加集成。让我们从最简单的方法开始!
Astro CLI
Astro 带有一个命令行工具,用于合并内置集成:astro add
。此命令将
- 可选地安装所有必需的依赖项和对等依赖项
- 可选地修改您的
astro.config.*
文件以应用集成
要安装 @qwikdev/astro
,请从您的项目目录运行以下命令并按照提示操作
# Using NPM
npx astro add @qwikdev/astro
# Using Yarn
yarn astro add @qwikdev/astro
# Using PNPM
pnpm astro add @qwikdev/astro
设置 TypeScript 配置
集成需要在 tsconfig.json
中添加以下内容,以便 typescript 识别 Qwik 的 JSX 类型。
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "@builder.io/qwik"
}
如果您遇到任何问题,请 在 Github 上发布它们 并尝试以下手动安装。
手动安装
首先,安装 @qwikdev/astro
集成,如下所示
npm install @qwikdev/astro
通常,包管理器会安装对等依赖项。但是,如果您在启动 Astro 时收到 Cannot find package '@builder.io/qwik'
警告,请安装 Qwik。
npm install @builder.io/qwik
现在,使用 integrations
属性将集成添加到您的 astro.config.*
文件中
// astro.config.mjs
import { defineConfig } from 'astro/config';
+ import qwikdev from '@qwikdev/astro';
export default defineConfig({
// ...
integrations: [qwikdev()],
// ^^^^^
});
如果您还使用其他集成,例如
react()
或preact()
,则需要将qwikdev()
放在列表中的它们之前。否则,您将遇到Not a QRL
错误。类似于以下内容:integrations: [qwikdev(), react(), preact()]
。
Qwik 不进行水合,它 **从根本上不同**
Astro 因其部分水合方法而受欢迎,而 Qwik 不需要水合。
添加 Qwik 组件
在其他 UI 框架中,需要水合指令来实现交互性,例如 client:only
或 client:load
。在 Qwik 中不需要这些,因为没有水合!
在元框架(如 Astro 或 Qwik City)中使用 Qwik 时,组件在服务器上加载,在单独的线程中预取,并在客户端“恢复”。
例如,以下是如何在 Qwik 中创建计数器组件(例如,在 src/components/counter.tsx
中)。
import { component$, useSignal } from "@builder.io/qwik";
export const Counter = component$(() => {
const counter = useSignal(0);
return <button onClick$={() => counter.value++}>{counter.value}</button>;
});
它可以在我们的 index.astro
页面中按以下方式使用
---
import { Counter } from "../components/counter";
---
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width" />
<meta name="generator" content={Astro.generator} />
<title>Astro</title>
</head>
<body>
<h1>Astro.js - Qwik</h1>
/* no hydration directive! */
<Counter />
</body>
</html>
快速启动,保持快速
Astro 的主要功能之一是 **默认情况下无 JS**。不幸的是,在添加 JavaScript 框架和任何后续组件后,通常情况并非如此。
如果我们想使用 React、Vue、Svelte 等框架引入交互性,那么框架运行时就会被引入。添加到页面的组件数量也会随着 JavaScript 数量的增加而线性增加 O(n)。
Astro + Qwik
Qwik 在 Astro 的 **默认情况下无 JS** 原则的基础上构建,并且更进一步。由于可恢复性,组件只有在恢复时才会执行。即使有交互性,框架也只有在需要时才会执行。它是 O(1) 常数,并且对开发者来说是零工作量。
相反,在页面加载时,一个称为 Qwikloader 的微型 1kb 压缩 JavaScript 代码会下载应用程序的其余部分,具体取决于需要。
细粒度延迟加载
水合会迫使你的手 急切地执行代码。对于树之外的组件(如模态框)来说这不是问题,但它必须彻底检查渲染树中的每个组件,以防万一。
Qwik 在 Astro 中表现出色,这得益于 Resumability 和它以细粒度方式延迟加载代码的能力。尤其适用于营销网站、博客和以内容为中心的网站,这些网站包含许多组件。
即时交互
从 @qwikdev/astro
v0.4 开始,我们在 Astro 中添加了对 推测模块获取 的支持。
这为你的 Qwik 组件提供了即时交互性。推测模块获取将在服务工作者的后台预取应用程序捆绑包,以便在需要时,代码已存在于浏览器缓存中。
你应该能够开箱即用地使用 Qwik insights!
容器与岛屿
虽然 Astro 通常采用与其他框架类似的岛屿架构,但 Qwik 使用一种称为 Qwik 容器 的不同策略。尽管方法不同,但两者都具有类似的限制。
在 DOM 中,你可能会注意到没有 <astro-island>
自定义元素,这是因为对于 Astro 来说,Qwik 看起来像静态数据。
这是因为在 Qwik 中,处理程序本身是应用程序的根/入口点。
跨容器通信
一个常见的限制是尝试将状态传递到另一个岛屿或容器中。
在现代 Web 开发中,共享状态至关重要。问题是,当需要跨不同容器或岛屿共享状态时,我们如何实现这一点?
为什么不使用全局信号或 nanostores?
其他与 Astro 结合使用的框架通过使用 nano stores 或 全局信号 来解决这个问题。
虽然你可能会看到所有测试都通过了,并且应用程序按预期工作,但我们不建议使用 nanostores 或全局信号。它们可能会在 SSR 上下文中导致一些意外行为。
例如,在 Solid 的教程中提到了以下内容
虽然可以使用全局状态和计算,但 Context 有时是更好的解决方案。此外,重要的是要注意,全局状态不应在 SSR(服务器端渲染)解决方案(如 Solid Start)中使用。在服务器上,全局状态在请求之间共享,并且缺乏数据隔离会导致(并且会)导致错误、内存泄漏并具有安全隐患。建议始终通过上下文提供应用程序状态,而不是依赖全局状态。
自定义事件
在 Qwik 中,不包含全局信号状态是一个设计决策。
相反,我们建议使用 **自定义事件**,它提供了以下几个优点
- 性能(避免不必要的状态同步)
- 不会在页面加载时唤醒框架
- 微前端 (MFE) 支持
- 页面上可以存在不同版本
- 事件驱动
- 解耦
此示例 展示了如何在整个应用程序中使用自定义事件。请注意 counter.tsx
、random-island.tsx
和我们的 index.astro
页面。
使用多个 JSX 框架
要在 Astro 中使用多个 JSX 框架(如 Qwik、React、Preact 或 Solid),你需要设置规则来确定每个框架应该处理哪些文件。
例如,你可以将所有 Qwik 组件放在名为 qwik
的文件夹中。然后,配置 Astro 使用 Qwik 集成来处理此文件夹中的任何文件。
import { defineConfig } from "astro/config";
import qwik from "@qwikdev/astro";
import react from "@astrojs/react";
export default defineConfig({
integrations: [
qwik({ include: "**/qwik/*" }),
react({ include: "**/react/*" }),
solid({ include: "**/solid/*" }),
],
});
上面我们正在同一个 Astro 项目中使用 Qwik、React 和 Solid 集成。
如果我们看一下第一个集成,它将查找 qwik
文件夹中的任何文件,并使用 Qwik 处理此文件夹中的任何文件。
为简单起见,请考虑将常见框架组件分组到同一个文件夹中(如 /components/react/
和 /components/qwik/
)。但是,这是可选的。
Qwik React
如果你使用 React,我们建议使用 @builder.io/qwik-react
集成。它是 @astrojs/react
的直接替代品,允许无缝过渡到 Qwik。
import { defineConfig } from "astro/config";
import qwikdev from "@qwikdev/astro";
import { qwikReact } from "@builder.io/qwik-react/vite";
// https://astro.build/config
export default defineConfig({
integrations: [qwikdev()],
vite: {
plugins: [qwikReact()],
},
});
使用 Qwik-React,我们可以“qwikify”我们的 React 组件,并在我们的 Qwik 应用程序中使用它们,甚至在 Astro 文件之外嵌套 Qwik 和 React 组件!
你不需要使用 qwikReact 指定 include 属性。
这是一个示例,展示了使用 qwik-react
集成的 React 组件。
/** @jsxImportSource react */
import { qwikify$ } from "@builder.io/qwik-react";
import { useState } from "react";
const ReactCounter = () => {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>React {count}</button>;
};
// "Qwikified" React component
export const QReactCounter = qwikify$(ReactCounter);
创建计数器后,可以在我们的 index.astro 文件中使用它。
<QReactCounter qwik:visible />
请注意,在 .astro
文件中,我们使用 qwik:
水合指令前缀,这是为了避免与 Astro 开箱即用提供的指令冲突。
你也可以使用 client:*
前缀,但只能在 tsx 文件中使用。你可以在 Qwik 文档的 添加交互性 部分找到指令列表。
Qwik React 组件仍然具有水合功能,因此建议使用 Qwik-React 作为迁移到可恢复组件的策略。
jsxImportSource
不幸的是,TypeScript 只能有一个 jsxImportSource
默认值。如果你在 Astro 应用程序中与 React、Solid 或 Preact 的 Astro 集成一起使用,请覆盖每个组件的导入源。
如果你使用的是 @astrojs/react,你可以使用 qwik-react。适当的配置将开箱即用地得到支持。
/** @jsxImportSource react */
import { useState } from "react";
export const ReactCounter = () => {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
};
例如,Solid JS 是
/** @jsxImportSource solid-js */
例如,Preact 是
/** @jsxImportSource preact */
命名插槽
对于 Astro 中的命名插槽,请添加 slot
而不是在标记中添加 q:slot
。
my-slot-comp.tsx
import { Slot, component$, useSignal } from "@builder.io/qwik";
export const MySlotComp = component$<{ initial: number }>((props) => {
return (
<>
<Slot name="test" />
</>
);
});
index.astro
<MySlotComp>
<div slot="test">Content inside the slot named test!</div>
</MySlotComp>
默认插槽在它们的 Qwik City 对应物中按预期工作。
社区指南
-
Paul Scanlon 展示了 在 Astro 中使用 Qwik 而不是 React 和 Vanilla JS 的实践操作。
-
Rishi Raj Jain 编写了一份关于使用 Astro 的 Vercel SSR 适配器 设置 Qwik 的出色指南。
-
Paul Scanlon 探讨了在 Astro 中使用 Qwik 作为 React 的替代方案。
视频
-
观看 Jason 和 Steve [讨论 Qwik Astro 集成] 在 LWJ show 上的讨论。
-
Awesome 的 Qwik + Astro 视频 介绍了 Astro 如何变得更快。
贡献
我们很乐意你贡献!首先阅读我们的 贡献指南。它包含了所有你需要参与的信息,包括一个关于集成在幕后如何工作的深入部分。
在 builder.io 的 discord 中还有一个 qwik-astro
频道,用于讨论 API 更改、集成中可能的想法以及其他很酷的东西。😊
鸣谢
特别感谢 Astro 核心团队的 Matthew 和 Nate!如果没有他们的帮助,这个集成将不可能实现。
Nate 的句柄