原文出处

前者品质与非常申报

2018/08/22 · 底工技能 ·
性能

初藳出处: counterxing   

概述

对自此台开采来讲,记录日志是风流倜傥种十二分置身事外的付出习于旧贯,常常咱们会使用try...catch代码块来积极抓获错误、对于每一回接口调用,也会记录下每一回接口调用的时日消耗,以便大家监察和控制服务器接口质量,举办难点逐个审查。

刚进集团时,在张开Node.js的接口开荒时,笔者不太习于旧贯每一趟排查难题都要透过跳板机登上服务器看日志,后来逐步习贯了这种艺术。

举个例证:

JavaScript

金沙网址,/** * 获取列表数据 * @parma req, res */ exports.getList = async
function (req, res) { //获取央浼参数 const openId =
req.session.userinfo.openId; logger.info(`handler getList, user openId
is ${openId}`); try { // 得到列表数据 const startTime = new
Date().getTime(); let res = await ListService.getListFromDB(openId);
logger.info(`handler getList, ListService.getListFromDB cost time ${new
Date().getTime() – startDate}`); // 对数码管理,重临给前端 // … }
catch(error) { logger.error(`handler getList is error,
${JSON.stringify(error)}`); } };

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 获取列表数据
* @parma req, res
*/
exports.getList = async function (req, res) {
    //获取请求参数
    const openId = req.session.userinfo.openId;
    logger.info(`handler getList, user openId is ${openId}`);
 
    try {
        // 拿到列表数据
        const startTime = new Date().getTime();
        let res = await ListService.getListFromDB(openId);
        logger.info(`handler getList, ListService.getListFromDB cost time ${new Date().getTime() – startDate}`);
        // 对数据处理,返回给前端
        // …
    } catch(error) {
        logger.error(`handler getList is error, ${JSON.stringify(error)}`);
    }
};

以下代码平日会自不过然在用Node.js的接口中,在接口中会总结查询DB所耗费时间间、亦可能总括RPC劳动调用所耗时间,以便监测品质瓶颈,对质量做优化;又或者对那几个使用try ... catch积极抓获,以便任何时候对难点实行回看、还原问题的场景,进行bug的修复。

而对此前带来讲呢?能够看之下的光景。

近几来在进展二个须求开垦时,不时开采webgl渲染影像失利的情景,恐怕说影象会冒出解析失利的动静,大家或许根本不精通哪张印象会深入深入分析或渲染战败;又或如近期支付的此外八个须要,大家会做叁个有关webgl渲染时间的优化和形象预加载的须要,若是缺点和失误质量监察和控制,该怎么总计所做的渲染优化和印象预加载优化的优化比例,怎么着验证自身所做的事务具备价值呢?也许是经过测量试验同学的黑盒测量检验,对优化前后的岁月开展录屏,剖析从进来页面到影象渲染落成到底经过了微微帧图像。那样的数额,只怕既不规范、又比较片面,捏造测验同学并不是真正的客户,也无可奈何复苏真实的顾客他们所处的网络情状。回过头来开采,大家的档期的顺序,固然在服务端层面做好了日志和本性总结,但在前面一个对极度的监督和质量的总结。对于前端的品质与那多少个申报的大势研究是有无法贫乏的。

分外捕获

对此前带来讲,大家须要的格外捕获无非为以下三种:

  • 接口调用境况;
  • 页面逻辑是还是不是错误,举个例子,客户走入页面后页面展现白屏;

对此接口调用处境,在前端平常要求上报客商端相关参数,比方:顾客OS与浏览器版本、央浼参数(如页面ID卡塔尔国;而对此页面逻辑是还是不是错误问题,常常除了顾客OS与浏览器版本外,要求的是报错的库房新闻及现实报错地方。

特别捕获方法

全局捕获

能够透过全局监听相当来捕获,通过window.onerror或者addEventListener,看之下例子:

JavaScript

window.onerror = function(errorMessage, scriptU福睿斯I, lineNo, columnNo,
error) { console.log(‘errorMessage: ‘ + errorMessage); // 分外消息console.log(‘scriptULANDI: ‘ + scriptU中华VI); // 十分文件路线console.log(‘lineNo: ‘ + lineNo); // 至极行号 console.log(‘columnNo: ‘ +
columnNo); // 十分列号 console.log(‘error: ‘ + error); // 卓殊仓库消息// … // 卓殊上报 }; throw new Error(‘这是三个错误’);

1
2
3
4
5
6
7
8
9
10
window.onerror = function(errorMessage, scriptURI, lineNo, columnNo, error) {
  console.log(‘errorMessage: ‘ + errorMessage); // 异常信息
  console.log(‘scriptURI: ‘ + scriptURI); // 异常文件路径
  console.log(‘lineNo: ‘ + lineNo); // 异常行号
  console.log(‘columnNo: ‘ + columnNo); // 异常列号
  console.log(‘error: ‘ + error); // 异常堆栈信息
  // …
  // 异常上报
};
throw new Error(‘这是一个错误’);

金沙网址 1

通过window.onerror事件,能够博得实际的那三个音信、相当文件的UENCOREL、十分的行号与列号及极其的商旅音讯,再捕获卓殊后,统生龙活虎申报至我们的日记服务器。

亦或是,通过window.addEventListener主意来开展非常申报,道理同理:

JavaScript

window.add伊夫ntListener(‘error’, function() { console.log(error); // …
// 非常上报 }); throw new Error(‘那是多少个错误’);

1
2
3
4
5
6
window.addEventListener(‘error’, function() {
  console.log(error);
  // …
  // 异常上报
});
throw new Error(‘这是一个错误’);

金沙网址 2

try… catch

使用try... catch尽管能够较好地扩充非常捕获,不至于使得页面由于大器晚成处错误挂掉,但try ... catch抓获情势呈现过分肥壮,多数代码应用try ... catch包装,影响代码可读性。

家常便饭难点

跨域脚本不恐怕准确捕获十分

平时说来状态下,大家会把静态能源,如JavaScript本子放到专门的静态能源服务器,亦恐怕CDN,看以下例子:

<!DOCTYPE html> <html> <head>
<title></title> </head> <body> <script
type=”text/javascript”> // 在index.html window.onerror =
function(errorMessage, scriptULacrosseI, lineNo, columnNo, error) {
console.log(‘errorMessage: ‘ + errorMessage); // 极度新闻console.log(‘scriptUPAJEROI: ‘ + scriptUENVISIONI); // 非常文件路径console.log(‘lineNo: ‘ + lineNo); // 非凡行号 console.log(‘columnNo: ‘ +
columnNo); // 卓殊列号 console.log(‘error: ‘ + error); // 分外饭店信息// … // 卓殊上报 }; </script> <script
src=”./error.js”></script> </body> </html>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!DOCTYPE html>
<html>
<head>
  <title></title>
</head>
<body>
  <script type="text/javascript">
    // 在index.html
    window.onerror = function(errorMessage, scriptURI, lineNo, columnNo, error) {
      console.log(‘errorMessage: ‘ + errorMessage); // 异常信息
      console.log(‘scriptURI: ‘ + scriptURI); // 异常文件路径
      console.log(‘lineNo: ‘ + lineNo); // 异常行号
      console.log(‘columnNo: ‘ + columnNo); // 异常列号
      console.log(‘error: ‘ + error); // 异常堆栈信息
      // …
      // 异常上报
    };
 
  </script>
  <script src="./error.js"></script>
</body>
</html>

JavaScript

// error.js throw new Error(‘那是二个谬误’);

1
2
// error.js
throw new Error(‘这是一个错误’);

金沙网址 3

结果突显,跨域之后window.onerror终身捕获不到科学的充足音信,而是统生机勃勃重临三个Script error

消除方案:对script标签增添三个crossorigin=”anonymous”,而且服务器增加Access-Control-Allow-Origin

<script src=””
crossorigin=”anonymous”></script>

1
<script src="http://cdn.xxx.com/index.js" crossorigin="anonymous"></script>

sourceMap

日常来说在生育碰到下的代码是因而webpack卷入后减去混淆的代码,所以大家或然会碰着那样的主题素材,如图所示:

金沙网址 4

大家发掘持有的报错的代码行数都在第大器晚成行了,为何吗?那是因为在分娩情形下,大家的代码被压缩成了意气风发行:

JavaScript

!function(e){var n={};function r(o){if(n[o])return n[o].exports;var
t=n[o]={i:o,l:!1,exports:{}};return
e[o].call(t.exports,t,t.exports,r),t.l=!0,t.exports}r.m=e,r.c=n,r.d=function(e,n,o){r.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:o})},r.r=function(e){“undefined”!=typeof
Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:”Module”}),Object.defineProperty(e,”__esModule”,{value:!0})},r.t=function(e,n){if(1&n&&(e=r(e)),8&n)return
e;if(4&n&&”object”==typeof e&&e&&e.__esModule)return e;var
o=Object.create(null);if(r.r(o),Object.defineProperty(o,”default”,{enumerable:!0,value:e}),2&n&&”string”!=typeof
e)for(var t in e)r.d(o,t,function(n){return e[n]}.bind(null,t));return
o},r.n=function(e){var n=e&&e.__esModule?function(){return
e.default}:function(){return e};return
r.d(n,”a”,n),n},r.o=function(e,n){return
Object.prototype.hasOwnProperty.call(e,n)},r.p=””,r(r.s=0)}([function(e,n){throw
window.onerror=function(e,n,r,o,t){console.log(“errorMessage:
“+e),console.log(“scriptURI: “+n),console.log(“lineNo:
“+r),console.log(“columnNo: “+o),console.log(“error: “+t);var
l={errorMessage:e||null,scriptURI:n||null,lineNo:r||null,columnNo:o||null,stack:t&&t.stack?t.stack:null};if(XMLHttpRequest){var
u=new
XMLHttpRequest;u.open(“post”,”/middleware/errorMsg”,!0),u.setRequestHeader(“Content-Type”,”application/json”),u.send(JSON.stringify(l))}},new
Error(“这是二个乖谬”)}]);

