vue组件化(第四天)

vue两大核心

1.数据驱动界面
2.组件化

什么是组件化?

1.组件化就是把一个大界面拆分成一个个小组件,相同结构的组件可以进行复用,每一个小界面就是一个组件
组件化有利于提高复用性,简化Vue实例化代码

全局组件

全局组件在任何一个Vue实例控制区域都能使用

Vue中如何创建组件?
  1. 创建组件构造器
  2. 注册已经创建好的组件
  3. 使用注册号的组件
    [注意]:创建组件构造器的时候只能有一个根元素,因此我们通常用div直接把我们想包含的元素包起来
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="vue.js"></script>

</head>
<body>
<div id="app">
<!--    3.使用注册号的组件-->
    <shanjialan></shanjialan>
</div>
<script>
    // 1. 创建组件构造器
    let component=Vue.extend({
        template:`
            <div>
                <p>shanjialan111</p>
                <p>shanjialan222</p>
            </div>
    `});
    // 2.注册已经创建好的组件
    Vue.component("shanjialan",component);
    let vue=new Vue({
        el:"#app",
        data:{
            name:"单佳兰"
        },
        methods:{
            myFn(){
                alert("sjl");
            }
        }
    });
</script>
</body>

</html>
image.png
vue创建组件的简写方法
// 注册组件,传入一个扩展过的构造器
Vue.component('my-component', Vue.extend({ /* ... */ }))

// 注册组件,传入一个选项对象 (自动调用 Vue.extend)
Vue.component('my-component', { /* ... */ })

// 获取注册的组件 (始终返回构造器)
var MyComponent = Vue.component('my-component')

2. 注册组件,传过来一个组件构造器

    Vue.component("shanjialan",Vue.extend({
        template:`<a href="#">link</a>`
    }))

3.省略组件构造器Vue-extend,也会直接调用

    Vue.component("bcd",{
        template:`<a href="#">zhiboyu</a>`
    })

总结:上面三种方法定义组件构造器html代码都没有提示,效率比较低,因此我们要想其他办法来避免这个问题——使用script标签添加id属性,并且将type设为text/html即可,再通过id作为构造器的第二个参数传递给vue-component,代码如下:

<script id="info" type="text/html">
    <a href="#">单佳兰</a>
</script>

但是这种方法其实是不大科学的,明明是HTML,为什么要用script进行封装?于是有了第五种方法,使用<template>加上id属性来封装,这个template就相当于<script id="#" type="text/html>

<template id="shanshun">
    <a>shuanshun</a>
</template>
Vue.component("shanshun",{
        template:"#shanshun"
    })

局部组件

局部组件的使用——在vue实例中添加components以键值对的方式定义,里面的格式和全局组件一样

<template id="shanshun">
    <a>shuanshun</a>
</template>
let vue=new Vue(
components:{
  "shanshun":{
        template:"#shanshun"
    })
}

data和method

1.自定义组件中的data和methods
Vue实例相当于一个大的组件,其中有data和methods属性,相同地,自定义组件相当于一个小的组件,因此也有data和methods属性,但是在自定义组件中data不大一样,在自定义组件中使用data必须赋值一个函数,通过函数返回值来定义数据

