重走vue之vue组件

组件

概述:组件 (Component) 是 Vue.js 最强大的功能之一。组件可以扩展HTML元素,封装可重用的代码 。在较高层面上,组件是自定义元素,Vue的编译器为它添加特殊功能。在有些情况下,组件也可以 表现为用is特性进行了扩展的原生 HTML 元素。组件注册的时候需要为该组件指定各种参数。

组件的定义

因为组件是可复用的 Vue 实例,所以它们与 new Vue 接收相同的选项,例如 data、computed 、watch、methods 以及生命周期钩子等。仅有的例外是像 el 这样根实例特有的选项

组件可复用

可以将组件进行任意次数的复用,因为,每个组件都会各自独立维护它的 data。因为你每用一次组件,就会有一个它的新实例被创建。

data必须是一个函数

一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝:

组件注册

  1. 全局注册

可以使用Vue.component(tagName, options) 注册一个全局组件, 注册之后可以用在任何新创建的 Vue 根实 例的模板中

Vue.component('my-component-name',component)
  1. 局部注册

全局注册往往是不够理想的。比如,如果你使用一个像 webpack 这样的构建系统,全局注册所有的组件意味着即便你已经不再使用一个组件了,它仍然会被包含在你最终的构建结果中。这造成了用户下载的 JavaScript 的 无谓的增加。局部注册的组件只能在当前组件中使用

new Vue({
     el: '#app', 
     components: { 'component-a': ComponentA, 'component-b': ComponentB } })

基础实例:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>组件基础</title>
    <script src="../js/vue.js"></script>
</head>

<body>
    <div id="app">
        {{msg}}
        <br>
        ----组件使用
        <my-com></my-com>
        <my-com></my-com>
        <my-com></my-com>
    </div>
    <script>
        //1.创建组件
        let myCom = {
            data() {
                return {
                    comMsg: '组件数据'
                }
            },
            //模板
            template: `
            <div>
                <span>{{comMsg}}</span>    
                <span>{{comMsg}}</span>  
                <button @click="comMsg='新数据'">更改数据模型中的数据</button>  
            </div>
            `
        };
        //2.组件注册
        //全局注册
        Vue.component('my-com', myCom);
        new Vue({
            el: "#app",
            data: {
                msg: "hello"
            }
        })
    </script>
</body>

</html>

组件交互

组件 A 在它的模板中使用了组件 B。它们之间必然需要相互通信:父组件可能要给子组件下发 数据,子组件则可能要将它内部发生的事情告知父组件。在 Vue 中,父子组件的关系可以总结为 prop 向下传递,事件向上传递。父组件通过 prop 给子组件下发数据,子组件通过事件给父组件发 送消息

props

父组件通过属性绑定的方式将参数传递给子组件,子组件通过props声明期望从父组件那里获取 的参数。camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命 名:

Vue.component('blog-post', { 
    // 在 JavaScript 中是 camelCase 的 
    props: ['postTitle'], 
    template: '<h3>{{ postTitle }}</h3>' })
<!-- 在 HTML 中是 kebab-case 的 --> 
<blog-post post-title="hello!"></blog-post>
props类型

希望每个prop都有指定的值类型。这时,你可以以对象形式列出prop,这些property的名称和 值分别是prop各自的名称和类型

props: {
    title: String, 
    likes: Number, 
    isPublished: Boolean, 
    commentIds: Array, 
    author: Object, 
    callback: Function,
    contactsPromise: Promise
      // or any other constructor
     }
props验证

我们可以为组件的 prop 指定验证要求,例如你知道的这些类型。如果有一个需求没有被满足 ,则 Vue 会在浏览器控制台中警告你。这在开发一个会被别人用到的组件时尤其有帮助。

Vue.component('my-component', { 
    props: {
         // 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证) 
         propA: Number, 
         // 多个可能的类型 
         propB: [String, Number], 
         // 必填的字符串 
         propC: { type: String, required: true }, 
         // 带有默认值的数字
          propD: { type: Number, default: 100 } 
          }
})
动态传参

prop 可以通过v-bind动态赋值。传递什么类型的变量就可以将该类型变量直接进行赋值,也可 以赋常量。如果不使用v-bind就只能传递字符串

  1. 传入一个数字
