6.1 Prop
学习:状态选项props 以及 实例属性 $attrs
一个组件需要显式声明它所接受的 props,这样 Vue 才能知道外部传入的哪些是 props,哪些是透传 attribute
props 需要使用 props 选项来定义:
{
props: ['foo'],
created() {
// props 会暴露到 `this` 上
console.log(this.foo)
}
}
除了使用字符串数组来声明 prop 外,还可以使用对象的形式:
{
props: {
title: String,
likes: Number
}
}
对于以对象形式声明中的每个属性,key 是 prop 的名称,而值则是该 prop 预期类型的构造函数。比如,如果要求一个 prop 的值是 number
类型,则可使用 Number
构造函数作为其声明的值。
如果一个 prop 的名字很长,应使用 camelCase 形式,因为它们是合法的 JavaScript 标识符,可以直接在模板的表达式中使用,也可以避免在作为属性 key 名时必须加上引号。
虽然理论上你也可以在向子组件传递 props 时使用 camelCase 形式 (使用 DOM 模板时例外),但实际上为了和 HTML attribute 对齐,我们通常会将其写为 kebab-case 形式
<my-com :likeNum="100"></my-com>
===><my-com :like-num="100"></my-com>
对于组件名我们推荐使用 PascalCase,因为这提高了模板的可读性,能帮助我们区分 Vue 组件和原生 HTML 元素。然而对于传递 props 来说,使用 camelCase 并没有太多优势,因此我们推荐更贴近 HTML 的书写风格-短横线。
所有的 props 都遵循着单向绑定原则(
单项数据流
),props 因父组件的更新而变化,自然地将新的状态向下流往子组件,而不会逆向传递。这避免了子组件意外修改父组件的状态的情况,不然应用的数据流将很容易变得混乱而难以理解。另外,每次父组件更新后,所有的子组件中的 props 都会被更新到最新值,这意味着你不应该在子组件中去更改一个 prop。
6.1.1 父组件给子组件传值1
完整案例:29_parent_child_component_value1.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>29_父组件给子组件传值 方式1 - 数组</title>
</head>
<body>
<div id="app">
<my-parent></my-parent>
</div>
</body>
<template id="parent">
<div>
我是父组件
<!-- 父组件调用子组件的地方,添加自定义的属性,如果属性的值是变量,boolean类型,number类型,对象,数组,null,undefined,需要使用绑定属性 -->
<my-child :msg="msg" :flag="true" :num="100" :obj="{a: 1, b: 2}" :arr="['a', 'b', 'c']"></my-child>
</div>
</template>
<template id="child">
<div>
我是子组件 - {{ msg }} - {{ flag }} - {{ num }} - {{ obj }} - {{ arr }}
</div>
</template>
<script src="lib/vue.global.js"></script>
<script>
// 在定义子组件的地方,添加props选项,
// props的数据类型为数组,数组的元素即为 自定义的属性名, 之后就可以直接在子组件中通过 自定义的属性名 使用 传递的值
const Child = {
props: ['msg', 'flag', 'num', 'obj', 'arr'],
template: '#child'
}
const Parent = {
template: '#parent',
components: {
MyChild: Child
},
data () {
return {
msg: 'hello parent'
}
}
}
const { createApp } = Vue
const app = createApp({
components: {
MyParent: Parent
}
})
app.mount('#app')
</script>
</html>
虽然上述案例已经完成了父组件给子组件传值,但是不够严谨
可能A负责父组件的编写,B负责了子组件的编写,容易造成 不知道 自定义的属性名的 数据类型
6.1.2 父组件给子组件传值2
完整案例:30_parent_child_component_value2.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>30_父组件给子组件传值 方式2 - 对象-数据类型</title>
</head>
<body>
<div id="app">
<my-parent></my-parent>
</div>
</body>
<template id="parent">
<div>
我是父组件
<!-- 父组件调用子组件的地方,添加自定义的属性,如果属性的值是变量,boolean类型,number类型,对象,数组,null,undefined,需要使用绑定属性 -->
<my-child :msg="msg" :flag="true" :num="100" :obj="{a: 1, b: 2}" :arr="['a', 'b', 'c']"></my-child>
</div>
</template>
<template id="child">
<div>
我是子组件 - {{ msg }} - {{ flag }} - {{ num }} - {{ obj }} - {{ arr }}
</div>
</template>
<script src="lib/vue.global.js"></script>
<script>
// 在定义子组件的地方,添加props选项,
// 方式1:props的数据类型为数组,数组的元素即为 自定义的属性名, 之后就可以直接在子组件中通过 自定义的属性名 使用 传递的值
// 方式2:props的数据类型为对象,对象的key值为自定义的属性名,value值为数据类型(不会阻碍程序运行,警告代码不严谨)
// 如果一个自定义的属性的值既可以是 String 类型,也可以是 Number类型 key: String | Number
const Child = {
// props: ['msg', 'flag', 'num', 'obj', 'arr'],
props: {
msg: String,
flag: Boolean,
num: String | Number,
obj: Object,
arr: Array
},
template: '#child'
}
const Parent = {
template: '#parent',
components: {
MyChild: Child
},
data () {
return {
msg: 'hello parent'
}
}
}
const { createApp } = Vue
const app = createApp({
components: {
MyParent: Parent
}
})
app.mount('#app')
</script>
</html>
现在只能知道哪一个属性是哪一种数据类型,但是有时候我们可以不需要设置 自定义的属性(. )
<input />
<===><input type="text" />
6.1.3 父组件给子组件传值3
完整案例: 31_parent_child_component_value3.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>31_父组件给子组件传值 方式3 - 对象-数据类型-默认值</title>
</head>
<body>
<div id="app">
<my-parent></my-parent>
</div>
</body>
<template id="parent">
<div>
我是父组件
<!-- 父组件调用子组件的地方,添加自定义的属性,如果属性的值是变量,boolean类型,number类型,对象,数组,null,undefined,需要使用绑定属性 -->
<my-child :msg="msg" :flag="true" :num="100" :obj="{a: 1, b: 2}" :arr="['a', 'b', 'c']"></my-child>
<my-child :flag="true" :num="2000" :arr="['aa']"></my-child>
</div>
</template>
<template id="child">
<div>
我是子组件 - {{ msg }} - {{ flag }} - {{ num }} - {{ obj }} - {{ arr }}
</div>
</template>
<script src="lib/vue.global.js"></script>
<script>
// 在定义子组件的地方,添加props选项,
// 方式1:props的数据类型为数组,数组的元素即为 自定义的属性名, 之后就可以直接在子组件中通过 自定义的属性名 使用 传递的值
// 方式2:props的数据类型为对象,对象的key值为自定义的属性名,value值为数据类型(不会阻碍程序运行,警告代码不严谨)
// 如果一个自定义的属性的值既可以是 String 类型,也可以是 Number类型 key: String | Number
// 方式3: props的数据类型为对象,对象的key值为自定义的属性名,value值为 一个新的对象
// 对象的key值可以为 type, 那么value值就为 数据类型
// 对象的key值可以为 required, 那么value值就为 true / false --- 该属性是不是必须传递,即使有默认值如果未传递还是要报警告
// 对象的key值可以为 default, 那么value值就为 默认值 -- 如果属性的值是对象和数组,那么默认值设置为函数返回即可
// 对象的key值可以为 validator 函数 ,可以自定义验证规则,但是不能验证多属性
const Child = {
// props: ['msg', 'flag', 'num', 'obj', 'arr'],
// props: {
// msg: String,
// flag: Boolean,
// num: String | Number,
// obj: Object,
// arr: Array
// },
props: {
msg: {
type: String,
required: true,
default: 'hello vue',
validator (val) {
return val.length > 20
}
},
flag: Boolean,
num: {
// type: Number | String,
type: Number,
default: 3000,
validator (val) { // 猜测:不能验证多类型数据 Right-hand side of 'instanceof' is not an object
return val > 2000
}
},
obj: {
type: Object,
default () { return { a: 3, b: 4}}
},
arr: {
type: Array,
default () { return ['1', '2', '3', '4']},
validator (val) {
return val.length > 2
}
}
},
template: '#child'
}
const Parent = {
template: '#parent',
components: {
MyChild: Child
},
data () {
return {
msg: 'hello parent'
}
}
}
const { createApp } = Vue
const app = createApp({
components: {
MyParent: Parent
}
})
app.mount('#app')
</script>
</html>
6.1.4 $attrs
一个包含了组件所有透传 attributes 的对象。
透传 Attributes 是指由父组件传入,且没有被子组件声明为 props 或是组件自定义事件的 attributes 和事件处理函数。
<my-button class="btn"></my-button>
子组件模板只有button,那么my-button的class直接透传给子组件
<button class="btn"></button>
默认情况下,若是单一根节点组件,$attrs
中的所有属性都是直接自动继承自组件的根元素。而多根节点组件则不会如此,同时你也可以通过配置 inheritAttrs 选项来显式地关闭该行为。
6.2 监听事件
学习:状态选项emits、实例方法 $emit
6.2.1 子组件给父组件传值- $emit
在组件的模板表达式中,可以直接使用 $emit
方法触发自定义事件
$emit()
方法在组件实例上也同样以 this.$emit()
的形式可用
父组件可以通过 v-on
(缩写为 @
) 来监听事件
同样,组件的事件监听器也支持 .once
修饰符
像组件与 prop 一样,事件的名字也提供了自动的格式转换。注意这里我们触发了一个以 camelCase 形式命名的事件,但在父组件中可以使用 kebab-case 形式来监听。与 prop 大小写格式一样,在模板中我们也推荐使用 kebab-case 形式来编写监听器。
完整案例:32_child_parent_component_value1.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>32_子组件给父组件传值</title>
</head>
<body>
<div id="app">
<my-parent></my-parent>
</div>
</body>
<template id="parent">
<div>
我是父组件
<!-- 在父组件调用子组件的地方,绑定一个自定义的事件,该事件由父组件实现,默认参数即为子组件传递给父组件的值 -->
<my-child @my-event="getData"></my-child>
</div>
</template>
<template id="child">
<div>
我是子组件
<button @click="$emit('my-event', 2000)">传2000</button>
<button @click="sendData(3000)">传3000</button>
</div>
</template>
<script src="lib/vue.global.js"></script>
<script>
// 在子组件的某一个事件内部,通过 this.$emit('自定义的事件名',传递的参数)完成子组件给父组件传值,内联事件处理器中不需要写this
const Child = {
template: '#child',
mounted () {
this.$emit('my-event', 1000)
},
methods: {
sendData (num) {
this.$emit('my-event', num)
}
}
}
const Parent = {
template: '#parent',
components: {
MyChild: Child
},
methods: {
getData (val) {
console.log('接收到子组件的数据:' + val)
}
}
}
const { createApp } = Vue
const app = createApp({
components: {
MyParent: Parent
}
})
app.mount('#app')
</script>
</html>
6.2.2 子组件给父组件传值-声明触发的事件
组件要触发的事件可以显式地通过 emits 选项来声明:
{ emits: ['inFocus', 'submit'] }
这个 emits
选项还支持对象语法,它允许我们对触发事件的参数进行验证:
{ emits: { submit(payload) { // 通过返回值为 `true` 还是为 `false` 来判断 // 验证是否通过 } } }
要为事件添加校验,那么事件可以被赋值为一个函数,接受的参数就是抛出事件时传入 this.$emit
的内容,返回一个布尔值来表明事件是否合法。
{
emits: {
// 没有校验
click: null,
// 校验 submit 事件
submit: ({ email, password }) => {
if (email && password) {
return true
} else {
console.warn('Invalid submit event payload!')
return false
}
}
},
methods: {
submitForm(email, password) {
this.$emit('submit', { email, password })
}
}
}
完整案例:33_child_parent_component_value2.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>33_子组件给父组件传值-声明触发的事件并且验证</title>
</head>
<body>
<div id="app">
<my-parent></my-parent>
</div>
</body>
<template id="parent">
<div>
我是父组件
<!-- 在父组件调用子组件的地方,绑定一个自定义的事件,该事件由父组件实现,默认参数即为子组件传递给父组件的值 -->
<my-child @my-event="getData"></my-child>
</div>
</template>
<template id="child">
<div>
我是子组件
<button @click="$emit('my-event', 2000)">传2000</button>
<button @click="sendData(3000)">传3000</button>
</div>
</template>
<script src="lib/vue.global.js"></script>
<script>
// 在子组件的某一个事件内部,通过 this.$emit('自定义的事件名',传递的参数)完成子组件给父组件传值,内联事件处理器中不需要写this
const Child = {
template: '#child',
// emits: ['my-event'],
emits: {
'my-event': function (payload) {
console.log(payload)
return payload > 2000
}
},
mounted () {
this.$emit('my-event', 1000)
},
methods: {
sendData (num) {
this.$emit('my-event', num)
}
}
}
const Parent = {
template: '#parent',
components: {
MyChild: Child
},
methods: {
getData (val) {
console.log('接收到子组件的数据:' + val)
}
}
}
const { createApp } = Vue
const app = createApp({
components: {
MyParent: Parent
}
})
app.mount('#app')
</script>
</html>
6.2.3 自定义表单组件使用v-model
自定义事件可以用于开发支持 v-model
的自定义表单组件
完整案例:34_custom_form_v-model.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>34_自定义表单组件配合 v-model</title>
</head>
<body>
<div id="app">
<my-input v-model="userName" placeholder="用户名"></my-input> {{ userName }}
<my-input type="password" v-model="password" placeholder="密码"></my-input>{{ password }}
<my-button @my-click="submit"></my-button>
</div>
</body>
<template id="input">
<input :type="type" :placeholder="placeholder" :value="modelValue" @input="$emit('update:modelValue', $event.target.value)" />
</template>
<template id="btn">
<!-- 实际上执行2次,因为子组件模板中只有button -->
<!-- <button @click="$emit('click')">提交</button> -->
<button @click="$emit('my-click')">提交</button>
</template>
<script src="lib/vue.global.js"></script>
<script>
const Input = {
template: '#input',
props: {
type: {
type: String,
default: 'text'
},
placeholder: String,
modelValue: String // 自定义表单组件 v-model 内部 为 modelValue 属性和 input事件
},
emits: ['update:modelValue']
}
const Button = {
template: '#btn'
}
const { createApp } = Vue
const app = createApp({
components: {
MyInput: Input,
MyButton: Button
},
data () {
return {
userName: '',
password: ''
}
},
methods: {
submit () {
console.log({
userName: this.userName,
password: this.password
})
}
}
})
app.mount('#app')
</script>
</html>
另一种在组件内实现 v-model
的方式是使用一个可写的,同时具有 getter 和 setter 的计算属性。get
方法需返回 modelValue
prop,而 set
方法需触发相应的事件:
const Input = {
template: `#input`,
props: ['modelValue'],
emits: ['update:modelValue'],
computed: {
value: {
get() {
return this.modelValue
},
set(value) {
this.$emit('update:modelValue', value)
}
}
}
}
6.2.4 多个v-model的绑定
仅限于 vue3
完整案例:35_custom_form_v-model_params.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>35_多个 v-model 绑定</title>
</head>
<body>
<div id="app">
<!-- <my-form v-model:username="username" v-model:password="password"></my-form> -->
<my-custom v-model:username="username" v-model:password="password"></my-custom>
<my-button @my-click="submit"></my-button>
</div>
</body>
<template id="custom">
<div>
<input type="text" placeholder="用户名" :value="username" @input="$emit('update:username', $event.target.value)">
<input type="password" placeholder="密码" :value="password" @input="$emit('update:password', $event.target.value)">
</div>
</template>
<template id="btn">
<!-- 实际上执行2次,因为子组件模板中只有button -->
<!-- <button @click="$emit('click')">提交</button> -->
<button @click="$emit('my-click')">提交</button>
</template>
<script src="lib/vue.global.js"></script>
<script>
const Custom = {
template: '#custom',
props: {
username: String,
password: String
},
emits: ['update:username', 'update:password']
}
const Button = {
template: '#btn'
}
const { createApp } = Vue
const app = createApp({
components: {
MyCustom: Custom,
MyButton: Button
},
data () {
return {
username: '',
password: ''
}
},
methods: {
submit () {
console.log({
username: this.username,
password: this.password
})
}
}
})
app.mount('#app')
</script>
</html>
6.3 透传Attribute
“透传 attribute”指的是传递给一个组件,却没有被该组件声明为 props 或 emits 的 attribute 或者 v-on
事件监听器。最常见的例子就是 class
、style
和 id
。
6.3.1 attribute继承
当一个组件以单个元素为根作渲染时,透传的 attribute 会自动被添加到根元素上。举例来说,假如我们有一个 <MyButton>
组件,它的模板长这样:
<!-- <MyButton> 的模板 --> <button>click me</button>
一个父组件使用了这个组件,并且传入了 class
:
<MyButton class="large" />
最后渲染出的 DOM 结果是:
<button class="large">click me</button>
这里,<MyButton>
并没有将 class
声明为一个它所接受的 prop,所以 class
被视作透传 attribute,自动透传到了 <MyButton>
的根元素上。
6.3.2 对 class
和 style
的合并
如果一个子组件的根元素已经有了 class
或 style
attribute,它会和从父组件上继承的值合并。如果我们将之前的 <MyButton>
组件的模板改成这样:
<!-- <MyButton> 的模板 --> <button class="btn">click me</button>
则最后渲染出的 DOM 结果会变成:
<button class="btn large">click me</button>
6.3.3 v-on
监听器继承
同样的规则也适用于 v-on
事件监听器:
<MyButton @click="onClick" />
click
监听器会被添加到 <MyButton>
的根元素,即那个原生的 <button>
元素之上。当原生的 <button>
被点击,会触发父组件的 onClick
方法。同样的,如果原生 button
元素自身也通过 v-on
绑定了一个事件监听器,则这个监听器和从父组件继承的监听器都会被触发。
6.3.4 禁用 Attributes 继承
如果你不想要一个组件自动地继承 attribute,你可以在组件选项中设置 inheritAttrs: false
。
最常见的需要禁用 attribute 继承的场景就是 attribute 需要应用在根节点以外的其他元素上。通过设置 inheritAttrs
选项为 false
,你可以完全控制透传进来的 attribute 被如何使用。
6.3.5 多根节点的 Attributes 继承
$attrs
被显式绑定
完整案例: 36_attribute_transmission.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>36_透传attribute</title>
<style>
.btn {
border: 0;
padding: 10px 20px;
}
.btn-success {
background-color: rgb(29, 198, 29);
color: #fff;
}
.btn-danger {
background-color: rgb(218, 23, 39);
color: #fff;
}
.btn-primary {
background-color: rgb(75, 104, 236);
color: #fff;
}
</style>
</head>
<body>
<div id="app">
<my-button type="a" class="btn-success" @click="print('success')" @my-event="myalert(1)"></my-button>
<my-button type="b" class="btn-danger" @click="print('danger')" @my-event="myalert(2)"></my-button>
<my-button type="c" class="btn-primary" @click="print('primary')" @my-event="myalert(3)"></my-button>
</div>
</body>
<template id="button">
<!-- Attributes 继承(class 与style合并, v-on事件继承) -->
<!-- <button class="btn">按钮</button> -->
<!-- 深层组件继承 -->
<base-button></base-button>
</template>
<template id="base">
<button class="btn" v-bind="$attrs" >按钮</button>
<div >测试</button>
</template>
<script src="lib/vue.global.js"></script>
<script>
const Base = {
template: '#base',
mounted () { // 在js中访问透传的 attributes
console.log('2', this.$attrs)
},
// inheritAttrs: false // 不想要一个组件自动地继承 attribute,你可以在组件选项中设置
}
const Button = {
template: '#button',
components: {
BaseButton: Base
},
mounted () {
console.log('1', this.$attrs)
},
}
const { createApp } = Vue
const app = createApp({
components: {
MyButton: Button
},
methods: {
print (msg) {
console.log(msg)
}
},
myalert (num) {
alert(num)
}
})
app.mount('#app')
</script>
</html>
6.4 特殊Attribute -ref
学习:实例属性refs
用于注册模板引用。
ref
用于注册元素或子组件的引用。
使用选项式 API,引用将被注册在组件的 this.$refs
对象里
放在DOM元素上,获取DOM节点,放到组件上,获取子组件的实例,可以直接使用子组件的属性和方法
完整案例:37_attribute_ref.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>37_特殊的attribute——ref</title>
</head>
<body>
<div id="app">
<!-- ref用到自定义组件,可以获取到子组件的实例 -->
<my-child ref="childRef"></my-child>
<!-- 原生jsDOM操作 -->
<input type="text" id="userName" @input="getJsData">
<!-- ref用到DOM元素上,可以获取到当前的DOM元素 -->
<input type="text" ref="password" @input="getRefData">
</div>
</body>
<template id="child">
<div>
<h1>使用ref获取子组件的实例</h1>
</div>
</template>
<script src="lib/vue.global.js"></script>
<script>
const Child = {
template: '#child',
data () {
return {
count: 10
}
},
computed: {
doubleCount () {
return this.count * 2
}
},
methods: {
print () {
console.log(this.count)
}
}
}
const { createApp } = Vue
const app = createApp({
components: {
MyChild: Child
},
mounted () {
console.log(this)
// 父组件可以通过 ref 直接获取到子组件的实例的属性和方法等
console.log(this.$refs.childRef.count)
console.log(this.$refs.childRef.doubleCount)
this.$refs.childRef.print()
},
methods: {
getJsData () {
console.log(document.getElementById('userName').value)
},
getRefData () {
console.log(this.$refs.password.value)
}
}
})
app.mount('#app')
</script>
</html>
6.5 $parent
当前组件可能存在的父组件实例,如果当前组件是顶层组件,则为 null
。
完整案例38_parent.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>38_parent</title>
</head>
<body>
<div id="app">
<my-parent></my-parent>
</div>
</body>
<template id="parent">
<div>
我是父组件
<my-child></my-child>
</div>
</template>
<template id="child">
<div>
我是子组件 - {{ $parent.msg }}
<button @click="$parent.print()">执行</button>
<button @click="test">执行</button>
</div>
</template>
<script src="lib/vue.global.js"></script>
<script>
const Child = {
template: '#child',
methods: {
test () {
this.$parent.print()
}
}
}
const Parent = {
template: '#parent',
components: {
MyChild: Child
},
data () {
return {
msg: 'hello parent'
}
},
methods: {
print () {
console.log(this.msg)
}
}
}
const { createApp } = Vue
const app = createApp({
components: {
MyParent: Parent
}
})
app.mount('#app')
</script>
</html>
6.6$root
当前组件树的根组件实例。如果当前实例没有父组件,那么这个值就是它自己。
完整案例:39_root.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>39_root </title>
</head>
<body>
<div id="app">
<my-parent></my-parent>
</div>
</body>
<template id="parent">
<div>
我是父组件 --- {{ $root.count }}
<my-child></my-child>
</div>
</template>
<template id="child">
<div>
我是子组件 --- {{ $root.count }}
<button @click="$root.add()">加</button>
</div>
</template>
<script src="lib/vue.global.js"></script>
<script>
const Child = {
template: '#child',
created () {
console.log('child', this.$root)
}
}
const Parent = {
template: '#parent',
components: {
MyChild: Child
},
created () {
console.log('parent', this.$root)
}
}
const { createApp } = Vue
const app = createApp({
components: {
MyParent: Parent
},
data () {
return {
count: 100
}
},
methods: {
add () {
this.count++
}
}
})
app.mount('#app')
</script>
</html>
6.7 非父子组件传值
兄弟组件传值 - 中央事件总线传值 ---- vue2
完整案例:40_brother_value-vue2.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>40_vue2 兄弟组件传值</title>
</head>
<body>
<div id="app">
<my-content></my-content>
<my-footer></my-footer>
</div>
</body>
<script src="lib/vue.js"></script>
<script>
const bus = new Vue() // 中央事件总线传值
const Content = {
template: `<div>{{ type }}</div>`,
data () {
return {
type: '首页'
}
},
mounted () {
bus.$on('change-type', (val) => {
this.type = val
})
}
}
const Footer = {
template: `
<footer>
<ul>
<li @click="changeType('首页')">首页</li>
<li @click="changeType('分类')">分类</li>
<li @click="changeType('购物车')">购物车</li>
<li @click="changeType('我的')">我的</li>
</ul>
</footer>
`,
methods: {
changeType (type) {
bus.$emit('change-type', type)
}
}
}
new Vue({
components: {
MyContent: Content,
MyFooter: Footer
}
}).$mount('#app')
</script>
</html>
vue3中没有明确的兄弟组件传值的方案,可以使用状态提升(找到这两个组件共同的父级组件,然后通过父与子之间的传值实现)
完整案例:41_brother_value-vue3.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>41_vue3 兄弟组件传值-状态提升</title>
</head>
<body>
<div id="app">
<my-content :type="type" ></my-content>
<my-footer @change-type="getVal"></my-footer>
</div>
</body>
<script src="lib/vue.global.js"></script>
<script>
const Content = {
props: ['type'],
template: `<div>{{ type }}</div>`,
}
const Footer = {
template: `
<footer>
<ul>
<li @click="changeType('首页')">首页</li>
<li @click="changeType('分类')">分类</li>
<li @click="changeType('购物车')">购物车</li>
<li @click="changeType('我的')">我的</li>
</ul>
</footer>
`,
methods: {
changeType (type) {
this.$emit('change-type', type)
}
}
}
Vue.createApp({
components: {
MyContent: Content,
MyFooter: Footer
},
data () {
return {
type: '首页'
}
},
methods: {
getVal (type) {
this.type = type
}
}
}).mount('#app')
</script>
</html>