惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Google DeepMind News
Google DeepMind News
Stack Overflow Blog
Stack Overflow Blog
Hugging Face - Blog
Hugging Face - Blog
博客园_首页
T
The Blog of Author Tim Ferriss
博客园 - 叶小钗
N
Netflix TechBlog - Medium
腾讯CDC
C
Check Point Blog
P
Proofpoint News Feed
Engineering at Meta
Engineering at Meta
GbyAI
GbyAI
S
SegmentFault 最新的问题
F
Fortinet All Blogs
美团技术团队
U
Unit 42
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
博客园 - 司徒正美
F
Full Disclosure
Recorded Future
Recorded Future
D
DataBreaches.Net
博客园 - 【当耐特】
Martin Fowler
Martin Fowler
J
Java Code Geeks
I
InfoQ
Y
Y Combinator Blog
A
About on SuperTechFans
AI
AI
爱范儿
爱范儿
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Forbes - Security
Forbes - Security
W
WeLiveSecurity
M
MIT News - Artificial intelligence
雷峰网
雷峰网
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Simon Willison's Weblog
Simon Willison's Weblog
Schneier on Security
Schneier on Security
The GitHub Blog
The GitHub Blog
Security Archives - TechRepublic
Security Archives - TechRepublic
aimingoo的专栏
aimingoo的专栏
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
G
GRAHAM CLULEY
Know Your Adversary
Know Your Adversary
Latest news
Latest news
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
D
Docker
Recent Commits to openclaw:main
Recent Commits to openclaw:main
量子位
V2EX - 技术
V2EX - 技术
Project Zero
Project Zero

吃猫的鱼个人博客-个人编程技术

鸿蒙安装 Google Play 应用:microG 方案实践 OpenClaw 不是更聪明的 AI,而是能长期跑起来的 AI - 吃猫的鱼个人博客-个人编程技术 GAIA 测评体系 - 吃猫的鱼个人博客-个人编程技术 MCP(模型上下文协议)是什么 - 吃猫的鱼个人博客-个人编程技术 AI简历开发-新增功能AI对话,深度润色经历、面试拷打 - 吃猫的鱼个人博客-个人编程技术 AI简历-做了个简历生成的项目 - 吃猫的鱼个人博客-个人编程技术 AI简历项目开发-从产品AI功能构思到封装AI润色组件 - 吃猫的鱼个人博客-个人编程技术 前端进阶-事件循环 - 吃猫的鱼个人博客-个人编程技术 AI简历开发 - 记录如何实现纯前端切换简历多模板 - 吃猫的鱼个人博客-个人编程技术
AI简历开发-自定义指令实现图片懒加载 - 吃猫的鱼个人博客-个人编程技术
吃猫的鱼 · 2025-03-05 · via 吃猫的鱼个人博客-个人编程技术

前言

