Vue 事件修饰符

事件修饰符概述

事件修饰符是一种特殊的语法,用于指示 Vue 对 DOM 事件监听器行为进行特定的修改。

事件修饰符的基本用法:

<p v-on:事件类型.事件修饰符="xxx"></p>
<p v-on:事件类型.事件修饰符1.事件修饰符2="xxx"></p>

Vue 的基本事件修饰符:

名称描述
.stop阻止事件冒泡
.prevent阻止默认事件行为
.capture事件捕获,和事件冒泡相反
.self将事件绑定到自身,只有自身才能触发事件
.once事件只触发一次

.stop

在 Vue 中,.stop 修饰符用于阻止事件冒泡。

当一个元素触发了某个事件,如果该元素的父元素也注册了相同的事件,默认情况下,事件会从子元素向上冒泡到父元素,从而触发父元素的相同事件处理函数,而使用 .stop 修饰符可以阻止这种冒泡行为。


示例:.stop 修饰符

入口页面(index.html):

<div id="app">
    <!--
        button作为div的子元素
        当点击button时,按钮点击事件会冒泡(上浮)到div
        故先触发button的点击事件,再触发div的点击事件(即事件冒泡)
        .stop加给谁,谁的事件就不会冒泡
    -->
    <div @click="doParent" style="width: 200px; height: 200px; background-color: green">
        <button @click="doThis">事件冒泡</button>
        <button @click.stop="doThis">阻止事件冒泡</button>
    </div>
</div>

<script>
    let vm = new Vue({
        el: '#app',
        methods : {
            doParent() {
                console.log("父元素被点击")
            },
            doThis() {
                console.log("子元素被点击")
            }
        }
    })
</script>

示例效果:


.prevent

在 Vue 中,.prevent 修饰符用于阻止默认事件。

默认事件是指浏览器为某些元素或操作预设的行为,如当点击一个链接时,浏览器会默认跳转到链接指向的页面,使用 .prevent 修饰符可以阻止这些默认行为的发生。


示例:.prevent 修饰符

入口页面(index.html):

<div id="app">
    <!--
        a标签的默认行为时跳转
        阻止a标签的默认行为,页面不会进行跳转
    -->
    <a href="https://www.duozai.cn/">不阻止默认行为</a>
    <a href="https://www.duozai.cn/" @click.prevent>阻止默认行为</a>
</div>

<script>
    let vm = new Vue({
        el: '#app'
    })
</script>

示例效果:


.capture

在 Vue 中,.capture 修饰符用于事件捕获阶段触发处理函数。

在 DOM 事件流中,事件的传播分为三个阶段:捕获阶段、目标阶段和冒泡阶段,默认情况下事件处理程序在冒泡阶段被触发, 使用 .capture 修饰符可以让事件在捕获阶段被处理。


示例:.capture 修饰符

入口页面(index.html):

<div id="app">
    <!--
        事件捕获的执行顺序由外向内,与事件冒泡相反
        点击button按钮,div的点击事件被捕获
        先触发div的点击事件,再触发button的点击事件
    -->
    <div @click.capture="doParent" style="width: 200px; height: 200px; background-color: green">
        <button @click="doThis">事件捕获1</button>
        <button @click="doThis">事件捕获2</button>
    </div>
</div>

<script>
    let vm = new Vue({
        el: '#app',
        methods : {
            doParent() {
                console.log("父元素被点击")
            },
            doThis() {
                console.log("子元素被点击")
            }
        }
    })
</script>

示例效果:


.self

在 Vue 中,.self 修饰符用于事件监听,只有当事件是在自身元素而非子元素上触发时,才会执行相应的处理函数。


示例:.self 修饰符

入口页面(index.html):

<div id="app">
    <!--
        button存在事件冒泡
    -->
    <div @click="doParent" style="width: 200px; height: 200px; background-color: green">
        <button @click.self="doThis">01</button>
    </div>

    <!--
        div的点击事件只有自身被点击才会被触发
        button虽然存在事件冒泡,但是不会影响到div的点击事件
    -->
    <div @click.self="doParent" style="width: 200px; height: 200px; background-color: red">
        <button @click="doThis">02</button>
    </div>
</div>

<script>
    let vm = new Vue({
        el: '#app',
        methods : {
            doParent() {
                console.log("父元素被点击")
            },
            doThis() {
                console.log("子元素被点击")
            }
        }
    })
</script>

示例效果:


.once