1
!function(e){var n={};function r(o){if(n[o])return n[o].exports;var t=n[o]={i:o,l:!1,exports:{}};return e[o].call(t.exports,t,t.exports,r),t.l=!0,t.exports}r.m=e,r.c=n,r.d=function(e,n,o){r.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:o})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,n){if(1&n&&(e=r(e)),8&n)return e;if(4&n&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(r.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var t in e)r.d(o,t,function(n){return e[n]}.bind(null,t));return o},r.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(n,"a",n),n},r.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},r.p="",r(r.s=0)}([function(e,n){throw window.onerror=function(e,n,r,o,t){console.log("errorMessage: "+e),console.log("scriptURI: "+n),console.log("lineNo: "+r),console.log("columnNo: "+o),console.log("error: "+t);var l={errorMessage:e||null,scriptURI:n||null,lineNo:r||null,columnNo:o||null,stack:t&&t.stack?t.stack:null};if(XMLHttpRequest){var u=new XMLHttpRequest;u.open("post","/middleware/errorMsg",!0),u.setRequestHeader("Content-Type","application/json"),u.send(JSON.stringify(l))}},new Error("这是一个错误")}]);

在自己的花销进程中也遇上过那个难题,小编在支付三个功能组件库的时候,使用npm link了本人的组件库,可是出于组件库被npm link后是包裹后的生育条件下的代码,全数的报错都稳固到了第意气风发行。

消弭办法是开启webpacksource-map,大家运用webpack包装后的浮动的风姿浪漫份.map的本子文件就足以让浏览器对错误地点张开跟踪了。此处可以参照webpack
document。

实质上便是webpack.config.js中丰盛风度翩翩行devtool: 'source-map',如下所示,为示范的webpack.config.js

JavaScript

