夜猫子的知识栈 夜猫子的知识栈
首页
  • 前端文章

    • JavaScript
  • 学习笔记

    • 《JavaScript教程》
    • 《Web Api》
    • 《ES6教程》
    • 《Vue》
    • 《React》
    • 《TypeScript》
    • 《Git》
    • 《Uniapp》
    • 小程序笔记
    • 《Electron》
    • JS设计模式总结
  • 《前端架构》

    • 《微前端》
    • 《权限控制》
    • monorepo
  • 全栈项目

    • 任务管理日历
    • 无代码平台
    • 图书管理系统
  • HTML
  • CSS
  • Nodejs
  • Midway
  • Nest
  • MySql
  • 其他
  • 技术文档
  • GitHub技巧
  • 博客搭建
  • Ajax
  • Vite
  • Vitest
  • Nuxt
  • UI库文章
  • Docker
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

夜猫子

前端练习生
首页
  • 前端文章

    • JavaScript
  • 学习笔记

    • 《JavaScript教程》
    • 《Web Api》
    • 《ES6教程》
    • 《Vue》
    • 《React》
    • 《TypeScript》
    • 《Git》
    • 《Uniapp》
    • 小程序笔记
    • 《Electron》
    • JS设计模式总结
  • 《前端架构》

    • 《微前端》
    • 《权限控制》
    • monorepo
  • 全栈项目

    • 任务管理日历
    • 无代码平台
    • 图书管理系统
  • HTML
  • CSS
  • Nodejs
  • Midway
  • Nest
  • MySql
  • 其他
  • 技术文档
  • GitHub技巧
  • 博客搭建
  • Ajax
  • Vite
  • Vitest
  • Nuxt
  • UI库文章
  • Docker
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 基础

  • 响应式

  • 组件

  • 过渡&动画

  • 可复用性

    • Mixin混入
    • 渲染函数
    • 全局API
    • Teleport
    • 组合式API
    • 引入css样式
    • 自定义hook
    • 自定义插件
    • 自定义事件
    • 自定义指令
      • 实践 permission指令
      • 实践 v-loading
        • vue2
        • vue3
  • 工具

  • 规模化

  • 路由(核心)

  • 组合式

  • Vuex

  • Pinia

  • 其他

  • Vue3 源码学习记录

  • 《Vue》笔记
  • 可复用性
夜猫子
2022-06-26
目录

自定义指令

# 自定义指令

  • 参数:

    • {string} name

    • {Function | Object} [definition]

  • 生命周期钩子 可以在第二个参数中使用 created() {} 等一系列的生命周期钩子。

    const myDirective = {
      // 在绑定元素的 attribute 前
      // 或事件监听器应用前调用
      created(el, binding, vnode, prevVnode) {
        // 下面会介绍各个参数的细节
      },
      // 在元素被插入到 DOM 前调用
      beforeMount(el, binding, vnode, prevVnode) {},
      // 在绑定元素的父组件
      // 及他自己的所有子节点都挂载完成后调用
      mounted(el, binding, vnode, prevVnode) {},
      // 绑定元素的父组件更新前调用
      beforeUpdate(el, binding, vnode, prevVnode) {},
      // 在绑定元素的父组件
      // 及他自己的所有子节点都更新后调用
      updated(el, binding, vnode, prevVnode) {},
      // 绑定元素的父组件卸载前调用
      beforeUnmount(el, binding, vnode, prevVnode) {},
      // 绑定元素的父组件卸载后调用
      unmounted(el, binding, vnode, prevVnode) {}
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21

binding 中的参数:

  1. instance:使用指令的组件实例。
  2. value:传递给指令的值。例如,在 v-my-directive="1 + 1" 中,该值为 2。
  3. oldValue:先前的值,仅在 beforeUpdate 和 updated 中可用。无论值是否有更改都可用。
  4. arg:传递给指令的参数(如果有的话)。例如在 v-my-directive:foo 中,arg 为 "foo"。
  5. modifiers:包含修饰符(如果有的话) 的对象。例如在 v-my-directive.foo.bar 中,修饰符对象为 {foo: true,bar: true}。
  6. dir:一个对象,在注册指令时作为参数传递。
<div id="dynamic-arguments-example" class="demo">
  <p>Scroll down the page</p>
  <p v-pin="200">Stick me 200px from the top of the page</p>
</div>
1
2
3
4
// 全局指令
const app = Vue.createApp({})

app.directive('pin', {
  mounted(el, binding) {
    el.style.position = 'fixed'
    // binding.value 是我们传递给指令的值——在这里是 200
    el.style.top = binding.value + 'px'
  }
})

app.mount('#dynamic-arguments-example')
1
2
3
4
5
6
7
8
9
10
11
12
// 组件内指令
<script setup>
// 在模板中启用 v-focus
const vFocus = {
  mounted: (el) => el.focus()
}
</script>

<template>
  <input v-focus />
</template>
1
2
3
4
5
6
7
8
9
10
11

# 实践 permission指令

实现一个按钮级别的颗粒权限控制指令。

// directive.js
const permissions = ["base:soft:add", "base:soft:cut"]
app.directive('permission', (el, binding) => {
    const value = binding.value
    const isBind=Reflect.has(permissions, "*:*:*")||Reflect.has(permissions, value)
    if (!isBind) {
        el.parentNode && el.parentNode.removeChild(el)
      }
})
1
2
3
4
5
6
7
8
9
// mian.js
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)