在 Vue 中,.once 修饰符用于事件监听,它会使绑定的事件只触发一次,之后再触发相应事件时将不会有响应。


示例:.once 修饰符

入口页面(index.html):

<div id="app">
    <!-- 按钮的点击事件只会被触发一次 -->
    <p v-text="count"></p>
    <button @click.once="count+=Math.random()">点击</button>
</div>

<script>
    let vm = new Vue({
        el: '#app',
        data: {
            count: 0
        }
    })
</script>

示例效果:


Vue 组件

组件概述

Vue 可以进行组件化开发,组件是 Vue 的基本结构单元。使用组件开发,能实现复杂的页面结构,提高代码的可复用性。

Vue 组件的基本用法:

<div id="app">
    <!-- 调用组件 -->
    <my-component></my-component>
    <my-component></my-component>
</div>

<script>
    // Vue.component():全局定义组件
    // 参数1:组件名称,使用短横杠命名法
    // 参数2:组件的配置对象
    Vue.component('组件名称', {
        // data:组件使用的数据
        // data必须是一个函数,防止data被复用
        data() {
            return {
                // ...
            }
        },
        // methods:组件内的方法
        methods: {
            // ...
        },
        // template:组件模板,即组件显示的内容
        template: ''
    })
</script>

示例:全局注册组件

入口页面(index.html):

<div id="app">
    <button-component></button-component>
    <button-component></button-component>
    <button-component></button-component>
</div>

<script>
    Vue.component('button-component', {
        data() {
            return {
                count: 0
            }
        },
        template: '<button @click="count++">被单击了{{count}}次</button>'
    })

    let vm = new Vue({
       el: '#app',
    })
</script>

示例效果:


局部注册组件

组件还可以进行局部注册,局部注册的组件,只对当前的 Vue 实例有效。

局部注册组件的基本用法:

<script>
    let vm = new Vue({
        el: '#app',
        // components:局部注册组件
        components: {
            // 组件名称,只能使用驼峰命名法
            组件名称: {
                // ...
            }
        }
    })
</script>

示例:局部注册组件

入口页面(index.html):

<div id="app">
    <my-component></my-component>
    <my-component></my-component>
    <my-component></my-component>
</div>

<script>
    let vm = new Vue({
        el: '#app',
        components: {
           myComponent: {
               data() {
                   return {
                       count: 0
                   }
               },
               template: '<button @click="count++">被单击了{{count}}次</button>'
           }
        }
    })
</script>

示例效果:


template 模板

Vue 提供了 template 组件标签来定义结构的模板,可以在标签中书写复杂的 HTML 代码,然后将模板绑定给组件。

template 组件的基本用法:

<template id="myTemplate">
    <!-- <template>标签内必须有且只能有一个根标签 -->
    <div>
        <!-- ... -->
    </div>
</template>

<script>
    let vm = new Vue({
        el: '#app',
        components: {
            myComponent: {
                // 使用id选择器为组件绑定模板
                template: '#myTemplate'
            }
        }
    })
</script>

示例:template 模板

入口页面(index.html):

<div id="app">
    <my-component></my-component>
    <my-component></my-component>
    <my-component></my-component>
</div>

<template id="myTemplate">
    <div>
        点击以下按钮:
        <button @click="count++">被单机了{{count}}次</button>
    </div>
</template>

<script>
    let vm = new Vue({
        el: '#app',
        components: {
            myComponent: {
                data() {
                    return {
                        count: 0
                    }
                },
                template: '#myTemplate'
            }
        }
    })
</script>

示例效果:


组件插槽

Vue 中的组件中使用 template 模板定义 HTML 结构,为了方便使用 template 公共模板结构,Vue 提出了插槽(Slots)的概念,插槽就是定义在组件内部的 template 模板。

组件插槽的基本用法:

<my-component>
    <!-- 组件内部的模板 -->
    <div>xxx</div>
</my-component>

<template id="myComponent">
    <div>
        <!-- <slot>:用于接收自定义组件的内容 -->
        <slot></slot>
    </div>
</template>

示例:组件插槽

入口页面(index.html):

<div id="app">
    <my-component>
        <div>Hello,Vue</div>
        <div>Hello,Component</div>
        <div>Hello,Slots</div>
    </my-component>
</div>

<template id="myTemplate">
    <div>
        Hello,MyTemplate:
        <slot></slot>
    </div>
</template>