var path = require(‘path’); module.exports = { devtool: ‘source-map’,
mode: ‘development’, entry: ‘./client/index.js’, output: { filename:
‘bundle.js’, path: path.resolve(__dirname, ‘client’) } }

1
2
3
4
5
6
7
8
9
10
var path = require(‘path’);
module.exports = {
    devtool: ‘source-map’,
    mode: ‘development’,
    entry: ‘./client/index.js’,
    output: {
        filename: ‘bundle.js’,
        path: path.resolve(__dirname, ‘client’)
    }
}

webpack装进后转移对应的source-map,那样浏览器就可以看到牢固到实际错误的岗位:

金沙网址 5

开启source-map的后天不良是宽容性,近些日子唯有Chrome浏览器和Firefox浏览器才对source-map支撑。可是大家对那生机勃勃类境况也许有杀绝办法。能够采用引入npm库来扶助source-map,能够参照mozilla/source-map。这个npm库不仅能够运转在客户端也得以运作在服务端,不过更为推荐的是在服务端使用Node.js对接到到的日志音讯时行使source-map深入分析,以幸免源代码的透漏招致风险,如下代码所示:

JavaScript

const express = require(‘express’); const fs = require(‘fs’); const
router = express.Router(); const sourceMap = require(‘source-map’);
const path = require(‘path’); const resolve = file =>
path.resolve(__dirname, file); // 定义post接口 router.get(‘/error/’,
async function(req, res) { // 获取前端传过来的报错对象 let error =
JSON.parse(req.query.error); let url = error.scriptU大切诺基I; // 压缩文件路线if (url) { let fileUrl = url.slice(url.indexOf(‘client/’)) + ‘.map’; //
map文件路线 // 分析sourceMap let consumer = await new
sourceMap.SourceMapConsumer(fs.readFileSync(resolve(‘../’ + fileUrl),
‘utf8’)); // 再次回到三个promise对象 // 解析原始报错数据 let result =
consumer.originalPositionFor({ line: error.lineNo, // 压缩后的行号
column: error.columnNo // 压缩后的列号 }); console.log(result); } });
module.exports = router;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const express = require(‘express’);
const fs = require(‘fs’);
const router = express.Router();
const sourceMap = require(‘source-map’);
const path = require(‘path’);
const resolve = file => path.resolve(__dirname, file);
// 定义post接口
router.get(‘/error/’, async function(req, res) {
    // 获取前端传过来的报错对象
    let error = JSON.parse(req.query.error);
    let url = error.scriptURI; // 压缩文件路径
    if (url) {
        let fileUrl = url.slice(url.indexOf(‘client/’)) + ‘.map’; // map文件路径
        // 解析sourceMap
        let consumer = await new sourceMap.SourceMapConsumer(fs.readFileSync(resolve(‘../’ + fileUrl), ‘utf8’)); // 返回一个promise对象
        // 解析原始报错数据
        let result = consumer.originalPositionFor({
            line: error.lineNo, // 压缩后的行号
            column: error.columnNo // 压缩后的列号
        });
        console.log(result);
    }
});
module.exports = router;

正如图所示,大家早就足以旁观,在服务端已经打响拆解分析出了现实错误的行号、列号,我们可以通过日记的法子张开记录,达到了后面一个格外监察和控制的指标。

金沙网址 6

Vue捕获非常

在作者的档次中就赶过那样的主题材料,使用了js-tracker如此这般的插件来统风流罗曼蒂克开展全局的特别捕获和日志上报,结果发现大家一向捕获不到Vue零零件的非常,查阅资料得悉,在Vue中,极度恐怕被Vue自身给try ... catch了,不会流传window.onerror事件触发,那么我们什么把Vue组件中的相当作统豆蔻年华捕获呢?

使用Vue.config.errorHandler这样的Vue全局配置,能够在Vue钦定组件的渲染和观看比赛时期未捕获错误的管理函数。那几个管理函数被调用时,可得到错误消息和Vue
实例。

JavaScript

Vue.config.errorHandler = function (err, vm, info) { // handle error //
`info` 是 Vue 特定的错误新闻,比方错误所在的生命周期钩子 // 只在
2.2.0+ 可用 }

1
2
3
4
5
Vue.config.errorHandler = function (err, vm, info) {
  // handle error
  // `info` 是 Vue 特定的错误信息,比如错误所在的生命周期钩子
  // 只在 2.2.0+ 可用
}

React中,能够使用ErrorBoundary零零件包涵业务组件的格局打开足够捕获,同盟React 16.0+新出的componentDidCatch API,能够达成合并的百般捕获和日志上报。

JavaScript

class ErrorBoundary extends React.Component { constructor(props) {
super(props); this.state = { hasError: false }; }
componentDidCatch(error, info) { // Display fallback UI this.setState({
hasError: true }); // You can also log the error to an error reporting
service logErrorToMyService(error, info); } render() { if
(this.state.hasError) { // You can render any custom fallback UI return
<h1>Something went wrong.</h1>; } return
this.props.children; } }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }
 
  componentDidCatch(error, info) {
    // Display fallback UI
    this.setState({ hasError: true });
    // You can also log the error to an error reporting service
    logErrorToMyService(error, info);
  }
 
  render() {
    if (this.state.hasError) {
      // You can render any custom fallback UI
      return <h1>Something went wrong.</h1>;
    }
    return this.props.children;
  }
}

应用方式如下:

<ErrorBoundary> <MyWidget /> </ErrorBoundary>

1
2
3
<ErrorBoundary>
  <MyWidget />
</ErrorBoundary>

属性监察和控制

最简便的性质量监督控

最数不胜数的属性监察和控制要求则是急需大家总结客户从初步央求页面到全数DOM要素渲染完毕的时光,也便是俗称的首屏加载时间,DOM提供了那风华正茂接口,监听documentDOMContentLoaded事件与windowload事件可总括页面首屏加载时间即怀有DOM渲染时间:

<!DOCTYPE html> <html> <head>
<title></title> <script type=”text/javascript”> //
记录页面加载起先时间 var timerStart = Date.now(); </script>
<!– 加载静态能源,如样式能源 –> </head> <body>
<!– 加载静态JS财富 –> <script type=”text/javascript”>
document.add伊芙ntListener(‘DOMContentLoaded’, function() {
console.log(“DOM 挂载时间: “, Date.now() – timerStart); // 品质日志上报
}); window.addEventListener(‘load’, function() {
console.log(“全数能源加载成功时间: “, Date.now()-timerStart); //
质量日志上报 }); </script> </body> </html>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!DOCTYPE html>
<html>
<head>
  <title></title>
  <script type="text/javascript">
    // 记录页面加载开始时间
    var timerStart = Date.now();
  </script>
  <!– 加载静态资源,如样式资源 –>
</head>
<body>
  <!– 加载静态JS资源 –>
  <script type="text/javascript">
    document.addEventListener(‘DOMContentLoaded’, function() {
      console.log("DOM 挂载时间: ", Date.now() – timerStart);
      // 性能日志上报
    });
    window.addEventListener(‘load’, function() {
      console.log("所有资源加载完成时间: ", Date.now()-timerStart);
      // 性能日志上报
    });
  </script>
</body>
</html>

对于使用框架,如Vue或者说React,组件是异步渲染然后挂载到DOM的,在页面带头化时并未太多的DOM节点,能够参见下文至于首屏时间收罗自动化的技术方案来对渲染时间展开发银行贿。

performance

只是上述时间的监察过于轻便,比如大家想总结文书档案的网络加载耗费时间、深入分析DOM的耗费时间与渲染DOM的耗费时间,就不太好办到了,所幸的是浏览器提供了window.performance接口,具体可以见到MDN文档

金沙网址 7

差一些全数浏览器都扶持window.performance接口,上边来探视在调节台打字与印刷window.performance能够获得些什么:

金沙网址 8

能够见见,window,performance关键总结有memorynavigationtiming以及timeOriginonresourcetimingbufferfull方法。

  • navigation目的提供了在钦命的日子段里发出的操作相关音信,满含页面是加载还是刷新、发生了多少次重定向等等。
  • timing对象包括延迟相关的性质音信。那是我们页面加载性能优化供给中首要反映的相干新闻。
  • memoryChrome丰盛的一个非规范扩充,那些本性提供了一个方可拿走到中央内部存款和储蓄器使用意况的指标。在其他浏览器应该考虑到那个API的协作管理。
  • timeOrigin则赶回质量衡量起来时的年华的高精度时间戳。如图所示,精确到了小数点后多个人。
  • onresourcetimingbufferfull方法,它是一个在resourcetimingbufferfull事件触发时会被调用的event handler。那个事件当浏览器的财富时间质量缓冲区已满时会触发。能够透过监听那生龙活虎平地风波触发来预估页面crash,总结页面crash概率,以便中期的属性优化,如下示例所示:
JavaScript

