v-track
Version:
一个基于Vue指令的埋点插件
966 lines (811 loc) • 26.8 kB
JavaScript
function _typeof(obj) {
if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
_typeof = function (obj) {
return typeof obj;
};
} else {
_typeof = function (obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
};
}
return _typeof(obj);
}
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
function _defineProperty(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
function _objectSpread(target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i] != null ? arguments[i] : {};
var ownKeys = Object.keys(source);
if (typeof Object.getOwnPropertySymbols === 'function') {
ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) {
return Object.getOwnPropertyDescriptor(source, sym).enumerable;
}));
}
ownKeys.forEach(function (key) {
_defineProperty(target, key, source[key]);
});
}
return target;
}
function _objectWithoutPropertiesLoose(source, excluded) {
if (source == null) return {};
var target = {};
var sourceKeys = Object.keys(source);
var key, i;
for (i = 0; i < sourceKeys.length; i++) {
key = sourceKeys[i];
if (excluded.indexOf(key) >= 0) continue;
target[key] = source[key];
}
return target;
}
function _objectWithoutProperties(source, excluded) {
if (source == null) return {};
var target = _objectWithoutPropertiesLoose(source, excluded);
var key, i;
if (Object.getOwnPropertySymbols) {
var sourceSymbolKeys = Object.getOwnPropertySymbols(source);
for (i = 0; i < sourceSymbolKeys.length; i++) {
key = sourceSymbolKeys[i];
if (excluded.indexOf(key) >= 0) continue;
if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue;
target[key] = source[key];
}
}
return target;
}
function _slicedToArray(arr, i) {
return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest();
}
function _toConsumableArray(arr) {
return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread();
}
function _arrayWithoutHoles(arr) {
if (Array.isArray(arr)) {
for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];
return arr2;
}
}
function _arrayWithHoles(arr) {
if (Array.isArray(arr)) return arr;
}
function _iterableToArray(iter) {
if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter);
}
function _iterableToArrayLimit(arr, i) {
var _arr = [];
var _n = true;
var _d = false;
var _e = undefined;
try {
for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
_arr.push(_s.value);
if (i && _arr.length === i) break;
}
} catch (err) {
_d = true;
_e = err;
} finally {
try {
if (!_n && _i["return"] != null) _i["return"]();
} finally {
if (_d) throw _e;
}
}
return _arr;
}
function _nonIterableSpread() {
throw new TypeError("Invalid attempt to spread non-iterable instance");
}
function _nonIterableRest() {
throw new TypeError("Invalid attempt to destructure non-iterable instance");
}
/*
* @Author: 宋慧武
* @Date: 2019-04-08 11:13:34
* @Last Modified by: 宋慧武
* @Last Modified time: 2019-08-05 15:31:00
*/
/**
* @desc 判断给定变量是否为一个函数
*
* @param {*} v
* @return {Boolean}
*/
var isFun = function isFun(v) {
return typeof v === "function" || false;
};
/**
* @desc 判断给定变量是否是未定义
*
* @param {*} v
*/
var isUndef = function isUndef(v) {
return v === undefined || v === null;
};
/**
* @desc 判断给定变量是否是定义
*
* @param {*} v
*/
var isDef = function isDef(v) {
return v !== undefined && v !== null;
};
/**
* @desc 获取对象的键值
*
* @param {Object} value
* @returns {Array} [keys, values]
*/
function zipArray() {
var value = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
return [Object.values(value), Object.keys(value)];
}
/**
* @desc 防抖函数,至少间隔200毫秒执行一次
*
* @param {Function} fn callback
* @param {Number} [ms=200] 默认200毫秒
* @returns {Function}
*/
function debounce(fn) {
var ms = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 200;
var timeoutId;
return function () {
var _this = this;
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
clearTimeout(timeoutId);
timeoutId = setTimeout(function () {
return fn.apply(_this, args);
}, ms);
};
}
/**
* @desc 判断给定变量是否完全匹配目标数组
*
* @param {String[]} mdfs 目标数组
* @param {String} vals
* @returns {Boolean}
*/
function _exactMatch(mdfs, vals) {
var keys = Object.keys(mdfs);
return keys.length === vals.length && vals.every(function (v) {
return keys.includes(v);
});
}
/**
* @desc 判断给定变量是否匹配目标数组的一部分
*
* @param {String[]} mdfs 目标字符串数组
* @param {String} vals
* @returns {Boolean}
*/
function _partialMatch(mdfs, vals) {
var keys = Object.keys(mdfs);
return vals.some(function (v) {
return keys.includes(v);
});
}
/**
* @desc 判断两个节点是否为同一个vnode节点
*
* @param {VNode} a 虚拟节点
* @param {VNode} b 虚拟节点
*/
function sameVnode(a, b) {
return a.key === b.key && a.tag === b.tag && a.isComment === b.isComment && isDef(a.data) === isDef(b.data);
}
/**
* @desc 判断两个vnode节点是否全等
*
* @param {VNode} a 虚拟节点
* @param {VNode} b 虚拟节点
*/
function exactlySameVnode(vnode, oldVnode) {
if (!sameVnode(vnode, oldVnode)) return false;
var oldCh = oldVnode.children;
var ch = vnode.children; // vnode为非文本节点,且新旧节点的子节点都存在但不相同
if (isUndef(vnode.text) && isDef(oldCh) && isDef(ch)) {
if (oldCh.length !== ch.length) return false;
for (var i = 0; i < ch.length; i++) {
var c = ch[i];
if (isDef(c) && isDef(oldCh[i])) {
return exactlySameVnode(c, oldCh[i]);
}
}
} // vnode为文本节点,新旧节点内容不相同
else if (vnode.text !== oldVnode.text) return false;
return true;
}
/*
* @Author: 宋慧武
* @Date: 2019-04-14 15:55:15
* @Last Modified by: 宋慧武
* @Last Modified time: 2019-04-20 18:06:31
*/
var checkFun = function checkFun(fn) {
if (!isFun(fn)) {
throw new Error("The first parameter should be Function.");
}
};
/*
* @Author: 宋慧武
* @Date: 2019-04-08 11:13:34
* @Last Modified by: 宋慧武
* @Last Modified time: 2020-06-04 17:08:44
*/
/**
* @desc 是否为元素几点
*
* @param {DOMElement} ele 一个 DOM 元素
* @return {Boolean}
*/
var isElement = function isElement(ele) {
return ele && ele.nodeType === 1;
};
/**
* @desc 获取 DOM CSS 属性的值
*
* @param {DOMElement} ele A DOM 元素
* @returns {String}
*/
function getStylePropValue(ele, prop) {
return window.getComputedStyle(ele).getPropertyValue(prop);
}
/**
* @desc 元素是否在可视区域可见
*
* @param {Object} rect 元素大小及相对可视区域的位置信息
* @returns {Boolean} true => 可见 false => 不可见
*/
function isInViewport(rect, viewport) {
if (!rect || rect.width <= 0 || rect.height <= 0) {
return false;
}
return rect.bottom > 0 && rect.right > 0 && rect.top < window.innerHeight && rect.left < window.innerWidth && !(rect.left > viewport.right || rect.top > viewport.bottom || rect.right < viewport.left || rect.bottom < viewport.top);
}
/**
* @desc 元素是否隐藏
*
* @param {DOMElement} ele A DOM 元素
* @returns {Boolean} true => 未隐藏可见 false => 隐藏不可见
*/
function isVisible(ele) {
if (ele === window.document) {
return true;
}
if (!ele || !ele.parentNode) {
return false;
}
var parent = ele.parentNode;
var visibility = getStylePropValue(ele, "visibility");
var display = getStylePropValue(ele, "display");
if (visibility === "hidden" || display === "none") {
return false;
}
return parent ? isVisible(parent) : true;
}
/**
* @class
* @name VisMonitor
*
* @desc 目标元素控制器
*/
var VisMonitor =
/*#__PURE__*/
function () {
function VisMonitor(ele, ref) {
var refwin = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : window;
var percent = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 1;
_classCallCheck(this, VisMonitor);
if (!isElement(ele)) {
throw new Error("not an element node");
}
if (percent > 1 && percent <= 0) {
throw new Error("'percent' must be a number between 0 and 1");
}
this.ele = ele;
this.ref = ref;
this.refWin = refwin;
this.started = false;
this.percent = percent;
this.prevPerc = null; // 保存前一次曝光百分比
this.listeners = {};
this.removeScrollLisener = null;
this.init();
}
_createClass(VisMonitor, [{
key: "init",
value: function init() {
var _this = this;
if (!this.started) {
var listener = debounce(this.visibilitychange.bind(this));
listener();
this.removeScrollLisener = function (listener) {
if (_this.ref) {
return _this.ref.$on("scroll", listener);
} else {
_this.refWin.addEventListener("scroll", listener, true);
return function () {
return _this.refWin.removeEventListener("scroll", listener, true);
};
}
}(listener);
this.started = true;
}
}
}, {
key: "viewport",
value: function viewport() {
var win = this.refWin;
var rect = isElement(win) ? win.getBoundingClientRect() : win;
return {
top: isElement(win) ? rect.top : 0,
right: rect.right || rect.innerWidth,
bottom: rect.bottom || rect.innerHeight,
left: rect.left || 0,
height: win.innerHeight || win.offsetHeight,
width: win.innerWidth || win.offsetWidth
};
}
/**
* 监听自定义事件
*/
}, {
key: "$on",
value: function $on(evt, cbk) {
var queue = this.listeners[evt] || (this.listeners[evt] = []);
queue.push(cbk);
return this;
}
/**
* 移除监听自定义事件
*/
}, {
key: "$off",
value: function $off(evt, cbk) {
if (!cbk) return;
var queue = this.listeners[evt];
var v;
var i = queue.length;
while (i--) {
v = queue[i];
if (v === cbk || v.cbk === cbk) {
queue.splice(i, 1);
break;
}
}
return this;
}
/**
* 监听自定义事件,但只触发一次
*/
}, {
key: "$once",
value: function $once(evt, cbk) {
var _this2 = this;
var on = function on() {
_this2.$off(evt, on);
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
cbk.apply(_this2, args);
};
on.cbk = cbk;
this.$on(evt, on);
return this;
}
/**
* 触发当前实例的监听回调
*/
}, {
key: "$emit",
value: function $emit(evt) {
var _this3 = this;
for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
args[_key2 - 1] = arguments[_key2];
}
var queue = this.listeners[evt] || [];
queue.forEach(function (sub) {
return sub.apply(_this3, args);
});
return this;
}
/**
* 计算元素可见比例,如果比例为100%,则触发 fullyvisible 事件
*/
}, {
key: "visibilitychange",
value: function visibilitychange() {
var rect = this.ele.getBoundingClientRect();
var view = this.viewport();
if (!isInViewport(rect, view) || !isVisible(this.ele)) {
this.prevPerc = 0;
return 0;
}
var vh = 0;
var vw = 0;
var perc = 0;
if (view.top < 0) {
view.top = 0;
}
if (view.bottom > window.innerHeight) {
view.bottom = window.innerHeight;
}
if (view.left < 0) {
view.left = 0;
}
if (view.right > window.innerWidth) {
view.right = window.innerWidth;
}
if (rect.top >= view.top && rect.bottom > view.bottom) {
vh = view.bottom - rect.top;
} else if (rect.top < view.top && rect.bottom <= view.bottom) {
vh = rect.bottom - view.top;
} else {
vh = rect.height;
}
if (rect.left >= view.left && rect.right > view.right) {
vw = view.right - rect.left;
} else if (rect.left < view.left && rect.right <= view.right) {
vw = rect.right - view.left;
} else {
vw = rect.width;
}
perc = vh * vw / (rect.height * rect.width);
if (this.prevPerc < this.percent && perc >= this.percent) {
this.$emit("fullyvisible");
this.prevPerc = perc;
}
}
/**
* 销毁当前实例的事件
*/
}, {
key: "destroy",
value: function destroy() {
isFun(this.removeScrollLisener) && this.removeScrollLisener();
}
}]);
return VisMonitor;
}();
var MODIFIERS = ["async", "delay", "watch", "show", "once", "custom"]; // 修饰符
/*******************************************************************************
* @desc 监听数据发生改变时触发埋点,需处理两种情况:
* ① 初始化时开始监听 v-track:xxxxx.watch="{ common_exp }"
* ops.immediate 表示初始化时立即开始监听
*
* ② 点击事件之后开始监听 v-track:18016.click.async="{ refreshHotSpot, exposureId }"
* el.contains(this.target) 避免多个“地方”同时监听同一个值出现多次上报的问题
*******************************************************************************/
function _watcher(el, exp, cbk, ctt) {
var _this = this;
var ops = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
el.$unwatch = ctt.$watch(function () {
return ctt[exp];
}, function (nv, ov) {
nv !== ov && (ops.immediate || el.contains(_this.target)) && cbk();
_this.target = null; // 释放当前操作的watcher
});
}
/*************************************************************************
* @desc 自定义指令 v-track
*
* @param {*} el 指令所绑定的元素
* @param {String} arg 埋点对应event ID
* @param {Boolean} modifiers.click true: 事件行为埋点; false: 页面级埋点
* @param {Boolean} modifiers.watch 异步埋点
* @param {Boolean} modifiers.async 点击事件异步埋点
* @param {Boolean} modifiers.delay 埋点是否延迟执行,默认先执行埋点再执行cbk
*
* @property {Function} tck 对应埋点方法
*
* @example v-track:18015
* @example v-track:18015.watch
* @example v-track:18015.watch.delay
* @example v-track:18015.click
* @example v-track:18015.click.async
* @example v-track:18015.click.delay
* @example v-track:18015.[自定义事件名].delay
* @example v-track:18015.[自定义事件名].async
*************************************************************************/
function bind(el, _ref, _ref2, _, __, events) {
var _this2 = this;
var value = _ref.value,
id = _ref.arg,
modifiers = _ref.modifiers,
rawName = _ref.rawName;
var context = _ref2.context,
componentInstance = _ref2.componentInstance;
if (!events[id]) throw new Error("tracking event does not exist");
var queue = [];
var tck = events[id].bind(null, context);
var watcher = function watcher(exp, cbk, ops) {
return _watcher.call(_this2, el, exp, cbk, context, ops);
};
var exactMatch = function exactMatch() {
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
return _exactMatch.call(null, modifiers, args);
};
var partialMatch = function partialMatch() {
for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
args[_key2] = arguments[_key2];
}
return _partialMatch.call(null, modifiers, args);
};
if (!Object.keys(modifiers).length) {
events[id](context, value);
} // 异步埋点
else if (exactMatch("watch")) {
var exp = Object.keys(value).shift();
watcher(exp, tck, {
immediate: true
});
} // 指定延长时间埋点
else if (exactMatch("delay")) {
el.$timer && clearTimeout(el.$timer);
el.$timer = setTimeout(function () {
events[id](context);
}, value);
} else if (exactMatch("watch", "delay")) {
var delay = value.delay,
args = _objectWithoutProperties(value, ["delay"]);
var _exp = _toConsumableArray(Object.keys(args)).pop();
tck = function tck() {
el.$timer && clearTimeout(el.$timer);
el.$timer = setTimeout(function () {
var visible = isVisible(context.$el);
visible && events[id](context);
}, delay);
};
watcher(_exp, tck, {
immediate: true
});
} // 区域曝光埋点
else if (partialMatch("show")) {
var _events$id;
var _zipArray = zipArray(value),
_zipArray2 = _slicedToArray(_zipArray, 1),
_args = _zipArray2[0];
var _tck = (_events$id = events[id]).bind.apply(_events$id, [null, context].concat(_toConsumableArray(_args)));
var once = partialMatch("once");
var custom = partialMatch("custom");
if (!el.$visMonitor) {
setTimeout(function () {
var vm = new VisMonitor(el, custom && context.$refs[value.ref], value && context.$refs[value.viewport], value && value.percent);
(once ? vm.$once : vm.$on).call(vm, "fullyvisible", _tck);
el.$visMonitor = vm;
}, 0);
}
} else if (!componentInstance && modifiers.click || componentInstance && partialMatch("native")) {
/**
* @desc DOM元素事件行为埋点(需区分是否带参数)
* @var {Function} fn 获取第一个参数作为回调函数
* @var {String} exp 获取最后一个参数并作为监听对象
*/
switch (_typeof(value)) {
case "object":
{
var _events$id2;
var _zipArray3 = zipArray(value),
_zipArray4 = _slicedToArray(_zipArray3, 2),
_args2 = _zipArray4[0],
keys = _zipArray4[1];
var fn = _args2.shift();
var _exp2 = _toConsumableArray(keys).pop();
checkFun(fn);
tck = (_events$id2 = events[id]).bind.apply(_events$id2, [null, context].concat(_toConsumableArray(_args2)));
queue = [tck, fn.bind.apply(fn, [null].concat(_toConsumableArray(_args2)))];
modifiers.delay && queue.reverse();
modifiers.async && watcher(_exp2, queue.shift());
break;
}
case "function":
queue = [tck, value];
modifiers.delay && queue.reverse();
break;
}
el.$listener = function (e) {
_this2.target = e.target;
queue.forEach(function (sub) {
return sub(e);
});
};
el.addEventListener("click", el.$listener);
} else if (
/**
* @desc 组件自定义事件行为埋点(需区分是否带参数)
* @var {Function} fn 获取第一个参数作为回调函数
* @var {String} exp 获取最后一个参数并作为监听对象
*/
componentInstance && componentInstance.$el === el) {
var _args3, _keys, _fn, _exp3;
var eventName = Object.keys(modifiers).filter(function (key) {
return !MODIFIERS.includes(key);
}).pop();
if (_typeof(value) === "object") {
var _zipArray5 = zipArray(value);
var _zipArray6 = _slicedToArray(_zipArray5, 2);
_args3 = _zipArray6[0];
_keys = _zipArray6[1];
_fn = _args3.shift();
_exp3 = _toConsumableArray(_keys).pop();
checkFun(_fn);
}
if (el["$on_".concat(eventName)]) return;
componentInstance.$on(eventName, function () {
var _events$id3, _ref3;
_this2.target = el;
for (var _len3 = arguments.length, data = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
data[_key3] = arguments[_key3];
}
tck = (_events$id3 = events[id]).bind.apply(_events$id3, [null, context].concat(data));
queue = [tck, (_ref3 = _fn || value).bind.apply(_ref3, [null].concat(data))];
modifiers.delay && queue.reverse();
modifiers.async && watcher(_exp3, queue.shift());
queue.forEach(function (sub) {
return sub();
});
el["$on_".concat(eventName)] = true; // 避免重复监听
});
} else {
throw new Error("".concat(rawName, " directive is not supported"));
}
}
/**
* @desc 由于 DOM 更新采用 diff 算法更新,如果新旧节点相同,则 el 会全等,导致 bind 绑定无法更
* 新,出现事件绑定诡异的问题,但由于 DOM update 执行频率很高,会导致性能问题,所以这里加
* 了一层exactlySameVnode过滤,即只有在新旧节点发生变化时才会重新绑定,否则相反
*
* @param {*} el 同bind
* @param {...any} args 同bind
*/
function updated(el) {
if (!el.$listener) return;
for (var _len4 = arguments.length, args = new Array(_len4 > 1 ? _len4 - 1 : 0), _key4 = 1; _key4 < _len4; _key4++) {
args[_key4 - 1] = arguments[_key4];
}
if (!exactlySameVnode(args[1], args[2])) {
unbind.call(this, el);
bind.call.apply(bind, [this, el].concat(args));
}
}
function unbind(el) {
el.$listener && el.removeEventListener("click", el.$listener);
el.$timer && clearTimeout(el.$timer);
el.$unwatch && el.$unwatch();
el.$visMonitor && el.$visMonitor.destroy();
}
var VTrack =
/*#__PURE__*/
function () {
function VTrack() {
_classCallCheck(this, VTrack);
this.installed = false;
} // 保存当前点击的元素
_createClass(VTrack, null, [{
key: "install",
// Vue.use 将执行此方法
value: function install(Vue, _ref) {
var _this = this;
var trackEvents = _ref.trackEvents,
_ref$trackEnable = _ref.trackEnable,
trackEnable = _ref$trackEnable === void 0 ? {} : _ref$trackEnable;
trackEnable = _objectSpread({
UVPV: false,
TONP: false
}, trackEnable);
var TRACK_TONP = function TRACK_TONP(ctx, et) {
if (trackEnable.TONP) {
trackEvents.TONP(ctx, {
et: et,
dt: Date.now()
});
}
};
if (this.installed) return;
this.installed = true; // 注册v-track全局指令
Vue.directive("track", {
bind: function bind$1() {
var _hooks$bind;
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
return (_hooks$bind = bind).call.apply(_hooks$bind, [_this].concat(args, [trackEvents]));
},
componentUpdated: function componentUpdated() {
var _hooks$updated;
for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
args[_key2] = arguments[_key2];
}
return (_hooks$updated = updated).call.apply(_hooks$updated, [_this].concat(args, [trackEvents]));
},
unbind: function unbind$1() {
var _hooks$unbind;
for (var _len3 = arguments.length, args = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
args[_key3] = arguments[_key3];
}
return (_hooks$unbind = unbind).call.apply(_hooks$unbind, [_this].concat(args));
}
}); // 注册<track-view>全局组件
Vue.component("TrackView", {
render: function render(h) {
return h("span", {
style: "display: none"
});
}
});
Vue.mixin({
data: function data() {
return {
PAGE_ENTER_TIME: Date.now()
};
},
created: function created() {
var _this2 = this;
window.onbeforeunload = function () {
return TRACK_TONP(_this2, _this2.PAGE_ENTER_TIME);
};
},
// 统计UV、PV
beforeRouteEnter: function beforeRouteEnter(_, __, next) {
next(function (vm) {
trackEnable.UVPV && trackEvents.UVPV(vm);
});
},
beforeRouteUpdate: function beforeRouteUpdate(_, __, next) {
var _this3 = this;
// 确保导航升级完成
this.$watch("$route", function () {
if (trackEnable.UVPV && trackEnable.UVPV === "routeUpdate") {
trackEvents.UVPV(_this3);
}
});
next();
},
// 页面停留时间
beforeRouteLeave: function beforeRouteLeave(_, __, next) {
TRACK_TONP(this, this.PAGE_ENTER_TIME);
next();
}
});
}
}]);
return VTrack;
}();
_defineProperty(VTrack, "target", null);
export default VTrack;