Vue.js学习笔记 02、Vue组件篇笔记(下)

举报
长路 发表于 2022/11/28 21:50:00 2022/11/28
【摘要】 文章目录前言一、全局组件子组件应用组件优点:组件的可复用性全局组件缺点描述二、局部组件创建与注册局部组件(认识components属性)局部组件的默认规定(组件名称首字母全都大写)三、父子组件的传值(静态、动态)3.1、静态传值(name="技小胖")3.2、动态传值(v-bind:xx="xx"配合子组件中的props数组接收)动态传递值动态传递函数3.3、三种校验方式①类型校验(props需要

六、父子组件通信(子组件来发出指示让父组件执行指定函数)

目的:通过子组件来调用父组件的函数来改变父组件的data对象中的属性。

6.1、$emit来向外触发父组件函数(无参与有参)

无参向外触发

this.$emit('addOne');:意思是子组件向外发出了一个addOne的信号,外部可使用@add-one="handleAdd"来表示收到信息并执行父组件中的handleAdd()函数!

  • 这里@add-one并不是必须要使用的,xx-xx比较特殊对应的是xxXx,对于普通名称也是可以的。

示例

<body>
    <div id="app"></div>
</body>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/3.0.2/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                count: 0
            }
        },
        methods: {
            //1、父组件定义方法函数来操控data对象中的属性
            handleAdd() {
                this.count++;
            }
        },
        //2、:count="count":父组件向子组件传递属性
        //   @add-one="handleAdd":其中addOne为子组件向外进行触发的动作,handleAdd为父组件中本身的方法
        template: `
            <div>
                <changlu :count="count" @add-one="handleAdd"/>
            </div>
        `
    });

    app.component('changlu', {
        //来接收传递过来的属性
        props: ['count'],
        methods: {
            handleClick() {
                //重要:向外触发父组件的函数(通过传递的方式)
                this.$emit('addOne');
            }
        },
        template: `
            <span @click="handleClick()">{{count}}</span>
        `
    });

    app.mount("#app");
</script>

GIF


有参向外触发

方式:将需要向父组件传递的参数写在第二个参数中即可,如this.$emit(信号名,参数)

image-20210708235637721



6.2、emits属性(方便管理向外触发的函数名称,有警告效果,数组以及对象形式)

目的:配合$emit向外抛出的名称填写,用于方便查看该组件向外触发的函数!

使用方式:直接在组件对象中设置属性emits: ['xxx','xxx']来进行声明表示向外触发的函数!

效果:一旦使用了该emits属性,那么就会对内部向外发出($emit)的函数名称进行校验,若是向外触发的没有在数组中显示就会出现警告信息,依旧执行父组件函数的!

数组形式

image-20210709002221312


对象形式

附加功能:使用对象形式你能够编写对应的一个校验函数,若是返回true表示校验通过不会有警告信息,若是没有通过就会有警告信息依旧向外触发执行!

image-20210709002559506



6.3、v-model配合$emit():实现非调用父组件函数来修改父组件对象属性值

6.3.1、v-model="xxx"默认值接收

方式:原本需要先将指定值进行传递之后配合$emit()向外抛出执行命令来进行执行,使用了v-model直接让父组件的对象某属性与子组件进行绑定连通在子组件中即可对其进行修改只不过需要配合​$emit使用`

示例如下

<body>
    <div id="app"></div>
</body>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/3.0.2/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                count: 0
            }
        },
        //1、直接使用v-model来进行绑定对应属性值
        template: `
            <div>
                <changlu v-model="count"/>
            </div>
        `
    });

    app.component('changlu', {
        //2、使用v-model传递的默认必须使用modelValue来接收
        props: ['modelValue'],
        methods: {
            handleClick() {
                //3、依旧调用$emit()函数来执行,其中第一参数必须为update:modelValue
                //参数一:必须为update:modelValue
                //参数二:对于modelValue的操作,也就是说可以直接对外部属性进行修改
                this.$emit('update:modelValue', this.modelValue + 3);
            }
        },
        template: `
            <span @click="handleClick()">{{modelValue}}</span>
        `
    });

    app.mount("#app");
</script>

GIF



6.3.2、v-model:属性=“xxx”(设置别名接收)

针对于6.3.1进行简单修改即可!

好处:不用再在外部组件专门写一个方法来让内部向外触发调用了以及通过使用v-bind:xxx来进行传参!这里的方式更加的简洁明了推荐使用!!!

image-20210709004038186



七、slot插槽(5个知识点)

①目的。②引用父组件还是子组件值。③插槽默认值。④具名标签(区分传递指定插槽)。⑤作用域插槽(子组件传值到父组件要传递的插槽内容中)。

1、slot插槽目的是为了从父组件中传递指定标签到子组件中来进行使用。

2、父组件中的插槽内容若是使用了{{}}就是引用的父组件中的值,在子组件中使用{{}}就是引用的子组件值。

const app = Vue.createApp({
    data() {
        return {
            content: '我是父组件'
        }
    },
    //将传递的标签内容写在<子组件></子组件>中
    template: `
        <changlu>
            <span>{{content}}</span>
        </changlu>
    `
});
app.component('changlu', {
    //在对应的位置使用<slot></slot>即可插入
    template: `
        <div>
            <slot></slot>
        </div>
    `
});

3、插槽默认值:若是父组件中没有传递指定插槽内容,slot标签就会失效。若是我们想要给插槽中添加一些默认值(可以是标签或值),直接写在slot标签中即可!但要注意一旦父组件传递了插槽内容就不会显示定义在插槽中的默认值了!

//<slot>标签中可以定义默认内容,若是父组件不传递就会使用默认内容!传递就使用传递过来的内容
template: `
    <div>
        <slot>
            <button @click="handleclick()">点我下试试</button>    
        </slot>
    </div>
