vue组件

vue组件

组件的介绍

什么 是组件

组件的出现,就是为了拆分Vue实例的代码量的,能够让我们以不同的组件,来划分不同的功能模块,将来我们需要什么样的功能,就可以去调用对应的组件即可。类似于模块化,但是又不同于模块化

  • 模块化:是从代码逻辑的角度进行划分的;方便代码分层开发,保证每个功能模块的职能单一

  • 组件化:是从UI界面的角度进行划分的;前端的组件化,方便UI组件的重用

怎么定义以及使用组件

组件Component是 Vue.js 最强大的功能之一。组件可以扩展 HTML 元素,封装可重用的代码。全局组件的定义和注册有三种方式,我们接下来讲一讲。

方式1:

使用Vue.extend方法定义组件,使用 Vue.component方法注册组件。

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

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/vue-resource@1.5.1"></script>
    

</head>

<body>
    
    <div id="test">
         <!-- 如果要使用组件,直接把组件的名称,以 HTML 标签的形式,引入到页面中,即可 -->
        <account> </account>

       
    </div>

    <script>
       //第一步:使用 Vue.extend 定义组件
       var myAccount = Vue.extend({
        // 通过 template 属性,指定了组件要展示的HTML结构。template 是 Vue 中的关键字,不能改。
        template: '<div><h2>Login</h2> <h3>Register</h3></div>' 

       });
        //第二步:使用 Vue.component 注册组件
        // Vue.component('组件的名称', 创建出来的组件模板对象)
       Vue.component('account', myAccount); //第一个参数是组件的名称(标签名),第二个参数是模板对象
       new Vue({
        el: "#test"
       })
      
       
    </script>

</body>

</html>

上方代码中,在注册组件时,第一个参数是标签名,第二个参数是组件的定义。

上图中,注意两点:

注意1、红框部分,要保证二者的名字是一致的。如果在注册时,组件的名称是驼峰命名,比如:

Vue.component('myComponent', myAccount); //第一个参数是组件的名称(标签名),第二个参数是模板对象

那么,在标签中使用组件时,需要把大写的驼峰改为小写的字母,同时两个单词之间使用-进行连接:

<my-component> </my-component>

所以,为了避免名字不一致的问题,我们注册组件时,组件的名称可以直接写成my-component。比如:(避免驼峰不一致的建议写法)

    Vue.component('my-component', myAccount);

注意2、绿框部分,一定要用一个大的根元素(例如<div>)包裹起来。如果我写成下面这样,就没有预期的效果:

            template: '<h2>登录页面</h2> <h3>注册页面</h3>'

方式2:

Vue.component方法定义、注册组件(一步到位)。

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

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/vue-resource@1.5.1"></script>
    

</head>

<body>
    
    <div id="test">
       
        <account> </account>

       
    </div>

    <script>
       

       Vue.component('account', {
        template: '<div><h2>Login</h2> <h3>Register</h3></div>' 
       });
       new Vue({
        el: "#test"
       })
      
       
    </script>

</body>

</html>

方式3(推荐方式)

上面的写法一、写法二并不是很智能,因为在定义模板的时候,没有智能提示和高亮,容易出错。我们不妨来看看写法三。

将组件内容定义到template标签中去。、

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

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/vue-resource@1.5.1"></script>
    

</head>

<body>
    <template id="myAccount">
        <div>
            <h1>Login page</h1>
            <h3>Register Page</h3>
        </div>
       
    </template>
    
    <div id="test">
       
        <account> </account>

       
    </div>

    <script>
       

       Vue.component('account', {
        template: '#myAccount' 
       });
       new Vue({
        el: "#test"
       })
      
       
    </script>

</body>

</html>

代码截图如下:

img

写法三其实和方法二差不多,无非是把绿框部分的内容,单独放在了<template>标签中而已,这样有利于 html 标签的书写。

使用components定义私有组件

我们在上一段中定义的是全局组件,这样做的时候,多个Vue实例都可以使用这个组件。

我们还可以在一个Vue实例的内部定义私有组件,这样做的时候,只有当前这个Vue实例才可以使用这个组件。

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

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="vue2.5.16.js"></script>
</head>

<body>

    <!-- 定义模板 -->
    <template id="loginTmp">
        <h3>这是私有的login组件</h3>
    </template>

    <div id="app">
        <!-- 调用Vue实例内部的私有组件 -->
        <my-login></my-login>
    </div>

    <script>
        new Vue({
            el: '#app',
            data: {},
            components: { // 定义、注册Vue实例内部的私有组件
                myLogin: {
                    template: '#loginTmp'
                }
            }
        });
    </script>
</body>

</html>

为组件添加 data 和 methods

既然组件是一个页面,那么,页面中可能会有一些功能要动态展示。因此,我们有必要为组件添加 data 和 methods。

代码举例如下:

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

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/vue-resource@1.5.1"></script>
    

</head>

<body>
    <template id="myAccount">
        <div>
          <!-- 在组件的模板中,调用本组件中的data -->
            {{myData}}
            <a href="#" @click="login">Login</a>
            <h1>Login page</h1>
            <h3>Register Page</h3>
        </div>
       
    </template>
    
    <div id="test">
       
        <account> </account>
        <account> </account>


       
    </div>

    <script>
       

       Vue.component('account', {
        template: '#myAccount',
        //组件中的 data
            //【注意】组件中的data,不再是对象,而是一个方法(否则报错);而且这个方法内部,还必须返回一个对象才行
            // 组件中 的data 数据,使用方式,和实例中的 data 使用方式完全一样!!!
        data: function(){
            return {myData: "dany"}
        },
        methods:{
            login: function(){
                alert("login operator")
            }
        }
       });
       new Vue({
        el: "#test"
       })
      
       
    </script>

</body>

</html>

上方代码所示,我们在account组件中添加的data 和 methods,其作用域只限于account组件里,保证独立性。

注意,在为组件添加数据时,data不再是对象了,而是function,而且要通过 return的形式进行返回;否则,页面上是无法看到效果的。通过 function返回对象的形式来定义data,作用是:

  • 上方代码中,组件<account>被调用了两次(不像根组件那样只能调用一次),但是每个组件里的数据 myData是各自独立的,不产生冲突。
  • 换而言之,通过函数返回对象的目的,是为了让每个组件都有自己独立的数据存储,而不应该共享一套数据。
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="vue2.5.16.js"></script>
</head>

<body>
    <div id="app">
        <counter></counter>
        <hr>
        <counter></counter>
        <hr>
        <counter></counter>
    </div>

    <template id="tmpl">
        <div>
            <input type="button" value="让count加1" @click="increment">
            <h3>{{count}}</h3>
        </div>
    </template>

    <script>
        var dataObj = { count: 0 }

        // 这是一个计数器的组件, 身上有个按钮,每当点击按钮,让 data 中的 count 值 +1
        Vue.component('counter', {
            template: '#tmpl',
            data: function () {
                // return dataObj //当我们return全局的dataObj的时候,这个dataObj是共享的
                return { count: 0 } // 【重要】return一个**新开辟**的对象数据
            },
            methods: {
                increment() {
                    this.count++
                }
            }
        })

        // 创建 Vue 实例,得到 ViewModel
        var vm = new Vue({
            el: '#app',
            data: {},
            methods: {}
        });
    </script>
</body>

</html>

如上图所示,每当我们创建一个新的组件实例时,就会调用data函数,data函数里会return一个新开辟的对象数据。这样做,就可以保证每个组件实例有独立的数据存储

组件的切换

vue 指定控制切换

代码举例:(登录组件/注册组件,二选一)使用v-if和v-else结合flag进行切换

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

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/vue-resource@1.5.1"></script>
    

</head>

<body>

    
    <div id="test">
     <!-- 温馨提示:`.prevent`可以阻止超链接的默认事件 -->
        <a href="" @click.prevent="flag=true">login</a>
        <a href="" @click.prevent="flag=false">register</a>
	 <!-- 登录组件/注册组件,同时只显示一个 -->
        <login v-if="flag"></login>
        <register v-else="flag"></register>
       
    </div>

    <script>
       
      Vue.component('login',{
        template: "<h3>login page</h3>"
      })

      Vue.component('register',{
        template: "<h3>register page</h3>"
      })
       new Vue({
        el: "#test",
        data: {
            flag:true
        }
       })
      
       
    </script>

</body>

</html>

component标签实现组件切换

上面的例子中,我们是通过flag的值来进行组件的切换。但是,flag的值只可能有两种情况,也就是说,v-if和v-else只能进行两个组件之间的切换。

