在当下前后端分离的主流环境下,前端部分的优化变得越来越重要。为了提升前端的性能和用户体验,我觉得可能需要从三个维度采集数据进行分析。
采集用户的行为,监控产品在用户端的使用情况,根据数据可以明确,前端可以针对性的做优化和体验。
可以简单的初步建立这样的数据模型:
{
ip: ip地址,统计uv
ua: 浏览器的userAgent //方便区分浏览器的品牌
os: 系统名称
current_page_all: 当前页面访问总次数
width:浏览器宽度,
height:浏览器高度, //统计浏览器的尺寸
current_enter_time: 进入当前页面的时间戳
current_leave_time: 离开当前页面的时间戳
project_id: 项目的id,
url: 当前url,
user_uni_id: 临时分配给用户唯一的id
....
还有其他
}
2.在网页中植入对应的js代码
//进入
document.addEventListener('DOMContentLoaded',function(){
...
let args = {
ua: navigator.userAgent,
os: navigator.platform,
width: document.body.clientWidth || document.documentElement.clientWidth,
height: document.body.clientHeight || document.documentElement.clientHeight,
project_id: md5('abcd'),
user_uni_id: '临时分配给用户唯一的id',
url: window.location.href,
current_enter_time: new Date().getTime()
....
};
let img = new Image();
img.onload = function() {
img = null;
};
img.src= `https://localhost:9700/bury.gif?args=${qs(args)}`;
});
//离开
window.onbeforeunload = function() {
...
let args = {
...
leave_time: new Date().getTime()
....
};
let img = new Image();
img.onload = function() {
img = null;
};
img.src= `https://localhost:9700/bury.gif?args=${qs(args)}`;
}
const fs = require('fs');
route.get('/bury.gif',async (ctx)=>{
let queryStr = ctx.querystring;
let d = new Date();
let year = d.getFullYear();
let month = d.getMonth()+1;
let day = d.getDate()+1;
fs.writeFile(`../logs/${year}-${month}-${day}.log`,queryStr,{flag:'a',encoding:'utf-8',mode:'0666'},function(e){});
ctx.status = 200;
ctx.type = 'image/gif';
ctx.body = {};
});
简单的设想方案是先把数据收集到文本文件里,然后定时的分析这些文本文件,然后把筛选后的放到数据库中。
网页性能主要是收集是输入url地址到网页请求完成资源下载完成这段时间范围内一些请求、加载等指标。
比如举几个简单的指标:
首屏加载时长
HTML 文档被加载和解析完成
网页加载完成时间
白屏时间
其他…
//index.html
<html>
<head>
<script>
window.startTime = Date.now();
</script>
</head>
<body>
<script>
let diff = Date.now()-window.startTime;
console.log('白屏时长'+diff);
document.addEventListener('DOMContentLoaded',()=>{
console.log('HTML 文档被加载和解析完成时间'+Date.now()-window.startTime);
});
window.onload = function() {
console.log('网页加载完成时间'+Date.now()-window.startTime);
};
</script>
<script src="a.js"></script>
<script src="b.js"></script>
....
</body>
</html>
使用performance API
Performance是W3C性能小组引入进来的一个新的API,他可以很好的获取到首屏加载时间、白屏时间、dns查询时间等,是一个很方便的获取网页性能指标的API,而且目前大部分主流浏览器是支持的。
https://www.caniuse.com/
1.Performance一些常用用法的总结
let timing = window.performance.timing //白屏时间 timing.responseStart - timing.navigationStart //DNS 查询时长 timing.domainLookupEnd - timing.domainLookupStart //request请求耗时 timing.responseEnd - timing.responseStart //HTML 文档被加载和解析完成耗时 timing.domComplete - timing.domInteractive //网页加载完成耗时 timing.loadEventEnd - timing.navigationStart //重定向耗时 timing.redirectEnd - timing.redirectStart; //占用的内存 window.performance.memory.usedJSHeapSize;
2.此外还有一些高级用法,比如可以收集一些请求和静态资源的请求时间
let time = [];
let entryLists = window.performance.getEntries();
for(let i=0;i<entryLists.length;i++) {
let item = entryLists[i];
let obj = {};
let soureTypes = ['script','css','xmlhttprequest','link','img'];
if(soureTypes.indexOf(item.initiatorType)>=0){
obj.name = item.name;
//请求时间
obj.reqTime = item.responseEnd - item.responseStart;
time.push(obj);
}
}
3.关于Performance的更多用法可以参考:
https://www.jianshu.com/p/1355232d525a https://blog.csdn.net/hb_zhouyj/article/details/89888646
4.收集数据上报
收集上报数据,使用koa2创建接口performance.gif
const fs = require('fs');
route.get('/performance.gif',async (ctx)=>{
let queryStr = ctx.querystring;
let d = new Date();
let year = d.getFullYear();
let month = d.getMonth()+1;
let day = d.getDate()+1;
fs.writeFile(`../performance-logs/${year}-${month}-${day}.log`,queryStr,{flag:'a',encoding:'utf-8',mode:'0666'},function(e){});
ctx.status = 200;
ctx.type = 'image/gif';
ctx.body = {};
});
错误收集主要就是针对js报错、静态资源加载等出错信息进行收集。
try {
console.log(b);
} catch(e) {
console.log(e);
sendErrorReq();
};
但是使用使用的话,每个页面收集错误都要充斥这try catch,这样其实是不太好的。
而且catch似乎没办法捕获到异步的操作。
try {
setTimeout(()=>{
console.log(b);
})
} catch(e) {
console.log(e);
sendErrorReq();
};
试了一下没有执行到catch里边的sendErrorReq函数。
使用try catch是一种局部错误监听方式。
/**
*msg 错误信息
*url 错误所在的页面地址
*row 错误所在的行数
*col 错误所在的列数
**/
window.onerror = function(msg,url,row,col) {
console.log(msg,url,row,col, error);
};
b();
使用window.onerror可以检测到js的报错,但是没法监听静态资源加载失败的情况。
<img src="一个不存在的图片地址" alt="">
window.addEventListener(‘error’)能够监听到静态资源加载出错
window.addEventListener('error',(e)=>{
let localName = e.srcElement.localName;
let currentSrc = e.srcElement.currentSrc;
if(localName=='img') {
console.log(`图片${currentSrc}加载失败了`);
sendErrorData(currentSrc);
}
...
// e.preventDefault();
},true);
必须设为捕获过程中执行,否则依然无法监听。

