<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:atom="http://www.w3.org/2005/Atom"
>
<channel>
<title><![CDATA[齐大胜]]></title> 
<atom:link href="https://blog.hizhiji.com/rss.php" rel="self" type="application/rss+xml" />
<description><![CDATA[花有重开日，人无再少年。]]></description>
<link>https://blog.hizhiji.com/</link>
<language>zh-cn</language>
<generator>www.emlog.net</generator>
<item>
    <title>三种方式实现元素高度在0和auto之间的过渡动画</title>
    <link>https://blog.hizhiji.com/?post=25</link>
    <description><![CDATA[<h2>CSS 过渡动画（transition）仅对可计算的数值型属性生效，而 height: auto 属于关键字属性，浏览器无法计算其具体数值变化，导致直接切换时动画失效，出现生硬的瞬间显示 / 隐藏效果。以下三种方案从不同角度解决这一问题。</h2>
<h2>方案一：Grid 网格布局实现（优点：兼容性最好）</h2>
<h2>实现原理</h2>
<p>利用 CSS Grid 布局的 grid-template-rows 属性，通过 0fr（收起状态，高度为 0）和 1fr（展开状态，高度自适应内容）的切换，配合子元素 overflow: hidden 实现动画。fr 单位是可计算的网格轨道单位，因此能触发平滑过渡。</p>
<h2>代码实现</h2>
<pre><code class="language-html">&lt;template&gt;
  &lt;div class="demo-item demo1"&gt;
    &lt;h3&gt;1. 使用 Grid 布局实现&lt;/h3&gt;
    &lt;hr /&gt;
    &lt;el-button type="primary" @click="isExpand1 = !isExpand1"&gt;
      {{ isExpand1 ? "收起" : "展开" }}
    &lt;/el-button&gt;
    &lt;hr /&gt;
    &lt;!-- 容器：通过 grid-template-rows 控制高度 --&gt;
    &lt;div class="container" :style="{ gridTemplateRows: isExpand1 ? '1fr' : '0fr' }"&gt;
      &lt;!-- 内容容器：必须设置 overflow: hidden --&gt;
      &lt;div style="overflow: hidden;"&gt;{{ Mock.mock("@csentence(200, 400)") }}&lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/template&gt;

&lt;script setup&gt;
import Mock from 'mockjs';
import { ref } from "vue";
const isExpand1 = ref(false); // 控制展开收起状态
&lt;/script&gt;

&lt;style lang="scss" scoped&gt;
.demo1 {
  .container {
    display: grid; /* 启用 Grid 布局 */
    grid-template-rows: 0fr; /* 默认收起 */
    margin-left: 10px;
    transition: all 0.5s; /* 平滑过渡 */
  }
}
&lt;/style&gt;</code></pre>
<h2>方案二：interpolate-size 属性（现代浏览器原生方案）</h2>
<h2>实现原理</h2>
<p>interpolate-size 是 CSS 原生属性，用于控制浏览器如何插值计算关键字尺寸（如 auto、min-content 等）。设置为 allow-keywords 后，浏览器会自动计算 height: 0 到 height: auto 的中间过渡值，从而触发平滑动画。。</p>
<h2>代码实现</h2>
<pre><code class="language-html">&lt;template&gt;
  &lt;div class="demo-item demo2"&gt;
    &lt;h3&gt;2. 使用 interpolate-size: allow-keywords&lt;/h3&gt;
    &lt;hr /&gt;
    &lt;el-button type="primary" @click="isExpand2 = !isExpand2"&gt;
      {{ isExpand2 ? "收起" : "展开" }}
    &lt;/el-button&gt;
    &lt;hr /&gt;
    &lt;!-- 直接切换 height: 0 和 auto --&gt;
    &lt;div class="container" :style="{ height: isExpand2 ? 'auto' : '0' }"&gt;
      {{ Mock.mock("@csentence(200, 400)") }}
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/template&gt;

&lt;script setup&gt;
import Mock from 'mockjs';
import { ref } from "vue";
const isExpand2 = ref(false);
&lt;/script&gt;

&lt;style lang="scss" scoped&gt;
.demo2 {
  .container {
    margin-left: 10px;
    overflow: hidden; /* 关键：隐藏溢出内容 */
    interpolate-size: allow-keywords; /* 允许关键字插值计算 */
    transition: all 0.5s; /* 平滑过渡 */
  }
}
&lt;/style&gt;</code></pre>
<h2>方案三：calc-size () 函数（实验性方案）</h2>
<h2>实现原理</h2>
<p>calc-size() 是 CSS 实验性函数，用于计算元素的自适应尺寸，语法为 calc-size(auto, size)，表示 &quot; 计算元素的自动高度，并将其作为 size 变量供过渡使用 &quot;。配合 overflow: hidden 和 height 切换，实现平滑动画。</p>
<h2>代码实现</h2>
<pre><code class="language-html">&lt;template&gt;
  &lt;div class="demo-item demo3"&gt;
    &lt;h3&gt;3. 使用 calc-size(auto, size) 函数&lt;/h3&gt;
    &lt;hr /&gt;
    &lt;el-button type="primary" @click="isExpand3 = !isExpand3"&gt;
      {{ isExpand3 ? "收起" : "展开" }}
    &lt;/el-button&gt;
    &lt;hr /&gt;
    &lt;!-- 展开时使用 calc-size 计算自动高度，收起时为 0 --&gt;
    &lt;div class="container" :style="{ height: isExpand3 ? 'calc-size(auto, size)' : '0' }"&gt;
      {{ Mock.mock("@csentence(200, 400)") }}
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/template&gt;

&lt;script setup&gt;
import Mock from 'mockjs';
import { ref } from "vue";
const isExpand3 = ref(false);
&lt;/script&gt;

&lt;style lang="scss" scoped&gt;
.demo3 {
  .container {
    margin-left: 10px;
    overflow: hidden; /* 关键：隐藏溢出内容 */
    transition: all 0.5s; /* 平滑过渡 */
  }
}
&lt;/style&gt;</code></pre>
<h2>三种方案对比</h2>
<table>
<thead>
<tr>
<th>方案</th>
<th>兼容性</th>
<th>代码复杂度</th>
<th>适用场景</th>
</tr>
</thead>
<tbody>
<tr>
<td>Grid 布局</td>
<td>良好（IE 除外）</td>
<td>低</td>
<td>生产环境首选，需兼容多浏览器</td>
</tr>
<tr>
<td>interpolate-size</td>
<td>现代浏览器（Chrome 117+）</td>
<td>极低</td>
<td>内部系统、现代浏览器专属项目</td>
</tr>
<tr>
<td>calc-size()</td>
<td>实验性（少量浏览器支持）</td>
<td>极低</td>
<td>学习研究、未来场景预留</td>
</tr>
</tbody>
</table>
<h2><a href="http://blog.example.hizhiji.com/#/example?post=25" title="demo">示例</a></h2>]]></description>
    <pubDate>Tue, 16 Dec 2025 21:46:40 +0800</pubDate>
    <dc:creator>齐大胜</dc:creator>
    <guid>https://blog.hizhiji.com/?post=25</guid>
</item>
<item>
    <title>vue2响应式原理分析</title>
    <link>https://blog.hizhiji.com/?post=24</link>
    <description><![CDATA[<h3><strong>什么是响应式？</strong></h3>
<p>Vue 2的响应式以数据为中心的方式来构建用户界面。在Vue 2中，响应式系统通过<code>Object.defineProperty</code> API实现，使得数据对象的属性能够被监听，当属性值发生变化时，与之相关的视图也会自动更新。</p>
<p>具体来说，响应式系统包括以下几个关键概念：</p>
<ol>
<li>
<p><strong>数据劫持 Observer</strong>：使用<code>Object.defineProperty</code>来劫持数据对象的所有属性，为这些属性添加getter和setter。这样，每当属性被访问或修改时，Vue 2都能够知道，并作出相应的处理。</p>
</li>
<li>
<p><strong>依赖收集 Dep</strong>：当渲染函数或计算属性被执行时，它们会访问响应式数据的属性，触发这些属性的getter。在getter中，Vue 2会记录当前正在执行的渲染函数或计算属性作为依赖。</p>
</li>
<li>
<p><strong>观察者模式 Watcher</strong>：每个组件实例都对应一个观察者（Watcher）实例，它会在组件渲染的过程中把接触过的数据属性记录为依赖。当依赖项的setter触发时，会通知观察者，从而使它关联的组件重新渲染。</p>
</li>
<li>
<p><strong>异步更新队列</strong>：Vue 2在检测到数据变化时并不会立即更新DOM，而是把这些变化放到一个异步队列中。在下一个事件循环中，Vue 2会清空队列，并执行实际的DOM更新操作。这种机制可以避免不必要的重复渲染，提高性能。</p>
</li>
<li>
<p><strong>虚拟DOM</strong>：虽然虚拟DOM不是响应式系统的一部分，但它与响应式系统紧密协作。Vue 2使用虚拟DOM来计算出最小的DOM更新操作，从而提高页面渲染的效率。</p>
</li>
</ol>]]></description>
    <pubDate>Sun, 07 Apr 2024 14:51:00 +0800</pubDate>
    <dc:creator>齐大胜</dc:creator>
    <guid>https://blog.hizhiji.com/?post=24</guid>
</item>
<item>
    <title>vue2与vue3的区别</title>
    <link>https://blog.hizhiji.com/?post=23</link>
    <description><![CDATA[<p>Vue 2和Vue 3的响应式系统在实现上有显著的不同，主要体现在以下几个方面：</p>
<ol>
<li>
<p><strong>实现原理的不同</strong>：</p>
<ul>
<li>Vue 2的响应式系统主要基于<code>Object.defineProperty</code> API。它通过递归地为对象的每个属性添加getter和setter来实现数据的劫持和变化侦测。当属性被访问（getter被调用）时，Vue 2会记录当前的依赖（Watcher），并在属性值发生变化（setter被调用）时触发更新。</li>
<li>Vue 3的响应式系统则主要基于<code>Proxy</code> API。<code>Proxy</code>可以拦截对象的各种操作，包括属性的读取、设置、删除等。Vue 3使用<code>Proxy</code>创建响应式对象，能够更灵活地处理各种类型的数据变化，包括数组和嵌套对象的变化。</li>
</ul>
</li>
<li>
<p><strong>性能优化</strong>：</p>
<ul>
<li>Vue 2在初始化组件时会对数据对象的所有属性进行递归遍历和getter/setter的添加，这在处理大型对象时可能会导致较大的性能开销。</li>
<li>Vue 3通过<code>Proxy</code>实现了惰性创建副作用函数（effect），只有当副作用函数被真正使用时才会进行依赖收集。这样可以减少不必要的依赖收集和更新操作，提高了性能。</li>
</ul>
</li>
<li>
<p><strong>依赖追踪和更新机制</strong>：</p>
<ul>
<li>Vue 2使用全局变量<code>Dep</code>来追踪依赖关系，并将<code>Watcher</code>与<code>Dep</code>进行关联。每个属性都有一个对应的<code>Dep</code>实例，当属性被访问时，<code>Watcher</code>会将自身添加到<code>Dep</code>中，当属性发生变化时，<code>Dep</code>会通知所有关联的<code>Watcher</code>进行更新。</li>
<li>Vue 3使用<code>WeakMap</code>来存储依赖关系，将对象作为键，将属性的依赖集合作为值。这样可以避免内存泄漏，并且不需要全局变量来追踪依赖。</li>
</ul>
</li>
<li>
<p><strong>处理嵌套属性和数组</strong>：</p>
<ul>
<li>Vue 2对于嵌套属性和数组的处理较为复杂。对于嵌套属性，需要递归调用<code>Observer</code>进行响应式转换；对于数组，需要重写数组的一些方法来拦截变更操作。</li>
<li>Vue 3通过<code>Proxy</code>的拦截能力可以直接处理嵌套属性和数组，无需递归调用<code>Observer</code>或重写数组方法。</li>
</ul>
</li>
<li>
<p><strong>TypeScript支持</strong>：</p>
<ul>
<li>Vue 2对TypeScript的支持有限，需要额外的类型声明文件。</li>
<li>Vue 3对TypeScript提供了更好的支持，并且在源码中使用了大量的TypeScript类型定义，提高了开发效率和代码可靠性。</li>
</ul>
</li>
<li>
<p><strong>Composition API</strong>：</p>
<ul>
<li>Vue 2主要使用选项式API进行组件的编写，数据和方法通常定义在组件的<code>data</code>和<code>methods</code>选项中。</li>
<li>Vue 3引入了Composition API，提供了<code>ref</code>、<code>reactive</code>、<code>computed</code>和<code>watchEffect</code>等函数，使得逻辑复用和组织更加灵活，尤其是在处理复杂组件时。</li>
</ul>
</li>
</ol>
<p>总结来说，Vue 3在响应式系统上进行了一系列改进和优化，提升了性能、可维护性和开发体验。同时引入Composition API以及对TypeScript的支持也使得开发更加灵活和可靠。</p>]]></description>
    <pubDate>Sun, 07 Apr 2024 10:08:00 +0800</pubDate>
    <dc:creator>齐大胜</dc:creator>
    <guid>https://blog.hizhiji.com/?post=23</guid>