<blog-post v-bind:likes="42"></blog-post>
 <blog-post v-bind:likes="post.likes"></blog-post>
  1. 传入一个布尔值
<blog-post v-bind:is-published="false"></blog-post>
  1. 传入数组
<blog-post v-bind:comment-ids="[234, 266, 273]"></blog-post>
  1. 传入一个对象
<blog-post v-bind:author="{ name: 'Veronica', company: 'Veridian Dynamics' }" ></blog-post>
单项数据流

父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外变更 父级组件的状态,从而导致你的应用的数据流向难以理解

自定义事件

自定义事件是子组件向父组件传递状态的一种机制。事件名大小写严格区分
使用this.$emit() 参数:自定义事件名称,事件处理程序的实参(发射的数据)可以实现子组件向父组件传值
子组件

this.$emit('myEvent')

父组件

<my-component v-on:myEvent="doSomething"></my-component>

基于以上知识点,下面给出一个组件交互通信的完整例子:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>组件通信-交互</title>
    <script src="../js/vue.js"></script>
</head>

<body>
    <div id="app">
        {{msg}}
        <!-- 父组件给子组件传递数据 -->
        <my-com :title="msg" static-attr="父组件给子组件的静态数据" @my-event="myEventHandle"></my-com>
    </div>
    <script>
        let myCom = {
            //接受父组件传递的数据
            props: ["title", "staticAttr"],
            data() {
                //返回的值都是相互独立的
                return {
                    comMsg: '子组件数据'
                }
            },
            methods: {
                toEmit() {
                    //发射 参数:自定义事件名称,事件处理程序的实参(发射的数据)
                    this.$emit('my-event', this.comMsg, 100)
                }
            },
            //模板
            template: `
            <div>
                <span>子组件内部数据:{{comMsg}}</span> 
                <br>   
                <span>父组件数据{{title}}---{{staticAttr}}</span>  
               
                <button @click="comMsg='新数据'">更改数据模型中的数据</button>  
                <button @click="toEmit">发射数据</button>  

            </div>
            `
        };
        Vue.component('my-com', myCom);
        let vm = new Vue({
            el: "#app",
            data: {
                msg: "hello"
            },
            methods: {
                myEventHandle(a, b) {
                    console.log(a, b + "-----------")
                    this.msg = a;
                }
            }
        })
    </script>
</body>

</html>

插槽

普通插槽

插槽允许我们在调用子组件的时候为子组件传递模板。
子组件

<a v-bind:href="url" class="nav-link" > <slot>default</slot> </a>

父组件

<navigation-link url="/profile"> Your Profile </navigation-link>

当组件渲染的时候,<slot></slot> 将会被替换为“Your Profile”。插槽内可以包含任何模板代码,甚至是其他组件。如果 从函数的角度来理解,slot为形式参数,而navigation-link中的内容就为实参。Slot标签内部的内容为默认值,也就是当调用 navigation-link组件的时候没有设置插槽内容,则组件插槽内容默认为default.

具名插槽

在一个组件中有多个插槽,调用的时候为了给不同的组件传递参数就需要为插槽进行命名。
子组件<base-layout>

<div class="container">
 <header> <slot name="header"></slot> </header> 
 <main> <slot></slot> </main>
<footer> <slot name="footer"></slot> </footer>
 </div>

父组件

<base-layout> 
<template v-slot:header> <h1>Here might be a page title</h1> </template> 
</base-layout>

注意点:传递到插槽中的模板可以封装到template标签中。从函数角度出发,具名插槽可以理解为是传递了多个不同参数

作用域插槽

在一个组件中有多个插槽,调用的时候为了给不同的组件传递参数就需要为插槽进行命名。

子组件 <current-user>

<span> 
    <slot v-bind:user="user"> 
        {{ user.lastName }} 
    </slot> 
    </span>

这里将user这个变量作为slot的一个参数进行绑定,目的是为了让父组件可以访问

父组件

<current-user> 
    <template v-slot:default="slotProps"> 
        {{ slotProps.user.firstName }} 
    </template> 
</current-user>

