背景

公司以前采纳GrowingIo埋点,都是在每个我的项目中引入其提供的Web JS SDK,然而最近因为应用期限到了,加上一些其余层面的思考,筹备采纳神策做数据埋点和数据分析,因而须要移除每个我的项目中引入的GrowingIo Web JS SDK,而后引入神策提供的Web JS SDK

计划

如果还是采纳在每个我的项目中独立引入Web JS SDK,首先须要梳理出有多个我的项目有埋点需要,而后安顿告诉每个业务组的前端同学在迭代开发内引入。有没有感觉特地麻烦,明明是同一个Web JS SDK却须要屡次引入,还波及到跨组合作、任务分配、资源安顿等问题。更重要的是,如果下次又更换采纳另一个埋点工具,那反复的事件又要做一遍,吃力不讨好。

因而,咱们心愿可能在某个中央引入后,我的项目就不须要独立引入了,这个计划有没有想到跟网关服务、中间件、拦截器的思维特地像呢?咱们是否能采纳这类思维解决呢?答案是必定的,通过在前端路由服务申请到的动态html脚本中注入神策Web JS SDK

对于一些前后端拆散我的项目能够在前端路由服务中操作动态html脚本注入神策Web JS SDK实现,但对于一些前后端未分离的我的项目(例如freemarker)该怎么解决呢?能在webagent网关做对立解决吗?其实也是可行的,咱们能够判断响应头Content-Type: text/html,如果是,则操作html注入神策Web JS SDK;如果不是,则不做解决。但在理论状况中,鉴于前后端未分离我的项目较少,咱们还是采纳第一种计划,独立在我的项目中引入。

封装神策Web JS SDK

神策有提供全埋点性能,我的项目中心愿应用启用该性能,因而须要再封装,初始化SDK,能够参考如下封装:

;(function () {  /** 判断环境 */  var hostname = window.location.hostname;  var serverUrl = 'https://cj.casstime.com/sa?project=default';  if (hostname === 'www.cassmall.com' || hostname === 'h.cassmall.com') {    serverUrl = 'https://cj.casstime.com/sa?project=production';  }  // 开启全埋点,Web JS SDK 全埋点包含三种事件:Web 页面浏览、Web 元素点击、Web 视区停留  function addShenCeScript() {    window.cassSensors = window['sensorsDataAnalytic201505'];        // 初始化 SDK    window.cassSensors.init({      server_url: serverUrl,      is_track_single_page: true, // 单页面配置,默认开启,若页面中有锚点设计,须要将该配置删除,否则触发锚点会多触发 $pageview 事件      heatmap: {                /**                * Web 元素点击($WebClick)                * 是否开启点击图,default 示意开启,主动采集 $WebClick 事件,能够设置 'not_collect' 示意敞开。                * 默认只有点击 a input button textarea 四种元素时,才会触发 $WebClick 元素点击事件                */        clickmap: 'default',                /**                * 视区停留事件($WebStay)                * 是否开启触达图,default 示意开启,主动采集 $WebStay 事件,能够设置 'not_collect' 示意敞开。                * 须要 Web JS SDK 版本号大于 1.9.1                */        scroll_notice_map: 'default',                // 通过 collect_tags 配置是否开启其余任意元素的全埋点采集(默认不采集),其中 div 通过配置最多能够采集 3 层嵌套状况。        collect_tags: {           div: {            max_level: 3, // 默认是 1,即只反对叶子 div。可配置范畴是 [1, 2, 3],非该范畴配置值,会被当作 1 解决。          },          li: true,          span: true,          i: true,          img: true        },      },    })        // 注册公共属性    window.cassSensors.registerPage({      platform_type: 'web',      path_name: window.location.pathname,    })        /**        * Web 页面浏览($pageview)        * 设置之后,SDK 就会主动收集页面浏览事件,以及设置初始起源。        */    window.cassSensors.quick('autoTrack');        /** 获取用户登录ID,用户登录后,开发人员调用login,将用户登录ID传给SDK,后续该设施上所有事件的distinct_id就会变成用户所对应的登录ID。*/    ajax({      url: '/webim/user/jwt_token',      type: 'GET',      success: function (res) {        try {          var data = JSON.parse(res);          window.cassSensors.login(data.username);        } catch (e) { }      },      error: function (error) { }    });  }  var script = document.createElement('script');  script.setAttribute('type', 'text/javascript');  var explorer = window.navigator.userAgent;  if (explorer.indexOf('MSIE') >= 0) {    // ie    script.onreadystatechange = function () {      if (this.readyState === 'loaded' || this.readyState === 'complete') {        addShenCeScript();      }    }  } else {    // chrome    script.onload = function () {      addShenCeScript();    }  }  script.setAttribute(    'src',    'https://mstatic.cassmall.com/assets/sensors/sensorsdata1.19.4.min.js'  );        /** 封装ajax申请 */  function ajax(params) {    params = params || {};    params.data = params.data || {};    // 判断是ajax申请还是jsonp申请    var json = params.jsonp ? jsonp(params) : json(params);    // ajax申请     function json(params) {      // 申请形式,默认是GET      params.type = (params.type || 'GET').toUpperCase();      // 防止有特殊字符,必须格式化传输数据      params.data = formatParams(params.data);      var xhr = null;      // 实例化XMLHttpRequest对象       if (window.XMLHttpRequest) {        xhr = new XMLHttpRequest();      } else {        // IE6及其以下版本         xhr = new ActiveXObjcet('Microsoft.XMLHTTP');      };      // 监听事件,只有 readyState 的值变动,就会调用 readystatechange 事件      xhr.onreadystatechange = function () {        // readyState属性示意申请/响应过程的以后流动阶段,4为实现,曾经接管到全副响应数据        if (xhr.readyState == 4) {          var status = xhr.status;          // status:响应的HTTP状态码,以2结尾的都是胜利          if (status >= 200 && status < 300) {            var response = '';            // 判断承受数据的内容类型            var type = xhr.getResponseHeader('Content-type');            if (type.indexOf('xml') !== -1 && xhr.responseXML) {              response = xhr.responseXML; //Document对象响应             } else if (type === 'application/json') {              response = JSON.parse(xhr.responseText); //JSON响应             } else {              response = xhr.responseText; //字符串响应             };            // 胜利回调函数            params.success && params.success(response);          } else {            params.error && params.error(status);          }        };      };      // 连贯和传输数据       if (params.type == 'GET') {        // 三个参数:申请形式、申请地址(get形式时,传输数据是加在地址后的)、是否异步申请(同步申请的状况极少);        xhr.open(params.type, params.url + '?' + params.data, true);        xhr.send(null);      } else {        xhr.open(params.type, params.url, true);        //必须,设置提交时的内容类型         xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');        // 传输数据        xhr.send(params.data);      }    }    //格式化参数     function formatParams(data) {      var arr = [];      for (var name in data) {        // encodeURIComponent() :用于对 URI 中的某一部分进行编码        arr.push(encodeURIComponent(name) + '=' + encodeURIComponent(data[name]));      };      // 增加一个随机数参数,避免缓存       arr.push('v=' + random());      return arr.join('&');    }    // 获取随机数     function random() {      return Math.floor(Math.random() * 10000 + 500);    }  }  if (!window.cassSensors) {    document.getElementsByTagName('head')[0].appendChild(script);  }})()