今天,为我的AI简历项目(https://github.com/weidong-repo/AIResume)的图片加载做一个小的优化。

当图片进入用户视图的时候再进行加载,减少用户访问的时候发送请求数量,优化访问体验。

下面开始记录一下整个流程

IntersectionObserver监听元素视图

IntersectionObserver 是一个浏览器 API,主要用于 监听 DOM 元素是否进入视口(或某个容器),适用于 懒加载、无限滚动、曝光统计 等场景。
const observer = new IntersectionObserver(callback, options);
observer.observe(element);  // 观察某个元素
observer.unobserve(element); // 停止观察某个元素
observer.disconnect();  // 断开观察器,释放资源

其中:

options参数

options:配置观察器的参数,例如 触发条件观察区域

const options = {
  root: document.querySelector('.container'),  // 观察区域 (默认是视口)
  rootMargin: '0px 0px -50px 0px', // 观察区域的外边距(类似 CSS margin)
  threshold: [0, 0.5, 1] // 触发回调的可见比例(0=完全不可见,1=完全可见)
};

callback回调函数

callback(entries, observer):当被观察的元素状态发生变化时,会触发 callback 回调函数。

回调函数callback(entries, observer)接收两个参数:

entries:表示当前所有被观察到的元素的信息(比如,在一次滑动页面的时候,有多个图片同时进入视口,这个时候,entries中就包含这些触发的元素)。entries是一个数组,数组中每个entry的一些关键属性:

{
  time: 12345678,          // 触发回调的时间戳
  target: element,         // 被观察的 DOM 元素
  intersectionRatio: 0.5,  // 目标元素的可见比例 (0 ~ 1)
  isIntersecting: true,    // 是否进入观察区域 (true=进入, false=离开)
  boundingClientRect: {},  // 目标元素的尺寸 & 位置
  intersectionRect: {},    // 目标元素的可见部分信息
  rootBounds: {},          // 根容器的尺寸 & 位置
}

observer:当前 IntersectionObserver 实例。

vue3中directive自定义指令

是什么?

在 Vue 3 中,自定义指令允许你直接操作 DOM 元素,类似于原生的 v-ifv-for,但你可以为它们创建自定义行为。Vue 3 中的指令系统进行了优化,支持全局和局部注册,可以与 TypeScript 很好地配合使用。

基本使用(快速开始)

在main.ts中全局注册自定义指令

import { createApp } from 'vue';
import App from './App.vue';

const app = createApp(App);

app.directive('focus', {
  mounted(el) {
    el.focus(); // 在元素挂载到 DOM 后自动获取焦点
  }
});

app.mount('#app');
<template>
  <input v-focus />
</template>

生命周期函数钩子

自定义指令的生命周期函数钩子:

beforeMount:指令在绑定元素的父组件挂载之前调用。

mounted:指令在元素被挂载到 DOM 后调用。

beforeUpdate:指令在所在组件的 VNode 更新之前调用。

updated:指令在所在组件的 VNode 更新之后调用。

beforeUnmount:指令在元素从 DOM 中移除之前调用。

unmounted:指令在元素从 DOM 中移除后调用。

传参:

每个钩子函数都会传入不同的参数:

  • el:指令所绑定的元素,通常是一个 DOM 元素。
  • binding:一个对象,包含指令的信息,如参数、值等。
  • vnode:虚拟节点,包含了 Vue 内部的 VNode 数据。
  • prevVnode:前一个虚拟节点,只有在更新过程中才可用。

一个示例:

app.directive('tooltip', {
  mounted(el, binding) {
    const tooltipText = binding.value || '默认提示';  // 使用传入的值
    const tooltipPosition = binding.arg || 'top';  // 使用动态参数,默认值为 'top'
    // 创建 tooltip 元素
    const tooltip = document.createElement('span');
    tooltip.innerText = tooltipText;
    tooltip.style.position = 'absolute';
    tooltip.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
    tooltip.style.color = 'white';
    tooltip.style.padding = '5px';
    tooltip.style.borderRadius = '4px';
    tooltip.style.visibility = 'hidden';
    // 根据修饰符调整显示位置
    if (binding.modifiers.bottom) {
      tooltip.style.top = '100%';
    } else {
      tooltip.style.bottom = '100%';
    }
    // 将 tooltip 插入到目标元素
    el.style.position = 'relative';
    el.appendChild(tooltip);
    // 显示 tooltip
    el.addEventListener('mouseenter', () => {
      tooltip.style.visibility = 'visible';
    });
    // 隐藏 tooltip
    el.addEventListener('mouseleave', () => {
      tooltip.style.visibility = 'hidden';
    });
  },
});

Vue3中实现图片懒加载的自定义指令

首先是lazyLoad.ts,在这里定义了mounted生命周期,对元素指令绑定的元素进行钩子监听。

并且定义了一个回调函数loadImage,并且在IntersectionObserver进行监控触发回调,当图片即将进入视口的时候,触发回调,把图片url替换回图片原本的地址(一开始默认是loading图)

import myImage from '@/assets/imgs/loading.gif';
export default {
  mounted(el: HTMLImageElement, binding: any) {
    el.src = myImage
    const loadImage = (entries: IntersectionObserverEntry[], observer: IntersectionObserver) => {
      const entry = entries[0];
      if (entry.isIntersecting) {
        el.src = binding.value;
        observer.unobserve(el);
      }
    };
    const observer = new IntersectionObserver(loadImage, { root: null, threshold: 0.1 });
    observer.observe(el);
    (el as any).__lazyObserver__ = observer; // 绑定 observer 到元素
  },
  unmounted(el: HTMLImageElement) {
    if ((el as any).__lazyObserver__) {
      (el as any).__lazyObserver__.disconnect();
    }
  }
};

main.ts中使用,加入下面两行

import lazyLoad from './directives/lazyLoad';

app.directive('lazy', lazyLoad);

组件中直接使用:

<img v-lazy="getTemplateImage(template)" :alt="template.name" class="template-image" />