vue组件详解

使用组件的原因

提高代码的复用性

组件的使用方法

  1. 全局注册
Vue.component('my-component',{
        template:'<div>我是组件的内容</div>'
    })

占内存

  1. 局部注册
var app = new Vue({
        el: '#app',
        data: {

        },
        components: {
            'app-component': {
                template: '<div>app-component</div>'
    }
        }
    })
  1. 由于HTML标签的限制,如table中只能有trtdtbody,要想在table中使用组件就要用到is,这样一来,tbody在文档中就会被组件替换掉
<table>
        <tbody is="app-component"></tbody>
</table>

我们也可以利用这个属性来干掉其他事,比如动态组件,也就是点一个按钮就切换一个组件

组件使用的奇淫巧技

  1. 必须用连字符命名,如:my-compoent
  2. template中的内容必须要用DOM元素包裹
// 正确写法
template: '<div>app-component</div>'

// 错误写法
template: 'app-component'
  1. 组件的定义中,还可以有data,methods,computed
  2. data必须是一个方法
components: {
            'plus-component': {
                template: '<button @click="count111++">{{count111}}</button>',
                data: function () {
                    return {
                        count111: 1
                    }
                }
            }
        }

使用props传递数据 父亲向儿子传递数据

  1. 在子组件上声明属性msg,然后在子组件处用props来接收
<div id="app">
    我是爸爸
    <child-component :msg="fatherMsg"></child-component>
</div>
<script>
    var app = new Vue({
        el: '#app',
        components: {
            'child-component': {
                // 从父组件和本身data传入的数据在methods、template中都可以使用
                props: ['msg'],
                data: function(){
                    return {
                        count:1
                    }
                },
                template: '<div id="child">{{msg}}</div>',
            }
        }
    })
</script>
  1. 动态向子组件传入信息,则用v-model
<div id="app">
    我是爸爸
    <input type="text" v-model="fatherMsg">
    <child-component :msg="fatherMsg"></child-component>
</div>
<script src="../vue.js"></script>
<script>
    var app = new Vue({
        el: '#app',
        data: {
            count: 1,
            fatherMsg: 'hello'
        },
        methods: {
            plus: function () {
                this.count++
            }
        },
        components: {
            'child-component': {
                // 从父组件和本身data传入的数据在methods、template中都可以使用
                props: ['msg'],
                data: function(){
                    return {
                        count:1
                    }
                },
                template: '<div id="child">{{msg}}</div>',
            }
        }
    })
</script>
  1. ==在props中定义的属性都可以在组件内使用,可以在template、computed、methods中使用==

单向数据流

只能父组件向子组件传递信息,子组件不能向父组件传递信息

在组件中使用从props传来的数据可以直接用this.xxx来获取

 'child-component': {
                props: ['msg'],
                template: '<div id="child" >{{count}}</div>',
                data:function(){
                    // 组件中的data要返回一个函数
                    return {
                        count: this.msg
                    }
                }
            }
  1. 用v-bind来绑定style属性的时候可以使用对象语法,注意
<div :style="xxx" id="div1"></div>
computed:{
            xxx: function () {
                return {
                    //这是用到了v-bind绑定style的对象语法
                    width: this.ccc + 'px',
                    'background-color': 'red',
                    height: 10 + 'px'
                }
            }
        }

数据验证

在HTML中绝对不允许使用驼峰,因为HTML会把大写全部转化为小写。在props中可以用驼峰或短横线,在template和data、this.xxx中只能使用驼峰,这是因为在vue中短横线会被误认为减号,会报错。
==总结:在HTML中使用短横线,在vue中使用驼峰==

  1. 对传入的数据进行数据验证

注意props不能再返回一个数组,而是一个对象

<div id="app">
    <child-component :a="a" :b="b" :c="c" :d="d" :e="e" :f="f"></child-component>
