最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Vue3之事件綁定的實(shí)現(xiàn)邏輯詳解

 更新時(shí)間:2023年11月15日 09:42:47   作者:JonnyLan  
這篇文章主要介紹了Vue3之事件綁定的實(shí)現(xiàn)邏輯,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

Vue的事件綁定主要是通過v-on指令來實(shí)現(xiàn)的,這個(gè)指令既可以實(shí)現(xiàn)原生事件綁定,例如onclick等。

也可以實(shí)現(xiàn)組件的自定義事件,從而實(shí)現(xiàn)組件的數(shù)據(jù)通信。

本文我們就來分析下Vue的事件處理的邏輯。

v-on作用于普通元素

用在普通元素上時(shí),只能監(jiān)聽原生 DOM 事件,最多的就是onclick事件了。

我們就以onclick事件來分析原理。

案例

let click = () => {
  console.log("點(diǎn)擊我,很快樂")
};

<!-- template -->
<div v-on:click="click">點(diǎn)擊我吧</div>

分析實(shí)現(xiàn)邏輯

我們先來看下渲染函數(shù)

const _hoisted_1 = ["onClick"]

function render(_ctx, _cache) {
  with (_ctx) {
    const { openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue

    return (_openBlock(), _createElementBlock("div", { onClick: click }, "點(diǎn)擊我吧", 8 /* PROPS */, _hoisted_1))
  }
}

我們看到 渲染函數(shù)在創(chuàng)建VNode的時(shí)候傳了一個(gè)onClickpros;

我們先來看下patchProp函數(shù)中對(duì)onClick這個(gè)pros的處理邏輯

export const patchProp: DOMRendererOptions['patchProp'] = (
  el,
  key,
  prevValue,
  nextValue,
  isSVG = false,
  prevChildren,
  parentComponent,
  parentSuspense,
  unmountChildren
) => {
  if (isOn(key)) {
    patchEvent(el, key, prevValue, nextValue, parentComponent)
  }
}

function patchEvent(el, rawName, prevValue, nextValue, instance = null) {
  // vei = vue event invokers
  const invokers = el._vei || (el._vei = {});
  
  if (添加) {
    const invoker = (invokers[rawName] = createInvoker(nextValue, instance));
    el.addEventListener(event, handler, options)
  } else {
    el.removeEventListener(event, handler, options)
  }
}

我們可以看出來底層就是調(diào)用的addEventListener函數(shù)進(jìn)行事件監(jiān)聽綁定,調(diào)用removeEventListener進(jìn)行事件監(jiān)聽解綁。

其實(shí)這個(gè)實(shí)現(xiàn)邏輯很容易想到,沒什么難度。

重點(diǎn)分析—事件修飾符

oncecapture,passive

這兩個(gè)可以直接作為addEventListenerremoveEventListener 的第三個(gè)參數(shù)options 中的值,因?yàn)檫@是W3C支持的事件可選參數(shù)。

stop, prevent,capture, self等。

這類修飾符被封裝在另外一個(gè)withModifiers函數(shù)中。

export const withModifiers = (fn: Function, modifiers: string[]) => {
  return (event: Event, ...args: unknown[]) => {
    for (let i = 0; i < modifiers.length; i++) {
      const guard = modifierGuards[modifiers[i]]
      if (guard && guard(event, modifiers)) return
    }
    return fn(event, ...args)
  }
}

這里設(shè)計(jì)的非常精妙,每個(gè)修飾符都對(duì)應(yīng)一個(gè)執(zhí)行函數(shù),如果調(diào)用執(zhí)行函數(shù)guard(event, modifiers)返回true, 則函數(shù)withModifiers就直接返回了,不會(huì)再執(zhí)行事件的函數(shù)fn(event, ...args)了。

這里列一些這些修飾符對(duì)應(yīng)的函數(shù):

const modifierGuards: Record<
  string,
  (e: Event, modifiers: string[]) => void | boolean
> = {
  stop: e => e.stopPropagation(),
  prevent: e => e.preventDefault(),
  self: e => e.target !== e.currentTarget,
  ctrl: e => !(e as KeyedEvent).ctrlKey,
  shift: e => !(e as KeyedEvent).shiftKey,
  alt: e => !(e as KeyedEvent).altKey,
  meta: e => !(e as KeyedEvent).metaKey,
  left: e => 'button' in e && (e as MouseEvent).button !== 0,
  middle: e => 'button' in e && (e as MouseEvent).button !== 1,
  right: e => 'button' in e && (e as MouseEvent).button !== 2,
  exact: (e, modifiers) =>
    systemModifiers.some(m => (e as any)[`${m}Key`] && !modifiers.includes(m))
}

v-on作用于組件綁定自定義事件

實(shí)現(xiàn)案例

父組件中 有個(gè)子組件son, 使用v-on綁定了子組件的自定義事件,還有一個(gè)p顯示當(dāng)前的時(shí)間戳。

<Son v-on:children-clicked="childClickedHandler" />
<p>{{ date }}</p>

setup() {

  let childClickedHandler = (data: Date) => {
    date.value = data.getTime();
  }

  let date = ref(new Date().getTime());

  return {
    date,
    childClickedHandler
  };
},

子組件中有一個(gè)div, 每次點(diǎn)擊會(huì)觸發(fā)自定義事件childrenClicked, 并且傳遞了一個(gè)參數(shù)值為當(dāng)前時(shí)間。

<div v-on:click="clickevent">點(diǎn)擊我吧</div>

emits: ["childrenClicked"],
setup(props, {emit}) {

  let clickevent = () => {
    emit('childrenClicked', new Date());
  }
  return {clickevent};
},            

這樣點(diǎn)擊子組件后就會(huì)觸發(fā)父組件的childClickedHandler方法,從而更新當(dāng)前時(shí)間戳的顯示。

接下來我們就來看看這底層的邏輯是如何實(shí)現(xiàn)的?

實(shí)現(xiàn)邏輯

先看下兩個(gè)組件的渲染函數(shù)的重點(diǎn)部分

父組件:

_createVNode(_component_Son, { onChildrenClicked: childClickedHandler }, null, 8 /* PROPS */, ["onChildrenClicked"])

父組件給子組件綁定自定義事件是傳遞了一個(gè)事件pro,這個(gè)pro的名稱用駝峰命名, 例如本例中的onChildrenClicked。

子組件:

const _hoisted_1 = ["onClick"]

_createElementBlock("div", {
    onClick: $event => ($emit('childrenClicked', new Date()))
}, "點(diǎn)擊我吧", 8 /* PROPS */, _hoisted_1)

子組件div點(diǎn)擊的綁定前面說過,點(diǎn)擊的時(shí)候執(zhí)行$emit('childrenClicked', new Date(), 這個(gè)沒有什么特別的。

現(xiàn)在的問題就是為什么子組件$emit('childrenClicked', new Date()如何找到父組件的onChildrenClicked方法并執(zhí)行?

$emit來自于createSetupContext函數(shù)調(diào)用時(shí)候傳入的參數(shù)setupContext

export function createSetupContext(
  instance: ComponentInternalInstance
): SetupContext {
    return {
      get attrs() {
        return attrs || (attrs = createAttrsProxy(instance))
      },
      slots: instance.slots,
      emit: instance.emit,
      expose
    }
  }
}

$emit就是組件實(shí)例的emit方法。

實(shí)例的emit方法用于尋找對(duì)應(yīng)的自定義事件的函數(shù)

export function emit(
  instance: ComponentInternalInstance,
  event: string,
  ...rawArgs: any[]
) {
  const props = instance.vnode.props || EMPTY_OBJ

  // 傳入的傳參
  let args = rawArgs
  
  // TODO: 處理v-mode的方法
  const isModelListener = event.startsWith('update:')

  // 處理函數(shù)名,on+首字母大寫的函數(shù)名 或者 on+駝峰命名的函數(shù)名 
  let handlerName
  let handler =
    props[(handlerName = toHandlerKey(event))] ||
    props[(handlerName = toHandlerKey(camelize(event)))]
  if (!handler && isModelListener) {
    handler = props[(handlerName = toHandlerKey(hyphenate(event)))]
  }

  if (handler) {
    // 調(diào)用函數(shù),參數(shù)是外部傳入的參數(shù)
    callWithAsyncErrorHandling(
      handler,
      instance,
      ErrorCodes.COMPONENT_EVENT_HANDLER,
      args
    )
  }
  
}

如果函數(shù)名以update:開頭,說明是一個(gè)v-model的修改數(shù)據(jù)函數(shù),這部分邏輯會(huì)在v-model專門的文章中介紹;

然后在實(shí)例對(duì)象的props中找 on+首字母大寫的函數(shù)名 的函數(shù),如果沒找到,則找 on+首字母大寫且駝峰命名的函數(shù)名 的函數(shù);

如果找到了對(duì)應(yīng)的函數(shù),則調(diào)用函數(shù),調(diào)用函數(shù)的參數(shù)為傳入的參數(shù)。

總結(jié)

v-on作用于普通元素底層是利用 addEventListenerremoveEventListener,修飾符要么利用W3C標(biāo)準(zhǔn),要么利用函數(shù)調(diào)用來實(shí)現(xiàn);

v-on作用于組件是 子組件利用 emitpro 中搜尋到對(duì)應(yīng)的函數(shù)(由父組件傳入),然后執(zhí)行對(duì)應(yīng)的函數(shù)。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Vue表單組件進(jìn)階之如何打造屬于你的自定義v-model

    Vue表單組件進(jìn)階之如何打造屬于你的自定義v-model

    在Vue中自定義組件是一個(gè)強(qiáng)大的功能,它允許你將可重用的代碼封裝成獨(dú)立的模塊,從而提高開發(fā)效率和代碼的可維護(hù)性,這篇文章主要介紹了Vue表單組件進(jìn)階之如何打造屬于你的自定義v-model的相關(guān)資料,需要的朋友可以參考下
    2025-11-11
  • vue實(shí)現(xiàn)動(dòng)態(tài)添加元素(可刪除)

    vue實(shí)現(xiàn)動(dòng)態(tài)添加元素(可刪除)

    文章介紹了如何在Vue中動(dòng)態(tài)添加和刪除元素,通過使用Vue的響應(yīng)式數(shù)據(jù)和v-for指令,可以輕松地實(shí)現(xiàn)這一功能,文章還詳細(xì)講解了如何處理元素的添加和刪除事件,以及如何更新視圖以反映這些變化
    2024-12-12
  • vue-quill-editor二次封裝,實(shí)現(xiàn)自定義文件上傳方式

    vue-quill-editor二次封裝,實(shí)現(xiàn)自定義文件上傳方式

    這篇文章主要介紹了vue-quill-editor二次封裝,實(shí)現(xiàn)自定義文件上傳方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • Vue實(shí)現(xiàn)根據(jù)hash高亮選項(xiàng)卡

    Vue實(shí)現(xiàn)根據(jù)hash高亮選項(xiàng)卡

    這篇文章主要為大家詳細(xì)介紹了Vue實(shí)現(xiàn)根據(jù)hash高亮選項(xiàng)卡,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-05-05
  • Vue的Options用法說明

    Vue的Options用法說明

    這篇文章主要介紹了Vue的Options用法說明,具有很好的參考價(jià)值,希望對(duì)大家有所
    2020-08-08
  • Vue如何清空對(duì)象

    Vue如何清空對(duì)象

    這篇文章主要介紹了Vue如何清空對(duì)象,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • 如何使用Webpack優(yōu)化Vue項(xiàng)目的打包流程

    如何使用Webpack優(yōu)化Vue項(xiàng)目的打包流程

    在開發(fā)基于Vue.js的應(yīng)用時(shí),隨著項(xiàng)目規(guī)模的擴(kuò)大,單個(gè)文件的體積也會(huì)隨之增長,特別是當(dāng)涉及到大量的依賴庫和復(fù)雜的業(yè)務(wù)邏輯時(shí),本文將詳細(xì)介紹如何使用Webpack來優(yōu)化Vue項(xiàng)目的打包流程,需要的朋友可以參考下
    2024-09-09
  • Vue解決ajax跨域的問題

    Vue解決ajax跨域的問題

    這篇文章主要介紹了Vue解決ajax跨域的問題,跨域請(qǐng)求:是指協(xié)議名、主機(jī)名、端口號(hào)三者有任何一個(gè)不一樣,而且跨域請(qǐng)求是請(qǐng)求發(fā)出去了,服務(wù)器接收并返回了結(jié)果,只是瀏覽器沒有接收響應(yīng)結(jié)果。感興趣的同學(xué)可以參考閱讀
    2023-04-04
  • 詳解Vue.js搭建路由報(bào)錯(cuò) router.map is not a function

    詳解Vue.js搭建路由報(bào)錯(cuò) router.map is not a function

    這篇文章主要介紹了詳解Vue.js搭建路由報(bào)錯(cuò) router.map is not a function,非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2017-06-06
  • vue.js實(shí)現(xiàn)的經(jīng)典計(jì)算器/科學(xué)計(jì)算器功能示例

    vue.js實(shí)現(xiàn)的經(jīng)典計(jì)算器/科學(xué)計(jì)算器功能示例

    這篇文章主要介紹了vue.js實(shí)現(xiàn)的經(jīng)典計(jì)算器/科學(xué)計(jì)算器功能,具有基本四則運(yùn)算計(jì)算器以及科學(xué)計(jì)算器的功能,可實(shí)現(xiàn)開方、乘方、三角函數(shù)以及公式運(yùn)算等功能,需要的朋友可以參考下
    2018-07-07

最新評(píng)論

万年县| 迁安市| 醴陵市| 衡东县| 中牟县| 于都县| 旬阳县| 齐齐哈尔市| 彰武县| 河池市| 保定市| 洪雅县| 收藏| 四会市| 米林县| 盐亭县| 河津市| 龙口市| 淳化县| 婺源县| 惠东县| 田林县| 水富县| 中卫市| 油尖旺区| 靖江市| 绥滨县| 图们市| 峡江县| 黄大仙区| 巫山县| 独山县| 南宁市| 万安县| 孟村| 通城县| 夏津县| 杨浦区| 蕉岭县| 赞皇县| 桃源县|