读者投稿:一名老前端浅谈前端性能优化

写在开头
  • 本文来自前端巅峰读者投稿,作者shadow,一位老前端,在这里也欢迎各位读者朋友们积极投稿

说起前端性能,好像从萌新到大佬都能说出一些,但是如果让你系统的整理一套方案,你该怎么做?

通俗的说,什么样才算好?
  • 在此,我说一下自己的见解(一起探讨,学习),欢迎指正

    • 1:加载速度

    • 2:seo,

    • 3:内存优化(算法)

    • 4:兼容性好

    • 5:量化指标

1:加载速度,老生常谈的一个话题
  • 资源压缩合并,减少http请求,css,js,合并压缩,图片压缩(小图标使用精灵图)懒加载,

  • 按需加载,非必要资源异步加载,

    • js,使用document.createElement创建一个script标签,即document.createElement('script')

    • defer:在HTML解析完之后才会执行。如果是多个,则按照加载的顺序依次执行。

    • async:在加载完之后立即执行。如果是多个,执行顺序和加载顺序无关。

  • 缓存(重要):资源文件本地读取,而不会重新请求资源。

  • 缓存分为:

    • 强缓存:不用请求服务器,直接使用本地的缓存。http header:Expires或Cache-Control实现的。

    • Expires:服务器返回的绝对时间

    • 浏览器再次请求这个资源时,先从缓存中寻找,找到这个资源后,拿出它的Expires跟当前的请求时间比较,如果请求时间在Expires的时间之前,则命中缓存,否则不行。

    • 如果没有命中,Expires Header在重新请求资源的时候会被更新

    • 缺点:服务器的事件和客户端的时间不一致时。服务器时间与客户端时间相差较大时,缓存管理容易出现问题,影响缓存命中的结果,比如:修改客户端时间(羊毛党,qa党)

    • Cache-Control:服务器返回的相对时间。

    • 第一次请求资源,在之后的相对时间之内,都可以利用本地缓存。超出这个时间,则不能命中缓存。重新请求时,Cache-Control会被更新

    • 协商缓存:本地有资源,但是是否使用,怎么办呢,去问问服务器。http header: Last-Modified,If-Modified-Since ETag,If-None-Match Last-Modified,If-Modified-Since:

    • 第一次请求,服务器在返回这个资源的同时,会加上Last-Modified这个 response header,这个header表示这该资源在服务器上的最后修改时间,

    • 再次请求这个资源时,会加上If-Modified-Since这个 request header,这个header的值就是上一次返回的Last-Modified的值,

    • 服务器收到第二次请求时,会比对浏览器传过来的If-Modified-Since和资源在服务器上的最后修改时间Last-Modified,判断资源是否有变化。

    • 如果没有变化则返回304 Not Modified, 但不返回资源内容(此时,服务器不会返回 Last-Modified 这个 response header),如果有变化,就正常返回资源内容(继续重复整个流程)。

    • (304:从缓存中加载资源)

    • ETag、If-None-Match

    • 第一次请求一个资源,服务器在返回这个资源的同时,会加上ETag这个 response header,这个header是服务器根据当前请求的资源生成的唯一标识。这个唯一标识是一个字符串,只要资源有变化这个串就不同,跟最后修改时间无关

    • 再次请求这个资源时,会加上If-None-Match这个 request header,这个header的值就是上一次返回的ETag的值。

    • 第二次请求时,会对比浏览器传过来的If-None-Match和服务器重新生成的一个新的ETag,判断资源是否有变化。如果没有变化则返回304 Not Modified,但不返回资源内容(此时,由于ETag重新生成过,response header中还会把这个ETag返回,即使这个ETag并无变化)。

    • 如果有变化,就正常返回资源内容(继续重复整个流程)。这是服务器返回304时的response header

    • 使用cdn加载:第一次打开页面的时候,浏览器无缓存,使用CDN明显提高速度

  • dns预解析:

    • 通过 DNS 预解析来告诉浏览器未来我们可能从某个特定的 URL 获取资源,当浏览器真正使用到该域中的某个资源时就可以尽快地完成 #### DNS 解析。

    • 1:打开或关闭DNS预解析

<meta http-equiv="x-dns-prefetch-control" content="on"> 
  • on:表明强制打开浏览器的预解析。

  • 2:对指定的域名进行DNS预解析:

<link rel="dns-prefetch" href="http://www.smyhvae.com/"> 
  • 从该 URL 请求一个资源时,就不再需要等待 DNS 解析的过程。该技术对使用第三方资源特别有用。