</item>
<item>
    <title>使用JavaScript的MutationObserver实时监测DOM变化</title>
    <link>https://blog.hizhiji.com/?post=21</link>
    <description><![CDATA[<p><a href="http://blog.example.hizhiji.com/#/example?post=21" title="demo">demo-http://blog.example.hizhiji.com/#/example?post=21</a></p>
<h2>使用MutationObserver</h2>
<h3>1.创建一个MutationObserver对象，并传入一个回调函数作为参数</h3>
<pre><code class="language-javascript">// 创建一个MutationObserver对象
var observer = new MutationObserver((mutationRecoard,observe) =〉〉 {
    //mutationRecoard：存放所有dom变化的数组
    //observe：观察者实例
});</code></pre>
<h3>2.使用observe()方法来指定需要观察的DOM节点和观察的选项。例如，可以指定要观察的子节点变化、属性变化、文本内容等。</h3>
<pre><code class="language-javascript">// 需要观察的目标节点
var targetNode = document.getElementById('myDiv');

// 观察配置项
var config = { childList: true };

// 使用MutationObserver开始观察目标节点
observer.observe(targetNode, config);</code></pre>
<h4>配置项的详细属性如下：</h4>
<table>
<thead>
<tr>
<th>属性</th>
<th>类型</th>
<th>含义</th>
</tr>
</thead>
<tbody>
<tr>
<td>childList</td>
<td>Boolean</td>
<td>子节点的变动（指新增，删除或者更改）</td>
</tr>
<tr>
<td>attributes</td>
<td>Boolean</td>
<td>属性的变动</td>
</tr>
<tr>
<td>characterData</td>
<td>Boolean</td>
<td>节点内容或节点文本的变动</td>
</tr>
<tr>
<td>subtree</td>
<td>Boolean</td>
<td>表示是否将该观察器应用于该节点的所有后代节点</td>
</tr>
<tr>
<td>attributeOldValue</td>
<td>Boolean</td>
<td>表示观察attributes变动时，是否需要记录变动前的属性值</td>
</tr>
<tr>
<td>characterDataOldValue</td>
<td>Boolean</td>
<td>表示观察characterData变动时，是否需要记录变动前的值</td>
</tr>
<tr>
<td>attributeFilter</td>
<td>Array</td>
<td>表示需要观察的特定属性（比如[‘class’,‘src’]）</td>
</tr>
</tbody>
</table>
<h3>3.当指定的DOM节点发生变化时，MutationObserver对象会执行回调函数，并提供一个MutationRecord对象作为参数，该对象包含有关变化的详细信息。</h3>
<table>
<thead>
<tr>
<th>属性</th>
<th>含义</th>
</tr>
</thead>
<tbody>
<tr>
<td>type</td>
<td>观察的变动类型（attribute、characterData或者childList）</td>
</tr>
<tr>
<td>target</td>
<td>发生变动的DOM节点</td>
</tr>
<tr>
<td>addedNodes</td>
<td>新增的DOM节点</td>
</tr>
<tr>
<td>removedNodes</td>
<td>删除的DOM节点</td>
</tr>
<tr>
<td>previousSibling</td>
<td>前一个同级节点，如果没有则返回null</td>
</tr>
<tr>
<td>nextSibling</td>
<td>下一个同级节点，如果没有则返回null</td>
</tr>
<tr>
<td>attributeName</td>
<td>发生变动的属性。如果设置了attributeFilter，则只返回预先指定的属性</td>
</tr>
<tr>
<td>oldValue</td>
<td>变动前的值。这个属性只对attribute和characterData变动有效，如果发生childList变动，则返回null</td>
</tr>
</tbody>
</table>
<h3>4.MutationObserver方法</h3>
<table>
<thead>
<tr>
<th>方法名</th>
<th>参数</th>
<th>含义</th>
</tr>
</thead>
<tbody>
<tr>
<td>observe()</td>
<td>targetNode, config</td>
<td>开始观察目标节点</td>
</tr>
<tr>
<td>disconnect()</td>
<td></td>
<td>停止MutationObserver 实例继续观察</td>
</tr>
<tr>
<td>takeRecords()</td>
<td></td>
<td>停止MutationObserver 从观察队列中移除所有待处理的记录</td>
</tr>
</tbody>
</table>
<h3>5.全部代码</h3>
<pre><code class="language-javascript">// 创建一个MutationObserver对象
var observer = new MutationObserver(function(mutations) {
  mutations.forEach(function(mutation) {
    if (mutation.type === 'childList') {
      console.log('子节点发生变化');
    }
  });
});

// 需要观察的目标节点
var targetNode = document.getElementById('myDiv');

// 观察选项
var config = { childList: true };

// 使用MutationObserver开始观察目标节点
observer.observe(targetNode, config);</code></pre>
<p>在上述示例中，创建了一个MutationObserver对象，并传递了一个回调函数作为参数。回调函数中使用forEach()循环遍历每个触发的变化，并通过type属性判断是否是子节点的变化。如果是，则输出相应的提示信息。</p>]]></description>
    <pubDate>Fri, 22 Sep 2023 10:24:00 +0800</pubDate>
    <dc:creator>齐大胜</dc:creator>
    <guid>https://blog.hizhiji.com/?post=21</guid>