function buffer\_full(event) { console.log("WARNING: Resource Timing
Buffer is FULL!"); performance.setResourceTimingBufferSize(200); }
function init() { // Set a callback if the resource buffer becomes
filled performance.onresourcetimingbufferfull = buffer\_full; }
&lt;body onload="init()"&gt;

<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<tbody>
<tr class="odd">
<td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
<div class="crayon-num" data-line="crayon-5b8f00bfee161383152889-1">
1
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f00bfee161383152889-2">
2
</div>
<div class="crayon-num" data-line="crayon-5b8f00bfee161383152889-3">
3
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f00bfee161383152889-4">
4
</div>
<div class="crayon-num" data-line="crayon-5b8f00bfee161383152889-5">
5
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f00bfee161383152889-6">
6
</div>
<div class="crayon-num" data-line="crayon-5b8f00bfee161383152889-7">
7
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f00bfee161383152889-8">
8
</div>
<div class="crayon-num" data-line="crayon-5b8f00bfee161383152889-9">
9
</div>
</div></td>
<td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
<div id="crayon-5b8f00bfee161383152889-1" class="crayon-line">
function buffer_full(event) {
</div>
<div id="crayon-5b8f00bfee161383152889-2" class="crayon-line crayon-striped-line">
  console.log(&quot;WARNING: Resource Timing Buffer is FULL!&quot;);
</div>
<div id="crayon-5b8f00bfee161383152889-3" class="crayon-line">
  performance.setResourceTimingBufferSize(200);
</div>
<div id="crayon-5b8f00bfee161383152889-4" class="crayon-line crayon-striped-line">
}
</div>
<div id="crayon-5b8f00bfee161383152889-5" class="crayon-line">
function init() {
</div>
<div id="crayon-5b8f00bfee161383152889-6" class="crayon-line crayon-striped-line">
  // Set a callback if the resource buffer becomes filled
</div>
<div id="crayon-5b8f00bfee161383152889-7" class="crayon-line">
  performance.onresourcetimingbufferfull = buffer_full;
</div>
<div id="crayon-5b8f00bfee161383152889-8" class="crayon-line crayon-striped-line">
}
</div>
<div id="crayon-5b8f00bfee161383152889-9" class="crayon-line">
&lt;body onload=&quot;init()&quot;&gt;
</div>
</div></td>
</tr>
</tbody>
</table>

测算网站质量

使用performancetiming质量,能够得到页面品质相关的数码,这里在众多篇章都有涉及有关利用window.performance.timing笔录页面质量的篇章,譬如alloyteam组织写的初探
performance –
监察和控制网页与程序品质,对于timing的每一种品质含义,能够依据摘今后文的下图明白,以下代码摘自此文作为计量网站质量的工具函数参照他事他说加以考察:

金沙网址 9

JavaScript

// 获取 performance 数据 var performance = { // memory
是非标准属性,只在 Chrome 有 // 财富难题:我某些许内部存款和储蓄器 memory: {
usedJSHeapSize: 16100000, // JS
对象(包含V8引擎内部对象卡塔尔占用的内部存款和储蓄器,一定小于 totalJSHeapSize
totalJSHeapSize: 35100000, // 可应用的内存 jsHeapSizeLimit: 793000000 //
内部存储器大小限定 }, // 理学难题:笔者从何地来? navigation: { redirectCount:
0, // 若是有重定向的话,页面通过一遍重定向跳转而来 type: 0 // 0 即
TYPE_NAVIGATENEXT 平时步入的页面(非刷新、非重定向等卡塔 尔(英语:State of Qatar) // 1 即
TYPE_RELOAD 通过 window.location.reload() 刷新的页面 // 2 即
TYPE_BACK_FOEvoqueWA昂CoraD 通过浏览器的升高后退按键步入的页面(历史记录卡塔 尔(英语:State of Qatar) //
255 即 TYPE_UNDEFINED 非以上办法踏入的页面 }, timing: { //
在同三个浏览器上下文中,前一个网页(与当下页面不自然同域卡塔尔国unload
的时刻戳,若是无前八个网页 unload ,则与 fetchStart 值相等
navigationStart: 144111269一九三二, // 前二个网页(与如今页面同域卡塔 尔(阿拉伯语:قطر‎unload
的时光戳,假如无前一个网页 unload 恐怕前叁个网页与近日页面不一样域,则值为
0 unload伊芙ntStart: 0, // 和 unloadEventStart 相对应,再次回到前二个网页
unload 事件绑定的回调函数实行实现的时刻戳 unloadEventEnd: 0, // 第二个HTTP 重定向产生时的日子。有跳转且是同域名内的重定向才算,不然值为 0
redirectStart: 0, // 最终三个 HTTP
重定向达成时的大运。有跳转且是同域名内部的重定向才算,不然值为 0
redirectEnd: 0, // 浏览器策动好利用 HTTP
央浼抓取文档的时刻,那发生在检查本地缓存早先 fetchStart: 1441112692155,
// DNS 域名询问开头的时光,若是应用了地面缓存(即无 DNS
查询卡塔 尔(阿拉伯语:قطر‎或长久连接,则与 fetchStart 值相等 domainLookupStart:
1441112692155, // DNS 域名询问实现的时间,要是应用了本地缓存(即无 DNS
查询卡塔 尔(英语:State of Qatar)或悠久连接,则与 fetchStart 值相等 domainLookupEnd: 1441112692155,
// HTTP(TCP卡塔 尔(英语:State of Qatar) 初叶创建连接的时光,假诺是长久连接,则与 fetchStart
值相等 //
注意倘若在传输层发生了不当且再一次树立连接,则这里显得的是新确立的三番五次起来的时间
connectStart: 1441112692155, // HTTP(TCP卡塔尔国完毕建设构造连接的时日(完结握手卡塔尔国,如若是同心同德连接,则与 fetchStart 值相等
//
注意如若在传输层产生了错误且再一次确立连接,则这里显得的是新确立的连天产生的岁月
// 注意这里握手截至,蕴涵平安连接创建达成、SOCKS 授权通过 connectEnd:
1441112692155, // HTTPS 连接起来的时日,若是或不是优哉游哉连接,则值为 0
secureConnectionStart: 0, // HTTP
乞请读取真实文书档案初叶的年华(落成创建连接卡塔尔国,满含从地方读取缓存 //
连接错误重连时,这里体现的也是新建设构造连接的时间 requestStart:
1441112692158, // HTTP
最初接到响应的时刻(获取到第一个字节卡塔 尔(英语:State of Qatar),包含从本地读取缓存
responseStart: 1441112692686, // HTTP
响应全部接到实现的年华(获取到结尾多个字节卡塔尔,包罗从地面读取缓存
responseEnd: 1441112692687, // 伊始深入深入分析渲染 DOM 树的小时,那时Document.readyState 变为 loading,并将抛出 readystatechange 相关事件
domLoading: 1441112692690, // 达成分析 DOM 树的时日,Document.readyState
变为 interactive,并将抛出 readystatechange 相关事件 // 注意只是 DOM
树剖判达成,那个时候并未有起始加载网页内的能源 domInteractive:
1441112693093, // DOM 拆解深入分析完结后,网页国内资本源加载领头的岁月 // 在
DOMContentLoaded 事件抛出前发出 domContentLoaded伊芙ntStart:
1441112693093, // DOM 剖析完毕后,网页国内资本源加载成功的时日(如 JS
脚本加载试行实现卡塔尔 domContentLoaded伊夫ntEnd: 1441112693101, // DOM
树拆解剖析完毕,且财富也准备稳妥的年华,Document.readyState 变为
complete,并将抛出 readystatechange 相关事件 domComplete: 1441112693214,
// load 事件发送给文档,也即 load 回调函数起头进行的时辰 //
注意若无绑定 load 事件,值为 0 loadEventStart: 1441112693214, //
load 事件的回调函数推行完结的日子 loadEventEnd: 1441112693215 //
字母顺序 // connectEnd: 1441112692155, // connectStart: 1441112692155,
// domComplete: 1441112693214, // domContentLoaded伊芙ntEnd:
1441112693101, // domContentLoaded伊芙ntStart: 1441112693093, //
domInteractive: 1441112693093, // domLoading: 1441112692690, //
domainLookupEnd: 1441112692155, // domainLookupStart: 1441112692155, //
fetchStart: 1441112692155, // load伊芙ntEnd: 1441112693215, //
loadEventStart: 1441112693214, // navigationStart: 144111269一九三二, //
redirectEnd: 0, // redirectStart: 0, // requestStart: 1441112692158, //
responseEnd: 1441112692687, // responseStart: 1441112692686, //
secureConnectionStart: 0, // unload伊夫ntEnd: 0, // unload伊芙ntStart: 0 }
};

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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
// 获取 performance 数据
var performance = {  
    // memory 是非标准属性,只在 Chrome 有
    // 财富问题:我有多少内存
    memory: {
        usedJSHeapSize:  16100000, // JS 对象(包括V8引擎内部对象)占用的内存,一定小于 totalJSHeapSize
        totalJSHeapSize: 35100000, // 可使用的内存
        jsHeapSizeLimit: 793000000 // 内存大小限制
    },
    //  哲学问题:我从哪里来?
    navigation: {
        redirectCount: 0, // 如果有重定向的话,页面通过几次重定向跳转而来
        type: 0           // 0   即 TYPE_NAVIGATENEXT 正常进入的页面(非刷新、非重定向等)
                          // 1   即 TYPE_RELOAD       通过 window.location.reload() 刷新的页面
                          // 2   即 TYPE_BACK_FORWARD 通过浏览器的前进后退按钮进入的页面(历史记录)
                          // 255 即 TYPE_UNDEFINED    非以上方式进入的页面
    },
    timing: {
        // 在同一个浏览器上下文中,前一个网页(与当前页面不一定同域)unload 的时间戳,如果无前一个网页 unload ,则与 fetchStart 值相等
        navigationStart: 1441112691935,
        // 前一个网页(与当前页面同域)unload 的时间戳,如果无前一个网页 unload 或者前一个网页与当前页面不同域,则值为 0
        unloadEventStart: 0,
        // 和 unloadEventStart 相对应,返回前一个网页 unload 事件绑定的回调函数执行完毕的时间戳
        unloadEventEnd: 0,
        // 第一个 HTTP 重定向发生时的时间。有跳转且是同域名内的重定向才算,否则值为 0
        redirectStart: 0,
        // 最后一个 HTTP 重定向完成时的时间。有跳转且是同域名内部的重定向才算,否则值为 0
        redirectEnd: 0,
        // 浏览器准备好使用 HTTP 请求抓取文档的时间,这发生在检查本地缓存之前
        fetchStart: 1441112692155,
        // DNS 域名查询开始的时间,如果使用了本地缓存(即无 DNS 查询)或持久连接,则与 fetchStart 值相等
        domainLookupStart: 1441112692155,
        // DNS 域名查询完成的时间,如果使用了本地缓存(即无 DNS 查询)或持久连接,则与 fetchStart 值相等
        domainLookupEnd: 1441112692155,
        // HTTP(TCP) 开始建立连接的时间,如果是持久连接,则与 fetchStart 值相等
        // 注意如果在传输层发生了错误且重新建立连接,则这里显示的是新建立的连接开始的时间
        connectStart: 1441112692155,
        // HTTP(TCP) 完成建立连接的时间(完成握手),如果是持久连接,则与 fetchStart 值相等
        // 注意如果在传输层发生了错误且重新建立连接,则这里显示的是新建立的连接完成的时间
        // 注意这里握手结束,包括安全连接建立完成、SOCKS 授权通过
        connectEnd: 1441112692155,
        // HTTPS 连接开始的时间,如果不是安全连接,则值为 0
        secureConnectionStart: 0,
        // HTTP 请求读取真实文档开始的时间(完成建立连接),包括从本地读取缓存
        // 连接错误重连时,这里显示的也是新建立连接的时间
        requestStart: 1441112692158,
        // HTTP 开始接收响应的时间(获取到第一个字节),包括从本地读取缓存
        responseStart: 1441112692686,
        // HTTP 响应全部接收完成的时间(获取到最后一个字节),包括从本地读取缓存
        responseEnd: 1441112692687,
        // 开始解析渲染 DOM 树的时间,此时 Document.readyState 变为 loading,并将抛出 readystatechange 相关事件
        domLoading: 1441112692690,
        // 完成解析 DOM 树的时间,Document.readyState 变为 interactive,并将抛出 readystatechange 相关事件
        // 注意只是 DOM 树解析完成,这时候并没有开始加载网页内的资源
        domInteractive: 1441112693093,
        // DOM 解析完成后,网页内资源加载开始的时间
        // 在 DOMContentLoaded 事件抛出前发生
        domContentLoadedEventStart: 1441112693093,
        // DOM 解析完成后,网页内资源加载完成的时间(如 JS 脚本加载执行完毕)
        domContentLoadedEventEnd: 1441112693101,
        // DOM 树解析完成,且资源也准备就绪的时间,Document.readyState 变为 complete,并将抛出 readystatechange 相关事件
        domComplete: 1441112693214,
        // load 事件发送给文档,也即 load 回调函数开始执行的时间
        // 注意如果没有绑定 load 事件,值为 0
        loadEventStart: 1441112693214,
        // load 事件的回调函数执行完毕的时间
        loadEventEnd: 1441112693215
        // 字母顺序
        // connectEnd: 1441112692155,
        // connectStart: 1441112692155,
        // domComplete: 1441112693214,
        // domContentLoadedEventEnd: 1441112693101,
        // domContentLoadedEventStart: 1441112693093,
        // domInteractive: 1441112693093,
        // domLoading: 1441112692690,
        // domainLookupEnd: 1441112692155,
        // domainLookupStart: 1441112692155,
        // fetchStart: 1441112692155,
        // loadEventEnd: 1441112693215,
        // loadEventStart: 1441112693214,
        // navigationStart: 1441112691935,
        // redirectEnd: 0,
        // redirectStart: 0,
        // requestStart: 1441112692158,
        // responseEnd: 1441112692687,
        // responseStart: 1441112692686,
        // secureConnectionStart: 0,
        // unloadEventEnd: 0,
        // unloadEventStart: 0
    }
};

 

JavaScript

// 总括加载时间 function getPerformance提姆ing() { var performance =
window.performance; if (!performance) { // 当前浏览器不帮忙console.log(‘你的浏览器不扶持 performance 接口’); return; } var t =
performance.timing; var times = {}; //【首要】页面加载成功的年月
//【原因】那大致代表了客商等待页面可用的时间 times.loadPage =
t.loadEventEnd – t.navigationStart; //【主要】分析 DOM 树结构的时日
//【原因】反省下您的 DOM 树嵌套是或不是太多了! times.domReady =
t.domComplete – t.responseEnd; //【主要】重定向的年华
//【原因】拒却重定向!比方, 就不应该写成
times.redirect = t.redirectEnd – t.redirectStart;
//【首要】DNS 查询时间 //【原因】DNS
预加载做了么?页面内是或不是行使了太多不一致的域名引致域名查询的时光太长?
// 可接收 HTML5 Prefetch 预查询 DNS ,见:[HTML5
prefetch]()
times.lookupDomain = t.domainLookupEnd – t.domainLookupStart;
//【主要】读取页面第八个字节的年华
//【原因】那能够知晓为客商得到您的能源占用的岁月,加异地机房了么,加CDN
管理了么?加带宽了么?加 CPU 运算速度了么? // TTFB 即 提姆e To First
Byte 的意味 //
维基百科: times.ttfb
= t.responseStart – t.navigationStart; //【首要】内容加载成功的年华
//【原因】页面内容通过 gzip 压缩了么,静态能源 css/js 等压缩了么?
times.request = t.responseEnd – t.requestStart; //【首要】实践 onload
回调函数的时间 //【原因】是还是不是太多没有须要的操作都放到 onload
回调函数里进行了,思索过延迟加载、按需加载的战略么? times.loadEvent =
t.loadEventEnd – t.load伊芙ntStart; // DNS 缓存时间 times.appcache =
t.domainLookupStart – t.fetchStart; // 卸载页面包车型地铁命宫 times.unloadEvent
= t.unloadEventEnd – t.unload伊夫ntStart; // TCP 建构连接成功握手的岁月
times.connect = t.connectEnd – t.connectStart; return times; }

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
// 计算加载时间
function getPerformanceTiming() {
    var performance = window.performance;
    if (!performance) {
        // 当前浏览器不支持
        console.log(‘你的浏览器不支持 performance 接口’);
        return;
    }
    var t = performance.timing;
    var times = {};
    //【重要】页面加载完成的时间
    //【原因】这几乎代表了用户等待页面可用的时间
    times.loadPage = t.loadEventEnd – t.navigationStart;
    //【重要】解析 DOM 树结构的时间
    //【原因】反省下你的 DOM 树嵌套是不是太多了!
    times.domReady = t.domComplete – t.responseEnd;
    //【重要】重定向的时间
    //【原因】拒绝重定向!比如,http://example.com/ 就不该写成 http://example.com
    times.redirect = t.redirectEnd – t.redirectStart;
    //【重要】DNS 查询时间
    //【原因】DNS 预加载做了么?页面内是不是使用了太多不同的域名导致域名查询的时间太长?
    // 可使用 HTML5 Prefetch 预查询 DNS ,见:[HTML5 prefetch](http://segmentfault.com/a/1190000000633364)            
    times.lookupDomain = t.domainLookupEnd – t.domainLookupStart;
    //【重要】读取页面第一个字节的时间
    //【原因】这可以理解为用户拿到你的资源占用的时间,加异地机房了么,加CDN 处理了么?加带宽了么?加 CPU 运算速度了么?
    // TTFB 即 Time To First Byte 的意思
    // 维基百科:https://en.wikipedia.org/wiki/Time_To_First_Byte
    times.ttfb = t.responseStart – t.navigationStart;
    //【重要】内容加载完成的时间
    //【原因】页面内容经过 gzip 压缩了么,静态资源 css/js 等压缩了么?
    times.request = t.responseEnd – t.requestStart;
    //【重要】执行 onload 回调函数的时间
    //【原因】是否太多不必要的操作都放到 onload 回调函数里执行了,考虑过延迟加载、按需加载的策略么?
    times.loadEvent = t.loadEventEnd – t.loadEventStart;
    // DNS 缓存时间
    times.appcache = t.domainLookupStart – t.fetchStart;
    // 卸载页面的时间
    times.unloadEvent = t.unloadEventEnd – t.unloadEventStart;
    // TCP 建立连接完成握手的时间
    times.connect = t.connectEnd – t.connectStart;
    return times;
}

日记上报

独立的日志域名

对于日记上报利用单独的日记域名的目标是防止对事情形成影响。其生机勃勃,对于服务器来讲,大家明确不愿意占用专门的学问服务器的测算财富,也不希望过多的日志在作业服务器堆叠,产生职业服务器的积攒空间非常不足的情事。其二,大家明白在页面先河化的进程中,会对页面加载时间、PV、UV等数码开展报告,这一个报告要求会和加载业务数据差相当的少是同不平日候刻爆发,而浏览器平日会对同多个域名的哀求量有并发数的约束,如Chrome会有对并发数为6个的节制。因而需求对日记系统独立设定域名,最小化对页面加载质量形成的影响。

跨域的标题

对于单身的日记域名,确定会涉嫌到跨域的标题,接收的减轻方案经常常有以下三种:

  • 一种是结构空的Image指标的形式,其原因是号令图片并不关乎到跨域的主题材料;
JavaScript

var url = 'xxx'; new Image().src = url;

<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<tbody>
<tr class="odd">
<td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
<div class="crayon-num" data-line="crayon-5b8f00bfee170123843269-1">
1
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f00bfee170123843269-2">
2
</div>
</div></td>
<td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
<div id="crayon-5b8f00bfee170123843269-1" class="crayon-line">
var url = 'xxx';
</div>
<div id="crayon-5b8f00bfee170123843269-2" class="crayon-line crayon-striped-line">
new Image().src = url;
</div>
</div></td>
</tr>
</tbody>
</table>

  • 利用Ajax报告日志,必需对日记服务器接口开启跨域诉求尾部Access-Control-Allow-Origin:*,这里Ajax就并不强制行使GET恳请了,就可以战胜URL长度约束的难题。
JavaScript

if (XMLHttpRequest) { var xhr = new XMLHttpRequest();
xhr.open('post', 'https://log.xxx.com', true); //
上报给node中间层处理 xhr.setRequestHeader('Content-Type',
'application/json'); // 设置请求头
xhr.send(JSON.stringify(errorObj)); // 发送参数 }

<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<tbody>
<tr class="odd">
<td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
<div class="crayon-num" data-line="crayon-5b8f00bfee174544186263-1">
1
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f00bfee174544186263-2">
2
</div>
<div class="crayon-num" data-line="crayon-5b8f00bfee174544186263-3">
3
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f00bfee174544186263-4">
4
</div>
<div class="crayon-num" data-line="crayon-5b8f00bfee174544186263-5">
5
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f00bfee174544186263-6">
6
</div>
</div></td>
<td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
<div id="crayon-5b8f00bfee174544186263-1" class="crayon-line">
if (XMLHttpRequest) {
</div>
<div id="crayon-5b8f00bfee174544186263-2" class="crayon-line crayon-striped-line">
  var xhr = new XMLHttpRequest();
</div>
<div id="crayon-5b8f00bfee174544186263-3" class="crayon-line">
  xhr.open('post', 'https://log.xxx.com', true); // 上报给node中间层处理
</div>
<div id="crayon-5b8f00bfee174544186263-4" class="crayon-line crayon-striped-line">
  xhr.setRequestHeader('Content-Type', 'application/json'); // 设置请求头
</div>
<div id="crayon-5b8f00bfee174544186263-5" class="crayon-line">
  xhr.send(JSON.stringify(errorObj)); // 发送参数
</div>
<div id="crayon-5b8f00bfee174544186263-6" class="crayon-line crayon-striped-line">
}
</div>
</div></td>
</tr>
</tbody>
</table>

在本身的花色中利用的是第风度翩翩种的点子,也正是结构空的Image对象,可是大家领略对于GET恳请会有长度的限制,须求保障的是伸手的长短不会超越阈值。

节约响应大旨

对此我们举报日志,其实对于顾客端的话,并不必要思考上报的结果,以至对于举报退步,大家也没有须要在前面多少个做其余交互作用,所以报告来讲,其实选拔HEAD号召就够了,接口重临空的结果,最大地回降上报日志产生的能源浪费。

统一反映

恍如于百事可乐图的构思,如若我们的施用须求报告的日记数量超多,那么有供给归拢日志实行合併的陈述。

建设方案得以是尝尝在客户间距页面或然零件销毁时发送一个异步的POST伸手来进展报告,不过尝试在卸载(unload卡塔尔文书档案早先向web服务器发送数据。保障在文书档案卸载时期发送数据平昔是四个费劲。因为顾客代理经常会忽略在卸载事件微机中产生的异步XMLHttpRequest,因为当时已经会跳转到下多少个页面。所以这边是必得设置为联合的XMLHttpRequest请求吗?

JavaScript

window.addEventListener(‘unload’, logData, false); function logData() {
var client = new XMLHttpRequest(); client.open(“POST”, “/log”, false);
// 第1个参数表明是一齐的 xhr client.setRequestHeader(“Content-Type”,
“text/plain;charset=UTF-8”); client.send(analyticsData); }

1
2
3
4
5
6
7
8
window.addEventListener(‘unload’, logData, false);
 
function logData() {
    var client = new XMLHttpRequest();
    client.open("POST", "/log", false); // 第三个参数表明是同步的 xhr
    client.setRequestHeader("Content-Type", "text/plain;charset=UTF-8");
    client.send(analyticsData);
}

动用同步的法子势必会对客商体验变成影响,以至会让顾客体会到浏览器卡死以为,对于付加物来讲,体验特不佳,通过查看MDN文档,能够使用sendBeacon()艺术,将会使客户代理在有机遇时异步地向服务器发送数据,同一时候不会延迟页面包车型地铁卸载或影响下黄金年代导航的载入品质。这就一蹴即至了交给剖析数据时的兼具的难题:使它可信,异步并且不会潜濡默化下意气风发页面的加载。别的,代码实际上还要比其他手艺轻易!

上面包车型地铁例子展现了三个理论上的总计代码方式——通过行使sendBeacon()艺术向服务器发送数据。

JavaScript

window.addEventListener(‘unload’, logData, false); function logData() {
navigator.sendBeacon(“/log”, analyticsData); }

1
2
3
4
5
window.addEventListener(‘unload’, logData, false);
 
function logData() {
    navigator.sendBeacon("/log", analyticsData);
}

小结

作为前端开辟者来讲,要对产品保持敬畏之心,时刻保持对品质追求十二万分,对丰盛不可忍受的情态。前端的性子监察和控制与丰裕申报显得更为关键。

代码难免有题目,对于那八个能够采取window.onerror或者addEventListener的法子足够全局的不行捕获侦听函数,但只怕应用这种措施不能正确捕获到不当:对于跨域的台本,必要对script标签增添一个crossorigin=”anonymous”;对于临蓐条件打包的代码,不能正确定位到不行发生的行数,可以利用source-map来解决;而对于使用框架的处境,要求在框架统大器晚成的老大捕获处埋点。

而对此质量的监督,所幸的是浏览器提供了window.performance API,通过那个API,很便捷地获得到最近页面品质相关的数码。

而那一个非凡和属性数据如何反馈呢?日常说来,为了制止对事情产生的熏陶,会独自建设构造日志服务器和日志域名,但对于不相同的域名,又会时有产生跨域的难点。我们能够通过组织空的Image对象来消除,亦或者通过设定跨域诉求底部Access-Control-Allow-Origin:*来消除。其余,如果反映的性能和日志数据高频触发,则足以在页面unload时统一举报,而unload时的异步央求又只怕会被浏览器所忽视,且不能够改为联合央求。那个时候navigator.sendBeacon API可算帮了大家大忙,它可用以通过HTTP将一些些数量异步传输到Web服务器。而忽略页面unload时的熏陶。

1 赞 1 收藏
评论

金沙网址 10

相关文章