Qwik 框架入门实战:从开源项目 Qwik City 开始,用可恢复性替代水合

一、引言

你打开一个网页,页面内容已经渲染出来了------文字、图片、布局都完美呈现。但当你点击一个按钮,它没有任何反应。过了几秒,按钮才变得可用。这关键的几秒,就是"水合税"。

这是所有 React 和 Vue SSR 应用都面临的问题。浏览器收到服务器渲染的 HTML 后,还需要下载、解析、执行整套 JavaScript 运行时和组件代码,才能让页面变得可交互。这个过程叫做水合(Hydration)

Qwik------由 Angular 之父 Misko Hevery 创建的前端框架------提出了一种全新的解决方案:可恢复性(Resumability)。它不水合,而是从服务器中断的地方继续执行。

本文将从 Qwik 开源项目出发,介绍其核心概念,然后通过 Qwik City 元框架构建一个完整的待办事项应用。


二、水合的问题与可恢复性的答案

2.1 水合税

传统的服务器端渲染(SSR)流程如下:

  1. 服务器渲染 HTML → 浏览器看到页面(但不可交互)

  2. 浏览器下载 JavaScript 运行时 + 组件代码

  3. 浏览器重新执行整个组件树,附加事件处理器

  4. 页面变得可交互

这个流程的问题在于步骤 2 和 3 阻塞了交互性。React 的运行时约 40KB(gzipped),加上组件代码,初始 JS 包通常在 100KB 以上。用户看到页面后,必须等待这些代码下载、解析、执行完毕,才能点击任何东西。

更重要的是,页面越复杂,水合时间越长------因为所有组件都要重新执行一遍。

2.2 Qwik 的可恢复性

Qwik 的解决思路完全不同:

  1. 服务器渲染 HTML → 将应用状态序列化到 HTML 中

  2. 浏览器加载约 1.6KB 的 QwikLoader

  3. QwikLoader 读取 HTML 中的序列化状态

  4. 用户交互时,只加载对应组件的代码

  5. 不需要重新执行组件树,直接恢复

关键区别在于:传统框架需要重新执行组件树来重建状态,而 Qwik 把状态保存在 HTML 中,直接恢复

水合就像你写了一半的文档,关掉电脑,第二天打开后重新从头开始写。可恢复性就像你保存了文档,第二天打开后从昨天中断的地方继续写。显然后者更高效。

2.3 水合 vs 可恢复性对比


三、Qwik 开源项目介绍

3.1 项目背景

Qwik 由 Misko Hevery (Angular 框架创始人)创建,于 2023 年 5 月发布 1.0 稳定版。目前 GitHub 上拥有 22K+ stars,最新稳定版为 1.20.0(2026 年 5 月发布),2.0 正处于 beta 阶段。

技术栈:

  • Qwik(核心框架)+ Qwik City(元框架)

  • TypeScript + JSX

  • Vite(构建工具)

  • MIT 开源许可证

3.2 核心 API

Qwik 使用 JSX 语法,但引入了一系列 $ 后缀的 API 来标记可延迟加载的边界:

复制代码
// component$ 定义可延迟加载的组件
export const Counter = component$(() => {
  const count = useSignal(0);  // useSignal 响应式状态
  return (
    <button onClick$={() => count.value++}>  // onClick$ 延迟加载事件处理器
      Count: {count.value}
    </button>
  );
});

核心 API 一览:

API 用途 说明
component$() 定义组件 标记组件可延迟加载
useSignal() 基本状态管理 类似 SolidJS 的信号
useStore() 复杂状态对象 深层响应式对象
routeLoader$() 服务端数据加载 类似 Next.js 的 getServerSideProps
routeAction$() 服务端操作 表单提交、数据修改
$ 后缀 标记懒加载边界 组件、事件、加载器都带 $

3.3 Qwik City 架构


四、实战:构建待办事项应用

4.1 项目初始化

复制代码
npm create qwik@latest
# 选择 Basic App 模板
cd qwik-todo-app
npm install
npm start

4.2 目录结构

复制代码
qwik-todo-app/
├── src/
│   ├── components/         # 通用组件
│   ├── routes/             # 路由目录(文件系统路由)
│   │   ├── index.tsx       # 首页
│   │   ├── layout.tsx      # 根布局
│   │   └── todos/          # 待办事项路由
│   │       └── index.tsx
│   ├── root.tsx            # 根组件
│   ├── entry.ssr.tsx       # SSR 入口
│   └── global.css          # 全局样式
├── public/                 # 静态资源
├── vite.config.ts           # Vite 配置
└── package.json

4.3 用 routeLoader$ 加载数据

复制代码
import { component$, useSignal } from '@builder.io/qwik';
import { routeLoader$, routeAction$, Form } from '@builder.io/qwik-city';

interface Todo {
  id: number;
  text: string;
  completed: boolean;
}

// routeLoader$ 在服务端加载数据
export const useTodoList = routeLoader$(async () => {
  const todos: Todo[] = [
    { id: 1, text: '学习 Qwik 基础知识', completed: true },
    { id: 2, text: '理解可恢复性概念', completed: false },
    { id: 3, text: '构建一个 Qwik 应用', completed: false },
  ];
  return todos;
});

