// 子组件
// 1.创建子组件
<template>
<div>
<!-- 注意:template中没有this,这里没有this,即不用this.parentMsg -->
我是子组件,父组件的数据:{{parentMsg}}
</div>
</template>
<script>
export default {
data() {
return {
msg: "children",
parentMsg:""
};
},
mounted(){
console.log(this.$parent)
this.parentMsg = this.$parent.msg
}
};
</script>
<style scoped>
</style>
// 父组件
<template>
<!-- 4.使用 -->
<div>
<div class="main">我是父组件,子组件的数据:{{childrenMsg}}</div>
<!-- 单独放在根组件的下一级,保证层级关系不被影响 -->
<p-com></p-com>
</div>
</template>
<script>
// 2.引入
import pCom from "./pCom";
export default {
components: { pCom }, // 3.挂载
data() {
return {
msg: "parent",
childrenMsg: ""
};
},
mounted() {
console.log(this.$children)
this.childrenMsg = this.$children[0].msg;
}
};
</script>
<style scoped>
</style>
两个vue文件放在同一目录下即可运行。
中间需要间插入el组件时,父子关系变为子孙,获取数据失败:
// 父组件模板改为此
<template>
<!-- 4.使用 -->
<div>
<div class="main">我是父组件,子组件的数据:{{childrenMsg}}</div>
<!-- 单独放在根组件的下一级,保证层级关系不被影响 -->
<el-card>
<p-com></p-com>
</el-card>
</div>
</template>
然而插入非element组件(即html标签),则父子关系不变:
// 父组件模板改为此
<template>
<!-- 4.使用 -->
<div>
<div class="main">我是父组件,子组件的数据:{{childrenMsg}}</div>
<!-- 单独放在根组件的下一级,保证层级关系不被影响 -->
<div>
<p-com></p-com>
</div>
</div>
</template>
官方推荐的父子关系通信方式,不用在乎层级关系,而且没有多余数据传输,父组件给什么,子组件就拿什么、用什么。(原理也是监听和广播,后第三种方式一样,只不过这是局部的,别忘了@是v-on的省略)。props应该也可以放函数地址然后子组件调用,如果可以的话也可以替代emit(待测)。
父组件:
<template>
<!-- 4.使用 -->
<div>
父组件:
<div class="main">
我是父组件,我自己拿的子组件数据:{{childrenMsg}}
<el-button type="text" @click="alertBox(true)">点我出弹框</el-button>
<br />
子组件主动给我更新的数据:{{childrenMsg2}}
</div>
<!-- 单独放在根组件的下一级,保证层级关系不被影响 -->
<br />子组件:
<p-com
:parentMsg2="msg"
memo="这是爸爸传给你的字符串"
@alertBox="flag=>{alertBox(flag)}"
@setParentMsg="msg=>{setMsg(msg)}"
></p-com>
</div>
</template>
<script>
// 2.引入
import pCom from "./pCom";
export default {
components: { pCom }, // 3.挂载
data() {
return {
msg: "parent",
childrenMsg: "",
childrenMsg2: ""
};
},
methods: {
alertBox(flag) {
alert(`调用了${flag ? "自己" : "爸爸"}的弹框`);
},
setMsg(msg) {
this.childrenMsg2 = msg;
}
},
mounted() {
console.log(this.$children);
this.childrenMsg = this.$children[1].msg;
}
};
</script>
<style scoped>
</style>
子组件:
<template>
<div>
<!-- 注意:template中没有this,这里没有this,即不用this.parentMsg -->
我是子组件,我自己拿的父组件数据:{{parentMsg}}
<br />
爸爸用props传给我的信息:{{parentMsg2}}
(备注:{{memo}})
<el-button type="text" @click="useParentMethods">点我出弹框,并且修改爸爸的数据</el-button>
</div>
</template>
<script>
export default {
props: ["parentMsg2", "memo"],
data() {
return {
msg: "children",
parentMsg: ""
};
},
methods: {
useParentMethods() {
this.$emit("alertBox", false);
this.$emit("setParentMsg", `看你还嚣张! ${Date().split(' ')[4]}`);
}
},
mounted() {
console.log(this.$parent);
this.parentMsg = this.$parent.msg;
}
};
</script>
<style scoped>
</style>
监听和广播:
优点:使用简单,导入即用。
缺点:阅读性差,因为你只能打印出这个bus(vue实例),看到所有的监听事件,但并不知道事件谁用了,在哪用的,用来干什么,所以仅适合小规模开发,非父子组件交互较少场景,仅用来处理非父子组件间的通信。
使用:
1.封装一个bus
model下新建eventBus.js(一般是这个目录)
内容
import Vue from 'vue'
var EmitData = new Vue();
export default EmitData; // 暴露一个vue实例,统一监听、广播所有事件
2.父组件导入并且监听(非父子组件也如此)
<template>
<!-- 4.使用 -->
<div>
父组件:
<div class="main">
我是父组件,我自己拿的子组件数据:{{childrenMsg}}
<el-button type="text" @click="alertBox(true)">点我出弹框</el-button>
<br />
子组件主动给我更新的数据:{{childrenMsg2}}
<div>广播更新时间:{{time}}</div>
</div>
<!-- 单独放在根组件的下一级,保证层级关系不被影响 -->
<br />子组件:
<p-com
:parentMsg2="msg"
memo="这是爸爸传给你的字符串"
@alertBox="flag=>{alertBox(flag)}"
@setParentMsg="msg=>{setMsg(msg)}"
></p-com>
</div>
</template>
<script>
// 2.引入
import pCom from "./pCom";
import eventBus from "@/model/eventBus.js";
export default {
components: { pCom }, // 3.挂载
data() {
return {
msg: "parent",
childrenMsg: "",
childrenMsg2: "",
time: ""
};
},
methods: {
alertBox(flag) {
alert(`调用了${flag ? "自己" : "爸爸"}的弹框`);
},
setMsg(msg) {
this.childrenMsg2 = msg;
}
},
mounted() {
console.log(this.$children);
this.childrenMsg = this.$children[1].msg;
eventBus.$on("updateTime", time => {
this.time = time;
});
}
};
</script>
<style scoped>
</style>
3.子组件导入并且广播(触发事件)(非父子组件也如此)
<template>
<div>
<!-- 注意:template中没有this,这里没有this,即不用this.parentMsg -->
我是子组件,我自己拿的父组件数据:{{parentMsg}}
<br />
爸爸用props传给我的信息:{{parentMsg2}}
(备注:{{memo}})
<el-button type="text" @click="useParentMethods">点我出弹框,并且修改爸爸的数据</el-button>
<el-button @click="updateParentTime">点我更新时间</el-button>
</div>
</template>
<script>
import eventBus from "@/model/eventBus.js";
export default {
props: ["parentMsg2", "memo"],
data() {
return {
msg: "children",
parentMsg: ""
};
},
methods: {
useParentMethods() {
this.$emit("alertBox", false);
this.$emit("setParentMsg", `看你还嚣张! ${Date().split(' ')[4]}`);
},
updateParentTime(){
eventBus.$emit("updateTime",Date().split(' ')[4])
}
},
mounted() {
console.log(this.$parent);
this.parentMsg = this.$parent.msg;
}
};
</script>
<style scoped>
</style>
移除监听:
eventBus.$off('updateTime', {})
如果在普通的 DOM 元素上使用ref,引用指向的就是 DOM 元素;如果用在element组件上,引用就指向组件vue实例,可以通过实例直接调用组件的方法、数据或 DOM 元素。
所以ref是单向的,父操作子的数据和方法。
使用:
给子组件注入ref=“xx”,之后xx就是这个子组件的实例了(在this.$refs.xx调用时要注意他是最后加载的,一般为了确保加载完成可以使用setTimeout或者this.$nextTick(()=>{this.$refs.xx}))
子组件不用变,父组件增加ref
<template>
<!-- 4.使用 -->
<div>
父组件:
<div class="main">
我是父组件,我自己拿的子组件数据:{{childrenMsg}}
<el-button type="text" @click="alertBox(true)">点我出弹框</el-button>
<br />
子组件主动给我更新的数据:{{childrenMsg2}}
<div>广播更新时间:{{time}}</div>
<el-button @click="getChild()">点我使用refs拿子组件数据和方法</el-button>
</div>
<!-- 单独放在根组件的下一级,保证层级关系不被影响 -->
<br />子组件:
<p-com
:parentMsg2="msg"
memo="这是爸爸传给你的字符串"
@alertBox="flag=>{alertBox(flag)}"
@setParentMsg="msg=>{setMsg(msg)}"
ref="pCom"
></p-com>
</div>
</template>
<script>
// 2.引入
import pCom from "./pCom";
import eventBus from "@/model/eventBus.js";
export default {
components: { pCom }, // 3.挂载
data() {
return {
msg: "parent",
childrenMsg: "",
childrenMsg2: "",
time: ""
};
},
methods: {
alertBox(flag) {
alert(`调用了${flag ? "自己" : "爸爸"}的弹框`);
},
setMsg(msg) {
this.childrenMsg2 = msg;
},
getChild(){
alert("子组件msg:"+this.$refs.pCom.msg+" ;并且更新自己的时间")
this.$refs.pCom.updateParentTime()
}
},
mounted() {
console.log(this.$children);
this.childrenMsg = this.$children[1].msg;
eventBus.$on("updateTime", time => {
this.time = time;
});
}
};
</script>
<style scoped>
</style>
provide/ inject 是vue2.2.0新增的api,在组件多层嵌套的时候,根组件只要通过provide来提供变量, 然后任意子组件(孙组件、重孙等等)中通过inject来注入变量(注入this中)即可拿到根组件数据、方法。不局限于只能从当前父组件的props属性中获取数据、方法。由此可知,provide / reject是单向的,只提供子孙等组件获取根组件数据、方法。
父组件:
<template>
<!-- 4.使用 -->
<div>
父组件:
<div class="main">
我是父组件,我自己拿的子组件数据:{{childrenMsg}}
<el-button type="text" @click="alertBox(true)">点我出弹框</el-button>
<br />
子组件主动给我更新的数据:{{childrenMsg2}}
<div>广播更新时间:{{time}}</div>
<el-button @click="getChild()">点我使用refs拿子组件数据和方法</el-button>
</div>
<!-- 单独放在根组件的下一级,保证层级关系不被影响 -->
<br />子组件:
<p-com
:parentMsg2="msg"
memo="这是爸爸传给你的字符串"
@alertBox="flag=>{alertBox(flag)}"
@setParentMsg="msg=>{setMsg(msg)}"
ref="pCom"
></p-com>
</div>
</template>
<script>
// 2.引入
import pCom from "./pCom";
import eventBus from "@/model/eventBus.js";
export default {
components: { pCom }, // 3.挂载
data() {
return {
msg: "parent",
childrenMsg: "",
childrenMsg2: "",
time: ""
};
},
provide() {
return {
rootMsg: this.msg,
rootAlertBox: this.alertBox
};
},
methods: {
alertBox(flag) {
alert(`调用了${flag ? "自己" : "爸爸"}的弹框`);
},
setMsg(msg) {
this.childrenMsg2 = msg;
},
getChild() {
alert("子组件msg:" + this.$refs.pCom.msg + " ;并且更新自己的时间");
this.$refs.pCom.updateParentTime();
}
},
mounted() {
console.log(this.$children);
this.childrenMsg = this.$children[1].msg;
eventBus.$on("updateTime", time => {
this.time = time;
});
}
};
</script>
<style scoped>
</style>
子组件:
<template>
<div>
<!-- 注意:template中没有this,这里没有this,即不用this.parentMsg -->
我是子组件,我自己拿的父组件数据:{{parentMsg}}
<br />
爸爸用props传给我的信息:{{parentMsg2}}
(备注:{{memo}})
<el-button type="text" @click="useParentMethods">点我出弹框,并且修改爸爸的数据</el-button>
<el-button @click="updateParentTime">点我更新时间</el-button>
<el-button @click="userPJ()">点我看看provide / reject有没有获取到</el-button>
</div>
</template>
<script>
import eventBus from "@/model/eventBus.js";
export default {
props: ["parentMsg2", "memo"],
data() {
return {
msg: "children",
parentMsg: ""
};
},
inject: {
rootMsg: { default: "获取根组件数据失败" },
rootAlertBox: {
default: () => {
return "获取根组件函数失败";
}
}
},
methods: {
useParentMethods() {
this.$emit("alertBox", false);
this.$emit("setParentMsg", `看你还嚣张! ${Date().split(" ")[4]}`);
},
updateParentTime() {
eventBus.$emit("updateTime", Date().split(" ")[4]);
},
userPJ(){
alert(this.rootMsg)
this.rootAlertBox()
}
},
mounted() {
console.log(this.$parent);
this.parentMsg = this.$parent.msg;
}
};
</script>
<style scoped>
</style>
在vue2.4中,引入了$attrs 和$listeners , 新增了inheritAttrs 选项。
inheritAttrs:默认值true,继承所有的父组件属性(除props的特定绑定)作为普通的HTML特性应用在子组件的根元素上,如果你不希望组件的根元素继承特性设置inheritAttrs: false,但是class属性会继承(简单的说,inheritAttrs:true 继承除props之外的所有属性;inheritAttrs:false 只继承class属性)
$attrs–继承所有的父组件属性(除了prop传递的属性、class 和 style ),一般用在子组件的子元素上,如第一个例子的
$listeners–属性,它是一个对象,里面包含了作用在这个组件上的所有监听器,你就可以配合 v-on="$listeners" 将所有的事件监听器指向这个组件的某个特定的子元素。(相当于子组件继承父组件的事件)
实现:
1.同级创建pCom的子组件ppCom
<template>
<el-button @click="runGrand()">点我调用 attrs / listeners</el-button>
</template>
<script>
export default {
methods: {
runGrand() {
alert(this.$attrs.noProp);
this.$listeners.alertBox(false);
}
},
mounted() {
console.log(this.$attrs);
console.log(this.$listeners);
}
};
</script>
2.pCom
<template>
<div>
<!-- 注意:template中没有this,这里没有this,即不用this.parentMsg -->
我是子组件,我自己拿的父组件数据:{{parentMsg}}
<br />
爸爸用props传给我的信息:{{parentMsg2}}
(备注:{{memo}})
<el-button type="text" @click="useParentMethods">点我出弹框,并且修改爸爸的数据</el-button>
<el-button @click="updateParentTime">点我更新时间</el-button>
<el-button @click="userPJ()">点我看看provide / reject有没有获取到</el-button>
<!--
attrs:在模板外需要用this.$attrs获取,他包含了父组件的所有数据
listeners:在模板外需要用this.$listeners获取,他包含了父组件的所有事件(监听)
inheritAttrs - 默认true:
inheritAttrs:true 子标签继承除props之外的所有特性;inheritAttrs:false 只继承class属性
传给下一级,注入到他的this上
-->
<br />
pCom的子组件:
<my-ppcom v-bind="$attrs" v-on="$listeners" ></my-ppcom>
</div>
</template>
<script>
import eventBus from "@/model/eventBus.js";
import ppCom from "./ppCom"
export default {
props: ["parentMsg2", "memo"],
components:{"my-ppcom":ppCom},
data() {
return {
msg: "children",
parentMsg: ""
};
},
inject: {
rootMsg: { default: "获取根组件数据失败" },
rootAlertBox: {
default: () => {
return "获取根组件函数失败";
}
}
},
methods: {
useParentMethods() {
this.$emit("alertBox", false);
this.$emit("setParentMsg", `看你还嚣张! ${Date().split(" ")[4]}`);
},
updateParentTime() {
eventBus.$emit("updateTime", Date().split(" ")[4]);
},
userPJ(){
alert(this.rootMsg)
this.rootAlertBox()
}
},
mounted() {
console.log(this.$parent);
this.parentMsg = this.$parent.msg;
}
};
</script>
<style scoped>
</style>
3.父组件
<template>
<!-- 4.使用 -->
<div>
父组件:
<div class="main">
我是父组件,我自己拿的子组件数据:{{childrenMsg}}
<el-button type="text" @click="alertBox(true)">点我出弹框</el-button>
<br />
子组件主动给我更新的数据:{{childrenMsg2}}
<div>广播更新时间:{{time}}</div>
<el-button @click="getChild()">点我使用refs拿子组件数据和方法</el-button>
</div>
<!-- 单独放在根组件的下一级,保证层级关系不被影响 -->
<br />子组件:
<p-com
:parentMsg2="msg"
memo="这是爸爸传给你的字符串"
@alertBox="flag=>{alertBox(flag)}"
@setParentMsg="msg=>{setMsg(msg)}"
ref="pCom"
noProp="子组件不要prop接受我,这是给孙组件的字符串"
></p-com>
</div>
</template>
<script>
// 2.引入
import pCom from "./pCom";
import eventBus from "@/model/eventBus.js";
export default {
components: { pCom }, // 3.挂载
data() {
return {
msg: "parent",
childrenMsg: "",
childrenMsg2: "",
time: ""
};
},
provide() {
return {
rootMsg: this.msg,
rootAlertBox: this.alertBox
};
},
methods: {
alertBox(flag) {
alert(`调用了${flag ? "自己" : "爸爸"}的弹框`);
},
setMsg(msg) {
this.childrenMsg2 = msg;
},
getChild() {
alert("子组件msg:" + this.$refs.pCom.msg + " ;并且更新自己的时间");
this.$refs.pCom.updateParentTime();
}
},
mounted() {
console.log(this.$children);
this.childrenMsg = this.$children[1].msg;
eventBus.$on("updateTime", time => {
this.time = time;
});
}
};
</script>
<style scoped>
</style>
通信比较简单,数据和状态比较混乱,不太容易维护。
localStorage 将数据存入浏览器,关闭浏览器再次打开仍然存在,需要手动清除;
sessionStorage将数据存在对话框,关闭这个页面就没了。
他们的操作几乎是一样的,也都是js内置方法,也可以自己封装以下(我的另一篇博客有封装localStorage vue项目将token存在(vuex)store和localstorage中)。使用原生的话注意每次存入要JSON格式化,拿出要JSON解析。
localStorage 使用:
在任何地方使用 localStorage.setItem("k", JSON.stringify("v"))存入数据(方法-函数地址);
在任何地方使用 JSON.parse(localStorage.getItem("k")) 读取数据(方法-函数地址);
任何地方包括vue实例以外,因为他是原生js。
数据状态管理器,适用于数据复杂、交互频繁场景,选择时要在不使用vue的困难和使用vuex的繁琐中权衡,当然啦,不管用不用都应该拿个例子来学,见我的另一篇博客
Vue使用Vuex一步步封装并使用store