Vue.component("shanshun", {
        template: "#shanshun",
        data: function() {
            return{
                name: "shanshun22"
              }
        },
    methods:{
         myApp(){alert('shanjialan');}
    })

为什么自定义组件的data不用对象返回,而是通过函数返回的,因为通过对象定义,那么多个组件就会共用一份数据,就会导致数据混乱,将这个方法返回的数据和当前创建的组件绑定在一起,就避免了数据混乱的问题

image.png
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="vue.js"></script>

</head>
<body>
<div id="app">
    <ab></ab>
    <ab></ab>
    <ab></ab>
</div>
<template id="addnum">
    <div>
        <button @click="add">按钮</button>
        <p>{{number}}</p>
    </div>
</template>
<script>
    //自定义组件
    Vue.component("ab",{
        template:"#addnum",
            data:function () {
                return {
                    number: 0
                };
            },
            methods:{
                 add(){
                this.number++;
             }
        }});
    let vue=new Vue(
        {
            el:"#app"
        }
    )
</script>
</body>
</html>
组件切换

用v-if 的方式进行组件的动态切换

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="vue.js"></script>

</head>
<body>
<div id="app">
    <button @click="toggle">按钮</button>
<photo v-if="isShow"></photo>
    <home v-else></home>
</div>
<template id="photo">
    <a href="vue-animation.html">lainjei</a>
</template>
<template id="home">
    <p>2222</p>
</template>
<script>
    Vue.component("photo",{
        template:"#photo",
    });
    Vue.component("home",{
        template:"#home",
    })
    let vue=new Vue({
        el:"#app",
        data:{
            name:"单佳兰",
            isShow:true
        },
        methods:{
            myFn(){
                alert("sjl");
            },
            toggle(){
                this.isShow=!this.isShow;
            }
        }
    });
</script>
</body>

</html>

这种方式并不是太专业,vue给我们提供了更加专业的做法进行[动态组件 & 异步组件]
语法:<component v-bind:is="currentTabComponent"></component>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="vue.js"></script>

</head>
<body>
<div id="app">
    <button @click="toggle">按钮</button>
    <component v-bind:is="name"></component>
</div>
<template id="photo">
    <a href="vue-animation.html">lainjei</a>
</template>
<template id="home">
    <div>
        <input type="checkbox">
        <span>2222</span>
    </div>
</template>
<script>
    Vue.component("photo",{
        template:"#photo",
    });
    Vue.component("home",{
        template:"#home",
    })
    let vue=new Vue({
        el:"#app",
        data:{
            isShow:true,
            name:'photo'
        },
        methods:{

            toggle(){
               this.name = this.name==='photo'? 'home':'photo';
            }
        }
    });
</script>
</body>

</html>
image.png

image.png

image.png

我们发现切换之后之前选中的状态不见了,那么怎么样保持之前的状态呢?Vue提供了Keep-alive

<div id="app">
    <button @click="toggle">按钮</button>
    <keep-alive>
        <component v-bind:is="name"></component>
    </keep-alive>
</div>

这样切换也可以保存之前的状态了

组件动画

给组件添加动画和给元素添加动画是一样的,不过不同的是过渡动画的离开和进入动画是同时进行的,要想不同时进行,必须给transition设置mode属性

            <component v-bind:is="name"></component>
    </transition>

父子组件

父子组件数据传递
  1. 在Vue子组件不能访问父组件的数据,如果子组件想要访问父组件的数据,必须通过父组件传递
  2. 如何传递?
  • 在父组件中通过v-bind传递数据
    接受格式 v-bind:自定义接受名 = “要传递的数据”
  • 在子组件中通过props接收数据
    接收格式: props:[自定义接受名]
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="vue.js"></script>

</head>
<body>
<div id="app">
    <father></father>
</div>
<template  id="father">
    <div>
        <p>我是父组件</p>
        <p>{{name}}---{{age}}</p>
// - 在父组件中通过v-bind传递数据
 //   接受格式 v-bind:自定义接受名 = “要传递的数据”
        <son :parentname="name" :parentage="age"></son>
    </div>
</template>
<template  id="son">
    <div>
        <p>我是子组件</p>
        <p>{{parentname}}---{{parentage}}</p>
    </div>
</template>
<script>
    Vue.component("father",{
        template:"#father",
        data:function (){
            return {name:"shanjialan",age:19};
        },
        components:{
            "son":{
                template: "#son",
//  - 在子组件中通过props接收数据
//    接收格式: props:[自定义接受名]
                props:["parentname","parentage"]
            }
        }
    });

    let vue=new Vue({
        el:"#app",
        data:{
            name:"单佳兰"
        },
        methods:{
            myFn(){
                alert("sjl");
            }
        }
    });
</script>
</body>

</html>
父子组件数据传递方法
  1. 在Vue子组件不能直接使用父组件的方法,如果子组件想要使用父组件的方法,必须通过父组件传递
  2. 如何传递?
  • 在父组件中通过v-on传递方法
    接受格式 v-on:自定义接受名 = “要传递的方法”
  • 在子组件methods中自定义一个方法,方法中通过this.$emit(自定义接收名)来接受方法
  • 在子组件中通过v-on绑定子组件的方法
    接收格式: @click=”子组件的方法"
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="vue.js"></script>
</head>
<body>
<div id="app">
    <father></father>
</div>
<template  id="father">
    <div>
        <button @click="say">parent</button>
        <p>我是父组件</p>
        <p>{{name}}---{{age}}</p>
 <!--   - 在父组件中通过v-on传递方法 -->
 <!--     接受格式 v-on:自定义接受名 = “要传递的方法” -->
        <son :parentname="name" :parentage="age" @parentsay="say"></son>
    </div>
</template>
<template  id="son">
    <div>
 <!--   - 在子组件中通过v-on绑定子组件的方法 -->
 <!--   接收格式: @click=”子组件的方法" -->
        <button @click="mySon">son</button>
        <p>我是子组件</p>
        <p>{{parentname}}---{{parentage}}</p>
    </div>
</template>
<script>
    Vue.component("father",{
        template:"#father",
        data:function (){
            return {name:"shanjialan",age:19};
        },
// - 在子组件methods中自定义一个方法,方法中通过this.$emit(自定义接收名)来接受方法
        methods: {
            say(){
                console.log("hello,father");
            }
        },
        components:{
            "son":{
                template: "#son",
                props:["parentname","parentage"],
                methods:{
                    mySon(){
                        this.$emit("parentsay");
                    }
                }
            }
        }
    });

    let vue=new Vue({
        el:"#app",
        data:{
            name:"单佳兰"
        },
        methods:{
            myFn(){
                alert("sjl");
            }
        }
    });
</script>
</body>

</html>
子组件给父组件方法传递参数

this.$emit(第一个参数,第二个参数)

  • 第一个参数,父组件需要传递的方法
  • 第二个参数,子组件给父组件传递的参数

this.$emit("parentsay","家和万事兴");

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="vue.js"></script>

</head>
<body>
<div id="app">
    <father></father>
</div>
<template  id="father">
    <div>
        <button @click="say">parent</button>
        <p>我是父组件</p>
        <p>{{name}}---{{age}}</p>
        <son :parentname="name" :parentage="age" @parentsay="say"></son>
    </div>
</template>
<template  id="son">
    <div>
        <button @click="mySon">son</button>
        <p>我是子组件</p>
        <p>{{parentname}}---{{parentage}}</p>
    </div>
</template>
<script>
    Vue.component("father",{
        template:"#father",
        data:function (){
            return {name:"shanjialan",age:19};
        },
        methods: {
            say(data){
                console.log("hello,father");
                console.log(data);
            }
        },
        components:{
            "son":{
                template: "#son",
                props:["parentname","parentage"],
                methods:{
                    mySon(){
                        //第一个参数,父组件需要传递的方法,第二个参数,子组件给父组件传递的参数
                        this.$emit("parentsay","家和万事兴");
                    }
                }
            }
        }
    });

    let vue=new Vue({
        el:"#app",
        data:{
            name:"单佳兰"
        },
        methods:{
            myFn(){
                alert("sjl");
            }
        }
    });
</script>
</body>
</html>
vue组件的命名注意点
  • 注册组件“驼峰命名法”,在使用时用“短横线”代替
<div id="app">
<!--    两者都可以-->
    <my-father></my-father>
    <my-Father></my-Father>
</div>
  • 如果想使用驼峰命名的数据,在传递时必须使用使用短横线+小写的方式,接受时自动转化为驼峰命名

  • 如果是传递方法,则不能用驼峰命名法,只能用短横线命名法

        <son :parent-name="name" :parent-age="age" @parentsay="say"></son>
 components:{
            "son":{
                template: "#son",
                props:["parentName","parentAge"],
                methods:{
                    mySon(){
                        //第一个参数,父组件需要传递的方法,第二个参数,子组件给父组件传递的参数
                        this.$emit("parentsay","家和万事兴");
                    }
                }
            }
        }

数据和方法多级传递

补充:
多层传递只能一层一层往下传递,不管是方法还是数据都是如此!

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="vue.js"></script>

</head>
<body>
<div id="app">
<grandfather></grandfather>
</div>
<template  id="grandfather">
    <div>
        <button @click="say">爷爷</button>
        <p>{{name}}</p>
        <father :gfname="name" @gfsay="say"></father>
    </div>
</template>
<template  id="father">
    <div>
        <button @click="fathersay">爸爸</button>
        <p>{{gfname}}</p>
        <son :fname="gfname" @ssay="fathersay"></son>
    </div>
</template>
<template  id="son">
    <div>
        <button @click="sonsay">儿子</button>
        <p>{{fname}}</p>
    </div>
</template>
<script>
    Vue.component("grandfather",{
        template: "#grandfather",
        data:function (){
            return{
                name:"grandfather"
            }
        },
        methods: {
            say(){
                console.log("hello");
            }
        },
        components:{
            "father":{
                template:"#father",
                methods: {
                    fathersay() {
                        this.$emit("gfsay");
                    }
                },
                props:["gfname"],
                components: {
                    "son":{
                        template:"#son",
                        props: ["fname"],
                        methods:{
                            sonsay(){
                            this.$emit("ssay");
                            }
                        }
                    }
                }
            }
        }
    })
    let vue=new Vue({
        el:"#app",
        data:{
            name:"单佳兰"
        },
        methods:{
            myFn(){
                alert("sjl");
            }
        }
    });
</script>
</body>
</html>
image.png
vue匿名插槽

默认情况下是不能给子组件添加额外的内容的,如果想要添加,必须使用插槽,——插槽就像一个“坑”,由使用者根据自己的需求来填“坑”,如果不填坑,则显示默认数据,如果填坑的话则将添加的内容替换掉slot;

匿名插槽:有多少个匿名插槽,填充的数据则会拷贝几份;虽然我们可以制定多个匿名插槽,但是在企业开发中推荐只写一个匿名插槽,若一定要有几分,则使用具名插槽

具名插槽

由于匿名插槽的弊端,因此有了具名插槽来弥补不足
如何使用??

  1. 定义插槽的时候给slot指定name
  2. 填充的内容指定slot="name"来填充指定插槽
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="vue.js"></script>

</head>
<body>
<div id="app">
    <father>
        <div slot="one">111</div>
    </father>
    <son>
        <div slot="two">222</div>
    </son>
</div>
<template  id="son">
    <div>
        <p>头部</p>
        <slot name="one"></slot>
        <p>底部</p>
    </div>
</template>
<template  id="father">
    <div>
        <p>头部</p>
        <slot name="two"></slot>
        <p>底部</p>
    </div>
</template>
<script>
    Vue.component(
        "father",{
            template:"#father"
        }
    );
    Vue.component(
        "son",{
            template:"#son"
        }
    );
    let vue=new Vue({
        el:"#app",
        data:{
            name:"单佳兰"
        },
        methods:{
            myFn(){
                alert("sjl");
            }
        }
    });
</script>
</body>
</html>

v-slot指令

专门用于替换slot属性,vue推荐的方式
【注意点】v-slot只能添加在template标签上,v-slot:slotname,简写方式:#

<!--</html>-->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="vue.js"></script>

</head>
<body>
<div id="app">
    <father>
        <template v-slot:two>
            <div>slot1</div>
            <div>slot11</div>
        </template>
    </father>
    <son>
        <template v-slot:one>
            <div>slot2</div>
            <div>slot22</div>
        </template>
    </son>
</div>

<template  id="son">
    <div>
        <p>头部</p>
        <slot name="one"></slot>
        <p>底部</p>
    </div>
</template>
<template  id="father">
    <div>
        <p>头部</p>
        <slot name="two"></slot>
        <p>底部</p>
    </div>
</template>
<script>
    Vue.component(
        "father",{
            template:"#father"
        }
    );
    Vue.component(
        "son",{
            template:"#son"
        }
    );
    let vue=new Vue({
        el:"#app",
        data:{
            name:"单佳兰"
        },
        methods:{
            myFn(){
                alert("sjl");
            }
        }
    });
</script>
</body>
</html>

作用域插槽

  1. 什么是作用域插槽?
    作用域插槽就是带数据的插槽,让父组件在填充子组件插槽的同时能够使用自组件的数据;
    作用域插槽的应用场景: 子组件提供数据, 父组件决定如何渲染

2.怎样使用作用域插槽?
2.1 先给自组件的插槽slot添加v-bind:外界访问的名称="需要暴露的数据"
2.2 在父组件添加的那个插槽<template>中添加slot-scope="scopename"属性接收数据
2.3 在template中通过`scopename.外界访问的名称"接收访问数据

v-bind:names="names"作用: 将子组件names数据暴露给父组件;
slot-scope="abc"作用: 接收子组件插槽暴露的数据

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="vue.js"></script>

</head>
<body>
<div id="app">
<father></father>
</div>
<template id="father" >
    <son>
        <template slot-scope="abc">
            <li v-for="(name,index) in abc.names">{{name}}--{{index}}</li>
        </template>
    </son>
</template>
<template id="son">
    <div>
        <p>头部{{names}}</p>
        <slot v-bind:names="names"></slot>
        <p>底部</p>
    </div>
</template>
<script>
    Vue.component(
        "father", {
            template: "#father",
            components: {
                "son": {
                    template: "#son",
                    data: function () {
                        return {names: ["zs", "ls", "ww"]};
                    }
                }
            }
        }
    );
    let vue=new Vue({
        el:"#app",
        data:{
            name:"单佳兰"
        },
        methods:{
            myFn(){
                alert("sjl");
            }
        }
    });
</script>
</body>
</html>
image.png
v-slot指令
  • 在 2.6.0 中,我们为具名插槽和作用域插槽引入了一个新的统一的语法 (即 v-slot 指令)。
    它取代了 slot 和 slot-scope
  • 也就是说我们除了可以通过v-slot指令告诉Vue内容要填充到哪一个具名插槽中,还可以通过v-slot指令告诉Vue如何接收作用域插槽暴露的数据
  • v-slot:插槽名称="作用域名称"
    匿名插槽名称为default
<template v-slot:default="abc">
      <li v-for="(name, index) in abc.names">{{name}}</li>
 </template>

简写:

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

推荐阅读更多精彩内容