2:seo: 为什么把seo作为前端性能的一部分?
  • 这个见仁见智,有很多人seo归为运营,这当然没错。但是当你老板问你,为什么你们公司的网站搜不到(搜索引擎前几页),你咋说?最简单的办法当然是花钱,不过,天下老板都一样,不扯了,回归正题

  • 1:meta,设置meta,但是现在单页面应用横行天下,这个看你的个人项目吧,ssr是一个解决方案

  • 2:img 标签的alt必须写,而且,图片尽量小,alt里面,是对图片的描述,不要乱写哦

  • 3:最大加资源的加载时间缩短

  • 4:dom结构层级不可过深,一个一面,你无限嵌套标签,所有人都会觉得你垃圾,而且对seo极为不友好。合理设计吧

  • 5:dom尽可能的加载快(和页面 加载快不完全是一个概念)

3:内存优化
  • 1:降低时间复杂度,什么叫时间复杂度,这里就不科普了。一个页面,你写的运行时消耗500M内存,别人写的50M,你说那个好?所以,大厂招人,很喜欢考这个,这就是原因。

  • 2:变量开辟,监听等,有释放就要记得回收。其实坚持一个原则即可,有始有终。

  • 3:尽可能的减少回流与重绘,频繁的修改,内存,CPU,GPU,都是在快速消耗,例如,使用echarts绘制图表的时候

4:兼容性好
  • 前端兼容,重要性不言而喻,面对的系统:Android(品牌多如狗),iOS,Mac,window等。各个系统还有很多的版本,有时候不是你的代码有问题,而是兼容这些版本出了问题。

  • 1:使用通用标签,前端的发展,固然出现了很多很好的用的语义化标签,但是兼容是个很大问题。所以,还是老实点,新的固然好,但是现实很残酷

  • 2:css,要加兼容前缀,1:-moz-代表firefox浏览器私有属性2:-ms-代表ie浏览器私有属性 3:-webkit-代表safari、chrome私有属性 4:-o-代表Opera

  • 3:js的兼容,这个就不说了,用babel吧,babel已经做得很好了。现在主流都是编译为es5,再老,也就基本不考虑了

5:量化指标
  • 前端的性能怎么量化?为什么要量化

  • 假设你们的产品很好,但是有一堆的用户打不开,你们老板心急如焚,问你们问题出在哪,数据怎么样,你咋办?

  • 错误监听收集。window.onerror 闪亮登场。

  • 收集哪些?

    • 1:navigator.userAgent,设备信息

    • 2:页面加载时长

    • 3:网络状态

    • 4:css状态,

    • 5:js是否报错

  • 一般来说,收集这些也就差不多了,结合你们的应用,再把userid,uuid等收集起来,你们就可以监听分析了。

  • 举个栗子吧:

  • 这个一般放在页面最上面,一般情况下,记得让运维开启资源跨域许可,不然,你的页面会报错哦,window.onerror 只能收集同源的。

