自定义指令

149 阅读2分钟

前言

vue在除了提供默认的十几个内置的指令外,还允许开发人员根据实际情况自定义指令,那我们在何时使用它呢? 在vue项目中,一般情况下都可以操作dom来修改试图,但偶尔避免不了操作原生dom,当需要操作原生dom的时候,就可以用到自定义指令啦。

学习背景

页面上就两个元素:1号和2号,需求是点击2号,让1号显示;点击1号,1号也显示;点击1号和2号之外的区域,1号隐藏。所以“点击1号和2号之外的区域,1号隐藏”,这个自定义指令就派上用场了。 仔细想一下,element的select下拉框,点击下拉框以外的内容,下拉框消失,el-date也是。

实现思想

1.监听点击事件
2.判断点击事件是否为当前dom
3.取消监听

代码实现

<template>
  <div class="whole">
    <div v-show="state.showOne" ref="one" class="one" @click.stop="clickOne">这是第一个元素</div>
    <div v-show="state.showTwo" ref="two" class="two" @click.stop="clickTwo">这是第二个元素</div>
  </div>
</template>

<script setup>
import { reactive, getCurrentInstance } from 'vue';
const { proxy } = getCurrentInstance();

const state = reactive({
  showOne: true,
  showTwo: true
});
const clickOne = () => {
  state.showOne = true;
};
const clickTwo = () => {
  state.showOne = true;
};
document.addEventListener('click', e => {
  if (proxy.$refs.one || proxy.$refs.two) {
    let isSelf = proxy.$refs.one.contains(e.target) || proxy.$refs.two.contains(e.target);
    if (!isSelf) {
      state.showOne = false;
    }
  }
});
</script>

<style lang="less" scoped>
.whole {
  display: flex;
  align-items: center;
  justify-content: space-around;
  margin-top: 20px;
  text-align: center;
  .one {
    width: 150px;
    height: 150px;
    background: #ff4eff;
    border-radius: 10px;
    cursor: pointer;
  }
  .two {
    width: 150px;
    height: 150px;
    background: #3c3fdd;
    border-radius: 10px;
    cursor: pointer;
  }
}
</style>

这样确实可以实现,如果只是一个地方还好,直接就给 document 增加一个 click 事件就完事,如果多个地方都用到那就比较麻烦,这时封装成一个指令就很便捷了。

vue3的写法:

app.directive('clickoutside', {
  beforeMount(el, binding) {
    document.addEventListener('click', (e) => {
      el.contains(e.target) && binding.value();
    }, false)
  },
  unmounted() {
    document.removeEventListener('click', () => {})  // 不要忘记移除
  }
})

具体使用:

<template>
  <button v-clickoutside="clickoutside">点击</button>
</template>

<script setup>
import { defineComponent } from "vue";
export default defineComponent({
  setup() {
    return {
      clickoutside(e) {
        console.log('非自身点击触发')
      }
    };
  },
});
</script>

vue2的实现过程:

  1. 实现clickoutside .js
const clickOutside = {
	bind(el, binding) {
		const documentHandler = (e) => {
			if (el.contains(e.target)) return false;
			if (binding.expression){
			    binding.value(e);
			}
		};
		el.documentHandler = documentHandler;
		document.addEventListener('click', documentHandler, false);
	},
	unbind(el){
		document.removeEventListener('click', el.documentHandler, false);
		delete el.documentHandler;
            }
    };

export default clickOutside;
  1. 实现vue中自定义组件的目录index.js
import clickOutside from "../clickOutside";
const directives = {
	clickOutside
    };
export default{
	install(Vue){
		Object.keys(directives).forEach((key) => {
			Vue.directive(key, directives[key]);
		});
            }
    };
  1. main.js中统一注册自定义指令
import Vue from "vue";
import App from "./App.vue";
import directives from "./components/index";

Vue.use(directives);

new Vue({
	router,
	store,
	render: h => h(App)
}).$mount("#app");
  1. 页面使用
<template>
<div class="title" v-show="isShow" v-clickOutside="clickOutside">最新课程</div>
      <span v-show="isOne">11111111111</span>
</template>
<script>
export default {
	data() {
            return {
               isOne: false,
               isShow: true
                 }
              },
              methods: {
		clickOutside(){
                    this.isShow = false;
                    this.isOne = true;
                   }
               }
</script>

欧克,至此v-clickOutside的指令就完成啦!撒花
自定义指令很方便,而在 Vue2 中给一个指令定义对象可以提供 bindinsertedupdatecomponentUpdatedunbind 五个钩子函数。详情

在新发布的 Vue3 中对指令也做了一些改造,主要就是对其中的钩子函数进行了优化升级,更多的自定义指令可以参考下面这篇文章