vue是資料驅動檢視更新的框架, 我們平時開發,都會把頁面不同模組拆分成一個一個vue元件, 所以對於vue來說元件間的資料通訊非常重要,那麼元件之間如何進行資料通訊的呢?
首先我們需要知道在vue中元件之間存在什麼樣的關係, 才更容易理解他們的通訊方式。
一般我們分為如下關係:
父子元件之間通訊
非父子元件之間通訊(兄弟元件、隔代關係元件、跨層級元件等)
Vue2.x 元件通訊共有12種
父子元件通訊可以用:
兄弟元件通訊可以用:
跨層級元件通訊可以用:
下面把每一種元件通訊方式的寫法一一列出
父元件向子元件傳送資料,這應該是最常用的方式了
子元件接收到資料之後,不能直接修改父元件的資料。會報錯,所以當父元件重新渲染時,資料會被覆蓋。如果子元件內要修改的話推薦使用 computed
格式:
// 陣列:不建議使用
props:[]
// 物件
props:{
inpVal:{
type:Number, //傳入值限定型別
// type 值可為String,Number,Boolean,Array,Object,Date,Function,Symbol
// type 還可以是一個自定義的建構函式,並且通過 instanceof 來進行檢查確認
required: true, //是否必傳
default:200, //預設值,物件或陣列預設值必須從一個工廠函數獲取如 default:()=>[]
validator:(value) {
// 這個值必須匹配下列字串中的一個
return ['success', 'warning', 'danger'].indexOf(value) !== -1
}
}
}
事例:
// Parent.vue 傳送
<template>
<child :msg="msg"></child>
</template>
// Child.vue 接收
export default {
// 寫法一 用陣列接收
props:['msg'],
// 寫法二 用物件接收,可以限定接收的資料型別、設定預設值、驗證等
props:{
msg:{
type:String,
default:'這是預設資料'
}
},
mounted(){
console.log(this.msg)
},
}
注意:
prop 只可以從上一級元件傳遞到下一級元件(父子元件),即所謂的單向資料流。而且 prop 唯讀,不可被修改,所有修改都會失效並警告。
子傳父的方法,子元件通過派發事件的方式給父元件資料,或者觸發父元件更新等操作
// Child.vue 派發
export default {
data(){
return { msg: "這是發給父元件的資訊" }
},
methods: {
handleClick(){
this.$emit("sendMsg",this.msg)
}
},
}
// Parent.vue 響應
<template>
<child v-on:sendMsg="getChildMsg"></child>
// 或 簡寫
<child @sendMsg="getChildMsg"></child>
</template>
export default {
methods:{
getChildMsg(msg){
console.log(msg) // 這是父元件接收到的訊息
}
}
}
和 .sync 類似,可以實現將父元件傳給子元件的資料為雙向繫結,子元件通過 $emit 修改父元件的資料
// Parent.vue
<template>
<child v-model="value"></child>
</template>
<script>
export default {
data(){
return {
value:1
}
}
}
// Child.vue
<template>
<input :value="value" @input="handlerChange">
</template>
export default {
props:["value"],
// 可以修改事件名,預設為 input
model:{
// prop:'value', // 上面傳的是value這裡可以不寫,如果屬性名不是value就要寫
event:"updateValue"
},
methods:{
handlerChange(e){
this.$emit("input", e.target.value)
// 如果有上面的重新命名就是這樣
this.$emit("updateValue", e.target.value)
}
}
}
</script>
ref 如果在普通的DOM元素上,參照指向的就是該DOM元素;
如果在子元件上,參照的指向就是子元件範例,然後父元件就可以通過 ref 主動獲取子元件的屬性或者呼叫子元件的方法
// Child.vue
export default {
data(){
return {
name:"RDIF"
}
},
methods:{
someMethod(msg){
console.log(msg)
}
}
}
// Parent.vue
<template>
<child ref="child"></child>
</template>
<script>
export default {
mounted(){
const child = this.$refs.child
console.log(child.name) // RDIF
child.someMethod("呼叫了子元件的方法")
}
}
</script>
可以幫我們實現父元件向子元件傳遞的資料 的雙向繫結,所以子元件接收到資料後可以直接修改,並且會同時修改父元件的資料
// Parent.vue
<template>
<child :page.sync="page"></child>
</template>
<script>
export default {
data(){
return {
page:1
}
}
}
// Child.vue
export default {
props:["page"],
computed(){
// 當我們在子元件裡修改 currentPage 時,父元件的 page 也會隨之改變
currentPage {
get(){
return this.page
},
set(newVal){
this.$emit("update:page", newVal)
}
}
}
}
</script>
多層巢狀元件傳遞資料時,如果只是傳遞資料,而不做中間處理的話就可以用這個,比如父元件向孫子元件傳遞資料時。
$attrs
:包含父作用域裡除 class 和 style 除外的非 props 屬性集合。通過 this.$attrs 獲取父作用域中所有符合條件的屬性集合,然後還要繼續傳給子元件內部的其他元件,就可以通過 v-bind="$attrs"。
場景:如果父傳子有很多值,那麼在子元件需要定義多個 props
解決:$attrs獲取子傳父中未在 props 定義的值
// 父元件
<home title="這是標題" width="80" height="80" imgUrl="imgUrl"/>
// 子元件
mounted() {
console.log(this.$attrs) //{title: "這是標題", width: "80", height: "80", imgUrl: "imgUrl"}
},
相對應的如果子元件定義了 props,列印的值就是剔除定義的屬性。
props: {
width: {
type: String,
default: ''
}
},
mounted() {
console.log(this.$attrs) //{title: "這是標題", height: "80", imgUrl: "imgUrl"}
},
$listeners
:包含父作用域裡 .native 除外的監聽事件集合。如果還要繼續傳給子元件內部的其他元件,就可以通過 v-on="$linteners"。
場景:子元件需要呼叫父元件的方法
解決:父元件的方法可以通過 v-on="$listeners" 傳入內部元件——在建立更高層次的元件時非常有用
// 父元件
<home @change="change"/>
// 子元件
mounted() {
console.log(this.$listeners) //即可拿到 change 事件
}
如果是孫元件要存取父元件的屬性和呼叫方法,直接一級一級傳下去就可以。
$children
:獲取到一個包含所有子元件(不包含孫子元件)的 VueComponent 物件陣列,可以直接拿到子元件中所有資料和方法等。
$parent
:獲取到一個父節點的 VueComponent 物件,同樣包含父節點中所有資料和方法等
// Parent.vue
export default{
mounted(){
this.$children[0].someMethod() // 呼叫第一個子元件的方法
this.$children[0].name // 獲取第一個子元件中的屬性
}
}
// Child.vue
export default{
mounted(){
this.$parent.someMethod() // 呼叫父元件的方法
this.$parent.name // 獲取父元件中的屬性
}
}
$children
和$parent
並不保證順序,也不是響應式的,只能拿到一級父元件和子元件。
provide / inject 為依賴注入,主要為高階外掛/元件庫提供用例。說是不推薦直接用於應用程式程式碼中,但是在一些外掛或元件庫裡卻是被常用,所以我覺得用也沒啥,還挺好用的。
provide
:可以讓我們指定想要提供給後代元件的資料或方法
inject
:在任何後代元件中接收想要新增在這個元件上的資料或方法,不管元件巢狀多深都可以直接拿來用
要注意的是 provide 和 inject 傳遞的資料不是響應式的,也就是說用 inject 接收來資料後,provide 裡的資料改變了,後代元件中的資料不會改變,除非傳入的就是一個可監聽的物件
所以建議還是傳遞一些常數或者方法
// 父元件
export default{
// 方法一 不能獲取 this.xxx,只能傳寫死的
provide:{
name:"RDIF",
},
// 方法二 可以獲取 this.xxx
provide(){
return {
name:"RDIF",
msg: this.msg // data 中的屬性
someMethod:this.someMethod // methods 中的方法
}
},
methods:{
someMethod(){
console.log("這是注入的方法")
}
}
}
// 後代元件
export default{
inject:["name","msg","someMethod"],
mounted(){
console.log(this.msg) // 這裡拿到的屬性不是響應式的,如果需要拿到最新的,可以在下面的方法中返回
this.someMethod()
}
}
EventBus 是中央事件匯流排,不管是父子元件,兄弟元件,跨層級元件等都可以使用它完成通訊操作。
定義方式有三種:
// 方法一
// 抽離成一個單獨的 js 檔案 Bus.js ,然後在需要的地方引入
// Bus.js
import Vue from "vue"
export default new Vue()
// 方法二 直接掛載到全域性
// main.js
import Vue from "vue"
Vue.prototype.$bus = new Vue()
// 方法三 注入到 Vue 根物件上
// main.js
import Vue from "vue"
new Vue({
el:"#app",
data:{
Bus: new Vue()
}
})
使用如下,以方法一按需引入為例:
// 在需要向外部傳送自定義事件的元件內
<template>
<button @click="handlerClick">按鈕</button>
</template>
import Bus from "./Bus.js"
export default{
methods:{
handlerClick(){
// 自定義事件名 sendMsg
Bus.$emit("sendMsg", "這是要向外部傳送的資料")
}
}
}
// 在需要接收外部事件的元件內
import Bus from "./Bus.js"
export default{
mounted(){
// 監聽事件的觸發
Bus.$on("sendMsg", data => {
console.log("這是接收到的資料:", data)
})
},
beforeDestroy(){
// 取消監聽
Bus.$off("sendMsg")
}
}
以方法二直接掛載在全域性:
// 在 main.js
Vue.prototype.$eventBus=new Vue()
// 傳值元件
this.$eventBus.$emit('eventTarget','這是eventTarget傳過來的值')
// 接收元件
this.$eventBus.$on("eventTarget",v=>{
console.log('eventTarget',v);//這是eventTarget傳過來的值
})
state:定義存貯資料的倉庫 ,可通過this.$store.state 或mapState存取。
getter:獲取 store 值,可認為是 store 的計算屬性,可通過this.$store.getter 或 mapGetters存取。
mutation:同步改變 store 值,為什麼會設計成同步,因為mutation是直接改變 store 值,vue 對操作進行了記錄,如果是非同步無法追蹤改變,可通過mapMutations呼叫。
action:非同步呼叫函數執行mutation,進而改變 store 值,可通過 this.$dispatch或mapActions存取。
modules:模組,如果狀態過多,可以拆分成模組,最後在入口通過...解構引入。
這一塊內容過長,如果基礎不熟的話可以看這個Vuex,然後大致用法如下:
比如建立這樣的檔案結構
index.js 裡內容如下
import Vue from 'vue'
import Vuex from 'vuex'
import getters from './getters'
import actions from './actions'
import mutations from './mutations'
import state from './state'
import user from './modules/user'
Vue.use(Vuex)
const store = new Vuex.Store({
modules: {
user
},
getters,
actions,
mutations,
state
})
export default store
然後在 main.js 引入
import Vue from "vue"
import store from "./store"
new Vue({
el:"#app",
store,
render: h => h(App)
})
然後在需要的使用元件裡
import { mapGetters, mapMutations } from "vuex"
export default{
computed:{
// 方式一 然後通過 this.屬性名就可以用了
...mapGetters(["引入getters.js裡屬性1","屬性2"])
// 方式二
...mapGetters("user", ["user模組裡的屬性1","屬性2"])
},
methods:{
// 方式一 然後通過 this.屬性名就可以用了
...mapMutations(["引入mutations.js裡的方法1","方法2"])
// 方式二
...mapMutations("user",["引入user模組裡的方法1","方法2"])
}
}
// 或者也可以這樣獲取
this.$store.state.xxx
this.$store.state.user.xxx
$root
可以拿到 App.vue 裡的資料和方法
// 父元件
mounted(){
console.log(this.$root) //獲取根範例,最後所有元件都是掛載到根範例上
console.log(this.$root.$children[0]) //獲取根範例的一級子元件
console.log(this.$root.$children[0].$children[0]) //獲取根範例的二級子元件
}
將父元件的 template 傳入子元件
分類:
A.匿名插槽(也叫預設插槽): 沒有命名,有且只有一個;
// 父元件
<todo-list>
<template v-slot:default>
任意內容
<p>我是匿名插槽 </p>
</template>
</todo-list>
// 子元件
<slot>我是預設值</slot>
//v-slot:default寫上感覺和具名寫法比較統一,容易理解,也可以不用寫
B.具名插槽: 相對匿名插槽元件slot標籤帶name命名的;
// 父元件
<todo-list>
<template v-slot:todo>
任意內容
<p>我是匿名插槽 </p>
</template>
</todo-list>
//子元件
<slot name="todo">我是預設值</slot>
C.作用域插槽: 子元件內資料可以被父頁面拿到(解決了資料只能從父頁面傳遞給子元件)
// 父元件
<todo-list>
<template v-slot:todo="slotProps" >
{{slotProps.user.firstName}}
</template>
</todo-list>
//slotProps 可以隨意命名
//slotProps 接取的是子元件標籤slot上屬性資料的集合所有v-bind:user="user"
// 子元件
<slot name="todo" :user="user" :test="test">
{{ user.lastName }}
</slot>
data() {
return {
user:{
lastName:"Zhang",
firstName:"yue"
},
test:[1,2,3,4]
}
},
// {{ user.lastName }}是預設資料 v-slot:todo 當父頁面沒有(="slotProps")
1.方案一
// 路由定義
{
path: '/describe/:id',
name: 'Describe',
component: Describe
}
// 頁面傳參
this.$router.push({
path: `/describe/${id}`,
})
// 頁面獲取
this.$route.params.id
2.方案二
// 路由定義
{
path: '/describe',
name: 'Describe',
component: Describe
}
// 頁面傳參
this.$router.push({
name: 'Describe',
params: {
id: id
}
})
// 頁面獲取
this.$route.params.id
3.方案三
// 路由定義
{
path: '/describe',
name: 'Describe',
component: Describe
}
// 頁面傳參
this.$router.push({
path: '/describe',
query: {
id: id
`}
)
// 頁面獲取
this.$route.query.id
4.三種方案對比
方案二引數不會拼接在路由後面,頁面重新整理引數會丟失
方案一和三引數拼接在後面,醜,而且暴露了資訊
Vue.observable
用法:讓一個物件可響應。Vue 內部會用它來處理 data 函數返回的物件;
返回的物件可以直接用於渲染函數和計算屬性內,並且會在發生改變時觸發相應的更新;
也可以作為最小化的跨元件狀態記憶體,用於簡單的場景。
通訊原理實質上是利用Vue.observable實現一個簡易的 vuex
// 檔案路徑 - /store/store.js
import Vue from 'vue'
export const store = Vue.observable({ count: 0 })
export const mutations = {
setCount (count) {
store.count = count
}
}
//使用
<template>
<div>
<label for="bookNum">數 量</label>
<button @click="setCount(count+1)">+</button>
<span>{{count}}</span>
<button @click="setCount(count-1)">-</button>
</div>
</template>
<script>
import { store, mutations } from '../store/store' // Vue2.6新增API Observable
export default {
name: 'Add',
computed: {
count () {
return store.count
}
},
methods: {
setCount: mutations.setCount
}
}
</script>
vue.js: https://v2.cn.vuejs.org/
vuex是什麼:https://v3.vuex.vuejs.org/zh/
工作中要使用Git,看這篇文章就夠了:http://www.guosisoft.com/article/detail/410508049313861
企業數位化轉型如何做?看過來:http://www.guosisoft.com/article/detail/408745545576517
如果本文對你有一點點幫助,點個贊支援一下吧,你的每一個【贊】都是我創作的最大動力 _
更多技術文章請往:http://www.guosisoft.com/article,大家一起共同交流和進步呀
作者:
RDIF
出處:
http://www.cnblogs.com/huyong/
Email:
[email protected]
QQ:
406590790
微信:
13005007127(同手機號)
框架官網:
http://www.guosisoft.com/
http://www.rdiframework.net/
框架其他部落格:
http://blog.csdn.net/chinahuyong
http://www.cnblogs.com/huyong
國思RDIF開發框架
,
給使用者和開發者最佳的.Net框架平臺方案,為企業快速構建跨平臺、企業級的應用提供強大支援。
關於作者:系統架構師、資訊系統專案管理師、DBA。專注於微軟平臺專案架構、管理和企業解決方案,多年專案開發與管理經驗,曾多次組織並開發多個大型專案,在物件導向、面向服務以及資料庫領域有一定的造詣。現主要從事基於
RDIF
框架的技術開發、諮詢工作,主要服務於金融、醫療衛生、鐵路、電信、物流、物聯網、製造、零售等行業。
如有問題或建議,請多多賜教!
本文版權歸作者和CNBLOGS部落格共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連線,如有問題,可以通過微信、郵箱、QQ等聯絡我,非常感謝。