深入自定义事件和原生DOM事件

自定义事件

  1. 在组件上标签上添加的事件就是自定义事件,不管系统是否带这些事件

  2. 比如添加<自定义组件 @自定义事件 = "回调函数"></自定义组件> 那么@自定义事件在自定义组件上就是自定义事件

  3. <自定义组件 @click = "回调函数"></自定义组件>,那么@click就是自定义事件

  4. 添加的事件如果没有传入参数,那么输出就是undefined

  • 如图,传入了参数,单击button,输出为10,如果没有传递参数10,则输出undefined

  1. 自定义组件上绑定原生DOM事件使用native和不使用的区别,如下图
  • 如图

自定义事件示例

App.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<template>
<div id="app">
<!-- 给自定义组件绑定自定义事件为click事件,并将事件的回调函数绑定在事件check上 -->
<MyComponent @click="check"></MyComponent>
</div>
</template>

<script>
import MyComponent from '@/components/MyComponent'
export default {
name: 'App',
components: {
MyComponent,
},
methods:{
//给自定义组件绑定的自定义事件
check(event){
console.log("event",event);
}
}
}
</script>

自定义组件MyComponent

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
<template>
<div class="my">
这是我的组件
<!-- 用于触发给其绑定的自定义事件 -->
<button @click="chufa">触发自定义事件</button>
</div>
</template>

<script>
export default {
name: 'MyComponent',
methods:{
//触发自定义事件click,并传入一个参数100
chufa(){
this.$emit("click",1000);
}
}
}
</script>

<style scoped>
.my{
border: 1px solid red;
}
</style>

效果

原生DOM事件

  1. 在HTML标签上添加就是原生DOM事件,比如说@click @mousemove这些系统自带的原生事件

  2. 添加的事件如果没有传入参数,那么系统会默认传入event参数

    1
    2
    3
    <button @click="test1">我是按钮1</button>
    //等同于
    <button @click="test1($event)">我是按钮2</button>

vue自定义的事件在html标签和组件标签上的区别

  1. 在html标签上添加自定义事件无意义,所以自定义事件是给组件标签添加的

  2. 事件名可以任意,也可以和原生DOM事件名相同,但是是自定义的

  3. 如果自定义事件上想绑定原生的事件,那么就需要在事件对象名称后面添加 .native 并且绑定的事件添加在添加到组件根元素上,通过委派的形式使得子元素可以被触发

  • 如图

深入理解v-model

  1. 首先我们需要知道v-model在HTML标签上的原理

  2. v-model原来写法

    1
    2
    // 标准v-model写法 普通写法
    <input type="text" v-model="msg" />
  3. v-model拆解写法(等同于上方直接写v-model)

    • 先使用v-bind绑定一个值给输入框,再添加事件@input,当触发就将当前触发对象的值传递给v-bind绑定的那个值,就这样子完成了数据更新
    1
    2
    // v-model拆解写法
    <input type="text" :value="msg" @input="msg = $event.target.value"/>

那么v-model当中在自定义组件要怎么实现呢?

  • 根据v-model在原生DOM上拆解的写法,我们应该这样子写( 以自定义组件CustomInput为例 )

  • 需要知道的是: 在自定义组件当中,$event就是$emit当中第二个参数传递过来的数据

  • 组件

    1
    2
    3
    4
    5
    6
    <!--父亲给CustomInput传递 "msg2" 数据  -->
    <!--儿子要使用props接收( props:["value"] ),并且传递了自定义事件input -->
    <CustomInput :value="msg" @input="msg = $event"></CustomInput>
    <!--上面这一行代码可以简写为下面这一行但是子组件不能简写-->
    <!--必须要写下面这些内容!!!!!!!!!!!!!! -->
    <CustomInput v-model="msg"></CustomInput>
  • 组件 CustomInput.vue

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    <template>
    <div>
    <input type="text"
    // 绑定从父亲传递过来的参数
    :value = "value"
    // 绑定原生DOM事件@input,输入框内容发生改变,就将改变的值作为$emit的第二个参数
    // 并通过$emit触发父亲给子组件的自定义事件input
    @input="$emit('input',$event.target.value)"/>
    </div>
    </template>
    <script>
    export default {
    name: "CustomInput",
    // 接收从父亲传递过来的:value="msg"的值
    // 如果是简写形式(<CustomInput v-model="msg"></CustomInput>)也是从value接收
    props:["value"]
    }
    </script>

.sync修饰符实现父子数据同步和.sync和v-model的区别

1
2
[需求]
父亲向儿子传递一个值叫money),儿子每次都花100块钱,要求儿子花了多少钱,爸爸那边可以同步

