Rendering 渲染过程
Rendering指的是(基于以下两点)更新DOM的过程:
- 应用状态的变化
 - 组件模板
 
QWIK是独特的因为它知道怎么无序和异步渲染模板。
- 无序: 意味着Qwik在渲染一个组件时,不需要强制要求父组件或子组件也要渲染好。
 - 异步: 意味着Qwik组件的渲染函数理解它可能需要下载子组件,因此渲染操作是异步的。
 
简单的计数器例子:
export const Counter = component$(() => {
  const store = useStore({ count: 0 });
  return <button onClick$={() => store.count++}>{store.count}</button>;
});
一旦渲染完,HTML片段大概长下面这样:
<div>
  <button q:obj="123" on:click="./chunk-a.js#Counter_button_click[0]">0</button>
</div>
- 关于 
$符号的解释, 参考$and optimizer rules. - 关于 
q:obj的解释, 参考 serialization. - 关于 
on:click的解释, 参考 qwikloader. 
JSX
Qwik 使用 JSX 编写组件模板。 JSX 的讨论不在本文范畴。 如果在其他框架里使用过JSX语法,那就会对QWIK的JSX很熟悉,不过QWIK里JSX的渲染过程是不同的,让我们来看看。
渲染子组件
Qwik 按需懒加载组件。 为了最小化要下载的组件数量,QWIK只会当属性改变时才下载子组件。
export const Parent = component$(() => {
  const store = useStore({ count: 0, step: 1 });
  return (
    <>
      <button onClick$={(store.step *= -1)}>direction</button>
      <button onClick$={() => (store.count += store.step)}>{store.step}</button>
      <Greeter name={'World_' + store.count} />
    </>
  );
});
export const Greeter = component$((props: { name: string }) => {
  return <span>Hello {props.name}</span>;
});
上面例子,有两个按钮:
- 单击第一个按钮改变计数器的方向 (
store.step在+1和-1直接切换)。 改变store会引发组件的OnRender函数执行,JSX将会更新DOM以展示+1和-1(如果模板有引用store.step的话)。 但是改变计数器方向不会改变<Greeter name={'World_' + store.count}/>的属性值。 正是这个原因,组件Greeter的模板不需要下载或执行。 这样渐进式的修剪让QWIK可以最小化 当下要渲染的组件 的代码量。 - 单击第二个按钮增加 (或减小) 
store.count. 这会引起<Greeter name={'World_' + store.count}/>属性值的改变。 属性的改变意味着QWIK也要渲染<Greeter>了。 但是这时候可能子组件还没有下载,此时,QWIK会懒在家这个组件,当它的渲染函数已经可用时(子组件代码下载完了),就继续渲染。 
render() 是异步的
上面例子展示了为什么渲染过程必须是异步的。
渲染流水线可用懒加载子组件是重要的。
懒加载是异步操作;因此渲染过程就需要是异步的。
在实践上,这意味着render()函数是返回promise的。
大多数现在的框架都是同步render()过程。
同步渲染不能很容易地处理异步代码加载,所以同步渲染需要所有依赖的组件 在渲染前 就得早早地下载到浏览器。
DOM 更新缓冲
render()的异步特性意味着在组件下载时 用户可能看到UI的中间渲染过程。
看到中间状态是不好的;因此,QWIK会缓冲所有DOM的更新,一旦所有组件都下载好了,组件的JSX执行完了,再一次性地做DOM更新。
因此UI更新是原子操作,用户不会看到中间步骤。