我的编程空间,编程开发者的网络收藏夹
学习永远不晚

怎么写一个Vue3的自定义指令

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

怎么写一个Vue3的自定义指令

本篇内容主要讲解“怎么写一个Vue3的自定义指令”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“怎么写一个Vue3的自定义指令”吧!

背景

众所周知,Vue.js 的核心思想是数据驱动 + 组件化,通常我们开发页面的过程就是在编写一些组件,并且通过修改数据的方式来驱动组件的重新渲染。在这个过程中,我们不需要去手动操作 DOM。

然而在有些场景下,我们还是避免不了要操作 DOM。由于 Vue.js 框架接管了 DOM 元素的创建和更新的过程,因此它可以在 DOM 元素的生命周期内注入用户的代码,于是 Vue.js 设计并提供了自定义指令,允许用户进行一些底层的 DOM 操作。

举个实际的例子——图片懒加载。图片懒加载是一种常见性能优化的方式,由于它只去加载可视区域图片,能减少很多不必要的请求,极大的提升用户体验。

而图片懒加载的实现原理也非常简单,在图片没进入可视区域的时候,我们只需要让 img 标签的 class="lazy" data-src 属性指向一张默认图片,在它进入可视区后,再替换它的 class="lazy" data-src 指向真实图片地址即可。

如果我们想在 Vue.js 的项目中实现图片懒加载,那么用自定义指令就再合适不过了,那么接下来就让我手把手带你用 Vue3 去实现一个图片懒加载的自定义指令 v-lazy。

插件

为了让这个指令方便地给多个项目使用,我们把它做成一个插件:

