# 自定义指令封装

# v-once 指令

只渲染一次元素和组件,并且跳过以后的更新。

在随后的重新渲染,元素/组件及其所有子项将被当作静态内容并跳过渲染。这可以用来优化更新时的性能。


<script setup>
  import {ref} from "vue";

  const count = ref(0);

  setInterval(() => {
    count.value++;
  }, 1000);
</script>

<template>
  <span v-once>使它从不更新: {{ count }}</span>
</template>

# 使用自定义指令实现图片懒加载

自定义指令一般位于 main.js 中进行配置

//获取app实例
import {createApp} from "vue";

const app = createApp(App);
app.directive("img-lazy", {
    //mounted 自定义指令绑定元素挂载完毕时触发
    mounted(el, binding) {
        //el 组件实例 自定义指令绑定的那个元素 img
        //binding: 等于号指令表达式后面的值
        console.log("自定义指令", el, binding.value);
        // 使用vueuse提供的useIntersectionObserver方法判断当前绑定自定义指令的元素是否进入视口
        useIntersectionObserver(el, ([{isIntersecting}]) => {
            //isIntersecting 元素是否进入视口 布尔值
            console.log("元素进入视口", isIntersecting);
            //img标签在绑定src属性时会立即发送图片请求
            if (isIntersecting) el.src = binding.value;
        });
    },
});

# 封装图片懒加载插件

  • 在上述操作中,图片懒加载代码放在 main.js 入口函数中,而入口函数只是负责页面初始化,不因该包含太多逻辑代码
  • 可以通过插件的方法把懒加载指令封装为插件,main.js 入口函数只需要负责注册插件即可
  1. 项目 src 文件夹下新建directives插件文件夹 在此文件夹中存放插件
  2. 编写懒加载插件
//引入vueuse中监听元素进入视口的方法
import {useIntersectionObserver} from "@vueuse/core";
//定义懒加载插件
export const lazyPlugin = {
    //install函数 固定写法 参数为app实例
    install(app) {
        //懒加载指令逻辑
        //自定义指令
        app.directive("img-lazy", {
            mounted(el, binding) {
                //el 组件实例 自定义指令绑定的那个元素 img
                //binding: 等于号指令表达式后面的值

                const {stop} = useIntersectionObserver(el, ([{isIntersecting}]) => {
                    console.log("元素进入视口", isIntersecting);
                    if (isIntersecting) {
                        el.src = binding.value;
                        //图片加载完毕后停止监听
                        stop();
                    }
                });
            },
        });
    },
};
  1. 在 main.js 中注册插件
//引入懒加载指令并注册
import {lazyPlugin} from "@/directives";

app.use(lazyPlugin);
  1. 自定义指令的使用
<img v-img-lazy="item.picture" alt=""/>

# 监听元素宽高变化

//监听元素宽高变化
//创建元素数组
const map = new WeakMap()
//创建观察者
const ob = new ResizeObserver((entries) => {
    for (const entry of entries) {
        const handle = map.get(entry.target)
        const box = entry.borderBoxSize[0]
        if (handle) handle({width: box.inlineSize, height: box.blockSize})
    }
})

export const resizePlugin = {
    install(app) {
        app.directive('resize', {
            mounted(el, binding) {
                //监听元素尺寸变化
                ob.observe(el)
                map.set(el, binding.value)
            },
            unmounted(el) {
                ob.unobserve(el)
            }
        })
    }
}

# 使用


<template>
  <div v-resize='sizeChange'>监听元素尺寸变化</div>
</template>
<script lang="ts" setup>
  //元素宽高变化的处理函数
  const sizeChange = (value) => {
    console.log('元素宽高变化', value)
  }
</script>
上次更新: 3/1/2024, 3:03:16 PM