组件初始化、父子组件传值、插槽

本文介绍了Vue中的组件初始化、父子组件间的通信以及插槽的使用。组件化允许我们将页面拆分为独立的功能块,便于管理和扩展。文章详细讲解了组件的创建、注册和使用,包括全局和局部组件、父组件和子组件的概念。接着讨论了通过props和自定义事件实现父子组件通信的方法。最后,阐述了插槽的用途,包括默认插槽、具名插槽和作用域插槽,展示了如何提高组件的可扩展性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Vue学习二——组件初始化、父子组件传值、插槽

一、组件化初始化
1.什么是组件以及组件化?
如果我们将一个页面中所有的处理逻辑全部放到一起。处理起来就会变得非常复杂,而且不利于后续的管理以及扩展。为此,引出组件化的思想:

  • 将一个完整的页面分成很多组件
  • 每一个组件都用于实现页面的一个功能块
  • 而每一个组件又可以进行细分
    页面组件化
    组件化提供了一种抽象,让我们可以开发一个个独立可复用的小组件来构造我们的应用。任何的应用都会被抽象成一棵组件树
    组件树
    2.注册组件的基本步骤

1.创建组件构造器
2.注册组件
3.使用组件

注册组件的步骤视图:
注册组件的步骤图

<div id="app">
        <!-- 使用组件 -->
        <my-cpn></my-cpn>
        <my-cpn></my-cpn>
    </div>

    <script src="../JS/vue.js"></script>
    <script>
        // 1.创建组件构造器对象
        const cpnC = Vue.extend({
            template : `
            <div>
                <h2>我是标题</h2>
                <p>hahaha</p>
            </div>`
        })
        // 2.注册组件
        Vue.component('my-cpn',cpnC)

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

页面显示结果:

我是标题
hahaha
我是标题
hahaha

注册组件步骤解析:

  1. Vue.extend():

     调用Vue.extend()创建的是一个组件构造器
     通常在创建组件构造器时,传入template代表我们自定义组件的模板
     该模板就是在使用到组件的地方,要显示的HTML代码
     事实上,这种写法在Vue2.x的文档中几乎以及看不到了,它会直接使用语法糖,是基础
    
  2. Vue.component():

    调用Vue.component()是将刚才的组件构造器注册成一个组件,并且给他起一个组件的标签名称
    所以需要传递两个参数:
    	1.注册组件的标签名;
    	2.组件构造器
    
  3. 组件必须挂载在某一个Vue实例下,否则它不会生效

组件分类

  1. 全局组件和局部组件

    全局注册组件的写法:
    		Vue.component('cpn',cpnC)
    		//这种写法在任何一个vue实例中均可以使用
    局部注册组件的写法:
    		//只可以在所挂载的某个实例中使用
    
let app = new Vue({
            el : '#app',
            data : {
                message : 'hello'
            },
            components : {
                // cpn就是使用组件时的标签名
                // 局部注册组件,仅在app中可以使用
                cpn : cpnC
            }
        })
  1. 父组件和子组件
    在创建某一个组件中注册了另一个组件,其中创建组件的称为“父组件”,注册的组件称为“子组件”。
 <div id="app">
    <cpn2></cpn2>
   </div>
   <script src="../JS/vue.js"></script>
   <script>
    // 1.创建第一个组件构造器对象(子组件)
    const cpnC1 = Vue.extend({
        template : `
        <div>
            <h2>我是标题</h2>
            <p>hahaha</p>
        </div>`
    })
// 创建第二个组件构造器(父组件)
    const cpnC2 = Vue.extend({
        template : `
        <div>
            <h2>我是标题</h2>
            <p>heiheiheihei</p>
            <cpn1></cpn1>
        </div>
        `,
        //在组件二中注册组件一
        components : {
            cpn1 : cpnC1
        }
    })
    // 2.注册组件
    // Vue.component('my-cpn',cpnC1)
    // root组件
    let app = new Vue({
        el : '#app',
        data : {
            message : 'hello'
        },
        components : {
            cpn2 : cpnC2
        }
    })
</script>

在父组件中注册的子组件其作用域就是父组件,要是想要在全局作用还需要在vue实例中注册。

注册组件的语法糖:

  1. 全局组件语法糖
<script>
Vue.component('cpn1',{
            template : `
            <div>
                <h2>我是全局组件语法糖</h2>
                <p>hahaha</p>
            </div>`
        })
</script>
  1. 局部组件语法糖
<script>
let app = new Vue({
            el : '#app',
            data : {
                message : 'hello'
            },
            components : {
                'cpn2' : {
                    template : `
                         <div>
                             <h2>我是局部组件语法糖</h2>
                             <p>hahaha</p>
                         </div>`
                }
            }
        })
</script>

组件模板的分离写法:

	使用<script>标签:<script type="text/x-template" id="cpn">
