vue-组件进阶

目录

一、vue生命周期

1、组件生命周期

2、应用全局的配置选项

3、组件的注册方式

二、组件props属性进行验证

1、对prop属性进行验证 

2、props的只读性质

 3、组件数据注入

三、组件Mixin技术

1、使用Mixin来定义组件

2、Mixin选项的合并

3、全局Mixin

四、使用自定义指令

1、认识自定义指令

2、自定义指令的参数

五、使用组件的Teleport功能开发全局弹窗


一、vue生命周期

1、组件生命周期

<div class="app"></div>
    <script>
        const app = new Vue({
            el: '.app',
            beforeCreate() {
                console.log('组件即将创建前!');
            },
            created() {
                console.log('组件创建完成!');
            },
            beforeMount() {
                console.log('组件即将挂载前!');
            },
            mounted() {
                console.log('组件挂载完成!');
            },
            beforeUpdate() {
                console.log('组件即将更新前!');
            },
            updated() {
                console.log('组件更新完成!')
            },
            activated() {
                console.log('被缓存的组件激活时调用!');
            },
            deactivated() {
                console.log('被缓存的组件停用时调用!');
            },
            beforeUnmount() {
                console.log('组件即将被卸载前调用!');
            },
            unmounted() {
                console.log('组件被卸载后调用!');
            },
            errorCaptured: (err, vm, info) => {
                console.log('捕获到来自子组件的异常时调用!');
            },
            renderTracked({ key, target, type }) {
                console.log('虚拟DOM重新渲染时调用!');
            },
            renderTriggered({ key, target, type }) {
                console.log('虚拟DOM被触发渲染时调用!');
            },
        })
    </script>

 结果:

 从控制台打印的信息可以看到,本次页面渲染过程中执行了四个组件的生命周期方法,这是由于我们使用的是vue根组件,页面的渲染过程中只执行了组件的创建和挂载过程,并没有执行卸载过程。

<div class="app">
        <div class="text" v-if="show">hello world</div>
        <button @click="showHandle">点我</button>
    </div>
    <script>
        const app = new Vue({
            el: '.app',
            data() {
                return {
                    show: false
                }
            },
            methods: {
                showHandle() {
                    this.show = !this.show
                }
            },
            beforeCreate() {
                console.log('组件即将创建前!');
            },
            created() {
                console.log('组件创建完成!');
            },
            beforeMount() {
                console.log('组件即将挂载前!');
            },
            mounted() {
                console.log('组件挂载完成!');
            },
            beforeUpdate() {
                console.log('组件即将更新前!');
            },
            updated() {
                console.log('组件更新完成!')
            },
            activated() {
                console.log('被缓存的组件激活时调用!');
            },
            deactivated() {
                console.log('被缓存的组件停用时调用!');
            },
            beforeUnmount() {
                console.log('组件即将被卸载前调用!');
            },
            unmounted() {
                console.log('组件被卸载后调用!');
            },
            errorCaptured: (err, vm, info) => {
                console.log('捕获到来自子组件的异常时调用!');
            },
            renderTracked({ key, target, type }) {
                console.log('虚拟DOM重新渲染时调用!');
            },
            renderTriggered({ key, target, type }) {
                console.log('虚拟DOM被触发渲染时调用!');
            },
        })
    </script>

结果:

 点击按钮后:

<div class="app">
        <button @click="count++">点我{{count}}</button>
    </div>
    <script>
        const app = new Vue({
            el: '.app',
            data() {
                return {
                    count: 0
                }
            },
            beforeCreate() {
                console.log('组件即将创建前!');
            },
            created() {
                console.log('组件创建完成!');
            },
            beforeMount() {
                console.log('组件即将挂载前!');
            },
            mounted() {
                console.log('组件挂载完成!');
            },
            beforeUpdate() {
                console.log('组件即将更新前!');
            },
            updated() {
                console.log('组件更新完成!')
            },
            activated() {
                console.log('被缓存的组件激活时调用!');
            },
            deactivated() {
                console.log('被缓存的组件停用时调用!');
            },
            beforeUnmount() {
                console.log('组件即将被卸载前调用!');
            },
            unmounted() {
                console.log('组件被卸载后调用!');
            },
            errorCaptured: (err, vm, info) => {
                console.log('捕获到来自子组件的异常时调用!');
            },
            renderTracked({ key, target, type }) {
                console.log('虚拟DOM重新渲染时调用!');
            },
            renderTriggered({ key, target, type }) {
                console.log('虚拟DOM被触发渲染时调用!');
            },
        })
    </script>

 结果:

