组件的作用
作用: 提高代码的可复用性。
组件的使用方法
- 全局注册:
- 形式
Vue.component(tag,{
template:'<div></div>'
})
- 代码示例:
<div id = "app">
<!--可以把它当做HTML标签使用-->
<my-component></my-component>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script>
//注册组件名为my-component的组件
Vue.component('my-component',{
template:'<div>我是一个组件</div>'
});
var app = new Vue({
el: "#app",
data: {}
})
</script>
- 优点:所有的Vue示例都可以使用。
- 缺点:权限太大,容错率降低。
- 局部注册:
- 代码示例:
<script>
var app = new Vue({
el: '#app',
components: {
'app-component':{
template:'<div>我是一个局部组件</div>'
}
}
})
</script>
- html标签限制
vue组件的模板在某些情况下会受到html标签的限制,比如 <table> 中只能有 <tr> , <td> 这些元素,所以直接在table中使用组件是无效的,此时可以使用is属性来挂载组件。
- 代码示例:
<div id = "app">
<table>
<!--<my-component></my-component>-->
<tbody is = "my-component"></tbody>
</table>
</div>
组件是使用技巧----奇淫技巧
-
推荐使用小写字母加-进行命名(必须) 。child, my-componnet命名组件
-
template中的内容必须被一个DOM元素包括 ,也可以嵌套
-
在组件的定义中,除了template之外,还可以使用其他选项:
data,computed,methods
-
在组件中使用data必须是一个方法。
-
代码示例:解决点击一个按钮,两个都进行加的操作
<body>
<div id = "app">
<button @click = "plus">点击加{{count}}</button>
<button @click = "plus">点击加{{count}}</button>
<hr/>
<btn-component></btn-component>
<btn-component></btn-component>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script>
var app = new Vue({
el: "#app",
data: {
count: 1
},
components: {
'btn-component':{
template:'<button @click = "count++">{{count}}</button>',
data: function(){
return {
//每次返回的count是不一样的
count: 0
}
}
}
},
methods: {
plus:function(){
this.count ++;
}
}
})
</script>
</body>
使用props传递数据 (父亲向儿子传递数据)
-
在组件中使用props来从父亲组件接收参数,注意,在props中定义的属性,都可以在组件中直接使用
-
propps的数据来自父级,而组件中data 中return的数据就是组件自己的数据,两种情况作用域就是组件本身,因此这两种属性都可以在template,computed,methods中直接使用。
-
props的值有两种,一种是字符串数组,一种是对象
-
可以使用
v-bind
动态绑定父组件来的内容 -
代码示例:
<body>
在父组件向子组件传递消息:
<hr/>
<div id = "app" style = "border: 2px solid chartreuse;height: 360px;">
<h5 style = "text-align:center">我是父组件</h5>
<!--child-component就是app这个父组件的子组件,msg就是父组件传递给子组件的信息-->
确定要传递的数据:(使用v-bind与否的区别)
不使用:msg的时候,msg默认传入的是字符串
<child-component msg = "[3,6,8]"></child-component> //msg.length = 7
加了:msg,对传入的数据会进行自动识别
<child-component :msg = "[3,6,8]"></child-component> //msg.length = 3
<hr/>
v-bind动态的传递数据---把input中parentmsg传递给子组件:
<input type = "text" v-model = "parentmsg">
<bind-component :msg = "parentmsg"></bind-component>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script>
var app = new Vue({
el: '#app',
data: {
parentmsg: '今天月亮真圆'
},
components: {
'child-component':{
props:['msg'],
template: '<div style = "border: 1px solid red;height: 80px">{{msg.length}}</div>'
},
'bind-component':{
props:['msg'],
template: '<div style = "border: 1px solid red;height: 80px">{{msg}}</div>'
}
}
})
</script>
</body>
单向数据流
-
单向数据流含义 : 通过
props
传递数据是单向的了, 也就是父组件数据变化时会传递给子组件,但是反过来不行。 -
目的 :是尽可能将父子组件解稿,避免子组件无意中修改了父组件的状态。
-
应用场景: 业务中会经常遇到两种需要改变 props 的情况
- 一种是父组件传递初始值进来,子组件将它作为初始值保存起来,在自己的作用域下可以随意使用和修改。这种情况可以在组件 data 内再声明一个数据,引用父组件的 prop
步骤一:注册组件
步骤二:将父组件的数据传递进来,并在子组件中用props接收
步骤三:将传递进来的数据通过初始值保存起来
<body>
<div id = "app">
<my-component msg = "我是父组件传递的数据"></my-component>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script>
//注册组件
Vue.component('my-component',{
props: ['msg'],
template: '<div>{{count}}</div>',
//将传递进来的数据通过初始值保存起来
data: function(){
return {
//props中的值可以通过this.xxx直接来进行获取
count:this.msg
//无论msg如何变化,都会传递给count,只需要维护count即可
}
}
});
var app = new Vue({
el: '#app'
})
</script>
</body>
- 另一种情况就是 prop 作为需要被转变的原始值传入。这种情况用计算属性就可以了
步骤一:注册组件
步骤二:将父组件的数据传递进来,并在子组件中用props接收
步骤三:将传递进来的数据通过计算属性进行重新计算
<body>
<div id = "app">
<!--需求:通过input中输入的书直接改变div的宽度-->
传递的width仅仅是一个数据
<input type = "text" v-model = "width">
<width-component :width = "width"></width-component>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script>
Vue.component('width-component',{
props: ['width'],
template: '<div :style = "style"></div>',
//将传递进来的数据通过计算属性重新计算
computed: {
style: function(){
//return的值就是style
return {
width:this.width + 'px',
background: 'red',
height: '30px'
}
}
}
});
var app = new Vue({
el: '#app',
data: {
width: 0
}
})
</script>
</body>
数据验证
vue组件中camelCased (驼峰式) 命名与 kebabcase(短横线命名)
-
在html中,
myMessage
和mymessage
是一致的,在组件中的html中使用必须使用kebab-case
(短横线)命名方式。在html中不允许使用驼峰!!! -
在组件中, 父组件给子组件传递数据必须用短横线。在
template
中,必须使用驼峰命名方式,若为短横线的命名方式。则会直接保错。 -
在组件的data中,用this.XXX引用时,只能是驼峰命名方式。若为短横线的命名方式,则会报错。
验证的 type 类型可以是:
• String
• Number
• Boolean
• Object
• Array
• Function
- 相关代码示例:
Vue.component('type-component',{
//props中堆数据进行限定,必须使用对象
props: {
//限定a传入的数据必须是string
a: String,
//限定b传入的数据可以是string也可以是Number
b: [String,Number],
//c是布尔类型,默认值是true。既要指定默认值又要指定类型,就使用对象type--required--default
c:{
type:Boolean,
default: true
},
//穿入的是数字,而且是必须传入
d: {
type: Number,
required: true
},
//如果是数组或对象,默认值必须是一个函数来返回
e: {
type: Array,
default: function(){
//如果不向子组件传递e,默认取值333
return [333];
}
},
//自定义一个验证函数
f: {
validator: function(value){
//value就是传入的值88,<10就会报错
return value > 10
}
},
//定义一个function
g: {
type:Function
}
},
template:'<div>{{a}}---{{b}}--{{c}}--{{d}}--{{e}}--{{f}--{{g}}</div>'
});
组件通信
组件关系可分为父子组件通信、兄弟组件通信、跨级组件通信
自定义事件—子组件给父组件传递数据
使用v-on
除了监昕 DOM 事件外,还可以用于组件之间的自定义事件。
-
JavaScript 的设计模式 一一观察者模式,
dispatchEvent
和addEventListener
这两个方法。 Vue 组件也有与之类似的一套模式,子组件用$emit()
来 触发事件 ,父组件用
$on()
来 监昕子组件的事件 。 -
代码示例:
第一步:自定义事件
第二步: 在子组件中用$emit
触发事件,第一个参数是事件名,后边的参数是要传递的数据
第三步:在自定义事件中用一个参数来接受
<div id = "app">
您现在的银行卡余额是:{{total}}
//在父组件的作用域中定义一个自定义事件
<son-component @change = "handleTotal"></son-component>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script>
//需求,通过加号按钮和减号按钮来给父组件传递数据
//子组件的count值随时随地的赋值给父组件的total
Vue.component('son-component',{
template: '<div> <button @click = "handleincrease">+1000</button>\<button @click = "handlereduce">-1000</button></div>',
data: function(){
return {
count:1000
}
},
methods: {
handleincrease:function(){
this.count = this.count + 1000;
//触发自定义事件
this.$emit('change',this.count)
},
handlereduce:function(){
this.count = this.count - 1000;
this.$emit('change',this.count)
}
}
});
var app = new Vue({
el: '#app',
data: {
total: 2000
},
//自定义事件要接受一个参数
methods: {
handleTotal: function(value){
//此处的形参value就是传递过来的数据,就是this.count
this.total = value;
}
}
})
</script>
在组件中使用v-model
-
$emit
的代码,这行代码实际上会触发一个input
事件,input
后的参数就是传递给vmodel
绑定的属性的值.
-
v-model
其实是一个语法糖,这背后其实做了两个操作-
v-bind
绑定一个value
属性 -
v-on
指令给当前元素绑定input
事件
-
-
要使用
v-model
,要做到:- 接收一个
value
属性。 - 在有新的
value
时触发input
事件
- 接收一个
-
具体代码示例
<div id = "app">
您现在的银行卡余额是:{{total}}
<son-component v-model = "total"></son-component>
v-model其实就是绑定了input事件,当触发input时候,input事件就会自动接收传递过来的参数,并赋值给已经绑定的total。
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script>
//需求,通过加号按钮和减号按钮来给父组件传递数据
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)
}
}
});
var app = new Vue({
el: '#app',
data: {
total: 2000
}
})
</script>
非父组件之间的通信
image.png- 代码示例:
<body>
<div id = "app">
<my-acomponent></my-acomponent>
<my-bcomponent></my-bcomponent>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script>
Vue.component('my-acomponent',{
template: '<div><button @click = "handle">点击我向b组件传递数据</button></div>',
data: function(){
return {
aaa: '我是来自a组件的内容'
}
},
methods: {
handle: function(){
//在子组件中访问根组件this.$root
this.$root.bus.$emit('lala',this.aaa);
}
}
});
Vue.component('my-bcomponent',{
template: '<div></div>',
created: function(){
//a组件在实例创建的时候就监听事件---lala事件
//function中的参数传多少就有多少
this.$root.bus.$on('lala',function(value){
alert(value)
})
}
});
var app = new Vue({
el: '#app',
data: {
//bus中介
bus: new Vue()
}
})
</script>
</body>
- 父链:this.$parent
Vue.component('child-component',{
template: '<button @click = "setFatherData">通过点击我修改父亲的数据</button>',
methods: {
setFatherData:function(){
this.$parent.msg = '数据已经修改了'
}
}
});
- 子链:this.$refs
提供了为子组件提供索引的方法,用特殊的属性ref为其增加一个索引
<my-acomponent ref = "a"></my-acomponent>
<my-bcomponent ref = "b"></my-bcomponent>
<child-component ref = "c"></child-component>
var app = new Vue({
el: '#app',
data: {
//bus中介
bus: new Vue(),
msg: '数据还未修改',
formchild: '还未拿到'
},
methods: {
getChildData: function(){
//用来拿子组件中的内容
this.formchild = this.$refs.a.msg;
}
}
})
什么是slot(插槽)
为了让组件可以组合,我们需要一种方式来混合父组件的内容与子组件自己的模板。这个过程被称为内容分发。Vue.js 实现了一个内容分发 API,使用特殊的 slot
元素作为原始内容的插槽。
编译的作用域
在深入内容分发 API 之前,我们先明确内容在哪个作用域里编译。假定模板为:\
<child-component>
{{ message }}
</child-component>
message 应该绑定到父组件的数据,还是绑定到子组件的数据?答案是父组件。
组件作用域简单地说是:
-
父组件模板的内容在父组件作用域内编译;
<div id = "app"></div>
内的是父组件的作用域。 -
子组件模板的内容在子组件作用域内编译,
template
中的是子组件的作用域。
插槽的用法
父组件的内容与子组件相混合,从而弥补了视图的不足
混合父组件的内容与子组件自己的模板
- 单个插槽:
<body>
<div id = "app">
<my-component>
//在子组件中渲染出父组件的内容,sort中的内容会替换成p中的内容
<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'
})
</script>
</body>
- 具名插槽:
根据不同的视图和div使用不同的插槽
<body>
<div id = "app">
<name-component>
<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('name-component',{
template: '<div> <div class = "header">\n' +
'\t<slot name = "header">\n' +
'\t\t\n' +
'\t</slot>\n' +
'</div>\n' +
'<div class = "container">\n' +
'\t<slot>\n' +
'\t\t\n' +
'\t</slot>\n' +
'</div>\n' +
'<div class = "footer">\n' +
'\t<slot name = "footer">\n' +
'\t\t\n' +
'\t</slot>\n' +
'</div>\n</div>'
});
var app = new Vue({
el: '#app'
})
</script>
</body>
作用域插槽
主要作用: 从子组件中获取数据。
作用域插槽是一种特殊的slot,使用一个可以复用的模板来替换已经渲染的元素
-------slot-scope
====template模板是不会被渲染的。sort替换的是template里面的内容。
——从子组件获取数据
<body>
<div id = "app">
<my-component>
<!--一定要在template模板中使用,prop是临时变量名,任意取-->
<template slot = "abc" slot-scope = "prop">
{{prop.text}}
{{prop.name}} //拿不到,只能拿除了name以外的数据
{{prop.ss}} //可以拿到
</template>
</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 = "nihao" name = "abc"></slot></div>'
});
var app = new Vue({
el: '#app'
})
</script>
</body>
------vue2.5.0之后的写法:
<p slot = "abc" slot-scope = "prop">
{{prop.text}}
{{prop.ss}}
</P>
访问slot
通过this.$slots.(NAME)
Vue.component('my-component',{
template:'<div></div>',
mounted:function(){
//访问插槽,拿到的是虚拟节点VNde
var header = this.$slots.header;
var text = header[0].elm.innerText;
var html = header[0].elm.innerHTML;
console.log(header);
console.log(text);
console.log(html)
}
})
组件高级用法---动态组件
-
VUE给我们提供 了一个元素叫
component
-
作用: 用来动态的挂载不同的组件
-
实现:使用is特性来进行实现的
-
具体代码实现:
<body>
<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>
</body>
网友评论