默認狀況下數據不能在組件之間共享,可是能夠經過如下幾種方法實現組件之間的數據傳遞:vue
props
父子組件之間的數據傳遞:vuex
父傳子數組
父綁定數據,子接收數據緩存
詳細的格式:安全
Props:{
數據項名字:{
type:類型。指明從父組件中傳遞過來的數據必須是什麼類型。它的取值是:Object,Array,String,Number,Boolean 都是構造器。不要寫成字符串
default://默認值。當父組件沒有傳數據時,就用這個值
required:true/false 。是否必須必定要傳遞過來
}
}
複製代碼
子傳父bash
this.$emit
發出這個事件,發出事件的同時能夠攜帶數據 (this.$emit("事件名",附加的數據))父傳子傳孫,只能一級一級的傳,不能跨級傳app
示例:dom
<body>
<!-- ******************************************************************** -->
<!-- 父傳子 -->
<!-- <div id="app">
<h1>父組件 ->數據:{{num}}</h1>
<hr>
<son :sonnum="num" :sonname="name"></son>
</div>
<template id="son">
<div>
子組件 -> 數據:{{mysum}} -> {{sonname}}
<button @click="sonnum=200">修改數據爲200</button>
<h2>{{mysum}}</h2>
<button @click="mysum=200">修改數據爲200</button>
</div>
</template> -->
<!-- ******************************************************************** -->
<!-- 子傳父 -->
<div id="app">
<h1>父組件</h1>
<son @submitmsg="addmsg"></son>
<h2>{{a}}</h2>
</div>
<template id="son">
<div>
<h3>子組件</h3>
<button @click="fashe">發射</button>
</div>
</template>
<script>
// 子傳父************************************************************************
let Son = {
template: "#son",
data() {
return {
num:111,
}
},
methods: {
fashe() {
this.$emit("submitmsg", this.num)
}
}
}
let vm = new Vue({
el: "#app",
data: {
a: 0,
},
methods: {
addmsg(info) {
this.a = info
}
},
components: {
Son,
}
})
// 父傳子*************************************************************************
// let Son = {
// template: "#son",
// data() {
// return {
// mysum: this.sonnum,
// }
// },
// props: {
// sonnum: Number,
// sonname: {
// type: String,
// // required:true,
// default: "jun",
// }
// },
// methods: {
// }
// }
// let vm = new Vue({
// el: "#app",
// data: {
// num: 100,
// name: "fan"
// },
// methods: {
// },
// components: {
// Son,
// }
// })
//********************************************************************************
</script>
</body>
複製代碼
這麼多東西,相信你也懶得看,你能夠本身建一個文件,複製到裏面測試一下異步
$attrs
若是想要把父組件的數據傳遞給子子組件,若是使用props
綁定來進行信息的傳遞,顯然是比較繁瑣的
爲了解決該需求,引入了 $attrside
$attrs
能夠收集父組件中的全部傳過來的屬性除了那些在組件中沒有經過 props 定義的。
示例:
首先有三個組件A-B-C,而後想A中的屬性傳入C中,基本的作法是這樣的,一層一層經過 props 往下傳遞
<template>
<div id="app">
A{{msg}}
<component-b :msg="msg"></component-b>
</div>
</template>
<script>
let vm = new Vue({
el: "#app",
data: {
msg: "100"
},
components: {
ComponentB: {
props: ["msg"],
template: `<div>B<component-c :msg="msg"></component-c></div>`,
components: {
ComponentC: {
props: ["msg"],
template: "<div>C{{msg}}</div>"
}
}
}
}
});
</script>
複製代碼
ComponentB 組件中並無使用到父組件傳遞過來的屬性 msg,可是這樣寫就是想把屬性再傳遞給ComponentC,那麼除了這種寫法還能夠給ComponentC綁定$attrs
屬性。
<script>
let vm = new Vue({
el: "#app",
data: {
msg: "100"
},
components: {
ComponentB: {
inheritAttrs: false,
template: `<div>B<component-c v-bind="$attrs"></component-c></div>`,
components: {
ComponentC: {
props: ["msg"],
template: "<div>C{{msg}}</div>"
}
}
}
}
});
</script>
複製代碼
這樣就能夠很方便的作到數據傳遞,使用起來也比較簡單,避免多寫 props
的痛苦
通常在使用 $attrs
時,都會加上 inheritAttrs:false
它的做用就是沒有用到的數據,就不會顯示在DOM結構上
$listeners
說完了 $attrs
,知道了怎麼把數據從A傳遞給C,那麼此時咱們又想到了一個問題,怎麼把C組件的信息同步到A組件呢? 這時就用到了 $listeners
當組件的根元素不具有一些DOM事件,可是根元素內部元素具有相對應的DOM事件,那麼能夠使用 $listeners
獲取父組件傳遞進來的全部事件函數,再經過v-on="xxx"綁定到相對應的內部元素上便可。 簡單的來講,就是父組件向子組件傳遞的全部方法都存在在$listeners
中
有時候咱們會使用 .native
修飾符把原生事件綁定到組件上,可是這樣存在弊端,若是組件的根元素不能使用 某事件時,這個綁定就會失效,並且還不容易控制它的事件範圍,因此咱們通常不用這個修飾符
示例:
//父組件
<template>
<div>
ParentPage
<button @click="handleClick">ParentClick</button>
<Child @customClick="handleClick" />
</div>
</template>
<script>
import Child from "./Child";
export default {
name: "ParentPage",
components: {
Child
},
methods: {
handleClick() {
alert("hello");
}
}
};
</script>
複製代碼
//子組件
<template>
<div>
ChildPage
<!-- <button @click="$emit('customClick')">ChildClick</button> -->
<button @click="$listeners.customClick">ChildClick</button>
</div>
</template>
<script>
import SubChild from "./SubChild.vue";
export default {
name: "ChildPage",
components: {
SubChild
},
data() {
return {};
}
};
</script>
複製代碼
當多層組件引用時,子組件傳遞父組件方法 v-on="$listeners"
至子子組件
// 子組件
<template>
<div>
ChildPage
<!-- <button @click="$emit('customClick')">ChildClick</button> -->
<button @click="$listeners.customClick">ChildClick</button>
<SubChild v-on="$listeners" />
</div>
</template>
<script>
import SubChild from "./SubChild.vue";
export default {
name: "ChildPage",
components: {
SubChild
},
data() {
return {};
}
};
</script>
複製代碼
// 子子組件
<template>
<div>
SubChildPage
<button @click="$listeners.customClick">SubChildClick</button>
</div>
</template>
<script>
export default {
name: "SubChildPage",
data() {
return {};
}
};
</script>
複製代碼
相信若是好好看了以上的代碼,你就會理解的
$emit
1.父組件可使用props
把數據傳給子組件
2.子組件可使用 $emit
觸發父組件的自定義事件
vm.$emit( event, arg ) //觸發當前實例上的事件
vm.$on( event, fn );//監聽event事件後運行 fn;
複製代碼
示例:
//父組件
<template>
<div>
<div>$emit子組件調用父組件的方法並傳遞數據</div>
<h1>父組件數據:{{msg}}</h1>
<emit-ch @updateInfo="updateInfo" :sendData="msg"></emit-ch>
</div>
</template>
<script>
import emitCh from "./$emitCh";
export default {
name: "emitFa",
components: { emitCh },
data() {
return {
msg: "北京"
};
},
methods: {
updateInfo(data) {
// 點擊子組件按鈕時觸發事件
console.log(data);
this.msg = data.city; // 改變了父組件的值
}
}
};
</script>
複製代碼
<template>
<div class="train-city">
<h3>父組件傳給子組件的數據:{{sendData}}</h3>
<br />
<button @click="select()">點擊子組件</button>
</div>
</template>
<script>
export default {
name: "emitCh", // 至關於一個全局 ID,能夠不寫,寫了能夠提供更好的調試信息
props: ["sendData"], // 用來接收父組件傳給子組件的數據
data() {
return {};
},
computed: {},
methods: {
select() {
let data = {
city: "杭州"
};
this.$emit("updateInfo", data); // select事件觸發後,自動觸發updateInfo事件
}
}
};
</script>
複製代碼
整體來講,就是用子組件觸發父組件裏的方法,子組件裏面this.$emit
裏的this
,就指的是父組件
$refs
$refs
的使用方法就是在元素或組件標籤上添加ref
屬性指定一個引用信息,引用信息將會註冊在父組件的$refs
對象上,在js中使用$refs
來指向DOM元素或組件實例;
首先給你的子組件作標記:
<firstchild ref="test"></firstchild>
而後在父組件中,經過 this.$refs.test
就能夠訪問這個子組件,包括訪問子組件裏的 data 裏的數據,而且還能夠調用它的函數
$parent
與$children
說了上面的 $refs
接下來講說 $parent
與$children
this.$parent
能夠查找當前組件的父組件。this.$children
能夠查找當前組件的直接子組件,能夠遍歷所有子組件, 須要注意 $children
並不保證順序,也不是響應式的。固然你也可使用 this.$root
來查找根組件,並能夠配合$children
遍歷所有組件。
注:這兩個都是不限制距離的,就是說能夠直接查找到最外層數據或者最內層數據,固然,若是你能很清楚的知道子組件的順序,你也能夠用下標來操做
示例:
//父組件
<template>
<div class="game">
<h2>{{ msg }}</h2>
<LOL ref="lol"></LOL>
<DNF ref="dnf"></DNF>
</div>
</template>
<script>
import LOL from "@/components/game/LOL";
import DNF from "@/components/game/DNF";
export default {
name: "game",
components: {
LOL,
DNF
},
data() {
return {
msg: "Game",
lolMsg: "Game->LOL",
dnfMsg: "Game->DNF"
};
},
methods: {},
mounted() {
//注意 mounted
//讀取子組件數據,注意$children子組件的排序是不安全的
console.log(this.$children[0].gameMsg); //LOL->Game
//讀取命名子組件數據
console.log(this.$refs.dnf.gameMsg); //DNF->Game
//從根組件查找組件數據
console.log(this.$root.$children[0].msg); //APP
console.log(this.$root.$children[0].$children[0].msg); //Game
console.log(this.$root.$children[0].$children[0].$children[0].msg); //Game->LOL
console.log(this.$root.$children[0].$children[0].$children[1].msg); //Game->DNF
}
};
</script>
複製代碼
//子組件LOL
<template>
<div class="lol">
<h2>{{ msg }}</h2>
</div>
</template>
<script>
export default {
name: "LOL",
data() {
return {
msg: "LOL",
gameMsg: "LOL->Game"
};
},
methods: {},
created() {
//直接讀取父組件數據
this.msg = this.$parent.lolMsg;
}
};
</script>
複製代碼
//子組件DNF
<template>
<div class="dnf">
<h2>{{ msg }}</h2>
</div>
</template>
<script>
import Bus from "../../utils/bus.js";
export default {
name: "DNF",
data() {
return {
msg: "DNF",
gameMsg: "DNF->Game"
};
},
methods: {},
created() {
//從根組件向下查找父組件數據
this.msg = this.$root.$children[0].$children[0].dnfMsg;
//this.msg = this.$children.dnfMsg;
}
};
</script>
複製代碼
上面的有些是使用下標的,固然也能夠不使用下標,直接 $parent
獲取父組件的實例 $children
獲取全部的子組件
provide
與inject
provide
和inject
使用場景也是組件傳值,尤爲是祖父組件--子子組件等有跨度的組件間傳值,單向傳值(由provide
的組件傳遞給inject
的組件)。 不推薦使用
示例:
//父組件
<template>
<div>
<father-dom>
</father-dom>
</div>
</template>
<script>
import sonDom from "./sonDom.vue";
export default {
provide: {
fooNew: "bar"
},
data() {
return {};
},
components: { sonDom },
methods: {}
};
</script>
複製代碼
//子組件
<template>
<div>
<child-dom></child-dom>
</div>
</template>
<script>
import childDom from "./childDom.vue";
export default {
name: "son-dom",
components: { childDom }
};
</script>
複製代碼
//子子組件
<template>
<div>
<p>fooNew:{{fooNew}}</p>
</div>
</template>
<script>
export default {
name: "childDom",
inject: ["fooNew"],
methods: {}
};
</script>
複製代碼
Vuex
接下來講一下咱們的壓軸好戲,最 6 的一種數據傳遞方式:Vuex
當咱們的應用遇到多個組件共享狀態時,單向數據流的簡潔性很容易被破壞。
對於問題一,傳參的方法對於多層嵌套的組件將會很是繁瑣,而且對於兄弟組件間的狀態傳遞無能爲力。對於問題二,咱們常常會採用父子組件直接引用或者經過事件來變動和同步狀態的多份拷貝。以上的這些模式很是脆弱,一般會致使沒法維護的代碼。
所以,咱們爲何不把組件的共享狀態抽取出來,以一個全局單例模式管理呢?在這種模式下,咱們的組件樹構成了一個巨大的「視圖」,無論在樹的哪一個位置,任何組件都能獲取狀態或者觸發行爲!
另外,經過定義和隔離狀態管理中的各類概念並強制遵照必定的規則,咱們的代碼將會變得更結構化且易維護。
Vuex
//獲取state
this.$store.state.count
//vuex的輔助方法
import { mapState } from 'vuex'
computed:mapState([
'count'
])
複製代碼
//直接使用
this.$store.getters.doneTodosCount
//使用輔助方法
import { mapGetters } from 'vuex'
computed:mapGetters({
doneCount: 'doneTodosCount'
})
複製代碼
//觸發mutations
this.$store.commit('xxx')
//輔助函數
import { mapMutations } from 'vuex'
methods:mapMutations(['increment' ])
複製代碼
我的理解以下:
若是有異步的複雜邏輯而且能夠重複調用就使用Action。
//觸發action
store.dispatch('increment')
//輔助函數
import { mapActions } from 'vuex'
methods:mapActions(['increment' ])
複製代碼
爲了解決以上問題,Vuex 容許咱們將 store 分割成模塊(module)。每一個模塊擁有本身的 state、mutation、action、getter、甚至是嵌套子模塊——從上至下進行一樣方式的分割。
const moduleA = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: { ... },
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})
複製代碼
相信看了本篇文章,你確定會對組件之間的數據傳遞和通訊有了更深的理解