文章目錄
- 一、$children / $parent
- 二、props / $emit
- 三、eventBus
- 四、ref
- 五、provide / reject
- 六、$attrs / $listeners
- 七、localStorage / sessionStorage
- 八、Vuex
實例以element ui為例。例子從上往下逐漸變復雜(后面例子沒有刪前面的無用代碼,有時間重新整理一下!)
一、$children / $parent
- 拿到 $children / $parent 即可訪問子 / 父 組件的所有變量和方法。
- 官方說 $children / $parent 僅作為應急的通信方法,其中一個原因就是 $children / $parent僅代表的是根組件下的第一個子/父vue組件(普通html標簽不含有vue實例,不包含在內),當位置變化或者之間需要增加新的組件,對應的指向就變了,如以下例子。
- 一般用在組件簡單且不易改變的情形,單獨把子組件放在根組件的下一級。
- 使用實例(復制粘貼即可跑起來看效果):
// 子組件
// 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>
二、props / $emit
官方推薦的父子關系通信方式,不用在乎層級關系,而且沒有多余數據傳輸,父組件給什么,子組件就拿什么、用什么。(原理也是監聽和廣播,后第三種方式一樣,只不過這是局部的,別忘了@是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>
三、eventBus
監聽和廣播:
優點:使用簡單,導入即用。
缺點:閱讀性差,因為你只能打印出這個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', {})
四、ref
如果在普通的 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 / reject
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>
六、$attrs / $listeners
在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
通信比較簡單,數據和狀態比較混亂,不太容易維護。
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。
八、Vuex
數據狀態管理器,適用于數據復雜、交互頻繁場景,選擇時要在不使用vue的困難和使用vuex的繁瑣中權衡,當然啦,不管用不用都應該拿個例子來學,見我的另一篇博客
Vue使用Vuex一步步封裝并使用store