组件通讯(传事件、传属性、传内容)
1.父传子(父组件引入并使用子组件,通过 :属性名="父组件中要传递的属性名"传到子组件,子组件通过props接收并使用)
父组件App.js
import ComA from "./ComA.js";
export default {
components: {
ComA,
},
data() {
return {
title: "组件通讯",
msg: "父组件内容",
user: {
name: "jack",
age: 20,
},
};
},
methods: {
test() {},
},
/*html*/
template: `<div style="width:400px;height:400px;background-color:skyblue;">
<h2>{{title}}</h2>
<com-a :message="msg" :user="user"></com-a>
</div>`,
};
子组件ComA.js
export default {
// props: ["message", "user"],
// props: {
// message:String,
// user: Object,
// },
props: {
message: {
type: String, // 类型
default: "默认值", // 默认值
require: true, // 是否必传
// 校验
validator(val) {
console.log("val ", val);
if (val === "") {
return "内容不能为空!";
} else {
return val;
}
},
},
user: Object,
},
data() {
return {
title: "子组件内容",
count: 0,
content: this.message,
};
},
created() {
// 将外部数据赋值给内部状态content
// this.content = this.message
},
methods: {
bindConfirm() {
this.count++;
console.log("this.message ", this.message);
this.content = "新内容";
},
},
/*html*/
template: `<div style="width:200px;height:300px;background-color:pink;">
<h2>{{title}}</h2>
<p>外部数据: {{ message }}</p>
<p>外部数据: {{ user.name }} - {{ user.age}}</p>
<p>内部数据: {{ count }}</p>
<button @click="bindConfirm">确定</button>
<p>外部数据赋值给内部状态content: {{content}}</p>
</div>`,
};
2. 子传父(父组件在使用子组件位置绑定一个自定义事件,子组件通过emits接收自定义事件,并通过$emit触发自定义事件,将参数返回给父组件)
父组件App.js
import Child from "./Child.js";
/*
* 子传父
* 1. 父组件使用子组件位置 绑定自定义事件 @someEvent="bindEvent"
* 2. 子组件中定义emits选项接收自定义事件 emits:['someEvent']
* 这步可以不写,但建议写上
* 3. 触发自定义事件 this.$emit('someEvent',参数)
*/
export default {
components: {
Child,
},
data() {
return {
title: "父组件",
msg: "",
};
},
methods: {
bindSomEvent(parms) {
console.log("bindSomEvent >>> ", parms);
this.msg = parms;
},
},
/*html*/
template: `<div style="width:400px;height:400px;background-color:skyblue;">
<h2>{{title}}</h2>
<p>{{ msg }}</p>
<child @someEvent="bindSomEvent"></child>
</div>`,
};
子组件Child.js
export default {
emits: ["someEvent"], // 接收事件
data() {
return {
title: "子组件",
content: " 子组件数据content",
};
},
methods: {
bindConfirm(){
this.$emit('someEvent',this.content) // 触发someEvent事件
}
},
/*html*/
template: `<div style="width:200px;height:200px;background-color:pink;">
<h2>{{title}}</h2>
<button @click="bindConfirm">传参到父组件1</button>
<button @click="$emit('someEvent',content)">传参到父组件2</button>
</div>`,
};
3. ref属性操作组件(给组件标签添加ref属性,通过 this.$refs.添加的ref属性名 获取组件实例,调用其数据和方法)
父组件App.js
import ComA from "./ComA.js";
export default {
components: {
ComA,
},
data() {
return {
title: "组件通讯",
msg: "父组件内容",
};
},
methods: {
getComA(){
// 获取子组件实例,调用子组件方法和数据
const comAEle = this.$refs.comaRef
console.log('comAEle ',comAEle);
comAEle.bindConfirm()
console.log( 'comAEle.count',comAEle.count);
}
},
/*html*/
template: `<div style="width:400px;height:400px;background-color:skyblue;">
<h2>{{title}}</h2>
<com-a ref="comaRef"></com-a>
<button @click="getComA">确定</button>
</div>`,
};
子组件ComA.js
export default {
data() {
return {
title: "子组件内容",
count: 0,
};
},
methods: {
bindConfirm() {
this.count++;
},
},
/*html*/
template: `<div style="width:200px;height:300px;background-color:pink;">
<h2>{{title}}</h2>
<p>{{ count }}</p>
<button @click="bindConfirm">加</button>
</div>`,
};
4. 表单组件v-model双向绑定(默认情况下props接收值固定用modelValue,emits接收事件固定用update:modelValue。当然也可以通过指定参数来更改名字)
父组件App.js
import SearchInput from "./1-SearchInput.js";
import CustomInput from './2-CustomInput.js'
import ComputedInput from './3-ComputedInput.js'
import MyInput from './4-MyInput.js'
/**
* 根组件
* 搜索框组件输入内容,在父组件中获取展示
* 子传父+子传父
*/
export default {
components: {
SearchInput,//原生实现
CustomInput,//v-model实现
ComputedInput,//v-model实现
MyInput//更改名字
},
data() {
return {
title: "表单组件双向数据绑定",
searchContent:'hello',
CustomContent:'vue',
message:'js',
msg:'更改modleValue',
msg1:''
};
},
methods: {
bindSearchContent(val){
this.searchContent= val
}
},
/*html*/
template: `<div style="width:400px;height:600px;background-color:skyblue;">
<h2>{{title}}</h2>
<p>原生实现</p>
<p>{{searchContent}}</p>
<search-input :searchContent="searchContent" @searchEvent="bindSearchContent"></search-input>
<p>v-model实现方法一</p>
<p>{{CustomContent}}</p>
<custom-input v-model="CustomContent"></custom-input>
<p>v-model实现方法二</p>
<p>{{message}}</p>
<computed-input v-model="message"></computed-input>
<p>v-model更改名字,modelValue改为title</p>
<p>{{msg}}</p>
<my-input v-model:title="msg" v-model:msg="msg1"></my-input>
</div>`,
};
子组件1-SearchInput.js(原生实现)
export default {
props:['searchContent'],
emits:['searchEvent'],
data() {
return {};
},
/*html*/
template: `<div class="g-search">
<input type="text" name="search" :value="searchContent" @input="$emit('searchEvent', $event.target.value)" placeholder="请输入内容"/>
</div>`,
};
子组件2-CustomInput.js(v-model实现方法一)
export default {
props:['modelValue'],
emits:['update:modelValue'],
/*html */
template:`<div>
<input type="text" :value="modelValue" @input="$emit('update:modelValue',$event.target.value)" placeholder="请输入内容"/>
</div>`
}
子组件3-ComputedInput.js(v-model实现方法二)
export default {
props: ["modelValue"],
emits: ["update:modelValue"]
computed: {
inputValue: {
get() {
return this.modelValue; // 计算属性值为传入的modelValue值
},
set(newValue) {
this.$emit("update:modelValue", newValue);
},
},
},
/*html */
template: `<div>
<input type="text" v-model="inputValue" placeholder="请输入内容"/>
</div>`,
};
子组件4-MyInput.js(更改名字)
export default {
props: ["title","msg"],
emits: ["update:title","update:msg"],
computed: {
inputValue: {
get() {
return this.title; // 计算属性值为传入的modelValue值
},
set(newValue) {
this.$emit("update:title", newValue);
},
},
},
/*html */
template: `<div>
<input type="text" v-model="inputValue" placeholder="请输入内容"/>
</div>`,
};
5. 兄弟间传值Event Bus(一般不用,可以用状态管理或依赖注入)
vue2中可以用事件总线的方式,new一个vue实例,然后在一个组件中通过$emit触发事件,在另一个组件中通过$on监听事件
vue3中提供了一个mitt第三方库
6. 插槽(父组件与子组件之间内容传递/子组件定义插槽,父组件定义插槽内容。)
(1)子组件使用slot标签接收传入的内容
默认插槽 <slot></slot>
具名插槽 <slot name=""></slot>
作用域插槽:子组件通过插槽传参到父组件
(2)父组件内容有多个标签时,使用 template标签包裹, v-slot指令指定默认名称 v-slot:default(v-slot指令可以简写 #。如v-slot:content可以简写成#content)
实际应用:子组件头部尾部确定,中间内容部分不确定,定义成插槽。多个父组件调用,传入内容是什么就显示什么。
父组件App.js
import Child from "./Child.js";
export default {
components: {
Child,
},
data() {
return {
title: "父组件",
};
},
/*html*/
template: `<div style="width:400px;height:400px;background-color:skyblue;">
<h2>{{title}}</h2>
<child>
<!-- 默认插槽 -->
<template v-slot:default>
<h2>标题</h2>
<p>插槽内容</p>
</template>
<!-- 具名插槽 -->
<template v-slot:tree>
<p>树</p>
</template>
<!-- 简写 -->
<template #flower>
<p>花</p>
</template>
<!-- 作用域插槽
slotList相当于是插槽实例,可以自定义命名
slotList.books就可以拿到子组件数据了
-->
<template v-slot:listtemp="slotList">
<ul>
<li v-for="book in slotList.books">
{{book}}
</li>
</ul>
</template>
</child>
</div>`,
};
子组件Child.js
export default {
data() {
return {
title: "子组件",
list:['javascript高级编程','vue高级','css高级']
};
},
/*html*/
template: `<div style="width:200px;height:200px;background-color:pink;">
<h2>{{title}}</h2>
<slot></slot>
<p>绿化带</p>
<slot name="tree"></slot>
<p>绿化带</p>
<slot name="flower"></slot>
<!-- 子组件list -->
<slot name="listtemp" :books="list"></slot>
</div>`,
};
7. 依赖注入(provide-inject跨组件通讯)
一般父组件改变值,子组件的值不会跟着变(要实现响应式可在暴露的时候使用computed计算属性)
父组件提供provide,子组件通过inject接收数据
父组件App.js
import Child from "./Child.js";
import { computed } from '<https://unpkg.com/vue@3/dist/vue.esm-browser.js>'
export default {
components: {
Child,
},
data() {
return {
title: "父组件",
msg:'这是根组件数据'
}
},
// 提供数据
provide(){
return {
// message:this.msg
message: computed(()=>this.msg)//实现响应式
}
},
methods: {
bindUpdateMessage(){
this.msg = '新内容'
}
},
/*html*/
template: `<div style="width:600px;height:600px;background-color:skyblue;">
<h2>{{title}}</h2>
<child ></child>
<button @click="bindUpdateMessage">确定</button>
</div>`,
};
子组件Child.js
import Son from './Son.js'
export default {
components:{
Son
},
data() {
return {
title: "儿子组件",
};
},
/*html*/
template: `<div style="width:400px;height:400px;background-color:pink;">
<h2>{{title}}</h2>
<son></son>
</div>`,
};
孙组件Son.js
export default {
// 接收数据
// inject:['message'],
inject: {
msg: {//更改名字
from: 'message',
default: '默认值'
}
},
data() {
return {
title: "孙子组件",
sonmessage: '孙子sonmessage'
};
},
/*html*/
template: `<div style="width:200px;height:200px;background-color:green;">
<h2>{{title}}</h2>
<p>{{sonmessage}}</p>
<!-- <p>{{message}}</p> -->
<p>{{msg}}</p>
</div>`,
};