ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
[TOC] >[success] # 开发实现 Vue 中的自定义指令 在这之前想 **给input默认获取到焦点** ,我们都需要通过 **ref** 的方式来 **触发焦点** ,如下代码: **index.html** ~~~ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>开发实现 Vue 中的自定义指令</title> <!-- 通过cdn方式引入vue --> <script src="https://unpkg.com/vue@next"></script> </head> <body> <div id="root"></div> </body> <script> // 自定义指令 directive const app = Vue.createApp({ mounted(){ this.$refs.input.focus() }, template: ` <div> <input ref="input" /> </div> ` }) const vm = app.mount('#root') </script> </html> ~~~ 这么写代码, **自定聚焦的逻辑无被复用**,像 **对dom 的操作** ,其实我们可以通过 **封装自定义指令** ,来实现 **dom逻辑** 的 **复用** 。 >[success] ## (全局自定义指令)实现input默认触发焦点 下面的 **v-focus** 就是我们 **自定义的指令** ,**自定义指令** 有一个 **生命周期函数 mounted** ,指的是 **当我这个指令挂载到某一个 dom 元素上的时候** , **mounted函数会自动执行** ,执行之后会 **接收第一个参数 el** , **el 就是绑定指令的元素** , **挂载dom** 之后,执行 **el.focus()** 就实现自己聚焦的效果。 **index.html** ~~~ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>开发实现 Vue 中的自定义指令</title> <!-- 通过cdn方式引入vue --> <script src="https://unpkg.com/vue@next"></script> </head> <body> <div id="root"></div> </body> <script> // 自定义指令 directive const app = Vue.createApp({ template: ` <div> <input v-focus /> </div> ` }) // 定义全局自定义指令 app.directive('focus', { mounted(el){ // 接收el元素 el.focus() } }) const vm = app.mount('#root') </script> </html> ~~~ 那么除了 **mounted 生命周期函数** 还有哪些其它的 **生命周期函数** 呢,具体如下代码: ~~~ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>开发实现 Vue 中的自定义指令</title> <!-- 通过cdn方式引入vue --> <script src="https://unpkg.com/vue@next"></script> </head> <body> <div id="root"></div> </body> <script> // 自定义指令 directive const app = Vue.createApp({ data(){ return { hello: true } }, template: ` <div> <div v-show="hello"> <input v-focus /> </div> </div> ` }) // 定义全局自定义指令 app.directive('focus', { beforeMount(){ // 指令即将挂载到页面元素上的时候 console.log('beforeMount') }, // 接收el元素 mounted(el){ // 指令挂载到页面元素时 console.log('mounted') el.focus() }, beforeUpdate(){ // (v-show的值变更)重新渲染input之前时 console.log('beforeUpdate') }, updated(){ // (v-show的值变更)重新渲染input时 console.log('updated') }, beforeUnmount(){ // (v-if=false)被销毁之前 console.log('beforeUnmount') }, unmounted(){ // (v-if=false)被销毁之后 console.log('unmounted') } }) const vm = app.mount('#root') </script> </html> ~~~ >[success] ## (局部自定义指令)实现input默认触发焦点 **index.html** ~~~ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>开发实现 Vue 中的自定义指令</title> <!-- 通过cdn方式引入vue --> <script src="https://unpkg.com/vue@next"></script> </head> <body> <div id="root"></div> </body> <script> // 局部自定义指令 const directives = { focus: { mounted(el){ // 接收el元素 el.focus() } } } const app = Vue.createApp({ // directives: directives , // 等同于下面的写法 directives, // 引入局部自定义指令 template: ` <div> <input v-focus /> </div> ` }) const vm = app.mount('#root') </script> </html> ~~~ >[success] ## 实战案例 用 **自定义指令来决定header距离顶部的位置** ,我们通过 **传入data** 中的 **top值** ,就可以实现 **动态更改header距离顶部的距离** ,代码如下: **index.html** ~~~ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>开发实现 Vue 中的自定义指令</title> <style> .header{ position: absolute; } </style> <!-- 通过cdn方式引入vue --> <script src="https://unpkg.com/vue@next"></script> </head> <body> <div id="root"></div> </body> <script> // 自定义指令 directive const app = Vue.createApp({ data(){ return { top: 500 } }, template: ` <div> <div v-pos="top" class="header"> <input /> </div> </div> ` }) // 定义全局自定义指令 app.directive('pos', { /** * @param {object} el - 绑定指令的dom元素 * @param {object} binding - 自定义指令捆绑元素身上的信息 */ mounted(el, binding){ el.style.top = `${ binding.value }px` // 自定义指令传入过来的值 } }) const vm = app.mount('#root') </script> </html> ~~~ 但是这里有个问题,我们在 **浏览器** 通过 **vm.top = 1000** 修改 **top** 的位置,会发现 **header 位置 ,并没有改变** ,这是 **因为数据发生变化时,自定义指令中的 mounted 并不会重新执行** ,想要让位置改变需要使用 **updated 生命周期函数** ,代码如下: >[success] ### 更新数据 ~~~ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>开发实现 Vue 中的自定义指令</title> <style> .header{ position: absolute; } </style> <!-- 通过cdn方式引入vue --> <script src="https://unpkg.com/vue@next"></script> </head> <body> <div id="root"></div> </body> <script> // 自定义指令 directive const app = Vue.createApp({ data(){ return { top: 500 } }, template: ` <div> <div v-pos="top" class="header"> <input /> </div> </div> ` }) // 定义全局自定义指令 app.directive('pos', { /** * 初始化时 * @param {object} el - 绑定指令的dom元素 * @param {object} binding - 自定义指令捆绑元素身上的信息 */ mounted(el, binding){ el.style.top = `${ binding.value }px` // 自定义指令传入过来的值 }, /** * 数据更新时 * @param {object} el - 绑定指令的dom元素 * @param {object} binding - 自定义指令捆绑元素身上的信息 */ updated(el, binding){ el.style.top = `${ binding.value }px` // 自定义指令传入过来的值 } }) const vm = app.mount('#root') </script> </html> ~~~ >[success] ### 定义指令时逻辑简写 在 **vue** 中,如果你的 **自定义指令** 里只有 **mounted** 与 **updated** , **而且它俩逻辑写的同样情况下,可以把它简写** , **简写的形式 directive 的第二个参数是一个箭头函数** ,代码如下: ~~~ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>开发实现 Vue 中的自定义指令</title> <style> .header{ position: absolute; } </style> <!-- 通过cdn方式引入vue --> <script src="https://unpkg.com/vue@next"></script> </head> <body> <div id="root"></div> </body> <script> // 自定义指令 directive const app = Vue.createApp({ data(){ return { top: 500 } }, template: ` <div> <div v-pos="top" class="header"> <input /> </div> </div> ` }) // --------------------简写形式---------------------------------------------------- /** * 初始化时 * @param {object} el - 绑定指令的dom元素 * @param {object} binding - 自定义指令捆绑元素身上的信息 */ app.directive('pos', (el, binding) => { el.style.top = `${ binding.value }px` // 自定义指令传入过来的值 }) // --------------------非简写形式---------------------------------------------------- // // 定义全局自定义指令 // app.directive('pos', { // /** // * 初始化时 // * @param {object} el - 绑定指令的dom元素 // * @param {object} binding - 自定义指令捆绑元素身上的信息 // */ // mounted(el, binding){ // el.style.top = `${ binding.value }px` // 自定义指令传入过来的值 // }, // /** // * 数据更新时 // * @param {object} el - 绑定指令的dom元素 // * @param {object} binding - 自定义指令捆绑元素身上的信息 // */ // updated(el, binding){ // el.style.top = `${ binding.value }px` // 自定义指令传入过来的值 // } // }) const vm = app.mount('#root') </script> </html> ~~~ >[success] ### 模仿v-on:click的写法 在上面我们使用了自己定义的 **自定义指令 v-pos** ,那我们回想一下在 **vue** 中, 有 **v-on:click** ,那我们也可以这样写: **index.html** ~~~ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>开发实现 Vue 中的自定义指令</title> <style> .header{ position: absolute; } </style> <!-- 通过cdn方式引入vue --> <script src="https://unpkg.com/vue@next"></script> </head> <body> <div id="root"></div> </body> <script> // 自定义指令 directive const app = Vue.createApp({ data(){ return { distance: 500, // 距离 direction: 'left' // 方向 } }, template: ` <div> <div v-pos:[direction]="distance" class="header"> <input /> </div> </div> ` }) /** * 初始化时 * @param {object} el - 绑定指令的dom元素 * @param {object} binding - 自定义指令捆绑元素身上的信息 */ app.directive('pos', (el, binding) => { el.style[binding.arg] = `${ binding.value }px` // 自定义指令传入过来的值 }) const vm = app.mount('#root') </script> </html> ~~~