</item>
<item>
    <title>JavaScript中的IntersectionObserver</title>
    <link>https://blog.hizhiji.com/?post=19</link>
    <description><![CDATA[<p><a href="http://blog.example.hizhiji.com/#/example?post=19" title="demo">demo http://blog.example.hizhiji.com/#/example?post=19</a></p>
<h2>使用IntersectionObserver</h2>
<h3>1. 创建IntersectionObserver实例</h3>
<p>首先，我们需要创建一个IntersectionObserver的实例。可以通过<code>new IntersectionObserver()</code>构造函数来创建一个新的实例，将一个回调函数和一些配置选项作为参数传入。这个回调函数将在被观察的元素进入或离开视口时被调用。</p>
<pre><code class="language-javascript">const intersectionObserver = new IntersectionObserver(entries =&gt; {
  for (const entry of entries) {
    if (entry.isIntersecting) {
      console.log('元素进入视口');
      // 执行进入视口后的操作
    } else {
      console.log('元素离开视口');
      // 执行离开视口后的操作
    }
  }
}, options);</code></pre>
<p>在上面的示例中，我们创建了一个IntersectionObserver实例，并定义了一个回调函数。回调函数中的<code>entries</code>参数是一个数组，包含了所有被观察元素与视口的交叉状态信息。我们可以遍历这个数组来处理每个元素的交叉状态。</p>
<p><code>options</code>是一个可选的配置对象，可以设置一些参数来调整观察器的行为，例如<code>root</code>（视口元素）、<code>rootMargin</code>（视口边界）、<code>threshold</code>（交叉比例阈值）等。可以根据需要进行相应的配置。</p>
<h3>2. 监听元素的交叉状态</h3>
<p>创建IntersectionObserver实例后，我们可以通过调用<code>observe()</code>方法开始监听一个目标元素的交叉状态。</p>
<pre><code class="language-javascript">const targetElement = document.querySelector('.target');
intersectionObserver.observe(targetElement);</code></pre>
<p>在上面的示例中，我们选择一个目标元素，然后调用observe()方法开始监听这个目标元素的交叉状态变化。一旦目标元素进入或离开视口，我们之前定义的回调函数将被触发。</p>
<h3>3.停止监听元素的交叉状态</h3>
<p>如果我们想要停止监听某个元素的交叉状态，可以使用<code>unobserve()</code>方法。</p>
<pre><code class="language-javascript">intersectionObserver.unobserve(targetElement);</code></pre>
<p>上述代码会停止IntersectionObserver实例对目标元素的交叉状态进行监听。</p>
<h3>4.停止监听所有元素的交叉状态</h3>
<p>最后，如果我们想要停止监听所有元素的交叉状态，可以使用disconnect()方法。这将会停止IntersectionObserver实例的所有监听。</p>
<pre><code class="language-javascript">intersectionObserver.disconnect();</code></pre>
<h3>5.兼容性考虑</h3>
<p>可以使用以下代码进行简单的兼容性检测：</p>
<pre><code class="language-javascript">if ('IntersectionObserver' in window) {
  // 浏览器支持IntersectionObserver
} else {
  // 浏览器不支持IntersectionObserver，执行相关兼容处理
}</code></pre>]]></description>
    <pubDate>Sat, 12 Aug 2023 13:02:00 +0800</pubDate>
    <dc:creator>齐大胜</dc:creator>
    <guid>https://blog.hizhiji.com/?post=19</guid>
</item>
<item>
    <title>JavaScript中的ResizeObserver</title>
    <link>https://blog.hizhiji.com/?post=17</link>
    <description><![CDATA[<p><a href="http://blog.example.hizhiji.com/#/example?post=17" title="demo">demo http://blog.example.hizhiji.com/#/example?post=17</a></p>
<h1>JavaScript中的ResizeObserver</h1>
<p>在Web开发中，我们经常需要监测元素的尺寸变化以进行一些相应的操作。在过去，我们可能需要通过监听window的<code>resize</code>事件来实现这个功能，但这种方式在性能和逻辑上都存在一些局限性。</p>
<p>幸运的是，在浏览器中，有一个新的内置API称为ResizeObserver可以更方便地实现这个目标。ResizeObserver是一个观察者模式相关的API，专门用于监听DOM元素的尺寸改变。</p>
<h2>使用方法</h2>
<h3>1.  创建ResizeObserver实例</h3>
<p>首先，我们需要创建一个ResizeObserver的实例。可以通过<code>new ResizeObserver()</code>构造函数来创建一个新的实例，将一个回调函数作为参数传入。这个回调函数将在监听的元素尺寸变化时被调用。</p>
<pre><code class="language-javascript">const resizeObserver = new ResizeObserver(entries =&gt; {
  for (const entry of entries) {
    console.log(`元素的尺寸变化了：${entry.target}`);
    console.log(`新的尺寸：${entry.contentRect.width} x ${entry.contentRect.height}`);
  }
});</code></pre>
<p>在上面的示例中，我们创建了一个ResizeObserver实例，并定义了一个回调函数。回调函数中的entries参数是一个数组，包含了所有触发尺寸变化的元素对象。我们可以遍历这个数组来处理每个元素的尺寸变化。</p>
<h3>2.监听元素尺寸变化</h3>
<p>创建ResizeObserver实例后，我们可以通过调用observe()方法开始监听一个目标元素的尺寸变化。</p>
<pre><code class="language-javascript">const targetElement = document.querySelector('.target');
resizeObserver.observe(targetElement);</code></pre>
<p>在上面的示例中，我们选择一个目标元素，然后调用observe()方法开始监听这个元素的尺寸变化。一旦目标元素的尺寸发生变化，我们之前定义的回调函数将被触发。</p>
<h3>3.停止监听元素尺寸变化</h3>
<p>如果我们想要停止监听某个元素的尺寸变化，可以使用unobserve()方法。</p>
<pre><code class="language-javascript">resizeObserver.unobserve(targetElement);</code></pre>
<p>上述代码会停止ResizeObserver实例对目标元素的尺寸变化进行监听。</p>
<h3>4.停止监听所有元素尺寸变化</h3>
<p>最后，如果我们想要停止监听所有元素的尺寸变化，可以使用disconnect()方法。这将会停止ResizeObserver实例的所有监听。</p>
<pre><code class="language-javascript">resizeObserver.disconnect();</code></pre>
<h3>5.兼容性考虑</h3>
<p>需要注意的是，ResizeObserver是一个较新的API，尚未被所有旧版浏览器支持。为了确保兼容性，你可能需要在使用ResizeObserver之前检查浏览器是否支持它，或者使用第三方的polyfill库进行兼容性适配。</p>
<h3>6.总结</h3>
<p>ResizeObserver是一个非常有用的API，它让我们能够更方便地监听元素尺寸的改变。我们可以通过创建ResizeObserver实例、监听目标元素的尺寸变化、停止监听等操作，实现对元素尺寸变化的观察和处理。希望本文能够帮助你理解和使用JavaScript中的ResizeObserver API。</p>]]></description>
    <pubDate>Fri, 11 Aug 2023 23:18:00 +0800</pubDate>
    <dc:creator>齐大胜</dc:creator>
    <guid>https://blog.hizhiji.com/?post=17</guid>
