最佳实践

模板中的内联操作

如果操作在模板中内联,Qwik 优化器可以更好地优化应用程序的响应性。

次优实现
// Don't do this!
export default component$(() => {
  const signal = useSignal(0);
  const isBiggerThanZero = signal.value > 0 ? 'Bigger than zero' : 'Smaller than zero';
  return (
    <div>
      <button onClick$={() => signal.value++}>+</button>
      <button onClick$={() => signal.value--}>-</button>
      <div>{isBiggerThanZero} - Current value: { signal.value }</div>
    </div>
  );
});

上面的实现将在信号更改时导致整个模板重新渲染。这是因为 isBiggerThanZero 没有在模板中内联。

最佳实现
export default component$(() => {
  const signal = useSignal(0);
  return (
    <div>
      <button onClick$={() => signal.value++}>+</button>
      <button onClick$={() => signal.value--}>-</button>
        <div>
          {signal.value > 0 ? 'Bigger than zero' : 'Smaller than zero'} - Current
          value: {signal.value}
        </div>
    </div>
  );
});

将信号读取移动到 useTask$useComputed$

这与上面的提示类似。

每次 Qwik “读取” 信号/存储值时,“读取”发生所在的函数将在该信号的每次更改时重新运行。

因此,尽可能在 useTask$useComputed$ 函数内部而不是组件函数内部“读取”值(并跟踪它们)。

因为否则,您的组件函数将在每次更改时重新运行。

次优实现
// Don't do this!
export default component$(() => {
  const count = useSignal(1);
  const doubleCount = count.value*2;
  return (
    <div>{doubleCount}</div>
  );
});

上面的实现将在信号更改时导致整个模板重新渲染。

下面,只有 useComputed$ 函数将在任何 count.value 更改时重新运行

最佳实现
export default component$(() => {
  const count = useSignal(1);
  const dobuleCount = useComputed$(() => count.value*2);
  return (
    <div>{doubleCount.value}</div>
  );
});

useVisibleTask$() 作为最后的手段

虽然方便,但 useVisibleTask$() 会急切地运行代码并阻塞主线程,阻止用户交互,直到任务完成。您可以将其视为一个应急措施。

如有疑问,请勿使用 “useVisibleTask$()”,而是使用

  • useTask$() -> 在 SSR 模式下执行代码。
  • useOn() -> 监听 当前组件 根元素上的事件。
  • useOnWindow() -> 监听 window 对象上的事件。
  • useOnDocument() -> 监听 document 对象上的事件。

但有时,这是实现结果的唯一方法。

在这种情况下,您可以在 “useVisibleTask$” 之前的行添加 // eslint-disable-next-line qwik/no-use-visible-task 来消除警告。

使用 useOn()useOnWindow()useOnDocument() 注册 DOM 事件

Qwik 允许使用 useOn() 或使用 JSX 以声明方式注册事件监听器。

当使用 useVisibleTask$() 以编程方式注册事件时,我们正在急切地下载和执行 JavaScript,即使事件没有触发。

次优实现
// Don't do this!
useVisibleTask$(({ cleanup }) => {
  const listener = (event) => {
    const mouseEvent = event as MouseEvent;
    console.log(mouseEvent.x, mouseEvent.y);
  };
  document.addEventListener('mousemove', listener);
 
  cleanup(() => {
    document.removeEventListener('mousemove', listener);
  });
});

上面的实现会导致更多 JavaScript 急切地加载,而不是对用户事件做出精确响应。增加的前端 JavaScript 加载会导致应用程序性能下降。

相反,使用 useOnDocument() 钩子在 document 对象上注册事件,这样 Qwik 只有在事件触发时才会执行任何 JS。

最佳实现
useOnDocument(
  'mousemove',
  $((event) => {
    const mouseEvent = event as MouseEvent;
    console.log(mouseEvent.x, mouseEvent.y);
    // No manual clean up required!
  })
);

避免从 window 对象访问位置

不要直接访问 window.location,而是使用 useLocation() 钩子。

次优实现
// Don't do this!
useVisibleTask$(()=> {
    if (window.location.href).includes('foo') {
        //... do the thing
    }
})
// or
useTask$(() => {
  if (isBrowser) {
        if (window.location.href).includes('foo') {
        //... do the thing
    }
  }
})

许多与位置信息相关的操作可以在初始服务器端渲染期间执行,从而生成纯 HTML,而无需任何 JavaScript 开销。

强制此逻辑在客户端运行会导致增加的前端 JavaScript 并导致急切加载。

最佳实现
// Do this!
const location = useLocation();
 
if (location.url.href.includes('foo')) {
  // Do the thing
}

例外

当使用 SSG 生成纯静态文件时,在构建时间内不可避免地要依赖服务器,而没有当前位置信息。

但是,请谨慎!如果所需信息(例如查询参数)直到用户事件发生才需要,请将检查包含在您的事件处理代码中。

这种方法有助于防止 JavaScript 的急切加载并提高性能。

参见:useLocation() 文档

贡献者

感谢所有帮助改进本文档的贡献者!

  • mhevery
  • the-r3aper7
  • manucorporat
  • jakovljevic-mladen
  • kerbelp
  • wfairclough
  • cunzaizhuyi
  • reemardelarosa
  • un33k
  • egmaleta
  • mugan86
  • octoper
  • mrhoodz
  • VinuB-Dev
  • anartzdev
  • adamdbradley
  • hamatoyogi
  • maiieul