Vue.js 组件

为什么要使用组件

代码的复用性

组件的使用方法

1.全局注册

Vue.component('my-component',{
template:'<div>我是组件的内容</div>'
})
优点:所有的nue实例都可以用
缺点:权限太大,容错率降低

2.局部注册

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

  1. vue组件的模板在某些情况下会受到html标签的限制,比如 <table> 中只能还
    有 <tr> , <td> 这些元素,所以直接在table中使用组件是无效的,此时可以使用is属
    性来挂载组件
<table>
<tbody is="my-component"></tbody>
</table>

组件使用的技巧

  1. 推荐使用小写字母加­进行命名(必须) child, my­componnet命名组件
  2. template中的内容必须被一个DOM元素包括 ,也可以嵌套
  3. 在组件的定义中,除了template之外的其他选项—data,computed,methods
  4. data必须是一个方法
var app = new Vue({
            el: '#app',
            components: {
                "app-component": {
                    template: '<div>我是app局部注册的一个组件</div>'
                },
                'btn-component': {
                    template: '<button @click="count++">{{count}}</button>',
                    data: function () {
                        return {
                            count: 0
                        }
                    }
                }
            },
        }
  })
image.png

在data中定义一个方法返回的对象可以是一个component

父组件向子组件儿子传递数组

  1. 在组件中使用props来从父亲组件接收参数,注意,在props中定义的属性,都可以在组件中直接使用
  2. propps来自父级,而组件中data return的数据就是组件自己的数据,两种情况作用域就是组件本身,可以在template,computed,methods中直接使用
<div id="app" style='border: 2px solid blue;height: 400px;'>
        <h5 style="text-align: center;">我是父组件</h5>
        <child-component msg="我是来自父组件的内容"></child-component>
        <hr>
 </div>