// 引入全局指令
import { directive } from './directive/directive'
 
// 全局注册
// 注意:要放在 const app = createApp(App) 之后
directive(app)
app.mount('#app')
1
2
3
4
5
6
7
8
9
10
11
12

# 实践 v-loading

# vue2

import Vue from "vue";
import loading from "@/components/control/v-loading/loading.vue";

const LoadingComponents = Vue.extend(loading);

const insertDom = (parent, el) => {
  parent.appendChild(el.mask);
};

const toggleLoading = (el, binding) => {
  if (binding.value) {
    Vue.nextTick(() => {
      //   el.instance.visible = true;
      insertDom(el, el);
    });
  } else {
    // el.instance.visible = false;
    el.removeChild(el.instance.$el);
  }
};

export default {
  bind(el, binding, vnode) {
    const mask = new LoadingComponents({
      el: document.createElement("div"),
      data() {},
    });
    el.style.position = "relative";
    el.instance = mask;
    el.mask = mask.$el;
    el.mask.style.position = "absolute";
    el.mask.style.background = binding.arg || "#F8F8F8CC";
    el.mask.style.top = 0;
    el.mask.style.left = 0;
    binding.value && toggleLoading(el, binding);
  },
  update(el, binding) {
    if (binding.oldValue !== binding.value) {
      toggleLoading(el, binding);
    }
  },
  unbind(el, binding) {
    el.instance && el.instance.$destroy();
  },
};

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<template>
  <div class="loadingContent">
    <div class="loading">
      <span></span>
      <span></span>
      <span></span>
      <span></span>
      <span></span>
    </div>
  </div>
</template>

<script>
export default {
  components: {},
  data() {
    return {};
  },
  computed: {},
  watch: {},
  methods: {},
  created() {},
  mounted() {},
};
</script>
<style lang="css" scoped>
.loadingContent {
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
}
.loading {
  position: relative;
  width: 100px;
  height: 15px;
}
.loading span {
  position: absolute;
  display: inline-block;
  width: 15px;
  height: 100%;
  margin-right: 5px;
  background: #3485f8;
  -webkit-animation: load 1.04s ease infinite;
}
.loading span:last-child {
  margin-right: 0px;
}
@-webkit-keyframes load {
  0% {
    opacity: 1;
    -webkit-transform: scale(1.2);
  }
  100% {
    opacity: 0.2;
    -webkit-transform: scale(0.2);
  }
}
.loading span:nth-child(1) {
  -webkit-animation-delay: 0.13s;
}
.loading span:nth-child(2) {
  left: 20px;
  -webkit-animation-delay: 0.26s;
}
.loading span:nth-child(3) {
  left: 40px;
  -webkit-animation-delay: 0.39s;
}
.loading span:nth-child(4) {
  left: 60px;
  -webkit-animation-delay: 0.52s;
}
.loading span:nth-child(5) {
  left: 80px;
  -webkit-animation-delay: 0.65s;
}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
import vLoadingDirective from "@/components/control/v-loading/index";
Vue.directive("loading", vLoadingDirective);
1
2

# vue3

import { Spin } from 'ant-design-vue'
import { createVNode, render } from 'vue'

const appendEl = (el) => {
  el.appendChild(el.instance)
}

const removeEl = (el) => {
  el.removeChild(el.instance)
}

const loadingDirective = {
  name: 'v-loading',
  mounted(el, binding) {
    const div = document.createElement('div')
    div.setAttribute(
      'class',
      'absolute top-0 left-0 w-full h-full flex-row justify-center items-center'
    )
    render(createVNode(Spin), div)
    el.instance = div
    el.style.position = 'relative'
    if (binding.value) {
      appendEl(el)
    }
  },
  updated(el, binding) {
    if (binding.value !== binding.oldValue) {
      binding.value ? appendEl(el) : removeEl(el)
    }
  }
}

export default loadingDirective
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
编辑 (opens new window)
上次更新: 2024/11/25 16:00:01
自定义事件
Vue CLi v3 创建项目使用记录

← 自定义事件 Vue CLi v3 创建项目使用记录→

最近更新
01
IoC 解决了什么痛点问题?
03-10
02
如何调试 Nest 项目
03-10
03
Provider注入对象
03-10
更多文章>
Copyright © 2019-2025 Study | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式