组件详解 7

组件是Vue.js最核心的功能,也是整个框架设计最精彩的地方,当然也是最难掌握的。

组件与复用

组件用法
回顾一下我们创建Vue实例的方法

var app = new Vue({
        el: '#app'
  })

组件与之类似,需要注册后才能使用。注册有全局注册和局部注册两种方式。全局注册后,任何Vue实例都可以使用。全局注册示例代码如下:

    <div id="app">
        <my-component></my-component>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        Vue.component('my-component', {
            template: '<div>我是组件{{msg}}</div>',
            data() {
                return {
                    msg: '我是组件的data'
                }
            }
        })
        var app = new Vue({
            el: '#app'
        })
    </script>

局部组件

    <div id="app">
        <my-component></my-component>
        <login></login>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        var app = new Vue({
            el: '#app',
            components: {
                // 如果没有-,可以不带引号
                login: {
                    template: '<div>login</div>'
                },
                'my-component': {
                    template: '<div>局部组件</div>'
                }
            }
        })
    </script>

Vue组件的模板在某些情况下会受到HTML的限制,比如

内规定只允许是,可以使用特殊的is属性来挂载组件

    <div id="app">
        <table>
            <tbody is="my-component">

            </tbody>
        </table>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>

        Vue.component('my-component',{
            template: '<div>这是组件的内容</div>'
        })
        var app = new Vue({
            el: '#app',
        })
    </script>

使用props传递数据

基本用法

在组件中,使用选项props来声明需要从父级接收的数据,props的值可以是两种,一种是字符串数组,一种是对象,先介绍数组的用法,比如我们构造一个数组,接受一个来自父级的数据message,并把它在数组模板中渲染

    <div id="app">
        <my-component message="来自父组件的数据">
        </my-component>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        Vue.component('my-component', {
            props: ['message'],
            template: '<div>{{message}}</div>'
        })
        var app = new Vue({
            el: '#app',
        })
    </script>

props中声明的数据与组件data函数return的数据主要区别就是props来自父级,而data中的是组件自己的数据,作用域是组件本身,这两种数据都可以在模板template及计算属性computed和方法methods中使用。上例的数据message就是通过props从父级传递过来的,在组件的自定义标签上直接写该props的名称,如果要传递多个数据,在props数组中添加项即可
由于HTML特性不区分大小写,当使用DOM模板时,驼峰命名的props改为短横线分隔

    <div id="app">
        <my-component message-message="来自父组件的数据">
        </my-component>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        Vue.component('my-component', {
            props: ['messageMessage'],
            template: '<div>{{messageMessage}}</div>',
            data() {
                return{
                    msg: '组件自己的数据'
                }
            }
        })
        var app = new Vue({
            el: '#app'
        })
    </script>

很多时候,传递的数据不是写死的,而是来自父级的动态数据,可以使用v-bind动态绑定props的值,当父组件数据变化时,会传递给子组件

    <div id="app">
        <input type="text" v-model="parentMessage">
        <my-component :message="parentMessage">
        </my-component>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        Vue.component('my-component', {
            props: ['message'],
            template: '<div>{{message}}</div>',
            data() {
                return{
                    msg: '组件自己的数据'
                }
            }
        })
        var app = new Vue({
            el: '#app',
            data:{
                parentMessage:''
            }
        })
    </script>

这里用 v-model 绑定了父级的数据 parentMessage ,当通过输入框任意输入时,子组件接收到
props"message"也会实时响应,并更新组件模板。

组件通信

父组件向子组件通信,通过props传递数据,但Vue组件通信的场景不止有这一种,归纳起来,组件之间通信可以用下图表示
在这里插入图片描述
自定义事件

当子组件需要向父组件传递数据时,就要用到自定义事件,我们在介绍指令v-on时有提到,v-on除了监听DOM事件外,还可以用于组件之间的自定义事件

如果你了解过Javascript的设计模式————观察者模式,一定知道dispatchEvent和addEventListener这两种方法。Vue组件也有与之类似的一套模式,子组件用 e m i t ( ) 来 触 发 事 件 , 父 组 件 用 emit()来触发事件,父组件用 emit()on()来监听子组件的事件。

