您的位置:新葡亰496net > 新葡亰官网 > 新葡亰496net:来看看机智的前端童鞋怎么防盗,

新葡亰496net:来看看机智的前端童鞋怎么防盗,

发布时间:2019-07-14 05:14编辑:新葡亰官网浏览(61)

    前面壹本品质与极其申报

    2018/08/22 · 基本功本领 · 性能

    原稿出处: counterxing   

    原稿出处: happylindz   

    表明:本小说仅供就学沟通使用 如有侵犯权益马上删除 

    1, HTTP 协议 和 AJAX
    2, DOM 事件机制, 事件冒泡和事件捕获

    来拜望机智的前端童鞋怎么防盗

    2016/07/12 · JavaScript · 4 评论 · HTML5

    原作出处: VaJoy   

    众多支出的童鞋都以只身混江湖、夜宿城中村,若是居住的地点安全保卫欠缺,那么出门在外难免顾忌屋里的资金财产安全。

    其实世面上有非常多巨大上的防盗设备,但对此灵动的前端童鞋来讲,只要有一台附带录制头的微型计算机,就能够大致地促成一个防盗监察和控制种类~

    纯 JS 的“防盗”技巧一点都不小程度借助于 H5 canvas 的本领,且十一分有趣。借使你对 canvas 还不熟练,可以先点这里阅读作者的千家万户教程。

    step1. 调用录像头

    我们需求先在浏览器上访谈和调用录制头,用来监督房子里的一言一动。不一致浏览器中调用摄像头的 API 都略有出入,在那边大家以 chrome 做示范:

    JavaScript

    <video width="640" height="480" autoplay></video> <script> var video = document.querySelector('video'); navigator.webkitGetUserMedia({ video: true }, success, error); function success(stream) { video.src = window.webkitURL.createObjectURL(stream); video.play(); } function error(err) { alert('video error: ' err) } </script>

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    <video width="640" height="480" autoplay></video>
     
    <script>
        var video = document.querySelector('video');
     
        navigator.webkitGetUserMedia({
                    video: true
                }, success, error);
     
        function success(stream) {
            video.src = window.webkitURL.createObjectURL(stream);
            video.play();
        }
     
        function error(err) {
            alert('video error: ' err)
        }
    </script>

    运作页面后,浏览器出于安全性考虑,会了然是或不是同意当前页面访谈你的录像头设备,点击“允许”后便能直接在 <video> 上看出拍片头捕获到的镜头了:

    新葡亰496net 1

    step2. 捕获 video 帧画面

    只可是开着录像头监视房间可没有任何意义,浏览器不会帮你对监督画面实行分析。所以这里我们胜利动用脚本捕获 video 上的帧画面,用于在后续开始展览数据分析。

    从此间开始大家将要借助 canvas 力量了。在 Canvas入门(五)一文大家介绍过 ctx.drawImage() 方法,通过它能够捕获 video 帧画面并渲染到画布上。

    我们需求创制三个画布,然后这么写:

    JavaScript

    <video width="640" height="480" autoplay></video> <canvas width="640" height="480"></canvas> <script> var video = document.querySelector('video'); var canvas = document.querySelector('canvas'); // video捕获摄像头画面 navigator.webkitGetUserMedia({ video: true }, success, error); function success(stream) { video.src = window.webkitU福睿斯L.createObjectU普拉多L(stream); video.play(); } function error(err) { alert('video error: ' err) } //canvas var context = canvas.getContext('2d'); setTimeout(function(){ //把当前录制帧内容渲染到画布上 context.drawImage(video, 0, 0, 640, 480); }, 四千); </script>

    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
    <video width="640" height="480" autoplay></video>
    <canvas width="640" height="480"></canvas>
     
    <script>
        var video = document.querySelector('video');
        var canvas = document.querySelector('canvas');
     
        // video捕获摄像头画面
        navigator.webkitGetUserMedia({
                    video: true
                }, success, error);
     
        function success(stream) {
            video.src = window.webkitURL.createObjectURL(stream);
            video.play();
        }
     
        function error(err) {
            alert('video error: ' err)
        }
     
        //canvas
        var context = canvas.getContext('2d');
     
        setTimeout(function(){
            //把当前视频帧内容渲染到画布上
            context.drawImage(video, 0, 0, 640, 480);
        }, 5000);
     
    </script>

    如上代码所示,5秒后把摄像帧内容渲染到画布上(下方右图)

    新葡亰496net 2

    step3. 对捕获的三个帧画面实施差距混合

    在上头咱们关系过,要立见效能地辨认某些场景,需求对录像画面进行多少深入分析。

    那正是说要怎么分辨大家的房舍是不是有人猛然闯入了吗?答案相当的粗略 —— 定期地破获 video 画面,然后相比前后两帧内容是或不是留存相当的大变化。

    我们先轻便地写三个按期捕获的办法,并将捕获到的帧数据存起来:

    JavaScript

    //canvas var context = canvas.getContext('2d'); var preFrame, //前一帧 curFrame; //当前帧 //捕获并保存帧内容 function captureAndSaveFrame(){ console.log(context); preFrame = curFrame; context.drawImage(video, 0, 0, 640, 480); curFrame = canvas.toDataUENVISIONL; //转为base64并保存 } //定期捕获 function timer(delta){ setTimeout(function(){ captureAndSaveFrame(); timer(delta) }, delta || 500); } timer();

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
        //canvas
        var context = canvas.getContext('2d');
        var preFrame,   //前一帧
            curFrame;   //当前帧
     
        //捕获并保存帧内容
        function captureAndSaveFrame(){ console.log(context);
            preFrame = curFrame;
            context.drawImage(video, 0, 0, 640, 480);
            curFrame = canvas.toDataURL;  //转为base64并保存
        }
     
        //定时捕获
        function timer(delta){
            setTimeout(function(){
                captureAndSaveFrame();
                timer(delta)
            }, delta || 500);
        }
     
        timer();

    如上代码所示,画布会每隔500飞秒捕获并渲染二次 video 的帧内容(夭寿哇,做完那几个动作相当大心把饼干洒了一地。。。(“▔□▔)/)

    新葡亰496net 3

    注意这里咱们利用了 canvas.toDataURL 方法来保存帧画面。

    接着正是数据解析管理了,大家能够透过对照前后捕获的帧画面来剖断录像头是还是不是监察和控制到变化,那么怎么做吧?

    深谙设计的同校断定平日使用三个图层功用 —— 混合情势:

    新葡亰496net 4

    当有五个图层时,对顶层图层设置“差值/Difference”的混杂方式,能够看透地来看七个图层的歧异:

    新葡亰496net 5

    “图A”是自己二零一八年在店堂楼下拍的相片,然后作者把它多少调亮了一丝丝,并在上头画了三个X 和 O 获得“图B”。接着本人把它们以“差值”情势混合在共同,获得了最右的那张图。

    JavaScript

    “差值”方式原理:要掺杂图层双方的哈弗GB值中各样值分别进行相比,用高值减去低值作为合成后的水彩,平常用暗黑图层合成一图像时,能够收获负片效果的反相图像。用浅灰的话不产生别的变动(白色亮度最低,下层颜色减去最小颜色值0,结果和原来同样),而用藤黄会获得反相效果(下层颜色被减去,获得补值),其余颜色则基于它们的亮度水平

    1
    “差值”模式原理:要混合图层双方的RGB值中每个值分别进行比较,用高值减去低值作为合成后的颜色,通常用白色图层合成一图像时,可以得到负片效果的反相图像。用黑色的话不发生任何变化(黑色亮度最低,下层颜色减去最小颜色值0,结果和原来一样),而用白色会得到反相效果(下层颜色被减去,得到补值),其它颜色则基于它们的亮度水平

    在CSS3中,已经有 blend-mode 本性来协理那一个风趣的插花形式,但是大家开采,在主流浏览器上,canvas 的 globalCompositeOperation 接口也一度完美援助了图像混合格局:

    于是乎大家再建多三个画布来展现前后两帧差别:

    JavaScript

    <video width="640" height="480" autoplay></video> <canvas width="640" height="480"></canvas> <canvas width="640" height="480"></canvas> <script> var video = document.querySelector('video'); var canvas = document.querySelectorAll('canvas')[0]; var canvasForDiff = document.querySelectorAll('canvas')[1]; // video捕获摄像头画面 navigator.webkitGetUserMedia({ video: true }, success, error); function success(stream) { video.src = window.USportageL.createObjectUCR-VL(stream); video.play(); } function error(err) { alert('video error: ' err) } //canvas var context = canvas.getContext('2d'), diffCtx = canvasForDiff.getContext('2d'); //将首个画布混合形式设为“差别” diffCtx.globalCompositeOperation = 'difference'; var preFrame, //前一帧 curFrame; //当前帧 //捕获并保存帧内容 function captureAndSaveFrame(){ preFrame = curFrame; context.drawImage(video, 0, 0, 640, 480); curFrame = canvas.toDataULX570L(); //转为base64并保存 } //绘制base64图像到画布上 function drawImg(src, ctx){ ctx = ctx || diffCtx; var img = new Image(); img.src = src; ctx.drawImage(img, 0, 0, 640, 480); } //渲染前后两帧差异function renderDiff(){ if(!preFrame || !curFrame) return; diffCtx.clearRect(0, 0, 640, 480); drawImg(preFrame); drawImg(curFrame); } //定期捕获 function timer(delta){ setTimeout(function(){ captureAndSaveFrame(); renderDiff(); timer(delta) }, delta || 500); } timer(); </script>

    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
    <video width="640" height="480" autoplay></video>
    <canvas width="640" height="480"></canvas>
    <canvas width="640" height="480"></canvas>
     
    <script>
        var video = document.querySelector('video');
        var canvas = document.querySelectorAll('canvas')[0];
        var canvasForDiff = document.querySelectorAll('canvas')[1];
     
        // video捕获摄像头画面
        navigator.webkitGetUserMedia({
                    video: true
                }, success, error);
     
        function success(stream) {
            video.src = window.URL.createObjectURL(stream);
            video.play();
        }
     
        function error(err) {
            alert('video error: ' err)
        }
     
        //canvas
        var context = canvas.getContext('2d'),
            diffCtx = canvasForDiff.getContext('2d');
        //将第二个画布混合模式设为“差异”
        diffCtx.globalCompositeOperation = 'difference';
     
        var preFrame,   //前一帧
            curFrame;   //当前帧
     
        //捕获并保存帧内容
        function captureAndSaveFrame(){
            preFrame = curFrame;
            context.drawImage(video, 0, 0, 640, 480);
            curFrame = canvas.toDataURL();  //转为base64并保存
        }
     
        //绘制base64图像到画布上
        function drawImg(src, ctx){
            ctx = ctx || diffCtx;
            var img = new Image();
            img.src = src;
            ctx.drawImage(img, 0, 0, 640, 480);
        }
     
        //渲染前后两帧差异
        function renderDiff(){
            if(!preFrame || !curFrame) return;
            diffCtx.clearRect(0, 0, 640, 480);
            drawImg(preFrame);
            drawImg(curFrame);
        }
     
        //定时捕获
        function timer(delta){
            setTimeout(function(){
                captureAndSaveFrame();
                renderDiff();
                timer(delta)
            }, delta || 500);
        }
     
        timer();
     
    </script>

    职能如下(夭寿啊,做完那么些动作作者又把七喜洒在键盘上了。。。(#--)/ )

    新葡亰496net 6

    能够看到,当前后两帧差异非常小时,第三个画布大约是雾里看花的一片,独有当拍戏头捕获到动作了,第多少个画布才有真相大白的高亮内容出现。

    之所以,大家只必要对第八个画布渲染后的图像实行像素剖判——判别其高亮阈值是还是不是达到有些内定预期:

    JavaScript

    var context = canvas.getContext('2d'), diffCtx = canvasForDiff.getContext('2d'); //将第4个画布混合方式设为“差距” diffCtx.globalCompositeOperation = 'difference'; var preFrame, //前一帧 curFrame; //当前帧 var diffFrame; //寄放差异帧的imageData //捕获并保存帧内容 function captureAndSaveFrame(){ preFrame = curFrame; context.drawImage(video, 0, 0, 640, 480); curFrame = canvas.toDataU锐界L(); //转为base64并保存 } //绘制base64图像到画布上 function drawImg(src, ctx){ ctx = ctx || diffCtx; var img = new Image(); img.src = src; ctx.drawImage(img, 0, 0, 640, 480); } //渲染前后两帧差距 function renderDiff(){ if(!preFrame || !curFrame) return; diffCtx.clearRect(0, 0, 640, 480); drawImg(preFrame); drawImg(curFrame); diffFrame = diffCtx.getImageData( 0, 0, 640, 480 ); //捕获差距帧的imageData对象 } //总计差别 function calcDiff(){ if(!diffFrame) return 0; var cache = arguments.callee, count = 0; cache.total = cache.total || 0; //整个画布都以反革命时享有像素的值的总和 for (var i = 0, l = diffFrame.width * diffFrame.height * 4; i < l; i = 4) { count = diffFrame.data[i] diffFrame.data[i 1] diffFrame.data[i 2]; if(!cache.isLoopEver){ //只需在首先次循环里施行 cache.total = 255 * 3; //单个反革命像素值 } } cache.isLoopEver = true; count *= 3; //亮度放大 //重返“差距画布高亮部分像素总值”占“画布全亮情状像素总值”的比例 return Number(count/cache.total).toFixed(2); } //定期捕获 function timer(delta){ setTimeout(function(){ captureAndSaveFrame(); renderDiff(); setTimeout(function(){ console.log(calcDiff()); }, 10); timer(delta) }, delta || 500); } timer();

    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
        var context = canvas.getContext('2d'),
            diffCtx = canvasForDiff.getContext('2d');
        //将第二个画布混合模式设为“差异”
        diffCtx.globalCompositeOperation = 'difference';
     
        var preFrame,   //前一帧
            curFrame;   //当前帧
     
        var diffFrame;  //存放差异帧的imageData
     
        //捕获并保存帧内容
        function captureAndSaveFrame(){
            preFrame = curFrame;
            context.drawImage(video, 0, 0, 640, 480);
            curFrame = canvas.toDataURL();  //转为base64并保存
        }
     
        //绘制base64图像到画布上
        function drawImg(src, ctx){
            ctx = ctx || diffCtx;
            var img = new Image();
            img.src = src;
            ctx.drawImage(img, 0, 0, 640, 480);
        }
     
        //渲染前后两帧差异
        function renderDiff(){
            if(!preFrame || !curFrame) return;
            diffCtx.clearRect(0, 0, 640, 480);
            drawImg(preFrame);
            drawImg(curFrame);
            diffFrame = diffCtx.getImageData( 0, 0, 640, 480 );  //捕获差异帧的imageData对象
        }
     
        //计算差异
        function calcDiff(){
            if(!diffFrame) return 0;
            var cache = arguments.callee,
                count = 0;
            cache.total = cache.total || 0; //整个画布都是白色时所有像素的值的总和
            for (var i = 0, l = diffFrame.width * diffFrame.height * 4; i < l; i = 4) {
                count = diffFrame.data[i] diffFrame.data[i 1] diffFrame.data[i 2];
                if(!cache.isLoopEver){  //只需在第一次循环里执行
                    cache.total = 255 * 3;   //单个白色像素值
                }
            }
            cache.isLoopEver = true;
            count *= 3;  //亮度放大
            //返回“差异画布高亮部分像素总值”占“画布全亮情况像素总值”的比例
            return Number(count/cache.total).toFixed(2);
        }
     
        //定时捕获
        function timer(delta){
            setTimeout(function(){
                captureAndSaveFrame();
                renderDiff();
                setTimeout(function(){
                    console.log(calcDiff());
                }, 10);
     
                timer(delta)
            }, delta || 500);
        }
     
        timer();

    留意这里大家运用了 count *= 3 来放大差别高亮像素的亮度值,不然得出的数值实在太小了。大家运营下页面(图片十分的大加载会有一点点慢)

    新葡亰496net 7

    通过试(xia)验(bai),个人认为若是 calcDiff() 再次回到的比率若是过量 0.20,那么就足以定性为“一间空房屋,乍然有人闯进来”的图景了。

    step4. 上报非凡图片

    当上述的计算发现有情形时,要求有某种门路通告大家。有钱有活力的话能够配备个邮件服务器,直接发邮件以致短信公告到本人,but 本文走的吃吐少年路径,就不搞的那么高级了。

    那就是说要什么轻便地落实充足图片的反馈呢?作者临时想到的是 —— 直接把难题图片发送到有个别站点中去。

    此地我们采取网易的“日记”作用,它能够自由上传相关内容。

    JavaScript

    p.s.,其实这里原本是想平素把图纸传遍乐乎相册上的,缺憾POST央求的图样实体要求走 file 格式,即不能够通过脚本改变文件的 input[type=file],转 Blob 再上传也没用,只可以作罢。

    1
    p.s.,其实这里原本是想直接把图片传到博客园相册上的,可惜POST请求的图片实体要求走 file 格式,即无法通过脚本更改文件的 input[type=file],转 Blob 再上传也没用,只好作罢。

    咱俩在治本后台创立日记时,通过 Fiddler 抓包能够看到其央求参数特别轻巧:

    新葡亰496net 8

    为此能够一直组织多个呼吁:

    JavaScript

    //极度图片上传管理 function submit(){ //ajax 提交form $.ajax({ url : '', type : "POST", data : { '__VIEWSTATE': '', '__VIEWSTATEGENERATOR': '4773056F', 'Editor$Edit$txbTitle': '告警' Date.now(), 'Editor$Edit$EditorBody': '<img src="' curFrame '" />', 'Editor$Edit$lkbPost': '保存' }, success: function(){ console.log('submit done') } }); }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
        //异常图片上传处理
        function submit(){
     
            //ajax 提交form
            $.ajax({
                url : 'http://i.cnblogs.com/EditDiary.aspx?opt=1',
                type : "POST",
                data : {
                    '__VIEWSTATE': '',
                    '__VIEWSTATEGENERATOR': '4773056F',
                    'Editor$Edit$txbTitle': '告警' Date.now(),
                    'Editor$Edit$EditorBody': '<img src="' curFrame '" />',
                    'Editor$Edit$lkbPost': '保存'
                },
                success: function(){
                    console.log('submit done')
                }
            });
        }

    自然如若必要页面跟新浪域名不相同,是无能为力发送 cookie 导致哀告跨域而失效,可是那几个很好消除,直接修改 host 即可(怎么修改就不介绍了,自行百度呢)

    自己那边改完 host,通过  的地点访谈页面,发掘摄像头竟然失效了~

    通过谷歌(Google)的文书档案能够查出,这是为了安全性思索,非 HTTPS 的服务端需要都无法对接录制头。不过消除办法也是部分,以 window 系统为例,张开 cmd 命令行面板并一直到 chrome 安装文件夹下,然后实行:

    ZSH

    chrome --unsafely-treat-insecure-origin-as-secure="" --user-data-dir=C:testprofile

    1
    chrome --unsafely-treat-insecure-origin-as-secure="http://i.cnblogs.com/h5monitor/final.html"  --user-data-dir=C:testprofile

    此举将以沙箱形式张开一个独自的 chrome 进度,并对点名的站点去掉安全限制。注意我们在新开的 chrome 中得重新登Land Rover扑。

    这儿便能不奇怪访谈录制头了,大家对代码做下拍卖,当差距检查评定开掘分外时,创制一份日记,最小间隔时间为5秒(可是后来意识没须求,因为乐乎已经有做了光阴限制,大约10秒后本事发布新的日志)

    JavaScript

    //按时捕获 function timer(delta){ setTimeout(function(){ captureAndSaveFrame(); renderDiff(); if(calcDiff() > 0.2){ //监察和控制到那一个,发日志 submit() } timer(delta) }, delta || 500); } setTimeout(timer, 五千0 * 10); //设定张开页面十秒钟后才伊始监察和控制//格外图片上传管理 function submit(){ var cache = arguments.callee, now = Date.now(); if(cache.reqTime && (now - cache.reqTime < 伍仟)) return; //日记创建最小间隔为5秒 cache.reqTime = now; //ajax 提交form $.ajax({ url : '', type : "POST", timeout : 5000, data : { '__VIEWSTATE': '', '__VIEWSTATEGENERATOR': '4773056F', 'Editor$Edit$txbTitle': '告警' Date.now(), 'Editor$Edit$EditorBody': '<img src="' curFrame '" />', 'Editor$Edit$lkbPost': '保存' }, success: function(){ console.log('submit done') }, error: function(err){ cache.reqTime = 0; console.log('error: ' err) } }); }

    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
        //定时捕获
        function timer(delta){
            setTimeout(function(){
                captureAndSaveFrame();
                renderDiff();
                if(calcDiff() > 0.2){  //监控到异常,发日志
                    submit()
                }
     
                timer(delta)
            }, delta || 500);
        }
     
        setTimeout(timer, 60000 * 10);  //设定打开页面十分钟后才开始监控
     
     
        //异常图片上传处理
        function submit(){
            var cache = arguments.callee,
                now = Date.now();
            if(cache.reqTime && (now - cache.reqTime < 5000)) return;  //日记创建最小间隔为5秒
     
            cache.reqTime = now;
     
            //ajax 提交form
            $.ajax({
                url : 'http://i.cnblogs.com/EditDiary.aspx?opt=1',
                type : "POST",
                timeout : 5000,
                data : {
                    '__VIEWSTATE': '',
                    '__VIEWSTATEGENERATOR': '4773056F',
                    'Editor$Edit$txbTitle': '告警' Date.now(),
                    'Editor$Edit$EditorBody': '<img src="' curFrame '" />',
                    'Editor$Edit$lkbPost': '保存'
                },
                success: function(){
                    console.log('submit done')
                },
                error: function(err){
                    cache.reqTime = 0;
                    console.log('error: ' err)
                }
            });
        }

    试行功用:

    新葡亰496net 9

    日记也是妥妥的出来了:

    新葡亰496net 10

    点开就能够看出那么些的那张图纸了:

    新葡亰496net 11

    要小心的是,博客园对日记发表数量是有做每一天额度限制来防刷的,到达限额的话会导致当天的随笔和作品也敬敏不谢发表,所以得小心运用:

    新葡亰496net 12

    唯独这种样式仅能反映相当图片,一时半刻无法让大家马上收悉告警,有意思味的童鞋能够试着再写个 chrome 插件,定期去拉取日记列表做推断,假诺有新增添日记则触发页面 alert。

    除此以外大家当然希望能直接对闯入者举行警戒,那块比较好办 —— 搞个警示的节拍,在十二分的时候接触播放就可以:

    JavaScript

    //播放音频 function fireAlarm(){ audio.play() } //定期捕获 function timer(delta){ setTimeout(function(){ captureAndSaveFrame(); if(preFrame && curFrame){ renderDiff(); if(calcDiff() > 0.2){ //监控到不行 //发日记 submit(); //播放音频告警 fireAlarm(); } } timer(delta) }, delta || 500); } setTimeout(timer, 五千0 * 10); //设定展开页面十分钟后才起头监察和控制

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
        //播放音频
        function fireAlarm(){
            audio.play()
        }
     
     
        //定时捕获
        function timer(delta){
            setTimeout(function(){
                captureAndSaveFrame();
                if(preFrame && curFrame){
                    renderDiff();
                    if(calcDiff() > 0.2){  //监控到异常
                        //发日记
                        submit();
                        //播放音频告警
                        fireAlarm();
                    }
                }
                timer(delta)
            }, delta || 500);
        }
     
        setTimeout(timer, 60000 * 10);  //设定打开页面十分钟后才开始监控

    最后说一下,本文代码均挂在我的github上,风野趣的童鞋可以自助下载。共勉~

    1 赞 4 收藏 4 评论

    新葡亰496net 13

    概述

    对于后台开荒以来,记录日志是一种万分广阔的开销习于旧贯,经常大家会接纳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渲染时间的优化和印象预加载的须要,假设贫乏质量监察和控制,该如何总计所做的渲染优化和形象预加载优化的优化比例,怎么着注明本身所做的事体具备价值吧?恐怕是透过测量试验同学的黑盒测量检验,对优化前后的小时开始展览录屏,深入分析从进来页面到影象渲染实现到底经过了有个别帧图像。那样的数据,只怕既不可相信、又相比较片面,虚构测验同学实际不是实在的用户,也无计可施恢复生机真实的用户他们所处的互连网情形。回过头来开掘,大家的门类,即便在服务端层面做好了日志和性质计算,但在前端对充裕的监察和属性的总括。对于前端的品质与特别申报的主旋律探求是有必不可缺的。

    前言

    前面在对合营社的前端代码脚本错误举行排查,试图减少 JS Error 的错误量,结合自身前边的阅历对那方面内容开始展览了实施并总计,上面就此谈谈自个儿对前面贰个代码分外监察和控制的部分见解。

    正文大约围绕上面几点张开研讨:

    1. JS 管理特别的点子
    2. 反映方式
    3. 特别监控上报常见难题

    【原创内容】转载请评释出处!

    • 网站组成(四有个别)
      和煦 http, https(https 是加密的 http)
      主机 g.cn zhihu.com之类的网址
      端口 HTTP 协议暗许是 80,由此一般不要填写
      路线 下边包车型大巴「/」和「/question/31838184」都以渠道
      http://www.zhihu.com:80/
      https://www.zhihu.com/question/31838184

    不行捕获

    对于前端来讲,我们须求的不行捕获无非为以下二种:

    • 接口调用情形;
    • 页面逻辑是或不是错误,比如,用户步向页面后页面呈现白屏;

    对此接口调用景况,在前端平常须要报告客户端相关参数,例如:用户OS与浏览器版本、诉求参数(如页面ID);而对此页面逻辑是否错误难点,经常除了用户OS与浏览器版本外,要求的是报错的货仓消息及现实报错地方。

    JS 非凡处理

    对此 Javascript 来说,我们面前碰着的仅仅只是至极,万分的产出不会直接形成 JS 引擎崩溃,最多只会使当前实施的天职终止。

    1. 近日代码块将用作一个职务压入任务队列中,JS 线程会不断地从职责队列中提取职分实施。
    2. 当职务实行进程中出现分外,且非凡未有捕获处理,则会直接本着调用栈一层层向外抛出,最后休息当前职务的施行。
    3. JS 线程会继续从职分队列中领到下二个职务继续试行。
    JavaScript
    
    &lt;script&gt; error console.log('永远不会执行'); &lt;/script&gt;
    &lt;script&gt; console.log('我继续执行') &lt;/script&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-5a707ba987416418324373-1">
    1
    </div>
    <div class="crayon-num crayon-striped-num" data-line="crayon-5a707ba987416418324373-2">
    2
    </div>
    <div class="crayon-num" data-line="crayon-5a707ba987416418324373-3">
    3
    </div>
    <div class="crayon-num crayon-striped-num" data-line="crayon-5a707ba987416418324373-4">
    4
    </div>
    <div class="crayon-num" data-line="crayon-5a707ba987416418324373-5">
    5
    </div>
    <div class="crayon-num crayon-striped-num" data-line="crayon-5a707ba987416418324373-6">
    6
    </div>
    <div class="crayon-num" data-line="crayon-5a707ba987416418324373-7">
    7
    </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-5a707ba987416418324373-1" class="crayon-line">
    &lt;script&gt;
    </div>
    <div id="crayon-5a707ba987416418324373-2" class="crayon-line crayon-striped-line">
      error
    </div>
    <div id="crayon-5a707ba987416418324373-3" class="crayon-line">
      console.log('永远不会执行');
    </div>
    <div id="crayon-5a707ba987416418324373-4" class="crayon-line crayon-striped-line">
    &lt;/script&gt;
    </div>
    <div id="crayon-5a707ba987416418324373-5" class="crayon-line">
    &lt;script&gt;
    </div>
    <div id="crayon-5a707ba987416418324373-6" class="crayon-line crayon-striped-line">
      console.log('我继续执行')
    </div>
    <div id="crayon-5a707ba987416418324373-7" class="crayon-line">
    &lt;/script&gt;
    </div>
    </div></td>
    </tr>
    </tbody>
    </table>
    

    新葡亰496net 14

    在对脚本错误实行申报从前,大家须求对这个举行拍卖,程序须求先感知到脚本错误的发出,然后再谈格外申报。

    脚本错误一般分为三种:语法错误,运维时不当。

    下边就探讨三种十三分监察和控制的管理方式:

    注:

    不行捕获方法

    try-catch 极度管理

    try-catch 在我们的代码中时时见到,通过给代码块进行 try-catch 进行李包裹装后,当代码块产生出错开上下班时间 catch 将能捕捉到错误的音讯,页面也将得以继续施行。

    不过 try-catch 管理极度的力量有限,只可以捕获捉到运维时非异步错误,对于语法错误和异步错误就显得敬谢不敏,捕捉不到。

    1 文件夹一时半刻不支持直接下载,能够步向文件夹,调用列表打包函数举办下载   downFilelist()  无任何参数

    • 微型Computer通讯靠IP地址,IP地址记不住就注明了域名(domain name)
      接下来计算机自动向DNS服务器(domain name server)查询域名
      对应的IP地址

    • 诸如g.cn那样的网站,能够透过Computer的ping程序查出对应 IP 地址
      ➜ ping g.cn
      PING g.cn (74.125.69.160): 56 data bytes

    大局捕获

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

    JavaScript

    window.onerror = function(errorMessage, scriptUTiguanI, lineNo, columnNo, error) { console.log('errorMessage: ' errorMessage); // 相当新闻console.log('scriptUEnclaveI: ' 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('这是一个错误');

    新葡亰496net 15

    通过window.onerror事件,可以收获具体的要命音信、非常文件的U瑞虎L、极度的行号与列号及非常的饭馆新闻,再捕获格外后,统一申报至大家的日记服务器。

    亦或是,通过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('这是一个错误');

    新葡亰496net 16

    亲自过问:运营时不当

    JavaScript

    try { error // 未定义变量 } catch(e) { console.log('笔者领会不当了'); console.log(e); }

    1
    2
    3
    4
    5
    6
    try {
      error    // 未定义变量
    } catch(e) {
      console.log('我知道错误了');
      console.log(e);
    }

    新葡亰496net 17

    可是对于语法错误和异步错误就捕捉不到了。

    2 必须先步向本人的网盘 再下载   假使是人家的 要先保存到本身的网盘 再拓展下载

    try… catch

    使用try... catch固然如此能够较好地开展非常捕获,不至于使得页面由于一处错误挂掉,但try ... catch破获格局体现过于臃肿,多数代码应用try ... catch卷入,影响代码可读性。

    演示:语法错误

    JavaScript

    try { var error = 'error'; // 大写分号 } catch(e) { console.log('小编感知不到错误'); console.log(e); }

    1
    2
    3
    4
    5
    6
    try {
      var error = 'error';   // 大写分号
    } catch(e) {
      console.log('我感知不到错误');
      console.log(e);
    }

    新葡亰496net 18

    一般语法错误在编辑器就能够展现出来,常表现的错误音讯为: Uncaught SyntaxError: Invalid or unexpected token xxx 这样。可是这种漏洞非常多会一向抛出特别,常使程序崩溃,一般在编码时候轻易观望获得。

    代码初始

    • 端口是怎样?
      一个比如:
      用邮局相互写信的时候,ip也就是地点(也得以当做邮政编码,地址是域名)
      端口是收信人姓名(因为二个地方比方企业、家独有多个地点,不过却可能有数不清收信人)
      端口正是多个标志收信人的数字。
      端口是贰个 16 位的数字,所以范围是 0-65535(2**16)
      0000 0000 0000 0000
      1111 1111 1111 1111

    遍布难点

    示范:异步错误

    JavaScript

    try { setTimeout(() => { error // 异步错误 }) } catch(e) { console.log('小编感知不到不当'); console.log(e); }

    1
    2
    3
    4
    5
    6
    7
    8
    try {
      setTimeout(() => {
        error        // 异步错误
      })
    } catch(e) {
      console.log('我感知不到错误');
      console.log(e);
    }

    新葡亰496net 19

    除非你在 setTimeout 函数中再套上一层 try-catch,不然就不或许感知到其荒谬,但这么代码写起来比较啰嗦。

    var s5 = '' yunData.sign5;
    var s1 = '' yunData.sign1;
    var t1 = s(s5,s1);
    var SIGN = base64Encode(t1);
    SIGN = encodeURIComponent(SIGN);

    HTTP协议

    • 三个传输协议,协议正是两岸都遵从的正规。
      为何叫超文本传输协议呢,因为收发的是文件音讯。
      1,浏览器(客户端)根据分明的格式发送文书数据(央求)到服务器
      2,服务器深入分析呼吁,遵照分明的格式重回文本数据到浏览器
      3,浏览器深入分析得到的数据,并做相应管理

    • 恳请和重临是同一的数据格式,分为4部分:
      1,乞请行大概响应行
      2,Header(诉求的 Header 中 Host 字段是必须的,别的都以可选)
      3,rnrn(接二连三四个换行回车符,用来分隔Header和Body)
      4,Body(可选)

    浏览器访谈那个网站的时候
    https://list.jd.com/list.html?cat=670,677,11303&go=0
    实则发的诉求如下
    恳请的格式,注意分寸写(那是四个不分包Body的伏乞):
    原来数据如下
    'GET /list.html?cat=670,677,11303&go=0 HTTP/1.1rnhost:list.jd.comrnrn'
    打字与印刷出来如下
    GET /list.html?cat=670,677,11303&go=0 HTTP/1.1
    Host: list.jd.com
    Gua: hello

    gua=1

    其中
    1, GET 是呼吁方法(还会有POST等,这正是个标记字符串而已)
    2,/ 是呼吁的路径(那象征根路线)
    3,HTTP/1.1 中,1.1是本子号,通用了20年

    实际字符串是 'GET / HTTP/1.1rnhost:g.cnrnrn'

    回到的多少如下
    HTTP/1.1 301 Moved Permanently
    Content-Type: text/html; charset=UTF-8
    Location: http://www.google.cn/

    <html>
    很好
    </html>

    Body部分太长,先不贴了
    当中响应行(第一行):
    1,HTTP/1.1 是版本
    2,301 是「状态码」,参见文末链接
    3,Moved Permanently 是状态码的汇报
    浏览器会协和整析Header部分,然后将Body展现成网页

    ——前端驾驭 HTTP 协议有哪些用——

    能够用 JS 动态抓取内容塑造页面
    比方动态评价、加载数据
    比方说气候预先报告程序
    比方说壁纸图片库
    浏览器提供了运用 HTTP 协议收发数据的接口,名称叫 AJAX
    这是三个第一的手艺

    ——浏览器安全主题素材——

    跨域诉求

    // 事件冒泡
    var id1 = document.querySelector('#id1')
    var id2 = document.querySelector('#id2')
    var id3 = document.querySelector('#id3')
    id1.addEventListener('click', function(event){
    console.log('click id1', event)
    })
    id2.addEventListener('click', function(event){
    console.log('click id2', event)
    })
    id3.addEventListener('click', function(event){
    console.log('click id3', event)
    // 吃掉冒泡事件
    // event.cancelBubble = true
    })

    // 事件捕获是 addEventListener 的第2个参数 useCapture
    // id1.addEventListener('click', function(event){
    // console.log('capture click id1', event)
    // }, true)
    // id2.addEventListener('click', function(event){
    // console.log('capture click id2', event)
    // }, true)
    // id3.addEventListener('click', function(event){
    // console.log('capture click id3', event)
    // }, true)

    //
    //
    // // 获取登入页面
    // // 创建 AJAX 对象
    // var r = new XMLHttpRequest()
    // // 设置央求方法和呼吁地址
    // r.open('GET', '/login', true)
    // // 注册响应函数
    // r.onreadystatechange = function() {
    // if(r.readyState == 4) {
    // console.log('须求成功', r.responseText.length)
    // }
    // }
    // // 发送要求
    // r.send()
    //
    //
    //
    // // 发送登陆数据
    // // 创建 AJAX 对象
    // var r = new XMLHttpRequest()
    // // 设置需要方法和须要地址
    // r.open('POST', '/login', true)
    // // 设置发送的数额的格式
    // r.setRequestHeader('Content-Type', 'application/json')
    // // 注册响应函数
    // r.onreadystatechange = function() {
    // if (r.readyState === 4) {
    // console.log('state change', r, r.status, r.response)
    // var response = JSON.parse(r.response)
    // console.log('response', response)
    // } else {
    // console.log('change')
    // }
    // }
    // // 发送央浼
    // var account = {
    // username: 'gua',
    // password: '123',
    // }
    // var data = JSON.stringify(account)
    // r.send(data)
    //
    //
    // // 能够打包成这么的贰个函数
    // var ajax = function(method, path, headers, data, reseponseCallback) {
    // var r = new XMLHttpRequest()
    // // 设置央求方法和伸手地址
    // r.open(method, path, true)
    // // 设置发送的多寡的格式
    // r.setRequestHeader('Content-Type', 'application/json')
    // // 注册响应函数
    // r.onreadystatechange = function() {
    // if(r.readyState === 4) {
    // reseponseCallback(r)
    // }
    // // if (r.readyState === 4) {
    // // console.log('state change', r, r.status, r.response)
    // // var response = JSON.parse(r.response)
    // // console.log('response', response)
    // // } else {
    // // console.log('change')
    // // }
    // }
    // // 发送需要
    // r.send(data)
    // }
    //
    // ajax('GET', '/login', null, '', function(r){
    // console.log(r.status, r.response)
    // })
    //
    // ajax('GET', 'https://api.douban.com/v2/book/1220562', null, '', function(r){
    // // console.log(r.status, book)
    // var book = JSON.parse(r.response)
    // var imgUrl = book.image
    // var body = document.querySelector('body')
    // var img = // <img src=${imgUrl}> //
    // body.insertAdjacentHTML('beforeend', img)
    // })
    //
    //
    // // GET /v2/movie/subject/1764796
    // ajax('GET', '/v2/movie/subject/1764796', null, '', function(r){
    // // console.log(r.status, book)
    // var movie = JSON.parse(r.response)
    // console.log(movie)
    // // var imgUrl = book.image
    // // var body = document.querySelector('body')
    // // var img = // // <img src=${imgUrl}> // //
    // // body.insertAdjacentHTML('beforeend', img)
    // })

    跨域脚本不可能精确捕获卓殊

    万般状态下,我们会把静态资源,如JavaScript本子放到特意的静态能源服务器,亦或然CDN,看以下例子:

    <!DOCTYPE html> <html> <head> <title></title> </head> <body> <script type="text/javascript"> // 在index.html window.onerror = function(errorMessage, scriptU本田UR-VI, lineNo, columnNo, error) { console.log('errorMessage: ' errorMessage); // 非常新闻console.log('scriptU奥迪Q5I: ' scriptU奥迪Q5I); // 格外文件路径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('这是一个错误');

    新葡亰496net 20

    结果展现,跨域之后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>

    window.onerror 万分管理

    window.onerror 捕获非凡技艺比 try-catch 稍微强点,无论是异步依旧非异步错误,onerror 都能捕获到运转时不当。

    亲自去做:运营时一并错误

    JavaScript

    /** * @param {String} msg 错误音讯 * @param {String} url 出错文件 * @param {Number} row 行号 * @param {Number} col 列号 * @param {Object} error 错误详细音讯 */ window.onerror = function (msg, url, row, col, error) { console.log('笔者精晓不当了'); console.log({ msg, url, row, col, error }) return true; }; error

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    /**
    * @param {String}  msg    错误信息
    * @param {String}  url    出错文件
    * @param {Number}  row    行号
    * @param {Number}  col    列号
    * @param {Object}  error  错误详细信息
    */
    window.onerror = function (msg, url, row, col, error) {
      console.log('我知道错误了');
      console.log({
        msg,  url,  row, col, error
      })
      return true;
    };
    error

    新葡亰496net 21

    身体力行:异步错误

    JavaScript

    window.onerror = function (msg, url, row, col, error) { console.log('笔者精晓异步错误了'); console.log({ msg, url, row, col, error }) return true; }; setTimeout(() => { error; });

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    window.onerror = function (msg, url, row, col, error) {
      console.log('我知道异步错误了');
      console.log({
        msg,  url,  row, col, error
      })
      return true;
    };
    setTimeout(() => {
      error;
    });

    新葡亰496net 22

    可是 window.onerror 对于语法错误仍然力不能够支,所以大家在写代码的时候要尽量制止语法错误的,然而貌似那样的谬误会使得整个页面崩溃,照旧相比易于能够发掘到的。

    在实际上的应用进度中,onerror 首尽管来捕获预料之外的荒唐,而 try-catch 则是用来在可预言景况下监察和控制特定的荒谬,两个结合使用进一步高效。

    须要小心的是,window.onerror 函数独有在回到 true 的时候,非凡才不会向上抛出,否则正是是明白那一个的发出调节台依旧会来得 Uncaught Error: xxxxx。

    新葡亰496net 23

    至于 window.onerror 还或然有两点须求值得注意

    1. 对于 onerror 这种全局捕获,最佳写在装有 JS 脚本的眼下,因为您不大概担保你写的代码是或不是出错,如果写在前边,一旦发生错误的话是不会被 onerror 捕获到的。
    2. 其余 onerror 是无力回天捕获到网络极其的不当。

    当我们相见 <img src="./404.png">报 404 网络诉求极度的时候,onerror 是无力回天增加接济大家捕获到不行的。

    JavaScript

    <script> window.onerror = function (msg, url, row, col, error) { console.log('作者掌握异步错误了'); console.log({ msg, url, row, col, error }) return true; }; </script> <img src="./404.png">

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <script>
      window.onerror = function (msg, url, row, col, error) {
        console.log('我知道异步错误了');
        console.log({
          msg,  url,  row, col, error
        })
        return true;
      };
    </script>
    <img src="./404.png">

    新葡亰496net 24

    鉴于网络央求分外不会事件冒泡,因而必须在破获阶段将其捕捉到才行,可是这种方法即使能够捕捉到网络诉求的不行,可是不能判别HTTP 的图景是 404 依旧别的诸如 500 等等,所以还索要特别服务端日志才进行排查深入分析才得以。

    JavaScript

    <script> window.addEventListener('error', (msg, url, row, col, error) => { console.log('我知道 404 错误了'); console.log( msg, url, row, col, error ); return true; }, true); </script> <img src="./404.png" alt="">

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <script>
    window.addEventListener('error', (msg, url, row, col, error) => {
      console.log('我知道 404 错误了');
      console.log(
        msg, url, row, col, error
      );
      return true;
    }, true);
    </script>
    <img src="./404.png" alt="">

    新葡亰496net 25

    那点知识可能需求领会,要不然用户访谈网站,图片 CDN 不可能服务,图片加载不出去而开采人士未有开掘就窘迫了。

    function s(j,r){var a=[];var p=[];var o="";var v=j.length;for(var q=0;q<256;q ){a[q]=j.substr((q%v),1).charCodeAt(0);p[q]=q}for(var u=q=0;q<256;q ){u=(u p[q] a[q])%6;var t=p[q];p[q]=p[u];p[u]=t}for(var i=u=q=0;q<r.length;q ){i=(i 1)%6;u=(u p[i])%6;var t=p[i];p[i]=p[u];p[u]=t;k=p[((p[i] p[u])%6)];o =String.fromCharCode(r.charCodeAt(q)^k)}return o}
    function base64Encode(t){var r,e,a,n,i,o,s="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 /";for(a=t.length,e=0,r="";a>e;){if(n=255&t.charCodeAt(e ),e==a){r =s.charAt(n>>2),r =s.charAt((3&n)<<4),r ="==";break}if(i=t.charCodeAt(e ),e==a){r =s.charAt(n>>2),r =s.charAt((3&n)<<4|(240&i)>>4),r =s.charAt((15&i)<<2),r ="=";break}o=t.charCodeAt(e ),r =s.charAt(n>>2),r =s.charAt((3&n)<<4|(240&i)>>4),r =s.charAt((15&i)<<2|(192&o)>>6),r =s.charAt(63&o)}return r}

    sourceMap

    平常在生养条件下的代码是经过webpack包裹后回退混淆的代码,所以大家大概会遇上那样的难点,如图所示:

    新葡亰496net 26

    笔者们发掘具有的报错的代码行数都在首先行了,为啥呢?那是因为在生养遭受下,大家的代码被压缩成了一行:

    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,那样浏览器就可以稳定到具体错误的职位:

    新葡亰496net 27

    开启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.scriptUEscortI; // 压缩文件路径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;

    如下图所示,我们早就得以看到,在服务端已经打响分析出了切实错误的行号、列号,大家得以由此日记的不二等秘书诀张开记录,到达了前面三个相当监察和控制的目标。

    新葡亰496net 28

    Promise 错误

    透过 Promise 能够匡助大家消除异步回调鬼世界的主题材料,可是假使 Promise 实例抛出十一分而你从未用 catch 去捕获的话,onerror 或 try-catch 也力不能及,不能捕捉到错误。

    JavaScript

    window.addEventListener('error', (msg, url, row, col, error) => { console.log('小编感知不到 promise 错误'); console.log( msg, url, row, col, error ); }, true); Promise.reject('promise error'); new Promise((resolve, reject) => { reject('promise error'); }); new Promise((resolve) => { resolve(); }).then(() => { throw 'promise error' });

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    window.addEventListener('error', (msg, url, row, col, error) => {
      console.log('我感知不到 promise 错误');
      console.log(
        msg, url, row, col, error
      );
    }, true);
    Promise.reject('promise error');
    new Promise((resolve, reject) => {
      reject('promise error');
    });
    new Promise((resolve) => {
      resolve();
    }).then(() => {
      throw 'promise error'
    });

    新葡亰496net 29

    就算在写 Promise 实例的时候养成最终写上 catch 函数是个好习于旧贯,可是代码写多了就轻松糊涂,忘记写 catch。

    故而假诺你的运用用到很多的 Promise 实例的话,极度是你在有个别基于 promise 的异步库举例 axios 等自然要小心,因为您不明白哪天那个异步要求会抛出十一分而你并从未拍卖它,所以您最棒加多叁个Promise 全局十分捕获事件 unhandledrejection。

    JavaScript

    window.add伊芙ntListener("unhandledrejection", function(e){ e.preventDefault() console.log('作者精晓 promise 的失实了'); console.log(e.reason); return true; }); Promise.reject('promise error'); new Promise((resolve, reject) => { reject('promise error'); }); new Promise((resolve) => { resolve(); }).then(() => { throw 'promise error' });

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    window.addEventListener("unhandledrejection", function(e){
      e.preventDefault()
      console.log('我知道 promise 的错误了');
      console.log(e.reason);
      return true;
    });
    Promise.reject('promise error');
    new Promise((resolve, reject) => {
      reject('promise error');
    });
    new Promise((resolve) => {
      resolve();
    }).then(() => {
      throw 'promise error'
    });

    新葡亰496net 30

    自然,假如您的使用尚未做 Promise 全局十一分管理的话,这很恐怕就如某乎首页那样:

    新葡亰496net 31

    var FSID = 0;
    var FSIDS = new Array();
    var FNAME = '';
    var _timestamp = yunData.timestamp;
    var xFilePath = '';
    if($(".lpq5dAp:first").html() == "笔者的钱袋"){
        xFilePath = '/';
    }else{
        xFilePath = decodeURI(location.href.match(/=/(.){1,}/i)[0].slice(1));
    }
    var xBDLink1 = ';
    var xBDLink2 = '';
    var xBDLink3 = '';
    $.ajaxSetup({async : false});
    // 查看要下载的文书 是列表中的第多少个
    var cNum = 0;
    function getWhich(fileName){
        var cFileName = fileName;
        $.get(xBDLink1,function(data){
            var dataList = data.list;
            for(i in dataList){
                var currentFileName = dataList[i].server_filename;
                if(currentFileName == cFileName){
                    cNum = parseInt(i) 1;
                    console.log('列表第' cNum '个文件');
                }
            }
        });
    }
    // 获取单个文件id
    function getFileID(index){
        var ii = parseInt(index-1);
        var xFid = new Array();
        var xFileName = new Array();
        $.get(xBDLink1,function(data){
            var dataList = data.list;
            for(i in dataList){
                xFid.push(dataList[i].fs_id);
                xFileName.push(dataList[i].server_filename);
            }
            FSID = xFid[ii];
            FSID = '[' FSID ']';
            FSID = encodeURI(FSID);
            FNAME = xFileName[ii];
        });
    }
    // 获取列表文件id 集结
    function getListID(){
        var xFid = new Array();
        var xFileName = new Array();
        $.get(xBDLink1,function(data){
            var dataList = data.list;
            for(i in dataList){
                if(dataList[i].isdir != 1){
                    xFid.push(dataList[i].fs_id);
                }
            }
            FSIDS = xFid;
            FSIDS = '[' FSIDS ']';
            FSIDS = encodeURI(FSIDS);
        });
    }
    // 单个文件下载
    function getLink1(SIGN,FSID){
        xBDLink2 = '';
        $.get(xBDLink2,function(data){
            console.log('' FNAME ' 的下载地址为:');
            console.log('%c%s','color:#00ff00;background-color:#新葡亰496net:来看看机智的前端童鞋怎么防盗,前端性能与异常上报。000000;',data.dlink['0'].dlink);
        });
    }
    // 文件夹下载
    function getLink2(SIGN,FSID){
        xBDLink2 = '';
        $.get(xBDLink2,function(data){
            console.log('' FNAME ' 的下载地址为:');
            console.log('%c%s','color:#00ff00;background-color:#000000;',data.dlink);
        });
    }
    // 步入文件夹里面把富有文件打包下载
    function getLink3(SIGN,FSIDS){
        xBDLink3 = '';
        $.get(xBDLink3,function(data){
            console.log('列表打包下载地址为:(不分包文件夹)');
            console.log('%c%s','color:#00ff00;background-color:#000000;',data.dlink);
        });
    }
    // 下载单个文件;
    function downOneFile(fileName){
        getWhich(fileName);
        getFileID(cNum);
        getLink1(SIGN,FSID);
    }
    // 下载三个文书夹
    function downOneFileBox(fileName){
        getWhich(fileName);
        getFileID(cNum);
        getLink2(SIGN,FSID);
    }
    // 下载列表打包      不含有文件夹
    function downFilelist(){
        getListID();
        getLink3(SIGN,FSIDS);
    }*

    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>

    特别申报格局

    监督检查获得报错音讯之后,接下去就必要将捕捉到的错误消息发送到新闻搜罗平台上,常用的出殡情势首要有两种:

    1. 透过 Ajax 发送数据
    2. 动态创立 img 标签的款型
    JavaScript
    
    function report(error) { var reportUrl = 'http://xxxx/report'; new
    Image().src = reportUrl   'error='   error; }
    
    <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-5a707ba98744f433416112-1">
    1
    </div>
    <div class="crayon-num crayon-striped-num" data-line="crayon-5a707ba98744f433416112-2">
    2
    </div>
    <div class="crayon-num" data-line="crayon-5a707ba98744f433416112-3">
    3
    </div>
    <div class="crayon-num crayon-striped-num" data-line="crayon-5a707ba98744f433416112-4">
    4
    </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-5a707ba98744f433416112-1" class="crayon-line">
    function report(error) {
    </div>
    <div id="crayon-5a707ba98744f433416112-2" class="crayon-line crayon-striped-line">
      var reportUrl = 'http://xxxx/report';
    </div>
    <div id="crayon-5a707ba98744f433416112-3" class="crayon-line">
      new Image().src = reportUrl   'error='   error;
    </div>
    <div id="crayon-5a707ba98744f433416112-4" class="crayon-line crayon-striped-line">
    }
    </div>
    </div></td>
    </tr>
    </tbody>
    </table>
    

    实例 – 动态创立 img 标签实行报告

    *// 用火狐浏览器

    本性监察和控制

    监察上报常见难点

    下述例子笔者总体位于小编的 github 上,读者能够自动查阅,前面不再赘言。

    JavaScript

    git clone cd blog/code/jserror/ npm install

    1
    2
    3
    git clone https://github.com/happylindz/blog.git
    cd blog/code/jserror/
    npm install

    //文件名称获取格局  鼠标右键某多个文件 重命名  然后全选

    最简单易行的品质监察和控制

    最布满的品质监察和控制须要则是须要我们总括用户从开首乞求页面到独具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.addEventListener('DOMContentLoaded', function() { console.log("DOM 挂载时间: ", Date.now() - timerStart); // 品质日志上报 }); window.add伊芙ntListener('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节点,可以参见下文关于首屏时间访问自动化的缓和方案来对渲染时间实行贿赂。

    Script error 脚本错误是何许

    因为大家在线上的版本,平日做静态能源 CDN 化,这就能够促成大家常访谈的页面跟脚本文件来自不一样的域名,那时候若无开始展览额外的配备,就能够轻松生出 Script error。

    新葡亰496net 32

    可通过 npm run nocors 查看效果。

    Script error 是浏览器在同源攻略限制下产生的,浏览器处于对安全性上的设想,当页面引用非同域名外界脚本文件时中抛出特其余话,此时本页面是尚未职分知道那些报错新闻的,取代他的是出口 Script error 那样的音讯。

    新葡亰496net 33

    如此做的目标是制止数据外泄到不安全的域中,举个差不离的例证,

    JavaScript

    <script src="xxxx.com/login.html"></script>

    1
    <script src="xxxx.com/login.html"></script>

    地点我们并未引进四个 js 文件,而是二个 html,这些 html 是银行的报到页面,假若您已经报到了,那 login 页面就能自行跳转到 Welcome xxx...,如若未登入则跳转到 Please Login...,那么报错也会是 Welcome xxx... is not defined,Please Login... is not defined,通过这几个音讯方可剖断二个用户是还是不是登陆他的帐号,给侵略者提供了这几个惠及的论断渠道,那是一对一不安全的。

    介绍完背景后,那么大家理应去解决这么些标题?

    第一能够想到的方案分明是同源化攻略,将 JS 文件内联到 html 大概放到同域下,就算能轻易实用地消除 script error 难题,不过如此不能够使用好文件缓存和 CDN 的优势,不推荐使用。精确的方式应该是从根本上解决 script error 的谬误。


    performance

    但是上述时间的监察过于轻巧,比方我们想计算文书档案的互连网加载耗费时间、分析DOM的耗费时间与渲染DOM的耗费时间,就不太好办到了,所幸的是浏览器提供了window.performance接口,具体可知MDN文档

    新葡亰496net 34

    差一些全数浏览器都帮助window.performance接口,下边来走访在调控台打字与印刷window.performance能够猎取些什么:

    新葡亰496net 35

    能够看出,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>
    

    跨源财富分享机制( COTiguanS )

    先是为页面上的 script 标签加多 crossOrigin 属性

    JavaScript

    // <script> window.onerror = function (msg, url, row, col, error) { console.log('笔者领悟不当了,也领略不当新闻'); console.log({ msg, url, row, col, error }) return true; }; </script> <script src="" crossorigin></script> // setTimeout(() => { console.log(error); })

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // http://localhost:8080/index.html
    <script>
      window.onerror = function (msg, url, row, col, error) {
        console.log('我知道错误了,也知道错误信息');
        console.log({
          msg,  url,  row, col, error
        })
        return true;
      };
    </script>
    <script src="http://localhost:8081/test.js" crossorigin></script>
     
    // http://localhost:8081/test.js
    setTimeout(() => {
      console.log(error);
    })

    当你改改完前端代码后,你还需求相当给后端在响应头里加上 Access-Control-Allow-Origin: localhost:8080,这里小编以 Koa 为例。

    JavaScript

    const Koa = require('koa'); const path = require('path'); const cors = require('koa-cors'); const app = new Koa(); app.use(cors()); app.use(require('koa-static')(path.resolve(__dirname, './public'))); app.listen(8081, () => { console.log('koa app listening at 8081') });

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    const Koa = require('koa');
    const path = require('path');
    const cors = require('koa-cors');
    const app = new Koa();
     
    app.use(cors());
    app.use(require('koa-static')(path.resolve(__dirname, './public')));
     
    app.listen(8081, () => {
      console.log('koa app listening at 8081')
    });

    新葡亰496net 36

    读者可因此 npm run cors 详细的跨域知识作者就不实行了,风乐趣能够看看作者事先写的文章:跨域,你需求精晓的全在这里

    你感到这么就完了呢?并从未,下边就说有个别 Script error 你有的时候遇见的点:

    大家都晓得 JSONP 是用来跨域获取数据的,何况包容性非凡,在部分使用中如故会选拔到,所以您的品类中恐怕会用那样的代码:

    JavaScript

    // window.onerror = function (msg, url, row, col, error) { console.log('小编精通不当了,但不明了不当音讯'); console.log({ msg, url, row, col, error }) return true; }; function jsonpCallback(data) { console.log(data); } const url = ''; const script = document.createElement('script'); script.src = url; document.body.appendChild(script);

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // http://localhost:8080/index.html
    window.onerror = function (msg, url, row, col, error) {
      console.log('我知道错误了,但不知道错误信息');
      console.log({
        msg,  url,  row, col, error
      })
      return true;
    };
    function jsonpCallback(data) {
      console.log(data);
    }
    const url = 'http://localhost:8081/data?callback=jsonpCallback';
    const script = document.createElement('script');
    script.src = url;
    document.body.appendChild(script);

    因为重返的音讯会当做脚本文件来试行,一旦回到的本子内容出错了,也是无力回天捕捉到错误的新闻。

    新葡亰496net 37

    消除办法也轻易,跟在此之前同一,在拉长动态增加脚本的时候增加crossOrigin,何况在后端配上相应的 COSportageS 字段就可以.

    JavaScript

    const script = document.createElement('script'); script.crossOrigin = 'anonymous'; script.src = url; document.body.appendChild(script);

    1
    2
    3
    4
    const script = document.createElement('script');
    script.crossOrigin = 'anonymous';
    script.src = url;
    document.body.appendChild(script);

    读者能够通过 npm run jsonp 查看效果

    新葡亰496net 38

    理解原理之后您或许会以为无妨,不就是给各样动态变化的台本加多crossOrigin 字段嘛,不过在其实工程中,你也许是面向广大库来编程,比方利用 jQuery,Seajs 恐怕 webpack 来异步加载脚本,多数库封装了异步加载脚本的力量,以 jQeury 为例你恐怕是如此来触发异步脚本。

    JavaScript

    $.ajax({ url: '', dataType: 'jsonp', success: (data) => { console.log(data); } })

    1
    2
    3
    4
    5
    6
    7
    $.ajax({
      url: 'http://localhost:8081/data',
      dataType: 'jsonp',
      success: (data) => {
        console.log(data);
      }
    })

    一经这个库中从不提供 crossOrigin 的力量的话(jQuery jsonp 大概有,假装你不清楚),那您不得不去修改人家写的源代码了,所以作者那边提供三个思路,就是去勒迫document.createElement,一直自上去为各种动态变化的剧本增多 crossOrigin 字段。

    JavaScript

    document.createElement = (function() { const fn = document.createElement.bind(document); return function(type) { const result = fn(type); if(type === 'script') { result.crossOrigin = 'anonymous'; } return result; } })(); window.onerror = function (msg, url, row, col, error) { console.log('笔者掌握不当了,也领悟不当音讯'); console.log({ msg, url, row, col, error }) return true; }; $.ajax({ url: '', dataType: 'jsonp', success: (data) => { console.log(data); } })

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    document.createElement = (function() {
      const fn = document.createElement.bind(document);
      return function(type) {
        const result = fn(type);
        if(type === 'script') {
          result.crossOrigin = 'anonymous';
        }
        return result;
      }
    })();
    window.onerror = function (msg, url, row, col, error) {
      console.log('我知道错误了,也知道错误信息');
      console.log({
        msg,  url,  row, col, error
      })
      return true;
    };
    $.ajax({
      url: 'http://localhost:8081/data',
      dataType: 'jsonp',
      success: (data) => {
        console.log(data);
      }
    })

    意义也是同样的,读者能够经过 npm run jsonpjq 来查看效果:

    新葡亰496net 39

    诸如此比重写 createElement 理论上没什么难题,可是凌犯了本来的代码,不保证一定不会出错,在工程上照旧要求多品尝下看看再选用,可能存在包容性上难点,借使你认为会现身什么样难点的话也招待留言钻探下。

    至于 Script error 的难点就写到这里,假设您精晓了上边的开始和结果,基本上绝大多数的 Script error 都能解决。

    *// 1.除了文件夹之外的单个文件下载       文件名 后缀名   例:  downOneFile('abc.mp5')   单引号必须带着
    // downOneFile('某一个文书名称.后缀名');

    算算网址品质

    使用performancetiming属性,能够获得页面品质相关的多寡,这里在相当多篇章都有涉嫌有关采纳window.performance.timing笔录页面质量的小说,比方alloyteam团体写的初探 performance – 监察和控制网页与程序品质,对于timing的种种质量含义,能够注重摘自此文的下图驾驭,以下代码摘自此文作为计量网址品质的工具函数参谋:

    新葡亰496net 40

    JavaScript

    // 获取 performance 数据 var performance = { // memory 是非标准属性,只在 Chrome 有 // 能源难点:作者有些许内部存款和储蓄器 memory: { usedJSHeapSize: 16一千00, // JS 对象(包含V8引擎内部对象)占用的内部存款和储蓄器,一定小于 totalJSHeapSize totalJSHeapSize: 35100000, // 可采用的内存 jsHeapSizeLimit: 793000000 // 内存大小限制 }, // 文学难题:笔者从哪里来? navigation: { redirectCount: 0, // 要是有重定向的话,页面通过五次重定向跳转而来 type: 0 // 0 即 TYPE_NAVIGATENEXT 平常步入的页面(非刷新、非重定向等) // 1 即 TYPE_RELOAD 通过 window.location.reload() 刷新的页面 // 2 即 TYPE_BACK_FO宝马7系WA猎豹CS6D 通过浏览器的前进后退开关踏向的页面(历史记录) // 255 即 TYPE_UNDEFINED 非以上措施走入的页面 }, timing: { // 在同四个浏览器上下文中,前多个网页(与当前页面不料定同域)unload 的时辰戳,假如无前贰个网页 unload ,则与 fetchStart 值相等 navigationStart: 1441112691931, // 前多少个网页(与当前页面同域)unload 的日子戳,假若无前一个网页 unload 可能前三个网页与日前页面差异域,则值为 0 unloadEventStart: 0, // 和 unload伊芙ntStart 相对应,重临前三个网页 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 事件抛出前发生 domContentLoaded伊芙ntStart: 1441112693093, // DOM 剖析实现后,网页国内资本源加载成功的时日(如 JS 脚本加载试行实现) domContentLoadedEventEnd: 1441112693101, // DOM 树分析完结,且能源也筹算得当的年华,Document.readyState 变为 complete,并将抛出 readystatechange 相关事件 domComplete: 1441112693214, // load 事件发送给文书档案,也即 load 回调函数初叶施行的时间 // 注意若无绑定 load 事件,值为 0 load伊夫ntStart: 1441112693214, // load 事件的回调函数实践完结的光阴 loadEventEnd: 1441112693215 // 字母顺序 // connectEnd: 1441112692155, // connectStart: 1441112692155, // domComplete: 1441112693214, // domContentLoadedEventEnd: 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, // unloadEventEnd: 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 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; //【主要】重定向的大运//【原因】拒绝重定向!比如, 就不该写成 times.redirect = t.redirectEnd - t.redirectStart; //【主要】DNS 查询时间 //【原因】DNS 预加载做了么?页面内是或不是行使了太多差异的域名导致域名查询的光阴太长? // 可采取 HTML5 Prefetch 预查询 DNS ,见:[HTML5 prefetch]() times.lookupDomain = t.domainLookupEnd - t.domainLookupStart; //【首要】读取页面第三个字节的时光 //【原因】那足以知晓为用户得到你的能源占用的年月,加异地机房了么,加CDN 处理了么?加带宽了么?加 CPU 运算速度了么? // TTFB 即 Time To First Byte 的意趣 // 维基百科: times.ttfb = t.responseStart - t.navigationStart; //【首要】内容加载成功的光阴 //【原因】页面内容通过 gzip 压缩了么,静态财富 css/js 等压缩了么? times.request = t.responseEnd - t.requestStart; //【主要】推行 onload 回调函数的岁月 //【原因】是还是不是太多不供给的操作都放到 onload 回调函数里实行了,思量过延迟加载、按需加载的战略么? times.loadEvent = t.load伊芙ntEnd - t.load伊芙ntStart; // DNS 缓存时间 times.appcache = t.domainLookupStart - t.fetchStart; // 卸载页面包车型客车小运 times.unloadEvent = t.unload伊夫ntEnd - t.unloadEventStart; // 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;
    }

    window.onerror 能或无法捕获 iframe 的失实

    当您的页面有利用 iframe 的时候,你需求对您引进的 iframe 做老大监察和控制的拍卖,不然假设您引进的 iframe 页面出现了难点,你的主站展现不出来,而你却雾里看花。

    先是供给强调,父窗口直接选取 window.onerror 是力不胜任直接破获,假如你想要捕获 iframe 的不行的话,有分好三种情景。

    假使您的 iframe 页面和你的主站是同域名的话,直接给 iframe 增加 onerror 事件就能够。

    JavaScript

    <iframe src="./iframe.html" frameborder="0"></iframe> <script> window.frames[0].onerror = function (msg, url, row, col, error) { console.log('作者理解 iframe 的错误了,也清楚不当新闻'); console.log({ msg, url, row, col, error }) return true; }; </script>

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <iframe src="./iframe.html" frameborder="0"></iframe>
    <script>
      window.frames[0].onerror = function (msg, url, row, col, error) {
        console.log('我知道 iframe 的错误了,也知道错误信息');
        console.log({
          msg,  url,  row, col, error
        })
        return true;
      };
    </script>

    读者能够透过 npm run iframe 查看效果:

    新葡亰496net 41

    假让你置于的 iframe 页面和您的主站不是同个域名的,然则 iframe 内容不属于第三方,是你能够决定的,那么能够透过与 iframe 通讯的章程将那么些音讯抛给主站接收。与 iframe 通讯的艺术有过多,常用的如:postMessage,hash 只怕 name 字段跨域等等,这里就不进行了,感兴趣的话能够看:跨域,你须要明白的全在那边

    假诺是非同域且网址不受本身主宰以来,除了通过调节台看到详细的错误音信外,不能够捕获,那是由于安全性的思考,你引进了三个百度首页,人家页面报出的一无是处凭啥令你去监督呢,这会引出非常多安全性的标题。

    // 2.下载某二个文件夹   例:downOneFileBox('高校拉脱维亚语四六级')      单引号必须带着
    // 假设文件夹里面还可能有文件夹    就进来文件夹 调用  downFilelist()   进行列表打包下载    例:  downOneFileBox('大学乌Crane语六级课程')
    // downOneFileBox('某二个文件夹名称');

    日志上报

    调整和减弱代码如何定位到脚本非常地方

    线上的代码大致都由此了削减管理,几13个文本打包成了一个并丑化代码,当大家吸取 a is not defined 的时候,大家根本不晓得这些变量 a 终究是怎么着意义,此时报错的荒谬日志鲜明是行不通的。

    先是想到的情势是选拔 sourcemap 定位到错误代码的具体地方,详细内容能够参见:Sourcemap 定位脚本错误

    其余也足以透过在包装的时候,在每一种合併的文件之间增加几行空格,并相应增加某些讲授,这样在定位难题的时候很轻便能够领略是哪些文件报的谬误,然后再经过一些要害词的追寻,能够异常的快地稳住到标题标所在地方。

    // 3.进去文件夹里面把装有文件打包下载       下载文件夹的话 最棒应用这种格局
    // 注:打包下载  是不分包文件夹的
    // downFilelist();**

    独立的日志域名

    对此日记上报利用单独的日志域名的指标是制止对作业产生影响。其一,对于服务器来讲,大家必定不希望占用专门的学问服务器的持筹握算财富,也不指望过多的日志在事情服务器积聚,变成职业服务器的寄存空间相当不足的场地。其二,大家精晓在页面开头化的经过中,会对页面加载时间、PV、UV等数据开展上报,那几个反映须要会和加载业务数据差不离是同有的时候候刻发生,而浏览器一般会对同贰个域名的乞求量有并发数的界定,如Chrome会有对并发数为6个的范围。因而要求对日记系统独立设定域名,最小化对页面加载性能产生的熏陶。

    搜罗至极音信量太多,怎么做

    借让你的网址访谈量十分的大,假诺网页的 PV 有 1kw,那么多个自然的失实发送的音讯就有 1kw 条,大家得以给网址设置贰个收集率:

    JavaScript

    Reporter.send = function(data) { // 只搜集 伍分之一 if(Math.random() < 0.3) { send(data) // 上报错误消息 } }

    1
    2
    3
    4
    5
    6
    Reporter.send = function(data) {
      // 只采集 30%
      if(Math.random() < 0.3) {
        send(data)      // 上报错误信息
      }
    }

    以此搜集率能够由此切实实在的意况来设定,方法三种化,能够行使一个随意数,也得以切切实实依据用户的有些特征来开始展览判别。

    地点差不离是自家对前面一个代码监察和控制的有的明了,提起来轻易,不过只要在工程化应用,难免要求考虑到包容性等样样难题,读者能够通过本人的具体情况举办调节,前端代码分外监察和控制对于我们的网址的协调起珍视大的效果与利益。借使文中装有不对的地方,还望指正。

    代码截止

    跨域的主题材料

    对于单身的日记域名,显著会涉及到跨域的主题材料,采用的消除方案一般有以下二种:

    • 一种是组织空的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呼吁会有长度的界定,须求确认保证的是央浼的长度不会超越阈值。

    参照文章

    • 脚本错误量极致优化-监察和控制上报与Script error
    • 后边多个代码非凡日志搜罗与监督
    • 前面一个法力堂——非凡不仅是try/catch

      1 赞 2 收藏 评论

     

    节省响应中央

    对此大家申报日志,其实对于客户端的话,并没有须要考虑上报的结果,以至对于举报退步,大家也没有须要在前面四个做其余交互,所以报告来讲,其实选拔HEAD诉求就够了,接口再次回到空的结果,最大地减弱上报日志造成的财富浪费。

    用火狐浏览器

    群集反映

    看似于七喜图的合计,借使大家的运用须要申报的日记数量过多,那么有不可或缺合并日志实行合併的反馈。

    杀鸡取蛋方案得以是尝尝在用户距离页面也许零部件销毁时发送叁个异步的POST伸手来张开反映,然则尝试在卸载(unload)文书档案在此以前向web服务器发送数据。保障在文书档案卸载期间发送数据一向是一个不便。因为用户代理经常会忽略在卸载事件管理器中发生的异步XMLHttpRequest,因为那时曾经会跳转到下一个页面。所以那边是必须安装为共同的XMLHttpRequest请求吗?

    JavaScript

    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); }

    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);
    }

    开辟你要下载的录像页面  等页面加载成功 **然后**打开控制台 F12 控制台**

    小结

    作为前端开拓者来说,要对成品保持敬畏之心,时刻保持对品质追求极致,对足够不可忍受的情态。前端的性质量监督控与充足申报显得尤其关键。

    代码难免一时常,对于那三个能够使用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 收藏 评论

    新葡亰496net 42

    复制代码开端到代码截至里面包车型地铁富有内容 到调控台  选择3种下载方式中的一种就可以 

    去掉 downXXX() 前面包车型地铁 '//'  直接回车 就可以看到下载地址     不用的记念把后边的 '//' 加上

     

    **假诺以为下载慢的话  能够合营 IDM  临近满速下载**   

    链接: 密码: 8mpv

     

    倘若遇上报错  试试以下二种方法 :

    1、清除缓存强制刷新页面

    2、看看 console.log(111) 能或不可能寻常打字与印刷  假若不能够的话 就新建标签页 展开百度盘

    3、关闭浏览器 清除缓存 张开浏览器 张开百度盘

    借使上述措施都无法健康下载 能够给我留言

    再一次重申 以上内容仅供调换学习 

    本文由新葡亰496net发布于新葡亰官网,转载请注明出处:新葡亰496net:来看看机智的前端童鞋怎么防盗,

    关键词: