가장 빠른 프레임워크 qwik

2022-11-03
qwik logo[qwik logo]

https://qwik.builder.io/docs/

qwik
is a fine-grained lazy-loading framework representing a fundamentally new approach to building web applications.
qwik은 웹 애플리케이션 구축에 대한 근본적인 새로운 접근 방식을 나타내는 세분화된 레이지 로딩 프레임워크 입니다.

Qwik의 핵심 정책

Optimizer($)

import { component$, useStyles$ } from "@builder.io/qwik" import styles from "./style.css?inline" export default component$(() => { useStyles$(styles) return <div>hello Qwik</div> })
[Qwik 컴포넌트]

Qwik은 파일들을 symbol이라는 수많은 조각으로 나누게 되는데 $ 마크는 Optimizer가 추출할 기능과 그대로 두어야할 기능을 알려줍니다. 옵티마이저는 $기호를 보고 lazy-load 하거나 import 할 symbol을 찾아 적용합니다

번들링 중에 Vite 플러그인으로 실행됩니다. 옵티마이저의 목적은 응용 프로그램을 많은 작은 레이지 로드 가능한 청크로 나누는 것입니다. 옵티마이저는 표현식(일반적으로 함수)을 새 파일로 이동하고 표현식이 이동된 위치를 가리키는 참조를 뒤에 남깁니다.

https://qwik.builder.io/tutorial/events/basic/

버튼 이벤트 튜토리얼

⇒ 서버로 받은 html의 button에는 on:click속성이 chunk의 주소를 가지고있다.
⇒ HTML에는 Qwikloader 스크립트가 인라인되어있고, Qwikloader는 글로벌 리스너를등록합니다. Qwikloader는 1kb정도이고 1ms안에 실행합니다.
⇒ 버튼을 클릭하게되면 Qwikloader가 이벤트를 가로채고 on:clcik 값을 다운로드하고 실행합니다
*청크의 다운로드와 실행사이의 시간이 생기는것을 방지하기 위해 컴포넌트와 관련된 코드를 prepetch합니다.
*다운로드된 청크는 실행에 필요한 함수를 포함하고있습니다.

Resumable

기존의 하이드레이션 방식에 대한 이해
SSR,SSG 어플리케이션을 클라이언트에서 실행할때 세가지 정보를 복원해야합니다
1. Listeners - locate event listeners and install them on the DOM nodes to make the application interactive.(어플리케이션이 상호작용되도록 DOM 노드들에 리스너를 설치하고 위치시킵니다)
2. Component tree - build up an internal data structure representing the application component tree.(응용 프로그램 구성 요소 트리를 나타내는 내부 데이터 구조를 구축합니다.)
3. Application state - restore the application state.(애플리케이션 상태를 복원합니다.)

하이드레이션이 비싼 이유

1. 프레임워크는 현재 페이지와 관련된 모든 구성 요소 코드를 다운로드해야 합니다.
2. 프레임워크는 수신기 위치 및 내부 구성 요소 트리를 다시 빌드하기 위해 페이지의 구성 요소와 연결된 템플릿을 실행해야 합니다.
expensive_hydration[expensive_hydration]
qwik에서 Resumable을 실행하는 방법Qwik에서 Resumable을 실행하는 방법

코드 살펴보기(튜토리얼)

계속해서 코드를 보면서 Qwik의 사용법과 원리를 살펴보도록 하겠습니다.

useStore와 useStyle , 직렬화와 recursive

Qwik은 템플릿을 비순차적으로 비동기식으로 렌더링하고 있기 때문에 독특합니다.

https://qwik.builder.io/docs/components/rendering/

문서

