Qwik 是一个以 Resumability(可恢复性)为核心设计理念的现代前端框架,它彻底抛弃了传统框架的水合(Hydration)过程,让页面在首屏几乎不下载、不执行 JavaScript 就能交互。对于追求极致首屏性能与 SEO 的技术团队来说,Qwik 正在成为”零水合前端”的代表方案。
一、为什么你需要关注 Qwik
当你的 React 或 Vue 应用体积膨胀到几百 KB,浏览器需要先下载整包 JS、再执行一遍”水合”把事件绑定到 DOM 上,用户才能真正点击按钮。这个”先下载、再执行、后交互”的链路,正是 Core Web Vitals 中 TTI(可交互时间)和 TBT(总阻塞时间)被拉垮的主因。Qwik 换了一条路:服务器把页面渲染成带有 q: 指令标记的静态 HTML,浏览器只恢复”当前被点击的那一小块”逻辑,其余代码永远不下载。原生 Web Components 自定义元素 也能做到轻量,但 Qwik 在状态与路由层面做了更深的可恢复性改造。
二、Resumability 与 Hydration 的根本区别
2.1 传统框架的 Hydration 开销
React/Vue 的 SSR 只解决了”首屏有内容”,但客户端必须重新执行一次框架运行时来重建组件树、绑定事件,这部分 JS 通常占首屏下载量的 60% 以上。
2.2 Qwik 的 Resumability 思路
Qwik 在服务器端就序列化了”事件处理器在哪、需要加载哪个 chunk”的信息。点击发生时,框架按需懒加载那一个函数并恢复状态,而不是启动整个应用。下面的 HTML 片段直观对比了两者:
<!-- 传统 React 水合:需要整包 JS 才能绑定 onClick -->
<button onclick="__reactBoundHandler(123)">点击</button>
<!-- Qwik Resumability:仅记录处理器位置,点击时才加载 -->
<button q:listen:click="./chunk-a.js#handler1">点击</button>
三、快速上手:创建第一个 Qwik 应用
Qwik 官方提供了 create qwik 脚手架,一条命令即可拉起带路由、测试、适配器的基础工程:
# 交互式创建项目
npm create qwik@latest
# 启动开发服务器
npm install
npm run dev # 默认 http://localhost:5173
# 构建生产包(输出到 dist/)
npm run build
生成的关键目录:src/routes/ 存放页面与布局,src/components/ 存放可复用组件,src/entry.*.tsx 是不同环境的入口。配合 VS Code 的 ESLint 与 Qwik 官方插件,开发体验很顺滑。
四、组件与状态:用 signal 表达响应式
4.1 useSignal 与 useStore
Qwik 的响应式核心是 signal:它是细粒度的可变单元,读写都被框架追踪,状态变更只会更新引用它的那部分 DOM,无需虚拟 DOM 全量 diff。
import { component$, useSignal } from '@builder.io/qwik';
export const Counter = component$(() => {
const count = useSignal(0);
return (
<button onClick$={() => count.value++}>
已点击 {count.value} 次
</button>
);
});
注意事件名是 onClick$ 而非 onClick——后缀 $ 告诉 Qwik 这个处理器需要被懒加载与序列化,这正是 Resumability 的语法落地。
五、路由与按需恢复
Qwik 采用文件式路由,目录即路由。页面级的 routeLoader$ 能在服务端预取数据,并在客户端恢复时复用,避免重复请求:
import { component$, routeLoader$ } from '@builder.io/qwik';
export const useServerData = routeLoader$(async () => {
const res = await fetch('https://api.example.com/metrics');
return res.json();
});
export default component$(() => {
const data = useServerData();
return <p>当前指标:{data.value.count}</p>;
});
六、异步数据:useResource
对于客户端触发的异步请求,用 useResource$ 配合 useResource$ 的 track 机制声明依赖,框架会在依赖变化时自动重新拉取,并序列化缓存以便恢复:
import { component$, useSignal, useResource$ } from '@builder.io/qwik';
export const Search = component$(() => {
const keyword = useSignal('');
const results = useResource$(async ({ track, cleanup }) => {
track(() => keyword.value);
const res = await fetch(`/api/search?q=${keyword.value}`);
return res.json();
});
return (
<>
<input onInput$={(e) => keyword.value = e.target.value} />
<ul>{results.value?.map((r) => <li key={r.id}>{r.name}</li>)}</ul>
</>
);
});
数据请求层如果需要统一管理缓存与乐观更新,可参考我们之前写的 TanStack Query 数据请求实战,两者在”减少无谓请求”的目标上高度一致。
七、Qwik 与主流框架怎么选
| 维度 | Qwik | React | Vue | SolidJS |
|---|---|---|---|---|
| 首屏 JS 策略 | Resumability 零水合 | 客户端 Hydration | 客户端 Hydration | 编译期细粒度响应式 |
| 首屏下载体积 | 极小(按需) | 较大 | 中等 | 小 |
| 可恢复性 | 原生支持 | 需手动拆分 | 需手动拆分 | 部分支持 |
| 适用场景 | 内容站/电商/SEO 优先 | 复杂交互应用 | 渐进式应用 | 高性能 SPA |
如果你的站点以”内容展示 + 少量交互 + 强 SEO”为主(比如技术博客、营销页),Qwik 的收益最明显;若是高度动态的仪表盘,React/Vue 生态更成熟。
八、生产部署:Docker + Nginx
8.1 多阶段构建 Dockerfile
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
8.2 Nginx 反代与 SPA 回退
server {
listen 80;
server_name qwik.example.com;
root /usr/share/nginx/html;
location / {
try_files $uri $uri/ /index.html;
}
gzip on;
gzip_types text/html application/javascript image/svg+xml;
}
容器化与反代的细节我们在 Docker 容器编排 与 Nginx 反向代理 两篇里有更系统的讲解,建议结合阅读。把构建产物交给 GitHub Actions 做持续集成,可以做到 push 即部署。
九、性能与 SEO 收益
9.1 Core Web Vitals 实测
在同等内容下,Qwik 的 LCP(最大内容绘制)通常比 Hydration 型框架快 1–3 倍,因为关键内容在 HTML 里已经就位,无需等 JS 执行。配合静态预渲染,TTI 甚至可以降到毫秒级。对搜索引擎而言,首屏可见即收录友好,这也正是”零水合”对 SEO 最直接的价值。
十、Qwik City:路由、布局与渲染模式
Qwik 的应用层叫 Qwik City,它在框架之上补齐了路由、布局、SSR/SSG 与端点(endpoint)能力。一个目录既可以是页面,也可以配一个 layout.tsx 作为嵌套布局,多个页面共享同一外壳,避免重复加载导航与样式。渲染模式上,Qwik City 支持纯静态(SSG)、服务端渲染(SSR)和流式(streaming),你可以针对内容页用 SSG 把 HTML 直接落盘,针对个性化页用 SSR,两者都能保持零水合特性。
端点(endpoint)让一个路由直接返回 JSON 或处理表单提交,无需额外的 API 网关:
import { RequestHandler } from '@builder.io/qwik-city';
export const onGet: RequestHandler = ({ json }) => {
json(200, { ok: true, time: Date.now() });
};
这种”页面即接口”的约定,让前后端边界更清晰,也减少了一次网络往返。结合 SSE 流式推送 还能做服务端实时推送,但需注意不要让长连接阻塞 Qwik 的恢复链路。
十一、三个常见踩坑
11.1 闭包与序列化边界
处理器里的变量必须能被序列化。不要捕获无法序列化的大对象或函数,否则恢复时会失败。
11.2 不要滥用全局状态
signal 虽方便,但全局 store 过大会拖慢序列化。按组件局部状态优先,必要时才提升到 store。
11.3 第三方脚本阻塞恢复
非必要的分析脚本要用 async/defer 加载,避免阻塞 Qwik 的恢复链路,否则 Resumability 优势被抵消。
十二、落地建议
建议先用 Qwik 重写一个”重内容、轻交互”的页面(如首页或文档页)做 A/B 对比,量化 LCP/TTI 提升后再决定是否全面迁移。迁移时优先复用既有的后端 API,前端只替换渲染层。
十三、总结
Qwik 用 Resumability 把”首屏 JS 开销”几乎降为零,是追求性能与 SEO 团队值得一试的框架。它并非要取代 React/Vue,而是在”内容优先”的场景提供了更优解。理解 $ 后缀、signal 与按需恢复三件事,你就掌握了 Qwik 的精髓。




