Where is JavaScript?

Google Analytics 异步方案探讨

一般网站引入 Google Analytics 的方式为通过前端加载 Google 的 trackercode,通过获取的 analytics.js 执行具体的推送。
本文探讨一种异步推送 Google Analytics 的方式

目前 Google Analytics 的两个地址虽 www.google-analytics.com 和 ssl.google-analytics.com 已经可以在墙内正常解析,但一是客户端的网络情况不一,影响 analytics.js 的加载速度,从而影响前端页面的访问速度;二来有些浏览器插件会屏蔽 Google An­a­lyt­ics。这导致了数据统计不准的问题。

前端大神 JerryQu 在<本博客零散优化点汇总> 一文中提到了 Google Analytics 的异步处理方式:

把统计逻辑挪到了服务端;自己生成用户唯一标识,获取访问页面的标题、URL、Referer,获取用户 IP 和浏览器 UA,随着每次访问发给 Google 的统计地址。服务端向 Google 发起的请求是异步的,用户端访问速度丝毫不受影响。

参考 Google Analytics 的 官方文档 ,Google 也提供了 Measurement Protocol

方案调研

目前常见的处理方法是在后端处理和前后端结合处理

具体选择哪种方式需要视项目的具体情况而定,后端处理相对简单,但不一定适用于SSR类型的前端项目,前后端结合看似是一种靠谱的解决方式

方案部署

后端响应接口

本博客后端基于 Koa2 构建,新增 controller ga.js

              
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
/** * @file Google Analytics控制 * @author littlewin(littlewin.wang@gmail.com) */ const request = require('request') const config = require('config/env')[process.env.NODE_ENV || 'development'] class GA { static async post (ctx) { let { query } = ctx.request.body if (!query) { ctx.throw(404, "谷歌分析提交参数为空") } const ip = (ctx.header['x-forwarded-for'] || ctx.header['x-real-ip'] || ctx.ip || ctx.ips[0]) query += `&dh=${config.INFO.site}&tid=${config.INFO.GA}&uip=${ip}` ctx.status = 200, ctx.body = { success: true, message: "谷歌分析已提交" } request.get({ url: `https://www.google-analytics.com/collect${query}`, }) } } module.exports = GA

接收前端传入的 query 参数并添加 Google Analytics ID 和用户 IP 等信息,最后提交一个异步 get 请求推送数据至 https://www.google-analytics.com/collect

前端推送

前端根据首页加载和路由更新等触发来执行到后端的 post 请求

              
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
/** * @file Google 统计分析 * @author littlewin(littlewin.wang@gmail.com) */ import Service from '~/plugins/axios' import Cookies from 'js-cookie' const uuidv4 = require('uuid/v4') /* eslint-disable */ // 只在生产模式加载 if (process.env.NODE_ENV === 'production') { // Generate Client information let clientInfo = (window, document, navigator, location, route) => { let screen = window.screen; let encode = encodeURIComponent; // 从cookie中取_cid, 没有就新建并保存 let cid = Cookies.get('_cid') if (!cid) { cid = uuidv4() Cookies.set('_cid', cid) } // 组装query let data = [ 'v=1', 't=pageview', 'cid=' + cid, 'tid=' + 'UA-108992108-1', 'dt=' + encode(route.fullPath), 'dr=' + encode(document.referrer), 'dp=' + encode(route.fullPath), 'ul=' + (navigator.language || navigator.browserLanguage), 'sd=' + screen.colorDepth + '-bit', 'sr=' + screen.width + 'x' + screen.height, 'z=' + (+new Date) ] return '?' + data.join('&') } // 应用挂载后 window.onNuxtReady((app) => { // 告诉后端统计分析服务 增加新的页面访问统计 Service.post('/ga', { query: clientInfo(window, document, navigator, location, app.$route) }) // 页面路由切换 app.$nuxt.$on('routeChanged', (to, from) => { // 告诉后端统计分析服务 增加新的页面访问统计 Service.post('/ga', { query: clientInfo(window, document, navigator, location, to) }) }) }) }

性能分析

测试在同样的平台和网络环境下

同步情况下,trackercode 执行推送的时间
https://static.littlewin.wang/blog/31-1.png

采用本文讨论的异步方案
https://static.littlewin.wang/blog/31-2.png

异步方案性能是完胜同步方案的,而且有优化的空间

  • 取消post前的options请求
  • 增强后端服务器性能
  • 优化后端代码,用性能更好的NodeJS框架

本文于 2017-11-29  发布在  编程  分类下, 当前已被围观 653 次

并被添加「 前端JavaScriptNode.js 」标签

本站使用「 署名 4.0 国际」创作共享协议