【VUE】-前端探秘 VUE组件深入了解

1.通过is解决在使用组件中出现的bug(适用于<table>,<ul>,<ol>,<select>等标签)

场景:一个表格有多行,我想把行抽出来定义成一个自定义组件,然后在页面中显示出来.

    <div id="app">
        <table>
            <row></row>
            <row></row>
            <row></row>
        </table>
    </div>
    <script>
        Vue.component('row',{
           template:'<tr><td>this is a row</td></tr>'
        });
        var vm = new Vue({
            el:'#app'
        })
    </script>

如果一切正常,<row>中的内容应该出现在<table></table>内,但实际却出现在了与<table>标签同级的位置: 

这种情况下,我们可以采用is来解决,因为自定义标签破坏了HTML语法中<table>标签内必须是<tr>标签的标准语法.

    <div id="app">
        <table>
            <tr is="row"></tr>
            <tr is="row"></tr>
            <tr is="row"></tr>
        </table>
    </div>

可以看出,使用is后完美解决该bug. 

2.子组件的data必须是一个函数然后返回对象,不能像父组件里的data一样直接在其中定义对象.

    <div id="app">
        <table>
            <tr is="row"></tr>
            <tr is="row"></tr>
            <tr is="row"></tr>
        </table>
    </div>
    <script>
        Vue.component('row',{
            data:{
                msg:'this is a row'
            },
           template:'<tr><td>{{msg}}</td></tr>'
        });
        var vm = new Vue({
            el:'#app'
        })
    </script>

然后发现这样报错了,原因是每个子组件里的数据都应该是它独有的,各子组件之间的数据不应该被共享,所以子组件里定义的data要以函数的形式,返回数据. 

修改后:

        Vue.component('row',{
            data:function(){
                return{
                    msg:'this is a row'
                }
            },
           template:'<tr><td>{{msg}}</td></tr>'
        });
        var vm = new Vue({
            el:'#app'
        })

数据正常显示且无任何报错. 

 

3. 组件中的ref引用.

场景:尽管Vue不推荐操作DOM,但有些时候不得不去操作DOM,这时候我们可以通过this.$refs.xx来获取ref="xx"的元素.

<body>
    <div id="app">
        <div ref="test" @click="getRef">1</div>
    </div>
    <script>
        var vm = new Vue({
            el:'#app',
            method:{
               getRef:function(){
                alert(this.$refs.test.innerHTML)
                }
            }
        })
    </script>
</body>

在点击时可以看到浏览器弹出提示框1

基于此,可以开发一个具有2个子组件,点击子组件后子组件数值递增,并触发父组件进行求和的demo:

<body>
    <div id="app">
        <num @change="handleChange" ref="one"></num>
        <num @change="handleChange" ref="two"></num>
        <div>{{total}}</div>
    </div>
    <script>
        Vue.component('num',{
            data:function () {
                return{
                    num:0
                }
            },
            template:'<div v-on:click="add">{{num}}</div>',
            methods:{
                add:function () {
                    this.num++
                    this.$emit('change')
                }
            }
        });
        var vm = new Vue({
            el:'#app',
            data:{
                total:0
            },
            methods: {
                handleChange:function () {
                    this.total = this.$refs.one.num + this.$refs.two.num;
                }
            }
        })

4. 组件之间的值传递

4.1父组件向子组件传值

父组件向子组件传值比较简单,可以通过props进行传值,这里仅仅引出.