</item>
<item>
    <title>js防抖和节流</title>
    <link>https://blog.hizhiji.com/?post=16</link>
    <description><![CDATA[<p>在JavaScript中，防抖（debounce）和节流（throttle）是处理函数限制执行频率的两种常见技术。</p>
<ol>
<li>防抖（debounce）：防抖是指在频繁触发某个事件时，只执行最后一次触发的操作。例如，当用户不断调整窗口大小时，我们希望在用户停止调整窗口后再执行相关操作，而不是在每次调整时都执行操作。防抖的目的是减少不必要的操作，提高性能和用户体验。<br />
下面是一个简单的防抖函数的实现示例：</li>
</ol>
<pre><code class="language-javascript">function debounce(func, delay) {
  let timer = null;
  return function(...args) {
    clearTimeout(timer);
    timer = setTimeout(() =&gt; {
      func.apply(this, args);
    }, delay);
  };
}

// 使用示例
const myFunc = debounce(() =&gt; {
  console.log("Hey! I got debounced.");
}, 1000);

myFunc(); // 第一次调用
myFunc(); // 持续触发
// 在1秒后触发时，防抖函数执行输出结果</code></pre>
<ol start="2">
<li>节流（throttle）：节流是指在一定时间内只执行一次操作。例如，当用户不断触发某个事件时，我们可以设定一个固定的时间间隔，在此间隔内只执行一次操作。节流的目的是控制函数的执行频率，防止过于频繁的操作导致性能问题。<br />
下面是一个简单的节流函数的实现示例：</li>
</ol>
<pre><code class="language-javascript">function throttle(func, delay) {
  let timer = null;
  return function(...args) {
    if (!timer) {
      timer = setTimeout(() =&gt; {
        func.apply(this, args);
        timer = null;
      }, delay);
    }
  };
}

// 使用示例
const myFunc = throttle(() =&gt; {
  console.log("Hey! I got throttled.");
}, 1000);

myFunc(); // 第一次调用
myFunc(); // 在1秒内持续触发，但是只在第一次触发时执行输出结果</code></pre>
<h5>总结：防抖和节流都是通过控制函数的执行频率来提高性能和用户体验的技术。防抖只会在最后一次触发事件后执行一次函数，而节流会在固定的时间间隔内执行一次函数。具体使用哪种技术取决于不同的场景和需求。</h5>]]></description>
    <pubDate>Mon, 17 Jul 2023 09:17:00 +0800</pubDate>
    <dc:creator>齐大胜</dc:creator>
    <guid>https://blog.hizhiji.com/?post=16</guid>