<div id="app">
        <!-- 使用组件 -->
        <cpn></cpn>
    </div>
    <script type="text/x-template" id="cpn">
        <div>
            <h2>我是标题</h2>
            <p>hahaha</p>
        </div>
    </script>
    <script src="../JS/vue.js"></script>
    <script>
        Vue.component('cpn',{
            template : '#cpn'
        })

        let app = new Vue({
            el : '#app',
        })
    </script>
使用<template>标签
<div id="app">
        <!-- 使用组件 -->
        <cpn></cpn>
    </div>
    <template id="cpn">
        <div>
            <h2>我是标题</h2>
            <p>hahaha</p>
        </div>
    </template>
    <script src="../JS/vue.js"></script>
    <script>
        Vue.component('cpn',{
            template : '#cpn'
        })

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

注意:

	组件可以访问Vue实例数据吗?
		1.组件是一个单独功能模块的封装:
				这个模块有属于自己的HTML模板,也应该有属于自己的数据data
		2.组件不能直接访问Vue实例中的数据
			Vue组件应该有自己保存数据的地方

组件数据的存放

1.组件对象也有一个data属性(也可以有methods等属性)
2.这个data属性必须是一个函数
3.而且这个函数返回一个对象,对象内部保存着数据
 <div id="app">
        <cpn></cpn>
    </div>
    <template id="cpn">
        <div>
            <h2>{{title}}</h2>
            <p>hahaha</p>
            <button @click="btnClick">点击一下</button>
        </div>
    </template>
    <script src="../JS/vue.js"></script>
    <script>
        Vue.component('cpn',{
            template : '#cpn',
            data() {
                return {
                    title : 'hi'
                }
            },
            methods : {
                btnClick() {
                    console.log("click btn");
                }
            }
        })
        let app = new Vue({
            el : '#app',
        })
    </script>

二、父子组件的通信——传值

1.缘由

  • 子组件不能引用父组件或者Vue实例的数据
  • 在开发中一些数据需要从上层传递到下层:
    比如从服务器请求到很多数据,其中一部分数据需要由下面的子组件进行展示,这时并不会让子组件再次发送一个网络请求,而是直接让父组件将数据传递给子组件。
    • 父子组件间的通信方法

1.通过props向子组件传递数据;
2.通过事件向父组件发送信息

父子组件的通信

2.过程

(1) props基本用法——父传子

	 方式一:字符串数组,数组中的字符串就是传递时的名称
	 方式二:对象,对象可以设置传递时的类型,也可以设置默认值等
<div id="app">
        <!-- 使用v-bind绑定属性 -->
        <cpn :cmovies="movies" :cmessage="message"></cpn>
    </div>
    <template id="cpn">
        <div>
            <p>{{cmovies}}</p>
            <ol>
                <li v-for="item in cmovies">{{item}}</li>
            </ol>
            <h2>{{cmessage}}</h2>
        </div>
    </template>
    <script src="../JS/vue.js"></script>
    <script>
        const cpn = {
            template : '#cpn',
            props : ['cmovies','cmessage'], //采用数组,cmovies,cmessage是一个变量
            data() {
                return {}
            },
            methods : {}
        }
        const app = new Vue({
            el : '#app',
            data : {
                message : 'hello',
                movies : ['海的女儿','蜡笔小新','海绵宝宝']
            },
            components : {
                cpn
            }
        })
    </script>

补充:props数据验证

	当需要对props进行类型等验证时,就需要对象写法了。
	支持以下类型:
			-String  - Number  - Boolean  - Array 
			- Date  - Function - Symbol   - Object
<script>
        const cpn = {
            template : '#cpn',
            props : {
                //1.类型限制
                cmovies : Array,
                cmessage : String,
                // 2.提供一些默认值,type为类型,default为默认值
                ctitle : {
                    type : String,
                    default : 'Title',
                    required : true //必须使用v-bind绑定出现的属性
                },
                // 类型是对象或者数组时,默认值必须是一个函数
                cmovies : {
                    type : Array,
                    default() {
                        return ['猫和老鼠']
                    }
                }
            },
            data() {
                return {}
            },
            methods : {}
        }
    </script>

Prop 的大小写 (camelCase vs kebab-case)
HTML 中的 attribute 名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。这意味着当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名

(2)自定义事件——子传父

自定义事件的流程:
		1.在子组件中,通过$emit()来触发事件
		2.在父组件中,通过v-on来监听事件
<body>
    <!-- 父组件模板-->
    <div id="app">
       <cpn @item-click="cpnclick"></cpn>
    </div>
    <!-- 子组件模板 -->
    <template id="cpn">
        <div>
            <button v-for="item in list" @click="handleClick(item)">{{item.name}}</button>
        </div>
    </template>
    <script src="../JS/vue.js"></script>
    <script>
        // 子组件
        const cpn = {
            template : '#cpn',
            data() {
                return {
                    list : [
                        {id : 'aaa', name : '热门推荐'},
                        {id : 'bbb', name : '手机数码'},
                        {id : 'ccc', name : '家用电器'},
                        {id : 'ddd', name : '电脑办公'}
                    ]
                }
            },
            methods : {
                handleClick(item) {
                    // console.log(item.id);
                    // 发射事件:自定义事件
                    this.$emit('item-click',item)
                }
            }
        }
        let app = new Vue({
            el : '#app',
            data : {
                message : 'hello'
            },
            components :{
                cpn
             },
             methods : {
                 cpnclick(item) {
                     console.log("click cpn");
                     console.log(item.name);
                 }
             }
        })
    </script>