const lazyPlugin = {  install (app, options) {    app.directive('lazy', {      // 指令对象    })  }}export default lazyPlugin

然后在项目中引用它:

import { createApp } from 'vue'import App from './App.vue'import lazyPlugin from 'vue3-lazy'createApp(App).use(lazyPlugin, {  // 添加一些配置参数})

通常一个 Vue3 的插件会暴露 install 函数,当 app 实例 use 该插件时,就会执行该函数。在 install 函数内部,通过 app.directive 去注册一个全局指令,这样就可以在组件中使用它们了。

指令的实现

接下来我们要做的就是实现该指令对象,一个指令定义对象可以提供多个钩子函数,比如 mountedupdatedunmounted 等,我们可以在合适的钩子函数中编写相应的代码来实现需求。

在编写代码前,我们不妨思考一下实现图片懒加载的几个关键步骤。

图片的管理:

管理图片的 DOM、真实的 class="lazy" data-src、预加载的 url、加载的状态以及图片的加载。

可视区的判断:

判断图片是否进入可视区域。

关于图片的管理,我们设计了 ImageManager 类:

const State = {  loading: 0,  loaded: 1,  error: 2}export class ImageManager {  constructor(options) {    this.el = options.el    this.class="lazy" data-src = options.class="lazy" data-src    this.state = State.loading    this.loading = options.loading    this.error = options.error        this.render(this.loading)  }  render() {    this.el.setAttribute('class="lazy" data-src', class="lazy" data-src)  }  load(next) {    if (this.state > State.loading) {      return    }    this.renderclass="lazy" data-src(next)  }  renderclass="lazy" data-src(next) {    loadImage(this.class="lazy" data-src).then(() => {      this.state = State.loaded      this.render(this.class="lazy" data-src)      next && next()    }).catch((e) => {      this.state = State.error      this.render(this.error)      console.warn(`load failed with class="lazy" data-src image(${this.class="lazy" data-src}) and the error msg is ${e.message}`)      next && next()    })  }}export default function loadImage (class="lazy" data-src) {  return new Promise((resolve, reject) => {    const image = new Image()    image.onload = function () {      resolve()      dispose()    }    image.onerror = function (e) {      reject(e)      dispose()    }    image.class="lazy" data-src = class="lazy" data-src    function dispose () {      image.onload = image.onerror = null    }  })}

首先,对于图片而言,它有三种状态,加载中、加载完成和加载失败。

ImageManager 实例化的时候,除了初始化一些数据,还会把它对应的 img 标签的 class="lazy" data-src 执行加载中的图片 loading,这就相当于默认加载的图片。

当执行 ImageManager 对象的 load 方法时,就会判断图片的状态,如果仍然在加载中,则去加载它的真实 class="lazy" data-src,这里用到了 loadImage 图片预加载技术实现去请求 class="lazy" data-src 图片,成功后再替换 img 标签的 class="lazy" data-src,并修改状态,这样就完成了图片真实地址的加载。

有了图片管理器,接下来我们就需要实现可视区的判断以及对多个图片的管理器的管理,设计 Lazy 类:

const DEFAULT_URL = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'export default class Lazy {  constructor(options) {    this.managerQueue = []    this.initIntersectionObserver()        this.loading = options.loading || DEFAULT_URL    this.error = options.error || DEFAULT_URL  }  add(el, binding) {    const class="lazy" data-src = binding.value        const manager = new ImageManager({      el,      class="lazy" data-src,      loading: this.loading,      error: this.error    })        this.managerQueue.push(manager)        this.observer.observe(el)  }  initIntersectionObserver() {    this.observer = new IntersectionObserver((entries) => {      entries.forEach((entry) => {        if (entry.isIntersecting) {          const manager = this.managerQueue.find((manager) => {            return manager.el === entry.target          })          if (manager) {            if (manager.state === State.loaded) {              this.removeManager(manager)              return            }            manager.load()          }        }      })    }, {      rootMargin: '0px',      threshold: 0    })  }  removeManager(manager) {    const index = this.managerQueue.indexOf(manager)    if (index > -1) {      this.managerQueue.splice(index, 1)    }    if (this.observer) {      this.observer.unobserve(manager.el)    }  }}const lazyPlugin = {  install (app, options) {    const lazy = new Lazy(options)    app.directive('lazy', {      mounted: lazy.add.bind(lazy)    })  }}

这样每当图片元素绑定 v-lazy 指令,且在 mounted 钩子函数执行的时候,就会执行 Lazy 对象的 add 方法,其中第一个参数 el 对应的就是图片对应的 DOM 元素对象,第二个参数 binding 就是指令对象绑定的值,比如:

<img class="avatar" v-lazy="item.pic">

其中 item.pic 对应的就是指令绑定的值,因此通过binding.value 就可以获取到图片的真实地址。

有了图片的 DOM 元素对象以及真实图片地址后,就可以根据它们创建一个图片管理器对象,并添加到 managerQueue 中,同时对该图片 DOM 元素进行可视区的观察。

而对于图片进入可视区的判断,主要利用了 IntersectionObserver API,它对应的回调函数的参数 entries,是 IntersectionObserverEntry 对象数组。当观测的元素可见比例超过指定阈值时,就会执行该回调函数,对 entries 进行遍历,拿到每一个 entry,然后判断 entry.isIntersecting 是否为 true,如果是则说明 entry 对象对应的 DOM 元素进入了可视区。

然后就根据 DOM 元素的比对从 managerQueue 中找到对应的 manager,并且判断它对应图片的加载状态。

如果图片是加载中的状态,则此时执行manager.load 函数去完成真实图片的加载;如果是已加载状态,则直接从 managerQueue 中移除其对应的管理器,并且停止对图片 DOM 元素的观察。

目前,我们实现了图片元素挂载到页面后,延时加载的一系列处理。不过,当元素从页面卸载后,也需要执行一些清理的操作:

export default class Lazy {  remove(el) {    const manager = this.managerQueue.find((manager) => {      return manager.el === el    })    if (manager) {      this.removeManager(manager)    }  }}const lazyPlugin = {  install (app, options) {    const lazy = new Lazy(options)    app.directive('lazy', {      mounted: lazy.add.bind(lazy),      remove: lazy.remove.bind(lazy)    })  }}

当元素被卸载后,其对应的图片管理器也会从 managerQueue 中被移除,并且停止对图片 DOM 元素的观察。

此外,如果动态修改了 v-lazy 指令绑定的值,也就是真实图片的请求地址,那么指令内部也应该做对应的修改:

export default class ImageManager {  update (class="lazy" data-src) {    const currentclass="lazy" data-src = this.class="lazy" data-src    if (class="lazy" data-src !== currentclass="lazy" data-src) {      this.class="lazy" data-src = class="lazy" data-src      this.state = State.loading    }  }  }export default class Lazy {  update (el, binding) {    const class="lazy" data-src = binding.value    const manager = this.managerQueue.find((manager) => {      return manager.el === el    })    if (manager) {      manager.update(class="lazy" data-src)    }  }    }const lazyPlugin = {  install (app, options) {    const lazy = new Lazy(options)    app.directive('lazy', {      mounted: lazy.add.bind(lazy),      remove: lazy.remove.bind(lazy),      update: lazy.update.bind(lazy)    })  }}

至此,我们已经实现了一个简单的图片懒加载指令,在这个基础上,还能做一些优化吗?

指令的优化
在实现图片的真实 url 的加载过程中,我们使用了 loadImage 做图片预加载,那么显然对于相同 url 的多张图片,预加载只需要做一次即可。

为了实现上述需求,我们可以在 Lazy 模块内部创建一个缓存 cache:

export default class Lazy {  constructor(options) {    // ...    this.cache = new Set()  }}

然后在创建 ImageManager 实例的时候,把该缓存传入:

const manager = new ImageManager({  el,  class="lazy" data-src,  loading: this.loading,  error: this.error,  cache: this.cache})

然后对 ImageManager 做如下修改:

export default class ImageManager {  load(next) {    if (this.state > State.loading) {      return    }    if (this.cache.has(this.class="lazy" data-src)) {      this.state = State.loaded      this.render(this.class="lazy" data-src)      return    }    this.renderclass="lazy" data-src(next)  }  renderclass="lazy" data-src(next) {    loadImage(this.class="lazy" data-src).then(() => {      this.state = State.loaded      this.render(this.class="lazy" data-src)      next && next()    }).catch((e) => {      this.state = State.error      this.cache.add(this.class="lazy" data-src)      this.render(this.error)      console.warn(`load failed with class="lazy" data-src image(${this.class="lazy" data-src}) and the error msg is ${e.message}`)      next && next()    })    }}

在每次执行 load 前从缓存中判断是否已存在,然后在执行 loadImage 预加载图片成功后更新缓存。

通过这种空间换时间的手段,就避免了一些重复的 url 请求,达到了优化性能的目的。

到此,相信大家对“怎么写一个Vue3的自定义指令”有了更深的了解,不妨来实际操作一番吧!这里是编程网网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

怎么写一个Vue3的自定义指令

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

怎么写一个Vue3的自定义指令

本篇内容主要讲解“怎么写一个Vue3的自定义指令”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“怎么写一个Vue3的自定义指令”吧!背景众所周知,Vue.js 的核心思想是数据驱动 + 组件化,通
2023-06-26

Vue3怎么编写自定义指令插件

今天小编给大家分享一下Vue3怎么编写自定义指令插件的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。编写自定义插件// src
2023-07-02

怎么在Vue3中实现自定义指令

这篇文章主要介绍“怎么在Vue3中实现自定义指令”,在日常操作中,相信很多人在怎么在Vue3中实现自定义指令问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”怎么在Vue3中实现自定义指令”的疑惑有所帮助!接下来
2023-07-02

vue3自定义指令的方法是什么

这篇文章主要介绍“vue3自定义指令的方法是什么”,在日常操作中,相信很多人在vue3自定义指令的方法是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”vue3自定义指令的方法是什么”的疑惑有所帮助!接下来
2023-07-04

vue3自定义指令方法是什么

这篇文章主要讲解了“vue3自定义指令方法是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“vue3自定义指令方法是什么”吧!一、注册自定义指令以下实例都是实现一个输入框自动获取焦点的自定
2023-06-21

Vue3中的setup与自定义指令怎么使用

setup语法糖最大好处就是所有声明部分皆可直接使用,无需return出去注意:部分功能还不完善,如:name、render还需要单独加入script标签按compositionAPI方式编写//setup下还可以附加setup语法糖独有import{ref,reactive,toRefs}from&#39;vue&#39;consta=1;constnum=ref(99)//基本数据类型constuser=reactive({//引用数据类型age:11})//解构能获取响应式
2023-05-14

vue3中怎么自定义指令实现按钮防抖

这篇“vue3中怎么自定义指令实现按钮防抖”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“vue3中怎么自定义指令实现按钮防抖
2023-07-05

C++模拟LinuxShell编写一个自定义命令

这篇文章主要介绍了C++如何模拟LinuxShell实现编写一个自定义命令,本文通过实例代码进行命令行解析,代码简单易懂,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
2022-12-08

Vue3中的setup与自定义指令如何使用

本篇内容主要讲解“Vue3中的setup与自定义指令如何使用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Vue3中的setup与自定义指令如何使用”吧!setup语法糖最大好处就是所有声明部分
2023-07-06

vue中自定义指令怎么用

小编给大家分享一下vue中自定义指令怎么用,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!1、v-drag需求:鼠标拖动元素思路:元素偏移量 = 鼠标滑动后的坐标 - 鼠标初始点击元素时的坐标 + 初始点击时元素距离可视区域
2023-06-22

vue3+TS实现自定义指令长按触发绑定的函数

这篇文章主要介绍了vue3+TS实现自定义指令长按触发绑定的函数,文中给大家分享了编写自定义指令时遇到的几个难点,本文结合实例代码给大家介绍的非常详细,需要的朋友可以参考下
2022-12-29

Vue中的自定义指令怎么实现

今天小编给大家分享一下Vue中的自定义指令怎么实现的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。试炼:实现v-mymodel
2023-07-04

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录