Vue从零开始之Vue组件

本文详细介绍了Vue组件的基础,包括组件模板抽离、data和方法的定义、父子组件通信的props和自定义事件、以及插槽的使用。强调了组件的独立性和可复用性,还探讨了组件间的直接访问方式,如$children、$refs、$parent和$root。此外,还讨论了props的大小写问题以及如何在数据双向绑定中实现子传父。

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

Vue 组件

组件基础

组件化就是将一个页面拆成一个个可复用功能块(组件),方便扩展和复用.

注意:在实际开发中,我们并不会用以下方式开发组件,而是采用 vue-cli 创建 .vue 模板文件的方式开发,以下方法只是为了让大家理解什么是组件。

组件使用的三个步骤:

  • 创建组件构造器: 调用Vue.extend()方法,Vue2之后可以将extend中的对象在注册组件时传入
  • 注册组件
    • 使用 Vue.component() 方法注册全局组件,所有Vue实例都可使用
    • 在Vue实例中使用components注册局部组件,只有注册的Vue实例才能使用,注册的Vue实例的父实例不能使用该局部组件
  • 使用组件
    • 注意局部组件的作用域
    • 子组件中不能直接访问父组件的数据

组件是可复用的 Vue 实例,一般称new出来的Vue实例为根组件.

  • 组件名定义时如果使用的是驼峰命名法,在使用名字时要改为短横线分隔命名法
  • 每个组件必须只有一个根元素
<div id="vue">
    <ul>
        <!--若不想从vue的data数据中读值,可以直接使用aaa="xxx"向组件的aaa属性赋值-->
        <!-- =号左边的 aaa 为 props 定义的属性名,右边的为v-for遍历出的item -->
        <my-component-li v-for="item in items" v-bind:aaa="item"></my-component-li>
    </ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script type="text/javascript">
    const cpnc1 = Vue.extend({
        template: `
            <h1>啊啊啊啊</h1>
        `
    })
    // 组件中注册组件
    const cpnc2 = Vue.extend({
        template: `
            <h1>哈哈哈哈哈</h1>
            <cpn1></cpn1>
        `,
        //组件构造器中注册组件,
        components:{
           cpn1:  cpnc1
        }
    })
    //注册组件 cpn1为组件名
    Vue.component('cpn1',cpnc1)
    
    
    // 使用语法糖注册组件,{}就是传入extend的对象
    Vue.component('my-component-li', {
        //使用 props 属性向子组件传递数据,通过v-bind绑定属性
        props: ['aaa'],
        //组件的模板
        template: '<li>Hello {{aaa}}</li>'
    });

    var vm = new Vue({
        el: '#vue',
        data: {
            items: ["张三", "李四", "王五"]
        }
    });
    const app= new Vue({
        el: '#app',
        //注册局部组件
        components: {
            // cpn为使用组件的标签名,cpnc为组件构造器
            cpn: cpnc2
            // 使用语法糖可以省略 extend
            cpu2: {
                    template: `
                        <h1>哈哈哈哈哈</h1>
                        <cpn1></cpn1>
                    `,
                    components:{
                       cpn1:  cpnc1
                    }
                }
        }
    })
</script>

组件模板抽离的写法

  • script标签
//type必须为 text/x-template
<script type="text/x-template" id="cpn">
    <li>Hello</li>
</script>

<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script type="text/javascript">
    Vue.component('myli', {
        template: '#cpn'
    });
</script>
  • template标签
<template id="cpn">
    <li>Hello</li>
</template>

<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script type="text/javascript">
    Vue.component('myli', {
        template: '#cpn'
    });
</script>

组件中定义data/方法

  • 组件中的data只能使用函数定义,不能直接使用对象定义
  • 由于data定义为了函数,所以同一个组件创建的多个标签之间的data数据互不干扰
    • 不同标签的data都是通过函数在堆中重新创建的
    • 若在Vue实例外定义变量,data函数中直接返回则不同标签间共享该变量
<script type="text/javascript">
    Vue.component('myli', {
        template: '<h1>{{title}}</h1>',
        data(){
            return {
                title: 'abc'
            }
        },
        //组件中定义方法与Vue实例中一样
        methods: {
            
        }
    });
    
    
    // 强制组件创建的不同标签间data数据共享
    const obj={
        count: 0
    }
    Vue.component('myliii', {
        template: '<h1>{{count}}</h1>',
        data(){
            return obj
        }
    });
