Vue組件通訊黑科技

来源:https://www.cnblogs.com/qiqingfu/archive/2019/03/13/10520289.html
-Advertisement-
Play Games

Vue組件通訊 組件可謂是 Vue框架的最有特色之一, 可以將一大塊拆分為小零件最後組裝起來。這樣的好處易於維護、擴展和復用等。 提到 Vue的組件, 相必大家對Vue組件之間的數據流並不陌生。最常規的是父組件的數據通過 prop傳遞給子組件。子組件要進行數據更新,那麼需要通過自定義事件通知父組件。 ...


Vue組件通訊

組件可謂是 Vue框架的最有特色之一, 可以將一大塊拆分為小零件最後組裝起來。這樣的好處易於維護、擴展和復用等。

提到 Vue的組件, 相必大家對Vue組件之間的數據流並不陌生。最常規的是父組件的數據通過 prop傳遞給子組件。子組件要進行數據更新,那麼需要通過自定義事件通知父組件。

那麼還有其他方法, 方便父子組件乃至跨組件之間的通訊嗎?

props $emit

可以通過 prop屬性從父組件向子組件傳遞數據

// child.vue
Vue.component('child', {
    props: ['msg'],
    template: `<div>{{ msg }}</div>`
})


// parent.vue
<div>
    <child :msg="message"></child>
<div>
// 部分省略...
{
  data(){
    return {
      message: 'hello.child'
    }
  }
}

以上代碼父子組件通訊是通過 prop的傳遞, Vue是單向數據流, 子組件不能直接修改父組件的數據。可以通過自定義事件通知父組件修改,和要修改的內容

provide / inject

provide 和 inject 主要為高階插件/組件庫提供用例。並不推薦直接用於應用程式代碼中。但是我們如果合理的運用, 那還是非常方便的

provide 應該是一個對象, 或者是一個函數返回一個對象。它主要的作用就是給應用provide的組件的子孫組件提供註入數據。就相當於 Vuex的store,用來存放數據。

inject: 給當前的組件註入 provide提供的數據。切記 provideinject 綁定並不是可響應的。

看一下這兩個怎麼使用

a組件給子組件b提供數據

  // a.vue
  export default {
    provide: {
      name: 'qiqingfu'
    }
  }
  
  // b.vue
  export default {
    inject: ['name'],
    mounted(){
      console.log(this.name)  // qiqingfu
    }
  }

以上代碼父組件a提供了 name: qiqingfu的數據, 子組件和子孫組件如果需要就通過 inject註入進來就可以使用了

provide / inject 替代 Vuex存儲用戶的登錄數據實例
這裡並不是一定要替代 vuex, 介紹另一種可行性

我們在使用 webpack進行構建 Vue項目時, 都會有一個入口文件 main.js, 裡面通常都導入一些第三方庫如 vuexelementvue-router等。但是我們也引入了一個 Vue的根組件 App.vue。簡單的app.vue是這樣子的

  <template>
     <div>
       <router-view></router-view>
     </div>
  </template>
  <script>
    export default {
      
    }
    </script>

這裡的 App.vue 是一個最外層的組件, 可以用來存儲一些全局的數據和狀態。因為組件的解析都是以App.vue作為入口。引入你項目中所有的組件,它們的共同根組件就是App.vue。所以我們可以在 App.vue中將自己暴露出去

  <template>
  <div>
    <router-view></router-view>
  </div>
</template>
<script>
  export default {
    provide () {
      return {
        app: this
      }
    }
  }
</script>

以上代碼把整個 app.vue實例的 this對外提供, 並且命令為 app。那麼子組件使用時只需要訪問或者調用 this.app.xxx或者訪問 app.vue的 datacomputed等。

因為 App.vue只會渲染一次, 很適合做一些全局的狀態數據管理, 比如用戶的登錄信息保存在 App.vuedata中。

app.vue

<template>
  <div>
    <router-view></router-view>
  </div>
</template>
<script>
  export default {
    provide () {
      return {
        app: this
      }
    },
    data: {
      userInfo: null
    },
    mounted() {
      this.getUserInfo()
    },
    methods: {
      async getUserInfo() {
        const result = await axios.get('xxxxxx')
        if (result.code === 200) {
          this.userInfo = result.data.userinfo
        }
      }
    }
  }
</script>

以上代碼,通過介面獲取用戶對數據信息, 保存在 App.vue的data中。而且provide將實例提供給任何子組件使用。所以任何頁面和子組件都可以通過 inject註入 app。並且通過 this.app.userInfo來取得用戶信息。

那麼如果用戶要修改當前的信息怎麼辦? App.vue只初始化一次呀?

  // header.vue
  <template>
    <div>
      <p>用戶名: {{ app.userInfo.username }}</p>
    </div>
  <template>

  <script>
    export default {
      name: 'userHeader',
      inject: ['app'],
      methods: {
        async updateUserName() {
          const result = await axios.post(xxxxx, data)
          if (result === 200) {
            this.app.getUserInfo()
          }
        }
      }
    }
  </script>

