除了 Vue 内置的一系列指令 (比如 v-model
或 v-show
) 之外,Vue 还允许你注册自定义的指令 (Custom Directives)。
一个自定义指令由一个包含类似组件生命周期钩子的对象来定义。钩子函数会接收到指令所绑定元素作为其参数。
在 中,任何以
v
开头的驼峰式命名的变量都可以被用作一个自定义指令。
- <template>
- <div>
- <button>切换button>
-
- <A v-move:myParam.myModifier="{ background: 'pink' }">A>
- div>
- template>
-
- <script setup lang="ts">
- import { reactive, ref, Directive } from 'vue';
- import A from './components/A.vue';
- import B from './components/B.vue';
- let flag = ref
(true) - const vMove: Directive = {
- created() {
- console.log('created')
- },
- beforeMount() {
- console.log('beforeMount')
- },
- mounted(...args: Array
) { - console.log('mounted')
- console.log(args);
-
- },
- beforeUpdate() {
- console.log('beforeUpdate')
- },
- updated() {
- console.log('updated')
- },
- beforeUnmount() {
- console.log('beforeUnmount')
- },
- unmounted() {
- console.log('unmounted')
- }
- }
- script>
-
- <style scoped lang="less">style>
- <template>
- <div>
- <button>切换button>
-
- <A v-move:myParam.myModifier="{ background: 'pink' }">A>
- div>
- template>
-
- <script setup lang="ts">
- import { reactive, ref, Directive, DirectiveBinding } from 'vue';
- import A from './components/A.vue';
- import B from './components/B.vue';
- let flag = ref
(true) - type Dir = {
- background: string;
- }
- const vMove: Directive = {
- created() {
- console.log('created')
- },
- beforeMount() {
- console.log('beforeMount')
- },
- mounted(el: HTMLElement, dir: DirectiveBinding
) { - console.log('mounted')
- console.log(el);
- console.log(dir);
- el.style.background = dir.value.background;
- },
- // 传入的数据发生变化(比如此时的background)时触发 beforeUpdate 和 updated
- beforeUpdate() {
- console.log('beforeUpdate')
- },
- updated() {
- console.log('updated')
- },
- beforeUnmount() {
- console.log('beforeUnmount')
- },
- unmounted() {
- console.log('unmounted')
- }
- }
- script>
-
- <style scoped lang="less">style>
- <!-- App.vue -->
- <template>
- <div class="btns">
- <button v-has-show="123">创建</button>
- <button>编辑</button>
- <button>删除</button>
- </div>
- </template>
-
- <script setup lang="ts">
- import { reactive, ref, DirectiveBinding } from 'vue';
- import type { Directive } from 'vue'
- const vHasShow: Directive = (el, binding) => {
- console.log(el, binding) ;
- }
- </script>
-
- <style scoped lang="less">
- .btns {
- button {
- margin: 10px;
- }
- }
- </style>
根据能否从 localStorage(或者后台返回) 中获取数据,来判断是否显示某个按钮。
- <!-- App.vue -->
- <template>
- <div class="btns">
- <button v-has-show="'shop:create'">创建</button>
- <button v-has-show="'shop:edit'">编辑</button>
- <button v-has-show="'shop:delete'">删除</button>
- </div>
- </template>
-
- <script setup lang="ts">
- import { reactive, ref, DirectiveBinding } from 'vue';
- import type { Directive } from 'vue'
- localStorage.setItem('userId', 'xiuxiu')
- // mock 后台返回的数据
- const permissions = [
- 'xiuxiu:shop:create',
- // 'xiuxiu:shop:edit', // 后台没有相应数据,则不显示该对应的按钮
- 'xiuxiu:shop:delete'
- ]
- const userId = localStorage.getItem('userId') as string
- const vHasShow: Directive = (el, binding) => {
- if(!permissions.includes(userId + ':' + binding.value)) {
- el.style.display = 'none'
- }
- }
- </script>
-
- <style scoped lang="less">
- .btns {
- button {
- margin: 10px;
- }
- }
- </style>
拖拽粉色框移动大盒子。
- <!-- App.vue -->
- <template>
- <div v-move class="box">
- <div class="header"></div>
- <div>内容</div>
- </div>
- </template>
-
- <script setup lang="ts">
- import { Directive, DirectiveBinding } from 'vue';
-
-
- const vMove:Directive<any,void> = (el:HTMLElement, binding:DirectiveBinding)=> {
- let moveElement:HTMLElement = el.firstElementChild as HTMLElement;
- console.log(moveElement);
- const mouseDown = (e:MouseEvent) => {
- // 记录原始位置
- // clientX 鼠标点击位置的X轴坐标
- // clientY 鼠标点击位置的Y轴坐标
- // offsetLeft 鼠标点击的子元素距离其父元素的左边的距离
- // offsetTop 鼠标点击的子元素距离其父元素的顶部的距离
- let X = e.clientX - el.offsetLeft;
- let Y = e.clientY - el.offsetTop;
- const move = (e:MouseEvent) => {
- console.log(e);
- el.style.left = e.clientX - X + 'px';
- el.style.top = e.clientY - Y + 'px';
- }
- document.addEventListener('mousemove', move);
- document.addEventListener('mouseup', () => {
- document.removeEventListener('mousemove', move);
- })
- }
- moveElement.addEventListener('mousedown', mouseDown);
- }
- </script>
-
- <style scoped lang="less">
- .box {
- position: fixed;
- height: 200px;
- width: 200px;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- border: 1px solid #000;
- .header {
- height: 50px;
- width: 100%;
- background: pink;
- border-bottom: #000 1px solid;
- }
- }
- </style>
let imageList = import.meta.glob('./assets/images/*.*', { eager: true })
let imageList = import.meta.glob('./assets/images/*.*')
- // 判断图片是否在可视区
- const observer = new IntersectionObserver((e)=> {
- console.log(e[0]);
- })
- // 监听元素
- observer.observe(el)
- <!-- App.vue -->
- <template>
- <div>
- <div>
- <img v-lazy="item" width="400" height="500" v-for="item in arr" alt="">
- </div>
- </div>
- </template>
-
- <script setup lang="ts">
- import { Directive, DirectiveBinding } from 'vue';
-
- let imageList:Record<string,{default:string}> = import.meta.glob('./assets/images/*.*', { eager: true })
- let arr = Object.values(imageList).map(item=>item.default)
- console.log(arr);
- let vLazy:Directive<HTMLImageElement,string> = async (el,binding)=> {
- const def = await import('./assets/pinia.svg')
- el.src = def.default
- // 判断图片是否在可视区
- const observer = new IntersectionObserver((e)=> {
- console.log(e[0],binding.value);
- if(e[0].intersectionRatio > 0) {
- setTimeout(()=> {
- el.src = binding.value
- },2000)
- observer.unobserve(el)
- }
- })
- // 监听元素
- observer.observe(el)
- }
- </script>
-
- <style scoped lang="less"></style>