<script>
    Vue.component('my-component', {
        template: '#myTemplate'
    })

    let vm = new Vue({
        el: '#app'
    })
</script>

示例效果:


具名插槽

当有多个插槽时,可以为插槽命名。

具名插槽的基本用法:

<my-component>
    <template v-slot:插槽名称>
    <div>xxx</div>
    </template>
    <template v-slot:插槽名称>
    <div>xxx</div>
    </template>
</my-component>

<template id="myComponent">
    <div>
        <slot name="插槽名称"></slot>
        <slot name="插槽名称"></slot>
    </div>
</template>

示例:具名插槽

入口页面(index.html):

<div id="app">
    <my-component>
        <template v-slot:slot01>
            <div>Hello,Vue</div>
        </template>
        <template v-slot:slot02>
            <div>Hello,Component</div>
        </template>
        <template v-slot:slot03>
            <div>Hello,Slots</div>
        </template>
    </my-component>
</div>

<template id="myTemplate">
    <div>
        Hello,MyTemplate:
        <slot name="slot01"></slot>
        <hr>
        <slot name="slot02"></slot>
        <hr>
        <slot name="slot03"></slot>
    </div>
</template>

<script>
    Vue.component('my-component', {
        template: '#myTemplate'
    })

    let vm = new Vue({
        el: '#app'
    })
</script>

示例效果:


组件数据传递

组件实例内部具有自己的独立作用域,不能直接被外部访问。

组件之间的数据传递需要借助一些工具来实现,且组件之间的数据传递是单向的。

父组件向子组件传递数据,使用 props 传值。


示例:父组件向子组件传递数据

入口页面(index.html):

<div id="app">
    <!-- 调用子组件,设置子组件的"属性",进行父向子传值 -->
    <my-component title="A"></my-component>
    <my-component title="AB"></my-component>
    <my-component  title="ABC"></my-component>
</div>

<script>
    let vm = new Vue({
        el: '#app',
        components: {
            myComponent: {
                // props:接收父组件给子组件传递的数据(参数列表)
                props: ['title'],
                // data:组件使用的数据
                data() {
                    return {
                        count: 0
                    }
                },
                template: '<button @click="count++">按钮{{title}}被单击了{{count}}次</button>'
            }
        }
    })
</script>

示例效果:


子组件向父组件传递数据,使用 $emit 传值。


示例:子组件向父组件传递数据

入口页面(index.html):

<div id="app">
    <!-- 调用父组件 -->
    <parent></parent>
</div>

<template id="parent">
    <div style="border: green solid 2px; padding: 20px">
        <!--
            父组件调用子组件
            @child-fn:给调用的子组件绑定了一个child-fn事件
            注意事件的名称要使用短横杠命名法,驼峰命名法可能无法使用
            @child-fn:子组件向父组件发送消息时,触发父组件的transContent方法
        -->
        <child @child-fn="transContent"></child>
        子组件传来的消息:{{message}}
    </div>
</template>

<template id="child">
    <div style="border: blue solid 2px; padding: 20px;">
        <input type="text" v-model="message">
        <button @click="send">发送</button>
    </div>
</template>

<script>
    // 注册父组件
    Vue.component('parent', {
        template: '#parent',
        data() {
            return {
                message: ''
            }
        },
        methods: {
            // playload:子组件传递给父组件的数据
            transContent(playload) {
                this.message = playload
            }
        }
    })

    Vue.component('child', {
        template: '#child',
        data() {
            return {
                message: '我是子组件'
            }
        },
        methods : {
            send() {
                // this.$emit:向父组件传递子组件的message
                // child-fn:调用子组件时绑定的事件名称
                // this.message:要传递的数据
                this.$emit('child-fn', this.message)
            }
        }
    })

    let vm = new Vue({
       el: '#app',
    })
</script>

示例效果:


组件切换

实现组件切换的方式:

  • 使用 v-if 控制组件的显示和隐藏。
  • 使用 component 标签的 is 属性绑定组件名称。

示例:组件切换

入口页面(index.html):

<div id="app">
    <a href="#" @click.prevent="flag ? flag : flag=!flag">登录</a>
    <a href="#" @click.prevent="flag ? flag=!flag : flag">注册</a>
    <login v-if="flag"></login>
    <register v-else></register>

    <br>

    <a href="#" @click.prevent="componentName='login'">登录</a>
    <a href="#" @click.prevent="componentName='register'">注册</a>
    <component v-bind:is="componentName"></component>
</div>