通过slotProps可以访问到绑定到插槽中的所有属性,除了使用slotProps接受所有的属性外,还可以通过解构形式获取 从函数角度来理解,作用域插槽实际上为回调函数。

缩写

v-bindv-on类似,v-slot也具有缩写形式,即把参数之前的所有内容 (v-slot:) 替换为字符#。例如 v-slot:header 可以被重写为 #header

基于以上知识点,我们可以组件封装一个组件,实例:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>自定义封装组件</title>
    <script src="../js/vue.js"></script>
</head>

<body>
    <div id="app">
        {{msg}}
        <my-table :data="stus">
            <template #header>
                <th>编号</th>
                <th>名称</th>
                <th>年龄</th>
            </template>

            <template #body="{item}">
                <td>{{item.id}}</td>
                <td>{{item.name}}</td>
                <td>{{item.age}}</td>
            </template>
        </my-table>
    </div>
    <script>
        Vue.component('my-table', {
            data() {
                return {}
            },
            props: ['data'],
            template:
                `
            <table>
                <thead>
                    <tr><slot name="header"></slot></tr>    
                </thead>
                <tbody>
                    <tr v-for="(item,index) in data" :key="index">
                        <slot :item="item" name="body"></slot>
                    </tr>    
                </tbody>
             </table>
                `
        })
        new Vue({
            el: "#app",
            data: {
                msg: "hello",
                stus: [
                    {
                        id: 1001,
                        name: "zhangsan",
                        age: 12
                    },
                    {
                        id: 1002,
                        name: "lisi",
                        age: 15
                    },
                    {
                        id: 1003,
                        name: "xiaoming",
                        age: 14
                    }
                ],
                teachers: [
                    {
                        id: 101,
                        name: "terry",
                        salary: 10000,
                        genderL: "male"
                    },
                    {
                        id: 100,
                        name: "terry",
                        salary: 10000,
                        genderL: "male"
                    },
                    {
                        id: 103,
                        name: "terry",
                        salary: 10000,
                        genderL: "male"
                    },
                    {
                        id: 103,
                        name: "terry",
                        salary: 10000,
                        genderL: "male"
                    }

                ],
                courses: [
                    {
                        id: 1,
                        name: 'html',
                        desc: '超文本'
                    },
                    {
                        id: 2,
                        name: 'css',
                        desc: '层叠样式'
                    },
                    {
                        id: 3,
                        name: 'js',
                        desc: '脚本语言'
                    }
                ]
            }
        })
    </script>
</body>

</html>

动态组件

动态渲染一个组件

<component v-bind:is="currentTabComponent"></component>

默认情况下,当组件在切换的时候都会重新创建组件,但是有些时候我们希望组件实例能够被 在它们第一次被创建的时候缓存下来。为了解决这个问题,我们可以用一个 <keep-alive> 元素将其 动态组件包裹起来

<!-- is属性的属性值为注册组件的名字 -->
<keep-alive> 
    <component v-bind:is="currentTabComponent"></component>
 </keep-alive>

完整实例:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>动态组件</title>
    <script src="../js/vue.js"></script>
</head>

<body>
    <div id="app">
        {{msg}}
        <button @click="current='my-a'">A组件</button>
        <button @click="current='my-b'">B组件</button>
        <button @click="current='my-c'">C组件</button>
        <!-- 静态is值为注册的组件的名称 -->
        <!-- <component is="my-a"></component> -->
        <keep-alive>
            <!-- v-bink:attributename="variate"  -->
            <component :is="current" :title="msg"></component>
        </keep-alive>

    </div>
    <script>
        let myA = {
            template: `
                <div>A组件内容</div>
            `,
            created() {
                console.log("A组件创建好了")
            }
        };
        let myB = {
            template: `
                <div>B组件内容</div>
            `,
            created() {
                console.log("B组件创建好了")
            }
        };

        let myC = {
            template: `
                <div>C组件内容</div>
            `,
            created() {
                console.log("C组件创建好了")
            }
        }
        Vue.component('my-a', myA);
        Vue.component('my-b', myB);
        Vue.component('my-c', myC);


        new Vue({
            el: "#app",
            data: {
                // current:myA    可行
                current: "my-a",
                msg: "hello"
            }
        })
    </script>
</body>

</html>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值