- 首屏时间
- 首屏可交互时间
- 首次有意义内容渲染时间
性能检测: https://developers.google.com/speed/pagespeed/insights
polyfill: https://polyfill.io/v3/url-builder
- 懒加载
- 异步加载
- polyfill
- 打包压缩,webpack4版本已经内置
- gzip,一种压缩算法,一般默认开启
- 图片格式的优化,压缩 tinypng.com,根据屏幕分辨率展示不同分辨率的图片,webp(可以使图片在无损的情况下进行压缩,需要判断浏览器是否支持webp)
- 尽量控制cookie的大小,每一个request header都要携带cookie,同域名请求都会带上所有的cookie,耗费多余的体积。
通过一些代码能力影响资源的加载。
promise.all
并行执行promise,并发发请求ssr
把打包放到服务端,好处是可以做缓存,更新更新的地方,并且seo友好prefetch, prerender, preload
// 碰到这行代码后会立即请求dns解析,也就是预解析
<link rel="dns-prefetch" href="xxx.com" />
// 预建立网络连接,包括dns解析,TLS协商,TCP握手
<link rel="preconnect" href="xxx.com" />
//预加载
<link rel="preload" as='image' href="xxx.com/p.png" />
cdn、cdn预热、cdn刷新。
cnd域名和业务域名通常是不一样的,为了避免cookie影响,增加请求消耗。
// 封装一个计时的装饰器函数
function measure(fn) {
return async function () {
console.time(fn.name);
const ret = await fn.apply(this, arguments);
console.timeEnd(fn.name);
return ret;
};
}
const fn = (name) => {
return new Promise((resolve) => {
setTimeout(() => {
console.log(name);
resolve(name);
}, 2000);
});
};
measure(fn)("reonce");
首先应该判断浏览器是否支持webp。canisuse.com可以看浏览器支持性。
封装一个判断方法:
function checkWebp() {
try {
return (
document
.createElement("canvas")
.toDataURL("image/webp")
.indexOf("data:image/webp") === 0
);
} catch (error) {
return false;
}
}
// document.createElement("canvas").toDataURL("image/webp")转化如下
// 'data:image/webp;base64,UklGRtgCAABXRUJQVlA4WAoAAAAwAAAAKwEAlQAASUNDUBgCAAAAAAIYAAAAAAQwAABtbnRyUkdCIFhZWiAAAAAAAAAAAAAAAABhY3NwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAA9tYAAQAAAADTLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlkZXNjAAAA8AAAAHRyWFlaAAABZAAAABRnWFlaAAABeAAAABRiWFlaAAABjAAAABRyVFJDAAABoAAAAChnVFJDAAABoAAAAChiVFJDAAABoAAAACh3dHB0AAAByAAAABRjcHJ0AAAB3AAAADxtbHVjAAAAAAAAAAEAAAAMZW5VUwAAAFgAAAAcAHMAUgBHAEIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFhZWiAAAAAAAABvogAAOPUAAAOQWFlaIAAAAAAAAGKZAAC3hQAAGNpYWVogAAAAAAAAJKAAAA+EAAC2z3BhcmEAAAAAAAQAAAACZmYAAPKnAAANWQAAE9AAAApbAAAAAAAAAABYWVogAAAAAAAA9tYAAQAAAADTLW1sdWMAAAAAAAAAAQAAAAxlblVTAAAAIAAAABwARwBvAG8AZwBsAGUAIABJAG4AYwAuACAAMgAwADEANkFMUEgSAAAAAQcQEREQkCT+/x9F9D/tf0MAVlA4IIAAAABwDQCdASosAZYAPm02mUmkIyKhICgAgA2JaW7hdrEbQAnsA99snIe+2TkPfbJyHvtk5D32ych77ZOQ99snIe+2TkPfbJyHvtk5D32ych77ZOQ99snIe+2TkPfbJyHvtk5D32ych77ZOQ99snIe+2TkPfasAAD+/9YAAAAAAAAAAA=='
通过创建一个 canvas
元素,将它转为base64格式的image/webp,如果浏览器支持,它的开头将带有 data:image/webp
的字符串。(如代码注释)。如果不存在或者报错,则返回false。
接下来看一下如何经过判断去拼接阿里oss:
const supportWebp = checkWebp();
export function getWebpImageUrl(url) {
// 不存在url,抛出异常
if (!url) {
throw new Error("url不能为空");
}
// 如果url是base64格式,直接返回
if (url.startsWith("data:")) {
return url;
}
// 如果浏览器不支持,直接返回
if (!supportWebp) {
return rul;
}
// 拼接阿里oss, 假定为:x-oss-process=image/format,webp
return `${url}?x-oss-process=image/format,webp`;
}
注意一个点,以上是默认图片都在oss上的
代码题,实现promise的并发控制,例如要求同时进行3个图片的加载,始终加载3个
这里不能用for循环+promise.all的原因是,后者是执行完3个再去执行3个,不符。
function limitLoad(urls, handler, limit) {
const sequence = [].concat(urls);
let promises = [];
promises = sequence.splice(0, limit).map((url, index) => {
return handler(url).then(() => {
// 当执行完之后,再填补一个,所有需要知道哪个执行完了
return index;
});
});
// 通过竞速选出第一个执行完的
// 补充,出现执行完的之后,其他的还会执行,只是race函数只返回了最快的。
let p = Promise.race(promises);
for (let i = 0; i < sequence.length; i++) {
// 最终p会变成 p.then().then().then()... 它是通过链式调用的形式去不断的竞速和填充
p = p.then((res) => {
promises[res] = handler(sequence[i]).then(() => {
return res;
});
return Promise.race(promises);
});
}
}
// 模拟url地址和加载时间
const mockUrls = [
{
url: "地址1",
time: 2000,
},
{
url: "地址2",
time: 2300,
},
{
url: "地址3",
time: 4000,
},
{
url: "地址4",
time: 3000,
},
{
url: "地址5",
time: 2000,
},
{
url: "地址6",
time: 1000,
},
];
// 设置要执行的任务
function loadImg(url) {
return new Promise((resolve, reject) => {
console.log("-----" + url.url + "start!");
setTimeout(() => {
console.log(url.url + "---OK!!!");
resolve();
}, url.time);
});
}
limitLoad(mockUrls, loadImg, 3);