Skip to content

Commit 90ad7c2

Browse files
author
zhangdong
committed
性能优化
2 parents af348e1 + 9bd7ebd commit 90ad7c2

File tree

7 files changed

+94
-109
lines changed

7 files changed

+94
-109
lines changed

.vitepress/config.mts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,6 @@ export default defineConfig({
301301
items: [
302302
{ text: "检查git commit内容", link: "/configure/project" },
303303
{ text: "难点", link: "/configure/difficult" },
304-
{ text: "要点", link: "/configure/bagu" },
305304
{ text: "工程化", link: "/configure/engine" },
306305
{ text: "性能优化", link: "/configure/opt" },
307306
],

configure/bagu.md

Lines changed: 0 additions & 78 deletions
This file was deleted.

configure/difficult.md

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -44,34 +44,6 @@ layout: doc
4444
这些资源可以从一个上下文转移到另一个,但是资源一次只能在一个上下文可用
4545

4646
## 组件库
47-
## 瀑布流组件
48-
性能优化
49-
50-
1. 首先是图片方面,为了降低FCP,LCP
51-
* 首先禁止上传gif格式。图片格式可以转成webp格式,按照华为云obs文档,在url上添加请求参数可以返回webp格式图片,但是这么请求的图片不会被http缓存,并且每请求一次都会重新进行格式转化,成本会大幅上升。所有需要上传的时候就将图片转成webp格式存放在桶里,返回的时候返回webp即可
52-
* 按照屏幕宽度请求合适尺寸的图片,但会回到之前那个问题也就是不会被http缓存,这样虽然可以加速首屏,但是之后每次都需要重新获取。我是通过service worker来解决的。通过拦截指定的图片域名请求,如果没有命中会放入cache中,命中则从cache中取。当然也可以给缓存设置过期时间,类似于强缓存的效果
53-
* 可以使用preconnect提前建立与obs域名的连接,当然如果需要与多个第三方域建立连接,全部preconnect可能会适得其反,可以替换为dns-prefetch
54-
* 我观察到现在的协议是http1.1,可以使用http2解决队头阻塞的问题,当然http2也只是解决了http层的阻塞,tcp层的阻塞没有解决,http3就是为了解决tcp层的队头阻塞问题,他使用的是quic协议,基于utp而非tcp,所有下一代方案是使用http3
55-
56-
2.代码或者框架层面
57-
* 我刚才又提到我自己fetch api拿到图片信息,之前是在for循环了一个一个await拿到,但是可以并发发送请求.因为每个网站都有最大连接数,需要写一个限制最大并发数的函数,并发数我设置的是6.
58-
* 刚才提到我实现了一个懒加载的自定义指令,每个图片里都会new 一个intersectionobserve,我想使用一个单例实现,并且我在mdn看了文档,发现它可以observe某个元素,不需要可以unobserve.证明这是可行的,所以我封装了一个hook用来创建intersectionobserve实例.这样只需要一个实例既可完成监听功能
59-
* 对于resizeObserve,因为他的触发频率会很高,所以使用了节流,并且将回调放在requestAnimationFrame里,还有就是我只关心它的宽度,如果宽度不变,高度改变,不会触发回调,这在初始化的时候高度会变化
60-
* 框架上则是使用computed缓存了一些计算结果,有使用到watch,但是这个watch只是第一次有用,使用了vue一个比较新的配置项once.来实现一次监听即销毁
61-
* 最后在umounted生命周期释放之前那两个web api实例的内存占用
62-
63-
用户体验上
64-
65-
1. ui上的要求是滚动时隐藏搜索条件,使用了vue的内置组件transion实现了v-if的动画。
66-
2. 对于瀑布流组件需要先获取数据才能渲染,为了视觉不会太突兀,这里准备了一个骨架屏
67-
68-
性能指标
69-
整体得分是从93到96,fcp提升12.5%,lcp提升18.75%,speed index提升了27.27%
70-
71-
问题
72-
如果第一页的数据没有铺满屏幕,就会请求两次接口
73-
74-
首先第一次接口拿到数据,然后渲染出来,这个时候intersectionobserve监听到,发送下一次请求,所以我根据第一次接口返回的数据判断是否需要请求下一页(根据total和record.length < pagesize.length),将这个属性传递给瀑布流组件,是否需要请求下一页
7547

7648
```js
7749
const CACHE_NAME = 'imageCache'

configure/engine.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ CICD
5757
* tree-shaking也是利用ast进行静态分析
5858
* UglifyJS也是利用ast进行代码压缩
5959
* vite用的esbuild有个功能是将cjs转出esm,也是通过ast来实现的
60+
61+
### tree-shaking
62+
基于静态分析,只适用于esm,因为esm是静态的,在编译时就可以确定导入导出,据此去除未使用的代码
6063
### 举例
6164
```js
6265
let a = 1

configure/opt.md

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,31 @@ layout: doc
1616
* 代码上要写成支持tree-shaking的格式以及提高复用率变相降低打包体积,框架上都会提供一些性能优化相关的api
1717
* 从指标inp来看,我们应该减少用户ui交互响应时间,这通常是因为长任务引起的阻塞,所以可以将长任务放到web woker里执行,甚至web worker里又可以创建其他线程,来加速任务的执行.充分利用现代计算机多核的优势.另一方面对于cpu密集型任务,js是一门解释型语言,我们可以利用web assembly直接提供二进制代码交给计算机执行
1818

19-
* cdn分为边缘节点也叫pop节点,用于直接向用户提供服务,还有骨干节点,如果边缘节点没命中,就会向骨干节点请求,如果同样没有命中,则会触发回源
19+
* cdn分为边缘节点也叫pop节点,用于直接向用户提供服务,还有骨干节点,如果边缘节点没命中,就会向骨干节点请求,如果同样没有命中,则会触发回源
20+
## 实践
21+
### 瀑布流组件
22+
1. 首先是图片方面,为了降低FCP,LCP
23+
* 首先禁止上传gif格式。图片格式可以转成webp格式,按照华为云obs文档,在url上添加请求参数可以返回webp格式图片,但是这么请求的图片不会被http缓存,并且每请求一次都会重新进行格式转化,成本会大幅上升。所有需要上传的时候就将图片转成webp格式存放在桶里,返回的时候返回webp即可
24+
* 按照屏幕宽度请求合适尺寸的图片,但会回到之前那个问题也就是不会被http缓存,这样虽然可以加速首屏,但是之后每次都需要重新获取。我是通过service worker来解决的。通过拦截指定的图片域名请求,如果没有命中会放入cache中,命中则从cache中取。当然也可以给缓存设置过期时间,类似于强缓存的效果
25+
* 可以使用preconnect提前建立与obs域名的连接,当然如果需要与多个第三方域建立连接,全部preconnect可能会适得其反,可以替换为dns-prefetch
26+
* 我观察到现在的协议是http1.1,可以使用http2解决队头阻塞的问题,当然http2也只是解决了http层的阻塞,tcp层的阻塞没有解决
27+
28+
2.代码或者框架层面
29+
* 我是通过fetch拿到图片信息,之前是在for循环了一个一个await拿到,但是可以并发发送请求.因为每个网站都有最大连接数,需要写一个限制最大并发数的函数
30+
* 懒加载我是通过自定义指令和intersectionobserve实现的,每个图片里都会new 一个intersectionobserve,我想使用一个单例实现,并且我在mdn看了文档,发现它可以observe某个元素,不需要可以unobserve.证明这是可行的,所以我封装了一个hook用来创建intersectionobserve实例.这样只需要一个实例既可完成监听功能
31+
* 对于resizeObserve,因为他的触发频率会很高,所以使用了节流,并且将回调放在requestAnimationFrame里,还有就是我只关心它的宽度,如果宽度不变,高度改变,不会触发回调,这在初始化的时候高度会变化
32+
* 框架上则是使用computed缓存了一些计算结果,有使用到watch,但是这个watch只是第一次有用,使用了vue一个比较新的配置项once.来实现一次监听即销毁
33+
* 最后在umounted生命周期释放之前那两个web api实例的内存占用
34+
35+
用户体验上
36+
37+
1. ui上的要求是滚动时隐藏搜索条件,使用了vue的内置组件transion实现了v-if的动画。
38+
2. 对于瀑布流组件需要先获取数据才能渲染,为了视觉不会太突兀,这里准备了一个骨架屏
39+
40+
性能指标
41+
整体得分是从93到96,fcp提升12.5%,lcp提升18.75%,speed index提升了27.27%
42+
43+
问题,重复接口请求
44+
如果第一页的数据没有铺满屏幕,就会请求两次接口
45+
46+
首先第一次接口拿到数据,然后渲染出来,这个时候intersectionobserve监听到,发送下一次请求,所以我根据第一次接口返回的数据判断是否需要请求下一页(根据total和record.length < pagesize.length),将这个属性传递给瀑布流组件,是否需要请求下一页

javascript/grammar/summary.md

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,51 @@ layout: doc
4141
console.log(value); // 输出: 1, 2, 3, 4
4242
}
4343
```
44+
4. ## 事件循环
45+
首先js是单线程语言,使用事件循环的原因是为了避免代码阻塞
4446

45-
4. this 打印结果
47+
由调用栈,任务队列,web Api组成
48+
49+
调用栈用来存储同步任务,按顺序执行函数,任务队列分为 宏任务队列(MacroTask Queue) 和 微任务队列(MicroTask Queue),webapi是浏览器提供的异步 API(如 setTimeout、fetch),处理完成后将回调推入队列。
50+
51+
第一步是同步任务直接进入调用栈执行,遇到异步任务交给 Web APIs 处理。第二步当调用栈为空时,事件循环优先清空微任务队列中的所有任务,第三步是从宏任务队列中取出一个任务(如 setTimeout、DOM 事件)执行,然后回到步骤 1。
52+
```js
53+
console.log("1"); // 同步任务
54+
55+
setTimeout(() => console.log("2"), 0); // 宏任务
56+
57+
Promise.resolve()
58+
.then(() => console.log("3")) // 微任务
59+
.then(() => console.log("4")); // 微任务
60+
61+
console.log("5"); // 同步任务
62+
63+
// 输出顺序:1 → 5 → 3 → 4 → 2
64+
```
65+
宏任务:`setTimeout``DOM 事件``I/O``requestAnimationFrame`
66+
67+
微任务:`Promise回调``MutationObserver`
68+
7. ## 浏览器搜索url
69+
首先浏览器是一个多进程的架构,例如browser进程,每个页签都有一个render进程,最重要的主线程就在其中
70+
71+
当在搜索栏搜索时,browser进程下的ui线程会判断搜索的是不是url,如果不是,会交给搜索引擎进行处理,反之
72+
73+
如果这是一个域名的话,需要进行dsn解析,因为域名只是方便记忆,访问的话还是需要ip地址
74+
75+
建立tcp连接,如果是https的话,还需要通过tls协议来建立安全连接,然后再发送http请求
76+
77+
拿spa应用举例,根据url请求服务器,如果url最后带斜杠,默认返回下面的index.html,如果没有这个资源,不同网站可能有重定向策略
78+
79+
80+
浏览器处理资源
81+
* 解析html生成dom树
82+
* 遇到css生成cssom树
83+
* 遇到普通js会阻塞html解析,需要下载再执行,加了defer或者async不会阻塞
84+
* 将dom树和cssom树合成渲染树
85+
* 在主线程计算几何布局,层次,交给合成器线程和GPU渲染页面
86+
* 请求的资源都会走http缓存
87+
88+
6. this 打印结果
4689

4790
```js
4891
var a = 1;

javascript/handlecode.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,25 @@ function myPromiseAllSettle(promises){
7575
})
7676
}
7777
```
78+
## 控制并发,错误处理,重试
79+
```js
80+
async function limit(urls,max){
81+
let res = []
82+
let s = new Set()
83+
for(let i = 0;i<urls.length;i++){
84+
if(s.size===max){
85+
await Promise.race(s)
86+
}
87+
const fn = query(urls[i]).then((data)=>{
88+
res.push(data)
89+
s.delete(fn)
90+
})
91+
s.add(fn)
92+
}
93+
await Promise.all(s)
94+
return res
95+
}
96+
```
7897
### 防抖和节流
7998
8099
```js

0 commit comments

Comments
 (0)