</item>
<item>
    <title>vue2 el-table列表组件自动滚动</title>
    <link>https://blog.hizhiji.com/?post=15</link>
    <description><![CDATA[<h3>1. 声明组件</h3>
<p><a href="http://blog.example.hizhiji.com/#/example?post=15" title="demo">demo</a></p>
<blockquote>
<p>./autoTableList.vue</p>
</blockquote>
<pre><code class="language-javascript">&lt;script&gt;
    import { Table } from 'element-ui';
    export default {
            name: 'autoTableList',
            props: {
                     // 播放类型 row | continuous | page
                     // row 每次滚动一行
                     // continuous 连续滚动
                     // page 整页滚动
                     type: {
                             typeof: String,
                             default: 'continuous'
                     },
                     // 是否需要动画过度 
                     // 仅 type='row'|'page'生效 默认为true
                     isAnimation: {
                             typeof: Boolean,
                             default: true
                     },
                    // 滚动速率 （每秒） 仅 type='continuous'生效 默认30 
                     speed: {
                             typeof: [Number, String],
                             default: 100
                     },
                    // 播放结束后等待多少时间重新播放 
                    endDelay: {
                            typeof: [Number, String],
                            default: 2000
                    },
                    // 单次过度时间，单位：毫秒。 默认值为 300毫秒
                    singleDuration: {
                            typeof: [Number, String],
                            default: 300
                    },
                    // 单次等待间隔，单位：毫秒。 默认值为 3000 毫秒
                    singleDelay: {
                            typeof: [Number, String],
                            default: 3000
                    },
                    // 是否允许鼠标hover控制。即鼠标进入停止，移出继续播放 默认为true
                    hoverStep: {
                            typeof: Boolean,
                            default: true
                    },
                    // 鼠标离开时等待多少秒开始播放仅在hoverStep为true是生效 单位：毫秒。 默认值为 3000 毫秒
                    hoverDelay: {
                            typeof: [Number, String],
                            default: 3000
                    },
            },
            extends: Table,
            data() {
                    return {
                            autoTable: {
                                    playTimer: null,
                                    singleDelayTimer: null,
                                    playIndex: 0,
                            }
                    }
            },
            watch: {
                    data: {
                          handler(newVal, oldVal) {
                                this.updateAutoTableVal()
                          },
                          deep: true
                    }
            },
            methods: {
                    updateAutoTableVal() {
                          this.autoTable.bodyWrapper = this.$el.querySelector('.el-table__body-wrapper');
                          this.autoTable.tableBody = this.autoTable.bodyWrapper.querySelectorAll('.el-table__body')[0];
                          this.autoTable.maxHeight = this.autoTable.tableBody.offsetHeight;
                          this.autoTable.clientHeight = this.autoTable.bodyWrapper.clientHeight;
                      },
                     autoPlay() {
                          if (this.autoTable.bodyWrapper.scrollTop &gt;= (this.autoTable.maxHeight - this.autoTable.clientHeight)) {
                                clearInterval(this.autoTable.playTimer)
                                this.autoTable.singleDelayTimer=setTimeout(() =&gt; {
                                      this.autoTable.bodyWrapper.scrollTop = 0;
                                      this.autoTable.playIndex = 0
                                      this.autoTable.playTimer = setInterval(this.autoPlay, this.singleDelay);
                                }, this.endDelay);
                                return
                              } else {
                                    this.autoTable.playIndex++
                                    let end = 0;
                                    if(this.type=='row'){
                                        end = this.autoTable.tableBody.querySelectorAll('.el-table__row')[this.autoTable.playIndex].offsetTop;
                                    }else if(this.type=='page'){
                                        end = this.autoTable.bodyWrapper.offsetHeight* this.autoTable.playIndex
                                    }
                                    if (this.isAnimation) {
                                        this.scrollTo(this.autoTable.bodyWrapper, end, this.singleDuration);
                                    } else {
                                        this.autoTable.bodyWrapper.scrollTop = end;
                                    }
                              }
                        },
                        continuousPlay(){
                              this.autoTable.playTimer=setInterval(()=&gt;{
                                    if (this.autoTable.bodyWrapper.scrollTop &gt;= (this.autoTable.maxHeight - this.autoTable.clientHeight)) {
                                          clearInterval(this.autoTable.playTimer)
                                          this.autoTable.singleDelayTimer = setTimeout(() =&gt; {
                                                this.autoTable.bodyWrapper.scrollTop = 0;
                                                this.continuousPlay();
                                          }, this.endDelay);
                                          return
                                    }else{
                                        this.autoTable.bodyWrapper.scrollTop ++;
                                    }
                              },this.speed)
                        },
                        MouseEnter() {//鼠标移入停止滚动
                            clearInterval(this.autoTable.playTimer);
                        },
                        MouseLeave() {//鼠标离开继续滚动
                            this.autoTable.playTimer = setInterval(this.autoPlay, this.hoverDelay);
                        },
                        easeInOutQuad(t, b, c, d) {
                            t /= d / 2;
                            if (t &lt; 1) return c / 2 * t * t + b;
                            t--;
                            return -c / 2 * (t * (t - 2) - 1) + b;
                        },
                        scrollTo(element, to, duration) {
                              const start = element.scrollTop;
                              const change = to - start;
                              const increment = 20;
                              let currentTime = 0;
                              let that = this;
                              function animateScroll() {
                                    currentTime += increment;
                                    const val = that.easeInOutQuad(currentTime, start, change, duration);
                                    element.scrollTop = val;
                                    if (currentTime &lt; duration) {
                                         setTimeout(animateScroll, increment);
                                        }else{
                                          if (that.$listeners['complete']) {
                                                that.$emit('complete',{
                                                      dataIndex: that.autoTable.playIndex,
                                                      scrollTop:to,
                                                })
                                         }
                                    }
                              }
                             animateScroll();
                        },
                        restart(){
                              clearTimeout(this.autoTable.singleDelayTimer)
                              clearInterval(this.autoTable.playTimer);
                              this.autoTable.playIndex=0;
                              if(this.type=='continuous'){
                                    this.continuousPlay()
                              }else{
                                    this.autoTable.playTimer = setInterval(this.autoPlay, this.singleDelay);
                              }
                        },
            },
            async mounted() {
                    await this.$nextTick();
                    this.updateAutoTableVal()
                    this.restart()
                    if (this.hoverStep) {
                          this.autoTable.bodyWrapper.addEventListener('mouseenter', this.MouseEnter);
                          this.autoTable.bodyWrapper.addEventListener('mouseleave', this.MouseLeave);
                    }
            },
             beforeDestroy() {
                    clearTimeout(this.autoTable.singleDelayTimer)
                    clearInterval(this.autoTable.playTimer);
                    if (this.hoverStep) {
                          this.autoTable.bodyWrapper.removeEventListener('mouseenter', this.MouseEnter);
                          this.autoTable.bodyWrapper.removeEventListener('mouseleave', this.MouseLeave);
                    }
            },
    };
&lt;/script&gt;</code></pre>
<h3>2.组件调用</h3>
<blockquote>
<p>./index.vue</p>
</blockquote>
<pre><code class="language-javascript">&lt;template&gt;
         &lt;autoTableList :data="tableData"  height="300" @complete="complete"&gt;
                 &lt;el-table-column type="index" width="50"&gt;&lt;/el-table-column&gt;
                 &lt;el-table-column prop="date" label="日期" width="180"&gt;&lt;/el-table-column&gt;
                 &lt;el-table-column prop="name" label="姓名" width="180"&gt;&lt;/el-table-column&gt;
                 &lt;el-table-column prop="address" label="地址"&gt;&lt;/el-table-column&gt;
         &lt;/autoTableList&gt;
 &lt;/template&gt;

&lt;script&gt;
        import autoTableList from './autoTableList1'
        export default {
                data() {
                        return {
                                tableData: [
                                        {
                                                date: '2016-05-04',
                                                name: '王小虎',
                                                address: '上海市普陀区金沙江路 1517 弄'
                                        },
                                        {
                                                date: '2016-05-04',
                                                name: '王小虎',
                                                address: '上海市普陀区金沙江路 1517 弄'
                                        },
                                        {
                                                date: '2016-05-04',
                                                name: '王小虎',
                                                address: '上海市普陀区金沙江路 1517 弄'
                                        },
                                        {
                                                date: '2016-05-04',
                                                name: '王小虎',
                                                address: '上海市普陀区金沙江路 1517 弄'
                                        },
                                        {
                                                date: '2016-05-04',
                                                name: '王小虎',
                                                address: '上海市普陀区金沙江路 1517 弄'
                                        },
                                        {
                                                date: '2016-05-02',
                                                name: '王小虎',
                                                address: '上海市普陀区金沙江路 1518 弄'
                                        },
                                        {
                                                date: '2016-05-04',
                                                name: '王小虎',
                                                address: '上海市普陀区金沙江路 1517 弄'
                                        },
                                        {
                                                date: '2016-05-01',
                                                name: '王小虎',
                                                address: '上海市普陀区金沙江路 1519 弄'
                                        },
                                        {
                                                date: '2016-05-01',
                                                name: '王小虎',
                                                address: '上海市普陀区金沙江路 1519 弄'
                                        },
                                        {
                                                date: '2016-05-03',
                                                name: '王小虎',
                                                address: '上海市普陀区金沙江路 1516 弄'
                                        }
                                ],
                        }
                },
                components: {
                        autoTableList
                },
                methods: {
                        complete(data) {
                                console.log(data);
                        },
                }
        }
&lt;/script&gt;</code></pre>
<h3>3.属性说明</h3>
<table>
<thead>
<tr>
<th>参数</th>
<th>默认值</th>
<th>类型</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>type</td>
<td>row</td>
<td>String</td>
<td>滚动类型：<br>row:每次滚动一行<br>continuous:连续滚动 <br>page:整页滚动</td>
</tr>
<tr>
<td>isAnimation</td>
<td>true</td>
<td>Boolean</td>
<td>是否需要动画过渡 （仅 type='row' 和 'page'生效 ）</td>
</tr>
<tr>
<td>speed</td>
<td>30</td>
<td>[Number, String]</td>
<td>滚动速度，单位：毫秒（仅 type='continuous'生效 ）</td>
</tr>
<tr>
<td>endDelay</td>
<td>2000</td>
<td>[Number, String]</td>
<td>播放结束后等待多少时间重新播放</td>
</tr>
<tr>
<td>singleDuration</td>
<td>300</td>
<td>[Number, String]</td>
<td>单次过度时间，单位：毫秒</td>
</tr>
<tr>
<td>singleDelay</td>
<td>3000</td>
<td>[Number, String]</td>
<td>单次等待间隔，单位：毫秒</td>
</tr>
<tr>
<td>hoverStep</td>
<td>true</td>
<td>Boolean</td>
<td>是否允许鼠标hover控制。即鼠标进入停止，移出继续播放</td>
</tr>
<tr>
<td>hoverDelay</td>
<td>3000</td>
<td>[Number, String]</td>
<td>鼠标离开时等待多少秒开始播放仅在hoverStep为true是生效 单位：毫秒</td>
</tr>
</tbody>
</table>
<p>其它参数： <a href="https://element.eleme.io/#/zh-CN/component/table#table-attributes" title="参考 el-table">参考 el-table(Attributes)</a></p>
<h3>4. 方法</h3>
<table>
<thead>
<tr>
<th>方法名</th>
<th>说明</th>
<th>参数</th>
</tr>
</thead>
<tbody>
<tr>
<td>restart</td>
<td>重新开始</td>
<td>—</td>
</tr>
</tbody>
</table>
<p>其它方法：参考 <a href="https://element.eleme.io/#/zh-CN/component/table#table-methods" title="el-table(Events)">el-table(Events)</a></p>
<h3>5. 事件</h3>
<table>
<thead>
<tr>
<th>事件名</th>
<th>说明</th>
<th>参数</th>
</tr>
</thead>
<tbody>
<tr>
<td>complete</td>
<td>单行滚动结束</td>
<td>object</td>
</tr>
</tbody>
</table>
<p>其它事件：参考 <a href="https://element.eleme.io/#/zh-CN/component/table#table-events" title="el-table(Events)">el-table(Events)</a></p>]]></description>
    <pubDate>Tue, 11 Jul 2023 17:23:00 +0800</pubDate>
    <dc:creator>齐大胜</dc:creator>
    <guid>https://blog.hizhiji.com/?post=15</guid>
