vue資料傳遞–我有特殊的實現技巧

toBeTheLight發表於2018-01-11

前言

最近碰到了比較多的關於vue的eventBus的問題,之前定技術選型的時候也被問到了,vuex和eventBus的使用範圍。所以簡單的寫一下。同時有一種特殊的實現方案。

元件之間傳值有這麼幾種資料傳遞方式,vuex、props、eventBus和特殊的eventBus

vuex

我就傳兩個資料,vuex真是太麻煩了。用是不可能用的,理解又理解不了。

props

demo

父子元件傳值,官方api,只寫個demo。

  1. 父元件

    // 將事件繫結至子元件
    <son :info="info" @update="updateHandler"/>
    // data
    info: `sendToSon`
    // methods
    updateHandler (newVal) {
      this.info = newVal
    }
  2. 子元件

    // props
    props: [`info`]
    // 觸發繫結在元件上的事件,向上傳值,在父元件某個方法中使用
    this.$emit(`update`, `got`)

父向子傳值–>props
子向父傳值–>子元件繫結事件回撥定義在父元件,子元件觸發此事件。
因不推薦子元件內直接修改父元件傳入的props,需使用自定義事件。

限制

父子元件。

eventBus

demo

bus皆為匯入的bus例項

// bus
const bus = new Vue()
// 資料接收元件
// 當前元件接收值則
bus.$on(`event1`, (val)=>{})
// 資料發出元件
// 當前元件發出值則
bus.$emit(`event1`, val)

可以看出本質是一個vue例項充當事件繫結的媒介。
在所有例項中使用其進行資料的通訊。

雙(多)方使用同名事件進行溝通。

問題

  1. $emit時,必須已經$on,否則將無法監聽到事件,也就是說對元件是有一定的同時存在的要求的。(注:路由切換時,新路由元件先created,舊路由元件再destoryed,部分情況可以分別寫入這兩個生命週期,見此問題)。
  2. $on在元件銷燬後不會自動解除繫結,若同一元件多次生成則會多次繫結事件,則會一次$emit,多次響應,需額外處理。
  3. 資料非“長效”資料,無法儲存,只在$emit後生效。

所以是否有一種更適用的方案呢?

特殊的eventBus?

demo

我們先來看個程式碼,線上程式碼
bus皆為匯入的bus例項

// bus
const bus = new Vue({
  data () {
    return {
      // 定義資料
      val1: ``
    }
  },
  created () {
    // 繫結監聽
    this.$on(`updateData1`, (val)=>{
      this.val1 = val
    })
  }
})

// 資料發出元件

import bus from `xx/bus`
// 觸發在bus中已經繫結好的事件
bus.$emit(`update1`, `123`)

// 資料接收元件


{{val1}}
// 使用computed接收資料
computed {
  val1 () {
    // 依賴並返回bus中的val1
    return bus.val1
  }
}

不同

  1. 正統的eventBus只是用來繫結觸發事件,並不關心資料,不與資料發生交集。而這個方案多一步將資料直接新增在bus例項上。且事件監聽與資料新增需提前定義好。
  2. 資料接收方不再使用$on來得知資料變化,而是通過計算屬性的特徵被動接收。

解決的問題

  1. 通訊元件需同時存在?資料在bus上儲存,所以沒有要求。
  2. 多次繫結?繫結監聽都在bus上,不會重複繫結。
  3. 資料只在$emit後可用?使用計算屬性直接讀取存在bus上的值,不需要再次觸發事件。

探討

為什麼使用計算屬性

其實應該是為什麼不能直接新增到data上,如data1: bus.data1?我們可以再看一段程式碼,線上程式碼
將bus修改為

data () {
  return {
    // 多一層結構
    val: {
      result: 0
    }
  }
},
created () {
  this.$on(`update1`, val => {
    console.log(`觸發1`, i1++)
    this.val.result = val
  })
}

資料接收元件改為

// template
data中獲取直接修改值:{{dataResult}}
data中獲取直接修改值的父層:{{dataVal}}
computed中依賴直接修改值:{{computedResult}}
// js
data () {
    return {
      // 獲取直接修改值
      dataResult: bus.val.result,
      // 獲取直接修改值的父層
      dataVal: bus.val
    }
  },
  computed: {
    computedResult () {
      // 依賴直接修改值
      return bus.val.result
    }
  }

可以看到,data中獲取直接修改值時值的資料是無法動態響應的。

為什麼要用事件

其實不用$emit觸發,使用bus.val = 1直接賦值也是可以的,那麼為什麼不這麼做呢?

簡化版的vuex

其實這種eventBus就是簡化版的vuex。
vue文件中有這樣一段話:

元件不允許直接修改屬於 store 例項的 state,而應執行 action 來分發 (dispatch) 事件通知 store 去改變,我們最終達成了 Flux 架構。這樣約定的好處是,我們能夠記錄所有 store 中發生的 state 改變。

那麼可以用vuex中store對應bus例項,state對應dataaction對應事件dispatch對應$emit
同時vuex中元件獲取資料的方式正是通過計算屬性,那麼其實vuexFlux架構的理解和使用也沒有那麼難。

相關文章