那如何实现三个甚至三个以上的组件切换呢?这里,我们可以用到Vue提供的<component>标签。

我们先来看一下<component>标签的用法。

基于上面的代码,如果我想让login组件显示出来,借助<component>标签可以这样做:

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

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="vue2.5.16.js"></script>
</head>

<body>
    <div id="app">
        <!-- 点击按钮后,设置变量`comName`为不同的值,代表着后面的component里显示不同的组件 -->
        <a href="" @click.prevent="comName='login'">登录</a>
        <a href="" @click.prevent="comName='register'">注册</a>

        <!-- Vue提供了 component ,来展示对应名称的组件 -->
        <!-- component 是一个占位符, :is 属性,可以用来指定要展示的组件的名称 -->
        <!-- 此处的`comName`是变量,变量值为组件名称 -->
        <component :is="comName"></component>

    </div>

    <script>
        // 组件名称是 字符串
        Vue.component('login', {
            template: '<h3>登录组件</h3>'
        })

        Vue.component('register', {
            template: '<h3>注册组件</h3>'
        })

        // 创建 Vue 实例,得到 ViewModel
        var vm = new Vue({
            el: '#app',
            data: {
                comName: 'login' // 当前 component 中的 :is 绑定的组件的名称
            },
            methods: {}
        });
    </script>
</body>

</html>

组件间传值

父组件向子组件传值

我们可以这样理解:Vue实例就是一个父组件,而我们自定义的组件(包括全局组件、私有组件)就是子组件

【重点】需要注意的是,子组件不能直接使用父组件中的数据。父组件可以通过props属性向子组件传值

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

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/vue-resource@1.5.1"></script>
    

</head>

<body>

    
    <div id="test">
    <!-- 	父组件在引用子组件的时候, 通过 属性绑定(v-bind:)的形式,  -->
        <!--   把 需要传递给 子组件的数据,以属性绑定的形式,传递到子组件内部,供子组件使用 -->
        <component1 v-bind:parent-msg="msg"></component1>
    </div>
	
	<!-- 定义子组件的模板 -->
    <template id="tmp">
      <!-- 在子组件的模板中,使用props中的属性 -->
        <h2 @click="change">I'm child part, i want use the data in parent:{{parentMsg}}</h2>
    </template>

    <script>
       
     var vue = new Vue({
        el:"#test",
        data:{
            msg: "parent data is 123"
        },
        methods: {},
        components:{
         // 子组件默认无法访问到 父组件中的 data 中的数据 和 methods 中的方法
            component1:{ //将子组件的名称定义为 component1
                template: "#tmp",
                data: function(){// 注意: 子组件中的 data 数据,并不是通过 父组件传递过来的,而是子组件自身私有的,比如: 子组件通过 Ajax ,请求回来的数据,都可以放到 data 身上;
                    return { // data 上的数据,都是可读可写的
                        title: "chile title",
                        content: "child content"
                    }
                },
                  // 注意: 组件中的 所有 props 中的数据,都是通过 父组件 传递给子组件的
                    // props 中的数据,都是只读的,无法重新赋值
                    //props: ['parentMsg'], // 第一步:把父组件传递过来的 parentMsg 属性,先在 props 数组中,定义一下,这样,才能使用这个数据
                props: ['parentMsg'],
                directives: {},
                    filters: {},
                    components: {},
                methods:{
                    change(){
                        
                    }
                }

            }
        }
     })
       
    </script>

</body>

</html>

父组件给子组件传值的步骤

根据上方截图,我们可以总结出父组件给子组件传值的步骤如下。

(1)在子组件的props属性中声明父亲传递过来的数据

(2)定义子组件的模板时,使用props中的属性

(3)父组件在引用子组件时,进行属性绑定。

子组件中,data中的数据和props中的数据的区别

  • 子组件中的 data 数据,并不是通过 父组件传递过来的,而是子组件自身私有的,比如: 子组件通过 Ajax ,请求回来的数据,都可以放到 data 身上。props 中的数据,都是通过 父组件 传递给子组件的。
  • data中的数据是可读可写的;props中的属性只是可读的,无法重新赋值,重新赋值会报错(也就是说,子组件不要直接去修改父组件中的数据)。

父组件向子组件传递方法

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

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="vue2.5.16.js"></script>
</head>

