Vue.js进阶技巧Day04: directive自定义指令、实现骨架屏、自定义右键菜单

it2023-03-22  83

目录

0x00 局部注册

0x01 全局注册

                   0x02 案例:自定义右键菜单


0x00 局部注册

局部注册的 自定义指令 只能在该vue文件中使用,全局注册的自定义指令在任何文件中都可以使用

钩子函数:

bind: 只调用一次, 指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置inserted: 被绑定元素插入到DOM树时调用update: 绑定元素的值 更新时调用,因为可能多个 元素都绑定了该自定义指令,所以只要有一个元素的值发生了更新,update就会被触发很多次。componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。unbind:只调用一次,指令与元素解绑时调用。

DEMO: 当input框插入DOM树时,自动获取焦点

<template> <div> <!-- 基础 --> <input type="text" class='form-control' v-focus> </div> </template> <script> export default{ directives:{ //自定义指令名称 focus:{ //第一次绑定时 //el 绑定的dom节点,binding 绑定的信息 bind(el,binding,vNode){ }, //被绑定元素 插入到父节点时 inserted(el,binding){ el.focus() }, //节点更新的时候 update(el,binding){ } } } } </script> <style> </style>

demo2:

<template> <div> <!-- 列表更新 --> <ul class='list-group'> <li class='list-group-item' v-for='(item,index) in list' :key='index' v-list='item' @click='deleteItem(index)'>{{item}}</li> </ul> </div> </template> <script> export default{ data(){ return { list:[1,2,3,4,5,6,7,8,9] } }, directives:{ //自定义指令名称 list:{ //第一次绑定时 //el 绑定的dom节点,binding 绑定的信息 bind(el,binding,vNode){ console.log('----bind-----') }, //被绑定元素 插入到父节点时 inserted(el,binding){ console.log('----inserted-----') }, //节点更新的时候,如果有多个节点都绑定了该自定义指令,只要有一个节点更新了,每个节点绑定的指令的update都会被触发一次,所以需要判断oldValue 和 newValue是否相等来 执行相应的代码 update(el,binding){ if(binding.oldValue !== binding.value){ console.log('----update----') } } } }, methods:{ setItem(index){ // this.list[index] = Math.floor(Math.random()*10) //这种方式不能实现更新 let random_num = Math.floor(Math.random()*10) this.$set(this.list,index,random_num) }, deleteItem(index){ //如果删除了第一个,那么其余li都会被重新渲染一遍,update会被触发8次,且oldValue 和 newValue不相等 this.list.splice(index,1) } } } </script> <style> </style>

0x01 全局注册

案例:通过自定义指令实现骨架屏

思路:当指令绑定到元素上时,给元素初始化背景颜色 和字体颜色

这时后端的数据还没有接收到,当接收到后端传来的数据时,

元素的值就会发生变化,进而会触发 update方法,我们在update方法中 将给元素初始化的背景颜色 和字体颜色去除掉。

<template> <div> <div class='row'> <div class='col-4 mb-3' v-for="(item,index) in imgs" :key='index'> <img :src="item.src" class='w-100 mb-2 rounded' v-skeleton.img='item.src' style='min-height:50px;'> <h6 class='w-100' style='min-height:30px' v-skeleton.text='item.name'>{{item.name}}</h6> </div> </div> </div> </template> <script> export default { data() { return { imgs:[ ] } }, created(){ for(let i=0;i<6;i++){ this.imgs.push({src:"",name:""}); } }, //页面渲染完成后 mounted() { //模拟请求数据 setTimeout(()=>{ this.imgs=[ {src:"http://qiniu.databankes.cn/databank.png",name:"DataBank西电站"}, {src:"http://qiniu.databankes.cn/databank.png",name:"DataBank西电站"}, {src:"http://qiniu.databankes.cn/databank.png",name:"DataBank西电站"}, {src:"http://qiniu.databankes.cn/databank.png",name:"DataBank西电站"}, {src:"http://qiniu.databankes.cn/databank.png",name:"DataBank西电站"}, {src:"http://qiniu.databankes.cn/databank.png",name:"DataBank西电站"}, ] },1000) }, methods: { setItem(index) { // this.list[index] = Math.floor(Math.random()*10) //这种方式不能实现更新 let random_num = Math.floor(Math.random() * 10) this.$set(this.list, index, random_num) }, deleteItem(index) { //如果删除了第一个,那么其余li都会被重新渲染一遍,update会被触发8次,且oldValue 和 newValue不相等 this.list.splice(index, 1) } } } </script> <style> </style>

main.js

import Vue from 'vue' import App from './App.vue' Vue.config.productionTip = false //骨架屏指令 Vue.directive('skeleton', { //第一次绑定时 //el 绑定的dom节点,binding 绑定的信息 bind(el, binding, vNode) { console.log('----bind-----') el.style.backgroundColor = '#eee' el.style.color = '#eee' }, update(el, binding) { if (binding.oldValue !== binding.value) { console.log('----update----') //去除骨架屏 el.style.backgroundColor = '' el.style.color = '' //渲染真正的值 for (let key in binding.modifiers) { switch (key) { case 'img': el.src = binding.value break; case 'text': el.innerHTML = binding.value } } } } }) new Vue({ render: h => h(App), }).$mount('#app')

0x02 案例:自定义右键菜单

<template> <div> <ul class='list-group'> <li class='list-group-item' v-for="(item,index) in list" v-menu='item.menu' :key='index'>{{item.name}}</li> </ul> <!-- 下拉菜单样式 --> </div> </template> <script> export default { data() { return { list: [{ name: "列表1", menu: [{ name: "列表1的选项一" }, { name: "列表1的选项二" } ] }, { name: "列表1", menu: [{ name: "列表2的选项一" }, { name: "列表2的选项二" } ] } ] } }, directives: { menu: { //插入到dom树 inserted(el, binding) { //监听鼠标右键单击事件 el.addEventListener('contextmenu', (e) => { //取消默认行为 e.preventDefault() let getSubmenu = function() { return document.getElementById('sub-menu') } //关闭之前的菜单 let removeSubmenu = function() { let submenu = getSubmenu() if (submenu) { submenu.remove() } } removeSubmenu() //拿到鼠标坐标 let left = e.x; let top = e.y; let list = ''; //构建菜单列表 for (let i = 0; i < binding.value.length; i++) { list += "<li class='list-group-item list-group-item-primary list-group-item-action'>" + binding.value[i].name + "</li>" } //构建菜单 let template = ` <div id='sub-menu'> <!--让蒙版占满屏幕--> <div style="position:fixed;top:0;left:0;bottom:0;right:0"></div> <ul class='list-group' style='width:200px;position:fixed;top:${top}px;left:${left}px;z-index: 999;'> ${list} </ul> </div> `; //渲染到页面 el.innerHTML += template; //给蒙版增加点击事件 let currentSubmenu = getSubmenu() //获取到蒙版 currentSubmenu.children[0].addEventListener('click', (e) => { //关闭菜单 removeSubmenu() }) //给菜单增加点击事件 let lis = currentSubmenu.getElementsByClassName('list-group-item') for (let i = 0; i < lis.length; i++) { lis[i].addEventListener('click', () => { console.log(binding.value[i].name) removeSubmenu() }) } }) } } } } </script> <style> </style>

 

最新回复(0)