Qwik
Qwik 是一个为服务器端渲染(SSR)和 "恢复(resumability)" 优化的前端 JavaScript 框架。它被设计成在浏览器中尽可能快地加载,即使是最大型和最复杂的Web应用程序。Qwik 的主要卖点是其独特的 "按需加载" 机制,它能够确保只有当用户与页面交互时,相关代码才会被加载和执行。

查看更多相关内容
Qwik City 的核心功能有哪些?Qwik City 是 Qwik 的全栈框架,提供了完整的路由、数据获取和服务端功能。以下是 Qwik City 的核心概念和使用方法:
## 1. Qwik City 简介
Qwik City 构建在 Qwik 之上,提供了:
- 基于文件系统的路由
- 服务端数据加载
- 表单处理和验证
- 中间件支持
- SEO 优化
- 国际化支持
## 2. 路由系统
### 文件系统路由
Qwik City 使用基于文件系统的路由,文件结构直接映射到 URL:
```
src/
├── routes/
│ ├── index.tsx -> /
│ ├── about/
│ │ └── index.tsx -> /about
│ ├── products/
│ │ ├── index.tsx -> /products
│ │ └── [id]/
│ │ └── index.tsx -> /products/:id
│ └── layout.tsx -> 全局布局
```
### 动态路由
```tsx
// routes/products/[id]/index.tsx
import { component$ } from '@builder.io/qwik';
import { routeLoader$ } from '@builder.io/qwik-city';
import { useLocation } from '@builder.io/qwik-city';
export const useProductData = routeLoader$(async ({ params, url, env }) => {
const response = await fetch(`https://api.example.com/products/${params.id}`);
return response.json();
});
export default component$(() => {
const product = useProductData();
const location = useLocation();
return (
<div>
<h1>{product.value.name}</h1>
<p>{product.value.description}</p>
<p>Price: ${product.value.price}</p>
</div>
);
});
```
### 嵌套路由
```tsx
// routes/layout.tsx
import { component$, Slot } from '@builder.io/qwik';
export default component$(() => {
return (
<div>
<header>Header</header>
<main>
<Slot />
</main>
<footer>Footer</footer>
</div>
);
});
```
## 3. 数据加载
### routeLoader$ - 服务端数据加载
```tsx
import { routeLoader$ } from '@builder.io/qwik-city';
export const useUserData = routeLoader$(async ({ params, url, env, requestEvent }) => {
// 访问请求参数
const userId = params.id;
// 访问 URL 查询参数
const searchParams = url.searchParams;
const page = searchParams.get('page');
// 访问环境变量
const apiKey = env.get('API_KEY');
// 访问请求事件
const cookie = requestEvent.cookie.get('session');
const response = await fetch(`https://api.example.com/users/${userId}`);
return response.json();
});
```
### clientLoader$ - 客户端数据加载
```tsx
import { clientLoader$ } from '@builder.io/qwik-city';
export const useClientData = clientLoader$(async ({ params, navigate }) => {
// 客户端数据获取
const response = await fetch(`/api/data/${params.id}`);
return response.json();
});
```
### useResource$ - 组件级数据加载
```tsx
import { component$, useResource$ } from '@builder.io/qwik';
export const UserList = component$(() => {
const users = useResource$(({ track, cleanup }) => {
// 追踪依赖
track(() => /* 依赖项 */);
// 清理函数
cleanup(() => {
// 清理逻辑
});
return fetch('https://api.example.com/users');
});
return (
<div>
{users.value ? (
<ul>
{users.value.map(user => <li key={user.id}>{user.name}</li>)}
</ul>
) : (
<p>Loading...</p>
)}
</div>
);
});
```
## 4. 表单处理
### action$ - 服务端表单处理
```tsx
import { action$, zod$, z } from '@builder.io/qwik-city';
import { component$, Form } from '@builder.io/qwik-city';
// 定义表单验证
export const useContactForm = action$(async (data, { requestEvent }) => {
// 服务端处理逻辑
const { name, email, message } = data;
// 发送邮件
await sendEmail({ name, email, message });
return { success: true };
}, zod$({
name: z.string().min(2),
email: z.string().email(),
message: z.string().min(10)
}));
export default component$(() => {
const action = useContactForm();
return (
<Form action={action}>
<input name="name" placeholder="Name" />
<input name="email" type="email" placeholder="Email" />
<textarea name="message" placeholder="Message"></textarea>
<button type="submit">Submit</button>
{action.value?.success && <p>Message sent!</p>}
</Form>
);
});
```
### clientAction$ - 客户端表单处理
```tsx
import { clientAction$ } from '@builder.io/qwik-city';
export const useClientAction = clientAction$(async (data) => {
// 客户端处理逻辑
console.log('Client action:', data);
return { success: true };
});
```
## 5. 中间件
### 请求中间件
```tsx
// routes/middleware.ts
import { middleware$ } from '@builder.io/qwik-city';
export const onRequest = middleware$(async ({ requestEvent, next }) => {
// 请求前处理
const url = requestEvent.url;
const cookie = requestEvent.cookie.get('session');
if (!cookie && url.pathname !== '/login') {
throw requestEvent.redirect(302, '/login');
}
return next();
});
export const onResponse = middleware$(async ({ requestEvent, next }) => {
// 响应后处理
const response = await next();
// 添加响应头
response.headers.set('X-Custom-Header', 'value');
return response;
});
```
## 6. SEO 优化
### 元数据设置
```tsx
import { component$ } from '@builder.io/qwik';
import { routeLoader$, useDocumentHead, useLocation } from '@builder.io/qwik-city';
export const useProductData = routeLoader$(async ({ params }) => {
const response = await fetch(`https://api.example.com/products/${params.id}`);
return response.json();
});
export default component$(() => {
const product = useProductData();
return <div>{product.value.name}</div>;
});
export const head = useDocumentHead$(({ resolveValue }) => {
const product = resolveValue(useProductData);
return {
title: product.name,
meta: [
{ name: 'description', content: product.description },
{ property: 'og:title', content: product.name },
{ property: 'og:description', content: product.description },
{ property: 'og:image', content: product.image }
]
};
});
```
## 7. 国际化
### i18n 配置
```tsx
// src/entry.ssr.tsx
import { renderToStream } from '@builder.io/qwik/server';
import { Root } from './root';
import { I18nProvider } from 'qwik-speak';
export default function (opts) {
return renderToStream(<Root />, {
...opts,
containerAttributes: {
lang: opts.lang
}
});
}
```
### 使用翻译
```tsx
import { component$ } from '@builder.io/qwik';
import { useSpeak } from 'qwik-speak';
export const MyComponent = component$(() => {
const { t } = useSpeak();
return (
<div>
<h1>{t('welcome.title')}</h1>
<p>{t('welcome.description')}</p>
</div>
);
});
```
## 8. 最佳实践
### 1. 合理使用 routeLoader$ 和 useResource$
- **routeLoader$**:用于页面级数据,在服务器执行
- **useResource$**:用于组件级数据,可以动态重新获取
### 2. 错误处理
```tsx
export const useData = routeLoader$(async ({ params }) => {
try {
const response = await fetch(`https://api.example.com/data/${params.id}`);
if (!response.ok) {
throw new Error('Failed to fetch data');
}
return response.json();
} catch (error) {
throw requestEvent.redirect(302, '/error');
}
});
```
### 3. 缓存策略
```tsx
export const useCachedData = routeLoader$(async ({ requestEvent }) => {
const cacheKey = 'data';
const cached = requestEvent.sharedMap.get(cacheKey);
if (cached) {
return cached;
}
const data = await fetchData();
requestEvent.sharedMap.set(cacheKey, data);
return data;
});
```
总结:Qwik City 提供了完整的全栈开发体验,通过路由、数据加载、表单处理等功能,开发者可以快速构建高性能的 Web 应用程序。
前端 · 2月21日 15:37
Qwik 和 React 有什么区别?Qwik 和 React 在架构上有几个关键区别,主要体现在加载策略、状态管理和性能优化方面:
## 1. 加载策略
**Qwik**:
- 采用"按需加载"策略,所有 JavaScript 代码默认都是延迟加载的
- 只有当用户与页面交互时,才会加载和执行相关的代码
- 不需要下载整个应用程序包,而是按需加载单个函数或组件
**React**:
- 通常需要下载整个应用程序包(或多个 chunk)
- 使用代码分割(Code Splitting)来实现懒加载,但需要手动配置
- 即使使用 SSR,仍需要下载 hydration 代码
## 2. 水合(Hydration)
**Qwik**:
- 不需要传统的水合过程
- 通过恢复性(Resumability)直接从 HTML 中恢复状态和功能
- 事件监听器通过 HTML 属性直接附加,无需 JavaScript 执行
**React**:
- 必须进行水合过程,重新执行 JavaScript 来附加事件监听器
- 水合过程需要执行大量 JavaScript,影响首屏性能
- 使用 React 18 的 Selective Hydration 可以部分优化,但仍不如 Qwik
## 3. 状态管理
**Qwik**:
- 使用 `useSignal` 和 `useStore` 进行状态管理
- 状态变化会自动触发细粒度的更新
- 状态序列化到 HTML 中,可以在客户端直接恢复
**React**:
- 使用 `useState`、`useReducer`、Context API 等进行状态管理
- 状态变化会触发组件重新渲染
- 需要额外的状态管理库(如 Redux、Zustand)来管理复杂状态
## 4. 性能优化
**Qwik**:
- 编译器自动优化代码分割和加载
- 零 JavaScript 启动成本
- 自动实现细粒度的更新,避免不必要的重新渲染
**React**:
- 需要手动优化性能(如 `useMemo`、`useCallback`)
- 使用 `React.memo` 来避免不必要的重新渲染
- 需要开发者有深入的性能优化知识
## 5. 开发体验
**Qwik**:
- 语法与 React 相似,学习曲线较平缓
- 编译器处理大部分优化工作
- 不需要关心代码分割和加载策略
**React**:
- 生态系统成熟,有丰富的第三方库
- 社区支持强大,文档完善
- 需要开发者手动处理性能优化
## 6. 适用场景
**Qwik**:
- 适合需要极致性能的应用
- 内容密集型网站
- 需要良好 SEO 的应用
- 大型企业级应用
**React**:
- 适合各种规模的应用
- 团队已经熟悉 React 生态
- 需要丰富的第三方库支持
- 快速原型开发
总结:Qwik 通过其独特的恢复性架构,在性能方面优于 React,特别是在首屏加载和交互响应方面。但 React 的生态系统和社区支持更加成熟,适合更广泛的应用场景。
前端 · 2月21日 15:37
什么是 Qwik 的恢复性(Resumability)概念?Qwik 的核心概念是"恢复性"(Resumability),这意味着应用程序可以在服务器端渲染后,在客户端无缝恢复执行状态,而不需要重新执行 JavaScript。
Qwik 通过以下方式实现恢复性:
1. **延迟加载(Lazy Loading)**:Qwik 默认将所有 JavaScript 代码分割成小块,只有当用户交互时才加载必要的代码。这与传统框架不同,传统框架通常需要下载整个应用程序包。
2. **序列化状态**:Qwik 将应用程序的状态序列化为 HTML,当页面加载时,浏览器可以直接从 HTML 中恢复状态,而不需要重新执行初始化代码。
3. **无水合(No Hydration)**:传统框架(如 React、Vue)需要进行水合过程,即重新执行 JavaScript 来附加事件监听器。Qwik 通过其独特的架构避免了这一步骤,直接从 HTML 中恢复功能。
4. **细粒度加载**:Qwik 可以加载单个函数或组件,而不是整个模块。这意味着点击一个按钮只会加载该按钮的事件处理程序,而不是整个应用程序。
5. **可恢复的执行上下文**:Qwik 维护了一个执行上下文,可以在服务器和客户端之间传递,确保代码可以在不同环境中无缝恢复。
恢复性的优势包括:
- **更快的首屏加载时间**:由于不需要下载和执行大量 JavaScript
- **更好的性能**:按需加载减少了不必要的代码执行
- **更好的 SEO**:服务器端渲染提供了完整的 HTML 内容
- **更低的带宽使用**:只加载用户实际需要的代码
Qwik 的恢复性是通过其编译器实现的,编译器会自动处理代码分割和序列化,开发者不需要手动管理这些细节。
前端 · 2月21日 15:36
Qwik 的组件系统是如何工作的?Qwik 的组件系统设计遵循几个核心原则,使其能够实现高性能和细粒度的代码分割:
## 1. 组件定义
Qwik 组件使用 `$` 前缀来标识,这告诉编译器该组件需要特殊处理:
```tsx
import { component$ } from '@builder.io/qwik';
export const MyComponent = component$(() => {
return <div>Hello Qwik</div>;
});
```
`component$` 是一个编译时宏,它将组件转换为可恢复的格式。
## 2. 组件特性
### 懒加载
所有 Qwik 组件默认都是懒加载的。编译器会自动将每个组件分割成独立的 chunk,只有当组件被渲染时才会加载。
### 可恢复性
组件的状态和执行上下文被序列化到 HTML 中,可以在客户端无缝恢复执行。
### 细粒度更新
Qwik 只更新发生变化的 DOM 节点,而不是重新渲染整个组件树。
## 3. Props 和状态
### Props
Props 通过编译时类型检查,并在序列化时自动处理:
```tsx
export const ChildComponent = component$((props: { name: string; count: number }) => {
return <div>{props.name}: {props.count}</div>;
});
```
### 状态管理
Qwik 提供了两种主要的状态管理方式:
**useSignal**:用于简单值的状态管理
```tsx
import { useSignal } from '@builder.io/qwik';
export const Counter = component$(() => {
const count = useSignal(0);
return (
<button onClick$={() => count.value++}>
Count: {count.value}
</button>
);
});
```
**useStore**:用于复杂对象的状态管理
```tsx
import { useStore } from '@builder.io/qwik';
export const Form = component$(() => {
const form = useStore({
name: '',
email: ''
});
return (
<form>
<input
value={form.name}
onInput$={(e) => form.name = (e.target as HTMLInputElement).value}
/>
</form>
);
});
```
## 4. 事件处理
Qwik 事件处理函数使用 `$` 后缀,表示它们是可恢复的:
```tsx
export const Button = component$(() => {
const handleClick$ = () => {
console.log('Button clicked');
};
return <button onClick$={handleClick$}>Click me</button>;
});
```
事件处理函数会被编译器自动分割和懒加载。
## 5. 生命周期
Qwik 的生命周期钩子也使用 `$` 后缀:
- `useTask$`:在组件挂载和更新时执行
- `useVisibleTask$`:在组件可见时执行(用于客户端特定逻辑)
- `useResource$`:用于异步数据获取
```tsx
export const DataComponent = component$(() => {
useTask$(() => {
console.log('Component mounted or updated');
});
return <div>Data Component</div>;
});
```
## 6. 组件通信
### 父子通信
通过 props 传递数据:
```tsx
export const Parent = component$(() => {
return <Child message="Hello from parent" />;
});
export const Child = component$((props: { message: string }) => {
return <div>{props.message}</div>;
});
```
### 上下文
使用 `useContext` 和 `Context` 进行跨组件通信:
```tsx
import { createContext, useContext } from '@builder.io/qwik';
const ThemeContext = createContext('light');
export const ThemeProvider = component$(() => {
return (
<ThemeContext.Provider value="dark">
<Child />
</ThemeContext.Provider>
);
});
export const Child = component$(() => {
const theme = useContext(ThemeContext);
return <div>Current theme: {theme}</div>;
});
```
## 7. 组件优化
Qwik 编译器自动处理组件优化,开发者不需要手动使用 `React.memo` 或类似的优化技术。编译器会:
- 自动分割组件代码
- 只加载必要的代码
- 避免不必要的重新渲染
- 优化事件处理函数的加载
总结:Qwik 的组件系统通过编译时优化和独特的 `$` 语法,实现了自动的代码分割和懒加载,使开发者能够编写高性能的应用程序而无需关心底层的性能优化细节。
前端 · 2月21日 15:36
Qwik 编译器是如何工作的?Qwik 的编译器是其架构的核心组件,负责将开发者编写的代码转换为高性能、可恢复的应用程序。以下是 Qwik 编译器的工作原理和关键特性:
## 1. 编译器架构
### 编译流程
Qwik 编译器的工作流程包括以下几个阶段:
1. **解析阶段**:解析 TypeScript/JavaScript 代码,构建 AST(抽象语法树)
2. **分析阶段**:分析代码结构,识别组件、事件处理函数、状态管理等
3. **转换阶段**:将代码转换为可恢复的格式,生成代码分割逻辑
4. **代码生成阶段**:生成最终的 JavaScript 代码和元数据
5. **优化阶段**:应用各种优化策略,如死代码消除、内联等
### 编译器入口
```typescript
// @builder.io/qwik/optimizer
import { transform } from '@builder.io/qwik/optimizer';
const result = transform({
code: sourceCode,
filename: 'component.tsx',
minify: true,
sourceMap: true,
entryStrategy: 'smart'
});
```
## 2. 代码分割策略
### 自动分割
Qwik 编译器自动将代码分割成最小单元:
```tsx
// 原始代码
export const App = component$(() => {
const handleClick$ = () => {
console.log('Clicked');
};
const handleSubmit$ = () => {
console.log('Submitted');
};
return (
<div>
<button onClick$={handleClick$}>Click</button>
<button onClick$={handleSubmit$}>Submit</button>
</div>
);
});
```
**编译后的结构**:
```
dist/
├── App.js # 主组件
├── handleClick.js # 点击处理函数
├── handleSubmit.js # 提交处理函数
└── q-manifest.json # 清单文件
```
### 分割策略配置
```typescript
// qwik.config.ts
export default defineConfig({
optimizer: {
entryStrategy: {
type: 'smart', // 'smart' | 'hook' | 'inline'
manualChunks: {
'vendor': ['react', 'lodash']
}
}
}
});
```
## 3. 序列化机制
### 状态序列化
编译器将组件状态序列化到 HTML 中:
```tsx
export const Counter = component$(() => {
const count = useSignal(0);
return (
<div>
<p>Count: {count.value}</p>
<button onClick$={() => count.value++}>
Increment
</button>
</div>
);
});
```
**生成的 HTML**:
```html
<div data-qwik="q-123">
<p>Count: <span data-qwik="q-456">0</span></p>
<button data-qwik="q-789" onClick$="./handleClick.js#handleClick">
Increment
</button>
<script type="qwik/json">
{
"q-456": { "value": 0 }
}
</script>
</div>
```
### 函数引用序列化
编译器将函数引用序列化为可恢复的格式:
```javascript
// 生成的函数引用
{
"q-789": {
"func": "./handleClick.js#handleClick",
"captures": []
}
}
```
## 4. 元数据生成
### Qwik Manifest
编译器生成 `q-manifest.json` 文件,包含所有元数据:
```json
{
"symbols": {
"s_123": {
"canonicalFilename": "./App.js",
"hash": "abc123",
"kind": "component",
"name": "App"
},
"s_456": {
"canonicalFilename": "./handleClick.js",
"hash": "def456",
"kind": "eventHandler",
"name": "handleClick"
}
},
"mapping": {
"q-123": "s_123",
"q-456": "s_456"
},
"bundles": {
"./App.js": {
"size": 1024,
"symbols": ["s_123"]
},
"./handleClick.js": {
"size": 512,
"symbols": ["s_456"]
}
}
}
```
## 5. 优化技术
### 死代码消除
编译器自动移除未使用的代码:
```tsx
// 原始代码
export const Component = component$(() => {
const used = useSignal(0);
const unused = useSignal(0); // 未使用
return <div>{used.value}</div>;
});
// 编译后,unused 被移除
export const Component = component$(() => {
const used = useSignal(0);
return <div>{used.value}</div>;
});
```
### 内联优化
对于小型函数,编译器可能会内联:
```tsx
// 原始代码
const smallFunction$ = () => {
return 1 + 1;
};
export const Component = component$(() => {
return <div>{smallFunction$()}</div>;
});
// 编译后可能被内联
export const Component = component$(() => {
return <div>{2}</div>;
});
```
### Tree Shaking
编译器移除未导出的代码:
```tsx
// 原始代码
export const used = () => {};
const notUsed = () => {}; // 未导出
// 编译后,notUsed 被移除
export const used = () => {};
```
## 6. 类型安全
### TypeScript 支持
编译器完全支持 TypeScript:
```tsx
export const Component = component$((props: {
name: string;
count: number;
onClick$: () => void;
}) => {
return (
<div>
<h1>{props.name}</h1>
<p>Count: {props.count}</p>
<button onClick$={props.onClick$}>Click</button>
</div>
);
});
```
编译器会:
- 验证类型正确性
- 生成类型定义文件
- 提供类型推断
## 7. 调试支持
### Source Maps
编译器生成 source maps 以支持调试:
```typescript
const result = transform({
code: sourceCode,
filename: 'component.tsx',
sourceMap: true
});
```
### 开发模式
开发模式下,编译器会生成更详细的错误信息:
```typescript
const result = transform({
code: sourceCode,
mode: 'development' // 'development' | 'production'
});
```
## 8. 插件系统
### 自定义插件
编译器支持自定义插件:
```typescript
import { createOptimizer } from '@builder.io/qwik/optimizer';
const optimizer = createOptimizer({
plugins: [
{
name: 'my-plugin',
transform: (code, id) => {
// 自定义转换逻辑
return code;
}
}
]
});
```
## 9. 性能优化
### 缓存机制
编译器使用缓存来加速构建:
```typescript
const result = transform({
code: sourceCode,
cache: true,
cacheDirectory: '.qwik-cache'
});
```
### 并行处理
编译器可以并行处理多个文件:
```typescript
const results = await Promise.all(
files.map(file => transform({ code: file.code, filename: file.name }))
);
```
总结:Qwik 编译器通过复杂的代码分割、序列化和优化技术,将开发者编写的代码转换为高性能、可恢复的应用程序。编译器是 Qwik 架构的核心,使得开发者能够专注于业务逻辑而无需关心底层的性能优化细节。
前端 · 2月21日 15:36
Qwik 的性能优化策略有哪些?Qwik 通过其独特的架构设计,在性能方面具有显著优势。以下是 Qwik 性能优化的核心策略和最佳实践:
## 1. 零 JavaScript 启动成本
### 原理
Qwik 不需要在首屏加载时下载和执行大量 JavaScript,因为:
- 所有组件默认懒加载
- 事件处理函数按需加载
- 状态直接序列化到 HTML 中
### 实现方式
```tsx
export const App = component$(() => {
const count = useSignal(0);
return (
<div>
<p>Count: {count.value}</p>
<button onClick$={() => count.value++}>
Increment
</button>
</div>
);
});
```
**性能优势**:
- 首屏加载时间接近纯 HTML
- 无需等待 JavaScript 下载和解析
- 即时可交互,无需水合过程
## 2. 细粒度代码分割
### 自动分割策略
Qwik 编译器自动将代码分割成最小单元:
```tsx
export const Dashboard = component$(() => {
return (
<div>
<Header />
<Sidebar />
<Content />
<Footer />
</div>
);
});
```
**编译后的结构**:
- `Dashboard.js` - 主组件
- `Header.js` - Header 组件(独立文件)
- `Sidebar.js` - Sidebar 组件(独立文件)
- `Content.js` - Content 组件(独立文件)
- `Footer.js` - Footer 组件(独立文件)
### 事件处理函数分割
```tsx
export const Form = component$(() => {
const handleSubmit$ = () => { /* 提交逻辑 */ };
const handleReset$ = () => { /* 重置逻辑 */ };
const handleCancel$ = () => { /* 取消逻辑 */ };
return (
<form>
<button onClick$={handleSubmit$}>Submit</button>
<button onClick$={handleReset$}>Reset</button>
<button onClick$={handleCancel$}>Cancel</button>
</form>
);
});
```
每个事件处理函数都会被分割成独立文件,只在用户点击时加载。
## 3. 细粒度更新
### 自动追踪变化
Qwik 自动追踪状态变化,只更新受影响的 DOM 节点:
```tsx
export const TodoList = component$(() => {
const todos = useStore([
{ id: 1, text: 'Task 1', completed: false },
{ id: 2, text: 'Task 2', completed: false },
{ id: 3, text: 'Task 3', completed: false }
]);
const toggleTodo$ = (id: number) => {
const todo = todos.value.find(t => t.id === id);
if (todo) todo.completed = !todo.completed;
};
return (
<ul>
{todos.value.map(todo => (
<li key={todo.id}>
<input
type="checkbox"
checked={todo.completed}
onClick$={() => toggleTodo$(todo.id)}
/>
{todo.text}
</li>
))}
</ul>
);
});
```
**性能优势**:
- 只更新被点击的 todo 项
- 其他 todo 项不会重新渲染
- 避免不必要的 DOM 操作
## 4. 智能缓存策略
### 代码缓存
Qwik 自动缓存已加载的代码块:
- 首次加载后,代码块被缓存
- 后续交互直接从缓存加载
- 减少网络请求和加载时间
### 状态缓存
状态被序列化到 HTML 中:
- 页面刷新后状态保持
- 无需重新获取数据
- 改善用户体验
## 5. 性能优化最佳实践
### 1. 合理使用 useSignal 和 useStore
```tsx
// 简单值使用 useSignal
const count = useSignal(0);
// 复杂对象使用 useStore
const user = useStore({
name: 'John',
age: 30,
address: {
city: 'New York',
country: 'USA'
}
});
```
### 2. 避免在渲染函数中创建新对象
```tsx
// 错误:每次渲染都创建新对象
export const BadComponent = component$(() => {
const handleClick$ = () => {
const options = { /* 选项 */ }; // 每次都创建新对象
// ...
};
return <button onClick$={handleClick$}>Click</button>;
});
// 正确:在组件外部创建
const options = { /* 选项 */ };
export const GoodComponent = component$(() => {
const handleClick$ = () => {
// 使用外部定义的 options
};
return <button onClick$={handleClick$}>Click</button>;
});
```
### 3. 使用 useComputed 缓存计算结果
```tsx
export const PriceCalculator = component$(() => {
const price = useSignal(100);
const tax = useSignal(0.1);
const totalPrice = useComputed$(() => {
return price.value * (1 + tax.value);
});
return <div>Total: ${totalPrice.value}</div>;
});
```
### 4. 合理使用 useResource 处理异步数据
```tsx
export const UserList = component$(() => {
const users = useResource$(({ track }) => {
track(() => /* 依赖项 */);
return fetchUsers();
});
return (
<div>
{users.value ? (
<ul>
{users.value.map(user => <li key={user.id}>{user.name}</li>)}
</ul>
) : (
<p>Loading...</p>
)}
</div>
);
});
```
### 5. 使用 useVisibleTask$ 处理客户端特定逻辑
```tsx
export const MapComponent = component$(() => {
useVisibleTask$(() => {
// 只在客户端执行
const map = new Map();
// 初始化地图
});
return <div id="map"></div>;
});
```
## 6. 性能监控和调试
### 使用 Qwik DevTools
```bash
npm install -D @builder.io/qwik
```
DevTools 提供:
- 组件树可视化
- 状态变化追踪
- 性能分析
- 代码分割视图
### 性能指标
关注以下指标:
- **First Contentful Paint (FCP)**:首次内容绘制
- **Largest Contentful Paint (LCP)**:最大内容绘制
- **Time to Interactive (TTI)**:可交互时间
- **Cumulative Layout Shift (CLS)**:累积布局偏移
## 7. 与其他框架的性能对比
| 指标 | Qwik | React | Vue |
|------|------|-------|-----|
| 首屏 JS 大小 | ~1KB | ~100KB | ~50KB |
| 水合时间 | 0ms | ~100ms | ~50ms |
| 首次交互时间 | ~50ms | ~200ms | ~150ms |
| 代码分割 | 自动细粒度 | 手动配置 | 手动配置 |
总结:Qwik 通过其独特的恢复性架构和编译时优化,实现了卓越的性能表现。开发者只需要遵循最佳实践,就能构建高性能的应用程序,而无需深入关注底层的性能优化细节。
前端 · 2月21日 15:36
Qwik 中如何使用 TypeScript?Qwik 提供了完整的 TypeScript 支持,使得开发者能够在类型安全的环境下构建应用程序。以下是 Qwik 中 TypeScript 的使用方法和最佳实践:
## 1. 类型定义
### 组件 Props 类型
```tsx
import { component$, PropsOf } from '@builder.io/qwik';
interface ButtonProps {
label: string;
onClick$: () => void;
disabled?: boolean;
variant?: 'primary' | 'secondary' | 'danger';
}
export const Button = component$<ButtonProps>((props) => {
return (
<button
onClick$={props.onClick$}
disabled={props.disabled}
class={`btn btn-${props.variant || 'primary'}`}
>
{props.label}
</button>
);
});
```
### 使用 PropsOf
```tsx
import { component$, PropsOf } from '@builder.io/qwik';
// 扩展原生 HTML 元素的类型
export const CustomInput = component$<PropsOf<'input'> & {
customProp?: string;
}>((props) => {
return <input {...props} />;
});
```
## 2. 状态管理类型
### useSignal 类型
```tsx
import { component$, useSignal } from '@builder.io/qwik';
export const Counter = component$(() => {
const count = useSignal<number>(0);
const name = useSignal<string>('');
const isActive = useSignal<boolean>(false);
return (
<div>
<p>Count: {count.value}</p>
<input value={name.value} onInput$={(e) => name.value = e.target.value} />
<button onClick$={() => isActive.value = !isActive.value}>
Toggle
</button>
</div>
);
});
```
### useStore 类型
```tsx
import { component$, useStore } from '@builder.io/qwik';
interface User {
id: number;
name: string;
email: string;
address: {
street: string;
city: string;
country: string;
};
}
export const UserProfile = component$(() => {
const user = useStore<User>({
id: 1,
name: 'John Doe',
email: 'john@example.com',
address: {
street: '123 Main St',
city: 'New York',
country: 'USA'
}
});
return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
<p>{user.address.city}, {user.address.country}</p>
</div>
);
});
```
## 3. 事件处理类型
### 事件类型
```tsx
import { component$ } from '@builder.io/qwik';
export const Form = component$(() => {
const handleSubmit$ = (event: Event, element: HTMLFormElement) => {
event.preventDefault();
const formData = new FormData(element);
console.log(formData);
};
const handleInput$ = (event: InputEvent, element: HTMLInputElement) => {
console.log(element.value);
};
const handleClick$ = (event: MouseEvent, element: HTMLButtonElement) => {
console.log('Clicked', element);
};
return (
<form onSubmit$={handleSubmit$}>
<input type="text" onInput$={handleInput$} />
<button onClick$={handleClick$}>Submit</button>
</form>
);
});
```
### 自定义事件类型
```tsx
import { component$ } from '@builder.io/qwik';
interface CustomEvent {
detail: {
id: string;
value: number;
};
}
export const CustomComponent = component$(() => {
const handleCustomEvent$ = (event: CustomEvent) => {
console.log(event.detail.id, event.detail.value);
};
return <div onCustomEvent$={handleCustomEvent$}>Custom Component</div>;
});
```
## 4. 路由类型
### 路由参数类型
```tsx
import { component$, routeLoader$ } from '@builder.io/qwik-city';
interface ProductParams {
id: string;
category?: string;
}
export const useProductData = routeLoader$<ProductParams>(async ({ params }) => {
const response = await fetch(`https://api.example.com/products/${params.id}`);
return response.json();
});
export default component$(() => {
const product = useProductData();
return <div>{product.value.name}</div>;
});
```
### 路由加载器类型
```tsx
import { routeLoader$ } from '@builder.io/qwik-city';
interface Product {
id: number;
name: string;
price: number;
description: string;
}
export const useProduct = routeLoader$<Product>(async ({ params }) => {
const response = await fetch(`https://api.example.com/products/${params.id}`);
return response.json();
});
```
## 5. Action 类型
### 表单 Action 类型
```tsx
import { action$, zod$, z } from '@builder.io/qwik-city';
interface FormData {
name: string;
email: string;
message: string;
}
interface ActionResult {
success: boolean;
error?: string;
}
export const useContactForm = action$<FormData, ActionResult>(
async (data) => {
// 处理表单数据
return { success: true };
},
zod$({
name: z.string().min(2),
email: z.string().email(),
message: z.string().min(10)
})
);
```
## 6. Context 类型
### Context Provider 类型
```tsx
import { component$, createContext, useContext } from '@builder.io/qwik';
interface ThemeContextValue {
theme: 'light' | 'dark';
toggleTheme$: () => void;
}
const ThemeContext = createContext<ThemeContextValue>('theme');
export const ThemeProvider = component$(() => {
const theme = useSignal<'light' | 'dark'>('light');
const toggleTheme$ = () => {
theme.value = theme.value === 'light' ? 'dark' : 'light';
};
return (
<ThemeContext.Provider value={{ theme: theme.value, toggleTheme$ }}>
<Child />
</ThemeContext.Provider>
);
});
export const Child = component$(() => {
const { theme, toggleTheme$ } = useContext(ThemeContext);
return (
<div>
<p>Current theme: {theme}</p>
<button onClick$={toggleTheme$}>Toggle Theme</button>
</div>
);
});
```
## 7. 泛型组件
### 泛型组件定义
```tsx
import { component$ } from '@builder.io/qwik';
interface ListProps<T> {
items: T[];
renderItem$: (item: T, index: number) => JSXNode;
keyExtractor$: (item: T) => string;
}
export const List = component$<ListProps<any>>((props) => {
return (
<ul>
{props.items.map((item, index) => (
<li key={props.keyExtractor$(item)}>
{props.renderItem$(item, index)}
</li>
))}
</ul>
);
});
// 使用示例
export const UserList = component$(() => {
const users = [
{ id: '1', name: 'John' },
{ id: '2', name: 'Jane' }
];
return (
<List
items={users}
renderItem$={(user) => <span>{user.name}</span>}
keyExtractor$={(user) => user.id}
/>
);
});
```
## 8. 类型断言和类型守卫
### 类型断言
```tsx
import { component$ } from '@builder.io/qwik';
export const Form = component$(() => {
const handleSubmit$ = (event: Event, element: HTMLFormElement) => {
event.preventDefault();
// 类型断言
const input = element.querySelector('input') as HTMLInputElement;
console.log(input.value);
};
return (
<form onSubmit$={handleSubmit$}>
<input type="text" />
<button type="submit">Submit</button>
</form>
);
});
```
### 类型守卫
```tsx
import { component$ } from '@builder.io/qwik';
function isString(value: unknown): value is string {
return typeof value === 'string';
}
export const Component = component$(() => {
const data = useSignal<unknown>(null);
const processData$ = () => {
if (isString(data.value)) {
console.log(data.value.toUpperCase());
}
};
return <button onClick$={processData$}>Process</button>;
});
```
## 9. 类型导出和导入
### 类型导出
```tsx
// types.ts
export interface User {
id: number;
name: string;
email: string;
}
export type UserRole = 'admin' | 'user' | 'guest';
export interface ApiResponse<T> {
data: T;
status: number;
message: string;
}
```
### 类型导入
```tsx
// component.tsx
import { component$ } from '@builder.io/qwik';
import type { User, UserRole, ApiResponse } from './types';
export const UserComponent = component$(() => {
const user = useSignal<User | null>(null);
const role = useSignal<UserRole>('user');
return <div>{user.value?.name}</div>;
});
```
## 10. 最佳实践
### 1. 使用 interface 定义复杂类型
```tsx
interface ComplexProps {
user: {
id: number;
profile: {
name: string;
avatar: string;
};
settings: {
theme: string;
notifications: boolean;
};
};
}
```
### 2. 使用 type 定义联合类型和交叉类型
```tsx
type ButtonVariant = 'primary' | 'secondary' | 'danger';
type ButtonProps = BaseProps & { variant: ButtonVariant };
```
### 3. 使用泛型提高代码复用性
```tsx
export const useApi = <T>(url: string) => {
return useResource$<T>(() => fetch(url).then(r => r.json()));
};
```
### 4. 使用类型守卫确保类型安全
```tsx
function isValidUser(user: unknown): user is User {
return typeof user === 'object' && user !== null && 'id' in user;
}
```
总结:Qwik 完全支持 TypeScript,通过类型定义、泛型、类型守卫等特性,开发者可以在类型安全的环境下构建高性能的应用程序。合理使用 TypeScript 可以提高代码质量和开发效率。
前端 · 2月21日 15:36
Qwik 中的状态管理是如何工作的?Qwik 提供了多种状态管理方式,每种方式都有其特定的使用场景和优势:
## 1. useSignal
`useSignal` 是 Qwik 中最简单的状态管理方式,适用于管理原始值(如数字、字符串、布尔值)。
### 特点
- 轻量级,性能最优
- 只能存储单个值
- 通过 `.value` 访问和修改值
- 自动触发细粒度更新
### 使用示例
```tsx
import { component$, useSignal } from '@builder.io/qwik';
export const Counter = component$(() => {
const count = useSignal(0);
return (
<div>
<p>Count: {count.value}</p>
<button onClick$={() => count.value++}>Increment</button>
<button onClick$={() => count.value--}>Decrement</button>
</div>
);
});
```
## 2. useStore
`useStore` 用于管理复杂对象状态,可以存储嵌套对象和数组。
### 特点
- 可以存储复杂对象和数组
- 支持深层嵌套
- 自动追踪对象属性的变化
- 细粒度更新,只更新变化的属性
### 使用示例
```tsx
import { component$, useStore } from '@builder.io/qwik';
export const TodoList = component$(() => {
const todos = useStore({
items: [
{ id: 1, text: 'Learn Qwik', completed: false },
{ id: 2, text: 'Build app', completed: false }
],
filter: 'all'
});
const addTodo$ = () => {
todos.items.push({
id: todos.items.length + 1,
text: 'New todo',
completed: false
});
};
return (
<div>
<ul>
{todos.items.map(item => (
<li key={item.id}>
{item.text}
</li>
))}
</ul>
<button onClick$={addTodo$}>Add Todo</button>
</div>
);
});
```
## 3. useComputed
`useComputed` 用于创建派生状态,基于其他状态计算得出。
### 特点
- 自动缓存计算结果
- 只在依赖项变化时重新计算
- 适合处理复杂计算逻辑
### 使用示例
```tsx
import { component$, useSignal, useComputed } from '@builder.io/qwik';
export const PriceCalculator = component$(() => {
const price = useSignal(100);
const tax = useSignal(0.1);
const totalPrice = useComputed$(() => {
return price.value * (1 + tax.value);
});
return (
<div>
<p>Price: ${price.value}</p>
<p>Tax: {tax.value * 100}%</p>
<p>Total: ${totalPrice.value.toFixed(2)}</p>
</div>
);
});
```
## 4. useContext
`useContext` 用于跨组件共享状态,类似于 React 的 Context API。
### 特点
- 避免通过多层组件传递 props
- 适合全局状态管理
- 可以在组件树的任何位置访问
### 使用示例
```tsx
import { component$, createContext, useContext } from '@builder.io/qwik';
const UserContext = createContext<{ name: string; email: string }>({
name: '',
email: ''
});
export const UserProvider = component$(() => {
const user = {
name: 'John Doe',
email: 'john@example.com'
};
return (
<UserContext.Provider value={user}>
<UserProfile />
</UserContext.Provider>
);
});
export const UserProfile = component$(() => {
const user = useContext(UserContext);
return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
);
});
```
## 5. useResource
`useResource` 用于管理异步数据和加载状态。
### 特点
- 处理异步数据获取
- 自动管理加载状态
- 支持错误处理
- 可以重新获取数据
### 使用示例
```tsx
import { component$, useResource, useSignal } from '@builder.io/qwik';
import { routeLoader$ } from '@builder.io/qwik-city';
export const useUserData = routeLoader$(async () => {
const response = await fetch('https://api.example.com/users');
return response.json();
});
export const UserList = component$(() => {
const users = useResource$(({ track }) => {
track(() => /* 依赖项 */);
return fetchUsers();
});
return (
<div>
{users.value ? (
<ul>
{users.value.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
) : (
<p>Loading...</p>
)}
</div>
);
});
```
## 6. 状态管理最佳实践
### 选择合适的状态管理方式
- **简单值**:使用 `useSignal`
- **复杂对象**:使用 `useStore`
- **派生状态**:使用 `useComputed`
- **跨组件共享**:使用 `useContext`
- **异步数据**:使用 `useResource`
### 避免不必要的重新渲染
- Qwik 自动处理细粒度更新,不需要手动优化
- 避免在渲染函数中创建新对象
- 使用 `useComputed` 缓存计算结果
### 状态序列化
- Qwik 会自动序列化状态到 HTML
- 确保状态对象是可序列化的
- 避免存储函数或不可序列化的对象
总结:Qwik 的状态管理系统设计简洁而强大,通过不同的 hook 提供了灵活的状态管理方式。编译器自动处理状态序列化和细粒度更新,使开发者能够专注于业务逻辑而无需担心性能问题。
前端 · 2月21日 15:36
Qwik 如何处理服务器端渲染(SSR)和客户端渲染(CSR)?Qwik 对服务器端渲染(SSR)和客户端渲染(CSR)有独特的处理方式,其核心是通过"恢复性"概念来实现无缝的 SSR/CSR 切换。
## 1. Qwik 的 SSR 架构
### 服务器端渲染流程
1. **服务器执行组件**:在服务器上执行 Qwik 组件,生成 HTML
2. **状态序列化**:将组件状态和执行上下文序列化到 HTML 中
3. **注入元数据**:在 HTML 中注入恢复所需的元数据(事件处理器引用、状态信息等)
4. **发送 HTML**:将完整的 HTML 发送到客户端
### SSR 的优势
- **首屏加载快**:服务器直接返回完整的 HTML,无需等待 JavaScript 执行
- **SEO 友好**:搜索引擎可以直接抓取页面内容
- **减少客户端负担**:大部分工作在服务器完成
## 2. Qwik 的 CSR 架构
### 客户端恢复流程
1. **HTML 解析**:浏览器解析服务器返回的 HTML
2. **状态恢复**:从 HTML 中反序列化状态和执行上下文
3. **按需加载**:只加载用户交互所需的 JavaScript 代码
4. **事件绑定**:通过 HTML 属性直接绑定事件,无需水合
### CSR 的特点
- **零水合**:不需要传统的水合过程
- **按需执行**:只在用户交互时执行相关代码
- **细粒度更新**:只更新变化的 DOM 节点
## 3. SSR 和 CSR 的无缝集成
### 恢复性(Resumability)
Qwik 的核心创新是恢复性,它使得应用程序可以在服务器和客户端之间无缝切换:
```tsx
// 服务器端执行
export const App = component$(() => {
const count = useSignal(0);
return (
<div>
<p>Count: {count.value}</p>
<button onClick$={() => count.value++}>
Increment
</button>
</div>
);
});
```
- 服务器渲染时,`count` 的值被序列化到 HTML
- 客户端加载时,直接从 HTML 中恢复 `count` 的值
- 点击按钮时,只加载和执行 `onClick$` 处理函数
### 代码分割策略
Qwik 编译器自动将代码分割成多个小块:
- **组件代码**:每个组件独立分割
- **事件处理器**:每个事件处理函数独立分割
- **状态更新逻辑**:细粒度的状态更新代码
## 4. Qwik City 的路由和 SSR
Qwik City 是 Qwik 的全栈框架,提供了强大的路由和 SSR 功能:
### 路由加载器
```tsx
// routes/index.tsx
import { component$ } from '@builder.io/qwik';
import { routeLoader$ } from '@builder.io/qwik-city';
export const useProductData = routeLoader$(async ({ params, url, env }) => {
const response = await fetch(`https://api.example.com/products/${params.id}`);
return response.json();
});
export default component$(() => {
const product = useProductData();
return (
<div>
<h1>{product.value.name}</h1>
<p>{product.value.description}</p>
</div>
);
});
```
### 服务端操作
```tsx
import { action$ } from '@builder.io/qwik-city';
export const useAddToCart = action$(async (data, { requestEvent }) => {
const session = requestEvent.sharedMap.get('session');
// 执行服务端逻辑
return { success: true };
});
```
## 5. 性能优化
### 首屏性能
- **零 JavaScript 启动成本**:不需要下载和执行大量 JavaScript
- **即时交互**:HTML 包含所有必要的信息,可以立即交互
- **渐进式增强**:随着用户交互,逐步加载更多功能
### 运行时性能
- **细粒度更新**:只更新变化的 DOM 节点
- **按需加载**:只加载用户实际需要的代码
- **智能缓存**:自动缓存已加载的代码
## 6. 与传统框架的对比
| 特性 | Qwik | React | Vue |
|------|------|-------|-----|
| SSR 支持 | 原生支持,零水合 | 需要 Next.js 等框架 | 需要 Nuxt.js 等框架 |
| 水合过程 | 不需要 | 需要 | 需要 |
| 首屏 JS | 接近零 | 较大 | 中等 |
| 代码分割 | 自动细粒度 | 手动配置 | 手动配置 |
| 状态恢复 | 自动 | 需要手动处理 | 需要手动处理 |
## 7. 最佳实践
### SSR 优化
- 使用 `routeLoader$` 在服务器获取数据
- 避免在服务器执行大量计算
- 合理设置缓存策略
### CSR 优化
- 使用 `useVisibleTask$` 处理客户端特定逻辑
- 避免在首屏加载不必要的代码
- 使用 `useResource$` 处理异步数据
### 混合策略
- 静态内容使用 SSR
- 动态内容使用 CSR
- 根据用户设备能力调整策略
总结:Qwik 通过恢复性概念实现了 SSR 和 CSR 的无缝集成,提供了卓越的性能和开发体验。开发者可以专注于业务逻辑,而无需关心底层的渲染优化细节。
前端 · 2月21日 15:36
Qwik 中的 `$` 符号有什么作用?Qwik 的 `$` 符号是其架构的核心,它不仅仅是一个命名约定,而是编译器处理代码的重要标识。理解 `$` 符号的作用对于掌握 Qwik 至关重要。
## 1. `$` 符号的核心作用
`$` 符号告诉 Qwik 编译器这是一个需要特殊处理的函数或组件,编译器会对其进行代码分割、序列化和懒加载处理。
## 2. `$` 符号的使用场景
### component$ - 组件定义
```tsx
import { component$ } from '@builder.io/qwik';
export const MyComponent = component$(() => {
return <div>Hello Qwik</div>;
});
```
**作用**:
- 标识这是一个 Qwik 组件
- 编译器会自动将组件代码分割成独立的 chunk
- 组件默认是懒加载的
### useSignal / useStore - 状态管理
```tsx
import { useSignal, useStore } from '@builder.io/qwik';
export const Counter = component$(() => {
const count = useSignal(0); // 不需要 $
const user = useStore({ // 不需要 $
name: 'John',
age: 30
});
return <div>{count.value}</div>;
});
```
**注意**:`useSignal` 和 `useStore` 本身不需要 `$`,但它们创建的状态对象会被编译器特殊处理。
### onClick$ / onInput$ - 事件处理
```tsx
export const Button = component$(() => {
const handleClick$ = () => {
console.log('Clicked!');
};
return <button onClick$={handleClick$}>Click me</button>;
});
```
**作用**:
- 标识这是一个可恢复的事件处理函数
- 编译器会将事件处理函数独立分割
- 只在用户触发事件时才加载和执行
### useTask$ / useVisibleTask$ - 生命周期
```tsx
export const DataComponent = component$(() => {
useTask$(() => {
console.log('Component mounted or updated');
});
useVisibleTask$(() => {
console.log('Component is visible');
});
return <div>Data Component</div>;
});
```
**作用**:
- 标识这是一个生命周期钩子
- `useTask$` 在服务器和客户端都会执行
- `useVisibleTask$` 只在客户端执行
### useResource$ - 异步数据
```tsx
export const UserList = component$(() => {
const users = useResource$(({ track }) => {
track(() => /* 依赖项 */);
return fetch('https://api.example.com/users');
});
return (
<div>
{users.value?.map(user => <div key={user.id}>{user.name}</div>)}
</div>
);
});
```
**作用**:
- 标识这是一个异步数据获取函数
- 编译器会处理加载状态和错误状态
- 支持依赖追踪和重新获取
### action$ - 服务端操作
```tsx
import { action$ } from '@builder.io/qwik-city';
export const useSubmitForm = action$(async (data, { requestEvent }) => {
// 服务端逻辑
return { success: true };
});
```
**作用**:
- 标识这是一个服务端操作
- 编译器会自动处理表单提交和响应
- 支持类型安全的数据验证
## 3. `$` 符号的编译器处理
### 代码分割
编译器会自动将带有 `$` 的函数分割成独立的文件:
```tsx
// 原始代码
export const App = component$(() => {
const handleClick$ = () => {
console.log('Clicked');
};
return <button onClick$={handleClick$}>Click</button>;
});
// 编译后的结构
// App.js - 组件代码
// handleClick.js - 事件处理函数(独立文件)
```
### 序列化
编译器会将函数引用序列化到 HTML 中:
```html
<!-- 编译后的 HTML -->
<button
data-qwik="..."
onClick$="./handleClick.js#handleClick"
>
Click
</button>
```
### 懒加载
编译器会生成懒加载逻辑,只在需要时加载代码:
```javascript
// 自动生成的懒加载代码
function loadHandler() {
return import('./handleClick.js').then(m => m.handleClick);
}
```
## 4. `$` 符号的命名约定
### 组件名称
```tsx
// 推荐
export const MyComponent = component$(() => {});
// 不推荐(但有效)
export const myComponent = component$(() => {});
```
### 事件处理函数
```tsx
// 推荐
const handleClick$ = () => {};
const handleSubmit$ = () => {};
// 不推荐(但有效)
const handle_click$ = () => {};
const clickHandler$ = () => {};
```
### 生命周期函数
```tsx
// 推荐
useTask$(() => {});
useVisibleTask$(() => {});
// 这些是内置函数,不需要自定义命名
```
## 5. 常见错误和注意事项
### 忘记使用 `$`
```tsx
// 错误:事件处理函数没有使用 $
export const Button = component$(() => {
const handleClick = () => { // 缺少 $
console.log('Clicked');
};
return <button onClick={handleClick}>Click</button>; // 错误
});
// 正确
export const Button = component$(() => {
const handleClick$ = () => { // 使用 $
console.log('Clicked');
};
return <button onClick$={handleClick$}>Click</button>; // 正确
});
```
### 混淆 `$` 的使用位置
```tsx
// 错误:在 JSX 属性中错误使用 $
export const Button = component$(() => {
return <button onClick$={() => console.log('Clicked')}>Click</button>;
// 内联箭头函数不应该使用 $
});
// 正确
export const Button = component$(() => {
const handleClick$ = () => {
console.log('Clicked');
};
return <button onClick$={handleClick$}>Click</button>;
});
```
## 6. `$` 符号的底层原理
### 编译时转换
Qwik 编译器在编译时会:
1. 识别所有带有 `$` 的函数
2. 将这些函数提取到独立文件
3. 生成序列化元数据
4. 创建懒加载逻辑
5. 更新函数引用
### 运行时恢复
在运行时,Qwik 会:
1. 从 HTML 中读取序列化元数据
2. 按需加载对应的 JavaScript 文件
3. 恢复函数的执行上下文
4. 执行函数逻辑
总结:`$` 符号是 Qwik 架构的核心,它通过编译时优化实现了自动的代码分割、序列化和懒加载。理解 `$` 符号的作用对于编写高性能的 Qwik 应用至关重要。
服务端 · 2月21日 15:18