`

4、若是插槽中有多个标签,此时不同的标签我们想用在不同位置,就可以使用具名标签,也就是使用template标签包裹并使用v-slot:xxx来进行标识写在template中,可简写为#,在子组件中的slot标签里并配置name="xxx" 来进行配对父组件传递过来的

//父组件:将传递的内容模块化<template v-slot:xxx></template>
//v-slot:btn1 => #btn1,可进行简化
template: `
    <changlu>
        <template v-slot:btn1>
            <button >按钮1</button>
        </template>
        <template v-slot:btn2>
            <button >按钮2</button>
        </template>
    </changlu>
`

//子组件:对应slot标签中设置name="xxx"来指定接收
template: `
    <div>
        <slot name="btn1"></slot>
        <div>content</div>
        <slot name="btn2"></slot>
    </div>
`

5、作用域插槽:若是我们想要遍历父组件传递的插槽内容时,如何让插槽内容能够引用子组件里的data对象的值?

在插槽中使用:xx="xx"用来传递到父组件,在父组件使用子组件标签中使用v-slot="obj"来接收,传递来的值以键值对形式保存在obj对象里中,之后即可使用该对象中的属性进行{{}}显示操作了!!!

<body>
    <div id="app"></div>
</body>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/3.0.2/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                content: '我是父组件'
            }
        },
        //v-slot="{item}"来接收子组件传递过来的值,这里使用到了对象解构
        template: `
            <changlu v-slot="{item,dataArr}">
                <span>{{item}}</span>
            </changlu>
        `
    });

    app.component('changlu', {
        data() {
            return {
                dataArr: [1, 2, 3]
            }
        },
        //使用:item="item"来进行传值绑定
        //可以传多个值都是可以的,因为是通过对象的形式来接收的
        template: `
            <div>
                <slot v-for="item in dataArr" :item="item" :dataArr="dataArr"></slot>
            </div>
        `
    });

    app.mount("#app");
</script>


八、动态组件(切换组件)

8.1、原生切换组件方式(借助v-if、v-show,keep-alive标签保存input状态)

原生切换自定义组件

v-if来进行切换时input标签中的内容会清空,不会保存状态。若是想要保存状态使用<keep-alive>标签包裹。

v-show会保存状态。(原理是display:none,仅仅只是隐藏组件)

//父组件的template
data() {
    return {
        isSpan: true
    }
},
methods: {
    //改变isSpan的布尔值达到切换效果
    handleclick() {
        this.isSpan = this.isSpan ? false : true;
    }
},
template: `
    <m-span v-if="isSpan"></m-span>
    <keep-alive>
        <m-input v-if="!isSpan" ></m-input>
    </keep-alive>
    <button @click="handleclick">点我切换</button>
