父组件通过自定义属性向子组件传值
子组件通过props接收父组件的值
html模板
<div id="app">
<h1>父组件的内容----{{num}}h1>
<p><button @click="change">改变父组件自己的值:{{msg}}button>p>
<hr>
<child :msg="msg" :num="num" :things-list="thingsList">child>
div>
js代码
<script src="./js/vue.js"></script>
<script>
let Child = {
props: ['msg', 'num', 'thingsList'],
template: `
{{msg}}----{{num}}
{{thingsList}}
`
}
//根组件,父组件
var vm = new Vue({
el: '#app',
data: {
msg: 'Happy New Year',
num: [23, 45],
thingsList: ['a']
},
methods: {
change() {
this.msg = '父组件改变的值'
}
},
components: {
Child
}
});
</script>
使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名:
<child :msg="msg" :num="num" :things-list="thingsList">child>
在子组件内部用驼峰的方式接收
props: ['thingsList'],
通常希望每个 prop 都有指定的值类型。
<script>
Vue.component("Child", {
props: {
title: String,
likes: Number,
isPublished: Boolean,
commentIds: Array,
author: Object,
},
template: ``
})
</script>
Vue.component('my-component', {
props: {
// 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
propA: Number,
// 多个可能的类型
propB: [String, Number],
// 必填的字符串
propC: {
type: String,
required: true
},
// 带有默认值的数字
propD: {
type: Number,
default: 100
},
// 带有默认值的对象
propE: {
type: Object,
// 对象或数组默认值必须从一个工厂函数获取
default: function () {
return { message: 'hello' }
}
},
// 自定义验证函数
propF: {
validator: function (value) {
// 这个值必须匹配下列字符串中的一个
return ['success', 'warning', 'danger'].indexOf(value) !== -1
}
}
}
})
子组件传递数据给父组件是通过$emit触发自定义事件来做到的
父组件通过监听子组件发送的自定义事件来接收数据
推荐始终使用 kebab-case (短横线分隔命名)的事件名
<div id="app">
<h1>父组件h1>
<p>子组件传过来的值:{{info}}p>
<hr>
<child @send-data="handleSend">child>
div>
<script src="./js/vue.js">script>
<script>
//子组件
let Child = {
template: `
`,
data() {
return {
msg: '子组件的数据'
}
},
methods: {
handleClick() {
//$emit向父组件发送一个自定义事件
this.$emit("send-data", this.msg)
}
}
}
var vm = new Vue({
el: '#app',
data: {
info: ''
},
methods: {
handleSend(param) {
console.log(param);
this.info = param
}
},
components: {
Child
}
});
script>
在一个组件的根元素上直接监听一个原生事件,需要使用native修饰符
<div id="app">
<child @click.native="show">child>
div>
<script src="./js/vue.js">script>
<script>
// 子组件
let Child = {
template: `
`
}
// 根组件
var vm = new Vue({
el: '#app',
data: {},
methods: {
show() {
alert(1);
}
},
components: {
Child
}
});
script>
真正的双向绑定会带来维护上的问题,因为子组件可以变更父组件,且在父组件和子组件都没有明显的变更来源。
可以使用sync修饰符进行父子组件双向绑定
<div id="app">
<child :msg.sync="msg">child>
<br> {{msg}}
div>
<script src="./js/vue.js">script>
<script>
let Child = {
template: `
`,
methods: {
send() {
// 因为子组件要修改父组件,要在属性前加update
// update:msg为扩展的事件,目的是配合sync使用
this.$emit("update:msg", 'child')
}
}
}
var vm = new Vue({
el: '#app',
data: {
msg: 'parent'
},
methods: {
getData(newVal) {
this.msg = newVal
}
},
components: {
Child
}
});
script>
思路: 把兄弟组件共享的数据定义在父组件,A组件通过子传父的方式,向父组件传值,父组件再通过父向子的方式,向B组件传值
适用场景 : 非父子组件传值
中央事件总线(EventBus 非父子组件间通信)
新建一个Vue事件bus对象,然后通过bus. e m i t 触 发 事 件 , b u s . emit触发事件,bus. emit触发事件,bus.on监听触发的事件
缺点
EventBus通信方式是无法进行有效的组件化开发的,假设一个场景,一个页面上有多个公共组件,我们只要向其中的一个传递数据,但是每个公共组件都绑定了数据接收的方法。
css样式
html代码
<div id="app">
<brother1>brother1>
<brother2>brother2>
div>
<script src="./js/vue.js">script>
<script>
Vue.component('brother1', {
data() {
return {
mymessage: 'hello brother1'
}
},
template: `
this is brother1 component!
`,
methods: {
passData(val) {
//触发全局事件globalEvent
bus.$emit('globalEvent', val)
}
}
})
Vue.component('brother2', {
template: `
this is brother2 component!
brother1传递过来的数据:{{brothermessage}}
`,
data() {
return {
mymessage: 'hello brother2',
brothermessage: ''
}
},
mounted() {
//绑定全局事件globalEvent
bus.$on('globalEvent', (val) => {
this.brothermessage = val;
})
}
})
//中央事件总线
var bus = new Vue();
var app = new Vue({
el: '#app'
})
script>
vuex算是vue中处理复杂组件通信的最佳方案,毕竟vue和vuex一个娘胎里出来的。而且vuex底层也是用vue实现的。后续会有专门的文章来讲解Vuex
attrs和attrs和listeners 主要用于孙组件获取父组件的属性和方法
$attrs
是在vue的2.40版本以上添加的$attrs
prop
和$emit
,$on
会很繁琐vuex
会大材小用,只是在这几个组件中使用,没必要使用vuex
eventBus
,使用不恰当的话,有可能会出现事件多次执行<div id="app">
<!-- 使用子组件 -->
<child :msg="msg" :num="num"></child>
</div>
<script src="./js/vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
msg: '祖先的数据',
num: 10
},
components: {
// 注册子组件
Child
},
methods: {}
});
</script>
父组件(Father.vue),给子组件关联数据,子组件如果不用props接收,那么这些数据就作为普通的HTML特性应用在子组件的根元素上
v-bind="$attrs"
// 子组件
let Child = {
// 注册孙组件
components: {
Grandson
},
// 使用孙组件
template: `
`,
inheritAttrs: true,
}
inheritAttrs: false
的含义是不希望本组件的根元素继承父组件的attribute,同时父组件传过来的属性(没有被子组件的props接收的属性),也不会显示在子组件的dom元素上,
// 孙组件
let Grandson = {
template: `
`,
created() {
console.log(this.$attrs);
}
}
l i s t e n e r s − − 属 性 , 它 是 一 个 对 象 , 里 面 包 含 了 作 用 在 这 个 组 件 上 的 所 有 监 听 器 , 你 就 可 以 配 合 ‘ v − o n = " listeners--属性,它是一个对象,里面包含了作用在这个组件上的所有监听器,你就可以配合 `v-on=" listeners−−属性,它是一个对象,里面包含了作用在这个组件上的所有监听器,你就可以配合‘v−on="listeners"` 将所有的事件监听器指向这个组件的某个特定的子元素。
<div id="app">
根组件A <br>
<com-b :msg-a1="msgA1" :msg-a2="msgA2" @test1="onTest1" @test2="onTest2">com-b>
div>
<script src="./js/vue.js">script>
<script>
let ComC = {
template: `
孙组件ComC
`,
mounted() {
// 孙组件直接获取A组件的属性值
console.log('c', this.$attrs);
// A组件可以直接监听到C组件触发的事件
// console.log('c', this.$listeners);
this.$emit("test1", 'C的数据')
}
}
let ComB = {
// C组件中能直接触发test的原因在于 B组件调用C组件时 使用 v-on 绑定了$listeners 属性
// 通过v-bind 绑定$attrs属性,C组件可以直接获取到A组件中传递下来的props(除了B组件中props声明的)
template: `
子组件ComB
`,
components: {
ComC
}
}
// 根组件A
var vm = new Vue({
name: "A",
el: '#app',
data: {
// 根组件A的数据
msgA1: 'msgA1',
msgA2: 'msgA2'
},
methods: {
// 根组件A的事件处理程序
onTest1(data) {
console.log('onTest1', data);
},
onTest2() {
console.log('onTest2');
}
},
components: {
ComB
}
});
script>
父组件中通过provider来提供变量,然后在子组件中通过inject来注入变量。
不论子组件有多深,只要调用了inject那么就可以注入provider中的数据。
<div id="app">
<parent>parent>
div>
<script src="./js/vue.js">script>
<script>
Vue.component('child', {
inject: ['for'], //得到父组件传递过来的数据
data() {
return {
mymessage: this.for
}
},
template: `
`
})
Vue.component('parent', {
template: `
this is parent compoent!
`,
// 向后代传递数据
provide: {
for: '父组件的test'
},
data() {
return {
message: 'hello'
}
}
})
var vm = new Vue({
el: '#app',
})
script>
parent和children 用于有直接父子关系 的 组件 之间的通信
$refs 获取子组件的实例
$parent 用于 获取组件的父组件实例
$children 用于 获取组件的子组件实例
Document
父组件----{{msg}}---------------