React Vs Qwik
比较 React 和 Qwik 的几个例子。
Hello world 组件
⚛️ React
export const HelloWorld = () => {
return <div>Hello world</div>;
}
⚡️ Qwik
export const HelloWorld = component$(() => {
return <div>Hello world</div>;
});
component$ 和 $ 是什么?
component$ 用于声明一个 Qwik 组件。
美元符号 $ 用于在 Qwik 将应用程序拆分为许多我们称之为 symbols 的小块时,向优化器和开发人员发出信号。
带有点击处理程序的按钮
⚛️ React
export const Button = () => {
return <button onClick={() => console.log('click')}>Click me</button>;
}
⚡️ Qwik
export const Button = component$(() => {
return <button onClick$={() => console.log('click')}>Click me</button>;
});
还记得 $ 吗?
在 qwik 中,事件处理程序的行为与其他框架中的行为相同,只需记住它们的内容将以延迟方式加载,这要归功于美元后缀。
注意 ⚠️:JSX 处理程序(如 onClick$ 和 onInput$)仅在客户端上执行。这是因为它们是 DOM 事件,由于服务器上没有 DOM,它们不会在服务器上执行。
使用 Props
⚛️ React
export const Parent = (() => {
const [ count, setCount ] = useState<number>(0);
const increment = (() => {
setCount((prev) => prev + 1)
})
return <Child count={count} increment={increment} />;
})
interface ChildProps {
count: number;
increment: () => void;
}
export const Child = ((props: ChildProps) => {
return (
<>
<button onClick={props.increment}>Increment</button>
<p>Count: {props.count}</p>
</>
);
})
⚡️ Qwik
export const Parent = component$(() => {
const userData = useStore({ count: 0 });
return <Child userData={userData} />;
});
interface ChildProps {
userData: { count: number };
}
export const Child = component$<ChildProps>(({ userData }) => {
return (
<>
<button onClick$={() => userData.count++}>Increment</button>
<p>Count: {userData.count}</p>
</>
);
});
等等,我不需要将处理程序作为 prop 传递吗?
useSignal() 返回的响应式信号由一个具有单个属性 .value 的对象组成。如果更改信号的 value 属性,任何依赖于它的组件都将自动更新。
声明本地状态
⚛️ React
export function UseStateExample() {
const [value, setValue] = useState(0);
return <div>Value is: {value}</div>;
}
⚡️ Qwik
export const LocalStateExample = component$(() => {
const count = useSignal(0);
return <div>Value is: {count.value}</div>;
});
如果我有一个更复杂的状态怎么办?
useStore() 的工作方式与 useSignal() 非常相似,但它以对象作为初始值,并且默认情况下,反应性扩展到嵌套对象和数组。可以将 store 视为多值信号或由多个信号组成的对象。
创建一个计数器组件
⚛️ React
export function Counter() {
const [count, setCount] = useState(0);
return (
<>
<p>Value is: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</>
);
}
⚡️ Qwik
export const Counter = component$(() => {
const count = useSignal(0);
return (
<>
<p>Value is: {count.value}</p>
<button onClick$={() => count.value++}>Increment</button>
</>
);
});
创建一个每秒递增的时钟
⚛️ React
export function Clock() {
const [seconds, setSeconds] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setSeconds(seconds + 1);
}, 1000);
return () => clearInterval(interval);
});
return <p>Seconds: {seconds}</p>;
}
⚡️ Qwik
export const Clock = component$(() => {
const seconds = useSignal(0);
useVisibleTask$(({ cleanup }) => {
const interval = setInterval(() => {
seconds.value++;
}, 1000);
cleanup(() => clearInterval(interval));
});
return <p>Seconds: {seconds.value}</p>;
});
每次状态更改时执行一次 fetch 请求
⚛️ React
export function Fetch() {
const [url, setUrl] = useState('https://api.github.com/repos/BuilderIO/qwik');
const [responseJson, setResponseJson] = useState(undefined);
useEffect(() => {
fetch(url)
.then((res) => res.json())
.then((json) => setResponseJson(json));
}, [url]);
return (
<>
<p>{responseJson?.name} has {responseJson?.stargazers_count} ✨'s</p>
<input name="url" value={url} onChange={(e) => setUrl(e.target.value)} />
</>
);
}
⚡️ Qwik
export const Fetch = component$(() => {
const url = useSignal('https://api.github.com/repos/BuilderIO/qwik');
const responseJson = useSignal(undefined);
useTask$(async ({ track }) => {
track(() => url.value);
const res = await fetch(url.value);
const json = await res.json();
responseJson.value = json;
});
return (
<>
<p>{responseJson.value?.name} has {responseJson.value?.stargazers_count} ✨'s</p>
<input name="url" bind:value={url} />
</>
);
});
useTask$ 和 bind 是什么?
useTask$() 注册一个钩子,在组件创建时执行,它将在服务器或浏览器中至少运行一次,具体取决于组件最初在哪里呈现。
bind 属性 是一个方便的 API,用于将 input
的值与 Signal 进行双向数据绑定。
声明一些上下文并使用它
⚛️ React
export const MyContext = createContext({ message: 'some example value' });
export default function Parent() {
return (
<MyContext.Provider value={{ message: 'updated example value' }}>
<Child />
</MyContext.Provider>
);
}
export const Child = () => {
const value = useContext(MyContext);
return <p>{value.message}</p>;
};
⚡️ Qwik
export const MyContext = createContextId('my-context');
export const Parent = component$(() => {
const message = useSignal('some example value');
useContextProvider(MyContext, message);
return (
<>
<Child />
</>
);
});
export const Child = component$(() => {
const message = useContext(MyContext);
return <p>{message.value}</p>;
});
创建一个防抖输入框
⚛️ React
export const DebouncedInput = () => {
const [value, setValue] = useState('');
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const debounced = setTimeout(() => setDebouncedValue(value), 1000);
return () => clearTimeout(debounced);
}, [value]);
return (
<>
<input
value={value}
onChange={(ev) => setValue((ev.target as HTMLInputElement).value)}
/>
<p>{debouncedValue}</p>
</>
);
};
⚡️ Qwik
export const DebouncedInput = component$(() => {
const inputText = useSignal('');
const debouncedValue = useSignal('');
useTask$(({ track, cleanup }) => {
track(() => inputText.value);
const debounced = setTimeout(() => {
debouncedValue.value = inputText.value;
}, 1000);
cleanup(() => clearTimeout(debounced));
});
return (
<>
<input bind:value={inputText} />
<p>{debouncedValue.value}</p>
</>
);
});
每次按钮点击时随机更改背景颜色
⚛️ React
export function DynamicBackground() {
const [red, setRed] = useState(0);
const [green, setGreen] = useState(0);
const [blue, setBlue] = useState(0);
return (
<div
style={{
background: `rgb(${red}, ${green}, ${blue})`,
}}
>
<button
onClick={() => {
setRed(Math.random() * 256);
setGreen(Math.random() * 256);
setBlue(Math.random() * 256);
}}
>
Change background
</button>
</div>
);
}
⚡️ Qwik
export const DynamicBackground = component$(() => {
const red = useSignal(0);
const green = useSignal(0);
const blue = useSignal(0);
return (
<div
style={{
background: `rgb(${red.value}, ${green.value}, ${blue.value})`,
}}
>
<button
onClick$={() => {
red.value = Math.random() * 256;
green.value = Math.random() * 256;
blue.value = Math.random() * 256;
}}
>
Change background
</button>
</div>
);
});
创建一个渲染总统列表的组件
⚛️ React
export function Presidents() {
const presidents = [
{ name: 'George Washington', years: '1789-1797' },
{ name: 'John Adams', years: '1797-1801' },
{ name: 'Thomas Jefferson', years: '1801-1809' },
{ name: 'James Madison', years: '1809-1817' },
{ name: 'James Monroe', years: '1817-1825' },
{ name: 'John Quincy Adams', years: '1825-1829' },
{ name: 'Andrew Jackson', years: '1829-1837' },
{ name: 'Martin Van Buren', years: '1837-1841' },
{ name: 'William Henry Harrison', years: '1841-1841' },
{ name: 'John Tyler', years: '1841-1845' },
{ name: 'James K. Polk', years: '1845-1849' },
{ name: 'Zachary Taylor', years: '1849-1850' },
{ name: 'Millard Fillmore', years: '1850-1853' },
{ name: 'Franklin Pierce', years: '1853-1857' },
{ name: 'James Buchanan', years: '1857-1861' },
{ name: 'Abraham Lincoln', years: '1861-1865' },
{ name: 'Andrew Johnson', years: '1865-1869' },
{ name: 'Ulysses S. Grant', years: '1869-1877' },
{ name: 'Rutherford B. Hayes', years: '1877-1881' },
{ name: 'James A. Garfield', years: '1881-1881' },
{ name: 'Chester A. Arthur', years: '1881-1885' },
{ name: 'Grover Cleveland', years: '1885-1889' },
];
return (
<ul>
{presidents.map((president) => (
<li key={president.name + president.years}>
{president.name} ({president.years})
</li>
))}
</ul>
);
}
⚡️ Qwik
export const Presidents = component$(() => {
const presidents = [
{ name: 'George Washington', years: '1789-1797' },
{ name: 'John Adams', years: '1797-1801' },
{ name: 'Thomas Jefferson', years: '1801-1809' },
{ name: 'James Madison', years: '1809-1817' },
{ name: 'James Monroe', years: '1817-1825' },
{ name: 'John Quincy Adams', years: '1825-1829' },
{ name: 'Andrew Jackson', years: '1829-1837' },
{ name: 'Martin Van Buren', years: '1837-1841' },
{ name: 'William Henry Harrison', years: '1841-1841' },
{ name: 'John Tyler', years: '1841-1845' },
{ name: 'James K. Polk', years: '1845-1849' },
{ name: 'Zachary Taylor', years: '1849-1850' },
{ name: 'Millard Fillmore', years: '1850-1853' },
{ name: 'Franklin Pierce', years: '1853-1857' },
{ name: 'James Buchanan', years: '1857-1861' },
{ name: 'Abraham Lincoln', years: '1861-1865' },
{ name: 'Andrew Johnson', years: '1865-1869' },
{ name: 'Ulysses S. Grant', years: '1869-1877' },
{ name: 'Rutherford B. Hayes', years: '1877-1881' },
{ name: 'James A. Garfield', years: '1881-1881' },
{ name: 'Chester A. Arthur', years: '1881-1885' },
{ name: 'Grover Cleveland', years: '1885-1889' },
];
return (
<ul>
{presidents.map((president) => (
<li key={president.name + president.years}>
{president.name} ({president.years})
</li>
))}
</ul>
);
});