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

Vue3中進行點擊事件埋點

 更新時間:2026年01月21日 10:09:08   作者:jeff渣渣富  
本文主要介紹了Vue3中進行點擊事件埋點,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

函數(shù)埋點是一個常見的需求,之前進行埋點是通過 babel 進行解析插入或者手工進行添加

前幾天在某 babel 群里有人提出疑問:如何在 Vue 中對每個點擊事件插入一個函數(shù)?由于 .vue 文件是將 <template><script><style> 分開進行單獨解析,所以不能通過 babel 將監(jiān)聽函數(shù)準確解析出來(或許自己沒好好看文檔,不知道??‍♂?。要補補 webpack 了)。于是想了如下方法:

通過修改源碼的方式添加額外函數(shù)

如果公司有自己魔改的 Vue 框架,還是挺方便的。源碼版本為:3.2.20,位于 runtime-dom/src/modules/events.ts:

export function patchEvent(
  el: Element & { _vei?: Record<string, Invoker | undefined> },
  rawName: string,
  prevValue: EventValue | null,
  nextValue: EventValue | null,
  instance: ComponentInternalInstance | null = null
) {
  const invokers = el._vei || (el._vei = {})
  const existingInvoker = invokers[rawName]
  
  //TODO: 添加額外函數(shù)

  if (nextValue && existingInvoker) {
    // 更新監(jiān)聽函數(shù)
    existingInvoker.value = nextValue
  } else {
    const [name, options] = parseName(rawName)
    if (nextValue) {
      // 添加監(jiān)聽函數(shù)
      const invoker = (invokers[rawName] = createInvoker(nextValue, instance))
      addEventListener(el, name, invoker, options)
    } else if (existingInvoker) {
      // 移除監(jiān)聽函數(shù)
      removeEventListener(el, name, existingInvoker, options)
      invokers[rawName] = undefined
    }
  }
}

patchEvent 方法中,通過 createInvoker 為監(jiān)聽函數(shù)封裝了一層用于執(zhí)行額外任務,隨后添加至元素的事件回調(diào)隊列中:

function createInvoker(
  initialValue: EventValue,
  instance: ComponentInternalInstance | null
) {
  const invoker: Invoker = (e: Event) => {
    const timeStamp = e.timeStamp || _getNow()

    if (skipTimestampCheck || timeStamp >= invoker.attached - 1) {
      callWithAsyncErrorHandling(
        patchStopImmediatePropagation(e, invoker.value),
        instance,
        ErrorCodes.NATIVE_EVENT_HANDLER,
        [e]
      )
    }
  }
  invoker.value = initialValue
  invoker.attached = getNow()
  return invoker
}

patchStopImmediatePropagation 的作用是:在 invoker.value 是數(shù)組的情況下生成新的監(jiān)聽函數(shù)數(shù)組,檢查 e._stopped 決定是否執(zhí)行監(jiān)聽函數(shù)

callWithAsyncErrorHandling 的作用是:執(zhí)行函數(shù)隊列,函數(shù)的參數(shù)為傳入的 第四個參數(shù)的解構(gòu)

實現(xiàn)功能

function createInvokerValueWithSecretFunction (rawName: string, v: EventValue | null) {
  if (!v) return v

  const targetName = 'click'

  const [name] = parseName(rawName)
  const newValue: EventValue = isArray(v) ? v : [v]

  if (name === targetName) {
    newValue.unshift(insertFunction)
  }

  return newValue

  function insertFunction (e: Event) {
    console.log('Hello Click')
  }
}

/**
 * 完成之前的TODO
 *  - //TODO: 添加額外函數(shù)
 *  + nextValue = createInvokerValueWithSecretFunction(rawName, nextValue)
 */

通過 Vue 插件的方式添加額外函數(shù)

目的是在 生在渲染函數(shù) 時增加額外的轉(zhuǎn)換,將類似 @click="fn" 修改為 @click="[insertFn, fn]",更多細節(jié)待讀者完善。由于是在生成渲染函數(shù)時轉(zhuǎn)換,所以 自定義的渲染函數(shù)組件不支持 添加額外函數(shù)

增加額外編譯轉(zhuǎn)換函數(shù)可以直接修改實例配置,考慮和 babel 插件功能相似還是選擇單獨寫成 Vue 插件

相關(guān)源碼介紹,源碼版本為:3.2.20,位于 runtime-core/src/component.ts:

export function finishComponentSetup(
  instance: ComponentInternalInstance,
  isSSR: boolean,
  skipOptions?: boolean
) {
  /* ... */
  if (!instance.render) {
    if (!isSSR && compile && !Component.render) {
      /* ... */
      if (template) {
        /* ... */
        const { isCustomElement, compilerOptions } = instance.appContext.config
        const { delimiters, compilerOptions: componentCompilerOptions } =
          Component
        const finalCompilerOptions: CompilerOptions = extend(
          extend(
            {
              isCustomElement,
              delimiters
            },
            compilerOptions
          ),
          componentCompilerOptions
        )
        /* ... */
        Component.render = compile(template, finalCompilerOptions)
      }
    }
    /* ... */
  }
  
  /* ... */
}

在生成渲染函數(shù)前,會從 instance.appContext.config 即 Vue 實例的全局上下文的配置中獲取編譯配置對象 compilerOptions,也會從當前組件的配置對象獲取編譯配置對象 compilerOptions

位于 runtime-core/src/compile.ts:

export function baseCompile(
  template: string | RootNode,
  options: CompilerOptions = {}
): CodegenResult {
  /* ... */
  const ast = isString(template) ? baseParse(template, options) : template
  /* ... */
  transform(
    ast,
    extend({}, options, {
      prefixIdentifiers,
      nodeTransforms: [
        ...nodeTransforms,
        ...(options.nodeTransforms || []) // user transforms
      ],
      directiveTransforms: extend(
        {},
        directiveTransforms,
        options.directiveTransforms || {} // user transforms
      )
    })
  )
  return generate(
    ast,
    extend({}, options, {
      prefixIdentifiers
    })
  )
}

在生成完抽象語法樹后,通過 transform 進行源碼級別的轉(zhuǎn)換,并可以從中看到會合并使用者傳入的選項:options.nodeTransformsoptions.directiveTransforms,兩者的區(qū)別在于前者是針對所有抽象語法樹的每個節(jié)點,后者是僅針對內(nèi)置指令

實現(xiàn)功能

// 可自行修改,這里簡單描述
type Options = {
  event: string
  fn?: (e: Event) => void
}
function nodeTransformPluginForInsertFunction (app:App, options: Options = { event: 'click' }) {
  const { event, fn } = options

  if (typeof fn !== 'undefined' && typeof fn !== 'function') {
    console.warn(/* 警告 */)
    return
  }

  // 全局添加統(tǒng)一函數(shù),用于渲染函數(shù)獲取
  const globalInsertFnName = '$__insertFunction__'
  app.config.globalProperties[globalInsertFnName] = fn || defaultInsertFunction

  const transformEventOfElement: NodeTransform = (node, context) => {
    if (node.type === NodeTypes.ELEMENT /* 1 */ && node.props && node.props.length > 0) {
      for (let i = 0; i < node.props.length; i++) {
        const prop = node.props[i]

        if (
          /* 指令 */
          prop.type === NodeTypes.DIRECTIVE /* 7 */ && prop.name === 'on'
          /* 符合條件的指令 */
          && prop.arg && prop.arg.type === NodeTypes.SIMPLE_EXPRESSION /* 4 */ && prop.arg.content === event
          /* 確保 exp 存在 */
          && prop.exp && prop.exp.type === NodeTypes.SIMPLE_EXPRESSION /* 4 */ && prop.exp.content.trim()
        ) {
          let trimmedContent = prop.exp.content.trim()

          // 將類似 `@click="fn"` 修改為 `@click="[insertFn, fn]"`
          // 還需要考慮其他情況,文章這里就簡單處理了
          if (trimmedContent[0] === '[') {
            trimmedContent = `[${globalInsertFnName}, ${trimmedContent.substr(1)}`
          } else {
            trimmedContent = `[${globalInsertFnName}, ${trimmedContent}]`
          }

          prop.exp.content = trimmedContent
        }

      }
    }
  }

  // 增加編譯轉(zhuǎn)換函數(shù)
  const nodeTransforms: NodeTransform[] =
    (app.config.compilerOptions as any).nodeTransforms|| ((app.config.compilerOptions as any).nodeTransforms = [])
  nodeTransforms.push(transformEventOfElement)

  function defaultInsertFunction (e: Event) {}
}

還有很多細節(jié)可以完善,可以參考內(nèi)置的轉(zhuǎn)換函數(shù)讓自身的函數(shù)更加健壯

到此這篇關(guān)于Vue3中進行點擊事件埋點的文章就介紹到這了,更多相關(guān)Vue3 點擊事件埋點內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Vue中實現(xiàn)過渡動畫效果實例詳解

    Vue中實現(xiàn)過渡動畫效果實例詳解

    最近在寫vue的一個項目要實現(xiàn)過渡的效果,雖然vue動畫不是強項,庫也多,但是基本的坑還是得踩扎實,下面這篇文章主要給大家介紹了關(guān)于Vue中實現(xiàn)過渡動畫效果的相關(guān)資料,需要的朋友可以參考下
    2022-08-08
  • vue中多路由表頭吸頂實現(xiàn)的幾種布局方式

    vue中多路由表頭吸頂實現(xiàn)的幾種布局方式

    這篇文章主要介紹了vue項目多路由表頭吸頂實現(xiàn)的幾種布局方式,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-04-04
  • vue使用axios的小技巧分享

    vue使用axios的小技巧分享

    這篇文章主要為大家詳細介紹了一些vue使用axios的小技巧,文中的示例代碼講解詳細,對我們深入掌握vue有一定的幫助,感興趣的小伙伴可以跟隨小編一起學習一下
    2023-10-10
  • Vue中slot的使用詳解

    Vue中slot的使用詳解

    這篇文章主要介紹了Vue中slot的使用詳解,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-05-05
  • Vue中watch與watchEffect的區(qū)別詳細解讀

    Vue中watch與watchEffect的區(qū)別詳細解讀

    這篇文章主要介紹了Vue中watch與watchEffect的區(qū)別詳細解讀,watch函數(shù)與watchEffect函數(shù)都是監(jiān)聽器,在寫法和用法上有一定區(qū)別,是同一功能的兩種不同形態(tài),底層都是一樣的,需要的朋友可以參考下
    2023-11-11
  • Vue3.x中使用element-plus的各種方式詳解

    Vue3.x中使用element-plus的各種方式詳解

    這篇文章主要介紹了Vue3.x中使用element-plus的各種方式詳解,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-04-04
  • vue中echarts視圖不更新問題及解決

    vue中echarts視圖不更新問題及解決

    這篇文章主要介紹了vue中echarts視圖不更新問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • vue之計算屬性的緩存computed的用法解讀

    vue之計算屬性的緩存computed的用法解讀

    這篇文章主要介紹了vue之計算屬性的緩存computed的用法解讀,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-11-11
  • vue3+vite+vant4手機端項目實戰(zhàn)記錄

    vue3+vite+vant4手機端項目實戰(zhàn)記錄

    這篇文章主要給大家介紹了關(guān)于vue3+vite+vant4手機端項目實戰(zhàn)的相關(guān)資料,Vue3是一種前端開發(fā)框架,它的目標是提供更好的性能和開發(fā)體驗,需要的朋友可以參考下
    2023-08-08
  • vue3中的render函數(shù)里定義插槽和使用插槽

    vue3中的render函數(shù)里定義插槽和使用插槽

    這篇文章主要介紹了vue3中的render函數(shù)里定義插槽和使用插槽方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-07-07

最新評論

鄢陵县| 邹平县| 施甸县| 拉萨市| 突泉县| 关岭| 定远县| 平湖市| 西昌市| 松溪县| 云和县| 西昌市| 平湖市| 禄劝| 沾益县| 永丰县| 江城| 云阳县| 兴国县| 八宿县| 博野县| 长泰县| 县级市| 桃源县| 临颍县| 四川省| 剑川县| 阳城县| 建德市| 宁远县| 浙江省| 鹤壁市| 湘潭市| 广丰县| 原阳县| 定远县| 甘谷县| 蓝田县| 德昌县| 江陵县| 米泉市|