summaryrefslogtreecommitdiff
path: root/2025/07
diff options
context:
space:
mode:
authormuqiuhan <[email protected]>2025-07-31 10:50:58 +0000
committermuqiuhan <[email protected]>2025-07-31 10:50:58 +0000
commit6ba051fa1ba4e23ee47adef5553263ef654e6008 (patch)
treee7ce59ac5c0bdb117103f926a9c56c5fad01e547 /2025/07
parent17e9059a904c1d9f58063aace4cc4feed4dfc5b7 (diff)
downloadblog-6ba051fa1ba4e23ee47adef5553263ef654e6008.tar.gz
deploy: ef7c3e415ce43d2b5307bcfdd5c98109faa25721
Diffstat (limited to '2025/07')
-rw-r--r--2025/07/29/nestjs-nested-object-dto/index.html5
-rw-r--r--2025/07/31/svelte-lazyquery/index.html336
2 files changed, 341 insertions, 0 deletions
diff --git a/2025/07/29/nestjs-nested-object-dto/index.html b/2025/07/29/nestjs-nested-object-dto/index.html
index f6b0f7c0..3e7695e5 100644
--- a/2025/07/29/nestjs-nested-object-dto/index.html
+++ b/2025/07/29/nestjs-nested-object-dto/index.html
@@ -241,6 +241,11 @@
<nav class="post-nav">
<div class="prev-item">
+ <div class="icon arrow-left"></div>
+ <div class="post-link">
+ <a href="/2025/07/31/svelte-lazyquery/">Prev</a>
+ </div>
+
</div>
<div class="next-item">
diff --git a/2025/07/31/svelte-lazyquery/index.html b/2025/07/31/svelte-lazyquery/index.html
new file mode 100644
index 00000000..ccfa3744
--- /dev/null
+++ b/2025/07/31/svelte-lazyquery/index.html
@@ -0,0 +1,336 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
+<meta http-equiv="X-UA-Compatible" content="ie=edge">
+
+ <meta name="author" content="韩暮秋">
+
+
+ <meta name="subtitle" content="暮秋小屋">
+
+
+ <meta name="description" content="这里是暮秋小屋,思念和灵感的寄存处">
+
+
+ <meta name="keywords" content="韩暮秋,MuqiuHan,'Muqiu Han', 'muqiu han', muqiuhan">
+
+
+
+
+ <title>
+
+ 使用 Runed intersection observer 与 tanstack query 实现通用的惰性加载组件 |
+ 暮秋小屋
+ </title>
+
+
+
+ <link rel="icon" href="/favicon.ico">
+
+
+ <style>
+ @font-face {
+ font-family: CarroisSong;
+ src: url('/fonts/CarroisSong.ttf');
+ }
+ </style>
+
+
+
+ <!-- stylesheets list from _config.yml -->
+
+ <link rel="stylesheet" href="/css/style.css">
+
+
+
+
+
+ <!-- scripts list from _config.yml -->
+
+ <script
+ src="/js/menu.js"></script>
+
+
+
+
+
+ <script
+ src="https://polyfill.alicdn.com/polyfill.js?features=es6"></script>
+ <script
+ id="MathJax-script"
+ async
+ src="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-M/mathjax/3.2.0/es5/tex-mml-chtml.js"></script>
+
+
+
+
+ <meta name="generator" content="Hexo 6.3.0"></head>
+ <body>
+ <div class="mask-border">
+ </div>
+
+ <div class="wrapper">
+
+ <div class="header">
+ <div class="flex-container">
+ <div class="header-inner">
+ <div class="site-brand-container">
+ <a href="/">
+
+ 暮秋小屋
+
+ </a>
+ </div>
+ <div id="menu-btn" class="menu-btn" onclick="toggleMenu()">
+ 菜单
+ </div>
+ <nav class="site-nav">
+ <ul class="menu-list">
+
+
+ <li class="menu-item">
+ <a href="/">
+ 主页
+ </a>
+ </li>
+
+
+
+ <li class="menu-item">
+ <a href="/categories/gallery/">
+ 日记本
+ </a>
+ </li>
+
+
+
+ <li class="menu-item">
+ <a href="/tags/Medicine/">
+ 医学
+ </a>
+ </li>
+
+
+
+ <li class="menu-item">
+ <a href="/tags/Technique/">
+ 计算机/互联网
+ </a>
+ </li>
+
+
+
+ <li class="menu-item">
+ <a href="/tags/Life/">
+ 生活
+ </a>
+ </li>
+
+
+
+ <li class="menu-item">
+ <a href="/archives">
+ 全部
+ </a>
+ </li>
+
+
+
+ <li class="menu-item">
+ <a href="/about">
+ 关于
+ </a>
+ </li>
+
+
+
+ <li class="menu-item search-btn">
+ <a href="#">Search</a>
+ </li>
+
+ </ul>
+ </nav>
+ </div>
+ </div>
+</div>
+
+ <div class="main">
+ <div class="flex-container">
+ <article id="post">
+
+
+ <div class="post-head">
+ <div class="post-info">
+ <div class="tag-list">
+
+
+ <span class="post-tag">
+ <a href="/tags/Technique/">
+ Technique
+ </a>
+ </span>
+
+
+ </div>
+ <div class="post-title">
+
+
+ 使用 Runed intersection observer 与 tanstack query 实现通用的惰性加载组件
+
+
+ </div>
+ <span class="post-date">
+ Jul 31, 2025
+ </span>
+ </div>
+ <div class="post-img">
+
+ <div class="h-line-primary"></div>
+
+ </div>
+</div>
+ <div class="post-content">
+ <p>在前端,一个常见的性能瓶颈是初始加载时请求了用户当前 viewpoint 之外的非必要数据。惰性加载(Lazy Loading)是一种关键策略,它将数据获取推迟到组件进入 viewpoint 时才执行。本文将阐述如何结合使用 Svelte 5、<code>@tanstack/svelte-query</code> 和 <code>runed.dev</code> 的 <code>useIntersectionObserver</code>,构建一个通用的、可复用的惰性加载组件。</p>
+<p><strong>核心依赖与环境</strong></p>
+<ul>
+<li><p><strong>Svelte 5:</strong> 本实现依赖于 Svelte 5 的符文(Runes)特性,它提供了更精细、更直观的状态管理能力。</p>
+</li>
+<li><p><strong>@tanstack&#x2F;svelte-query:</strong> TanStack Query 的 Svelte 适配版,用户获取数据。</p>
+</li>
+<li><p><strong>runed.dev:</strong> 一个提供多种 Svelte 5 实用工具的库,本文主要使用其 <code>useIntersectionObserver</code>。</p>
+</li>
+</ul>
+<h4 id="设计哲学:分离关注点"><a href="#设计哲学:分离关注点" class="headerlink" title="设计哲学:分离关注点"></a><strong>设计哲学:分离关注点</strong></h4><p>该方案的核心思想是将“何时加载”与“如何加载”这两个关注点进行解耦。</p>
+<ol>
+<li><p><strong>何时加载 (When to Load):</strong> 组件的可见性决定了数据加载的时机。我们利用 <code>Intersection Observer API</code> 来精确、高效地监听一个元素是否进入 viewpoint。<code>runed.dev</code> 库为此提供了名为 <code>useIntersectionObserver</code> 的便捷封装。</p>
+</li>
+<li><p><strong>如何加载 (How to Load):</strong> 数据获取、缓存、同步和状态管理的复杂性由 <code>@tanstack/svelte-query</code> (TanStack Query) 处理。它提供了一套强大的工具集来管理异步数据。</p>
+</li>
+</ol>
+<p>通过将这两者结合,可以创建一个名为 <code>LazyQuery</code> 的抽象组件。该组件内部处理可见性检测,并根据检测结果动态控制 TanStack Query 的执行,而将具体的查询逻辑(<code>queryFn</code>)和键(<code>queryKey</code>)完全交由使用者定义。</p>
+<h4 id="LazyQuery-组件的实现"><a href="#LazyQuery-组件的实现" class="headerlink" title="LazyQuery 组件的实现"></a><code>LazyQuery</code> 组件的实现</h4><blockquote>
+<p>目标:封装惰性加载逻辑,并向外暴露一个标准的 TanStack Query 接口。</p>
+</blockquote>
+<p>一、对组件的接口类型做如下定义:</p>
+<figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> <span class="keyword">type</span> &#123; <span class="title class_">CreateQueryOptions</span>, <span class="title class_">QueryKey</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;@tanstack/svelte-query&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="keyword">type</span> &#123; <span class="title class_">Snippet</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;svelte&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> <span class="title class_">Props</span>&lt;</span><br><span class="line"> <span class="title class_">TQueryFnData</span> = <span class="built_in">unknown</span>,</span><br><span class="line"> <span class="title class_">TError</span> = <span class="title class_">Error</span>,</span><br><span class="line"> <span class="title class_">TData</span> = <span class="title class_">TQueryFnData</span>,</span><br><span class="line"> <span class="title class_">TQueryKey</span> <span class="keyword">extends</span> <span class="title class_">QueryKey</span> = <span class="title class_">QueryKey</span></span><br><span class="line">&gt; = &#123;</span><br><span class="line"> <span class="comment">/// 一个标准的 TanStack Query 配置对象</span></span><br><span class="line"> <span class="attr">queryOptions</span>: <span class="title class_">CreateQueryOptions</span>&lt;<span class="title class_">TQueryFnData</span>, <span class="title class_">TError</span>, <span class="title class_">TData</span>, <span class="title class_">TQueryKey</span>&gt;;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/// Svelte 5 的 `Snippet` 类型,它允许父组件向子组件传递一段可执行的 UI 模板。</span></span><br><span class="line"> <span class="comment">/// 这个 `Snippet` 会接收到 `createQuery` 返回的完整查询对象 `query`,</span></span><br><span class="line"> <span class="comment">/// 从而可以访问 `data`, `isLoading`, `error` 等所有状态。</span></span><br><span class="line"> <span class="attr">children</span>: <span class="title class_">Snippet</span>&lt;&#123;</span><br><span class="line"> <span class="attr">query</span>: <span class="title class_">ReturnType</span>&lt;<span class="keyword">typeof</span> createQuery&lt;<span class="title class_">TQueryFnData</span>, <span class="title class_">TError</span>, <span class="title class_">TData</span>, <span class="title class_">TQueryKey</span>&gt;&gt;;</span><br><span class="line"> &#125;&gt;;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
+
+<p>二、组件逻辑:</p>
+<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">script</span> <span class="attr">lang</span>=<span class="string">&quot;ts&quot;</span>&gt;</span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">import</span> &#123; createQuery, type <span class="title class_">CreateQueryOptions</span>, type <span class="title class_">QueryKey</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;@tanstack/svelte-query&#x27;</span>;</span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">import</span> &#123; useIntersectionObserver &#125; <span class="keyword">from</span> <span class="string">&#x27;runed&#x27;</span>;</span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">import</span> type &#123; <span class="title class_">Snippet</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;svelte&#x27;</span>;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"> <span class="comment">// [上述的类型定义]</span></span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">let</span> &#123; queryOptions, children &#125;: <span class="title class_">Props</span> = $props();</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">let</span> <span class="attr">el</span>: <span class="title class_">Element</span>;</span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">const</span> &#123; isIntersecting &#125; = <span class="title function_">useIntersectionObserver</span>(</span></span><br><span class="line"><span class="language-javascript"> <span class="function">() =&gt;</span> el,</span></span><br><span class="line"><span class="language-javascript"> &#123;</span></span><br><span class="line"><span class="language-javascript"> <span class="attr">rootMargin</span>: <span class="string">&#x27;200px&#x27;</span>,</span></span><br><span class="line"><span class="language-javascript"> &#125;</span></span><br><span class="line"><span class="language-javascript"> );</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">const</span> query = <span class="title function_">createQuery</span>(&#123;</span></span><br><span class="line"><span class="language-javascript"> ...queryOptions,</span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">get</span> <span class="title function_">enabled</span>() &#123;</span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">return</span> ($state.<span class="title function_">is</span>(isIntersecting) &amp;&amp; (queryOptions.<span class="property">enabled</span> ?? <span class="literal">true</span>));</span></span><br><span class="line"><span class="language-javascript"> &#125;</span></span><br><span class="line"><span class="language-javascript"> &#125;);</span></span><br><span class="line"><span class="language-javascript"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">bind:this</span>=<span class="string">&#123;el&#125;</span>&gt;</span></span><br><span class="line"> &#123;#snippet children(&#123; query &#125;)&#125;</span><br><span class="line"> &#123;/snippet&#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure>
+
+<ul>
+<li>我们创建一个 <code>div</code> 元素作为哨兵(sentinel),并用 <code>bind:this=&#123;el&#125;</code> 将其 DOM 引用绑定到变量 <code>el</code>。</li>
+<li><code>useIntersectionObserver</code> 接收一个返回目标元素的函数 <code>() =&gt; el</code>。<ul>
+<li>它返回一个响应式的状态对象,其中 <code>isIntersecting</code> 是一个布尔值的符文(rune),当 <code>div</code> 元素进入 viewpoint 时为 <code>true</code>,否则为 <code>false</code>。</li>
+</ul>
+</li>
+<li><code>rootMargin: &#39;200px&#39;</code> 是一个优化选项,它会在元素距离 viewpoint 还有 200px 时就触发加载,从而提升用户体验。</li>
+</ul>
+<h4 id="使用"><a href="#使用" class="headerlink" title="使用"></a>使用</h4><p>使用 <code>LazyQuery</code> 组件非常直观。开发者只需关注数据获取的业务逻辑,而无需关心惰性加载的实现细节,假设有一个获取图表数据的场景:</p>
+<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">script</span> <span class="attr">lang</span>=<span class="string">&quot;ts&quot;</span>&gt;</span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">import</span> &#123; <span class="title class_">QueryClientProvider</span>, <span class="title class_">QueryClient</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;@tanstack/svelte-query&#x27;</span>;</span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">import</span> <span class="title class_">LazyQuery</span> <span class="keyword">from</span> <span class="string">&#x27;./LazyQuery.svelte&#x27;</span>;</span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">import</span> <span class="title class_">CopdGoldGradingChart</span> <span class="keyword">from</span> <span class="string">&#x27;./CopdGoldGradingChart.svelte&#x27;</span>;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">const</span> queryClient = <span class="keyword">new</span> <span class="title class_">QueryClient</span>();</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"> <span class="comment">// Define the query configuration object, just like with a standard `createQuery`.</span></span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">const</span> queryOptions = &#123;</span></span><br><span class="line"><span class="language-javascript"> <span class="attr">queryKey</span>: [<span class="string">&#x27;goldGradingData&#x27;</span>],</span></span><br><span class="line"><span class="language-javascript"> <span class="attr">queryFn</span>: <span class="title function_">async</span> () =&gt; &#123;</span></span><br><span class="line"><span class="language-javascript"> <span class="comment">// Simulate a network request</span></span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">await</span> <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="function"><span class="params">resolve</span> =&gt;</span> <span class="built_in">setTimeout</span>(resolve, <span class="number">1500</span>));</span></span><br><span class="line"><span class="language-javascript"> <span class="comment">// In a real application, this would be an API call</span></span></span><br><span class="line"><span class="language-javascript"> <span class="comment">// const response = await fetch(&#x27;/api/gold-grading&#x27;);</span></span></span><br><span class="line"><span class="language-javascript"> <span class="comment">// return await response.json();</span></span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">return</span> &#123; <span class="attr">data</span>: &#123; <span class="attr">totalPatients</span>: <span class="number">1234</span>, <span class="attr">chartPoints</span>: [<span class="comment">/* ... */</span>] &#125; &#125;;</span></span><br><span class="line"><span class="language-javascript"> &#125;</span></span><br><span class="line"><span class="language-javascript"> &#125;;</span></span><br><span class="line"><span class="language-javascript"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">QueryClientProvider</span> <span class="attr">client</span>=<span class="string">&#123;queryClient&#125;</span>&gt;</span></span><br><span class="line"> <span class="tag">&lt;<span class="name">div</span> <span class="attr">style</span>=<span class="string">&quot;height: 200vh;&quot;</span>&gt;</span></span><br><span class="line"> <span class="tag">&lt;<span class="name">p</span>&gt;</span>Scroll down to see the chart...<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line"> <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"></span><br><span class="line"> <span class="tag">&lt;<span class="name">LazyQuery</span> &#123;<span class="attr">queryOptions</span>&#125;&gt;</span></span><br><span class="line"> &#123;#snippet children(&#123; query &#125;)&#125;</span><br><span class="line"> &#123;#if query.isLoading&#125;</span><br><span class="line"> <span class="tag">&lt;<span class="name">p</span>&gt;</span>Loading chart data...<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line"> &#123;:else if query.error&#125;</span><br><span class="line"> <span class="tag">&lt;<span class="name">p</span>&gt;</span>Error: &#123;query.error.message&#125;<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line"> &#123;:else if query.data&#125;</span><br><span class="line"> <span class="tag">&lt;<span class="name">CopdGoldGradingChart</span></span></span><br><span class="line"><span class="tag"> <span class="attr">isLoading</span>=<span class="string">&#123;query.isFetching&#125;</span></span></span><br><span class="line"><span class="tag"> <span class="attr">error</span>=<span class="string">&#123;query.error&#125;</span></span></span><br><span class="line"><span class="tag"> <span class="attr">refetch</span>=<span class="string">&#123;()</span> =&gt;</span> query.refetch()&#125;</span><br><span class="line"> data=&#123;query.data.data&#125;</span><br><span class="line"> totalPatients=&#123;query.data.data.totalPatients&#125;</span><br><span class="line"> /&gt;</span><br><span class="line"> &#123;/if&#125;</span><br><span class="line"> &#123;/snippet&#125;</span><br><span class="line"> <span class="tag">&lt;/<span class="name">LazyQuery</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">QueryClientProvider</span>&gt;</span></span><br></pre></td></tr></table></figure>
+
+<ul>
+<li>页面初始加载时,<code>LazyQuery</code> 组件被渲染,但由于其 <code>div</code> 在 viewpoint 之外,<code>isIntersecting</code> 为 <code>false</code>。</li>
+<li><code>createQuery</code> 被调用,但因为 <code>enabled</code> 条件为 <code>false</code>,查询处于禁用状态,不会发起任何网络请求。</li>
+<li><code>children</code> 片段被渲染,此时 <code>query.isLoading</code> 为 <code>true</code>(这是 TanStack Query 禁用查询时的初始状态),显示 “Loading chart data…”。</li>
+<li>当用户向下滚动,<code>div</code> 元素进入 viewpoint(或进入 <code>200px</code> 的预加载区域)。</li>
+<li><code>useIntersectionObserver</code> 将 <code>isIntersecting</code> 的值更新为 <code>true</code>。</li>
+<li>这个变化被 <code>createQuery</code> 的 <code>enabled</code> 访问器捕获,查询被自动激活,<code>queryFn</code> 开始执行。</li>
+<li>TanStack Query 负责管理后续的状态变化(<code>isFetching</code>, <code>data</code>, <code>error</code>),并驱动 <code>children</code> 片段内的 UI 自动更新。</li>
+</ul>
+<h4 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h4><ul>
+<li><a target="_blank" rel="noopener" href="https://runed.dev/docs/utilities/use-intersection-observer">https://runed.dev/docs/utilities/use-intersection-observer</a></li>
+<li><a target="_blank" rel="noopener" href="https://tanstack.com/table/latest/docs/introduction">https://tanstack.com/table/latest/docs/introduction</a></li>
+</ul>
+
+</div>
+
+<script>
+ window.onload = detectors();
+</script>
+ <div class="post-footer">
+ <div class="h-line-primary"></div>
+ <nav class="post-nav">
+ <div class="prev-item">
+
+ </div>
+ <div class="next-item">
+
+ <div class="icon arrow-right"></div>
+ <div class="post-link">
+ <a href="/2025/07/29/nestjs-nested-object-dto/">Next</a>
+ </div>
+
+ </div>
+ </nav>
+</div>
+
+
+ <div class="post-comment">
+
+
+
+
+
+
+
+</div>
+
+
+</article>
+ </div>
+ </div>
+
+ <div class="footer">
+ <div class="flex-container">
+ <div class="footer-text">
+
+
+ 韩暮秋 |
+
+
+ 希望路过的人可以添点柴火让这里暖和点
+
+ </div>
+ </div>
+</div>
+
+ </div>
+
+
+ <div class="search-popup">
+ <div class="search-popup-overlay">
+ </div>
+ <div class="search-popup-window" >
+ <div class="search-header">
+ <div class="search-input-container">
+ <input autocomplete="off" autocapitalize="off" maxlength="80"
+ placeholder="Search Anything" spellcheck="false"
+ type="search" class="search-input">
+ </div>
+ <div class="search-close-btn">
+ <div class="icon close-btn"></div>
+ </div>
+ </div>
+ <div class="search-result-container">
+ </div>
+ </div>
+</div>
+
+<script>
+ const searchConfig = {
+ path : "/search.xml",
+ top_n_per_article: "1",
+ unescape : "false",
+ trigger: "auto",
+ preload: "false"
+ }
+</script>
+<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/search.js"></script>
+<script src="/js/search.js"></script>
+
+
+
+ </body>
+</html>