首頁 > web前端 > js教程 > 主體

在vue元件中事件如何傳遞

亚连
發布: 2018-06-14 15:44:38
原創
2913 人瀏覽過

最近的工作需要用到vue,所以最近接觸最多的就是vue,下面我給大家介紹下vue組件間事件傳遞,需要的朋友參考下吧

由於新工作需要用vue,所以最近接觸最多的也是vue,因為之前一直在用react,所以對於vue上手還是很快的。

我也盡量找一些他們兩個的異同點,除了多了一些輔助用的方法以外,最大的不同應該是對於組件間的通信,不僅有props,還有一種事件監聽,也是可以透過組件間傳遞的。

但是,在vue2. 中,vue引入了diff演算法和虛擬dom來提升效率。我們知道這些事為了處理頻繁更新dom元素所提出的一種優化方案,可頻繁變動更新以及事件監聽的初始化之間是否會有矛盾,當組件需要變動時,有沒有對註冊過的事件進行解綁?我們來寫一些簡單的程式碼印證一下。

我們寫兩個p做的按鈕,一個是寫的html程式碼,一個是透過元件的形式插入,兩個按鈕完全一樣,但我們加一個disabled的屬性在外層,並透過if- else來判斷disabled從而顯示不同的按鈕(當然正常場景下我們不會這麼去寫程式碼,這裡只是透過這種方式模擬一種特殊場景,我們自行考慮在我們的業務中是否存在這種場景)。

<template>
 <p class="test">
 <p class="btn" v-if="disabled" @click="handleClick">可点击</p>
 <p class="btn" v-else >不可点击</p>
 <Button v-if="disabled" @clickTest="handleClick">可点击</Button>
 <Button v-else>不可点击</Button>
 </p>
</template>

<script>
import Button from &#39;./Button&#39;
export default {
 data () {
 return {
  disabled: true
 }
 },
 methods: {
 handleClick() {
  alert(&#39;可点击&#39;)
 }
 },
 components: {
 Button,
 },
 mounted() {
 setTimeout(() => {
  this.disabled = false
 }, 1000)
 }
}
</script>
<style>
.btn{
 margin: 100px auto;
 width: 200px;
 line-height: 50px;
 border: 1px solid #42b983;
 border-radius: 5px;
 color: #42b983;
}
</style>
登入後複製

我們加一點樣式,讓他盡量好看一點,看著很簡單,兩個按鈕,可點擊時為他綁定一個點擊事件,不可點擊時不為他綁定。不同點是一個是直接寫的html程式碼,一個是元件。元件的程式碼如下:

<template>
 <p class="btn" @click="handleClick"><slot></slot></p>
</template>
<script>
 export default {
  methods: {
   handleClick() {
    this.$emit(&#39;clickTest&#39;)
   }
  }
 }
</script>
登入後複製

然後在mounted週期加一個1秒的settimeout將disabled變成false,然後我們測試一下

當disabled還是true得時候,兩個按鈕點選都會跳出可點選的alert。但當disebled變成false的時候,上面用html寫的不會再彈框,可是下面用元件寫的就還是會彈跳窗。

這種問題出現時是非常不好定位的,因為程式碼上很顯然不會去調取這個clicktest事件,而在頁面上,我們也能確定按鈕已經變成不可點擊的那一個了。那為什麼這個事件還是會被調取呢?

這先要從diff演算法說起,傳統的diff tree演算法的演算法複雜度是O(n^3),而react在引入diff演算法時,拋除了跨級移動的情況,即只比對同一層的節點異同,讓演算法複雜度降低到了O(n),讓我們可以肆無忌憚(當然也要適可而止)的頻繁刷新整個頁面。

(呵呵,沒圖)

diff有一個策略是擁有相同類別的兩個元件將會產生相似的樹狀結構,擁有不同類別的兩個元件將會產生不同的樹形結構。所以它的比對順序就是

1)tree diff

2)component diff

3)element diff

#回到我們的程式碼上,我們在進行component diff時,認為他們是相同的組件,然後進行element diff,即進行新增刪除和移動所以問題就是發生在了這裡,在實例化組件的時候我們初始化了事件監聽,但在替換相同元件裡的dom時,vue並沒有對已加入元件上的事件監聽做刪除。

我們看一下vue的程式碼,###
Vue.prototype.$emit = function (event: string): Component {
 const vm: Component = this
 if (process.env.NODE_ENV !== &#39;production&#39;) {
  const lowerCaseEvent = event.toLowerCase()
  if (lowerCaseEvent !== event && vm._events[lowerCaseEvent]) {
  tip(
   `Event "${lowerCaseEvent}" is emitted in component ` +
   `${formatComponentName(vm)} but the handler is registered for "${event}". ` +
   `Note that HTML attributes are case-insensitive and you cannot use ` +
   `v-on to listen to camelCase events when using in-DOM templates. ` +
   `You should probably use "${hyphenate(event)}" instead of "${event}".`
  )
  }
 }
 let cbs = vm._events[event]
 if (cbs) {
  cbs = cbs.length > 1 ? toArray(cbs) : cbs
  const args = toArray(arguments, 1)
  for (let i = 0, l = cbs.length; i < l; i++) {
  try {
   cbs[i].apply(vm, args)
  } catch (e) {
   handleError(e, vm, `event handler for "${event}"`)
  }
  }
 }
 return vm
 }
登入後複製
###vue是透過vdom裡的_events屬性下確定是否有綁定事件的。我們看一下不可點擊的按鈕的_events###
:
clickTest
:
Array(1)
0
:
ƒ invoker()
length
:
登入後複製
###發現clicktest還在。這就是問題所在了。 ######那我們該如何去迴避這樣的問題呢,還是該從diff的比對方式來解決問題,還是看程式碼。 ###
function sameVnode (a, b) {
 return (
 a.key === b.key && (
  (
  a.tag === b.tag &&
  a.isComment === b.isComment &&
  isDef(a.data) === isDef(b.data) &&
  sameInputType(a, b)
  ) || (
  isTrue(a.isAsyncPlaceholder) &&
  a.asyncFactory === b.asyncFactory &&
  isUndef(b.asyncFactory.error)
  )
 )
 )
}
登入後複製
###也就是對diff來說,所謂相同的第一判定原則就是key。 ######key也是react引入diff時添加的屬性,用來判斷前後vdom樹上是否為統一元素(注意是同級關係上),所以我們只需要在程式碼上加上key,就可以避免這個問題###
<Button key="1" v-if="disabled" @clickTest="handleClick">可点击</Button>
<Button key="2" v-else>不可点击</Button>
登入後複製
###這樣,我們在點擊按鈕時,就不會再出彈框了。 ######key的作用很廣泛,當我們在遍歷數組生成dom時,添加一個可確定的唯一id(注意不應該用數組索引),會優化我們的比對效率以及更少的操作dom 。我們也會在某個p上添加key以確保他不會因為兄弟元素的變動而被重新渲染(這類p一般會被綁定react或vue以外的事件或動作,如在這個p中生成了一個canvas等)。 ######那麼除了在元件上加上這種不必要key值以外,還有別的方法可以解決嗎? ######有的,這裡有一種很反vue但是類別react的方式,就是把回呼事件透過props的方式傳遞,向下面著這樣,###
<Button v-if="disabled" :clickTest="handleClick">可点击</Button>
<Button v-else>不可点击</Button>
  props: {
   &#39;clickTest&#39;: {
    type: Function
   }
  },
  methods: {
   handleClick() {
    //this.$emit(&#39;clickTest&#39;)
    this.clickTest && this.clickTest()
   }
  }
登入後複製

虽然vue给了我们更方便的事件传递的方式,但props里是允许我们去传递任何类型的,我的期望是在真实的dom上或者在公共组件的入口处以外的地方,都是通过props的方式来传递结果的。虽然这种方式很不vue,而且也享受不到v-on给我们带来的遍历,但是这样确实可以减少不必要的麻烦。

当然既然用了vue,更好的利用vue给我们带来的便利也很重要,所以对于这种很少会出现的麻烦,我们有一个预期,并可以快速定位并修复问题,就可以了。

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

在vue中如何通过v-for处理数组

使用vue如何实现收藏夹

在node.js中有关npm和webpack配置方法

如何通过js将当前时间格式化?

使用vue引入css,less相关问题

以上是在vue元件中事件如何傳遞的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!