vue事件及自定义指令


事件

触发与监听事件

标题解读:子组件调用父组件传递的方法,在调用时父组件监听该方法;

在组件模板表达式中,可以直接使用$emit方法触发自定义事件(父组件传递的事件)

<!-- MyComponent -->
<button @click="$emit('someEvent')">click me</button>

父组件代码

<MyComponent @some-event="callback" />
<MyComponent @some-event.once="callback" />

💯注意命名方式

事件参数

子组件传递参数

<button @click="$emit('increaseBy', 1)">
  Increase by 1
</button>

父组件接受方式

<MyButton @increase-by="(n) => count += n" />
<MyButton @increase-by="increaseCount" />

声明触发事件

显示声明组件接收的函数

<script setup>
const emit = defineEmits(['inFocus', 'submit'])

function buttonClick() {
  emit('submit')
}
</script>
export default {
  emits: ['inFocus', 'submit'],
  setup(props, ctx) {
    ctx.emit('submit')
  },
  methods:{
    buttonClick:function(t){
      this.$emit('someEvent2',t)
    }
  }
}
export default {
  emits: ['inFocus', 'submit'],
  setup(props, { emit }) {
    emit('submit')
  },
  methods:{
    buttonClick:function(t){
      this.$emit('someEvent2',t)
    }
  }
}

这个 emits 选项还支持对象语法,它允许我们对触发事件的参数进行验证:

<script setup>
const emit = defineEmits({
  submit(payload) {
    // 通过返回值为 `true` 还是为 `false` 来判断
    // 验证是否通过
  }
})
</script>

事件校验

<script setup>
const emit = defineEmits({
  // 没有校验
  click: null,

  // 校验 submit 事件 根据返回的布尔值进行事件拦截
  submit: ({ email, password }) => {
    if (email && password) {
      return true
    } else {
      console.warn('Invalid submit event payload!')
      return false
    }
  }
})

function submitForm(email, password) {
  emit('submit', { email, password })
}
</script>

🛑配合v-model使用

自定义事件可以用于开发支持v-model的自定义表单组件。

<input v-model="searchText" />

上面的代码解释之后就是

<input
  :value="searchText"
  @input="searchText = $event.target.value"
/>

当使用在自定义组件上,展开形式如下

<CustomInput
  :modelValue="searchText"
  @update:modelValue="newValue => searchText = newValue"
/>

这个例子工作起来组件内部需要做两件事:
将内部input元素的value绑定到modelValue值;输入新值在input上触发update:modalValue事件。

完整的案例

<template>
  <CustomInput
    :modalValue="message"
    @update:modalValue="newV=>message=newV"
  />
<!--  <CustomInput v-model="searchText" />  -->
</template>
<script setup>
  import { defineProps,defineEmits } from 'vue';
  defineProps(['modalValue']);
  defineEmits(['update:modalValue']);
</script>
<template>
  <h3>自定义组件配合v-model使用{{ modalValue }}</h3>
  <input :value="modalValue" @input="$emit('update:modalValue',$event.target.value)" />
</template>

另一种实现方案

借助组件的computed,使用一个可写的,同时具有 getter 和 setter 的计算属性。get 方法需返回 modelValue prop,而 set 方法需触发相应的事件。

<NextCustomInput
    v-model="nextMessage"
 />
<script setup>
import { computed } from 'vue';
  const props=defineProps(['modelValue']);
  const emit=defineEmits(['update:modelValue']);

  const value=computed({
    get(){
        return props.modelValue;
    },
    set(value){
      emit('update:modelValue',value);
    }
  })
</script>

<template>
  <h3>另一种方案自定义组件v-model实现{{ value }}</h3>
  <input v-model="value" />
</template>

v-model的参数(起别名)

<MyComponent v-model:title="bookTitle" />

就是这么简单,子组件应声明一个titleprop,通过update:title 事件更新父组件值

<AliasInput v-model:title="nextMessage" />
<script setup>
   const props=defineProps(['title']);
   const emits=defineEmits(['update:title']);
</script>
<template>
  <h3>自定义组件v-model起别名</h3>
  <input
    :value="props.title"
    @input="$emit('update:title',$event.target.value)"
  />