</script>

父子组件通信传值

父传子:props属性

  • 子组件中定义props的属性
  • 在组件模板中绑定属性
  • 在标签中对props定义的属性进行传值

避免在子组件中直接改变props属性的值,而是在子组件中使用data或计算属性接收props属性的值后再进行双向绑定或修改

<div id="vue">
    <ul>
        <!--若不想从vue的data数据中读值,可以直接使用aaa="xxx"向组件的aaa属性赋值-->
        <!-- =号左边的 aaa 为 props 定义的属性名,右边的为v-for遍历出的item -->
        <my-component-li v-for="item in items" v-bind:aaa="item"></my-component-li>
    </ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script type="text/javascript">
    Vue.component('my-component-li', {
        //props可使用数组或对象
        
        //使用数组只能指定变量名
        //props: ['aaa'],
        
        //使用对象时可指定类型,默认值,是否必传
            //类型是对象或数组时,默认值必须是函数
            // 还可验证自定义类型
        props:{
            // 只指定类型
            aaa: String,
            // 指定类型和默认值,必传值
            bbb: {
                type: String,
                default: 'aaaaaa',
                required: true
            }
            cage: Number,
            cpp: {
                type: Array,
                default(){
                    return []
                }
            },    
            propE: {
              type: Object,
              // 对象或数组默认值必须从一个工厂函数获取
              default: function () {
                return { message: 'hello' }
              }
            },
            // 自定义验证函数
            propF: {
              validator: function (value) {
                // 这个值必须匹配下列字符串中的一个
                return ['success', 'warning', 'danger'].indexOf(value) !== -1
              }
            }
        }
        template: '<li>Hello {{aaa}}</li>',
        data(){
            return {
                //将 props中的aaa赋值给data
                daaa: this.aaa
            }
        }
    });
    var vm = new Vue({
        el: '#vue',
        data: {
            items: ["张三", "李四", "王五"]
        }
    });
</script>
props大小写问题

HTML 中的 attribute 名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。所以使用驼峰命名法的 props 名需要使用其等价的短横线分隔命名

Vue.component('blog-post', {
  // 在 JavaScript 中是 驼峰 的
  props: ['postTitle'],
  template: '<h3>{{ postTitle }}</h3>'
})

<!-- 在 HTML 中是 短横线分隔 的 -->
<blog-post post-title="hello!"></blog-post>

子传父: 自定义事件

  • 子组件模板中监听某个事件,发生时调用子组件中的方法
  • 子组件方法中通过this.$emit('自定义事件名', 参数)设置自定义事件
  • 父组件模板监听 自定义事件,发生时调用父组件中的方法
  • 父组件方法中可操作父组件的数据,最终达到子组件向父组件传值的过程
<div id="vue">
    <!-- 监听子组件的自定义事件,发生后调用指定的方法 -->
    <!-- 浏览器事件不指定实参会传入event对象,自定义事件不指定实参会传入this.$emit设置的参数 -->
    <myarr @itemclick="cpnclick"></myarr>
</div>

<template id="cpn">
    <div>
        <button v-for="item in items" @click="btnclick(item)">
            {{item}}
        </button>
    </div>
</template>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script type="text/javascript">
    Vue.component('myarr', {
        template: "#cpn",
        data(){
            return{
               items: ["张三", "李四", "王五"]
            }
        },
        methods: {
            btnclick(item){
                // 子组件获取到点击的按钮
                console.log(item);
                // 子组件向父组件发送指定事件
                this.$emit('itemclick', item)
            }
        }
    });
    var vm = new Vue({
        el: '#vue',
        methods: {
            cpnclick(item): {
                // 父组件监听到事件
                console.log('cpnclick',item);
            }
        }
    });
</script>
数据双向绑定时 子传父
  • v-model拆成v-bind:valueinput事件,在不影响数据双向绑定的前提下,在input事件调用的方法中添加自定义事件完成子组件向父组件传值.
    注意:可能产生类型错误,在发生错误的地方自己转换即可.
  • 使用v-model+watch实现,v-model完成数据的双向绑定,watch监听数据发生改变时执行自定义事件.