var app = new Vue({
            el: '#app',
            data: {
                parentmsg: '今天月亮真圆'
            },
            components: {
                'child-component': {
                    props: ['msg'],
                    template: '<div style="border: 2px solid red;height: 80px">{{msg}}</div>',
                    data: function () {
                        return {
                            count: 1
                        }
                    }
                }
   })

image.png
  1. props的值有两种,一种是字符串数组,一种是对象,本节先只讲数组
  2. 可以使用v­bind动态绑定父组件来的内容
<div id="app" style='border: 2px solid blue;height: 400px;'>
        使用v-bind进行数据的动态绑定,把input中的msg传递给子组件
        <input type="text" v-model="parentmsg">
        <bind-component v-bind:msg='parentmsg'></bind-component>
    </div>

var app = new Vue({
            el: '#app',
            data: {
                parentmsg: '今天月亮真圆'
            },
            components: {
                'bind-component': {
                    props: ['msg'],
                    template: '<div style="border: 2px solid red;height: 80px">{{msg}}</div>',
                    data: function () {
                        return {
                            count: 1
                        }
                    }
                }

            }
        })

单项数据流

解释 : 通过 props 传递数据 是单向的了, 也就是父组件数据变化时会传递给子组
件,但是反过来不行。
目的 :是尽可能将父子组件解稿,避免子组件无意中修改了父组件的状态。
应用场景: 业务中会经常遇到两种需要改变 prop 的情况

一种是父组件传递初始值进来,子组件将它作为初始值保存起来,在自己的作用域
下可以随意使用和修改。这种情况可以在组件 data 内再声明一个数据,引用父组件
的 prop
步骤一:注册组件
步骤二:将父组件的数据传递进来,并在子组件中用props接收
步骤三:将传递进来的数据通过初始值保存起来

<my-component msg="我是父组件传递的数据"></my-component>
Vue.component('my-component', {
            props: ['msg'],
            template: '<div>{{count}}</div>',
            data: function () {
                return {
                    // props中的值可以通过this.xxx直接获取
                    count: this.msg

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

image.png

另一种情况就是 prop 作为需要被转变的原始值传入。这种情况用计算属性就可以了
步骤一:注册组件
步骤二:将父组件的数据传递进来,并在子组件中用props接收
步骤三:将传递进来的数据通过计算属性进行重新计算

<input type="text" v-model='width'>
        <width-component :width='width'></width-component>
Vue.component('width-component', {
            props: ['width'],
            template: '<div :style="style"></div>',
            computed: {
                style: function () {
                    return {
                        width: this.width + 'px',
                        background: 'red',
                        height: '60px'
                    }
                }
            }
        })

数据验证

命名规则

@ 在html中, myMessage 和 mymessage 是一致的,,因此在组件中的html
中使用必须使用kebab­case(短横线)命名方式。在html中不允许使用驼
峰!!!!!!
@ 在组件中, 父组件给子组件传递数据必须用短横线。在template中,必
须使用驼峰命名方式,若为短横线的命名方式。则会直接保错。
@ 在组件的data中,用this.XXX引用时,只能是驼峰命名方式。若为短横线
的命名方式,则会报错。

数据验证

验证的 type 类型可以是:
• String
• Number
• Boolean
• Object
• Array
• Function

<type-component :a="a" :b='666' :c="c" :d='d' :f='f' :g='g'></type-component>
Vue.component('type-component', {
            props: {
                a: String,
                b: [String, Number],
                c: {
                    type: Boolean,
                    default: true //默认值
                },
                d: {
                    type: String,
                    required: true
                },
                e: {
                    type: Array,
                    default: function () {
                        return [666];
                    }
                },
                f: {
                    validator: function (value) {
                        return value > 10
                    }
                },
                g: {
                    type: Function

                }
            },
            template: '<div>{{a}}--{{b}}--{{c}}--{{d}}--{{e[0]}}</div>',
            data: function () {
                return {
                    abc: this.myMsg
                }
            }
        })
        var app = new Vue({
            el: '#app',
            data: {
                a: "1",
                b: "666",
                c: "",
                d: "32423",
                e: [],
                f: 88,
                g: console.log(123)
            }
        })
image.png

组件通信

子组件给父组件传递数据

第一步:自定义事件
第二步: 在子组件中用$emit触发事件,第一个参数是事件名,后边的参数是要传递的数据
第三步:在自定义事件中用一个参数来接受

<div id="app">
        您现在的银行卡余额是{{total}}
        <son-component @change='handleTotal'></son-component> //第一步:自定义事件
    </div> 
 Vue.component('son-component', {
            template: '<div>\
                <button @click="handleincrease">+1000</button>\
                <button @click="handlereduse">-1000</button>\
            </div>',
            data: function () {
                return {
                    count: 1000
                }
            },
            methods: {
                handleincrease: function () {
                    this.count = this.count + 1000
                    this.$emit('change', this.count) //第二步: 在子组件中用$emit触发事件,第一个参数是事件名,后边的参数是要传递的数据
                },
                handlereduse: function () {
                    this.count = this.count - 1000
                    this.$emit('change', this.count) //第二步: 在子组件中用$emit触发事件,第一个参数是事件名,后边的参数是要传递的数据

                },
            }
        })
        var app = new Vue({
            el: '#app',
            data: {
                total: 2000
            },
            methods: {
                handleTotal: function (value) {
                    //第三步:在自定义事件中用一个参数来接受
                    this.total = value
                }
            }
        })
image.png

在组件中使用v-model

v-model其实就是绑定了input事件,当触发input时候,input事件就会自动接受传递过来的参数,并赋值给已经绑定的total

v­model 其实是一个语法糖,这背后其实做了两个操作

  • v­bind 绑定一个 value 属性
  • v­on 指令给当前元素绑定 input 事件
    要使用v­model,要做到:
  • 接收一个 value 属性。
  • 在有新的 value 时触发 input 事件
<son-component v-model='total'></son-component> //接收一个 value 属性。
      
 Vue.component('son-component', {
            template: '<div>\
                <button @click="handleincrease">+1000</button>\
            </div>',
            data: function () {
                return {
                    count: 1000
                }
            },
            methods: {
                handleincrease: function () {
                    this.count = this.count + 1000
                    this.$emit('input', this.count) //在有新的 value 时触发 input 事件

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

非父组子件之间的通信

子组件之间的通信

<div id="app">
        <my-acomponent ref='a'></my-acomponent>
        <my-bcomponent ref='b'></my-bcomponent>
    </div>
<script>
Vue.component('my-acomponent',{
template:'<div><button @click="handle">点击我向B组件传递数据</b
utton></div>',
data:function () {
return{
aaa:'我是来自A组件的内容'
}
},
methods:{
handle:function () {
this.$root.bus.$emit('lala',this.aaa);
}
}
})
Vue.component('my-bcomponent',{
template:'<div></div>',
created:function () {
//A组件在实例创建的时候就监听事件---lala事件
this.$root.bus.$on('lala',function (value) {
alert(value)
});
}
})

父链:this.$parent

Vue.component('child-component',{
template:'<button @click="setFatherData">通过点击我修改父亲的数
据</button>',
methods:{
setFatherData:function () {
this.$parent.msg = '数据已经修改了'
}
}
})

子链:this.$refs
提供了为子组件提供索引的方法,用特殊的属性ref为其增加一个索引

var app = new Vue({
el:'#app',
data:{
//bus中介
bus:new Vue(),
msg:'数据还未修改',
formchild:'还未拿到'
},
methods:{
getChildData:function () {
//用来拿子组件中的内容 ---- $refs
this.formchild = this.$refs.c.msg;
}
}
})
image.png

插槽

编译的作用域

父组件模板的内容在父组件作用域内编译;
子组件模板的内容在子组件作用域内编译。

 Vue.component('my-component', {
            template: '<div v-show="childshow">我是一个组件</div>',
            data: function () {
                return {
                    message: '我是子组件的内容',
                    childshow: false
                }
            }
        })
        var app = new Vue({
            el: '#app',
            data: {
                message: '我是父组件的内容',
                childshow: true
            }
        })
<div id="app">
        <my-component>
        </my-component>
    </div>

无法得到子组件的内容,因为子组件的childshow为false

插槽的用法

单个插槽的用法

混合父组件的内容和子组件的模板

<div id="app">
        <my-component>
            <p>我是父组件插入的内容</p>
        </my-component>
    </div>


    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
    <script>
        //全局注册组件
        Vue.component('my-component', {
            template: '<div>\
                            <slot>\
                                     如果父组件没有插入内容,我就作为默认出现\
                            </slot>\
                         </div>',
        })
        var app = new Vue({
            el: '#app',
            data: {}
        })
    </script>
image.png

具名插槽的用法

<name-component>
            <h3 slot='header'>我是标题</h3>
            <h3 slot='header'>我是副标题</h3>
            <p>我是正文内容</p>
            <p>正文内容有两段</p>
            <p slot="footer">我是底部信息</p>
        </name-component>
Vue.component('name-component', {
            template: '<div>\
                            <div class="header">\
                                <slot name="header"></slot>\
                            </div>\
                            <div class="container">\
                                <slot></slot>\
                            </div>\
                            <div class="footer">\
                                <slot name="footer"></slot>\
                            </div>\
                        </div>',
        })
image.png

作用域插槽

从子组件中获取数据

<div id="app">
        <my-component>
            <!-- <template slot='abc' slot-scope='prop'>
                {{prop.text}}
                {{prop.ss}}
            </template> -->
            <p slot='abc' slot-scope='prop'>
                {{prop.text}}
                {{prop.ss}}
            </p>
        </my-component>
    </div>


    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
    <script>
        //全局注册组件
        Vue.component('my-component', {
            template: '<div>\
                            <slot text="我是来自子组件的数据" ss="fdk" name="abc">\
                            </slot>\
                         </div>',
        })
        var app = new Vue({
            el: '#app',
            data: {}
        })
    </script>
image.png

访问slot的方式

通过this.$slots.(NAME)

<div id="app">
        <my-component>
            <p>我是父组件插入的内容</p>
        </my-component>
        <hr>
        <name-component>
            <h3 slot='header'>
                <span>
                    我是标题
                </span>
            </h3>
            <h3 slot='header'>我是副标题</h3>
            <p>我是正文内容</p>
            <p>正文内容有两段</p>
            <p slot="footer">我是底部信息</p>
        </name-component>
    </div>


    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
    <script>
        //全局注册组件
        Vue.component('my-component', {
            template: '<div>\
                            <slot>\
                                     如果父组件没有插入内容,我就作为默认出现\
                            </slot>\
                         </div>',
        })
        Vue.component('name-component', {
            template: '<div>\
                            <div class="header">\
                                <slot name="header"></slot>\
                            </div>\
                            <div class="container">\
                                <slot></slot>\
                            </div>\
                            <div class="footer">\
                                <slot name="footer"></slot>\
                            </div>\
                        </div>',
            mounted: function () {
                //访问插槽
                var header = this.$slots.header;
                var text = header[0].elm.innerText;
                var html = header[0].elm.innerHTML;
                console.log(html)
                console.log(text)
                console.log(header)
            }
        })
        var app = new Vue({
            el: '#app',
            data: {}
        })
    </script>
image.png

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

VUE给我们提供 了一个元素叫component
作用是: 用来动态的挂载不同的组件
实现:使用is特性来进行实现的

<div id="app">
        <component :is="thisView">
        </component>
        <button @click="handleView('A')">第一句</button>
        <button @click="handleView('B')">第二句</button>
        <button @click="handleView('C')">第三句</button>
        <button @click="handleView('D')">第四句</button>
    </div>


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

推荐阅读更多精彩内容

  • 本文章是我最近在公司的一场内部分享的内容。我有个习惯就是每次分享都会先将要分享的内容写成文章。所以这个文集也是用来...
    Awey阅读 9,451评论 4 67
  • 组件的作用 作用: 提高代码的可复用性。 组件的使用方法 全局注册: 形式 代码示例: 优点:所有的Vue示例都可...
    学的会的前端阅读 189评论 0 0
  • 什么是组件 组件(Component)是 Vue.js 最强大的功能之一。组件可以扩展 HTML 元素,封装可重用...
    angelwgh阅读 784评论 0 0
  • 组件 (Component) 是 Vue.js 最强大的功能之一。组件可以扩展 HTML 元素,封装可重用的代码。...
    封燐阅读 88评论 0 0
  • 组件系统 组件通讯是vue.js的核心之一,不管是在项目中,还是面试中,都是必考的知识点之一。组件通讯通常涉及到父...
    大脸猫的前端之路阅读 815评论 0 3