本篇內容介紹了“vue的組件通訊方法有哪些”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
父組件向子組件傳送數據,這應該是最常用的方式了
子組件接收到數據之后,不能直接修改
父組件的數據。會報錯,所以當父組件重新渲染時,數據會被覆蓋。如果子組件內想要修改的話,推薦使用computedprops
可以是數組或對象,用于接收來自父組件的數據。
// 父組件 List.vue <template> <div> <List-item :str="str" :obj="obj" :arr="arr"></List-item> </div> </template> <script> import ListItem from "./ListItem"; export default { data() { return { str: "給子組件傳值", obj: {msg: "給子組件傳值"}, arr: [1, 2, 3] } }, components: { ListItem } } </script> // 子組件 ListItem.vue <template> <div> <div>{{msg}}</div> <div>{{obj}}</div> <div>{{arr}}</div> </div> </template> <script> export default { props: { msg: String, // props是字符串 obj: Object, // props是對象 arr: Array // props是數組 } } </script>
修飾符 .sync,它對 props
起到了一種修飾的作用,使用 .sync
進行修飾的 props
意味子組件有修改它的意圖,這種情況下它只起到一個標注性作用,有它沒它都不會影響邏輯
使用 .sync
修改上邊的代碼:
// 父組件 List.vue <template> <!-- 這里不寫 .sync 也不會影響結果 --> <List-item :title.sync="title" @update:title="updataTitle"></List-item> </template> <script> import ListItem from "./ListItem"; export default { data() { return { title: "我是title", } }, components: { ListItem }, methods: { updataTitle(res) { this.title = res; } } } </script> // 子組件 ListItem.vue <template> <div> <button @click="handleClick">Click me</button> <div>{{title}}</div> </div> </template> <script> export default { props: { title: String, }, methods: { handleClick() { // 子組件向父組件傳值 this.$emit('update:title', '我要父組件更新 title'); } } } </script>
.sync
向子組件傳遞 多個props:當我們用一個對象同時設置多個 prop
的時候,也可以將這個 .sync
修飾符和 v-bind
配合使用:
<text-document v-bind.sync="doc"></text-document>
這樣會把 doc
對象中的每一個屬性 (如 title) 都作為一個獨立的 prop
傳進去,然后各自添加用于更新的 v-on 監聽器
。
盡管存在 prop
和事件,有的時候你仍可能需要在 JavaScript 里直接訪問一個子組件。為了達到這個目的,可以通過 ref
特性為這個子組件賦予一個 ID 引用。
<template> <div> <List-item ref="item" :title="title"></List-item> <div>{{data}}</div> </div> </template> <script> import ListItem from "./List-item"; export default { data() { return { title: "我是title", data: "" } }, components: { ListItem }, mounted() { this.data = this.$refs.item.message; } } </script>
這種方式,從嚴格意思上講不是值的傳遞,而是一種"取"(不推薦直接通過實例進行值的獲?。?。
可以通過 Vue 的實例屬性 $parent
獲得父組件的實例,借助實例可以調用父實例中的方法,或者獲取父實例上的屬性,從而達到取值的目的。
// 父組件 List.vue ... <script> export default { data() { return { message: "hello children", msg: "hello" } }, methods: { sendMessage() { return this.message; } } } </script> // 子組件 ListItem.vue <template> <div> <div>{{data}}</div> <div>{{msg}}</div> </div> </template> <script> export default { data() { return { data: "", msg: "" } }, mounted() { this.data = this.$parent.sendMessage(); // 調用父實例中的方法 this.msg = this.$parent.msg; // 獲取父實例中的屬性 } } </script>
通過 $parent
獲取父實例 this.$parent.event
。
通過 props
傳遞方法。
通過 $emit
監聽父組件中的方法 this.$emit("envnt")
。
子組件使用 $emit
發送一個自定義事件,事件名稱是一個字符串。
父組件使用指令 v-on
綁定子組件發送的自定義事件。
// 父組件 List.vue <template> <div> <!-- 監聽自定義事件 --> <List-item v-on:welcome="getWelcome"></List-item> </div> </template> <script> import ListItem from "./List-item"; export default { components: { ListItem }, methods: { getWelcome(data) { alert(data) } } } </script> // 子組件 ListItem.vue <template> <button @click="handleClick">Click me</button> </template> <script> export default { methods: { handleClick() { // 使用 $emit 發送自定義事件 welcome this.$emit('welcome', 'hello'); } } } </script>
獲取父組件下的所有子組件的實例,返回的是一個數組 使用范圍:該屬性只針對vue組件,與js中childNodes還是有區別的。
$ildren: 獲取子組件實例集合
hildNodes: 獲取子節點集合
使用方法:
<template> <A></A> <B></B> </template> <script> export default{ data(){}, mounted(){ // 通過$children可以獲取到A和B兩個子組件的實例 console.log('children:',this.$children) } } </script> 其中, this.$children[0] 可以獲取到A組件的實例,一樣的,我們可以使用A組件的屬性以及他的方法。
Vuex介紹
Vuex
是一個專為 Vue.js 應用程序開發的狀態管理模式
。它采用集中式存儲管理應用的所有組件的狀態,并以相應的規則保證狀態以一種可預測的方式發生變化.
Vuex
解決了多個視圖依賴于同一狀態和來自不同視圖的行為需要變更同一狀態的問題,將開發者的精力聚焦于數據的更新而不是數據在組件之間的傳遞上
Vuex各個模塊
state
:用于數據的存儲,是store中的唯一數據源
getters
:如vue中的計算屬性一樣,基于state數據的二次包裝,常用于數據的篩選和多個數據的相關性計算
mutations
:類似函數,改變state數據的唯一途徑,且不能用于處理異步事件
actions
:類似于mutation,用于提交mutation來改變狀態,而不直接變更狀態,可以包含任意異步操作
modules
:類似于命名空間,用于項目中將各個模塊的狀態分開定義和操作,便于維護
Vuex實例應用
這里我們先新建 store文件夾
, 對Vuex進行一些封裝處理
在 store 文件夾下添加 index.js
文件
// index.js // 自動掛載指定目錄下的store import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) let modules = {} // @/store/module 目錄下的文件自動掛載為 store 模塊 const subModuleList = require.context('@/store/modules', false, /.js$/) subModuleList.keys().forEach(subRouter => { const moduleName = subRouter.substring(2, subRouter.length - 3) modules[moduleName] = subModuleList(subRouter).default }) export default new Vuex.Store({ state: {}, mutations: {}, actions: {}, modules })
在 store 文件夾下添加 module
文件夾,在module文件夾再新建 user.js
文件
// user.js import user from '@/utils/user.js' import userApi from '@/apis/user' import { OPEN_ACCOUNT_STAGE, STAGE_STATUS } from '@/constant' let getUserPromise = null export default { namespaced: true, state() { return { userInfo: null, // 用戶信息 isLogined: !!user.getToken(), // 是否已經登錄 } }, mutations: { // 更新用戶信息 updateUser(state, payload) { state.isLogined = !!payload state.userInfo = payload }, }, actions: { // 獲取當前用戶信息 async getUserInfo(context, payload) { // forceUpdate 表示是否強制更新 if (context.state.userInfo && !payload?.forceUpdate) { return context.state.userInfo } if (!getUserPromise || payload?.forceUpdate) { getUserPromise = userApi.getUserInfo() } // 獲取用戶信息 try { const userInfo = await getUserPromise context.commit('updateUser', userInfo) } finally { getUserPromise = null } return context.state.userInfo }, // 登出 async logout(context, payload = {}) { // 是否手動退出 const { manual } = payload if (manual) { await userApi.postLogout() } user.clearToken() context.commit('updateUser', null) }, } }
然后在項目的 main.js
文件中引入
import Vue from 'vue' import App from '@/app.vue' import { router } from '@/router' import store from '@/store/index' const vue = new Vue({ el: '#app', name: 'root', router, store, render: h => h(App), })
封裝完成,即可正常操縱
this.$store.state.user.isLogined this.$store.state.user.userInfo this.$store.commit('user/updateUser', {}) await this.$store.dispatch('user/logout', { manual: true })
ventBus
夠簡化各組件間的通信,讓我們的代碼書寫變得簡單,能有效的分離事件發送方和接收方(也就是解耦的意思),能避免復雜和容易出錯的依賴性和生命周期問題。
Event 事件。它可以是任意類型。
Subscriber 事件訂閱者。在EventBus3.0之前我們必須定義以onEvent開頭的那幾個方法,分別是onEvent、onEventMainThread、onEventBackgroundThread和onEventAsync,而在3.0之后事件處理的方法名可以隨意取,不過需要加上注解@subscribe(),并且指定線程模型,默認是POSTING。
Publisher 事件的發布者。我們可以在任意線程里發布事件,一般情況下,使用EventBus.getDefault()就可以得到一個EventBus對象,然后再調用post(Object)方法即可。
provide / inject
是vue2.2.0新增的api, 簡單來說就是父組件中通過provide來提供變量, 然后再子組件中通過inject來注入變量。
provide
選項應該是
一個對象或返回一個對象的函數。該對象包含可注入其子孫的屬性。在該對象中你可以使用 ES2015 Symbols 作為 key,但是只在原生支持 Symbol 和 Reflect.ownKeys 的環境下可工作。
inject
選項應該是:
一個字符串數組
一個對象(詳情點擊這里) 基本用法:
// 祖先組件 提供foo //第一種 export default { name: "father", provide() { return { foo: 'hello' } }, } //第二種 export default { name: "father", provide: { foo:'hello~~~~' }, } //后代組件 注入foo, 直接當做this.foo來用 export default { inject:['foo'], }
上面的兩種用法有什么區別嗎?
如果你只是傳一個字符串,像上面的hello
,那么是沒有區別的,后代都能讀到。
如果你需要this
對象屬性的值(如下所示代碼),那么第二種是傳不了的,后代組件拿不到數據。所以建議只寫第一種
//當你傳遞對象給后代時 provide() { return { test: this.msg } },
注意:一旦注入了某個數據,比如上面示例中的 foo,那這個組件中就不能再聲明 foo 這個數據了,因為它已經被父級占有。
provide 和 inject 綁定并不是可響應的。
這是刻意為之的。然而,如果你傳入了一個可監聽的對象
,那么其對象的屬性還是可響應的。因為對象是引用類型。
先來個值類型的數據(也就是字符串)例子,不會響應
provide(){ return{ test:this.msg } }, data() { return { msg: "Welcome to Your Vue.js App", } } mounted(){ setTimeout(()=>{ this.msg = "halo world"; console.log(this._provided.msg) //log:Welcome to Your Vue.js App },3000) },
如上所示,這樣做是不行的,打印出來的 _provided
中的數據并沒有改,子組件取得值也沒變。
你甚至可以直接給 this._provided.msg
賦值,但是即使是_provided.msg 里面的值改變了,子組件的取值,依然沒有變。
當你的參數是對象的時候,就可以響應了,如下:
provide(){ return{ test:this.activeData } }, data() { return { activeData:{name:'halo'}, } } mounted(){ setTimeout(()=>{ this.activeData.name = 'world'; },3000) }
這就是vue官方中寫道的對象的屬性是可以響應的
provide/inject 實現全局變量
provide/inject不是只能從祖先傳遞給后代嗎?是的,但是,如果我們綁定到最頂層的組件app.vue,是不是所有后代都接收到了,就是當做全局變量來用了。
//app.vue export default { name: 'App', provide(){ return{ app:this } }, data(){ return{ text:"it's hard to tell the night time from the day" } }, methods:{ say(){ console.log("Desperado, why don't you come to your senses?") } } } //其他所有子組件,需要全局變量的,只需要按需注入app即可 export default { inject:['foo','app'], mounted(){ console.log(this.app.text); // 獲取app中的變量 this.app.say(); // 可以執行app中的方法,變身為全局方法! } }
provide/inject 實現頁面刷新,不閃爍
用vue-router
重新路由到當前頁面,頁面是不進行刷新的
采用window.reload()
,或者router.go(0)
刷新時,整個瀏覽器進行了重新加載,閃爍,體驗不好
那我們怎么做呢?
跟上面的原理差不多,我們只在控制路由的組件中寫一個函數(使用v-if控制router-view的顯示隱藏,這里的原理不作贅述),然后把這個函數傳遞給后代,然后在后代組件中調用這個方法即可刷新路由啦。
//app.vue <router-view v-if="isShowRouter"/> export default { name: 'App', provide() { return { reload: this.reload } }, data() { return { isShowRouter: true, } }, methods:{ reload() { this.isShowRouter = false; this.$nextTick(() => { this.isShowRouter = true; }) } } } //后代組件 export default { inject: ['reload'], }
這里 provide 使用了函數傳遞給后代,然后后代調用這個函數,這種思路,也是可以做子后代向父組件傳參通訊的思路了。這里的原理,和 event 事件訂閱發布就很像了
通過 $root
,任何組件都可以獲取當前組件樹的根 Vue 實例
,通過維護根實例上的 data,就可以實現組件間的數據共享
。
//main.js 根實例 new Vue({ el: '#app', store, router, // 根實例的 data 屬性,維護通用的數據 data: function () { return { author: '' } }, components: { App }, template: '<App/>', }); <!--組件A--> <script> export default { created() { this.$root.author = '于是乎' } } </script> <!--組件B--> <template> <div><span>本文作者</span>{{ $root.author }}</div> </template>
注意:通過這種方式,雖然可以實現通信,但在應用的任何部分,任何時間發生的任何數據變化,都不會留下變更的記錄,這對于稍復雜的應用來說,調試是致命的,不建議在實際應用中使用。
多層嵌套組件傳遞數據時,如果只是傳遞數據,而不做中間處理的話就可以用這個,比如父組件向孫子組件傳遞數據時
$attrs
:包含父作用域里除 class 和 style 除外的非 props 屬性集合。通過this.attrs獲取父作用域中所有符合條件的屬性集合,然后還要繼續傳給子組件內部的其他組件,就可以通過v−bind = " attrs 獲取父作用域中所有符合條件的屬性集合,然后還要繼續傳給子組件內部的其他組件,就可以通過 v-bind="attrs獲取父作用域中所有符合條件的屬性集合,然后還要繼續傳給子組件內部的其他組件,就可以通過v−bind="attrs"
$listeners
:包含父作用域里 .native 除外的監聽事件集合。如果還要繼續傳給子組件內部的其他組件,就可以通過 v-on=“$linteners”
使用方式是相同的:
// Parent.vue <template> <child :name="name" title="1111" ></child> </template export default{ data(){ return { name:"小解" } } } // Child.vue <template> // 繼續傳給孫子組件 <sun-child v-bind="$attrs"></sun-child> </template> export default{ props:["name"], // 這里可以接收,也可以不接收 mounted(){ // 如果props接收了name 就是 { title:1111 },否則就是{ name:"小解", title:1111 } console.log(this.$attrs) } }
“vue的組件通訊方法有哪些”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。