Qwik 的 $ 符号是其架构的核心,它不仅仅是一个命名约定,而是编译器处理代码的重要标识。理解 $ 符号的作用对于掌握 Qwik 至关重要。
1. $ 符号的核心作用
$ 符号告诉 Qwik 编译器这是一个需要特殊处理的函数或组件,编译器会对其进行代码分割、序列化和懒加载处理。
2. $ 符号的使用场景
component$ - 组件定义
tsximport { component$ } from '@builder.io/qwik'; export const MyComponent = component$(() => { return <div>Hello Qwik</div>; });
作用:
- 标识这是一个 Qwik 组件
- 编译器会自动将组件代码分割成独立的 chunk
- 组件默认是懒加载的
useSignal / useStore - 状态管理
tsximport { 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$ - 事件处理
tsxexport const Button = component$(() => { const handleClick$ = () => { console.log('Clicked!'); }; return <button onClick$={handleClick$}>Click me</button>; });
作用:
- 标识这是一个可恢复的事件处理函数
- 编译器会将事件处理函数独立分割
- 只在用户触发事件时才加载和执行
useTask$ / useVisibleTask$ - 生命周期
tsxexport const DataComponent = component$(() => { useTask$(() => { console.log('Component mounted or updated'); }); useVisibleTask$(() => { console.log('Component is visible'); }); return <div>Data Component</div>; });
作用:
- 标识这是一个生命周期钩子
useTask$在服务器和客户端都会执行useVisibleTask$只在客户端执行
useResource$ - 异步数据
tsxexport 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$ - 服务端操作
tsximport { 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 编译器在编译时会:
- 识别所有带有
$的函数 - 将这些函数提取到独立文件
- 生成序列化元数据
- 创建懒加载逻辑
- 更新函数引用
运行时恢复
在运行时,Qwik 会:
- 从 HTML 中读取序列化元数据
- 按需加载对应的 JavaScript 文件
- 恢复函数的执行上下文
- 执行函数逻辑
总结:$ 符号是 Qwik 架构的核心,它通过编译时优化实现了自动的代码分割、序列化和懒加载。理解 $ 符号的作用对于编写高性能的 Qwik 应用至关重要。