主要是为了铺垫接下来父组件向子组件传值后比较容易犯的一个错(子组件不可直接修改父组件传递过来的值,因为父组件传递来的值可能被多个子组件使用,这里有点类似于java中多线程访问数据时的数据不安全性.

    <div id="app">
        <counter :count="num1"></counter>
        <counter :count="num2"></counter>
    </div>
    <script>
        var counter = {
            props:['count'],
            template:'<div>{{count}}</div>'
        }
        vm = new Vue({
            el:'#app',
            data:{
                num1:1,
                num2:2
            },
            components:{
                counter:counter
            }
        })
    </script>

 

当 prop 验证失败的时候,(开发环境构建版本的) Vue 将会产生一个控制台的警告.

父组件向子组件传值后,如果子组件想改变接收到的值之后,想改变父组件传过来的值,可以先对该数据进行一份拷贝,然后再操作拷贝后的值,这样就不会报错了,也就是官网所说的单向数据流.

    <script>
        var counter = {
            props:['count'],
            data:function(){
                return{
                   add:this.count
                }
            },
            methods:{
              sum:function () {
                  this.add++
              }
            },
            template:'<div @click="sum">{{add}}</div>'
        }
        vm = new Vue({
            el:'#app',
            data:{
                num:1
            },
            components:{
                counter:counter
            }
        })
    </script>

4.2组件参数校验

    <div id="app">
        <my-prop></my-prop>
    </div>
    <script>
        Vue.component('my-prop',{
            template:'<div>{{content}}</div>',
            props:{
                content:{
                    //参数类型校验
                    type:[String,Number],
                    //参数是否必传校验
                    required:false,
                    //参数如果不传,默认值指定
                    default:'laohan',
                    //参数值校验,要符合某种规则,比如长度必须大于5
                    validator:function (value) {
                        return (value.length > 5)
                    }
                }
            }
        });
        var vm = new Vue({
            el:'#app'
        })
    </script>

4.3非父子组件之间的传值(Bus/总线方式/观察者模式)

<div id="app">
    <bus-msg content="DELL"></bus-msg>
    <bus-msg content="LEE"></bus-msg>
</div>
<script>
    //定义全局挂载的bus
    Vue.prototype.bus = new Vue();
    Vue.component('bus-msg', {
        props: {
            content: {
                type: String
            }
        },
        //创建content的副本
        data:function(){
          return {
              myContent:this.content
          }
        },
        template: '<div @click="handleClick">{{myContent}}</div>',
        //页面加载完成后,点击会向外触发change事件
        methods: {
            handleClick:function () {
                this.bus.$emit('change',this.myContent)
            }
        },
        //通过bus监听该change事件,并更新副本中的值
        mounted:function () {
            var this_ = this;
            this.bus.$on('change',function (msg) {
                this_.myContent = msg;
            })
        }
    });
    var vm = new Vue({
        el: '#app'
    })
</script>

效果:

点击DELL页面上两个组件的值都变为DELL,点击LEE页面上两个组件的值都变为LEE

 

5.给组件绑定原生事件(.native)

如果想让子组件直接触发根组件的事件,我们可以给子组件绑定原生的事件,这样做可以节省很多代码量:

同样的功能,当点击子组件时触发父组件弹框,我们分别用非原生事件和原生事件实现一下:

     //非原生方式: 
    <script>
        Vue.component('native-event',{
            template:'<div @click="sayHi">hello</div>',
            methods: {
                sayHi:function () {
                    this.$emit('parent')
                }
            }
        });
        var vm = new Vue({
            el:'#app',
            methods:{
                sayHiParent:function () {
                    alert('hi-parent!')
                }
            }
        })
    </script>
    //原生实现
    <div id="app">
        <native-event @click.native="sayHiParent"></native-event>
    </div>
    <script>
        Vue.component('native-event',{
            template:'<div>hello</div>',
        });
        var vm = new Vue({
            el:'#app',
            methods:{
                sayHiParent:function () {
                    alert('hi-parent!')
                }
            }
        })
    </script>

6.插槽<slot>

在Vue中,可以使用插槽来动态地为子组件传递一些DOM,从而对组件做一些增强.

6.1普通插槽:普通插槽只允许有一个

    <div id="app">
        <my-slot>
            <h1>普通插槽</h1>
        </my-slot>
    </div>
    <script>
        Vue.component('my-slot',{
            template:'<div><p>hello</p><slot></slot></div>',
        });
        var vm = new Vue({
            el:'#app'
        })
    </script>

 6.2具名插槽:具名插槽可以有多个

    <div id="app">
        <my-slot>
            <h1 slot="header">头部</h1>
            <h2 slot="footer">尾部</h2>
        </my-slot>
    </div>
    <script>
        Vue.component('my-slot',{
            template:'<div><slot name="header"></slot><p>hello</p><slot name="footer"></slot></div>',
        });
        var vm = new Vue({
            el:'#app'
        })
    </script>

6.3作用域插槽

固定写法:

<template slot-scope="xx">
   {{xx.item}}
</template>

普通的插槽一般是显示父组件向子组件传递的内容,但作用域插槽显示的是子组件向父组件传递的内容,这样就可以在父组件调用子组件时灵活的决定子组件显示的样式了.

这么说可能有点绕,直接上代码:

<div id="app">
    <scope-slot>
        <template slot-scope="props">
            <h1>{{props.item}}</h1>
        </template>
    </scope-slot>
</div>
<script>
    Vue.component('scope-slot',{
        template:'<div><ul><slot v-for="item in list" :item="item"></slot></ul></div>',
        data:function () {
            return{
                list:[1,2,3,4,5]
            }
        }
    });
    let vm = new Vue({
        el:'#app'
    })
</script>

 

 

7.动态组件和v-once

有时候需要在多个组件之间来回切换,除了可以使用v-if,还可以使用动态组件来解决. 

固定语法是<component v-bind:is="组件名"></component>

<div id="app">
    <!--普通方式实现-->
    <!--<my-one v-if="type==='my-one'"></my-one>-->
    <!--<my-two v-if="type==='my-two'"></my-two>-->
    <!--动态组件实现-->
    <component :is="type"></component>
    <button @click="changeType">change</button>
</div>
<script>
    Vue.component('my-one',{
        template:'<div>one</div>'
    });
    Vue.component('my-two',{
        template:'<div>two</div>'
    });
    let vm = new Vue({
        el:'#app',
        data:{
            type:'my-one'
        },
        methods:{
            changeType:function () {
                this.type = (this.type==='my-one'? 'my-two' : 'my-one')
            }
        }
    })
</script>

当我们在点击切换组件后,组件之间可以完成切换:

 

但是在组件切换过程中,每次点击change都会销毁旧组件和创建新组件,这样是比较耗性能的,我们可以通过添加v-once指令让组件仅在第一次加载时被渲染,然后存储到缓存中,之后可以直接从缓存中取,不需要重复创建.

    Vue.component('my-one',{
        template:'<div v-once>one</div>'
    });
    Vue.component('my-two',{
        template:'<div v-once>two</div>'
    });

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值