第一次(无效果) (直接传递数据给儿子, 儿子每次单击都花100块钱)

  • 父亲 (直接传递数据给儿子)

    1
    2
     <!-- 会弹出警告说数据不同步 --> 
    <Child :money="moneyFather"></Child>

    弹出警告

  • 儿子

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    <template>
    <span>小明每次花100元</span>

    <!-- 每次单击爸爸的钱就减少100 -->
    <button @click="money = money - 100">单击我花钱</button>

    <!-- 显示爸爸剩余多少钱 -->
    爸爸还剩 {{ money }} 元
    </template>

    <script>
    export default {
    name:"Child",
    //接收父亲传递过来的信息,告诉了我现在父亲有多少钱
    props:["money"]
    }

    </script>
  • 结果

    • 儿子花钱按钮被单击,儿子当中的父亲的钱数量被改变,但是父亲兜兜里面的钱没有变化

第二次(有效果) 数据传递给儿子,但是儿子每次单击花钱的时候就告诉父亲我花钱了

  • 父亲 ($event在自定义组件当中是$emit传递的参数 父亲收到儿子传递过来的金钱数,就更新自己的金钱数)

    1
    2
    3
    4
    5
    <!-- $event在自定义组件当中是$emit传递的参数 -->
    <!-- @update:xxx为固定格式,不可以更改,xxx为绑定的数据属性也就是v-bind:xxx="值"(当中的xxx)
    对应简写属性 :xxx="值" 当中的xxx
    -->
    <Child :money="moneyFather" @update:money="moneyFather = $event"></Child>
  • 儿子每次单击爸爸的钱就减少100 并且告诉爸爸 (通过$emit),并且传递 金钱-100的 值 给父亲

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    <template>
    <div style="background: #ccc; height: 50px">
    <span>小明每次花100元</span>

    <!-- 每次单击爸爸的钱少100 并且告诉爸爸 $emit,并且传递 金钱-100的值 给父亲-->
    <!-- update:money 为自定义事件名称 -->
    <button @click="$emit('update:money', money - 100)">花钱</button>

    <!-- 显示爸爸剩余多少钱 -->
    爸爸还剩 {{ money }} 元
    </div>
    </template>

    <script type="text/ecmascript-6">
    export default {

    name: "Child",

    // 接收父亲传递过来的信息,告诉了我现在父亲有多少钱
    props: ["money"],
    };
    </script>

第三次(有效果)等同于第二次简写

  • 父亲 (使用sync修饰符)

    1
    2
    3

    <Child :money.sync="moneyFather"></Child>

  • 儿子 (和第二次一样) 每次单击爸爸的钱就减少100 并且告诉爸爸 (通过$emit),并且传递 金钱-100的 值 给父亲

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    <template>
    <div style="background: #ccc; height: 50px">
    <span>小明每次花100元</span>

    <!-- 每次单击爸爸的钱少100 并且告诉爸爸 $emit,并且传递 金钱-100的值 给父亲-->
    <!-- update:money 为自定义事件名称 -->
    <button @click="$emit('update:money', money - 100)">花钱</button>

    <!-- 显示爸爸剩余多少钱 -->
    爸爸还剩 {{ money }} 元
    </div>
    </template>

    <script type="text/ecmascript-6">
    export default {

    name: "Child",

    // 接收父亲传递过来的信息,告诉了我现在父亲有多少钱
    props: ["money"],
    };
    </script>

v-model的使用在数据同步的使用

  • 父亲依旧是通过:value向子传递 $event依旧是子通过$emit传递过来的数据

    • 自定义组件$event返回的都是$emit传递过来的数据)
    1
    <Child :value="moneyFather" @input="moneyFather = $event"></Child>
  • 儿子

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    <template>
    <div style="background: #ccc; height: 50px">
    <span>小明每次花100元</span>

    <!-- 每次单击爸爸的钱少100 并且告诉爸爸 $emit,并且传递 金钱-100的值 给父亲-->

    <button @click="$emit('input', value - 100)">花钱</button>

    <!-- 显示爸爸剩余多少钱 -->
    爸爸还剩 {{ value }} 元
    </div>
    </template>

    <script type="text/ecmascript-6">
    export default {

    name: "Child",

    // 接收父亲传递过来的信息,告诉了我现在父亲有多少钱
    props: ["value"],
    };
    </script>

.sync和v-model在数据同步使用区别

  • v-model和.sync都可以实现父子组件数据同步,下面是约定成俗的规定
    • v-model 是当子组件当中有表单类元素的时候使用
    • .sync 是当子组件当中不是表单类元素的时候使用

自定义带hover提示的el-button和$attrs和$listeners的使用

  • 前置知识
    • el-button
      • 如果想带图标,那么添加icon属性可以,注意: icon属性里面的值都是以el-icon-xxx形式出现的
      • element-ui的icon库
  • 原来的el-button组件并没有鼠标悬停上去就出现提示的功能,我们可以通过对el-button进行再次包装