</item>
<item>
    <title>vue加载进度组件函数式编写</title>
    <link>https://blog.hizhiji.com/?post=14</link>
    <description><![CDATA[<p>一个用来显示下载文件进度的功能，如果使用普通的组件的话，一种是要在需要用的地方引入，另外一种是在最外层引入，通过vuex或eventbus传递参数调用。上面两种方法有一个弊端就是。这个组件在不使用的情况下如果不使用v-if判断都是渲染在页面的，如果使用v-if判断。又需要通过vuex eventbus等注册并监听事件。<br />
<a href="http://blog.example.hizhiji.com/#/example?post=14" title="demo">demo</a></p>
<h3>1. 声明组件</h3>
<blockquote>
<p>./progress.jsx</p>
</blockquote>
<pre><code class="language-javascript">import { createApp} from 'vue';
import styled from 'vue3-styled-component'; //样式化组件,可以在js中写传统的css
import { ThemeProvider } from 'vue3-styled-component'
import { ElProgress } from 'element-plus'
//最外层 
const modal = styled('div')`
      position: absolute;
      left:0;
      top:0;
      background: ${props =&gt; props.background};
      height: ${props =&gt; props.prentIsBody ? '100vh' : '100%'};
      width: ${props =&gt; props.prentIsBody ? '100vw' : '100%'};
      z-index: 99999;
      display: flex;
      align-items: center;
    justify-content: center;
`;
const progressInner = styled('div')`
      display: flex;
      width: ${(props) =&gt; (props.type=='line' ? "100%" : "unset")};
      flex-direction: column;
      align-items: center;
`;
const loadingText = styled('p')`
      color: var(--el-color-primary);
      margin: 3px 0;
      font-size: 14px;
    margin-top: 16px;
`;
// 进度组件
let progressComponent = {
    inheritAttrs:false,
    props: {
        parentIsBody: {
            typeof: Boolean,
            require: true,
            default: true
        },
        percentage: {
            type: [Number, String, Object],
            default: 0
        },
        bottomText: {
            type: [String, Object],
            default: '数据加载中！'
        },
        background:{
            type: String,
            default: 'rgba(0,0,0,.6)'
        }
    },
    render(ctx) {
        const { $props, $emit, $attrs, $slots } = ctx;
         let percentage = (typeof $props.percentage == 'object') ? $props.percentage.value : $props.percentage;
         let bottomText = (typeof $props.bottomText == 'object') ? $props.bottomText.value : $props.bottomText;
        //pug代码
        return &lt;ThemeProvider class='theme-provider'&gt;
            &lt;modal class='qi-progress-modal' parentIsAppendBody={$props.parentIsAppendBody} background={$props.background}&gt;
                &lt;progressInner type={$attrs.type} class='qi-progress-inner'&gt;
                    &lt;ElProgress {...$attrs} percentage={percentage} style={{ width: (!$attrs.type || ['', 'line'].includes($attrs.type))?'95%':'unset'}}&gt;
                        {$slots.default(percentage)}
                    &lt;/ElProgress&gt;
                    &lt;loadingText&gt;{bottomText}&lt;/loadingText&gt;
                &lt;/progressInner&gt;
            &lt;/modal&gt;
        &lt;/ThemeProvider&gt;
    }
}

export const Qiprogress = (props = {}) =&gt; {
    let warpEl = document.createElement('div');
    warpEl.setAttribute('id','qi-progress-warp')
    if(props.el){
        if (props.el instanceof HTMLElement){
            props.el.appendChild(warpEl);
        }else{
            document.querySelector(props.el).appendChild(warpEl);
        }
    }else{
        document.body.appendChild(warpEl);
    }
    let parentIsBody = props.el?false:true;
    delete props.el;
    let background = props.background||null;
    delete props.background;
    let solts = ( props.content)||null;
    delete props.content;
    const app = createApp(&lt;progressComponent&gt;{solts}&lt;/progressComponent&gt;,{
        ...props,
        parentIsBody,
        background
    });
    app.mount(warpEl);
    return{
        destoryed:()=&gt;{
            app.unmount(warpEl);
            warpEl.remove();
        }
    }
}</code></pre>
<h3>2. 调用组件</h3>
<blockquote>
<p>./index.vue</p>
</blockquote>
<pre><code class="language-javascript">import { ref } from 'vue';
import { Qiprogress } from './progress.jsx';

let progressInstance = null;
let progressOptions={
    type: "circle",
    percentage: ref(0),
    bottomText: ref('获取数据中....'),
    background:'rgba(0,0,0,.2)',
     content:(data)=&gt;{
      return h('div', { class: 'tip-text' }, data+'%')
    }
}

progressInstance = Qiprogress(progressOptions)
setInterval(() =&gt; {
    progressOptions.percentage.value++;
    if (progressOptions.percentage.value&gt;100){
        progressInstance.destoryed()
    }
}, 300);</code></pre>
<h3>3.属性说明</h3>
<table>
<thead>
<tr>
<th>参数</th>
<th>默认值</th>
<th>类型</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>percentage</td>
<td>0</td>
<td>[Number, String, Object]</td>
<td>进度，如果是动态变化的需要使用ref()创建对象</td>
</tr>
<tr>
<td>bottomText</td>
<td>数据加载中！</td>
<td>[String, Object]</td>
<td>底部显示的提示文字，如果是动态变化的需要使用ref()创建对象</td>
</tr>
<tr>
<td>background</td>
<td>rgba(0,0,0,.6)</td>
<td>String</td>
<td>背景色</td>
</tr>
<tr>
<td>content</td>
<td>—</td>
<td>[String, Number,Function]</td>
<td>中间显示文本，即<el-progress></el-progress>的插槽 如果需要传入html，使用h函数，Function:(percentage)=&gt;{}</td>
</tr>
</tbody>
</table>
<p>其它参数： <a href="https://element-plus.gitee.io/zh-CN/component/progress.html#%E5%B1%9E%E6%80%A7" title="el-progress">参考 el-progress</a> 如：<br />
<img src="http://blog.hizhiji.com/content/uploadfile/202307/e20e1688982504.png" alt="element-plus/el-progress/参数" title="element-plus/el-progress/参数" /></p>
<h3>4. 方法</h3>
<table>
<thead>
<tr>
<th>方法名</th>
<th>说明</th>
<th>参数</th>
</tr>
</thead>
<tbody>
<tr>
<td>destoryed</td>
<td>卸载组件</td>
<td>—</td>
</tr>
</tbody>
</table>]]></description>
    <pubDate>Mon, 10 Jul 2023 09:50:00 +0800</pubDate>
    <dc:creator>齐大胜</dc:creator>
    <guid>https://blog.hizhiji.com/?post=14</guid>