</div>
<script src="../vue.js"></script>
<script>
    var app = new Vue({
        el: '#app',
        data: {
            a: 'hello',
            b: 123,
            c: true,
            d: 123,
            e:[456],
            f: 15
        },
        components: {
            'child-component': {
                props: {
                    a: String,     // 验证字符串,如果不是字符串页面会照常渲染但会报错
                    b: [String,Number], // 意思是传入的数据是String或是Number类型
                    c: {
                        type: Boolean,
                        default: false   // 默认值为false
                    },
                    d: {
                        type: Number,
                        required: true   // 表示d是必须要传入的值
                    },
                    e:{
                        // 如果是数组或对象,默认值要用函数来返回
                        type: Array,
                        default: function () {
                            return [789]
                        }
                    },
                    f:{
                        // 自定义一个验证函数
                        validator: function(value){
                            return value>10
                        }
                    }
                 },
                template: '<div id="child" >{{a}}--{{b}}--{{c}}--{{d}}--{{e}}--{{f}}</div>',
                data:function(){
                    // 组件中的data要返回一个函数
                    return {
                        count: this.msg
                    }
                }
            }
        }
    })
</script>

组件之间的通信

子组件给父组件传递信息

实现一个功能,点击子组件的按钮,改变父组件的数据

<div id="app">
    我的账户余额为:{{count}}
    <child-component @xxx="changeCount"></child-component>
</div>
<script src="../vue.js"></script>
<script>
    var app = new Vue({
        el: '#app',
        data: {
            count: 2000
        },
        methods: {
            changeCount: function (value) {
                this.count = value
            }
        },
        components: {
            'child-component': {
                // 注意这里template一定要有DOM元素包裹
                template: `<div>
                <button @click="handleIncrease">+1000</button>
                <button @click="handleReduce">-1000</button>
                </div>
                `,
                data:function(){
                    return {
                        count: 2000
                    }
                },
                methods: {
                    handleIncrease: function () {
                        this.count += 1000    // 这里的this.count是组件里面的count
                        this.$emit('xxx',this.count)   // emit表示向父组件通信,第一个参数是自定义事件的名称,后面是要传入的值,可以写无限个
                    },
                    handleReduce: function () {
                        this.count -= 1000
                        this.$emit('xxx',this.count)
                    }
                }
            }
        }
    })
</script>

子组件向父组件传递信息的步骤:

  1. 在父组件里写入自定义事件名,事件名后面跟着的是要执行的方法
  2. 子组件通过$emit向父组件传递信息,第一个参数是自定义的事件名,后面是要传递的参数,可以接无限个

在组件中使用v-model

v-model其实也是一个语法糖:

  1. 它等于先用v-bind绑定一个值
  2. 监听v-on了一个input事件
    上述代码又可以简化成
<div id="app">
    我的账户余额为:{{total}}
    <child-component v-model="total"></child-component>
</div>
<script src="../vue.js"></script>
<script>
    var app = new Vue({
        el: '#app',
        data: {
            total: 2000
        },
        components: {
            'child-component': {
                template: `<div>
                <button @click="handleIncrease">+1000</button>
                <button @click="handleReduce">-1000</button>
                </div>
                `,
                data:function(){
                    return {
                        count: 2000
                    }
                },
                methods: {
                    handleIncrease: function () {
                        this.count += 1000    // 这里的this.count是组件里面的count
                        this.$emit('input',this.count)   // emit表示向父组件通信,第一个参数是自定义事件的名称,后面是要传入的值,可以写无限个
                    },
                    handleReduce: function () {
                        this.count -= 1000
                        this.$emit('input',this.count)
                    }
                }
            }
        }
    })
</script>

非父组件之间的通信

非父组件之间的通信需要一个bus作为中介
在父组件的data内新建一个bus: new Vue()对象。
然后在A组件内用this.$parent.bus.$emit('事件名',参数)

Vue.component('a-component',{
        template: `
                <div>
                    <input type="text" v-model="text">
                </div>`,
        data:function () {
            return {
                text: '我是A组件'
            }
        },
        watch:{
            //watch的用法,key代表要监听的数据,后面接操作
            text: function () {
                this.$root.bus.$emit('xxx',this.text)
            }
        }
    })

在B组件内,在对应的钩子事件中用this.$parent.bus.$on('事件名',参数)

 Vue.component('b-component',{
        template: '<div>我是b组件---{{textb}}</div>',
        data:function () {
            return {
                textb: 'bbbbb'
            }
        },
        created: function () {
            var _this = this
            this.$root.bus.$on('xxx',function (value) {
                // alert('created')
                _this.textb = value
            })
        }
    })
  1. 修改父组件的数据,父链
this.$parent.xxxx
  1. 修改儿子组件的数据,子链
    在每个儿子组件上加上ref属性
    <a-component ref="a"></a-component>
    <b-component ref="b"></b-component>
    <c-component ref="c"></c-component>

