组件

组件是构建QWIK应用的基本块。QWIK组件是独特的因为:

  • Qwik 组件被Optimizer自动被拆分为多个懒加载的chunks。 (参考 Optimizer discussion)
  • 可恢复的. (组件可以在服务端创建,在客户端继续执行) (参考 resumable discussion)
  • 可以独立于页面其他组件渲染。(参考 rendering discussion)

component$()

组件是小的,可复用的代码段,用于构建一小块UI。

Qwik中, 声明一个组件使用 component$ 方法:

import { component$, useStore } from '@builder.io/qwik';

// Qwik components can be asynchronous
export const MyCmp = component$(async (props: MyCmpProps) => {
  // Declare local state
  const state = useStore({
    count: 0,
  });

  // Returns JSX
  return (
    <>
      <span>
        Hello, {props.name} {state.count}
      </span>
      <div>Times: {state.count}</div>
      <button
        onClick$={() => {
          // This will update the local state and cause a re-render.
          // Reactivity is at Qwik's core!
          state.count++;
        }}
      >
        Increment
      </button>
    </>
  );
});

注意

  • 关于 $ 符号的解释请参考 Lazy-loadingOptimizer
  • 更细节的关于props的讨论, 参考 Component/props。

Props

Props用于传递数据到组件。 Props作为组件的命名参数被声明。

下面例子里,Item组件声明了Props的可选字段name, quantity, description, 和 price

interface ItemProps {
   name?: string;
   quantity?: number;
   description?: string;
   price?: number;
}

export const Item = component$((props: ItemProps) => {
  return ...;
});

使用 components

Qwik 组件可以使用其他组件。

export const Counter = component$((props: {step?:number, initial?: number}) => {
  ...
});

export const MyApp = component$(() => {
  return (
    <>
      <div>Single: <Counter /></div>
      <div>Dozens: <Counter step={12}/></div>
    </>
  );
});

上面例子展示了MyApp 组件使用了Counter组件。 也展示了 使用组件的时候怎么传值到Counter组件的props。

Re-rendering on Reactivity

QWIK组件在组件级别是响应式的。 组件的props、stores都是proxies,这些代理对象跟踪读和写。

  • A proxy-read during OnRender method execution lets Qwik know that the OnRender method depends on a given property. A read creates a subscription on that property. In our example, OnRender reads{store.count}, which creates a subscription that tells Qwik that whenever the store.count changes, the component should be invalidated.
  • 代理对象被赋值时,会被QWIK追踪到,然后会通知所有订阅者 组件invalidated。

当组件get invalidated,他们会被添加到invalidation queue,这个队列会在下一次 requestAnimationFrame时重新渲染,这是组件批量渲染的一种方式。

关于响应式的更多讨论,请参考相关讨论。

懒加载

组件在打包过程中,对于父子组件的拆分也起到了很重要的作用。

export const Child = () => <span>child</span>;

const Parent = () => (
  <section>
    <Child />
  </section>
);

上面例子,Parent组件暗含了对Child组件的引用。当打包器创建chunk时, 引用了Parent组件的入口点,也要连带着打包Child的代码。 这些连带的引用依赖是一个问题,因为这意味着如果引用了根组件,可能会连带着引用整个应用的其他部分。 而这是QWIK本来尽力要避免的。

export const Child = component$(() => {
  return <span>child</span>;
});

export const Parent = component$(() => {
  return (
    <section>
      <Child />
    </section>
  );
});

上面这个例子Optimizer可以发挥作用,把代码拆分为下面这样:

const Child = componentQrl(qrl('./chunk-a', 'Child_onMount'));
const Parent = componentQrl(qrl('./chunk-b', 'Parent_onMount'));
const Parent_onMount = () => qrl('./chunk-c', 'Parent_onRender');
const Parent_onRender = () => (
  <section>
    <Child />
  </section>
);

NOTE 为了简化, 不是所有 transformations 都列出来了; all resulting symbols are kept in the same file for succinctness.

你会注意到当Optimizer转换完代码以后,Parent组件不再直接引用Child。 This is important because it allows the bundler (and tree shakers) to freely move the symbols into different chunks without pulling the rest of the application with it.

所以,当Parent组件渲染了而Child组件还没有下载时会怎样呢? 首先, Parent 组件会像这样渲染它的JSX:

<div>
  <section>
    <div></div>
  </section>
</div>

正如上面所示,<div/>就是Child组件的占位符,一旦Child组件懒加载完毕,它就会插入到这个位置。

心智模型

The Optimizer splits Qwik components into the host element and the behavior of the component. The host element gets bundled with the parent component's OnRender method, whereas the component's behavior is something that gets lazy-loaded on an as-needed basis.

API 概览

State

  • useStore(initialState) - creates a reactive store
  • useRef$(initialState) - creates a ref
  • createContext(contentName) - creates a context
  • useContextProvider$() - creates a context provider
  • useContext$() - use a context

Styles

  • useStylesScoped$() - creates a scoped style
  • useStyles$() - creates a global style

Events

  • useOn() - creates an event listener
  • useOnWindow() - creates a window event listener
  • useOnDocument() - creates a document event listener

Lifecycles

  • useMount$() - creates a post rendering
  • useServerMount$() - creates a post rendering
  • useWatch$() - creates a watch
  • useClientEffect$() - creates a post rendering
  • useResource$() - creates a resource

Other

  • $() - creates a QRL
  • noSerialize()
  • useErrorBoundary()

Components

  • <Slot> - creates a slot
  • <SSRStreamBlock> - creates a stream block
  • <SSRStream> - creates a stream
  • <Fragment> - creates a fragment

See Also

Made with ❤️ by