</item>
<item>
    <title>vue3 element-plus el-sidebarMenu菜单组件递归</title>
    <link>https://blog.hizhiji.com/?post=12</link>
    <description><![CDATA[<p><a href="http://blog.example.hizhiji.com/#/example?post=12" title="demo">demo http://blog.example.hizhiji.com/#/example?post=12</a></p>
<h3>1. 声明组件</h3>
<blockquote>
<p>./sidebarMenu.vue</p>
</blockquote>
<pre><code class="language-javascript">&lt;template&gt;
    &lt;template v-for="(item, index) in routes" :key="index"&gt;
        &lt;el-sub-menu 
            :route="item.path" 
            :index="item.path" 
            v-if="item.children &amp;&amp; item.children.length"&gt;
                &lt;template #title&gt;
                    &lt;el-icon v-if="item.meta?.icon||item.icon"&gt;
                         &lt;component :is="item.meta?.icon||item.icon"&gt;&lt;/component&gt;
                     &lt;/el-icon&gt;
                    &lt;span&gt;{{ item.meta?.name || item.name }}&lt;/span&gt;
              &lt;/template&gt;
              &lt;qi-sidebarMenu :routes="item.children"&gt;&lt;/qi-sidebarMenu&gt;
        &lt;/el-sub-menu&gt;
        &lt;el-menu-item 
            v-else 
            :route="item.path" 
            :index="item.path" 
            @mouseenter="menuItemEnter($event, item)" 
            @mouseleave="menuItemLeave"&gt;
             &lt;el-icon v-if="item.meta?.icon || item.icon"&gt;
                    &lt;component :is="item.meta?.icon||item.icon"&gt;&lt;/component&gt;
             &lt;/el-icon&gt;
             &lt;span&gt;{{ item.meta?.name || item.name }}&lt;/span&gt;
        &lt;/el-menu-item&gt;
     &lt;/template&gt;
     &lt;el-tooltip 
        ref="tooltipRef" 
        :visible="tooltipVisible"  
        placement="right" 
        :popper-options="{
            modifiers: [
                    {
                    name: 'computeStyles',
                    options: {
                        adaptive: false,
                        enabled: false,
                    },
                },
            ],
        }"
         :virtual-ref="menuItemRef" 
         virtual-triggering&gt;
              &lt;template #content&gt;
                    &lt;span&gt; {{ selectItem.meta?.name||selectItem.name}} &lt;/span&gt;
              &lt;/template&gt;
        &lt;/el-tooltip&gt;
&lt;/template&gt;
&lt;script setup name="SidebarMenu"&gt;
const props = defineProps({
      routes: {
            type: Array,
            default: []
      },
      isShowTooltip: {
            typeof: Boolean,
            default: false
      }
})
import { reactive, ref } from 'vue'
const menuItemRef = ref()
const tooltipRef = ref()
let selectItem=reactive({})
const tooltipVisible = ref(false);
const menuItemEnter = (ev,item) =&gt; {
    if(!props.isShowTooltip) return
    selectItem=item
    menuItemRef.value = ev.currentTarget;
    tooltipVisible.value=true;
}
const menuItemLeave = (ev) =&gt; {
    if (!props.isShowTooltip) return
    tooltipVisible.value = false
}
&lt;/script&gt;
&lt;style lang="scss" scoped&gt;
.el-menu-item {
    &amp;.is-active {
        background: #fff;
    }
}
&lt;/style&gt;</code></pre>
<hr />
<h3>2. 组件调用</h3>
<blockquote>
<p>./menu.vue</p>
</blockquote>
<pre><code class="language-javascript">&lt;template&gt;
    &lt;el-menu
        class="menu"
        :class="{ 'menu-colse': isMenuCollapse }" 
        :default-active="route.path"
        :collapse="isMenuCollapse" 
        :unique-opened="true" 
        menu-trigger="click" 
        :router="true"
        background-color="var(--el-color-primary)" 
        text-color="#ffffffa6" 
        active-text-color="var(--el-color-primary)"&gt;
            &lt;qi-sidebarMenu :routes="navRouter" :isShowTooltip="isMenuCollapse"&gt;&lt;/qi-sidebarMenu&gt;
    &lt;/el-menu&gt;
&lt;/template&gt;
&lt;script setup&gt;
import { ref, reactive } from "vue";
const isMenuCollapse = ref(false);
const navRouter = reactive([
    {
        path: '/slect1/',
        name: '选项1',
        icon: 'UserFilled',
        children:[
            {
                path: '/slect1-1/',
                name: '选项1-1',
                icon: 'UserFilled',
                children: [
                    {
                        path: '/slect1-1-1/',
                        name: '选项1-1-1',
                        icon: 'UserFilled',
                    }
                ],
            },
            {
                path: '/slect1-2/',
                name: '选项1-2',
                icon: 'UserFilled',
            }
        ]
    },
    {
        path: '/slect2/',
        name: '选项2',
        icon: 'UserFilled',
    },
    {
        path: '/slect3/',
        name: '选项3',
        icon: 'UserFilled',
    }
])
&lt;/script&gt;
&lt;style lang="scss"&gt;
.menu:not(.el-menu--collapse) {
    width: 200px;
    height: 100%;
    border: none;
}
.menu {
    position: relative;
    flex-shrink: 0;
    .el-menu-item {
        width: 80%;
        &amp;.is-active {
            background: #0c88e8;
            color: #fff;
            font-weight: bold;
        }
        &amp;:hover {
            background-color: none !important;
            color: #fff;
        }
    }
}
&lt;/style&gt;
</code></pre>
<hr />
<h3>3.  渲染结果</h3>
<p><img src="http://blog.hizhiji.com/content/uploadfile/202307/d2561688887205.png" alt="vue3 element-plus el-menu组件递归" title="vue3 element-plus el-menu组件递归" /></p>]]></description>
    <pubDate>Sun, 09 Jul 2023 14:46:00 +0800</pubDate>
    <dc:creator>齐大胜</dc:creator>
    <guid>https://blog.hizhiji.com/?post=12</guid>
</item></channel>
</rss>