new Promise((resolve,reject)=>{
reject('hi');
}).catch(e=>{
console.log(e);
sendErrorData(e)
});
axios.get(...).catch(e=>{
console.log(e);
sendErrorData(e)
})
但是有种情况,如果promise不加catch的话,
没法通过window.onerror去监听,但是还是通过监听unhandledrejection事件去收集的
window.addEventListener('unhandledrejection', event => {
console.log('error:'+event.reason);
sendErrorData(event.reason);
});
new Promise((resolve,reject)=>{
reject('hi');
});
在看下在vue中收集报错,以vue-cli3创建的项目进行演示
测试了一下window.onerror这种方式 无法监听错误的。
在网上找了下原因

可以看到在vue的源码里,因为如果没有定义errorHandler就会走到logError这个方法,所以没法使用window.onerror进行监听。
在vue的手册中,推荐监听vue报错的可以使用errorHandler这个配置方法。
//main.js
Vue.config.errorHandler = function (err, vm, info) {
console.log('错误是:', err)
sendErrorData(err);
}
然后随便故意写错
mounted() {
a();
}
当然最后把收集的错误上报给服务器,创建一个接口error.gif。
const fs = require('fs');
route.get('/error.gif',async (ctx)=>{
let queryStr = ctx.querystring;
....
fs.writeFile(`../error-logs/${year}-${month}-${day}.log`,queryStr,{flag:'a',encoding:'utf-8',mode:'0666'},function(e){});
ctx.status = 200;
ctx.type = 'image/gif';
...
});
Sinatra新手;我正在运行一些rspec测试,但在日志中收到了一堆不需要的噪音。如何消除日志中过多的噪音?我仔细检查了环境是否设置为:test,这意味着记录器级别应设置为WARN而不是DEBUG。spec_helper:require"./app"require"sinatra"require"rspec"require"rack/test"require"database_cleaner"require"factory_girl"set:environment,:testFactoryGirl.definition_file_paths=%w{./factories./test/
我有两个Rails模型,即Invoice和Invoice_details。一个Invoice_details属于Invoice,一个Invoice有多个Invoice_details。我无法使用accepts_nested_attributes_forinInvoice通过Invoice模型保存Invoice_details。我收到以下错误:(0.2ms)BEGIN(0.2ms)ROLLBACKCompleted422UnprocessableEntityin25ms(ActiveRecord:4.0ms)ActiveRecord::RecordInvalid(Validationfa
这篇文章是继上一篇文章“Observability:从零开始创建Java微服务并监控它(一)”的续篇。在上一篇文章中,我们讲述了如何创建一个Javaweb应用,并使用Filebeat来收集应用所生成的日志。在今天的文章中,我来详述如何收集应用的指标,使用APM来监控应用并监督web服务的在线情况。源码可以在地址 https://github.com/liu-xiao-guo/java_observability 进行下载。摄入指标指标被视为可以随时更改的时间点值。当前请求的数量可以改变任何毫秒。你可能有1000个请求的峰值,然后一切都回到一个请求。这也意味着这些指标可能不准确,你还想提取最小/
@作者:SYFStrive @博客首页:HomePage📜:微信小程序📌:个人社区(欢迎大佬们加入)👉:社区链接🔗📌:觉得文章不错可以点点关注👉:专栏连接🔗💃:感谢支持,学累了可以先看小段由小胖给大家带来的街舞👉微信小程序(🔥)目录自定义组件-behaviors 1、什么是behaviors 2、behaviors的工作方式 3、创建behavior 4、导入并使用behavior 5、behavior中所有可用的节点 6、同名字段的覆盖和组合规则总结最后自定义组件-behaviors 1、什么是behaviorsbehaviors是小程序中,用于实现
我正在尝试将以下SQL查询转换为ActiveRecord,它正在融化我的大脑。deletefromtablewhereid有什么想法吗?我想做的是限制表中的行数。所以,我想删除少于最近10个条目的所有内容。编辑:通过结合以下几个答案找到了解决方案。Temperature.where('id这给我留下了最新的10个条目。 最佳答案 从您的SQL来看,您似乎想要从表中删除前10条记录。我相信到目前为止的大多数答案都会如此。这里有两个额外的选择:基于MurifoX的版本:Table.where(:id=>Table.order(:id).
我目前正在用Ruby编写一个项目,它使用ActiveRecordgem进行数据库交互,我正在尝试使用ActiveRecord::Base.logger记录所有数据库事件具有以下代码的属性ActiveRecord::Base.logger=Logger.new(File.open('logs/database.log','a'))这适用于迁移等(出于某种原因似乎需要启用日志记录,因为它在禁用时会出现NilClass错误)但是当我尝试运行包含调用ActiveRecord对象的线程守护程序的项目时脚本失败并出现以下错误/System/Library/Frameworks/Ruby.frame
我有一个应用需要发送用户事件邀请。当用户邀请friend(用户)参加事件时,如果尚不存在将用户连接到该事件的新记录,则会创建该记录。我的模型由用户、事件和events_user组成。classEventdefinvite(user_id,*args)user_id.eachdo|u|e=EventsUser.find_or_create_by_event_id_and_user_id(self.id,u)e.save!endendend用法Event.first.invite([1,2,3])我不认为以上是完成我的任务的最有效方法。我设想了一种方法,例如Model.find_or_cr
我正在使用Ruby解决一些ProjectEuler问题,特别是这里我要讨论的问题25(Fibonacci数列中包含1000位数字的第一项的索引是多少?)。起初,我使用的是Ruby2.2.3,我将问题编码为:number=3a=1b=2whileb.to_s.length但后来我发现2.4.2版本有一个名为digits的方法,这正是我需要的。我转换为代码:whileb.digits.length当我比较这两种方法时,digits慢得多。时间./025/problem025.rb0.13s用户0.02s系统80%cpu0.190总计./025/problem025.rb2.19s用户0.0
在许多ruby类之间共享记录器实例的最佳(正确)方法是什么?现在我只是将记录器创建为全局$logger=Logger.new变量,但我觉得有更好的方法可以在不使用全局变量的情况下执行此操作。如果我有以下内容:moduleFooclassAclassBclassC...classZend在所有类之间共享记录器实例的最佳方式是什么?我是以某种方式在Foo模块中声明/创建记录器还是只是使用全局$logger没问题? 最佳答案 在模块中添加常量:moduleFooLogger=Logger.newclassAclassBclassC..
我正在寻找一个用ruby演示计时器的在线示例,并发现了下面的代码。它按预期工作,但这个简单的程序使用30Mo内存(如Windows任务管理器中所示)和太多CPU有意义吗?非常感谢deftime_blockstart_time=Time.nowThread.new{yield}Time.now-start_timeenddefrepeat_every(seconds)whiletruedotime_spent=time_block{yield}#Tohandle-vesleepinteravalsleep(seconds-time_spent)iftime_spent