내려받지 않은 요소의 업데이트에는 리렌더되지 않습니다.
다른 프레임워크와 다르게 render()가 비동기입니다
useStylesScoped$ 도 있는데 이것은 클래스네임의 중복을 피할 수 있다
import { component$, useStore, useStyles$ } from "@builder.io/qwik" import { QwikCity, RouterOutlet } from "@builder.io/qwik-city" import styles from "./style.css?inline" export default component$(() => { useStyles$(styles) const store = useStore( { todos: [ { id: 1, text: "투두1", checked: false, }, { id: 2, text: "투두2", checked: false, }, { id: 3, text: "투두3", checked: false, }, ], notDescended: "", }, { recursive: true } ) console.log(store) return ( <div> {store.todos.map((todo) => ( <div> <p>{todo.id}</p> <p>{todo.text}</p> <div onClick$={() => { store.todos[todo.id - 1].checked = !store.todos[todo.id - 1].checked }}> {todo.checked ? <p class="color-red">checked</p> : <p>unchecked</p>} </div> </div> ))} <button onClick$={() => { store.notDescended = store.notDescended + "1" }}> change not descended </button> </div> ) })
[checked 변화시 dom의 변화]
사용되지 않는 스타일은 포함되지 않았었는데, 필요시에 style 태그가 추가되는 것을 볼 수 있습니다.

useSignal,Slot(children) 사용, 인라인 컴포넌트

구성 요소가 다른 구성 요소와 함께 로드되도록 하려면 인라인 구성 요소를 만듭니다.
상위 요소가 렌더될때 함께 렌더링되고, 함께 번들링 된다.
use등의 훅스를 사용할 수 없다.
import { component$, Slot, useStore, useStyles$, useSignal, $ } from "@builder.io/qwik" import { QwikCity, RouterOutlet } from "@builder.io/qwik-city" import styles from "./style.css?inline" interface TodoProps { id?: number text?: string checked?: boolean } export const Todo = ({ id, text, checked }: TodoProps) => { return ( <div> <p>{id}</p> <p>{text}</p> <p onClick$={() => {}} class={checked ? "color-red" : undefined}> {checked ? "checked" : "unchecked"} </p> <Slot></Slot> </div> ) } export const LazyTodo = component$(({ id, text, checked }: TodoProps) => { const _checked = useSignal(!!checked) const toggleChecked = $(() => { _checked.value = !_checked.value }) return ( <div> <p>{id}</p> <p>{text}</p> <p onClick$={toggleChecked} class={_checked.value ? "color-red" : undefined}> {_checked.value ? "checked" : "unchecked"} </p> <Slot></Slot> </div> ) }) export default component$(() => { useStyles$(styles) const todos = [ { id: 1, text: "투두1", checked: false, }, { id: 2, text: "투두2", checked: false, }, { id: 3, text: "투두3", checked: false, }, ] return ( <div> {todos.map((todo, index) => ( <Todo {...todo}> <p>childeren = slot {index}</p> </Todo> ))} {todos.map((todo, index) => ( <LazyTodo {...todo}> <p>childeren = slot {index}</p> </LazyTodo> ))} </div> ) })

라이프사이클

라이프 사이클이 버전이 1.0으로 올라가면서 변경되어 

https://qwik.builder.io/docs/components/tasks/

공식문서
를 참고하시기 바랍니다.

Qwik City

Qwik City는 Qwik의 meta-framework로 리액트에서 Next.js,Vue와 Nuxt, svelte와 Svelte Kit를 생각하시면됩니다.
다음 기능을 제공합니다.
추가로 Next.js와 동일하게 ,public, useLocation, useEndPointer(getInitialProps,getServerSideProps의 결과물) SSG의

https://qwik.builder.io/qwikcity/static-site-generation/dynamic-routes/

getStaticPaths
제공합니다.

Qwik 사용한 소감

값비싼 하이드레이션을 레이지 로딩을 하고 과 번들의 용량을 줄여 근본적인 어플리케이션의 성능향상을 프레임워크에서 제공한다는 것이 매력적이다. 하지만 완전히 그것으로부터 자유롭지 않고 고려하며 개발해야하는 것에는 다름이 없다.
jsx 형태와 훅스형태는 기존 리액트 사용자가 사용하기에 사용하기 쉽도록 제작되어있고, 실제로 사용하기에 어려움이 없었다.
Next.js와 같은 QwikCity가 있어 서버사이드 렌더링 및 기타 편의성도 탁월하다
buy me a coffeebuy-me-a-coffee