自定义指令

除了核心功能默认内置的指令 , Vue 也允许注册自定义指令。
功能: 对页面展示效果功能的扩展 ==> 自定义指令 主要完成的是 页面 DOM 元素
的操作
MVVM 设计模式核心思想:
简化开发者对于 DOM 的操作, vue 基本实现了
相关操作,基本上不需要完成 DOM 的操作
vue 简化开发者对于 DOM 的操作,实际上就是 将 DOM 的操作封装成插值表
达式或者指令
自定义指令是 vue 提供给开发者 对 DOM 操作的接口(规范化的方法)

1.局部指令定义

范围:仅限于定义时关联的 vue 实例的容器中使用
定义:
key string ):指令名称 ,定义完成后,页面使用需 v - 前缀调用指令
Vue . createApp ({
directives :{ key [ string ]: value [ Fuction ] }
}) 如果名称为驼峰命名方式 (例如 imgLazy , 页面使用时需要转换为 连
字符 ( v-img-lazy
value Function ): function( el binding Vnode,preVnode ){}
令执行函数
el : 调用指令的当前 DOM 对象
binding : 是一个对象 ,对象中包含了指令构成 参数、修饰符、取值
……
name: 指令名称
rawName: 指令调用时的表达式
expression: 指令取值, = 右侧的取值表达式
arg: 指令参数 , : 后定义的参数名称
oldArg: 是指令 :
后定义的 旧的参数名称
value: 指令 = 右侧 表达式的计算结果
oldValue: 指令绑定变量发生变化后,调用该方法时 ,存储的 上次
的结果值
modifiers: 指令修饰符,
. 后定义的修饰符名称
modifiers Object{ key:value } 类型数据
key 为修饰符名称, value 取值 为 true
true 作为 value 表示当前指令被启用
对象中没有修饰符 key , 表示指令不启用
当没有修饰符时,表示该对象为空对象
Vnode :指令更新后的新虚拟 DOM
preVnode :指令更新前的旧虚拟 DOM
使用: < 标签 v - 自定义指令名 [: 参数 ][. 修饰符 . 修饰符 ……][= 取值 ] ></ 标签 >
例:鼠标左键的拖拽
​​​​​​​
.box{
            background-color: brown;
            width: 100px;
            height: 100px;
            border-radius: 6px;
            box-shadow: 0 0 6px #999;
            cursor: pointer;
        }
    </style>
</head>
<body>
    <div id="app">
        <p>app</p>
        <div class="box" v-drag></div>
    </div>
    <!-- <hr> -->


    <script type="module">
        import { createApp } from "../assets/vue/3.0/vue.esm-browser.js";

        const app = createApp({
            data(){
                return {

                }
            }
        })
        // vue3的全局概念不是在同一个环境就都属于vue语法的全局环境;
        // vue3的全局指的是由一个createApp作为全局启动代码,完成多个vue实例的加载,此时对于这些vue实例而言全局环境为创建app
        // 通过应用程序对象的 directive 方法完成一个自定义指令的描述
        // app.directive(指令名,指令的回调执行方法)
        app.directive("drag",function(el){
            el.style.position="absolute";
            el.onmousedown = function(event){
                if(event.button!=0) return;
                let ox = event.offsetX;
                let oy = event.offsetY;
                el.onmousemove = function(event){
                    let px = event.pageX;
                    let py = event.pageY;
                    el.style.top=(py-oy)+"px";
                    el.style.left=(px-ox)+"px";
                }
            }
            el.onmouseup = function(){
                el.onmousemove = null;
            }
        })
        app.mount("#app")


    </script>

2.全局指令定义

范围:可以在 vue 的 任意对象的容器中使用
语法: app.directive( id,definition )
id=name : 定义指令名称
definition : 指令的处理函数, function( el binding
newVnode,oldVnode ){}
参数参考 局部指令
定义: 全局指令定义必须在使用之前 app . directive ( "lazy" , function ( el binding Vnode , preVnode ){
})
 <style>
        body{
            padding-bottom: 800px;
        }
        .box{
            background-color: brown;
            width: 100px;
            height: 100px;
            border-radius: 6px;
            box-shadow: 0 0 6px #999;
            cursor: pointer;
        }
    </style>
</head>
<body>
    <div id="app">
        <p v-test>调用自定义指令test</p>
        <p v-test>调用自定义指令test</p>
        <p v-test>调用自定义指令test</p>
        <br>
        <div class="box" v-drag></div>
    </div>

    <!-- <div id="root">
        <p v-test>调用自定义指令test</p>
    </div> -->

    <script type="module">
        import { createApp } from "../assets/vue/3.0/vue.esm-browser.js";
        createApp({
            data(){
                return {

                }
            },
            // 通过属性配置方式定义多个局部指令
            //      局部指令只能在当前定义的vue实例对应的容器中
            directives:{
                // key  (String)  定义指令名称  => 在模板中可以通过 v-指令名称 方式进行调用
                // value   (Function)  定义指令的执行回调
                test:function(){
                    console.log("自定义局部指令test");
                },
                // el 参数为自定义指令在页面调用时所对应的vnode生成的DOM元素
                drag(el){
                    // console.log("自定义局部指令drag");
                    el.style.position="absolute";
                    // el.style.top="0px";
                    // el.style.left="0px";
                    // 绑定鼠标左键按下的事件
                    el.onmousedown = function(event){
                        // console.log("鼠标被点击:",event)
                        if(event.button!=0) return;
                        let ox = event.offsetX;
                        let oy = event.offsetY;

                        // 在按下的事件中再次绑定鼠标移动事件
                        el.onmousemove = function(event){
                            // console.log("移动:",event);
                            let px = event.pageX;
                            let py = event.pageY;
                            // console.log(px,ox);
                            // console.log(py,oy);
                            el.style.top=(py-oy)+"px";
                            el.style.left=(px-ox)+"px";
                        }
                    }
                    // 绑定鼠标左键弹起的事件
                    el.onmouseup = function(){
                        // 在弹起时移除鼠标移动事件
                        el.onmousemove = null;
                    }
                }
            }
        }).mount("#app")
Vue自定义指令=>局部指令定义及全局指令定义-LMLPHP

 

图片的预加载

方法一

 <style>
        body{
            padding-bottom: 800px;
        }
        img{
            width: 400px;
            height: 400px;
            border: 1px solid #dedede;
        }
    </style>
</head>
<body>
    <div id="app">
        <img v-preload=" img " style="object-fit:cover">
    </div>

    <script>
        let arr = document.querySelectorAll("img[v-preload]");
        arr.forEach((dom)=>{
            dom.src = "../assets/img/loading.gif"
            dom.tempStyle = dom.style.cssText;
            dom.style.objectFit="scale-down";
            dom.style.objectPosition="center";
        })
    </script>
    <script type="module">
        import { createApp } from "../assets/vue/3.0/vue.esm-browser.js";
        const app = createApp({
            data(){
                return {
                    img:"https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic.jj20.com%2Fup%2Fallimg%2F1112%2F11251Q24100%2F1Q125124100-5.jpg&refer=http%3A%2F%2Fpic.jj20.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1665718104&t=91a061cac8657bf9b1bc3ead30017584"
                }
            }
        })

        app.directive("preload", function(el,binding){
            if(binding.value==binding.oldValue) return;
            // el.src = "../assets/img/loading.gif"
            // let tempStyle = el.style.cssText;
            // el.style.objectFit="scale-down";
            // el.style.objectPosition="center";

            let imgDom = new Image();
            imgDom.src = binding.value;
            imgDom.onload = function(){
                // console.log("图片加载完成");
                el.src = binding.value;
                el.style.cssText = el.tempStyle;
            }

        })

        app.mount("#app")
    </script>
</body>

方法二

<style>
        body{
            padding-bottom: 800px;
        }
        img{
            width: 400px;
            height: 400px;
            border: 1px solid #dedede;
        }
        /* 骨架屏 loading */
        img[v-preload],.preload{
            background-color: #dedede;
            background-image: linear-gradient(to right, rgba(255,255,255,0) 0%,rgba(255,255,255,0.8) 10%,rgba(255,255,255,0) 20%);
            background-size: 230% 100%;
            background-position: 30% 0%;
            animation: skeleton 1.5s infinite;
        }
        @keyframes skeleton {
            0%{
                background-position: 30% 0%;
            }
            100%{
                background-position: -100% 0%;
            }
        }
    </style>
</head>
<body>
    <div id="app">
        <img v-preload=" img " style="object-fit:cover">
    </div>
    <script type="module">
        import { createApp } from "../assets/vue/3.0/vue.esm-browser.js";
        const app = createApp({
            data(){
                return {
                    // 此处代码不能换行
                    img:"https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic.jj20.com%2Fup%2Fallimg%2F1112%2F11251Q24100%2F1Q125124100-5.jpg&refer=http%3A%2F%2Fpic.jj20.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1665718104&t=91a061cac8657bf9b1bc3ead30017584"
                }
            }
        })

        app.directive("preload", function(el,binding){
            if(binding.value==binding.oldValue) return;
            el.classList.add("preload")
            let imgDom = new Image();
            imgDom.src = binding.value;
            imgDom.onload = function(){
                // console.log("图片加载完成");
                el.src = binding.value;
                el.classList.remove("preload")
            }
        })
        app.mount("#app")
    </script>
09-15 12:42