点击按钮后:

从两次测试来看,我们可以看出常用的生命周期方法有哪些

renderTracked()虚拟DOM重新渲染时调用、renderTriggered()虚拟DOM被触发渲染时调用、beforeUpdate()组件即将更新前、updated() 组件更新完成。

2、应用全局的配置选项

当调用Vue.createApp方法后,会创建一个Vue应用实例,对于此应用实例,其内部封装了一个config对象,我们可以通过这个对象的一些全局选项来对其配置。常用的配置项有异常与警告捕获配置和全局属性配置。

<div class="app">

</div>

<script>

const { createApp } = Vue

const app = createApp({

data() {

return {

count: 0

}

}

})

app.mount('.app')

app.config.errorHandler = function (err, vm, info) { };

app.config.warnHandler = function (msg, vm, trace) { };

</script>

全局数据可以通过globalProperties全局属性对象进行配置。

<div id="app">
        <test></test>
    </div>
    <script>
        const testComponent = {
            template: `<h2>hello world</h2>`,
            mounted() {
                console.log(this.name);
            },
        }
        const app = Vue.createApp({
            mounted() {
                console.log(this.name);
            },
        })
        app.config.globalProperties = {
            name: 'zs'
        }
        app.component('test', testComponent)
        app.mount('#app')
    </script>

结果:

3、组件的注册方式

组件的注册方式分为全局注册和局部注册两种。直接使用应用实例的component方法注册的组件是全局组件,即可以在应用内的任何地方使用这些组件。 在实例内部通过components注册的组件时局部组件。

如果创建组件时使用驼峰命名,调用组件的时候需要将驼峰改为横线-写法

<div class="app">
        <h2>hello world</h2>
        <global-com></global-com>
    </div>
    <script>
        const app = Vue.createApp({})
        //注册为局部组件
        const localComponent = {
            template: `<div>注册为局部组件</div>`
        }
        //注册为全局组件
        const globalComponent = {
            components: {
                'localCom': localComponent
            },
            template: `<div>
                注册为全局组件
                <local-com></local-com>
                </div>`,

        }
        app.component('globalCom', globalComponent)
        app.mount('.app')
    </script>

结果:

二、组件props属性进行验证

1、对prop属性进行验证 

某个自定义组件需要使用使用props进行外部传值,如果其要接受的参数为一个数值,但是最终调用方法传递了一个字符串类型的数据,则组件内部难免会出现错误,vue在定义组件的props时,可以通过添加约束的方式来对其类型、默认值、是否选填等配置。

<div class="app">
        <com :count="10"></com>
    </div>
    <script>
        const com = {
            template: `<div>
                <button @click="num++">点击</button>
                <div class='text'>{{total}}</div>
                </div>`,
            props: ['count'],
            data() {
                return {
                    num: 0
                }
            },
            computed: {
                total() {
                    return this.num + this.count
                }
            }
        }

        const app = Vue.createApp({
            components: {
                'com': com
            }
        })
        app.mount('.app')
    </script>

结果:

  

注意,在外部传递数值类型的数据带组件内部时,必需使用v-bind指令的方式进行传递,直接使用html属性设置的方式会将传递的数据作为字符串传递。

<com count="10"></com> 

结果:

我们可以对定义的props进行约束来显示地指定其类型。当将组件的props配置项配置为列表时,其表示当前定义的属性没有任何约束控制,如果将其配置为对象,则可以进行更多约束设置。

<div class="app">
        <com :count="10"></com>
    </div>
    <script>
        const com = {
            template: `<div>
                <button @click="num++">点击</button>
                <div class='text'>{{total}}</div>
                </div>`,
            props: {
                count: {
                    type: Number,
                    required: false,
                    default: 10
                }
            },
            data() {
                return {
                    num: 0
                }
            },
            computed: {
                total() {
                    return this.num + this.count
                }
            }
        }

        const app = Vue.createApp({
            components: {
                'com': com
            }
        })
        app.mount('.app')
    </script>

 如果传入的值类型不正确,控制台会输出警告信息。

<com count="10"></com>

结果:

如果只需要指定属性的类型,而不需要指定更加复杂的性质。

count1: String,

count2: Number,

count3: Boolean,

count4: Array,

count5: Object,

count6: Function,

如果一个属性可能是多种类型

count7: [String, Number, Boolean] 

在对属性的默认值进行配置时,如果默认值的获取方式比较复杂,也可以将其定义为函数,函数执行的结果会被当作当前属性的默认值。

count: {

default: function () {

return 20

}

}

2、props的只读性质