简易的包装(但是不能达到自己去传入配置设置的要求)

  • 如图 MyButton.vue对el-button进行包装

    1
    2
    3
    4
    5
    <template>
    <a title="这个是提示框">
    <el-button></el-button>
    </a>
    </template>
  • 包装后的使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    <template>
    <div>
    <h2>自定义带Hover提示的按钮</h2>
    <!-- 使用二次封装后的 -->
    <MyButton></MyButton>
    </div>
    </template>
    <script>
    <!-- 引入二次封装后的el-button组件 -->
    import MyButton from "./myButton.vue"
    export default {
    name:"AttrsListenersTest",
    <!-- 注册使用自定义组件 -->
    componets:{
    MyButton
    }
    }
    </script>
  • 效果图

使用$attrs和$listeners进行复杂包装(可自定义参数效果)

  • 前面要知道的

    • 同等效果
      • 传递数据给组件,可以使用 :key="value"或者 直接省略 : ,直接写 key= “value” 也是可以的,不过有冒号的 :key="value" value为js代码,没有冒号的 key=”value” value值为字符串!!
    1
    2
    3
    <MyButton aa="10"></MyButton>
    <!-- 上一行和下一行是同等效果 -->
    <MyButton aa="10"></MyButton>
    • 不同等效果

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      <!-- 传递字符串 "b" -->
      <MyButton aa="b"></MyButton>

      <!-- 传递变量b的值,即为10 -->
      <MyButton aa="b"></MyButton>

      <script>
      <!-- 引入二次封装后的el-button组件 -->
      import MyButton from "./myButton.vue"
      export default {
      name:"AttrsListenersTest",
      data(){
      return {
      b:10
      }
      },
      <!-- 注册使用自定义组件 -->
      componets:{
      MyButton
      }
      }
      </script>

  • $attrs 获取传递给组件的所有属性,它会排除 props已经声明接收的属性 以及class,style这二个样式

    • 如图

  • 排除props接收到了的

    • 如图

  • $listeners 获取父组件传递给子组件的所有自定义事件监听组成的对象

    • 如图

  • $attrs$listeners一键绑定在组件上

    • 可以通过v-bind一次性把父组件传递过来的属性添加给子组件(v-bind不可以简写为:)
    • 可以通过v-on 一次性把父组件传递过来的事件监听添加给子组件(v-on不可以简写@)
    • 如图

$children和$parent和$refs的使用

  • this.$refs放在HTML和组件标签身上的区别
    • this.$refs.名称 放在html标签身上拿到的就是这个DOM元素
    • this.$refs放在组件标签身上拿到的就是组件对象本身

this.$refs妙用

  • 可以直接通过this.$refs.名称来获取组件,并且在组件当中去操作这个获取到的组件里面的数据

    如图所示(父亲) 父亲直接通过this.$refs.son.money就操控了儿子和女儿的钱

$children 和 $parent的妙用

$parent

  • 前提:

    • 必须只有一个父亲才可以使用!如果这个组件有多个父亲,那么就不可以用!(为什么有多个父亲,因为存在组件复用的情况!)
  • 如图,儿子通过 this.$parent.money来获取父亲的钱并且修改

$children

  • 获取当前组件的所有的子组件,返回子组件的数组,(无顺序,不能说[0]一定就是儿子,[1]就一定是女儿)

  • 如图

本案例当中,均在配置对象当中书写了data数据

mixin混入的基本使用

  • @官方API:混入 (mixin) 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。一个混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被“混合”进入该组件本身的选项。

  • 说通俗点就是混入就是将别人的东东变为自己的东东,这里的混入只说一些基本的使用

  • 先来看示例吧

mixin/test.js文件的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const mixins = {
data(){
return {
sex:"男",
age:"18",
}
},
methods:{
sayHello(){
console.log("你好,世界");
}
}
};
export default mixins;

App.vue

使用mixin/test.js的混入

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
<template>
<div>
<div>姓名:{{name}}</div>
<div>年龄:{{sex}}</div>
<div>性别:{{age}}</div>
<button @click="sayHello">单击我 - 说hello</button>
<button @click="sayThankyou">单击我 - 说谢谢</button>
</div>
</template>

<script>
// 引入混入
import myMixin from "@/mixin/test";
export default {
name: "",
//使用混入
mixins:[myMixin],
data() {
return {
name:"李白",
}
},
methods:{
sayThankyou(){
console.log("谢谢你");
}
}

};
</script>

功能测试是否正常,可以看到,可以正常显示和调用函数

  • 当然了,还有很多情况,比如说混入的时候,当mixin/test.js里面的数据或者方法和App.vue当中的数据或者方法冲突的时候要怎么解决之类的,具体看官网吧~ @官网

之前做的一个混入的图