vue系列教程-09vue组件

本内容为系列内容,全部内容请看我的vue教程分类

什么是组件

还记得我们前面讲的spa单页面吗,那么这个只有一个单页面文件是如何实现切换显示和不同路径展示不同内容的呢?

这就是组件,我们还是用哔哩哔哩这个界面举例,这个每个红圈勾住的地方就可以看做是一个个组件,当然每个组件里面也是可以再嵌套组件的,可以看做是套娃行为

我们上面所写的全部实例化vue的代码它本身就是一个组件,我们一般称为父组件

vue教程-lookroot

全局组件和私有组件

上面说到组件里面可以嵌套组件,这里有个全局和私有的概念要解释一下,首先组件需要先定义然后再使用

比方说,这里有个father组件,我们在这个组件里面定义一个全局的 header组件和一个私有的footer组件,以及定义组件 son

如果们在当前father组件里面使用headerfooter组件都是可行的,如果我们在当前 father组件里面使用了 son 组件,然后再在这个 son组件里面使用 这个 header组件是可行的,但是使用footer组件就不行了,这就是全局与私有的区别

来示例一下

定义组件

定义全局组件

定义的位置呢在实例化 vue 之前,每个组件其实和我们一直在编写的父组件结构是一模一样的,只不过我们将内容写在template标签里面,还是有其他 methods data等标签

// 全局组件
Vue.component('vue-header', {
    template: '<h2>我是全局hearder</h2>',
    data:{
        ...
    }
});
let vm = ...

定义私有组件

私有组件就定义在 当前组件的属性中,意思就是组件里面还可以定义私有组件,套娃行为

let vm = new Vue({
    el: '#app',
    data() {
        return {
        }
    },
    methods: {
    },
    // 私有组件
    components: {
        'vue-footer': {
            template: '<h2 >我是footer私有组件</h2>',
        },
    },
})

直接将模板写在页面中

上面的写法有这样一个问题啊,比如页面比较复杂,那么我们在*JavaScript中写 templateIDE提示不够友好,写法也不够清晰,还记得前面的 #app 这个挂载吗,那么我们同样也可以使用这种方式

首先在页面中定义模板

<body>
    <div id="app">
        ...
    </div>
    <!-- 定义son组件 和上面没有关系 -->
    <template id="son">
        <div>
            <h2>我是son组件</h2>
        </div>
    </template>
</body>

然后我们需要到vue实例中去注册这个组件,我们可以注册为私有的也可以注册为全局的,这里我们注册为私有的

 components: {
     'vue-footer': {
         template: '<h2 >我是footer私有组件</h2>',
     },
     'son': {
         //选中页面中定义的 son 模板
         template: '#son',
     }
 },

使用组件

主要是注册了的组件,我们可以直接在页面中使用,标签名可以是原名,也可以转成驼峰式命名

<div id="app">
    <vue-header></vue-header>
    <son></son>
    <vue-footer></vue-footer>
</div>

浏览器看下效果

vue教程-lookroot

那么这里我们就验证一下上面说的全局和私有,我们来到页面中定义的 son组件,在这个组件里面使用刚刚定义的header和footer组件

 <!-- 定义son组件 和上面没有关系 -->
 <template id="son">
     <div>
         <h2>我是son组件</h2>
         <vue-header></vue-header>
         <vue-footer></vue-footer>
     </div>
 </template>

打开浏览器看看效果,看这里这个 header 就出现了两次,这个footer就提示未注册

vue教程-lookroot

动态组件

上面说的切换显示,这也没展示出来啊,难道切换显示是通过不同组件的 v-if来实现吗?这里我们就介绍动态组件,还是以bilibili的界面为例

v2

我们把在讲 vue绑定class这一节的代码复制过来做简单修改,我们这里主要是实现这个切换的效果,不具体展示后面的数据

首先定义三个组件模板

<template id="writeback">
    <div>
        <div>回复我的</div>
        <div>这是回复我的界面</div>
    </div>