(function (win) {
var endTime = ""; //启动时间
var u = navigator.userAgent; //用户设备信息
var ios = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/);
var Android = u.indexOf("Android") > -1 || u.indexOf("Linux") > -1;
var trident = u.indexOf("Trident") > -1; //IE内核
var presto = u.indexOf("Presto") > -1; //opera内核
var webKit = u.indexOf("AppleWebKit") > -1; //苹果、谷歌内核
var gecko = u.indexOf("Gecko") > -1 && u.indexOf("KHTML") == -1; //火狐内核
var safari = u.indexOf("Safari") == -1; //safari
var mobile = !!u.match(/AppleWebKit.*Mobile.*/) || !!u.match(/AppleWebKit/); //是否为移动终端
var os = ios
? "ios"
: Android
? "Android"
: trident
? "Trident"
: presto
? "Presto"
: webKit
? "AppleWebKit"
: gecko
? "Gecko"
: safari
? "safari"
: "other"; //操作系统
var networkStr = u.match(/NetType\/\w+/)
? u.match(/NetType\/\w+/)[0]
: "NetType/other"; //网络状态
networkStr = networkStr.toLowerCase().replace("nettype/", ""); //网络状态

win.TJJ_BUGCOLLECT = [];
// win.onerror = function (message, source, lineno, colno, error){
// console.log(message, source, lineno, colno, error)
// win.TJJ_BUGCOLLECT.push({
// message: error.toString(),
// uri: source,
// // lineno: lineno,
// // colno: colno,
// // error: error
// })
// }

// 监听资源加载错误(JavaScript Scource failed to load)
win.addEventListener(
"error",
function (event) {
console.log(event);
win.TJJ_BUGCOLLECT.push({
message: event.message || event.type + " in " + event.target.outerHTML,
uri: event.target.baseURI,
});
},
true
);

//首次进入系统,加载完成后执行
win.onload = function () {
var title = document.title; // 标题
// 程序启动信息
var errorObj = {
title: title,
userAgent: u,
errorCode: TJJ_BUGCOLLECT.length > 0 ? "-1" : "200",
endTime: new Date().getTime(),
os: os,
networkStr: networkStr,
errorArr: TJJ_BUGCOLLECT,
};
// console.log('onload');
// console.log('loadedTime', loadedTime);
// 创建标签存储信息
var script = document.createElement("script");
script.id = "tjjFissileErrorObj";
script.type = "text/javascript";
script.innerHTML = "tjjFissileBuger=" + JSON.stringify(errorObj);
document.body.appendChild(script);

if (TJJ_BUGCOLLECT.length > 0) {
errorLogUpload({
logLevel: 4,
errType: 1,
codeError: {
message: (function () {
var _msg = "";
TJJ_BUGCOLLECT.map(function (item) {
_msg += item.message + " && ";
});
return _msg;
})(),
uri: TJJ_BUGCOLLECT[0].uri,
},
});
}
};

// 获取url参数(&符号拼接参数)
function getQueryString(name) {
var reg = new RegExp("(^|&)" + name + "=([^&]*)", "ig");
var r;
r = window.location.search.substr(1).match(reg);
r == "null" || r == "undefined" ? (r = null) : "";
if (r) {
let val = r[r.length - 1].split("=")[1];
return val == "null" || val == "undefined" ? null : val;
}
// if (r) return unescape(r[2]);
r = getParams(name);
r == "null" || r == "undefined" ? (r = null) : "";
if (r) return r;
return null;
}
// 获取url参数(/符号拼接参数)
function getParams(key) {
var url = window.location.pathname;
var arr = url.split("/");
var index = arr.lastIndexOf(key);
return index < 0 ? null : arr[index + 1];
}

// 错误上报
win.errorLogUpload = function (obj) {
var uuid = getQueryString("version") || "";
var xhr = new XMLHttpRequest();
// console.log('报错:',obj)
var params = Object.assign(
{
base: {
title: document.title || "",
userAgent: u || "",
logTime: new Date().getTime() || "", // 日志时间
os: "h5",
appVer: getQueryString("version") || "",
webId: uuid, //设备号
token: getQueryString("token") || "",
uid: getQueryString("user_id") || "", //用户id
netType: networkStr || "", // 网络状态
sysVer: "",
imei: "",
uuid: uuid,
deviceId: uuid,
chan: "",
appType: 4,
traceId: "",
sessionId:
getQueryString("sessionid") || getQueryString("session_id") || "",
logType: 5,
cuid: uuid,
login_mode: "",
},
},
obj
);
// console.log('报错:', params)
console.log("报错:", JSON.stringify(params));
// xhr.open("POST", "http://app-log.tjjshop.cn/app/log", true);
if (window.location.href.indexOf("growth.taojiji.com") != -1) {
xhr.open(
"POST",
"https://fissile.taojiji.com/safety.php/index/pageLog",
true
);
} else {
xhr.open(
"POST",
"https://fissile.tjjshop.cn/safety.php/index/pageLog",
true
);
}

//设置发送数据的请求格式
// xhr.setRequestHeader('content-type', 'application/json');
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && (xhr.status == 200 || xhr.status == 304)) {
//根据服务器的响应内容格式处理响应结果
// if(xhr.getResponseHeader('content-type')==='application/json'){
// var result = JSON.parse(xhr.responseText);
// //根据返回结果判断验证码是否正确
// if(result.code===-1){
// alert('验证码错误');
// }
// } else {
// console.log(xhr.responseText);
// }
console.log("上报成功!");
}
};
// let sendData = {"tjjFissileBuger":errorObj,"safety_microtime":getQueryString("safety_microtime")};
//将用户输入值序列化成字符串
xhr.send(JSON.stringify(params));
};
})(window);
  • 总结:前端的优化是个复杂的系统性工程,以上也只是我的简单个人经验,每一点都有很多的知识点,我只是略懂皮毛,一起学习进步吧

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值