Vue2.x学习五:组件

Component组件是Vue.js最强大的功能之一,它能够扩展HTML元素,封装可重用的代码(减小代码冗余)。
在较高层面上,组件是自定义元素。好比说,咱们本身发明了标签,别人不承认,可是vue可以帮咱们去解析,帮咱们把它转换成你们都能承认的。javascript

注册

咱们开发组件的时候,须要告诉vue,咱们建立了一个新的组件,这里咱们就须要注册。
注册分为局部注册和全局注册。html

全局注册

就是说咱们能够注册一个组件,把它挂载到vue的全局变量上。
它里面有个属性叫Vue.component(组件名称,组件定义)
咱们能够简单的来测试一下:vue

<body>
    <div class="container">
        <!-- 引用自定义组件 -->
        <sf-line></sf-line>
        <!-- 若是咱们要写5个这样的组件,那么咱们能够用v-for -->
        <sf-line v-for="n in 5"></sf-line>
        <!-- 若是咱们想要在sf-line标签中直接插入值,能够用slot插槽 -->
        <sf-line>你好</sf-line>
    </div>
    <script>
        // 在建立vue对象以前,注册组件
        // 经过全局注册Vue.component(组件名称,组件定义),调用组件
        // 组件名称是自定义的
        // 组件定义template(模板),封装html标签,它里面是多个html的综合体,以反引号包裹html内容,(反引号的好处是能够任意回车,不用作字符串的拼接)
        Vue.component('sf-line',{
            template:`
                <div style="background-color:orange;border-radius:3px;padding:.5em 1em;margin:.5em">
                    <span>hello</span>
                    <!-- slot 插槽,能够用来接受sf-line中的值-->
                    <span><slot></slot></span>
                </div>
            `
        });

        //建立vue的实例
        new Vue({
            el:'.container',
            data:{  }
        });
    </script>
</body>

这就是一个最简单的全局注册的组件。java

如今,咱们不但愿将<span>hello</span>中的值写死,咱们但愿可以动态改变span标签中的值,即外部组件(咱们能够把.container理解为一个根组件)向内部组件传值。此时咱们能够借用一个属性props。
props表明属性,也就是说,外部能够传参数,咱们经过props来接收。node

<body>
    <div class="container">
        <!-- 动态绑定属性 属性的绑定用v-bind 
            咱们绑定一个在props中定义的属性名,
            它的属性值为vue实例data中,定义的lineText,而且lineText会被看成变量来解析
        -->
        <sf-line v-bind:node="lineText"></sf-line>
        <!-- 简写 -->
        <sf-line :node="lineText"></sf-line>
        <!-- 咱们也能够定义一个属性,不用v-bind -->
        <sf-line node="组件"></sf-line>
        <!-- 若是咱们这样写,lineText会被直接看成一个值打印出来,不会被看成变量解析 -->
        <sf-line node="lineText"></sf-line>
    </div>
    <script>
        //注册组件
        Vue.component('sf-line',{
            template:`
                <div style="background-color:orange;border-radius:3px;padding:.5em 1em;margin:.5em">
                    <!--接收到props中的值以后,经过双大括号来取出,双大括号中的值会被看成变量来解析-->
                    <span>{{node}}</span>
                </div>
            `,
            // 接收参数,假如参数为node
            props:['node']
        });

        //建立vue的实例
        new Vue({
            el:'.container',
            // 在.container中,咱们调用了一个子组件<sf-line>,如今咱们能够在vue实例的data中,定义一个值,好比说lineText
            data:{ 
                lineText:'component'
            }
        });
    </script>
</body>

这里咱们要补充一下外部组件与内部组件传值的机制,这里咱们要记住:
组件是彻底独立的,它有本身的data,methods,钩子函数等,而且这些内容都包含在组件定义中——Vue.component(组件名称,组件定义)。函数

<body>
    <div class="container"> 
        <!-- 这是vue实例中的msg -->       
        <p>{{msg}}</p>
        <!-- 这里显示组件中的msg -->
        <sf-line></sf-line>
    </div>
    <script>
        // 注册组件,组件必定要写在vue实例上面
        Vue.component('sf-line',{
            template:`
                <div>
                    <!-- 组件是彻底独立的,因此组件中的msg调用的就是组件data中的msg -->
                    <span>{{msg}}</span>
                </div>
            `,
            // 用来接收外部组件向内部组件传值
            props:[ ],
            // 组件样式中,一样有data,methods,钩子函数等...
            // 组件中的data是一个方法(不是属性),要有返回值return
            // data方法会返回一个对象,保证它是多实例的
            // 若是像vue实例中data:{}这样写,那么它就是单例的,它会出现一个问题,好比说:组件会应用屡次,若是改变其中一个组件,那么其余组件也会变,这就是单例的
            // 咱们但愿这个组件是彻底独立的,因此咱们要return{...},也就是说:咱们每次调用data的时候,都会新建一个对象
            data(){
                return{
                    msg:'这是组件中的msg'
                }
            },
            methods:{

            },
            created(){
                console.log('created');
            }
        });

        //建立vue的实例
        new Vue({
            el:'.container',
            data:{ 
                msg:'这是vue实例中的msg'
            }
        });
    </script>
