Vue组件相关

Posted by Zxd on March 19, 2018

父子组件间的数据传递

  • 父组件通过属性的形式(v-bind:[xxx])向子组件传递数据
  • 子组件通过事件触发的形式向父组件传值
  • 单项数据流:父组件可以向子组件传递任意数据,但是子组件不能修改传递过来的数据(clone一份使用),因为如果是引用类型的数据,更改会影响其他子组件的使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
    <div id="root">
<!-- 父组件通过属性的形式向子组件传递数据 :count加了冒号是数字01(加了冒号后面是js表达式) 不加是字符串-->
<counter :count="123" @inc="handleIncrease"></counter>
<counter :count="1" @inc="handleIncrease"></counter>
<div>{{total}}</div>
</div>
```
```js
let counter = {
props: ['count'],
data: function () {
return {
// 传递过来的参数不能直接修改,而是要clone一份自己使用
number: this.count
}
},
template: '<div @click="handleClick">{{number}}</div>',
methods: {

handleClick() {
// 单项数据流,子组件不能改父组件传来的数据,因为引用类型的数据,改了会影响其他子组件
// this.count++
this.number++
// 子组件通过事件触发的形式向组件传值
this.$emit('inc', 1)
// 单向数据流, 父组件可以向子组件传递任意数据,但是子组件不能修改子组件传递来的数据
}
}
}
let vm = new Vue({
el: '#root',
data: {
total: 124
},
components: {
counter: counter
},
methods: {
handleIncrease(step) {
this.total += step
}
}
})

组件参数校验与非props特性

props特性

  1. 父组件传递的属性不会再dom上显示
  2. 子组件可以接收和直接在组件中使用传递过来的属性(如本例中content)

非props特性

子组件内部并没有声明父组件要传递的属性

  1. 非props,属性会展示在最外层dom标签中
  2. 在子组件中没办法获取到内容,因为没有声明

参数校验
props传递的参数,在子组件内部可以用对象的形式对参数进行校验

1
2
3
4
<div id="root">
<!-- <child content="hello world"></child> -->
<child content="haha"></child>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
Vue.component('child', {
// props:['content'],
// 用对象的形式对参数进行类型校验
props: {
// 传递数字或字符串
// content: [Number, String]
content: {
type: String,
required: false,
default: 'default value',
// 自定义校验器
validator: function (value) {
return (value.length > 5)
}
}
},
template: '<div>{{content}}</div>'
})

let vm = new Vue({
el: '#root',
data: {

},
methods: {

}
})

给组件绑定原生事件

  • `@click.native`
  • 在父组件中,写在组件标签上的事件是监听子组件的自定义事件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<div id="root">
<!-- 父组件中给子组件绑定事件,写在组件标签上的事件是监听自定义事件 -->
<!-- 上面一个click是内部组件向外触发的click -->
<child @click="handleClick"></child>
<!-- 下面这个是监听组件上的原生click事件 -->
<child @click.native="handleClick"></child>
</div>
<script>
Vue.component('child', {
template: '<div @click="handleChildClick">Child</div>',
methods: {
handleChildClick() {
// 逻辑是点击触发子组件内部click原生事件,子组件执行,并向外发出了一个click自定义事件,
// 父组件触发监听的click,(这个click可以自定义 childClick等)
// alert('child click')
this.$emit('click')
}
}
})

let vm = new Vue({
el: "#root",
data: {

},
methods: {
handleClick() {
alert('click')
}
}
})
</script>

非父子组件间的传值

  1. Vuex解决
  2. 总线机制(Bus/总线/发布订阅模式/观察者模式)
1
2
3
4
<div id="root">
<child content="Dell"></child>
<child content="Lee"></child>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// 1 vuex 解决
// 2 发布订阅模式 总线机制 (Bus/总线/发布订阅模式/观察者模式)
Vue.prototype.bus = new Vue()

Vue.component('child', {
data: function () {
return {
selfContent: this.content
}
},
props: {
content: String
},
template: '<div @click="handleClick">{{selfContent}}</div>',
methods: {
handleClick() {
this.bus.$emit('change', this.selfContent)
}
},
mounted: function () {
// this.bus.$on('change', (msg) => {
// this.content = msg
// })
// 或者
let _this = this
this.bus.$on('change', function (msg) {
_this.selfContent = msg
})
}
})

let vm = new Vue({
el: '#root',
methods: {

}
})

在vue中使用插槽slot

slot基本用法

  • 当子组件中有一部分内容需要根据父组件的dom传递过来的时候
  • 父组件向子组件优雅的传递dom–slot
  • slot可以设置默认内容
1
2
3
4
5
6
<div id="root">
<child>
<!-- p标签对应slot区域,替换 -->
<p>Dell</p>
</child>
</div>
1
2
3
4
5
6
7
8
9
10
Vue.component('child', {
template: `<div>
<p>hello</p>
<slot>默认内容</slot>
</div>`
})

let vm = new Vue({
el: '#root'
})

具名插槽slot

  • 子组件中<slot name=header>
  • 父组件中<div class="header" slot="header">header</div>,
1
2
3
4
5
6
7
<div id="root">
<body-content>
<!-- 具名插槽,父组件slot="header",这里的div可以任意换 -->
<div class="header" slot="header">header</div>
<div class="footer" slot="footer">footer</div>
</body-content>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
Vue.component('body-content', {
template: ` <div>
<slot name="header">
<h1>default header</h1>
</slot>
<div class="content">content</div>
<slot name="footer"></slot>
</div>`
})

let vm = new Vue({
el: '#root'
})

作用域插槽

  • 当子组件作循环的某一部分应该由外部传递过来
1
2
3
4
5
6
7
8
9
10
11
<div id="root">
<!-- 父组件调用子组件child的时候,给子组件传递了一个slot-scope插槽 -->
<!-- 必须是template开头和结尾 -->
<!-- 当子组件作循环的某一部分应该由外部传递进来的时候,用作用域插槽-->
<!-- 子组件可以往插槽里面传数据 -->
<child>
<template slot-scope="props">
<li>{{props.item}}</li>
</template>
</child>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Vue.component('child', {
data: function () {
return {
list: [1,2,3,4]
}
},
template: `<div>
<ul>
<slot v-for="item of list" :item=item>
</slot>
</ul>
</div>`
})

let vm = new Vue({
el: '#root',
})

Vue中的动态组件与v-once指令

  • <component :is="type"></component>
  • :is标记使用哪个组件
  • v-once表示缓存组件,不用每次销毁重建
1
2
3
4
5
6
7
<div id="root">
<!-- <child-one v-if="type==='child-one'"></child-one>
<child-two v-if="type==='child-two'"></child-two> -->
<!-- 动态组件component -->
<component :is="type"></component>
<button @click="handleBtnClick">change</button>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Vue.component('child-one', {
// 通过v-once提高展示性能,缓存这个组件,不用每次销毁重建
template: '<div v-once>child-one</div>'
})

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

let vm = new Vue({
el: '#root',
data: {
type: 'child-one'
},
methods: {
handleBtnClick() {
this.type = (this.type === 'child-one' ? 'child-two' : 'child-one')
}
}
})