vue 自定义指令产生的背景
-
咱们晓得前端程序员,写的各种代码,最终是要作用于页面上的 DOM 元素的。比方用 HTML 固定好 DOM 的根底构造,用 CSS 设置 DOM 的款式,用 JS 去做一些 DOM 的交互。因为 DOM 元素相当于“物理层”,要出现给用户看的。DOM 相当于积木一样,咱们去操作这个积木,最终“搭建”成各式各样的成果进去。
所以所有花里胡哨的前端框架都是,基于前端三件套 HTML、CSS、JS 封装进去的
-
所以 jQuery 中就通过 $ 符,间接操作 DOM 就挺不便的,不便是不便了,然而间接操作 DOM 会引起页面的回流重绘,又会略为有点节约浏览器的性能,容易呈现页面卡顿的状况,造成用户体验不太好。
万事皆有利弊,间接操作 DOM,有时候,也可能,会是一个简略方便快捷的抉择。
-
所以 vue 和 react 就闪亮退场了,用数据驱动的思维,并不是间接操作 DOM,通过虚构 DOM 做一个缓冲,虚构 DOM 会收集谁须要操作 DOM,操作了 DOM 的哪些货色,对立先记录好,最终全副一块操作 DOM。有点像 js 文档碎片的感觉。这样的话,浏览器性能就会优化晋升不少。所以,vue 中并不提倡间接操作 DOM,因为有虚构 DOM 在呢,你还间接操作实在 DOM 干啥啊。用 vue 指令啊
虚构 DOM 的确是个好货色,给大佬点赞。咱们晓得 vue 提供的内置指令很不便,能解决大多数的 DOM 操作问题,然而,并不能解决所有的问题。
-
所以某些状况下,vue 内置指令不能满足咱们的需要,咋办呢。当然 Vue 创始人也想到了这个问题,所以,就搞了 vue 自定义指令,封装了一套钩子函数和对应参数的规定、以供咱们应用去,从而更不便解决相应问题
vue 自定义指令就是间接操作 DOM 了,某些场景下,vue 自定义指令是一个很好的抉择
vue 自定义指令的分类
vue 自定义指令有两种
1. 全局自定义指令(须要全局注册)
2. 组件自定义指令(须要组件外部注册)
自定义指令其实就是一个对象,对象身上有一些钩子函数,自定义指令对象身上的钩子函数和 vue 组件的生命周期钩子函数相似,后续会通过案例论述
个别应用全局自定义指令会多一些,毕竟复用不便
vue 自定义指令应用解说
案例一 el-input 初始获取焦点(组件自定义指令)
这个案例其实官网的案例,这里咱们批改一下,更加便于咱们了解之。
假如咱们须要这样一个成果:页面加载结束当前,el-input 输入框主动获取焦点,相似关上百度一下页面当前,输入框主动获取焦点一样。
<template>
<div>
<!-- 将指令挂在元素上 -->
<el-input v-myFocus></el-input>
</div>
</template>
<script>
export default {
name:"myCode"
// 本例是部分自定义指令,所以 directives 写在组件外部
directives: {
// 给指令起个名字,叫做 myFocus
myFocus: {inserted: function (el) { // inserted 钩子是这个元素行将插入到文档里的时候,具体程序是在 created 后、mounted 前。可看打印程序
// el 参数是:自定义写在谁身上,挂在那个 DOM 元素上,这个 el 就是那个 dom 元素对象,钩子函数还有别的参数,这个后续会说
console.log("自定义指令 -->", el.childNodes);
// 先让元素聚焦 el-input 是在原生 input 外包了一层,所以须要通过原生 js 取子元素,找到 input,具体看一下打印后果就分明了
el.childNodes[1].focus(); // 执行原生 input 的 focus 办法},
},
},
created() { console.log("created-->") },
mounted() { console.log("mounted-->") }
}
</script>
演示的话,间接复制粘贴代码即可
案例二 仿写 v -show 性能(全局自定义指令)
效果图如下
看了效果图,性能其实和 v -show 没啥区别,举这样一个例子是不便更加好了解 vue 自定义指令
第一步,新建 utils 文件夹中寄存 index.js 文件,此文件用于书写全局自定义指令
// 引入 vue 并应用 vue 的 directive 办法去注册一个自定义指令
import Vue from 'vue'
Vue.directive('showshow', { // 指令的名字叫做 showshow
// bind 函数个别用来做初始化数据,也能够绑定事件什么的
bind(el, binding, vnode) {console.log(el, binding.value, vnode);
// el 参数是以后应用指令的元素,bind 参数是指令绑定的数据,vnode 是虚构 dom
const flag = binding.value // 找到组件中绑定的标识
if (flag == false) {el.style.display = 'none'} else {el.style.display = 'inline-block'}
},
// inserted 函数是在元素插入 dom 节点调用
inserted(el, binding, vnode) { },
// update 和 componentUpdated 都是更新应用,然而前者更加罕用些,oldVnode 参数只有在这两个钩子中才会有
componentUpdated(el, binding, vnode, oldVnode) { },
update(el, binding, vnode, oldVnode) {
const flag = binding.value
if (flag == false) {el.style.display = 'none'} else {el.style.display = 'inline-block'}
},
// unbind 解绑时候应用,比方用来移除第一个 bind 函数中绑定的事件
unbind(el, binding, vnode) {}});
第二步,在 main.js 中引入这个书写全局自定义指令的文件(示意应用之)
import Vue from 'vue'
import App from './App.vue'
import router from "@/router/index.js" // 引入路由表
import store from './store/index' // 引入 vuex
// ...
// 引入就能够应用全局自定义指令啦 ^_^
import './views/utils/index.js'
let vvvue = new Vue({render: h => h(App),
router,
store // 挂载下来
}).$mount('#app')
第三步,在组件中应用全局自定义指令
<template>
<div>
<el-button @click="change" type="primary"> 显示暗藏来回切换 </el-button>
<!--
给这个按钮加上 vue 的自定义指令 v -showshow,并且让这个自定义指令绑定 vue 组件中 data 的数据
所以这里绑定的是 isShowBtn 标识,通过管制这个标识管制显示与暗藏
-->
<el-button type="primary" plain v-showshow="isShowBtn"> 仿写 v -show 按钮 </el-button>
</div>
</template>
<script>
export default {data() {
return {isShowBtn: true, // 初始为 true,让其显示};
},
methods: {
// 点击来回切换显示与暗藏
change() { this.isShowBtn = !this.isShowBtn},
},
};
</script>
有的道友说,蛤?这不是多此一举嘛?其实不是,因为还没遇到特定的场景。上述两个例子次要是学习自定义指令的思维。接下来咱们举一个小例子,点击文字一键复制性能。
案例三 实现 v -copy 自定义指令
效果图
点击就复制胜利了,而后在电脑的相应地位就能够执行 Ctrl V 就能够间接粘贴这个点击的文本了
第一步,写自定义指令代码
import Vue from 'vue'
Vue.directive('copy', { // 指令的名字叫做 v -copy
// bind 函数初始化
bind(el, binding, vnode) { },
// inserted 函数是在元素插入 dom 节点调用
inserted(el, binding, vnode) { // el 参数是以后应用指令的元素,bind 参数是指令绑定的数据,vnode 是虚构 dom
// 将 copyFn 函数挂在 el 身上方便使用
el.copyFn = () => {console.log('点击的是哪个 DOM', el);
// 创立选中范畴
var range = document.createRange();
// 抉择点击的这个 dom
range.selectNode(el);
// 移除剪切板中内容,不增加这个语句的话,在 ie 和 Edge 中复制不到
window.getSelection().removeAllRanges();
// 将 el 中的文字内容复制到剪切板
window.getSelection().addRange(range);
// 开启复制粘贴性能
let flag = document.execCommand('copy');
// 需注意兼容性问题
flag ? alert('复制胜利,能够粘贴啦') : alert('以后浏览器不反对一键复制性能,请手动复制粘贴')
}
el.addEventListener('click', el.copyFn)
},
// update 和 componentUpdated 都是更新应用,然而前者更加罕用些
// oldVnode 参数只有在这两个钩子中才会有,更新当前才会有更新前和更新后的 DOM
componentUpdated(el, binding, vnode, oldVnode) {console.log('componentUpdated');
},
update(el, binding, vnode, oldVnode) {console.log('update');
},
// unbind 解绑时候应用,比方用来移除第一个 bind 函数中绑定的事件
unbind(el, binding, vnode) {el.removeEventListener('click', el.copyFn)
}
});
/**
* 为了疾速上手自定义指令钩子,咱们能够这样简略了解。*
* bind 和 inserted 钩子 --> 相似 created 和 mounted 钩子
* componentUpdated 和 update 钩子 --> 相似 updated 钩子
* unbind 钩子相似 --> destroyed 钩子
*
* 具体程序大家能够拷贝代码运行当前打印看一下,这样更加不便精准了解
* */
第二步,在.vue 文件中应用
<template>
<div>
<!-- 给每一项加上 v -copy 自定义指令 -->
<div class="item" v-copy v-for="(item, index) in arr" :key="index">{{item}}</div>
</div>
</template>
<script>
export default {
name: "TestVue",
data() {
return {arr: ["孙悟空", "猪八戒", "沙和尚", "唐僧"],
};
},
};
</script>
<style lang="less" scoped>
.item {
margin-top: 12px;
font-size: 24px;
font-weight: bolder;
cursor: pointer;
}
</style>
补充案例中常识
- Range 对象:https://developer.mozilla.org…
- Selection 对象:https://developer.mozilla.org…
- execCommand 办法:https://developer.mozilla.org…
对于 MDN 说,execCommand 办法未来可能被删除的这个问题,思否上有一个答复挺好的,也顺带贴上:https://segmentfault.com/q/10…
总结
在什么时候须要用自定义指令?
当内置指令不够用的时候,当须要做一些非凡的成果的时候,当须要对一般 DOM 元素进行底层操作的时候
自定义指令就是贴近原生 js 书写形式,咱们正当应用其钩子函数以及相应参数,能够实现更加灵便的成果性能。毕竟 vue 尽管提供了很多的内置指令,不便咱们间接应用,上手快,然而也是限度了程序员的一些发散思维。各有利弊,所以须要衡量 ^_^