props:{
    name: ''
}
data(){
    num: 0
}
// watch 监听props/data中数据的改变
    // watch的属性名必须和props/data中的相同
    // watch可得到新旧值,也可值得到新值
watch:{
    name(newValue,oldValue){
        
    },
    num(newValue){
        
    }
}

父子组件访问

有时候我们需要父子组件之间可以直接访问,而不是仅仅传值通信

父访问子 c h i l d r e n 和 children和 childrenrefs

  • $children: 获取全部的子组件
  • $refs: 获取指定的子组件(常用)
this.$children //返回子组件的数组
this.$children[0].方法名/data属性名 // 执行/获取第一个子组件的方法/属性值


<mycpn ref="aaa"></mycpn>
this.$refs.aaa //获取指定的子组件
this.$refs.aaa.name //获取aaa子组件的name属性值

子访问父 p a r e n t 和 parent和 parentroot

  • $parent: 获取父组件
  • $root: 获取根组件即new出来的Vue实例
this.$parent //获取父组件
this.$root //获取根组件

Vue 插槽

slot与slot-scope

在 Vue.js 中我们使用 元素作为承载分发内容的出口,作者称其为 插槽,可以应用在组合组件的场景中,相当于占位符,提高组件的扩展性.

  • 单插槽
<!-- 
1.插槽可以设置默认值
2.使用时如果有多个值,同时放入到组件进行替换时,一起作为替换元素
-->
Vue.component('chacao', {
    template: '<div>
                   <slot></slot>
                   <slot><h1>默认值</h1></slot>
               </div>'
});


<div id="vue">
    <chacao></chacao>
    <chacao><h1>传入的值<h1></chacao>
    <chacao>
        <h1>传入的值1<h1>
        <h1>传入的值2<h1>
        <h1>传入的值3<h1>
    </chacao> 
</div>
  • 多插槽,指定name,指定具名插槽替换

slot的name属性值 和 替换元素的slot属性值相等时,替换指定插槽

Vue.component('chacao', {
    template: '<div>
                   <slot name="left"><h1>左<h1></slot>
                   <slot name="right"><h1>右<h1></slot>
               </div>'
});


<div id="vue">
    <chacao>
        <h1 slot="left">指定插槽替换<h1>
    </chacao>
</div>

插槽的作用域:

定义一个名为 todo 的待办事项组件, 该组件中放置了两个插槽,分别为 todo-title 和 todo-items

Vue.component('todo', {
    template: '<div>
                    <slot name="todo-title"></slot>
                    <ul>
                    <slot name="todo-items"></slot>
                    </ul>
               </div>'
});

定义一个名为 todo-title 的待办标题组件

Vue.component('todo-title', {
    props: ['title'],
    template: '<div>{{title}}</div>'
});

定义一个名为 todo-items 的待办内容组件

Vue.component('todo-items', {
    props: ['item', 'index'],
    template: '<li>{{index + 1}}. {{item}}</li>'
});

初始化

<div id="vue">
    <todo>
        <todo-title slot="todo-title" title="今日动漫推荐"></todo-title>
        <todo-items slot="todo-items" v-for="(item, index) in todoItems" v-bind:item="item" v-bind:index="index" :key="index"></todo-items>
    </todo>
</div>

var vm = new Vue({
    el: '#vue',
    data: {
        todoItems: ['《刀剑神域3》', '《关于我转生成为史莱姆这件事》', '《实力至上主义教室》']
    }
});

父模板中的变量会使用父组件的属性,上面的div属于根组件的模板,会从根组件寻找变量而不是从子组件中寻找

slot-scope:作用域插槽
:让替换插槽的内容能够访问子组件中的数据

常用于父组件对子组件数据渲染方式不满意的情况下,获取到子组件的数据自行展示.

<div id="vue">
    <todo>
        <!--
            1.最好在template上使用slot-scope,不然有的版本可能不支持
            2.slot-scope声明一个变量,接收插槽中使用的变量
        -->
        <template slot-scope="slot">
            {{slot.data}}
        </template>
    </todo>
</div>


Vue.component('todo', {
    template: '<div>
                    <slot :data="todoItems"></slot>
               </div>',
    data(){
        return{
            todoItems: ['《刀剑神域3》', '《关于我转生成为史莱姆这件事》', '《实力至上主义教室》']
        }
    }
});

var vm = new Vue({
    el: '#vue'
});
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值