然后在父组件上使用:

this.$refs.a.count = 'aaa' // 修改a组件的数据

使用slot分发内容

编译的作用域

<div id="app">

    <child-component v-show="bool">
            {{message}}
    </child-component>
</div>

message属于父组件的作用域
父组件模板内的内容在父组件内编译
子组件模板内的内容在子组件内编译

slot(插槽)的用法和作用

在下面这段代码中

    <child-component v-show="bool">
            {{message}}
    </child-component>

message其实是渲染不出来的,尽管message的作用域在父组件,但我们想让它子组件内的template中渲染出来,这时我们就要使用到了slot功能。

slot的作用是混合父组件的内容和子组件的模板,从而弥补视图的不足

<div id="app">
    <child-component >
            <p>hello world</p>
    </child-component>
</div>
......
Vue.component('child-component',{
        template: `
         <div style="border: 1px solid red;">
                <slot>
                        如果父组件没有内容,我就是子组件的默认内容
                </slot>
         </div>

具名插槽

在要插入数据的标签使用slot属性,在子组件的template处使用slot标签,并且写上对应的name

<child-component >
            <h1 slot="header">我是标题</h1>
            <p>hello world</p>
</child-component>
... ...
 Vue.component('child-component',{
        template: `
         <div style="border: 1px solid red;">
             <div style="color: red;">
             <slot name="header">
                    这里是header的默认内容
                </slot>
                </div>

                <slot>
                        如果父组件没有内容,我就是子组件的默认内容
                </slot>
         </div>
        `,

作用域插槽

作用域插槽是一个特殊的插槽,可以从子组件的插槽中获取数据。使用一个可以复用的模板来替换已经渲染的元素
用法:先在子组件的slot标签上name属性和自定义的属性

Vue.component('child-component',{
        template: `
         <div style="border: 1px solid red;">
             <div style="color: red;">
                <slot name="header" text="我是子组件的数据">
                    这里是header的默认内容
                </slot>
              </div>
         </div>
        `,

然后在父组件上,用slot对应其中的name,用slot-scope对应的自定义名字来获取从子组件传来的数据

<child-component >
        <template slot="header" slot-scope="props">
            {{props.text}}
        </template>
    </child-component>

现在已经可以不用template,可以直接使用标签如<p>

在子组件中访问自己的slot

通过this.$slots.(NAME)来访问

<div id="app">
    <child-component >
        <h1 slot="header">我是父组件的内容</h1>
    </child-component>
</div>
Vue.component('child-component',{
        template: `
         <div style="border: 1px solid red;">
             <div style="color: red;">
             <slot name="header">
                    这里是header的默认内容
               </slot>
               </div>
         </div>
        `,
        data:function () {
            return {
                message: '我是子组件的内容',
                bool: true
            }
        },
        mounted:function () {
            var header = this.$slots.header
            console.log(header)
            console.log(header[0].elm.innerText)
        }
    })

组件的高级用法-动态组件

所谓的动态组件就是通过is属性来动态切换组件

<div id="app">
    <component-a :is="thisView"></component-a>
    <button @click="changeToA">第一句</button>
    <button @click="changeToB">第二句</button>
    <button @click="changeToC">第三句</button>
    <button @click="changeToD">第四句</button>
</div>
<script src="../vue.js"></script>
<script>
    Vue.component('componentA',{
       template: `<div>离离原上草</div>`
    })
    Vue.component('componentB',{
       template: `<div>一岁一枯荣</div>`
    })
    Vue.component('componentC',{
       template: `<div>野火烧不尽</div>`
    })
    Vue.component('componentD',{
       template: `<div>春风吹又生</div>`
    })
    var app = new Vue({
        el: '#app',
        data: {
            thisView: 'componentA'
        },
        methods :{
            changeToA: function () {
                this.thisView = 'componentA'
            },
            changeToB: function () {
                this.thisView = 'componentB'
            },
            changeToC: function () {
                this.thisView = 'componentC'
            },
            changeToD: function () {
                this.thisView = 'componentD'
            }
        }
    })
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,009评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,808评论 2 378
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,891评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,283评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,285评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,409评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,809评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,487评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,680评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,499评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,548评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,268评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,815评论 3 304
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,872评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,102评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,683评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,253评论 2 341