我们不能再组件内部修改props属性的值。

<div class="app">
        <com :count="10"></com>
    </div>
    <script>
        const com = {
            template: `<div>
                <button @click="count++">点击</button>
                <div class='text'>{{count}}</div>
                </div>`,
            props: {
                count: Number
            },
        }

        const app = Vue.createApp({
            components: {
                'com': com
            }
        })
        app.mount('.app')

结果: 

 3、组件数据注入

数据注入是一种便捷的组件间数据传递方式。一般情况下,当父组件需要传递数据到子组件时,我们会使用props,但是当组件的嵌套层级很多,子组件需要使用多层之外的父组件的数据时,就非常麻烦,数据需要一层一层地进行传递。

数据注入,是指父组件可以向其所有子组件提供数据,不论在层级结构上次子组件的层级有多深。实现数据注入需要使用组件的provide和inject两个配置项,父组件设置provide配置项来提供数据,子组件需要设置inject配置项来获取数据。

<div class="app">
        <list></list>
    </div>
    <script>
        const labels = {
            template: `<div class="label" v-for="item in count" style="width: 50px;height: 40px;border: 1px solid #efefef;float: left;margin-left: 10px;margin-top:5px"></div>`,
            inject: ['count']
        }
        const item = {
            props: ['count'],
            template: `<div class="item" v-for="item in count" style="width: 800px;height: 50px;background-color: #ccc;margin: 35px auto;"><labels></labels></div>`,
            components: {
                "labels": labels
            }
        }
        const list = {
            template: `<div class="list" style="width: 1000px;height: 500px;border: 1px solid #ccc;"><item :count=count></item></div>`,
            components: {
                "item": item
            },
            data() {
                return {
                    count: 5
                }
            },
            provide() {
                return {
                    count: this.count
                }
            }
        }
        const app = Vue.createApp({
            components: {
                "list": list
            }
        })
        app.mount('.app')
    </script>

结果:

三、组件Mixin技术

1、使用Mixin来定义组件

当我们开发大型前端项目时,可能会定义非常多的组件,这些组件中可能有部分功能是通用的,对于这部分通用的功能,如果每个组件都编写一遍会非常繁琐,而且不易于维护。

这时,我们可以定义一个混入对象,混入对象中可以包含任意的组件定义选项,当此对象被混入组件时,组件会将混入对象中提供的选项引入当前组件内部。

<div class="app">
        <com1 title="zs"></com1>
        <com1 title="ls"></com1>
        <com1 title="ww"></com1>
    </div>
    <script>
const com1 = {
            props: ['title'],
            template: `<h3>hello {{title}}</h3>`,
        }
        const com2 = {
            props: ['title'],
            template: `<h3>hello {{title}}</h3>`,
        }
        const com3 = {
            props: ['title'],
            template: `<h3>hello {{title}}</h3>`,
        }
        const app = Vue.createApp({
            components: {
                "com1": com1
            }
        })
        app.mount('.app')

 使用Mixin技术简化代码

<div class="app">
        <com1 title="zs"></com1>
        <com1 title="ls"></com1>
        <com1 title="ww"></com1>
    </div>
    <script>
        const myMixin = {
            props: ['title'],
            template: `<h3>hello {{title}}</h3>`,
        }
        const com1 = {
            mixins: [myMixin]
        }
        const com2 = {
            mixins: [myMixin]
        }
        const com3 = {
            mixins: [myMixin]
        }
        const app = Vue.createApp({
            components: {
                "com1": com1,
                "com2": com2,
                "com3": com3,
            }
        })
        app.mount('.app')
    </script>

结果:

2、Mixin选项的合并

当混入对象与组件中定义了相同的选项时,vue可以非常智能地对这些选项进行合并。不冲突的配置将完整合并,冲突的配置会以组件中自己的配置为准。生命周期函数不会进行合并,会先触发Mixin的生命周期函数,再触发组件内部的生命周期函数。

<div class="app">
        <com></com>
    </div>
    <script>
        const myMixin = {
            data() {
                return {
                    a: "a",
                    b: "b",
                    c: "c",
                    d: "d"
                }
            },
            mounted() {
                console.log('Mixin的mounted()');
            },
        }
        const com = {
            mixins: [myMixin],
            data() {
                return {
                    a: "a",
                    b: "B",
                    e: "e"
                }
            },
            mounted() {
                console.log(`a:${this.a}-b:${this.b}-c:${this.c}-d:${this.d}-e:${this.e}`);
                console.log('组件的mounted()');
            },
            template: `<div></div>`
        }
        const app = Vue.createApp({
            components: {
                "com": com
            }
        })
        app.mount('.app')
    </script>

 结果:

3、全局Mixin

app.mixin({})

全局Mixin会为所有注册的组件默认混入,当程序出现问题,这会增加排查问题的难度,但是全局Mixin非常适合开发插件。

四、使用自定义指令

1、认识自定义指令

有时候我们仍需要直接操作DOM元素来实现业务功能,这时候就可以使用自定义指令。

调用实例的directive方法可以注册全局的自定义指令,在使用自定义指令时需要加上v-前缀。

input输入框自定获取焦点:

<div class="app">
        <input type="text" v-get-focus>
    </div>
    <script>
        const app = Vue.createApp({})
        app.directive("getFocus", {
            mounted(element) {
                element.focus()
            },
        })
        app.mount('.app')
    </script>

注册局部的自定义指令

<div class="app">
        <com></com>
    </div>
    <script>
        const com = {
            template: `<h2>局部定义</h2><input type="text" v-get-focus>`,
            directives: {
                getFocus: {
                    mounted(element) {
                        element.focus()
                    },
                }
            }
        }
        const app = Vue.createApp({
            components: {
                "com": com
            }
        })
        app.mount('.app')
    </script>

 结果:

2、自定义指令的参数

自定义指令也可以设置值和参数,这些设置数据会通过一个param对象传递到指令中实现的生命周期方法中。

<div class="app">
        <input type="text" v-get-focus="1">
    </div>
    <script>
        const app = Vue.createApp({})
        app.directive("getFocus", {
            mounted(element, param) {
                element.focus()
                console.log('我是getFocus参数:' + param.value);
            },
        })
        app.mount('.app')
    </script>

 结果:

五、使用组件的Teleport功能开发全局弹窗

Teleport可以将相关行为的逻辑和ui封装到同一个组件中,提高代码的聚合性。

<div class="app">
        <com></com>
    </div>
    <script>
        const com = {
            template: `<button class="pop" @click="show=true">弹出弹窗</button>
        <div v-if="show" class="popupwindow" style="width: 300px;
            height: 200px;
            border: 1px solid #000;
            position: absolute;
            top: 45%;
            left: 35%;">
            <div class="text" style="height: 50px;
            width: 100%;
            text-align: center;">弹窗</div>
            <button @click="show=false" class="hide" style="display: block;
            margin: 0 auto;">隐藏弹窗</button>
        </div>`,
            data() {
                return {
                    show: true
                }
            }
        }
        const app = Vue.createApp({
            components: {
                "com": com
            }
        })
        app.mount('.app')
    </script>

结果:

如果在其他组件内使用的话。

<div class="app">
        <com1></com1>
    </div>
    <script>
        const com = {
            template: `<button class="pop" @click="show=true">弹出弹窗</button>
        <div v-if="show" class="popupwindow" style="width: 300px;
            height: 200px;
            border: 1px solid #000;
            position: absolute;
            top: 45%;
            left: 35%;">
            <div class="text" style="height: 50px;
            width: 100%;
            text-align: center;">弹窗</div>
            <button @click="show=false" class="hide" style="display: block;
            margin: 0 auto;">隐藏弹窗</button>
        </div>`,
            data() {
                return {
                    show: true
                }
            }
        }
        const com1 = {
            template: `<div style="width:200px;height:100px;border:1px solid #efefef;position:absolute"><com></com></div>`,
            components: {
                "com": com,
            }
        }
        const app = Vue.createApp({
            components: {
                "com1": com1
            }
        })
        app.mount('.app')
    </script>

结果:

使用teleport

<div class="app">
        <com1></com1>
    </div>
    <script>
        const com = {
            template: `<button class="pop" @click="show=true">弹出弹窗</button>
            <teleport to="body">
            <div v-if="show" class="popupwindow" style="width: 300px;
            height: 200px;
            border: 1px solid #000;
            position: absolute;
            top: 45%;
            left: 35%;">
            <div class="text" style="height: 50px;
            width: 100%;
            text-align: center;">弹窗</div>
            <button @click="show=false" class="hide" style="display: block;
            margin: 0 auto;">隐藏弹窗</button>
        </div>
        </teleport>`,
            data() {
                return {
                    show: true
                }
            }
        }
        const com1 = {
            template: `<div style="width:200px;height:100px;border:1px solid #efefef;position:absolute"><com></com></div>`,
            components: {
                "com": com,
            }
        }
        const app = Vue.createApp({
            components: {
                "com1": com1
            }
        })
        app.mount('.app')
    </script>

结果:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值