props实现父子组件通信
Song.vue Singer.vue ==>父组件
SectionList.vue ==>子组件
SectionList 需要 标题+列表数据
爸爸传什么数据 我就渲染什么数据(需求)
Song.vue
<template>
<div id="song">
<SectionList v-bind:sectionObj="sectionObj"
></SectionList>
</div>
</template>
<script>
import SectionList from '@/components/SectionList';
export default{
name:"Song",
components:{
SectionList
},
data(){
return{
sectionObj:{
title:"歌曲列表",
arr:[
{id:1,name:"稻香"},
{id:2,name:"丑八怪"},
{id:3,name:"动物世界"},
{id:4,name:"梨花香"}
]
}
}
}
};
</script>
Singer.vue
<template>
<div id="singer">
<SectionList v-bind:sectionObj="sectionObj"></SectionList>
</div>
</template>
<script>
import SectionList from '@/components/SectionList';
export default{
name:"Singer",
components:{
SectionList
},
data(){
return{
msg:"我是song1给的小纸条"
sectionObj:{
title:"歌手列表",
arr:[
{id:1,name:"周杰伦"},
{id:2,name:"薛之谦"},
{id:3,name:"陈奕迅"},
{id:4,name:"孙燕姿"}
]
}
}
}
};
</script>
SelectionList.vue
<template>
<div>
<div>{{ sectionObj.title }}</div>
<ul>
<li v-for="item in sectionObj.arr" :key="item.id">{{ item.name }}</li>
</ul>
</div>
</template>
<script>
export default{
name:"SectionList",
// props 写自定义组件标签内的标签属性名接收了 就可以在此组件内 使用此标签属性内的对象
props:["sectionObj"]
}
</script>
流程复盘
1.要在父组件内 自定义属性:自定义属性名 = ‘我们想要传的对象’ 子
2. 子组件内要接收 props:[‘自定义属性名’]
3. 组件内使用数据时:直接使用 自定义属性名即可
注意
- props 形式 单向的 只适用于 父子组件之间
- 数据流向 只能 父 流向 子
- props 数据 只读 (不允许你去修改)
- 子组件内 修改了props 不会引发父级的变化(不让数据的流向难以理解)
- 父级内修改了 props 传的对象的数据 引发子组件的变化
如果我们希望在子组件内部 对传来的props内的属性 进行加工处理(可以用上computed)
<div>{{ handleObj.title }}</div>
handleTitle(){
return this.sectionObj.title+"!!!!"
}
错误示范:
created(){
// 这么做 违背了props内属性只读的原则
this.sectionObj.title +="!!!!!"
},
props 接收写法
数组写法:
props:["sectionObj"],props:['标签属性名']
接收多个属性 可以接收 动态绑定的 也可以接收普通属性
<SectionList v-bind:sectionObj="sectionObj"
title="我是歌曲"
:dataStr="msg"
></SectionList>
props:["sectionObj","title","dataStr"],
<template>
<div id="song">
<SectionList v-bind:sectionObj="sectionObj"
title="我是歌曲"
:dataStr="msg"
></SectionList>
<!-- <button @click="fn()">修改sectionObj的值</button> -->
</div>
</template>
<script>
import SectionList from '@/components/SectionList';
export default{
name:"Song",
components:{
SectionList
},
data(){
return{
msg:"我是song给的小纸条",
sectionObj:{
title:"歌曲列表",
arr:[
{id:1,name:"稻香"},
{id:2,name:"丑八怪"},
{id:3,name:"动物世界"},
{id:4,name:"梨花香"}
]
}
}
},
methods:{
fn(){
this.sectionObj.title+="!!!!"
}
}
};
</script>
<template>
<div id="singer">
<SectionList v-bind:sectionObj="sectionObj"
title="我是歌曲"
:dataStr="msg"
></SectionList>
</div>
</template>
<script>
import SectionList from '@/components/SectionList';
export default{
name:"Singer",
components:{
SectionList
},
data(){
return{
msg:"我是Singer给的小纸条",
sectionObj:{
title:"歌手列表",
arr:[
{id:1,name:"周杰伦"},
{id:2,name:"薛之谦"},
{id:3,name:"陈奕迅"},
{id:4,name:"孙燕姿"}
]
}
}
}
};
</script>
<template>
<div>
<div>{{ sectionObj.title }}</div>
<h1>{{ title }}</h1>
<!-- <div>{{ handleObj.title }}</div>
<div>{{ handleTitle }}</div> -->
<ul>
<li v-for="item in sectionObj.arr" :key="item.id">{{ item.name }}</li>
</ul>
<p>{{ dataStr }}</p>
</div>
</template>
<script>
export default{
name:"SectionList",
// props 写自定义组件标签内的标签属性名接收了 就可以在此组件内 使用此标签属性内的对象
props:["sectionObj","title","dataStr"],
computed:{
handleObj(){
let title = this.sectionObj.title+"!!!!";
return {
title,
arr: this.sectionObj.arr
}
},
handleTitle(){
return this.sectionObj.title+"!!!!"
}
}
};
</script>
props:{
sectionObj:Object ,接收 sectionObj标签属性内的数据 但是只接收 对象类型
},
sectionObj:[String,Array],允许多个类型
sectionObj:{
type:Object,//限制数据类型为对象
required:true,//这个数据必须传 不传会给警告
}
sectionObj:{
type:Object,
default:{},//没有传入数据时 使用默认值选项
}
sectionObj:{
type:Object,
default:function(){
// 适用于 值没法写死 可能要在这请求一下 或者计算一下 再return 出去
// return的值 作为default的值
return {} //return 出一个空对象
}
}
自定义验证函数
sectionObj:{
validator(value){
// value 就是 sectionObj传过来的值
// 函数内部 return true 就是验证通过 return false 验证不通过
// 可以在这里 定义各种各样的规则
return ["我","你","他"].includes(value);
}
}
props 没有接收的标签属性
没有被props 接收了的标签属性 会出现在根节点 被props 接收了的标签属性 不会出现在根节点会 识别到 子组件内 跟节点的 html标签上 作为标签属性
浏览器内 这个组件是子组件
没有被props接收的标签属性 出现在vc上的$attrs上接收
created(){
console.log(this.$attrs);
},
如果不希望 继承 自定义标签内的标签属性
// 修改默认继承属性的布尔值 false不继承
inheritAttrs:false,(根节点上,就不会出现那些未被props接收的标签属性)
<template>
<div>
<div>{{ sectionObj.title }}</div>
<h1>{{ title }}</h1>
<!-- <div>{{ handleObj.title }}</div>
<div>{{ handleTitle }}</div> -->
<!-- v-bind可以绑定一个对象 v-bind="$attrs" -->
<ul >
<li v-for="item in sectionObj.arr" :key="item.id">{{ item.name }}</li>
</ul>
<p>{{ dataStr }}</p>
</div>
</template>
<script>
export default{
name:"SectionList",
// props 写自定义组件标签内的标签属性名接收了 就可以在此组件内 使用此标签属性内的对象
props:["sectionObj","dataStr"],
computed:{
handleObj(){
let title = this.sectionObj.title+"!!!!";
return {
title,
arr: this.sectionObj.arr
}
},
handleTitle(){
return this.sectionObj.title+"!!!!"
}
},
created(){
console.log(this.$attrs);
},
// 修改默认继承属性的布尔值 false不继承
inheritAttrs:false,
};
</script>
子传父 通过自定义的事件
song.vue
<template>
<div id="song">
<h1>{{ msg }}</h1>
<SectionList v-bind:sectionObj="sectionObj" @changeMsg="fn"></SectionList>
<!-- <button @click="fn()">修改sectionObj的值</button> -->
</div>
</template>
<script>
import SectionList from '@/components/SectionList';
export default{
name:"Song",
components:{
SectionList
},
data(){
return{
msg:"我是song的小纸条",
sectionObj:{
title:"歌曲列表",
arr:[
{id:1,name:"稻香"},
{id:2,name:"丑八怪"},
{id:3,name:"动物世界"},
{id:4,name:"梨花香"}
]
}
}
},
methods:{
fn(){
// 父组件内
// 修改msg的操作
this.msg +="~~~~~~~";
}
}
};
</script>
Sectionlist.vue
<template>
<div>
<div>{{ sectionObj.title }}</div>
<ul >
<li v-for="item in sectionObj.arr" :key="item.id">{{ item.name }}</li>
</ul>
<button @click="childFn">点我触发自定义事件</button>
</div>
</template>
<script>
export default{
name:"SectionList",
// props 写自定义组件标签内的标签属性名接收了 就可以在此组件内 使用此标签属性内的对象
props:["sectionObj"],
methods:{
childFn(){
// 子组件内 在这里 父组件内 changeMsg 自定义事件
this.$emit("changeMsg ")
}
}
};
</script>
步骤复盘
1. 在父组件内 给子组件标签 添加自定义事件 changeMsg 事件回调函数 是 fn
<SectionList v-bind:sectionObj="sectionObj" @changeMsg="fn"></SectionList>
fn(){
// 父组件内
// 修改msg的操作
this.msg +="~~~~~~~";
}
2. 子组件内 在某个时机内 触发 父组件内 changeMsg 自定义事件
当前时机是 点击子组件内按钮时 触发 父组件内 changeMsg自定义事件
<button @click="childFn">点我触发自定义事件</button>
childFn(){
// 子组件内 在这里 父组件内 changeMsg 自定义事件
this.$emit("changeMsg ")
}
Song.vue
<template>
<div id="song">
<h1>{{ msg }}</h1>
<SectionList v-bind:sectionObj="sectionObj" @changeMsg="fn"></SectionList>
<!-- <button @click="fn()">修改sectionObj的值</button> -->
</div>
</template>
<script>
import SectionList from '@/components/SectionList';
export default{
name:"Song",
components:{
SectionList
},
data(){
return{
msg:"我是song的小纸条",
sectionObj:{
title:"歌曲列表",
arr:[
{id:1,name:"稻香"},
{id:2,name:"丑八怪"},
{id:3,name:"动物世界"},
{id:4,name:"梨花香"}
]
}
}
},
methods:{
fn(){
// 父组件内
// 修改msg的操作
this.msg +="~~~~~~~";
}
}
};
</script>
SectionList.vue
<template>
<div>
<div>{{ sectionObj.title }}</div>
<ul >
<li v-for="item in sectionObj.arr" :key="item.id">{{ item.name }}</li>
</ul>
<button @click="childFn">点我触发自定义事件</button>
</div>
</template>
<script>
export default{
name:"SectionList",
// props 写自定义组件标签内的标签属性名接收了 就可以在此组件内 使用此标签属性内的对象
props:["sectionObj"],
methods:{
childFn(){
// 子组件内 在这里 父组件内 changeMsg 自定义事件
this.$emit("changeMsg");
}
}
};
</script>
传参写法
子组件内
childFn(){
// 子组件内 在这里 触发 父组件内 changeMsg 自定义事件
this.$emit("changeMsg","我是儿子");
}
父组件为
fn(fromChildValue){
// fromChildValue 接收 “我是儿子” 这个参数
// 父组件内
// 获取msg的操作;
},
和之前的不同 $event
之前 $event是传入的是《事件对象》
<SectionList v-bind:sectionObj="sectionObj" @changeMsg='msg = $event'></SectionList>
this.$emit("changeMsg","我是儿子");传来的实参 “我是儿子”
song.vue
<template>
<div id="song">
<h1>{{ msg }}</h1>
<SectionList v-bind:sectionObj="sectionObj" @changeMsg='msg = $event'></SectionList>
<!-- <button @click="fn()">修改sectionObj的值</button> -->
</div>
</template>
<script>
import SectionList from '@/components/SectionList';
export default{
name:"Song",
components:{
SectionList
},
data(){
return{
msg:"我是song的小纸条",
sectionObj:{
title:"歌曲列表",
arr:[
{id:1,name:"稻香"},
{id:2,name:"丑八怪"},
{id:3,name:"动物世界"},
{id:4,name:"梨花香"}
]
}
}
},
methods:{
fn(fromChildValue){
// 父组件内
// 修改msg的操作
this.msg +=fromChildValue;
}
}
};
</script>
<template>
<div>
<div>{{ sectionObj.title }}</div>
<ul >
<li v-for="item in sectionObj.arr" :key="item.id">{{ item.name }}</li>
</ul>
<button @click="childFn">点我触发自定义事件</button>
</div>
</template>
<script>
export default{
name:"SectionList",
// props 写自定义组件标签内的标签属性名接收了 就可以在此组件内 使用此标签属性内的对象
props:["sectionObj"],
methods:{
childFn(){
// 子组件内 在这里 父组件内 changeMsg 自定义事件
this.$emit("changeMsg","我是儿子");
}
}
};
</script>
自定义事件的其他绑定方式
第一种 @自定义事件名='事件函数'
@changeMsg = 'msg = $event'
触发时 this.$emit('自定义事件名')
第二种
// 渲染之后
mounted(){
// 给他绑定 自定义事件
// this.$refs.section.$on("changeMsg",() =>{
// this.msg +="~~~~~";
// })
// $once 一次性绑定
this.$refs.section.$once("changeMsg",() =>{
this.msg +="~~~~~";
});
},
// 事件的解绑
destroyed() {
this.$refs.section.$off("changeMsg");
},
};
只适合儿子改变父亲 (兄弟之间不能成功 爷孙之间也不能成功)
实现点击子组件li 父组件 法起请求 路由跳转的功能
子组件内
<ul >
<li v-for="item in sectionObj.arr" :key="item.id"
@click = "childFn(item)"
>{{ item.name }}
</li>
</ul>
methods:{
childFn(item){
// 子组件内 在这里 触发 父组件内 changeMsg 自定义事件
this.$emit('changeMsg',item);
}
}
父组件内
<SectionList :sectionObj="sectionObj" @changeMsg="fn"></SectionList>
methods:{
fn(item){
// 通过id发起请求 请求数据
// 通过 路由跳转 另外的组件
}
}
全局事件总线(总栈)
在vm上挂载自定义事件 vm上触发自定义事件
- 第一步 把 vm对象 挂载到 Vue原型上
new Vue({
render: h => h(App),
beforeCreate(){
// 把vm对象 挂载到Vue的原型对象上去 为了方便 各个组件的vc对象能获取vm
Vue.prototype.$bus = this
}
}).$mount('#app')
- 第二步 vm注册上 自定义事件 (需要被改变的组件 需要被接收值的组件)
created(){
// 注册 事件
this.$bus.$on("changSongMsg",(value) =>{
this.msg += value;
});
}
- 第三步 触发 Vm上的自定义事件 (需要传值给别人的组件)
<button @click="changeApp">点击按钮 给app传值</button>
methods:{
changeApp(){
// vm.$emit("changeMsg") 报错
// this 这里的this指向的是vc
// vc 能使用到Vue.prototype 原型上的属性和方法
console.log(this.$bus);
console.log(this);
this.$bus.$emit("changeMsg","我是孙子传给你的值")
}
}
这种方案 适合于 任意组件之间的传值
相当于孙子给爷爷传值互相通信
App.vue
<template>
<div id="app">
<Song id="song"></Song>
<Singer id="singer"></Singer>
<p>{{ msg }}</p>
</div>
</template>
<script>
import Song from "./components/Song";
import Singer from "./components/Singer";
export default{
name:"App",
components:{
Song,
// Singer,
},
data(){
return{
msg:"我是App的数据"
}
},
created(){
// 绑定自定义事件 事件回调
this.$bus.$on("changeMsg",(value)=>{
// 我们修改l
this.msg += value
})
}
}
</script>
<style>
#song{
float:left;
width:200px;
background-color: yellow;
}
#singer{
float:left;
width: 200px;
background-color: skyblue;
}
</style>
Song.vue
<template>
<div id="song">
<h1>{{ msg }}</h1>
<!-- <SectionList v-bind:sectionObj="sectionObj" @changeMsg='msg = $event'></SectionList> -->
<SectionList v-bind:sectionObj="sectionObj" ref="section"></SectionList>
<!-- <button @click="fn()">修改sectionObj的值</button> -->
</div>
</template>
<script>
import SectionList from '@/components/SectionList';
export default{
name:"Song",
components:{
SectionList
},
data(){
return{
msg:"我是song的小纸条",
sectionObj:{
title:"歌曲列表",
arr:[
{id:1,name:"稻香"},
{id:2,name:"丑八怪"},
{id:3,name:"动物世界"},
{id:4,name:"梨花香"}
]
}
}
},
// 渲染之后
mounted(){
},
};
</script>
SectionList.vue
<template>
<div>
<div>{{ sectionObj.title }}</div>
<ul >
<li v-for="item in sectionObj.arr" :key="item.id">{{ item.name }}</li>
</ul>
<button @click="changeApp">点击按钮 给app传值</button>
</div>
</template>
<script>
export default{
name:"SectionList",
// props 写自定义组件标签内的标签属性名接收了 就可以在此组件内 使用此标签属性内的对象
props:["sectionObj"],
methods:{
changeApp(){
// vm.$emit("changeMsg") 报错
// this 这里的this指向的是vc
// vc 能使用到Vue.prototype 原型上的属性和方法
console.log(this.$bus);
console.log(this);
this.$bus.$emit("changeMsg","我是孙子传给你的值")
}
}
};
</script>
想给兄弟song传值
Song.vue
<template>
<div id="song">
<h1>{{ msg }}</h1>
<!-- <SectionList v-bind:sectionObj="sectionObj" @changeMsg='msg = $event'></SectionList> -->
<SectionList v-bind:sectionObj="sectionObj" ref="section"></SectionList>
<!-- <button @click="fn()">修改sectionObj的值</button> -->
</div>
</template>
<script>
import SectionList from '@/components/SectionList';
export default{
name:"Song",
components:{
SectionList
},
data(){
return{
msg:"我是song的小纸条",
sectionObj:{
title:"歌曲列表",
arr:[
{id:1,name:"稻香"},
{id:2,name:"丑八怪"},
{id:3,name:"动物世界"},
{id:4,name:"梨花香"}
]
}
}
},
// 渲染之后
mounted(){
},
created(){
// 注册 事件
this.$bus.$on("changSongMsg",(value) =>{
this.msg += value;
});
}
};
</script>
Singer.vue
<template>
<div id="singer">
<h1>{{ msg }}</h1>
<button @click="changeBrother">给兄弟song传值</button>
<SectionList v-bind:sectionObj="sectionObj"></SectionList>
</div>
</template>
<script>
import SectionList from '@/components/SectionList';
export default{
name:"Singer",
components:{
SectionList
},
data(){
return{
msg:"我是Singer的小纸条",
sectionObj:{
title:"歌手列表",
arr:[
{id:1,name:"周杰伦"},
{id:2,name:"薛之谦"},
{id:3,name:"陈奕迅"},
{id:4,name:"孙燕姿"}
]
}
}
},
methods: {
changeBrother(){
this.$bus.$emit("changSongMsg","我是你的兄弟singer")
}
}
};
</script>
共用型的知识
mixin 混入
把一些公共的 配置项 单独抽离到一个js文件中 需要使用这些公共配置项的组件 只需要引入mixin就可以了 ===> 配置的复用
// 导出对象
export const mixin = {
// 放 公共的配置项
data(){
return {
msg:"公共的数据"
}
},
methods:{
alertName(){
alert(this.name)
}
},
created(){
console.log("我是大可爱!!!")
}
}
song.vue
<template>
<div id="song">
<p>{{ msg }}</p>
<h2>公司名称:{{ name }}</h2>
<h2>公司地址:{{ address }}</h2>
<button @click="alertName">点我查看名字</button>
</div>
</template>
<script>
// 导入 mixin
import {mixin} from "@/mixin.js";
export default{
name:"Song",
data(){
return{
name:"社会有限责任公司",
address:"三里吨",
}
},
// methods:{
// alterName(){
// alert(this.name)
// }
// }
mixins:[mixin],
}
</script>
singer.vue
<template>
<div id="singer">
<p>{{ msg }}</p>
<h2>美女名称:{{ name }}</h2>
<h2>美女年龄:{{ age }}</h2>
<button @click="alertName">点我查看名字</button>
</div>
</template>
<script>
import {mixin} from "@/mixin.js";
export default{
name:"Singer",
data(){
return{
name:"迪丽热巴",
age: "18"
}
},
// methods: {
// alertName(){
// alert(this.name)
// }
// }
mixins:[mixin],
}
</script>
全局混入
Vue.mixin(mixin) //几乎不用
增强Vue功能 plugin (插件的意思)
- 第一步 创建 plugin.js 或者 plugin/index.js 或者 plugin/plugin.js
// 定义 创建内容 目的: 用于增强 Vue的功能 (全局功能)
export default{
// 接收的第一个形参 是Vue 构造函数
install(Vue){
// 添加全局指令
// Vue.directive(...)
// // 添加全局组件
// Vue.component(...)
// // 添加全局混入
// Vue.mixin(...)
// 添加原型方法
Vue.prototype.$busApi = function(){
console.log("plugin万岁")
}
}
}
- 第二步:在main.js 中导入对象 并使用
// 步写index.js 也可以找到 index.js (默认入口文件)
import plugins from './plugins'
Vue.use(plugins) //写在 挂载之前
触发 install函数运行 全局方法 指令就可以挂载成功了
install 函数 允许传参进来
install(Vue,option){...} option 接收到1
Vue.use(plugins,1,2)
main.js
/*
该文件是整个项目的入口文件
*/
// 引入vue
import Vue from 'vue'
// 引入App组件,它是所有组件的父组件
import App from './App.vue'
// 关闭vue的生产提示
Vue.config.productionTip = false
// 步写index.js 也可以找到 index.js (默认入口文件)
import plugins from './plugins'
Vue.use(plugins)
console.log(plugins)
// 创建Vue实例对象----vm
new Vue({
render: h => h(App),
beforeCreate(){
// 把vm对象 挂载到Vue的原型对象上去 为了方便 各个组件的vc对象能获取vm
Vue.prototype.$bus = this
}
}).$mount('#app')
Singer.vue
<template>
<div id="singer">
<p>{{ msg }}</p>
<h2>美女名称:{{ name }}</h2>
<h2>美女年龄:{{ age }}</h2>
<button @click="alertName">点我查看名字</button>
<button @click="fn">调用原型上的方法</button>
</div>
</template>
<script>
import {mixin} from "@/mixin.js";
export default{
name:"Singer",
data(){
return{
name:"迪丽热巴",
age: "18"
}
},
// methods: {
// alertName(){
// alert(this.name)
// }
// }
mixins:[mixin],
methods:{
fn(){
this.$busApi();
}
}
}
</script>
小tips
项目中 一般把通用型的方法 写在utils文件夹内
如 密码验证函数 数组去重 日期格式化函数 …