</template>
<template id="aite">
    <div>
        <div>@我的</div>
        <div>这是@我的界面</div>
    </div>
</template>
<template id="zan">
    <div>
        <div>收到的赞</div>
        <div>这是收到赞的界面</div>
    </div>
</template>

然后注册为私有组件

components: {
    'writeback': {
        template: '#writeback'
    },
    'aite': {
        template: '#aite'
    },
    'zan': {
        template: '#zan'
    },

然后我们在data中定义一个字符串,确定当前要显示的组件名称

showName: 'writeback',

然后我们修改一下上次列表的渲染和点击事件(点击切换当前显示的组件名),然后用flex简单布局一下这个页面,最后使用component标签来动态展示组件,:is="showName"就是使用data中定义的属性来确定组件要显示哪一个

<div id="app">
    <div class="bili-content">
        <div class="bili-leftnav">
            <ul>
                <li :class="{active:showName=='writeback'}" @click="showName='writeback'">回复我的</li>
                <li :class="{active:showName=='aite'}" @click="showName='aite'">@我的</li>
                <li :class="{active:showName=='zan'}" @click="showName='zan'">收到赞的</li>
            </ul>
        </div>
        <div class="bili-rightcontent">
            <!-- 动态组件 -->
            <component :is="showName">
                </omponent>
        </div>
    </div>
</div>

简单写下样式

<style>
    * {
        margin: 0;
        padding: 0;
    }
    body {
        background-color: #67a3e1;
    }
    .bili-content {
        display: flex;
    }
    .bili-content .bili-leftnav {
        width: 200px;
        background-color: #dee9f7;
    }
    .bili-content .bili-leftnav li {
        padding: 10px 20px;
        height: 50px;
        line-height: 50px;
    }
    .bili-content .bili-leftnav li:hover {
        cursor: pointer;
    }
    .bili-content .bili-leftnav .active {
        color: #5faee3;
    }
    .bili-content .bili-rightcontent {
        flex: 1;
        margin-left: 20px;
    }
    .bili-content .bili-rightcontent div:nth-child(1) div {
        margin-top: 10px;
        background: #ffffff;
        padding: 10px 10px;
    }
</style>

可以看下效果,这就实现了动态切换了是不是很简单啊

vue教程-lookroot

组件切换过渡效果

上面的切换是不是太枯燥了啊,我们这里可以给组件切换加上过渡效果的

我们这里还是使用 animate.css

 <link href="https://cdn.jsdelivr.net/npm/animate.css@3.5.1" rel="stylesheet" type="text/css">

加上 transition包裹

<div class="bili-rightcontent">
    <!-- 动态组件 -->
    <transition enter-active-class="animated fadeInDown" leave-active-class="animated fadeInUp"
        mode="out-in" :duration="200">
        <component :is="showName">
            </omponent>
    </transition>
</div>

这里有个补充内容,:duration可以决定过渡的时间, mode决定过渡的模式,因为这个组件切换其实是一个消失,一个出现的过程,所以不定义过渡模式的话,就会同时执行出现和消失的过渡效果,就花屏了 !

vue教程-lookroot

看下效果是不是就很不错了啊

vue教程-lookroot

vuecomponent组件间通信

其实大家很容易就想到啊,组件之间肯定不会是单独运行就行了的,肯定要做一些沟通啊这一节就是组件间的通信

这一节我们使用前面的 vue事件这一节的表单来完成示例

vue父组件传递值给子组件

我们把页面中的这个渲染userlist给它封装成一个组件

<template id="userlistcom">
    <ul>
        <li v-for="(user,index) in userlist" :key="index">
            <img :src="user.avatar" alt="">
            {{user.name+'-'+user.age}}
            {{user.sex===1?'男':'女'}}
        </li>
    </ul>
</template>

然后注册为私有组件,并在页面中使用

components: {
    'userlistcom': {
        template: '#userlistcom',
    },
},
<userListCom ></userListCom>

这样肯定是不行的啊,因为这个组件里面是没有定义 userlist看会报错啊,那么我们怎么做呢?如果在当前组件里面去定义 userlist并且加载数据的话,那么这个封装组件还有啥意义呢?我们本来就是为了复用,比如我在不同的地方都会使用到这个组件,但是不同地方请求的数据是不同的,那么这个方式肯定就不符合了

所以我们要使用父组件传递值给子组件的方法,:getuserlist的意思就是我们给子组件传递一个名叫 getuserlist的值,传递的值是当前父组件中的 userlist

<userListCom :userlist="userlist"></userListCom>

传是传递了?子组件怎么去获取呢这就要用到 prop,简答修改一下这个组件的注册

prop的类型可以为

vue教程-lookroot
'userlistcom': {
    template: '#userlistcom',
    //父组件传值
    props: {
        //接受值
        getuserlist: {
            //类型
            type: Array,
            //是否必须
            required: true
        },
    }

然后我们去子组件的列表渲染中简单修改,循环这个 getuserlist

<li v-for="(user,index) in getuserlist" :key="index">
    <img :src="user.avatar" alt="">
    {{user.name+'-'+user.age}}
    {{user.sex===1?'男':'女'}}
</li>

看下效果,能行

vue教程-lookroot

子组件传递值给父组件

这里我们将这个 form表单也封装为组件

<template id="userformcom">
    <div>
        <form action="">
            <input type="text" v-model="user.name">
            <input type="text" v-model="user.age">
            <input type="text" v-model="user.avatar">
            <select v-model="user.sex">
                <option :value="sex.count" v-for="(sex,index) in sexlist">{{sex.name}}</option>
            </select>
            <button @click.prevent="submit">存储</button>
        </form>
    </div>

然后注册,这里的话我们就将data中的usersexlist也移动到组件的定义里面去

'userformcom': {
    template: '#userformcom',
    data() {
        return {
            user: {
                name: 'lookroot',
                age: 12,
                sex: 2,
                avatar: ''
            },
            sexlist: [
                { count: 1, name: '男' },
                { count: 2, name: '女' },
            ]
        }
    },
    methods: {
    }
}

然后在页面中使用这个组件

<userFormCom></userFormCom>

这样肯定也是不行的,如果点击保存,怎么添加到父组件的userlist上面去呢?这就得用到子组件传递值给父组件了

我们给这个子组件绑定一个自定义事件@submit,如果子组件触发这个事件将会触发当前父组件中的savesubmit事件

<userFormCom @submit="savesubmit" ></userFormCom>

先定义父组件中的savesubmit事件

savesubmit(user) {
    user = Object.assign({}, user);
    this.userlist.push(user);
}

然后我们在子组件中修改表单的点击事件,让它触发这个父组件绑定的事件并且携带一个值

methods: {
    submit() {
        //触发父组件事件 并把user表单传递过去
        this.$emit('submit', this.user);
    }
}

查看效果是完全可以的

vue教程-lookroot

父组件触发子组件的事件

比如我们想在父组件的页面中点击按钮实现 清空 form组件的输入默认值

首先定义一个按钮,并绑定点击事件

<button @click="clear">清空表单</button>

然后我们给这个组件绑定一个 ref 用来操作它的dom

<userFormCom @submit="savesubmit" ref="userFormCom"></userFormCom>

然后添加这个点击事件

clear() {
    //触发子组件的事件
    this.$refs.userFormCom.clearForm();
},

这样就可以触发子组件的事件了,但是子组件里面还没有定义这个 clearForm事件

那么我们在子组件定义一下

 methods: {
     submit() {
         //触发父组件事件
         this.$emit('submit', this.user);
     },
     //清空表单
     clearForm() {
         this.user = {}
     }
 }

看看效果可以的

vue教程-lookroot
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345