</body>

(3)父子组件的访问

1.父组件访问子组件:使用$children或者 $refs
2.子组件访问父组件:使用 $parent (访问根组件使用 $root)

三、插槽——slot

  1. 组件的插槽

组件的插槽的目的:为了让我们封装的组件更加具有扩展性
让使用者可以决定组件内部的一些内容到底展示什么

	例子:移动网站中的导航栏
			几乎每一个页面都有导航栏,所以导航栏必然需要封装成一个插件,比如nav-bar组件。
			一旦有了组件,就可以在多个页面中复用了。
  1. 如何封装这类组件?

抽取共性,保留不同
将共性抽到组件中,将不同的暴露为插槽,一旦预留了插槽,就可以让使用者根据自己的需求决定插槽中插入什么内容。

  1. 插槽的基本使用

    (1)在子组件中写上<slot></slot>
    (2)插槽内部可以有默认值,例子:<slot><button>按钮</button></slot>
    (3)如果有多个值同时放入到组件进行替换时,可以将其一起作为替换元素

该插槽插入什么内容取决于父组件如何使用
<slot>中的内容表示,如果没有在该组件中插入任何其他内容,就默认显示该内容

<div id="app">
        <cpn></cpn>
        <cpn><h4>hi</h4></cpn>
        <cpn>
        	<p>我自己独有的p标签</p>
        	<h2>是我呀</h2>
        </cpn>
        <cpn></cpn>
        <cpn></cpn>
    </div>

    <template id="cpn">
        <div>
            <h2>我是组件</h2>
            <p>哈哈哈</p>
            <!-- 插槽就是预留一些空间,
            具体使用的时候再根据实际写入 -->
            <slot><button>按钮</button></slot>
            <!-- <slot></slot> -->
        </div>
    </template>
  1. 具名插槽

     给插槽一个name属性,选择需要更改的插槽中的内容,采用<xxx slot="name">
    
<div id="app">
        <p>替换没有给名字的slot</p>
        <cpn><span>标题</span></cpn>
        <p>替换指定名字的slot</p>
        <cpn><span slot="center">标题</span></cpn>
    </div>
    <template id="cpn">
        <div>
            <!-- 当给slot添加上名字之后父组件中的内容
            不会被显示,没有给予单一名字的slot标签中的内容会被覆盖 -->
            <slot name="left"><span>左边</span></slot>
            <slot name="center"><span>中间</span></slot>
            <slot name="right"><span>右边</span></slot>
            <br>
            <slot><span>hahahha</span></slot>
        </div>
    </template>
  1. 编译作用域

父组件模板的所有东西都会在父级作用域内编译;
子组件模板的所有东西都会在子级作用域内编译

例子:

div id="app">
         <!-- 此时v-show的作用域就是这个实例,isShow=true -->
        <cpn v-show="isShow"></cpn>
    </div>

     <template id="cpn">
         <div>
             <h2>我是子组件</h2>
             <p>我是内容</p>
             <!-- 此时v-show的作用域是这个组件,
            v-show=false,这个button不显示 -->
             <button v-show="isShow"></button>
         </div>
     </template>
<script>
         const app = new Vue({
            el : '#app',
            data : {
                 message : 'hi',
                 isShow : true
            },
            methods : {

            },
            components : {
                cpn : {
                    template : '#cpn',
                    data() {
                        return {
                            isShow : false
                        }
                    },
                }
            }
         })
     </script>
  1. 作用域插槽

父组件替换插槽的标签,但是内容由子组件来提供

在多个页面展示一组数据,但是展示效果不一样时,就需要父组件告诉我们如何展示,并且使用slot作用域插槽。

<div id="app">
        <cpn></cpn>
        <cpn>
            <!-- 目的获取子组件中的planguage -->
            <!-- 拿到slot对象:slot-scope="slot" -->
            <div slot-scope="slot">
                <span v-for="item in slot.data">{{item}} - </span>
                <br>
                <span>{{slot.data.join(' _ ')}}</span>
            </div>
        </cpn>
    </div>

    <template id="cpn">
        <div>
            <!-- 动态绑定属性,将pLanguages的数据传给data -->
            <slot :data="pLanguages">
                <ul>
                    <li v-for="item in pLanguages">{{item}}</li>
                </ul>
            </slot>
        </div>
    </template>

完成这一部分的学习,Vue不断更新,本博客一些内容可能有些滞后,博主正在努力学习中!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值