父子組件通信
1、父子組件通過prop傳遞數據
父組件可以將一條數據傳遞給子組件,這條數據可以是動態的,父組件的數據更改的時候,子組件接收的也會變化。
子組件被動的接收父組件的數據,子組件不要再更改這條數據了。
組件實例的作用域是孤立的,父組件不能直接使用子組件的數據,子組件也不能直接使用父組件的數據。
父組件在調用子組件的時候給子組件傳遞數據:
<template id="father">
<div class="father">
<p>我是父組件,這是我的fMsg:{{fMsg}}</p>
<input type = "text" v-model = "fMsg">
<hr>
<son msg = "你好"></son>
</div>
</template>
父組件給子組件傳遞數據的時候,子組件需要利用props的屬性來確定自己的預期數據,如果兒子沒有通過props屬性接受傳遞過來的數據,則數據會以自定義屬性的方式,放在兒子最外層的根元素上面。
子組件通過props來接受父組件傳遞過來的數據,并且通過{{msg}}使用
components:{
son:{
template:"<div>我是son子組件!這是父組件傳遞給我的msg:{{msg}}</div>",
//接收父組件傳遞來的屬性 msg
props:["msg"]
}
}
2、父組件通過v-bind指令傳遞自身變量給子組件
我們可以用 v-bind 來動態地將 prop 綁定到父組件的數據。每當父組件的數據變化時,該變化也會傳導給子組件。
<template id="father">
<div class="father">
<p>我是父組件,這是我的fMsg:{{fMsg}}</p>
<input type = "text" v-model = "fMsg">
<hr>
<!-- <son msg = "你好"></son> -->
<son :msg = "fMsg"></son>
</div>
</template>
如果如果父組件傳遞屬性給子組件的時候鍵名有'-'
<son :f-msg = "fMsg"></son>
子組件接收、使用的時候寫成小駝峰的模式
components:{
son:{
template:"<div>我是son子組件!這是父組件傳遞給我的msg:{{fMsg}}</div>",
//接收父組件傳遞來的屬性 msg
props:["fMsg"]
}
}
3、prop驗證傳遞過來的數據
我們可以為組件的 prop 指定驗證規則。如果傳入的數據不符合要求,Vue 會發出警告。這對于開發給他人使用的組件非常有用
驗證主要分為:類型驗證、必傳驗證、默認值設置、自定義驗證
類型驗證
// 類型驗證
// num:Number //父組件傳遞過來的num必須是Number類型
// 添加多個類型
num: [Number,String],
規定傳遞過來的數值類型必須是number,否則系統會報錯
必傳驗證
規定父組件必須給子組件傳遞該值
//必傳驗證
// num:{
// required: true
// }
默認值設置
當父組件不給子組件傳遞該值的時候,給子組件設定一個默認值
// 默認值設置
// num:{
// default:100
// }
自定義驗證
//自定義驗證
num:{
validator(val){
return val > 100
}
}
規定傳遞過來的數值必須要大于100,否則系統會報錯
4、父子組件依靠應用類型的地址傳遞共享數據
單向數據流
Prop 是單向綁定的:當父組件的屬性變化時,將傳遞給子組件,但是反過來不會。這是為了防止子組件無意間修改了父組件的狀態,來避免應用的數據流變得難以理解。
<template id="father">
<div class="father">
<input type = "text" v-model = "message">
<hr>
<son :message = "message"></son>
</div>
</template>
<template id = "son">
<div>
<p>這是子組件</p>
<input type = "text" v-model = "message"></input>
</div>
</template>
另外,每次父組件更新時,子組件的所有 prop 都會更新為最新值。這意味著你不應該在子組件內部改變 prop。如果你這么做了,Vue 會在控制臺給出警告。
所以如果我們想實現父子間的數據共享,依靠的就是應用類型的地址傳遞,應將message寫成對象的形式,傳遞的時候將對象傳遞給子組件,子組件引用的時候使用對象的value值。
<template id="father">
<div class="father">
<input type = "text" v-model = "message.value">
<hr>
<!-- 傳遞的時候將對象傳遞給子組件 -->
<son :message = "message"></son>
</div>
</template>
<template id = "son">
<div>
<p>這是子組件</p>
<!-- 引用的時候使用對象的value值 -->
<input type = "text" v-model = "message.value"></input>
</div>
</template>
這時候更改父組件的value值,子組件的數據同步更改,子組件修改value值的時候也同步修改了父組件的數據。這是因為不管是子組件還是父組件,我們操作的都是同一個對象,父組件直接把引用類型的地址傳遞給子組件,子組件沒有直接修改對象,只是更改了里面的屬性值。
父組件如果將一個引用類型的動態數據傳遞給子組件的時候,數據會變成雙向控制的,子組件改數據的時候父組件也能接收到數據變化,因為子組件改的時候不是在改數據(地址),而是在改數據里的內容,也就是說引用類型數據的地址始終沒有變化,不算改父組件數據。
注意:在 JavaScript 中對象和數組是引用類型,指向同一個內存空間,如果 prop 是一個對象或數組,在子組件內部改變它會影響父組件的狀態。 message:{val:""}
5、viewmodel關系鏈
在組件間可以用過ref形成ref鏈,組件還擁有一個關系鏈($parent),通過這兩種鏈;理論來說,任意的兩個組件都可以互相訪問,互相進行通信。
$parent:父組件
$children:子組件
$root:根組件
當子組件在set方法中修改父組件傳遞過來的值時,系統會報錯,因為子組件不能修改父組件的數據。
Vue.component("bbb",{
template:"#bbb",
props:["msg"],
computed:{
/* ownMessage(){
return this.msg;
} */
ownMessage:{
get(){
return this.msg;
},
set(val){
this.msg = val //系統報錯:子組件不能更改父組件傳遞的數據
}
}
}
})
所以這時候要使用$parent,讓父組件自己更改自己的數據
set(val){
// this.msg = val //系統報錯:子組件不能更改父組件傳遞的數據
// console.log(this)
// 相當于父組件自己更改了msg數據
this.$parent.msg = val;
}
6、父組件通過ref標記獲取子組件的數據
父組件在調用子組件的時候使用ref做標記
<template id="aaa">
<div>
<button @click = "get">點擊獲取bbb數據</button>
<!-- 組件間不僅可以用過$root/$parent/$children來獲取對應關系的組件,父組件還可以主動的通過ref為子組件做標記 -->
<bbb ref = "b"></bbb>
</div>
</template>
父組件的this屬性上有$refs標記,通過refs標記拿到子組件
// 通過ref標記更改子組件的數據
// this.$refs.b.message = "哈哈"
組件間不僅可以用過$parent/children/root來獲取對應關系的組件,父組件還可以主動的通過ref為子組件做標記 也可以給dom做標記,也會形成ref鏈,也可以交互.
<button ref="btn" @click="get">get</button>
<bbb ref="b></bbb>
注意多個子組件標記的是同一個鍵名,獲取到的應該是一個數組
<bbb ref = "b" v-for = "(item,index) in 3" :key = "index"></bbb>
// 通過下標修改對應的數值
this.$refs.b[0].message = "哈哈"
運行效果:
子父組件通信
1、子組件通過父組件傳遞的方法來更改父組件的數據
父組件可以將更改自身數據的方法傳遞給子組件,子組件調用這個方法的時候,就可以給父組件傳遞數據,父組件被動的接收子組件的數據。
子組件聲明一條自身的msg
Vue.component("son",{
template:"#son",
// 子組件接收父組件傳遞過來的方法
props:["change"],
data(){
return{
msg:"我是子組件"
}
}
})
父組件先聲明一條自己的數據
data(){
return{
// 父組件先聲明一條自己的數據
parentMsg:""
}
}
再寫一個可以更改自身數據的方法
methods:{
// 寫一個可以更改自身數據的方法
change(msg){
this.parentMsg = msg
}
}
將寫好的change方法傳遞給子組件
<template id="father">
<div>
<p>這是父組件</p>
<p>子組件傳遞過來的值是:{{parentMsg}}</p>
<hr>
<!-- 調用子組件的時候,將更改自身數據的方法傳遞給子組件 -->
<son :change = "change"></son>
</div>
</template>
子組件通過props接收父組件傳遞過來的change方法
props:["change"]
給p標簽添加點擊事件,點擊即觸發change方法,同時將自身的msg傳遞給父組件,相當于父組件的change方法被執行。
<template id="son">
<div>
<p>子組件說:{{msg}}</p>
<p @click = "change(msg)">點擊我觸發父親的change方法</p>
</div>
</template>
父組件可以在頁面中渲染子組件傳遞過來的數據
<p>子組件傳遞過來的值是:{{parentMsg}}</p>
運行效果:
2、通過自定義事件實現子父通信
每一個組件或者實例都會有自定義事件,和觸發事件的能力,父組件給子組件綁定一個自定義事件,這個事件的處理程序卻是父組件的一個方法,當子組件觸發這個事件的時候,相當于父組件的方法被執行。
父組件想獲取子組件的數據時,在調用子組件的時候給子組件綁定一個自定義事件change-event
<template id="father">
<div>
<p>這是父組件</p>
<p>子組件傳遞過來的值是:{{parentMsg}}</p>
<hr>
<!-- 給子組件綁定一個自定義事件 -->
<son @change-event = "change"></son>
</div>
</template>
在子組件中定義一個點擊事件,點擊p標簽執行changeWord方法
<p @click = "changeWord">點擊我觸發父親的change方法</p>
在方法中編寫changeWord方法,通過this.$emit來觸發綁定在自己身上的自定義事件,第一個參數為事件名稱change-event,第二個參數為觸發這個函數的時候給他傳遞的數值:自身的msg。
methods:{
changeWord(){
//觸發自身綁定的change事件
this.$emit("change-event",this.msg)//第一個參數為觸發事件的名字,第二個參數為觸發這個函數的時候給他傳遞的數值
}
}
一旦觸發綁定在自身上的自定義事件,相當于父組件的change方法被執行。
兄弟組件通信
1、通過viewmodel關系鏈
定義哥哥組件,給哥哥組件添加一個點擊事件,點擊觸發hitLittle方法
<template id = "big-brother">
<div>
<p>我是哥哥</p>
<button @click = "hitLittle">打弟弟</button>
</div>
</template>
定義弟弟組件,給弟弟組件添加一個p標簽,由crying數據控制其顯示與隱藏
<template id="little-brother">
<div>
<p>我是弟弟</p>
<p v-if = "crying">嗚嗚嗚</p>
</div>
</template>
在弟弟組件的data中聲明crying數據,默認為false
Vue.component("little-brother",{
template:"#little-brother",
data(){
return{
crying:false
}
}
})
在哥哥組件的methods中定義hitLittle方法,通過viewmodel關系鏈更改弟弟組件中的crying方法
Vue.component("big-brother",{
template:"#big-brother",
methods:{
hitLittle(){
//在兄弟組件之間的通信,可以采用關系鏈和ref鏈去使用,解決兄弟之間通信問題。
this.$parent.$children[1].crying = true;//讓littel改變自身的crying狀態
}
}
})
運行效果:
2、viewmodel關系鏈+ref鏈
在弟弟組件中添加ref標記
<little-brother ref = "little"></little-brother>
在哥哥組件的hitLittle方法中通過viewmodel和ref鏈配合使用更改弟弟組件中的crying數據
hitLittle(){
//在兄弟組件之間的通信,可以采用關系鏈和ref鏈去使用,解決兄弟之間通信問題。
// this.$parent.$children[1].crying = true;//讓littel改變自身的crying狀態
//viewmodel鏈和ref鏈配合使用
this.$parent.$refs.little.crying = true;
}
3、eventbus事件總線
創建一個空的實例
var angle = new Vue();
弟弟組件自己定義一個更改自身狀態的方法
methods:{
cry(){
this.crying = true
}
}
在mounted生命周期函數中綁定一個自定義事件,第一個參數為自定義事件名,第二個函數為需要處理的函數
mounted(){
// 綁定一個自定義事件,第一個參數為自定義事件名,第二個函數為需要處理的函數
angle.$on("hit-little",this.cry)
}
在哥哥組件中觸發自定義事件
hitLittle(){
//觸發little-brother組件的hit-little事件
angle.$emit("hit-little")
}
4、vuex狀態管理
vuex是vue提供的一個全局的狀態管理工具,主要處理項目中多組件間狀態共享。
Vuex是vue官方的一款狀態管理工具,什么是狀態呢?我們在前端開發中有一個概念:數據驅動,頁面中任意的顯示不同,都應該有一條數據來控制,而這條數據又叫做state,狀態。
在vue中。組件間進行數據傳遞、通信很頻繁,而父子組件和非父子組件的通信功能也比較完善,但是,唯一困難的就是多組件間的數據共享,這個問題由vuex來處理
(1)創建store
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
(2)設置state
state就是一個純對象,上面有一些狀態掛載
state: {
num:0,
name:"list"
}
(3)在根實例里配置store
這樣,我們就可以在任意的組件中通過this.$store來使用關于store的api
import store from './store/index'
Vue.config.productionTip = false
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
(4)在Home組件中使用state
computed:{
...mapState({
num:state=>state.num
})
},
(5)使用mutations更改state
mutations也是一個純對象,里面包含很多更改state 的方法,這些方法的形參接收到state,在函數體里更改,這時,組件用到的數據也會更改,實現響應式。
mutations: {
changeNum(state){
state.num++
}
}
(6)在組件中調用mutations方法,更改組件中的state。
//使用vuex提供的mapMutations幫助我們在組件中調用mutations方法
...mapMutations(["changeNum"]),
//給按鈕添加點擊事件
<button @click = "changeNum">點擊更改num值</button>
運行效果: