1. 程式人生 > >Vue中非父子元件間傳值

Vue中非父子元件間傳值

前言:最近好幾個月沒有更博了,最開始是忙,後面就是忙著過中秋國慶去了。很久沒有靜下心學習了,默默地反思,不該有藉口而怠慢。立個flag,以後每週末必須留出半天時間學習技術知識。 自己也遇到這個問題,直接轉載博主的文章了,原文出處:vue非父子元件通訊問題解決記錄。 **注意:**傳送和接受側事件名必須一致,而涉及到對本元件中資料操作時,需要提前儲存當前元件的this,$emit本身也是一個物件,直接用this則是指向了這個事件物件而非vue元件本身!!

、、、、、、、、、、、、、、、、、、、、、、華麗的分割線、、、、、、、、、、、、、、、、、、、、

問題描述:最近在做登入部分時遇到一個場景,當點選 “使用者”按鈕時,首先渲染login元件,在使用者登入後直接跳轉到使用者資訊介面(user元件)。這裡遇到了需要將login元件通過非同步請求獲得的使用者資訊傳向 user元件,但是login和login元件不是父子元件,就暫稱為兄弟元件吧。 簡而言之,我要解決的就是兄弟元件之間的資訊傳遞問題。

  1. //位於login.vue 中
  2. export default {
  3. methods:{
  4. login(){
  5. this.$axios({
  6. method: 'post',
  7. url: '/student/login/',
  8. data: {
  9. username: this.username,
  10. password: this.password
  11. }
  12. })
  13. .then(function
    (response)
    {
  14. this.$router.replace({path: 'user'});
  15. }.bind(this))
  16. .catch(function (error) {
  17. console.log(error);
  18. alert('使用者名稱或密碼錯誤');
  19. });
  20. }
  21. }
  22. }
在非同步請求完成後,就跳轉到user元件中,但是response的請求返回資訊就無法傳到非父子元件的user元件中。

.............................................................................................................................................................................................................

解決問題

一、首先我想到了將login元件 和 user元件放到一起(兩個元件合2為1),加避免了元件間的資訊傳遞,但是這種解決方案太low,破壞了專案原有的結構。

二、查文件得知了

  1. 非父子關係的兩個元件之間也需要通訊。在簡單的場景下,可以使用一個空的 Vue 例項作為事件匯流排:
  2. var bus = new Vue()
  3. // 觸發元件 A 中的事件
  4. bus.$emit('id-selected', 1)
  5. // 在元件 B 建立的鉤子中監聽事件
  6. bus.$on('id-selected', function (id) {
  7. // ...
  8. })
以自己遇到的問題為例,寫出具體的實現:(warning:  我自己遇到的情況不能用這種簡單的方式解決)

最好先新建一個js檔案,來創建出我們的eventBus,我們把它命名為bus.js

  1. import Vue from 'vue';
  2. export default new Vue();
然後在 login.vue 檔案中:
  1. //位於login.vue 中
  2. import Bus from '../bus.js';
  3. export default {
  4. methods:{
  5. login(){
  6. this.$axios({
  7. method: 'post',
  8. url: '/student/login/',
  9. data: {
  10. username: this.username,
  11. password: this.password
  12. }
  13. })
  14. .then(function (response) {
  15. /*
  16. * 這裡的 'login-on'是在Bus中自己構造的一個事件(不需要在別處申明),通過emit()函式
  17. * 手動觸發這個事件,然後在user.vue 中可以監聽這個事件,一旦發現事件觸發,就可以利用
  18. * 回撥函式接收 response 這個物件,達到資訊傳遞的目的
  19. * */
  20. Bus.$emit('login-on',response);
  21. this.$router.replace({path: 'user'});
  22. }.bind(this))
  23. .catch(function (error) {
  24. console.log(error);
  25. alert('使用者名稱或密碼錯誤');
  26. });
  27. }
  28. }
  29. }