<script>
    let vm = new Vue({
        el: '#app',
        data: {
            flag: true,
            componentName: 'login'
        },
        components : {
            login: {
                template: '<div>登录页面</div>'
            },
            register: {
                template: '<div>注册页面</div>'
            }
        }
    })
</script>

示例效果:


Vue 生命周期

生命周期概述

每个 Vue 实例在被创建时都要经过一系列的初始化过程,如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。

Vue 的生命周期:

在 Vue 的生命周期的过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会。

Vue 生命周期对应的钩子函数:

名称描述
beforeCreate实例创建之前执行
created实例创建之后执行
beforeMount页面挂载成功之前执行
mounted页面挂载成功之后执行
beforeUpdate组件更新之前执行
updated组件更新之后执行
beforeDestory实例销毁之前执行
destoryed实例销毁之后执行

beforeCreate/created

beforeCreate:在实例创建之前被调用,此时数据观测和事件配置尚未完成。

created:此时实例已经创建完成,数据观测和事件配置完成,但 DOM 尚未挂载,可进行一些数据请求和初始化操作。


示例:实例创建生命周期

入口页面(index.html):

<div id="app">
    <h3>{{name}}</h3>
</div>

<script>
    let vm = new Vue({
        el: '#app',
        data: {
            name: 'Vue实例创建成功!'
        },
        beforeCreate() {
            // 实例创建前:无法访问Vue实例的数据
            console.log('实例创建之前:' + this.name)
        },
        created() {
            // 实例创建后:可以访问Vue实例的数据
            console.log('实例创建之后:' + this.name)
        }
    })
</script>

示例效果:


beforeMount/mounted

beforeMount:在挂载之前被调用,此时模板已经编译完成,但尚未渲染到 DOM 中。

mounted:挂载完成,即模板已经渲染到 DOM 中,可以进行 DOM 操作和获取 DOM 元素。


示例:页面挂载生命周期

入口页面(index.html):

<div id="app">
    <h3>{{name}}</h3>
</div>

<script>
    let vm = new Vue({
        el: '#app',
        data: {
            name: 'Vue实例创建成功!'
        },
        beforeMount() {
            // this.$el.innerHTML:获取#app的HTML内容
            // 页面挂载前:数据未被渲染
            console.log('页面挂载之前:' + this.$el.innerHTML)
        },
        mounted() {
            // 页面挂载后:数据已经被渲染
            console.log('页面挂载之后:' + this.$el.innerHTML)
        }
    })
</script>

示例效果:


beforeUpdate/updated

beforeUpdate:数据更新之前调用,可在此时获取更新前的状态。

updated:数据更新完成后调用,可在此进行基于更新后 DOM 的操作。


示例:数据更新生命周期

入口页面(index.html):

<div id="app">
    <h3>{{name}}</h3>
    <button @click="showInfo">点我</button>
</div>

<script>
    let vm = new Vue({
        el: '#app',
        data: {
            name: 'Vue实例创建成功!'
        },
        methods: {
            showInfo() {
                this.name = '点击啦!'
            }
        },
        beforeUpdate() {
            // 数据更新前:数据未被渲染
            // 数据是新的,但是还没渲染到视图页面上
            console.log('数据更新之前:' + this.name)
            console.log('数据更新之前:' + this.$el.innerHTML)
        },
        updated() {
            // 数据更新后:数据已经被渲染
            // 数据和视图页面都是新的
            console.log('数据更新之后:' + this.name)
            console.log('数据更新之后:' + this.$el.innerHTML)
        }
    })
</script>

示例效果:


beforeDestroy/destroyed

beforeDestroy:实例销毁之前调用,可进行一些清理操作。

destroyed:实例销毁后调用,解绑相关的事件监听器和定时器等。


示例:实例销毁生命周期

入口页面(index.html):

<div id="app">
    <h3>{{name}}</h3>
    <button @click="showInfo">点我</button>
</div>

<script>
    let vm = new Vue({
        el: '#app',
        data: {
            name: 'Vue实例创建成功!'
        },
        methods: {
            showInfo() {
                this.name = '点击啦!'
            }
        },
        beforeDestroy() {
            // 实例销毁前:绑定、事件还未失效
            console.log('实例销毁之前')
        },
        destroyed() {
            // 实例销毁后:绑定、事件失效
            console.log('实例销毁之后')
        }
    })

    // 销毁实例
    vm.$destroy()
</script>

示例效果: