Фронтальная скрытая черная технология

внешний интерфейс JavaScript
Фронтальная скрытая черная технология

предисловие

Недавно я связался с большим количеством бизнеса, поэтому я хочу организовать соответствующую статью и поделиться ею со всеми, чтобы ее было легко прочитать самому.

Так как бизнес реализован с помощью vue2.x, то зарытая точка основана на vue2.x (стек технологий не важен и логика та же).

Если вы хотите играть в одиночку, вы можете использовать статистику скрытых точек Baidu (пакет npm vue-ba):портал

Похороненный

Если это внутренняя статистика скрытых точек, необходимо уточнить время срабатывания скрытых точек:

  • ready: срабатывает при входе на указанную страницу
  • click: срабатывает при нажатии на указанный элемент
  • view: срабатывает, когда открывается глазное яблоко в указанной области
  • выгрузить: срабатывает при выходе с указанной страницы

Похороненный

Введите указанную страницу. Точки триггера являются очень распространенным похороненным поведением, самым простым способом - охранять интерфейс точки поиска маршрута может быть похоронен. Но для того, чтобы не повторять маршрут каждой страницы, написанный охранник, мы можем извлечь унифицированное посыльное поведение.

Здесь используются еще две общие библиотеки инструментов: dayjs, underscore (вам не нужно, это зависит от человека)

import _ from 'underscore'
import dayjs from '@app/js/lib/dayjs'
import tracker from './tracker.js'

export const trackData = (data) => {
  const params = {
    head: { 
      token: 'xxx', // token
      sendTime: dayjs().valueOf(), // 发送时间
    },
    serviceDatas: [{
      eventId: data.id, // 事件ID
      occurTime: dayjs().valueOf(), // 事件触发时间
      serviceParam: data.data, // 事件数据
    }]
  }
  tracker(params)
}

Вы можете увидеть описанный выше метод, просто обработайте данные и вызовите интерфейс, его можно применить к большинству скрытых точек, таких как готовность, щелчок, выгрузка и т. д. Но как представление запускает его? Поскольку это включает в себя ряд проблем, таких как положение элементов, мы инкапсулируем еще один метод, чтобы решить эту проблему раз и навсегда.

import _ from 'underscore'
import Vue from 'vue'

// 判断当前元素是否在可视区域
const isInView = (el) => {

  var rect = el.getBoundingClientRect()
  
  var elemTop = rect.top
  
  var elemBottom = rect.bottom

  // 元素全部出现在视窗
  var isVisible = (elemTop >= 0) && (elemBottom <= window.innerHeight);

  return isVisible;

}

// 生成随机函数属性名
const createFunName = () => {
  return `track_f_${(Math.random() * 1000000 + '').split('.')[0]}`
}

// 已绑定的事件处理函数集合
const FunCollection = {}

// 进入页面处理函数
const readyFun = (el, binding) => {
  const occurTime = +el.dataset.enterTime
  
  const params = getParams(el, binding, occurTime)
  
  tracker(params)
}

// 点击处理函数
const clickFun = (el, binding) => {
  const occurTime = dayjs().valueOf()
  
  const params = getParams(el, binding, occurTime)
  
  tracker(params)
  
  el.removeEventListener('click', FunCollection[el.dataset.clickFun])
}

// 眼球曝光处理函数
const viewFun = _.throttle((el, binding) => {
  if (isInView(el)) {
    const occurTime = dayjs().valueOf()
    
    const params = getParams(el, binding, occurTime)
    
    tracker(params)
    
    window.removeEventListener('scroll', FunCollection[el.dataset.viewFun])
  }
}, 100)

// 离开页面处理函数
const unloadFun = (el, binding) => {
  const occurTime = +el.dataset.enterTime
  
  el.dataset.leaveTime = dayjs().valueOf()
  
  const params = getParams(el, binding, occurTime)
  
  tracker(params)
  
  window.removeEventListener('beforeunload', FunCollection[el.dataset.unloadFun])
}

// 埋点事件逻辑
const track = (el, binding, forceRun = false) => {

  const type = binding.value.act
  
  if (binding.value.act === 'ready') {
    readyFun(el, binding)
  }
  
  if (type === 'click') {
    const cf = el.dataset.clickFun
    
    if (cf && FunCollection[cf]) {
    
      el.removeEventListener('click', FunCollection[cf])
      
      delete FunCollection[cf]
    }
    
    const fs = createFunName()
    
    FunCollection[fs] = clickFun.bind(null, el, binding)
    
    el.dataset.clickFun = fs
    
    el.addEventListener('click', FunCollection[fs])
  }
  
  if (type === 'view') {
    const vf = el.dataset.viewFun
    
    if (vf && FunCollection[vf]) {
    
      window.removeEventListener('scroll', FunCollection[vf])
      
      delete FunCollection[vf]
    }
    
    const fs = createFunName()
    
    FunCollection[fs] = viewFun.bind(null, el, binding)
    
    el.dataset.viewFun = fs
    
    window.addEventListener('scroll', FunCollection[fs])
    
    FunCollection[fs](el, binding)
  }
  
  if (type === 'unload') {
  
    if (forceRun) {
      return unloadFun(el, binding)
    }
    
    const uf = el.dataset.unloadFun
    
    if (uf && FunCollection[uf]) {
    
      window.removeEventListener('beforeunload', FunCollection[uf])
      
      delete FunCollection[uf]
    }
    
    const fs = createFunName()
    
    FunCollection[fs] = unloadFun.bind(null, el, binding)
    
    el.dataset.unloadFun = fs
    
    window.addEventListener('beforeunload', FunCollection[fs])
  }
}
// 自定义指令
Vue.directive('track', {
  bind: function (el, binding) {
    el.dataset.enterTime = dayjs().valueOf()
    
    if((typeof binding.value.t === 'undefined') || binding.value.t === 'bind') {
      track(el, binding)
    }
    
  },
  update: function (el, binding) {
    if(binding.value.t === 'update' || binding.value.act === 'unload') {
      track(el, binding)
    }
  },
  unbind: function (el, binding) {
    el.dataset.leaveTime = dayjs().valueOf()
    
    if (binding.value.act === 'unload') {
      // 如果unbind时还没有unload则强制调用unload处理函数
      track(el, binding, true)
    } else if (binding.value.t === 'unbind') {
      track(el, binding)
    }

    // 移除未触发的事件
    const type = binding.value.act
    
    if (type === 'click') {
      const cf = el.dataset.clickFun
      
      if (cf && FunCollection[cf]) {
      
        el.removeEventListener('click', FunCollection[cf])
        
        delete FunCollection[cf]
      }
    }
    
    if (type === 'view') {
      const vf = el.dataset.viewFun
      
      if (vf && FunCollection[vf]) {
      
        window.removeEventListener('scroll', FunCollection[vf])
        
        delete FunCollection[vf]
      }
    }
    
  }
})

// 处理参数
const getParams = (el, binding, occurTime) => {
  const params = {
    head: { 
      token: 'xxx', // token
      sendTime: dayjs().valueOf(), // 发送时间
    },
    serviceDatas: [{
      eventId: binding.value.id , // 事件ID
      occurTime, // 事件触发时间
      serviceParam: binding.value.data, // 事件数据
    }]
  }
  
  return params
}

Там много чего, опишу кратко (по порядку).

  • isInView определяет, находится ли элемент в видимой области. Это можно настроить в соответствии с личными потребностями. Например, если элемент появляется в области просмотра, он также считается экспозицией.isVisible = elemTop < window.innerHeight && elemBottom >= 0можно отрегулировать или отрегулировать самостоятельно.

  • createFunName случайным образом генерирует имя атрибута функции.Поскольку нам нужно спрятать точки в нескольких местах, нам нужно сгенерировать несколько функций с одной и той же функцией, но с разными именами и разместить их под окном для прослушивания, а также удалить несработавшие события в любое время.

  • Методы, запускаемые каждой ситуацией readyFun, clickFun, viewFun и unloadFun.

  • Логика отслеживания скрытых точек щелчка и прокрутки. Само собой разумеется, слушайте события щелчка и прокрутки. beforeunload — это событие перед уходом страницы, которое можно использовать для замены функции защиты маршрутизации, о которой мы упоминали ранее.

  • Пользовательская команда вызывает метод скрытой точки при привязке, обновлении и отвязке соответственно. Например, в случае выгрузки скрытая точка будет срабатывать только тогда, когда страница покидает страницу.Нам нужно поместить ее в upadte, чтобы вызвать метод скрытой точки, вместо того, чтобы запускать ее, как только она будет привязана к привязке. В качестве другого примера, в unbind нам нужно иметь дело с некоторыми особыми случаями, например, если метод скрытой точки не запускается в течение всего цикла инструкции, он должен быть запущен один раз при unbind. И удалить несработавшие события.

Использование на странице (пример):

<template>
    <div v-track="{ act: 'unload', id: 1, data: { id: 1 }}">
        content
    </div>
</template>
// so on ...

Вышеупомянутая точка захоронения, которая отслеживает уход страницы, и уход вызывает поведение точки захоронения.

Значения, которые может принимать action, — это ситуации, которые мы перечислили выше: готовность, щелчок, просмотр и выгрузка.

id — это тип события.

data — присоединенный параметр, в зависимости от того, что нужно.

Если вы столкнулись со сценой, где команда не может завершить точку захоронения, вы можете напрямую вызвать метод (trackData), который мы инкапсулировали в начале, не передавая тип, просто вызовите его напрямую:

trackData({
    id: 1,
    data: {
        id: 1
    }
})

Наконец

Я видел все это здесь, тебе не нравится?

Обратите внимание на общедоступный номер: интерфейс Biscuit, получите больше знаний о интерфейсе ~