</body>

了解了组件传值以后,咱们来学习一下如何来给组件绑定事件
咱们根据下面代码来学习:学习

<body>
    <div class="container">
        <sf-line :node="lineText"></sf-line>
    </div>
    <script>
        Vue.component('sf-line',{
            template:`
                <div style="background-color:orange;border-radius:3px;padding:.5em 1em;margin:.5em;position:relative">
                    <span>{{node}}</span>
                    <!-- 目标:当点击关闭时,div消失 -->
                    <!-- 第一种方式:在父元素的角度上,将它隐藏 -->
                    <!-- 第二种方式:子元素本身将本身隐藏 -->
                    <!-- 父组件:.container ; 子组件:<sf-line> -->
                    <span style="position:absolute;right:1em;cursor:pointer">关闭</span>
                </div>
            `,
            props:['node'],
            methods:{

            }
        });

        new Vue({
            el:'.container',
            data:{ 
                lineText:'component'
            }
        });
    </script>
</body>

这里咱们主要讲解
第一种方式:在父元素的角度上,将它隐藏测试

<body>
    <div class="container">
        <sf-line :node="lineText"></sf-line>
    </div>
    <script>
        Vue.component('sf-line',{
            template:`
                <!-- 咱们在div中添加一个v-show,将隐藏操做放在这上面,在组件data中定义它是否显示 -->
                <div 
                    v-show="isShow"
                    style=" background-color:orange;
                            border-radius:3px;
                            padding:.5em 1em;
                            margin:.5em;
                            position:relative"
                >
                    <span>{{node}}</span>
                    <!-- 点击下面按钮关闭
                         因为close是组件内部函数,因此在组件的methods中定义属性
                     -->
                    <span
                        @click="close" 
                        style=" position:absolute;
                                right:1em;
                                cursor:pointer"
                    >关闭</span>
                </div>
            `,
            props:['node'],
            data(){
                return{
                    // 默认状况下让它显示
                    isShow:true
                }
            },
            methods:{
                close(){
                    // 当点击close时,让它隐藏
                    this.isShow=false;
                }
            }
        });

        new Vue({
            el:'.container',
            data:{ 
                lineText:'component'
            },
            methods:{
                
            }
        });
    </script>
</body>

此刻咱们点击的是外部<sf-line>标签的事件,是在component里面,可是外部vue实例中的methods并无定义方法。也就是说,子组件本身偷偷摸摸就没了,可是父组件根本就不知道。
若是说,父组件这时候想要在子组件被删除以后,从新刷新整个页面。即子组件要通知外面的父组件,它点击了按钮。那么咱们如今应该怎么作?
这里有一个知识点,子组件向父组件传递事件,相似于一种事件传递,但此处叫事件发射this

这里代码能够这样完善:spa

<body>
    <div class="container">
        <!-- 父组件此时要接收子组件发射的自定义事件
             v-on此时监听的是子组件发射的事件close,
             咱们为它在父组件中绑定一个方法closeHandler
         -->
        <sf-line :node="lineText" @close="closeHandler"></sf-line>
    </div>
    <script>
        Vue.component('sf-line',{
            template:`
                <!-- 咱们在div中添加一个v-show,将隐藏操做放在这上面,在组件data中定义它是否显示 -->
                <div 
                    v-show="isShow"
                    style=" background-color:orange;
                            border-radius:3px;
                            padding:.5em 1em;
                            margin:.5em;
                            position:relative"
                >
                    <span>{{node}}</span>
                    <!-- 点击下面按钮关闭
                         因为close是组件内部函数,因此在组件的methods中定义属性
                     -->
                    <span
                        @click="close" 
                        style=" position:absolute;
                                right:1em;
                                cursor:pointer"
                    >关闭</span>
                </div>
            `,
            props:['node'],
            data(){
                return{
                    // 默认状况下让它显示
                    isShow:true
                }
            },
            methods:{
                close(){
                    // 通知父元素
                    //(子组件向父组件发射emit自定义事件close)
                    this.$emit('close');
                    // 当点击close时,让它隐藏
                    this.isShow=false;
                }
            }
        });

        new Vue({
            el:'.container',
            data:{ 
                lineText:'component'
            },
            methods:{
                // 父组件中绑定的方法
                closeHandler(){
                    alert('closeHandler');
                }
            }
        });
    </script>