4.4 用 routeAction$ 处理操作

复制代码
// 添加操作
export const useAddTodo = routeAction$(async (data) => {
  return { success: true, message: `已添加: ${data.text}` };
});

// 删除操作
export const useDeleteTodo = routeAction$(async (data) => {
  return { success: true, message: `已删除 ID: ${data.id}` };
});

4.5 构建 UI 组件

复制代码
export default component$(() => {
  const todos = useTodoList();
  const addTodo = useAddTodo();
  const deleteTodo = useDeleteTodo();
  const newTodoText = useSignal('');

  return (
    <div style="max-width: 600px; margin: 2rem auto; padding: 1rem;">
      <h1>我的待办事项</h1>
      
      <Form action={addTodo} style="display: flex; gap: 0.5rem; margin-bottom: 1.5rem;">
        <input
          type="text" placeholder="输入新待办事项..."
          value={newTodoText.value}
          onInput$={(_, el) => { newTodoText.value = el.value }}
          name="text" required
        />
        <button type="submit">添加</button>
      </Form>

      <ul>
        {todos.value.map((todo) => (
          <li key={todo.id} style="display: flex; justify-content: space-between; padding: 0.75rem; border-bottom: 1px solid #eee;">
            <span style={todo.completed ? 'text-decoration: line-through; color: #999;' : ''}>
              {todo.text}
            </span>
            <Form action={deleteTodo}>
              <input type="hidden" name="id" value={todo.id} />
              <button type="submit" style="background: #e74c3c; color: white; border: none; border-radius: 4px; padding: 0.3rem 0.6rem; cursor: pointer;">
                删除
              </button>
            </Form>
          </li>
        ))}
      </ul>
    </div>
  );
});

4.6 运行测试

复制代码
npm start
# 访问 http://localhost:5173/todos

打开浏览器,你会看到待办事项列表。打开开发者工具的 Network 面板------你会发现初始加载时几乎没有 JS 代码被下载。只有当你点击"添加"或"删除"按钮时,Qwik 才会按需加载对应的处理代码。


五、Qwik vs 主流框架对比

5.1 核心架构

维度 Qwik React Vue Next.js
核心架构 可恢复性 水合 水合 水合 + RSC
初始 JS ~1.6KB ~40KB ~30KB ~40KB+
懒加载 自动($ 边界) 手动(React.lazy) 手动 手动 + 自动
响应式 信号(Signal) 状态(useState) 响应式(ref) 状态(useState)

5.2 路由与数据加载

维度 Qwik City Next.js Nuxt
路由方式 文件系统 文件系统 文件系统
数据加载 routeLoader$ RSC / getServerSideProps AsyncData
数据操作 routeAction$ Server Actions API Routes
布局系统 layout.tsx layout.tsx layout.vue

六、结语

Qwik 代表了一种全新的前端架构思路。它不试图优化水合过程,而是直接从根本上消除水合

在我看来,Qwik 最大的贡献不是"又一个前端框架",而是证明了"可恢复性"是一个可行的架构方案。它告诉我们:浏览器不需要重新执行服务器已经做过的工作。把状态保存在 HTML 中,从中断处恢复,这才是更高效的做法。

适用场景: 内容密集型网站、首屏性能严苛的项目、边缘部署 不适用场景: 复杂客户端交互应用、生态重度依赖项目

学习建议: 先理解"可恢复性"和"水合"的区别 → 通过官方教程创建项目 → 构建一个小型应用 → 深入了解 $ 系统


七、参考文献

  1. Qwik 官方文档

  2. Qwik GitHub 仓库

  3. Qwik City 元框架指南

  4. Framework Benchmarks

  5. Qwik vs React: Is Resumability the Future of Frontend?

  6. Qwik vs Next.js for Solo Developers

相关推荐
冬奇Lab1 小时前
开源项目第196期:LiveTalking — 实时交互流式数字人引擎,支持 Wav2Lip/MuseTalk/ER-NeRF
人工智能·开源·资讯
剑胆琴心静水深流1 小时前
全栈之路6---web集成与呈现
前端·vue.js·spring boot·分布式·spring·前端框架·npm
皮卡丘不断更2 小时前
从遥操作示范到可复现训练:LeRobot 0.6.1 的机器人学习工作流
人工智能·学习·机器人·开源·开发工具
IT_陈寒3 小时前
被Java的final坑惨了,这些细节你可能也忽略了
前端·人工智能·后端
Zadig3 小时前
企业 Agent 总烂在 Demo 里?Zadig 工作流 AI 任务给了一条路
后端·devops
Zadig3 小时前
告别"人肉扛雷":Zadig 用 AI 接管发布前最脏最累的 15 分钟
后端·aiops
Nturmoils4 小时前
向量数据库不该成为新孤岛:KingbaseES 多模融合架构如何减少数据搬运
后端
一座古城4 小时前
Claude Code 架构源码解析:AI 应用开发的范式跃迁
前端·后端
Nturmoils4 小时前
用蓝耘元生代做 GitHub 热榜解读:Dify Chatflow 接入和真实项目分析
后端