以上代碼, 在header.vue組件中用戶修改了個人信息, 只需要修改完成之後再調用一次 App.vue根組件的 getUserInfo方法, 就又會同步最新的修改數據。

dispatch / broadcast

父子組件通訊的方法。可以支持:

  • 父 - 子 傳遞數據 (廣播)
  • 子 - 父 傳遞數據 (派發)

先聊一下 Vue實例的方法 $emit()$on()

$emit: 觸發當前實例上的事件。附加參數都會傳給監聽器回調。

$on: 監聽當前實例上的事件

也就是一個組件向父組件通過 $emit自定義事件發送數據的時候, 它會被自己的 $on方法監聽到

   
   // child.vue
   export default {
     name: 'child',
     methods: {
       handleEvent() {
         this.$emit('test', 'hello, 這是child.vue組件')
       }
     },
     mounted() {
       // 監聽自定義事件
       this.$on('test', data => {
         console.log(data)  // hello, 這是child.vue組件
       })
     }
   }

   // parent
   <template>
    <div>
      <child v-on:test="handleTest"></child>
    </div>
   <template>
   
   <script>
    export default {
      methods: {
        handleTest(event) {
          console.log(event) // hello, 這是child.vue組件
        }
      }
    }
   </script>

以上代碼, $on 監聽自己觸發的$emit事件, 因為不知道何時會觸發, 所以會在組件的 createdmounted鉤子中監聽。

看起來多此一舉, 沒有必要在自己組件中監聽自己調用的 $emit。 但是如果當前組件的 $emit在別的組件被調用, 並且傳遞數據的話那就不一樣了。

舉個例子

  // child.vue 
   export default {
     name: 'iChild',
     methods: {
       sayHello() {
         // 如果我在子組件中調用父組件實例的 $emit
         this.$parent.$emit('test', '這是子組件的數據')
       }
     }
   }

   // parent.vue
   export default = {
     name: 'iParent',
     mounted() {
       this.$on('test', data => {
         console.log(data) // 這是子組件的數據
       })
     }
   }

以上代碼, 在子組件調用父組件實例的 $emit方法, 並且傳遞響應的數據。那麼在父組件的 mounted鉤子中可以用 $on監聽事件。

如果這樣寫肯定不靠譜, 所以我們要封裝起來。哪個子組件需要給父組件傳遞數據就將這個方法混入(mixins)到子組件

dispatch 封裝

    // emitter.js
   export default {
     methods: {
       dispatch(componentName, eventName, params) {
         let parent = context.$parent || context.$root
         let name = parent.$options.name

         while(parent && (!name || name !== componentNamee)) {
           parent = parent.$parent
           if (parent) {
             name = parent.$options.name
           }
         }
         if (parent) {
           parent.call.$emit(parent, eventName, params)
         }
       }
     }
   }

以上代碼對 dispatch進行封裝, 三個參數分別是 接受數據的父組件name自定義事件名稱傳遞的數據

對上面的例子重新修改

  import Emitter from './emitter'
  // child.vue 
   export default {
     name: 'iChild',
     mixins: [ Emitter ],  // 將方法混入到當前組件
     methods: {
       sayHello() {
         // 如果我在子組件中調用父組件實例的 $emit
         this.dispatch('iParent', 'test', 'hello,我是child組件數據')
       }
     }
   }

   // parent.vue
   export default = {
     name: 'iParent',
     mounted() {
       this.$on('test', data => {
         console.log(data) // hello,我是child組件數據
       })
     }
   }

以上代碼, 子組件要向父組件傳遞數據。可以將先 Emitter混入。然後再調用 dispatch方法。第一個參數是接受數據的父組件, 也可以是爺爺組件, 只要 name值給的正確就可以。然後接受數據的組件需要通過 $on來獲取數據。

broadcast

廣播是父組件向所有子孫組件傳遞數據, 需要在父組件中註入這個方法。實現如下:

  // emitter.js
  export default {
    methods: {
      broadcast(componentName, eventName, params) {
       this.$children.forEach(child => {
         let name = child.$options.name
         if (name === componentName) {
           child.$emit.apply(child, [eventName].concat(params))
         } else {
           // 遞歸
           broadcast.apply(child, [componentName, eventName].concat([params]))
         }
       })
      }
    }
  }

以上代碼是通過遞歸匹配子組件的 name, 如果沒有找到那麼就遞歸尋找。找到之後調用子組件實例的 $emit()方法並且傳遞數據。

使用:

   // 兒子組件 child.vue
   export default {
     name: 'iChild',
     mounted() {
       this.$on('test', data => {
         console.log(data)
       })
     }
   }

   // 父親組件 parent.vue 
   <template>
      <div>
        <child></child>  
      </div>
   <template>
   export default {
     name: 'iParent',
     components: {
       child
     }
   }

   // 爺爺組件 root.vue
   <template>
    <div>
      <parent></parent>
    </div>
   <template>
   import Emitter from './emitter'
   export default {
     name: 'iRoot',
     mixin: [ Emitter ],
     components: {
       parent
     },
     methods: {
       this.broadcast('iChild', 'test', '爺爺組件給孫子傳數據')
     }
   }