父组件也可以直接在子组件的自定义标签上使用v-on来监听子组件触发的自定义事件,示例

    <div id="app">
        <p>总数:{{total}}</p>
        <my-component @increase="handleGetTotal" @reduce="handleGetTotal">
        </my-component>
    </div>
    <template id="com">
        <div>
            <button @click="handleIncrease">
                +1
            </button>
            <button @click="handleReduce">
                -1
            </button>
        </div>
    </template>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        Vue.component('my-component', {
            template: '#com',
            data() {
                return {
                    counter: 0
                }
            },
            methods: {
                handleIncrease() {
                    this.counter++;
                    this.$emit('increase', this.counter);
                },
                handleReduce() {
                    this.counter--;
                    this.$emit('reduce', this.counter);
                }
            }
        })
        var app = new Vue({
            el: '#app',
            data: {
                total: 0
            },
            methods: {
            // 父组件内有方法,子组件内部通过触发自己的事件,然后通过this.$emit调用父组件的方法,同时传递值
                handleGetTotal(total) {
                    this.total = total;
                }
            }
        })
    </script>

上边示例,子组件有两个按钮,分别实现加1和减1的效果,在改变组件的data
"counter"后,通过 e m i t ( ) 再 把 它 传 递 给 父 组 件 , 父 组 件 用 v − o n : i n c r e a s e 和 v − o n : r e d u c e 。 emit()再把它传递给父组件,父组件用v-on:increase和v-on:reduce。 emit()von:increasevon:reduceemit()方法的第一个参数是自定义事件的名称,例如示例的increase和reduce后面的参数都是要传递的数据,可以不填或者填写多个

使用v-model
Vue2.x可以在自定义组件

    <div id="app">
        <p>总数:{{total}}</p>
        <my-component v-model="total"></my-component>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        Vue.component('my-component', {
            template: '<button @click="handleClick">+1</button>',
            data() {
                return {
                    counter: 0
                }
            },
            methods: {
                handleClick() {
                    this.counter++;
                    this.$emit('input', this.counter)

                }
            }
        })
        var app = new Vue({
            el: '#app',
            data: {
                total: 0
            }

        })
    </script>

和上边一样

    <div id="app">
        <p>总数:{{total}}</p>
        <my-component @input="handleGetTotal"></my-component>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        Vue.component('my-component', {
            template: '<button @click="handleClick">+1</button>',
            data() {
                return {
                    counter: 0
                }
            },
            methods: {
                handleClick() {
                    this.counter++;
                    this.$emit('input', this.counter)
                }
            }
        })
        var app = new Vue({
            el: '#app',
            data: {
                total: 0
            },
            methods: {
                handleGetTotal(total) {
                    this.total = total;
                }
            }

        })
    </script>

在这里插入图片描述

先看一个比较常规的网站布局,这个网站由一级导航,二级导航,左侧列表,正文以及底部版权信息5个模块组成,如果要将他们都组件化,这个结构可能会是:

        <app>
            <menu-main></menu-main>
            <menu-sub></menu-sub>
            <div class="container">
                <menu-left></menu-left>
                <container></container>
            </div>
            <app-footer></app-footer>
        </app>

当需要让组件组合使用,混合父组件的内容与子组件的模板时,就会用到slot,这个过程叫做内容分发。以为例,它有两个特点

  • 组件不知道它的挂载点会有什么内容,挂载点的内容是由的父组件决定的
  • 组件很可能有它自己的模板
    props传递数据、events触发事件和slot内容分发就构成了Vue组件的3个API来源,再复杂的组件也是由这3部分构成的

作用域

正式介绍slot前,需要先知道一个概念:编译的作用域。比如父组件中有如下模板:

        <child-component>
            {{message}}
        </child-component>

这里的message就是一个slot,但是它绑定的是父组件的数据,而不是组件的数据。
父组件模板的内容是在父组件作用域内编译,子组件模板的内容实在子组件作用域内编译,例

    <div id="app">
        <!-- 这里的状态showChild绑定的是父组件的数据, -->
        <child-component v-show="showChild">
        </child-component>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        Vue.component('child-component', {
            template: '<div>子组件</div>'
        })

        var app = new Vue({
            el: '#app',
            data: {
                showChild: true
            }
        })
    </script>
    <!-- 这里的状态showChild绑定的是父组件的数据,如果想在子组件上绑定,那应该是下边 -->
    <div id="app">
        <child-component>
        </child-component>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        Vue.component('child-component', {
            template: '<div v-show="showChild">子组件</div>',
            data() {
                return {
                    showChild: true
                }
            }
        })
        var app = new Vue({
            el: '#app'
        })
    </script>

因此, slot 分发的内容,作用域是在父组件上的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值