前言
前面我们介绍了一下 Qwik 这个新的框架,如果没有看过的童鞋可以移步这里(追求极致性能!Qwik 1.0版本发布)。
本文结合官方的教学文档,翻译整理,原文链接在文末。
安装Qwik的前提条件
- 本地安装
Node.js v16.8
或更高版本 - 有一个开发工具,例如:
Visual Studio Code
第一步 使用CLI安装应用
首先,使用 Qwik CLI 创建一个 Qwik 应用程序,它会生成一个空白的启动程序。Qwik 支持 NPM、yarn 和 pnpm。选择你喜欢的软件包管理器,然后运行以下命令(选其一):
npm create qwik@latestpnpm create qwik@latestyarn create qwikbun create qwik@latest
安装过程中会提示一些安装依赖项等相关问题,大家根据自己需求选择。将黑窗口切换到下载的目录下,运行下面的命令(选其一),即可启动本地服务:
npm startpnpm startyarn startbun start
第二步 创建一个笑话应用程序
Qwik 教程将指导您使用 Qwik 创建一个笑话应用程序,同时涵盖最重要的 Qwik 概念。该应用程序从 https://icanhazdadjoke.com 中随机显示一个笑话,并设有一个按钮,点击后即可获得一个新笑话。
1 创建一个路由
首先在特定路由上提供一个页面。这个基本应用程序在 /joke/
路由上随机提供一个爸爸笑话应用程序。本教程依赖 Qwik
的元框架 Qwikcity
,它使用基于目录的路由。开始使用
在你的项目中,在 /src/routes/
中创建一个 /joke/
目录,其中包含一个 index.tsx
文件。每个路由的 index.tsx
文件都必须有export default component$(...)
,以便 Qwikcity
知道要提供哪些内容。将以下内容粘贴到 src/routes/joke/index.tsx
:
import { component$ } from '@builder.io/qwik'; export default component$(() => {return <section class="section bright">A Joke!</section>;});
浏览器打开 http://127.0.0.1:5173/joke/
这个链接,查看是否可以正常工作。
注意:
您的笑话路由默认组件已被现有布局包围。请参阅 “布局”,了解有关什么是布局以及如何使用布局的更多详情。有关如何编写组件的更多详情,请参阅组件 API 部分。
2 加载数据
我们将使用 https://icanhazdadjoke.com
上的外部 JSON
API 来加载随机笑话。我们将使用路由加载器在服务器中加载这些数据,然后在组件中渲染。
打开 src/routes/joke/index.tsx
,添加以下代码:
import { component$ } from '@builder.io/qwik';import { routeLoader$ } from '@builder.io/qwik-city'; export const useDadJoke = routeLoader$(async () => {const response = await fetch('https://icanhazdadjoke.com/', {headers: { Accept: 'application/json' },});return (await response.json()) as {id: string;status: number;joke: string;};}); export default component$(() => {// Calling our `useDadJoke` hook, will return a reactive signal to the loaded data.const dadJokeSignal = useDadJoke();return (<section class="section bright"><p>{dadJokeSignal.value.joke}</p></section>);});
现在查看浏览器,会随机显示一个笑话。
代码解释:
传递给routeLoader$
的函数会在任何组件渲染之前在服务器上紧急调用,并负责加载数据。routeLoader$
返回一个使用挂钩useDadJoke()
,可在组件中用于检索服务器数据。
注意:
- 在渲染任何组件之前,服务器都会急切地调用
routeLoader$
,即使在任何组件中都未调用其使用钩子。RouteLoader$
的返回类型会在组件中推断,无需任何额外的类型信息。
3 向服务器发送数据
之前,我们使用 routeLoader$
将数据从服务器发送到客户端。要将数据从客户端发回服务器,我们使用 routeAction$
。
注意:routeAction$
是向服务器发送数据的首选方式,因为它使用的是浏览器本地表单 API,即使禁用 JavaScript 也能正常工作。
要声明一个动作,请添加以下代码:
import { routeLoader$, Form, routeAction$ } from '@builder.io/qwik-city'; export const useJokeVoteAction = routeAction$((props) => {// Leave it as an exercise for the reader to implement this.console.log('VOTE', props);});
更新 export default
组件,以便在 中使用
useJokeVoteAction
挂钩。
export default component$(() => {const dadJokeSignal = useDadJoke();const favoriteJokeAction = useJokeVoteAction();return (<section class="section bright"><p>{dadJokeSignal.value.joke}</p><Form action={favoriteJokeAction}><input type="hidden" name="jokeID" value={dadJokeSignal.value.id} /><button name="vote" value="up"></button><button name="vote" value="down"></button></Form></section>);});
现在,在 http://localhost:5173/joke/
上显示按钮,如果点击按钮,其值就会记录到控制台。
代码解释:
routeAction$
接收数据。- 每当张贴表单时,服务器就会调用传递给
routeAction$
的函数。 routeAction$
返回一个使用挂钩,即useJokeVoteAction
,你可以在组件中使用它来发布表单数据。Form
是一个方便的组件,它封装了浏览器的本地元素。
需要注意的事项:
- 有关验证,请参阅 zod validation
- 即使禁用了
JavaScript
,routeAction$
也能正常工作。 - 如果启用了
JavaScript
,Form
组件将阻止浏览器发布表单,而是使用JavaScript
发布数据,并在不完全刷新的情况下模拟浏览器的本地表单行为。
4 修改状态
跟踪状态和更新用户界面是应用程序的核心工作。Qwik
提供了一个 useSignal
钩子来跟踪应用程序的状态。要了解更多信息,请参阅状态管理。
从 qwik
引入 useSignal
import { component$, useSignal } from "@builder.io/qwik";
使用 useSignal()
声明组件的状态
const isFavoriteSignal = useSignal(false);
为组件添加一个按钮,以修改状态
<button onClick$={() => { isFavoriteSignal.value = !isFavoriteSignal.value; }}>{isFavoriteSignal.value ? '❤️' : ''}</button>
5 任务和调用服务器代码
在 Qwik
中,任务是指当状态发生变化时需要进行的工作(类似于其他框架中的 effect
)。在本例中,我们使用任务来调用服务器上的代码。
从 qwik
引入 useTask$
import { component$, useSignal, useTask$ } from "@builder.io/qwik";
创建跟踪 isFavoriteSignal
状态的新任务
useTask$(({ track }) => {});
添加 track
调用,以便在 isFavoriteSignal
状态改变时重新执行任务。
useTask$(({ track }) => {track(() => isFavoriteSignal.value);});
添加要在状态变化时执行的工作
useTask$(({ track }) => {track(() => isFavoriteSignal.value);console.log('FAVORITE (isomorphic)', isFavoriteSignal.value);});
如果只想让工作在服务器上进行,则用 server$()
进行封装
useTask$(({ track }) => {track(() => isFavoriteSignal.value);console.log('FAVORITE (isomorphic)', isFavoriteSignal.value);server$(() => {console.log('FAVORITE (server)', isFavoriteSignal.value);})();});
注意:
useTask$
的主体在服务器和客户端上都会执行(同构)。- 在
SSR
上,服务器会打印FAVORITE (isomorphic) false
和FAVORITE (server) false
。 - 当用户与
favorite
交互时,客户端会打印FAVORITE (isomorphic) true
,服务器会打印FAVORITE (server) true
。
6 样式
样式是任何应用程序的重要组成部分。Qwik
提供了一种将样式与组件关联起来并设定其范围的方法。
创建文件 src/routes/joke/index.css
p {font-weight: bold;}form {float: right;}
在文件 src/routes/joke/index.tsx
中引入样式文件
import styles from "./index.css?inline";
从 qwik
导入 useStylesScoped$
import { component$, useSignal, useStylesScoped$, useTask$ } from "@builder.io/qwik";
告诉组件加载样式
useStylesScoped$(styles);
代码解释:
?inline
参数告诉Vite
将样式内联到组件中。useStylesScoped$
调用告诉Qwik
仅将样式与组件关联(范围)。- 只有当样式尚未作为
SSR
的一部分内联时,才会被加载,而且只针对第一个组件。
本节的完整代码片段如下,以供参考:
import { component$, useSignal, useStylesScoped$, useTask$ } from '@builder.io/qwik';import { routeLoader$, Form, routeAction$, server$ } from '@builder.io/qwik-city';import styles from './index.css?inline'; export const useDadJoke = routeLoader$(async () => {const response = await fetch('https://icanhazdadjoke.com/', {headers: { Accept: 'application/json' },});return (await response.json()) as {id: string;status: number;joke: string;};}); export const useJokeVoteAction = routeAction$((props) => {console.log('VOTE', props);}); export default component$(() => {useStylesScoped$(styles);const isFavoriteSignal = useSignal(false);// Calling our `useDadJoke` hook, will return a reactive signal to the loaded data.const dadJokeSignal = useDadJoke();const favoriteJokeAction = useJokeVoteAction();useTask$(({ track }) => {track(() => isFavoriteSignal.value);console.log('FAVORITE (isomorphic)', isFavoriteSignal.value);server$(() => {console.log('FAVORITE (server)', isFavoriteSignal.value);})();});return (<section class="section bright"><p>{dadJokeSignal.value.joke}</p><Form action={favoriteJokeAction}><input type="hidden" name="jokeID" value={dadJokeSignal.value.id} /><button name="vote" value="up"></button><button name="vote" value="down"></button></Form><buttononClick$={() => (isFavoriteSignal.value = !isFavoriteSignal.value)}>{isFavoriteSignal.value ? '❤️' : ''}</button></section>);});
7 预览
我们创建了一个最小的应用程序,让您对 Qwik
的关键概念和 API
有一个大致的了解。该应用程序运行在开发模式下,使用热模块重载(HMR
)在更改代码的同时持续更新应用程序。
在开发模式下:
- 每个文件都是单独加载的,这可能会导致网络选项卡中出现瀑布流。
- 捆绑包没有投机加载,因此第一次交互时可能会有延迟。
让我们创建一个能消除这些问题的生产构建。
创建预览版
运行 npm run preview
创建生产构建。
注意:
- 您的应用程序现在应已完成生产构建,并在不同的端口上运行。
- 如果您现在与应用程序交互,开发工具的网络选项卡应显示捆绑包已从
ServiceWorker
缓存中即时交付。
总结
恭喜!你已经完成了 Qwik
的基础入门教学!希望这篇文章对你有所帮助,更多的 Qwik
知识,需要在实战中学习掌握。
下面是 Qwik
中各个 API
功能列表和解释:
component$
—— 每个组件导出时,必须使用这个函数routeLoader$
—— 任何组件渲染之前在服务器上紧急调用,并负责加载数据。并返回一个挂钩函数,可在组件中用于检索服务器数据。routeAction$
—— 将数据从客户端发回服务器。routeAction$
是向服务器发送数据的首选方式,因为它使用的是浏览器本地表单API
,即使禁用 JavaScript 也能正常工作。useSignal
—— 跟踪应用程序的状态。useTask$
—— 类似effect
。当状态发生变化时需要执行某些工作时,可以使用该API
,并且该API
还提供了:状态变化时,服务器执行某些动作的功能(配合server$
使用)。useStylesScoped$
—— 引入样式文件时使用
参考链接:
qwik 官方教程:https://qwik.builder.io/docs/getting-started/
文章首发地址:
Qwik开发使用入门 – Cikayo