以上代碼, root根組件給孫組件(child.vue)通過調用 this.broadcast找到對應name的孫組件實例。child.vue只需要監聽 test事件就可以獲取數據。

找到任意組件實例的方法

這些方法並非 Vue組件內置, 而是通過自行封裝, 最終返回你要找的組件的實例,進而可以調用組件的方法和函數等。

使用場景:

  • 由一個組件,向上找到最近的指定組件
  • 由一個組件,向上找到所有的指定組件
  • 由一個組件,向下找到最近的指定組件
  • 由一個組件,向下找到所有的指定組件
  • 由一個組件,找到指定組件的兄弟組件

找到最近的指定組件

findComponentUpwarp(context, componentName)

  export function findComponentUpwarp(context, componentName) {
    let parent = context.$parent
    let name = parent.$options.name

    while(parent && (!name || name !== componentName)) {
      parent = parent.$parent
      if (parent) {
        name = parent.$options.name
      }
    }
    return parent
  }

這個函數接受兩個參數,分別是當前組件的上下文(this),第二個參數是向上查找的組件 name。最後返回找到組件的實例。

向上找到所有的指定組件

findComponentsUpwarp(context, componentName)
return Array

  export function findComponentsUpwarp(context, componentName) {
    let parent = context.$parent
    let result = []
    if (parent) {
      if (parent.$options.name === componentName) result.push(parent)
      return result.concat(findComponentsUpwarp(parent, componentName))
    } else {
      return []
    }
  }

這個函數接受兩個參數,分別是當前組件的上下文(this),第二個參數是向上查找的組件 name。最後返回一個所有組件實例的數組

向下找到最近的指定組件

findComponentDownwarp(context, componentName)

  export function findComponentDownwarp(context, componentName) {
    let resultChild = null
    context.$children.forEach(child => {
      if (child.name === componentName) {
         resultChild = child
         break
      } else {
        resultChild = findComponentDownwarp(child, componentName)
        if (resultChild) break
      }
    })
    
    return resultChild
  }

以上代碼接受兩個參數, 當前組件的上下文(this)和向下查找的組件name。返回第一個name和componentName相同的組件實例

向下找到所有的指定組件

findComponentsDownwarp(context, componentName)

  export function findComponentsDownwarp(context, componentName) {
    return context.$children.reduce((resultChilds, child) => {
      if (child.$options.name === componentName) resultChilds.push(child)
      // 遞歸迭代
      let foundChilds = findComponentsDownwarp(child, componentName)
      return resultChilds.concat(foundChilds)
    }, [])
  }

以上代碼接受兩個參數, 當前組件的上下文(this)和向下查找的組件name。返回一個所有組件實例的數組

找到當前組件的其他兄弟組件

findBrothersComponents(context, componentName, exceptMe?)

exceptMe預設為true, 排除它自己, 如果設置為false則包括當前組件

  export function findBrothersComponents(context, componentName, exceptMe = true) {
    let res = context.$parent.$children.filter(child => child.$options.name === componentName)

    // 根據唯一表示_uid找到當前組件的下標
    let index = res.findIndex(item => item._uid === context._uid)
    if (exceptMe) res.splice(index, 1)
    return res
  }

以上代碼通過第一個參數的上下文, 拿到它父組件中的所有子元素,最後根據 exceptMe決定是否排除自己。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 分別通過JS,JAVA和MySql實現 1. JS方法 2. JAVA方法 3. MySql方法 ...
  • 今天需要做手機端的輸入表單自動生成器,突然就想到了手機端對input的輸入類型支持還不錯,於是翻遍了資料,有了下麵的使用方法,閑話少說,上正文: html5現在可以用的新input type類型一共有下麵類型 => <input type="email" /> <input type="url" / ...
  • 示例代碼: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>lightbox</title> <link href="css/lightbox.css" rel="stylesheet"> <script t ...
  • 示例代碼: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <link rel="stylesheet" href="../themes/default/default.cs ...
  • v-model用於數據的雙向綁定。bug不少,湊合看吧,主要是練習v-model。 ...
  • function myJquery(selector){ if(typeof selector=="string") { if (selector.charAt(0) == "<" && selector.slice(-1) == ">") { var ele = selector.slice(1, ...
  • 11111 ...
  • 該需求出現原因:想要實現一個在一開始載入頁面時就出現一行文字逐漸出現的效果,且需要實現的是一種逐漸的過渡出現效果為不是一種生硬的突然間歇性出現。於是便開始嘗試利用最近正在學習的jQuery技術和JS實現。 【註】:該篇文章適合初學者閱讀,大佬請跳過。 【需要的知識點】:JS中利用Timing中的兩個 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...