</body>

局部注册

局部注册没必要将每一个组件都注册到全局。咱们能够经过某个Vue实例或组件的实例的选项components注册在,仅在其做用域中可用的组件上。

<body>
    <div class="container">
        <sf-list :node="msg"></sf-list>
    </div>
    <script type="text/javascript">
        //我声明的组件
        let sfList={
            template:`
                <div>{{node}}</div>
            `,
            props:['node']
        };

        new Vue({
            el:'.container',
            data:{
                msg:'hello component'
            },
            // 局部注册
            components:{
                // 能够直接将组件定义写在这里面
                // 可是,为了避免使代码显得臃肿,我在上面声明一个组件sfList
                // 将上面注册的组件声明进来
                // 属性值:自定义的组件名
                // 属性值:上面声明的组件名
                'sf-list':sfList
                /*
                 sfList:sfList                
                 这里也能够用驼峰式命名
                */
                /*
                 sfList
                 直接这样简写也是能够的
                */
                //以上三种方式:<sf-list :node="msg"></sf-list>均可以解析
            }
        });
    </script>
</body>

slot插槽(补充)

插槽定义在组件中,用于接受组件内部的内容。
在全局注册的第一个例子里面咱们简单的用了slot插槽。
这种直接插入,不须要命名的插槽,咱们称为匿名插槽
咱们先来简单回顾一下:

<body>
    <div class="container">
        <!-- 在组件中,用slot接收插入的内容 -->
        <sf-container>hello</sf-container>
    </div>
    <script type="text/javascript">
        //注册组件
        Vue.component('sf-container',
            template`
                <div class="sf-container">
                    <div class="sf-header">
                        <!-- 用slot接收插入的内容 -->
                        <slot></slot>
                    </div>
                    <div class="sf-content"></div>
                    <div class="sf-footer"></div>
                </div>
            `,
        );
    </script>
</body>

在一个模板中,能够出现不少插槽。
咱们要去区分它们,咱们能够给它取个名字,叫作具名插槽

<body>
    <div class="container">
        <sf-container>
            <!-- 此时的属性值与slot中命名的名字相同 -->
            <div slot="header">header</div>
            <div slot="content">content</div>
            <div slot="footer">footer</div>
        </sf-container>
    </div>
    <script type="text/javascript">
        //注册组件
        Vue.component('sf-container',
            template`
                <div class="sf-container">
                    <div class="sf-header">
                        <!-- 为不一样的插槽取不一样的名字 -->
                        <slot name="header"></slot>
                    </div>
                    <div class="sf-content">
                        <slot name="content"></slot>
                    </div>
                    <div class="sf-footer">
                        <slot name="footer"></slot>
                    </div>
                </div>
            `,
        );
    </script>
</body>

组件中除了具名插槽外,还有一种插槽,就是做用域插槽

<body>
    <div class="container">
        <!-- 调用一个组件,里面绑定属性stuList,将studentList中的值传给stuList -->
        <sf-list :stulist="studentList">
            <!-- 提供一个模板,它里面可让咱们定义td的具体格式 -->
            <!-- slot-scope用于将元素或组件表示为做用域插槽,此属性不支持动态绑定 -->
            <!-- 属性名自定义 -->
            <template slot-scope="stuScope">
                <!-- 在这里显示插入的内容 -->
                <!-- 咱们能够在这里选择要插入的数据 -->
                <!-- 做用域插槽,至关于为咱们多了一个定制的功能 -->
                {{stuScope.foo.id}}
                <a href="#">{{stuScope.foo.name}}</a>
            </template>
        </sf-list>
    </div>
    <script type="text/javascript">
        //注册组件
        Vue.component('sf-list',{
            template:`
                <div>
                    <p>原来的写法</p>
                    <ul>
                        <li v-for="item in stulist">{{item.name}}</li>
                    </ul>
                    <p>做用域插槽</p>
                    <ul>
                        <li v-for="item in stulist">
                            <!-- 定义一个插槽,在里面随意绑定一个值foo-->
                            <slot :foo="item"></slot>
                        </li>
                    </ul>
                </div>
                
            `,
            // 接收studentList中的内容(外部组件向内部组件传值)
            // props接收的值不区分大小写,不要用驼峰式命名,不然可能报错
            props:['stulist']
        });
        //建立实例
        new Vue({
            el:'.container',
            data:{
                studentList:[{
                    id:1,
                    name:'terry'
                },{
                    id:2,
                    name:'larry'
                },{
                    id:3,
                    name:'tom'
                }]
            }
        });
    </script>
</body>
相关文章
相关标签/搜索