在user.vue檔案中:
  1. //位於user.vue 中
  2. import Bus from '../bus.js';
  3. export default {
  4. mouted : {
  5. Bus.$on('login-on', message => { // 這裡的message就是從login.vue中傳來的資料
  6. console.log(message);
  7. });
  8. }
  9. }
這樣就完成了資料傳遞。

但是在測試時發現在user.vue元件的監聽 沒有被觸發

分析:在我遇到的情境中,login.vue元件和user.vue 元件是互相替代的關係,即需要從login元件通過vue-router跳轉到user元件,在login.vue銷燬後才開始渲染user.vue元件,所以在user.vue中的

Bus.$on('login-on', message =>
監聽是在事件以及觸發之後才開始,自然不會產生回撥函式。

由此可以看出,官方推薦的eventbus 解決方案的缺陷在於, 在資料傳遞過程中,兩個元件必須都已經被渲染過。我的這種情景下,這種方法不適用。 三、vuex解決方案: 熟悉vuex的大佬們請直接跳過吧。

最好在src的根目錄下新建一個檔案,叫 store.js

  1. //位於store.js 中
  2. import Vue from 'vue'
  3. import Vuex from 'vuex'
  4. Vue.use(Vuex);
  5. export default new Vuex.Store({
  6. /*
  7. * state指的就是儲存的資料,
  8. * 下面的資料是我在專案中需要用的資料欄位
  9. * */
  10. state: {
  11. has_login: false,
  12. id: 1,
  13. mobile_num: '',
  14. name: ''
  15. },
  16. /*
  17. * mutations裡面規定的就是想要改變state(資料)的動作函式,
  18. * 下面的user_message 就是我將傳入的message賦值給倉庫中的
  19. * state資料欄位,達到更新資料的目的
  20. * */
  21. mutations: {
  22. user_message (state, message) {
  23. state.has_login = true;
  24. state.id = message.data.id;
  25. state.mobile_num = message.data.mobile_num;
  26. state.name = message.data.name;
  27. }
  28. }
  29. })

然後在login.vue元件中,提交收到的使用者資訊

  1. //位於login.vue 中
  2. import userMessage from '../store';
  3. export default new Vuex.Store({
  4. methods: {
  5. loginSubmit () {
  6. this.$axios({
  7. method: 'post',
  8. url: '/student/login/',
  9. data: {
  10. username: this.username,
  11. password: this.password
  12. }
  13. })
  14. .then(function (response) {
  15. //這裡呼叫 store.js中 mutations裡面的user_message函式,從而改變倉庫中的state資料
  16. userMessage.commit('user_message', response);
  17. this.$router.replace({path: 'user'});
  18. }.bind(this))
  19. .catch(function (error) {
  20. console.log(error);
  21. alert('使用者名稱或密碼錯誤');
  22. });
  23. }
  24. })
最後在user.vue元件中接收vuex倉庫中儲存的資訊,即先引入倉庫
  1. //位於user.vue 中
  2. import userMessage from '../store';
然後可以直接將 userMessage.state 賦值給user.vue作用域中的資料欄位,同時,vuex 的state有熱更新的屬性,對於資料的同步很有幫助,優點良多。

所以,中大型的專案還是在一開始就直接使用vuex是明智的決定,對於開發有很大的便利。

				<script>
					(function(){
						function setArticleH(btnReadmore,posi){
							var winH = $(window).height();
							var articleBox = $("div.article_content");
							var artH = articleBox.height();
							if(artH > winH*posi){
								articleBox.css({
									'height':winH*posi+'px',
									'overflow':'hidden'
								})
								btnReadmore.click(function(){
									articleBox.removeAttr("style");
									$(this).parent().remove();
								})
							}else{
								btnReadmore.parent().remove();
							}
						}
						var btnReadmore = $("#btn-readmore");
						if(btnReadmore.length>0){
							if(currentUserName){
								setArticleH(btnReadmore,3);
							}else{
								setArticleH(btnReadmore,1.2);
							}
						}
					})()
				</script>
				</article>