</template>

多个 v-model 绑定

<MoreInput v-model:one="message" v-model:two="nextMessage" />
<script setup>
const props = defineProps(["one", "two"]);
const emits = defineEmits(["update:one", "update:two"]);
</script>
<template>
  <h3>一个组件多个model</h3>
  <input @input='$emit("update:one",$event.target.value)' :value='one' />
  <input @input='$emit("update:two",$event.target.value)' :value='two' />
</template>

自定义指令

实战

点击跳转

简介

一个自定义指令由一个包含类似组件生命周期钩子的对象来定义。钩子函数会受到指令所绑定的元素作为其参数。

<script setup>
   const vFocus={
       mounted:(el)=>el.focus()
   }
</script>
<template>
   <input v-focus />
</template>
export default {
  setup() {
    /*...*/
  },
  directives: {
    // 在模板中启用 v-focus
    focus: {
      /* ... */
    }
  }
}

<script setup> 中,任何以 v 开头的驼峰式命名的变量都可以被用作一个自定义指令。

将自定义指令注册到应用层级

const app = createApp({})

// 使 v-focus 在所有组件中都可用
app.directive('focus', {
  /* ... */
})

指令钩子

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) {}
}

钩子参数

指令的钩子会传递以下几种参数:

  • el:指令绑定到的元素。这可以用于直接操作 DOM。
  • binding:一个对象,包含以下属性。
    • value:传递给指令的值。例如在 v-my-directive="1 + 1" 中,值是 2
    • oldValue:之前的值,仅在 beforeUpdateupdated 中可用。无论值是否更改,它都可用。
    • arg:传递给指令的参数 (如果有的话)。例如在 v-my-directive:foo 中,参数是 "foo"
    • modifiers:一个包含修饰符的对象 (如果有的话)。例如在 v-my-directive.foo.bar 中,修饰符对象是 { foo: true, bar: true }
    • instance:使用该指令的组件实例。
    • dir:指令的定义对象。
  • vnode:代表绑定元素的底层 VNode。
  • prevNode:之前的渲染中代表指令所绑定元素的 VNode。仅在 beforeUpdateupdated 钩子中可用。

举例来说,像下面这样使用指令:

<div v-example:foo.bar="baz">

binding 参数会是一个这样的对象:

{
  arg: 'foo',
  modifiers: { bar: true },
  value: /* `baz` 的值 */,
  oldValue: /* 上一次更新时 `baz` 的值 */
}

和内置指令类似,自定义指令的参数也可以是动态的。举例来说:

<div v-example:[arg]="value"></div>

这里指令的参数会基于组件的 arg 数据属性响应式地更新。

简化形式

当自定义指令仅仅需要在mounted和updated上实现相同的行为,不需要其他钩子

<div v-color="color"></div>
app.directive('color', (el, binding) => {
  // 这会在 `mounted` 和 `updated` 时都调用
  el.style.color = binding.value
})

对象字面量

<div v-demo="{ color: 'white', text: 'hello!' }"></div>
<!-- MyComponent 的模板 -->

<div> <!-- v-demo 指令会被应用在此处 -->
  <span>My component content</span>
</div>

在组件上使用

组件上使用自定义命令,始终应用于组件的根节点

<MyComponent v-demo="test" />
<!-- MyComponent 的模板 -->

<div> <!-- v-demo 指令会被应用在此处 -->
  <span>My component content</span>
</div>

需要注意的是组件可能含有多个根节点。当应用到一个多根组件时,指令将会被忽略且抛出一个警告。和 attribute 不同,指令不能通过 v-bind="$attrs" 来传递给一个不同的元素。总的来说,推荐在组件上使用自定义指令。


文章作者: ycs
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 ycs !
 上一篇
下一篇 
蔡元培就职演说 蔡元培就职演说
平时则放荡冶游,考试则熟读讲义,不问学问之有无,惟争分数之多寡;试验既终,书籍束之高阁,毫不过问,敷衍三四年,潦草塞责,文凭到手,即可借此活动于社会,岂非与求学初衷大相背驰乎?光阴虚过,学问毫无,是自误也。
2022-10-31
  目录