vue组件详解
使用组件的原因
提高代码的复用性
组件的使用方法
- 全局注册
Vue.component('my-component',{
template:'<div>我是组件的内容</div>'
})
占内存
- 局部注册
var app = new Vue({
el: '#app',
data: {
},
components: {
'app-component': {
template: '<div>app-component</div>'
}
}
})
- 由于HTML标签的限制,如
table
中只能有tr
、td
、tbody
,要想在table
中使用组件就要用到is
,这样一来,tbody
在文档中就会被组件替换掉
<table>
<tbody is="app-component"></tbody>
</table>
我们也可以利用这个属性来干掉其他事,比如动态组件,也就是点一个按钮就切换一个组件
组件使用的奇淫巧技
- 必须用连字符命名,如:my-compoent
- template中的内容必须要用DOM元素包裹
// 正确写法
template: '<div>app-component</div>'
// 错误写法
template: 'app-component'
- 组件的定义中,还可以有data,methods,computed
- data必须是一个方法
components: {
'plus-component': {
template: '<button @click="count111++">{{count111}}</button>',
data: function () {
return {
count111: 1
}
}
}
}
使用props传递数据 父亲向儿子传递数据
- 在子组件上声明属性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>
- 动态向子组件传入信息,则用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>
- ==在props中定义的属性都可以在组件内使用,可以在template、computed、methods中使用==
单向数据流
只能父组件向子组件传递信息,子组件不能向父组件传递信息
在组件中使用从props传来的数据可以直接用this.xxx来获取
'child-component': {
props: ['msg'],
template: '<div id="child" >{{count}}</div>',
data:function(){
// 组件中的data要返回一个函数
return {
count: this.msg
}
}
}
- 用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中使用驼峰==
- 对传入的数据进行数据验证
注意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>
子组件向父组件传递信息的步骤:
- 在父组件里写入自定义事件名,事件名后面跟着的是要执行的方法
- 子组件通过$emit向父组件传递信息,第一个参数是自定义的事件名,后面是要传递的参数,可以接无限个
在组件中使用v-model
v-model其实也是一个语法糖:
- 它等于先用v-bind绑定一个值
- 监听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
})
}
})
- 修改父组件的数据,父链
this.$parent.xxxx
- 修改儿子组件的数据,子链
在每个儿子组件上加上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'
}
}
})