<body>
    <div id="app">
        <!-- 父组件向子组件 传递 方法,是通过 事件绑定机制; v-on。当我们自定义了 一个 事件属性 parent-show(这个地方不能用驼峰命名)之后,-->
        <!-- 那么,子组件就能够,通过 emit 来调用 传递进去的 这个 方法了 -->
        <!-- 【第一步】。意思是说,`show`是父组件的方法名,`parent-show`是自定义的时间属性,稍后要在子组件中用到 -->
        <component1 @parent-show='show'></component1>
    </div>

    <!-- 定义子组件的模板 -->
    <template id="myTemplate">
        <!-- 【第二步】按照正常的写法来:点击按钮,调用子组件的方法 -->
        <div @click="childClick">我是子组件,点击调用父组件的方法</div>
    </template>

    <script>
        // 创建 Vue 实例,得到 ViewModel
        var vm = new Vue({
            el: '#app',
            data: { //父组件的data
                // msg: '父组件中的数据'
            },
            methods: {
                show: function () { // 定义父组件的show方法
                    console.log('父组件提供的方法');
                }
            },
            components: {
                component1: { //将子组件的名称定义为 component1
                    template: '#myTemplate',
                    data() { // 子组件的data
                        return {
                            // content: '子组件私有的数据 content'
                        }
                    },
                    props: [''],
                    directives: {},
                    filters: {},
                    components: {},
                    methods: {
                        childClick() {
                            // 当点击子组件的按钮时,如何 拿到 父组件传递过来的 func 方法,并调用这个方法???
                            //  emit 英文原意: 是触发,调用、发射。意思是,触发父组件的方法
                            // 【第三步】 在子组件的方法中,通过 emit 触发父组件的方法
                            this.$emit('parent-show');
                        }
                    }
                }
            }
        });
    </script>
</body>

</html>

子组件向父组件传值

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

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="vue2.5.16.js"></script>
</head>

<body>
    <div id="app">
        <component1 @parent-show='show'></component1>
    </div>

    <!-- 定义子组件的模板 -->
    <template id="myTemplate">
        <h2 @click="childClick">我是子组件,点击调用父组件的方法</h2>
    </template>

    <script>
        // 创建 Vue 实例,得到 ViewModel
        var vm = new Vue({
            el: '#app',
            data: { //父组件的data
                // msg: '父组件中的数据'
            },
            methods: { // 定义父组件的方法
                show: function (arg1, arg2) { //【第二步】父组件里放两个参数,这个两个参数就代表着子组件中的`child 123`、`child 789`
                    console.log('父组件提供的方法');
                    console.log('打印子组件传递过来的参数。参数一:' + arg1 + ',参数二:'+ arg2);
                }
            },
            components: {
                component1: { //将子组件的名称定义为 component1
                    template: '#myTemplate',
                    data() { // 子组件的data
                        return {
                            // content: '子组件私有的数据 content'
                        }
                    },
                    props: [''],
                    directives: {},
                    filters: {},
                    components: {},
                    methods: {
                        childClick() {
                            // 子组件如果要给父组件传递参数,在触发 emit 的时候,通过参数的形式带出去就可以了
                            // 【第一步】在子组件里,我们带两个参数出去,传给父组件
                            this.$emit('parent-show', 'child 123', 'child 789');
                        }
                    }
                }
            }
        });
    </script>
</body>

</html>

示例2:

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

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/vue-resource@1.5.1"></script>
    

</head>

<body>

    
    <div id="test">
        <component1 @parent-show="show"></component1>
    </div>

    <template id="tmp">
        <h2 @click="change">I'm child part, i want use the methd in parent</h2>
    </template>

    <script>
       
     var vue = new Vue({
        el:"#test",
        data:{
            // msg: "parent data is 123"
            parentData: null
        },
        methods: {
            show: function(arg){
                console.log("parent method")
                this.parentData=arg;
                console.log("this is child give parent" + JSON.stringify(this.parentData));
            }
        },
        components:{
            component1:{
                template: "#tmp",
                data: function(){
                    return {
                        childData: { //children params
                                name: 'children',
                                age: 26
                            }
                    }
                },
                props: [''],
                directives: {},
                    filters: {},
                    components: {},
                methods:{
                    change(){
                        this.$emit('parent-show',this.childData);

                    }
                }

            }
        }
     })
       
    </script>

</body>

</html>

案例:发表评论功能的实现

  • 27
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值