`

//两个子组件
app.component('m-span', {
    template: `
        <span>span标签显示</span>
    `
});
app.component('m-input', {
    template: `
        <input type="text"/>
    `
});


8.2、动态组件(component标签以及:is)

好处:解放了之前切换组件时要将多个组件都写在template模板里,切换几个就需要写几个之后通过控制data对象里的布尔属性来进行静态显示!

方式<component :is="自定义标签名"></component>,就是这么简单,我们只需要在data对象中定义对应的自定义标签名就能够实现动态切换不同的组件,牛逼!

  • 特殊:对于input标签,我们要想保存切换前的状态就需要在外面包裹<keep-alive>标签。

示例

<body>
    <div id="app"></div>
</body>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/3.0.2/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                //目标要显示的自定义组件名
                showlabel: 'm-span'
            }
        },
        methods: {
            handleclick() {
                //更改标签名
                this.showlabel = this.showlabel == 'm-span' ? 'm-input' : 'm-span';
            }
        },
        //动态组件实现:不需要通过v-if、v-show来进行判断显示,而是直接通过:is="组件名"的显示动态显示组件
        //<component :is="showlabel"></component>  => 动态组件
        //<keep-alive> => 保存input输入框状态
        template: `
            <keep-alive>
                <component :is="showlabel"></component>
            </keep-alive>
            <button @click="handleclick">点我切换</button>
        `
    });

    //自定义的两个组件
    app.component('m-span', {
        template: `
            <span>span标签显示</span>
        `
    });

    app.component('m-input', {
        template: `
            <input type="text"/>
        `
    });

    const vm = app.mount("#app");
</script>


九、异步组件(实现异步加载组件)

原本之前我们直接定义的{},或者app.component('组件名',{})定义的组件都是同步组件。

异步组件目的:通过使用异步组件来动态的加载一些组件,可以将一些大型的项目拆分为小的js文件,在需要这些组件的时候就可以进行引入并进行使用组件

  • 在父标签中若是使用了异步组件就会进行异步加载组件,不是同步加载了!

核心

1、Vue.defineAsyncComponent(函数),需要传入一个函数。

2、函数要返回一个Promise,也就是异步,最终通过resolve({})reject({})将对应的组件对象传递出去就称为异步组件!

//自定义异步组件
app.component('asynCom', Vue.defineAsyncComponent(() => {
    //返回一个Promise对象
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve({
                template: `
                    <span>异步显示的span</span>
                `
            });//将对应的组件进行传递!!!
        }, 3000);
    })
}));

示例:令组件延时3秒出现,可以看出异步的效果

<body>
    <div id="app"></div>
</body>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/3.0.2/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                item: 'synCom'
            }
        },

        template: `
            <asynCom/>
            <br/>
            <synCom/>
        `
    });

    //自定义的两个组件
    app.component('synCom', {
        template: `
            <span>span标签显示</span>
        `
    });

    //自定义异步组件
    app.component('asynCom', Vue.defineAsyncComponent(() => {
        //返回一个Promise对象
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve({
                    template: `
                        <span>异步显示的span</span>
                    `
                });//将对应的组件进行传递!!!
            }, 3000);
        })
    }));

    const vm = app.mount("#app");
</script>


十、补充知识点

10.1、v-once(让某个元素标签只渲染一次)

v-once:只显示一次值在页面上,之后若是动态修改值也不会进行渲染。(实际值进行了修改)

示例:

<body>
    <div id="app"></div>
</body>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/3.0.2/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                count: 1
            }
        },
        methods: {
            handleclick() {
                this.count += 1;
            }
        },
        //v-once="xxx"进行绑定
        template: `
            <span v-once="count">{{count}}</span>
            <button @click="handleclick">点我切换</button>
        `
    });
    const vm = app.mount("#app");
</script>


10.2、$refs(定位获取到指定设置ref的dom元素)

方式:在某个标签中添加ref="xxx",之后可通过js中使用$refs.xxx来获取到指定标签!

应用:获取到某个结点之后可以对其中内容来进行一些操作,最好还是操作dom元素!

示例:点击按钮之后来更改按钮中的值(其实完全可以进行双向绑定修改值的)

<script src="https://cdn.bootcdn.net/ajax/libs/vue/3.0.2/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                count: 1
            }
        },
        methods: {
            handleclick() {
                //2、获取到指定设置ref的dom元素
                console.log(this.$refs.mspan);
                //应用:插入新的内容
                this.$refs.mspan.innerHTML = "changlu";
            }
        },
        //1、在指定标签上定义ref="xxx"
        template: `
            <button ref="mspan" @click="handleclick">{{count}}</button>
        `
    });
    const vm = app.mount("#app");
</script>

GIF



10.3、provide属性以及inject属性(嵌套组件中传递值)

10.3.1、引出provide以及使用

引出provide以及inject属性

若是我们定义了两个子组件,在父组件中使用了第一个子组件,接着在第一个子组件中复用了第二个子组件,此时第二个子组件想要使用父组件中的值就需要一层一层借助通:xx="xx"+props属性来进行一层层往下传递。

弊端:若是有大量嵌套组件,需要重复进行v:bind以及props设置传参!


使用provide以及inject属性

方式:父组件定义provide: {}属性,孙子组件或其他子组件使用inject: ['xx']即可拿到!

注意provide对象想要动态使用this.属性拿到父组件data对象里的值,需要写成如provide(){ return {xxx=this.xxx}}

//想要拿到data对象里的值,需要编写provide函数接着子啊返回对象里进行获取!
provide() {
    return {
        count: this.count
    }
},

示例

<body>
    <div id="app"></div>
</body>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/3.0.2/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                count: 1
            }
        },
        //1、定义可供其他组件使用的provide属性
        provide: {
            count: 1
        },
        template: `
            <firstspan></firstspan>
        `
    });

    //子组件:嵌套了子组件
    app.component('firstspan', {
        template: `
            <secspan/>
        `
    });

    //子组件
    //2、想要获取到provide对象中的属性,需要使用inject数组设置对应属性名来进行接收
    app.component('secspan', {
        inject: ['count'],
        template: `
            <span>{{count}}</span>
        `
    })
    const vm = app.mount("#app");
</script>

image-20210711110909684



10.3.2、注意点provide属性绑定inject属性并不是双向绑定!!!

使用了provide属性可以方便父组件与嵌套组件方便进行传值,但是需要注意的是provide属性绑定inject属性并不是双向绑定,父组件中的值发生改变并不会重新传值过去来让子组件进行修改。

解决方案:之后会使用高级的语法来进行解决!(笨方法就是:xxx绑定以及使用props进行接收)

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。