Rendering 渲染过程

Rendering指的是(基于以下两点)更新DOM的过程:

  1. 应用状态的变化
  2. 组件模板

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>

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>;
});

上面例子,有两个按钮:

  1. 单击第一个按钮改变计数器的方向 (store.step+1-1直接切换)。 改变store会引发组件的OnRender函数执行,JSX将会更新DOM以展示+1-1(如果模板有引用store.step的话)。 但是改变计数器方向不会改变<Greeter name={'World_' + store.count}/>的属性值。 正是这个原因,组件Greeter的模板不需要下载或执行。 这样渐进式的修剪让QWIK可以最小化 当下要渲染的组件 的代码量。
  2. 单击第二个按钮增加 (或减小) store.count. 这会引起 <Greeter name={'World_' + store.count}/> 属性值的改变。 属性的改变意味着QWIK也要渲染<Greeter>了。 但是这时候可能子组件还没有下载,此时,QWIK会懒在家这个组件,当它的渲染函数已经可用时(子组件代码下载完了),就继续渲染。

render() 是异步的

上面例子展示了为什么渲染过程必须是异步的。 渲染流水线可用懒加载子组件是重要的。 懒加载是异步操作;因此渲染过程就需要是异步的。 在实践上,这意味着render()函数是返回promise的。

大多数现在的框架都是同步render()过程。 同步渲染不能很容易地处理异步代码加载,所以同步渲染需要所有依赖的组件 在渲染前 就得早早地下载到浏览器。

DOM 更新缓冲

render()的异步特性意味着在组件下载时 用户可能看到UI的中间渲染过程。 看到中间状态是不好的;因此,QWIK会缓冲所有DOM的更新,一旦所有组件都下载好了,组件的JSX执行完了,再一次性地做DOM更新。 因此UI更新是原子操作,用户不会看到中间步骤。

Made with ❤️ by