持续创作,加速成长!这是我参与「日新计划 6 月更文挑战」的第一天,点击查看活动详情
关于这期分享内容
性能优化一直是前端领域老生常谈的问题,系统的性能以及稳定性很大程度上决定着产品的用户体验以及产品所能达到的高度。而tob和toc系统又有着不同的业务场景,性能优化也有着不用的着力点。结合自己针对一个tob系统的性能优化实践去剖析一些大家可能共同关注的点,争取可以以小见大。
关于团队定位
我们输出的产品主要包括编辑器
和渲染器
两部分。
-
编辑器
除了提供基础的课件编辑制作能力外,还提供了组装各类教育资源的能力,这些教育资源包括互动题、cocos、pdf、ppt等。 -
渲染器
除了提供通用渲染器来支持基础课件的渲染缓存是什么意思以外,还支持接入各类教育资源的渲染器,来支持教育资源的渲染。
关于数据结腾讯会议app下载构,大致数据结构如下所示,类似ppt的数据结构,每一页单页课件是一个page,每页课件上中的文字图片音频视频都是一个节点,这些课件页以及节点都是以数组的形式来维护。
{
pages:[
data:{
nodes:[
'text','image','video','staticQuestion'...
]
}
]
}
简单了解业务之后我们才能结容器是什么合缓存视频变成本地视频具体的场景讨论性能优化过程中遇到的问题。容器对桌面的压强怎么算
性能优化历程
-
3-4 双月立项
我们的项目规划一般按照双月来制定目标,34双容器是什么月我们成立课件性能优化专项,双月目标是明显提升用户体验。
-
针对不同问题的解决方案
下面我会从遇到的具体case入手,来聊一聊我们是如何解决这些问题的。
-
课件列表页卡顿
-
- 原因分析容器云
我们课件系统的数据依然采用了序列化数据存储(未分页),而我们打开编辑器时,会发请求拿到课前端是什么工作件的所有内容,课件内容也会一股脑儿渲染在页面上,这样带来的结果就是页面的性能非常受课件体量的制约,随着课件内容越来越多,课件页面达到100页以上时,系统的性能就已经到达了瓶颈,具体表现为点击切换课件页卡顿以及列数据结构c语言版严蔚敏第二版答案表页滚动卡顿。
我们在列表的vue组件的updated 生命周期中添加了一个前端和后端 log 查看组件渲染次数:
updated(){
//查看该组件更新了多少次,勿删
console.log("%cleftviewerrerender",'color:red;');
},
Vue 的 updated官网这样解释道:
由于数据更改导致的虚拟 DOM 重新渲染和打补丁容器英文。当这个钩容器所能容纳什么叫做容器的容积子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。然容器技术而在大多数情况下,你应该避免在此期间更改状态。如果要相应状态改变,通常最好使用计算属性或watcher取而代之。
cn.vuejs.org/v2/a腾讯漫画pi/#upd…
于是我们发现点击整个单个课件页时,整个左侧列表都重新渲染,而每个课件页中的log也会执行,而且会渲染三次。
我们初步判断当点击单页时,组件执行了多余的render数据结构实验报告,而在重新渲染之前虚拟dom的计算阻塞了单线程,导致ui假死。虽然Vue内部对虚拟dom的计算做了很多优化,但是在这个案例中缓存文件夹名称我们看到,课容器设计件体量大时,单线程依然会阻塞,我们通过performance可以进一步证明我们的猜想。
通过 perfermance可腾讯地图以看到,一次点击事件的处理时间达到4.16s,这一前端学什么桢的时间是4500ms,在这四秒多时间内,浏览器是没有任何响应的,而通过观察我们缓存视频在手机哪里找发现这段时间耗时的操作就是Vu腾讯地图e的虚拟dom计算过程,在Bottom-up中也可以看到,耗时操作vue removeSub移除依赖的操作,还有虚拟do缓存清理m pat腾讯ch node 的计算,这个过程是为了合并更新,这个计算堆积起来就非常耗时。
排查到这里我将原因归结为组件太多,不必要的更新太多,我们去数据结构c语言版严蔚敏第二版答案查看了一下页面节点数量
200页的课缓存件全部渲染,页面节点已经到达了3w之多腾讯会议,而每次交互缓存更新量巨大,浏览器重绘的压力也比较大(虽然这个时间比js计算还是少很多)。
经过以上的排查,我们总结原因为:数据结构实验报告Vue的数据侦听应该更新变化了的dom,但是我们点击某个课件页时,由于处理Vuex的数据流的方式不太合理,使得许多组件依赖了本不需要的数据,导致Vue判断组件需要重新渲染。其次我们的页面结腾讯构过于复杂,没有做动态渲染或者feed流类似的分片加载策略,浪费了很多资源。
- 解决方案
基于以上的原因分析,我们尝试了比较多的方案。其中由于Vuex数据流不合理带来的过多rer前端开发ende腾讯先锋r,由于项目过于复杂,涉及到了互动题编辑器和模版编辑器,数据流的改动风险较缓存大数据结构,而且收益不一定明显。于是在3-4月腾讯客服的优化中,我们没有动现有的状态管理,而是在当前基础上容器中有某种酒精含量的酒精溶液,力求减少每次操作的计算量和渲染量,也就是在不合理的方案下去缓解用户体验问题。前端是什么工作我们的思路聚焦在:课件列表需要动态数据结构教程第5版李春葆答案加载,页面节点越少,挂载的组件越简单,Vue的计前端和后端哪个工资高算越快,浏览器渲染的速度也越快腾讯视频下载。 于是我们做了以下尝试:
-
IntersectionObserver
借鉴图片懒加载的方式,我们通过浏览器的IntersectionObserver进行 dom 的监腾讯会议听实现课件页的懒加载
//在需要懒加载的节点上添加ref属性为“containerNeedLazyLoad”
//将控制是否进行加载的boolean变量命名为“elementNeedLoad”
exportdefault{
data(){
return{
elementNeedLoad:false,
elementNeedVisible:false
};
},
mounted(){
consttarget=this.$refs.containerNeedLazyLoad;
constintersectionObserver=this.lazyLoadObserver(target);
intersectionObserver.observe(target);
},
methods:{
lazyLoadObserver(target){
returnnewIntersectionObserver((entries)=>{
entries.forEach((entry)=>{
if(entry.intersectionRatio>0){
if(this.elementNeedLoad===false){
this.elementNeedLoad=true;
this.$nextTick(()=>{
this.elementNeedVisible=true;
});
this.lazyLoadObserver().unobserve(target);
}
}else{
this.elementNeedLoad=false;
this.elementNeedVisible=false;
}
});
},{
threshold:[0,0.5,1],
});
},
}
};
简言之就是我们给200页课件每一页的dom容器元素都添加了一个监听器,当该课件进入数据结构知识点总结视窗时渲染内部的元素,在课件出视窗时注销元素,通过v-if指令来实现。该方案实现后,实时渲染的课件数量只有视窗内的7-8个,其他课件只渲染了容器组件,页面节点也少了很多,当我们点击切换课件时变得流畅了许多,那是因为未完全数据结构c语言版渲染的课件页Vue组件都变得「简单」了,vue的计算也会更快,点击课件的时间可以在300ms内响应。极大优化了体验,于是我们满心欢喜上线了该优化。
-
动态加载
一天后,又有教研老师反馈页面卡顿,列表页经常滚动比之前更卡,于是我们再次排查。上面的方案留下了一个比较大的问题腾讯漫画是,列表页在滚动的过程当中,需要实时监听dom,我们不怀疑浏览器api的性能,但是v-if指令会在值变化时,执行虚拟dom的计算并更新组件,而这个过程是在滚动的过程中实时进行的。
从腾讯视频下载上面的视频中我们可以腾讯视频下载看到在滚动时很容易出现掉帧的情况,所以图片懒加载的方案无法直接嫁接,我们需要更好的懒加载方案。
在竞品调研数据结构与算法过程中,我们比较关注google doc和腾讯文档的方案。
Google doc采用了滚动懒加载的方式加载文档,但是由于google的底层方案都是基于svg,方案无法复刻,但是动态加载的方式可以借鉴。
腾讯文容器是什么档则是采用了分页加载,首屏渲染之后再加载其他的内前端开发需要学什么容,但是由于我们在 url 上携带了课件 id需要定位到具体的课件页,而且数据方面没有分页,因此该方案暂时不考虑。此外我们页关注了某教研云的课件加载方案:
与预想中的一样,某教研云也采用了动态加载的方式,可见这也算是长列表比较常见的优化手段。
于是我们有了以下优化思缓存视频合并路:
- 默认每张课件不渲染任何节点,只渲染容器节点,dom结构会简单很多
- 监听列表容器的滚动事件缓存是什么意思,计算当前视图最中间的课件index,同时将上下数据结构严蔚敏各7张课件作为可渲染课件,添加滚前端动监听的防抖
- 添加可渲染课件index时通过 setTimeout 逐一添加实腾讯视频下载现流式加载
- 已经渲染的页面在下一次滚动中不再重复渲染
import{on,off,mapState,mapMutations}from'utils';
importdebouncefrom'lodash/debounce';
/**
*总共加载当前课件的ppt上下若干页课件,其他的通过滚动延迟加载
*/
constrenderPagesBuffer=7;
constrenderPagesBoundary=2*renderPagesBuffer+1;
constdebounceTime=400;
constprogressiveTime=150;//渐进式渲染间隔时间
/**
*持久化一下
*/
constbodyHeight=document.documentElement.clientHeight||document.body.clientHeight;
exportdefault{
data(){
return{
additionalPages:[],
commonPages:[]//前后两次滚动所需要渲染的公共页面
};
},
mounted(){
this.observeTarget=this.$refs.pprOverviewList;
on(this.observeTarget,'scroll',()=>{
this.handleClearTimer();
this.handleListScroll();
});
if(!this.renderAllPages){
this.updateCurrentPagesInView(newArray(renderPagesBoundary).fill(1).map((_,i)=>i));
}else{
/*先手动触发一次*/
consttimer=setTimeout(()=>{
this.handleClearTimer();
this.handleListScroll();
clearTimeout(timer);
},debounceTime*2);
}
},
beforeDestroy(){
off(this.observeTarget,'scroll',this.handleListScroll);
},
computed:{
...mapState('editor',['currentPagesInView']),
pagesLength(){
returnthis.pptDetail?.pages?.length||0;
},
renderAllPages(){
returnthis.pagesLength>renderPagesBoundary;
}
},
watch:{
additionalPages(val){
this.observerIndex=1;
this.handleRenderNextPage();
}
},
methods:{
...mapMutations('editor',['updateCurrentPagesInView']),
/**
*增加滚动事件的防抖设置,防止频繁更新
*/
handleListScroll:debounce(function(){
const{scrollTop,scrollHeight}=this.observeTarget;
constpercent=(scrollTop+bodyHeight/2)/scrollHeight;
//找到当前滚动位置位于页面中心的ppt
constcurrentMiddlePage=Math.floor(this.pagesLength*percent);
conststart=Math.max(currentMiddlePage-renderPagesBuffer,0);
constend=Math.min(currentMiddlePage+renderPagesBuffer,this.pagesLength+1);
//已经渲染了的页面集合(保证不重复渲染)
constcommonPages=[];
//滑动之后需要新渲染的页面集合
constadditionalPages=[];
for(leti=start;i<end;i++){
if(this.currentPagesInView.includes(i)){
commonPages.push(i);
}else{
additionalPages.push(i);
}
}
this.commonPages=commonPages;
this.additionalPages=additionalPages;
},debounceTime),
handleRenderNextPage(){
constnextPages=this.additionalPages.slice(0,this.observerIndex);
this.updateCurrentPagesInView(
[...nextPages,...this.commonPages]
);
this.observerIndex++;
if(this.observerIndex>=this.additionalPages.length){
this.handleClearTimer();
}else{
this.observerTimer=setTimeout(()=>{
this.animationTimer=requestAnimationFrame(this.handleRenderNextPage);
},progressiveTime);
}
},
handleClearTimer(){
this.observerTimer&&clearTimeout(this.observerTimer);
this.animationTimer&&cancelAnimationFrame(this.animationTimer);
}
}
};
其中需要注意的时,滚动的缓存视频合并监听添加了400ms的防抖,我们一直滚动会感受到非常流畅数据结构教程第5版李春葆答案,而在停止滚动开始渲染时,如果同时渲染计算得来的共15张课件,则在这些组件渲染完成之前页容器所能容纳什么叫做容器的容积面依然是卡死的状态,因此我们采用了setTimeout实现渐进式渲染,宏任务的好处就是让我们可以在每一次事件循环中插入微任务,比如当前课件正在进行流式渲染,这时点击了某张课件可以先切换再继数据结构教程第5版李春葆答案续渲染。
this.observerTimer=setTimeout(()=>{
this.animationTimer=requestAnimationFrame(this.handleRenderNextPage);
},progressiveTime);
另外当再次触发滚动事件时,需要清除此前所有的定时器重新计算,而已经渲染了的页面index我们存在数据结构实验报告 commonPages 数据中,在下一次计算时不进行缓存清除。最终优化的效果如下:
可以看到从用户体验上,已经解决了滚动的卡顿问题,同时也不会因为组件过多阻塞用户的点击事件。
通过性能监控也能看到在腾讯漫画滚动过程中几乎没有任何的计算,这也是滚动起来十分流畅的原因。
-
虚拟列表
我们前面也说到,这是在现有数据流不合理的情况下的无奈之举,而要彻底解决需要更完美的方案。由于Vue框架没有提供React的memo或者shouldCompon容器技术entUpdate类似的钩子函数,让开发者决定组件是否应该重新渲染,因此在更彻底的解决方案中,由组内另一数据结构位同学主导设计,我们腾讯会议app下载尝试用react虚拟列表进行重构。
长列表的终极优化方案还是要走向虚拟列表,无论是百度数据结构c语言版严蔚敏第二版答案贴吧中期的技术重数据结构与算法构,还是今日头条缓存的视频在哪的feed流,都曾基于该方案做过探索。
虚拟列表是一种根据滚动容器元素的可视区域来渲染长列表数据中某一个部分数据的技术,具体在实现的时候,需要一个用于滚动监听的虚拟容器,和一个用作元素渲染的真实容器。
虚拟列表通过计算滚动视窗,每次只渲染部分元素,既减少了首屏压力,长时间加载也不会有更多的性能负担。虚拟列表中的元素都是绝对定位的,无论列表有多长,实前端际渲染的元素始终保持在可控范围内。
前端领域各个社区也有了比较成熟的解决方案,react-virtualized、react-w缓存是什么意思indow 以及 vue-virtualize-list 等容器中有某种酒精含量的酒精溶液等,关于虚拟列表原理的叙述可以参考以下文章,这里限于篇幅不再赘述:
github.com/dwqs/blog/i…
import{SortableContainer}from'react-sortable-hoc';
import{areEqual,VariableSizeListasList}from'react-window';
importReact,{
useCallback,
useEffect,
useMemo,
useRef,
useState,
}from'react';
exportconstReactVirtualList:React.FC<any>=(props)=>{
constlist=useRef(null);
constinitialScrolled=useRef(false);
const[dragging,setDragging]=useState(-1);
const{pages,currentPageId,selectedPagesId}=props;
constpageGroups=buildPageGroups(pages);
useEffect(()=>{
props.onReady({scrollToTargetPage});
},[]);
useEffect(()=>{
list.current.resetAfterIndex(0);
},[pages]);
useEffect(()=>{
//进入页面定位到选择页面
if(!initialScrolled.current&&pages.length>0){
scrollToCurrentPage();
initialScrolled.current=true;
}
},[pages]);
constscrollToCurrentPage=()=>{
scrollToTargetPage(currentPageId,pages);
};
//渲染列表的每一项
return<SomeThing/>
}
同时由于最初我们缩略图的元素渲染器采用了跟用户操作区的同一个渲染组件,每个元素上都有缓存视频变成本地视频很多事件监听,新版本我们也封装了基于 re数据结构c语言版严蔚敏第二版答案act 的纯 ui 元素渲染器 ,去掉数据结构教程第5版李春葆答案了无用的事件监听,简化了dom结构。
两者结合就成了新版本的缩略图列表,目前已经上线完成,从各项指标以及用户体验来看,提升还是非常大的,其中更明显的拖拽排序,相较于此前的拖拽排序用户体验数据结构与算法要好得多。
-
内存泄漏
同样是上述课件,经过上述的优化,页面的节点数量已经少了很多,而且卡顿问题也得到了改善。但是当我们不断滚动左侧预览图时,一段时间之后还是会卡顿甚至卡死。此时要么是 cpu占用率过高缓存的视频在哪导致页面无法响应,要么出现了内存泄漏问题,经过排查发现,虽然页面中的节点缓存是什么意思在懒缓存是什么意思加载过程中会注销,但前端和后端的区别是这些节点依然会被保存在内存当中,一直没有释放,甚至达到10w之多,内存占用也一直线性增长,我们需要针对这一些内存中的节点进容器技术行优化,处理内存泄漏问题。
我们通过内存腾讯会议app下载快照可以看到,标记为 Detached 也就是脱离文档流的节点依然存储在内存当中,其中 DIVElement 有两万多个,展开发现都前端和后端哪个工资高是 element-rend腾讯nbaer (我们课件元素渲染器)中的元素,比如带有 render-wrap 或者 selection-zone 的这些类(都是我们项目中挂载在dom上的类名)。所以判断这个组件存在内存泄露问题。
排查的过程中,一度怀疑是vue 的 v-if 造成的,在数据结构c语言版严蔚敏第二版答案 vue的官网有关于 v-if 内存泄漏的相关内容。
cn.vuejs.org/v2/cookbook…
我尝试了通过手动挂载组件执行$mount,在课件滑到视图之外时手动注销,依然没有作用,甚至尝试实现自定义指令 v-clean-node,在懒加载过程中动态注前端面试题销节点。但事实证明,节点是已经前端和后端哪个工资高从dom结构中注销了的,只是对应的dom片段依然保存在内存当中,而且笔者也没有找到可以手动释放内存的方法,至少在浏览器环境还没有办腾讯会议app下载法办到。这里走了很多弯路,而思考的缓存是什么意思方向或许也能成为一些反面案例,不过性能优化这条路需要容器的容积一定比它的体积小做的也是勇敢尝试缓存的视频在哪,敢于试错,最终总能找到一个相对较优容器所能容纳什么叫做容器的容积的解缓存文件夹名称决办法。
我们换个思考的方向,既然不能手动释放内存,就去迎合v8内存管理的原理,代码怎样写才能保证内存被正常释放。我们想到的就是一直被引用容器苗的变量,其次就是未被注销的事件监听。腾讯会议
接下来的排查采用了打容器中有某种酒精含量的酒精溶液断点和注释代码的笨办法,逐渐缩小排查范围,最终锁定在了渲染富文本所使用的 rende前端开发需要学什么r 组件。
这个组件用到了兄弟团队缓存视频合并提供的render 库,而在 node_modules 是编译后的 es5代码,可读性还不错,于是我通过断点在在源码中进行调试,手动追踪调用栈。
在最终渲染的方法中,排查找到了这样一行代码:
这里每个富文本渲染都会添加一个body resize的事件监听,而笔者并没有找到相关unobserve的逻辑。类似这前端和后端的区别种事件监听的代码如果没有取消监听,很容易造成内存泄漏,注释这一行代码之后重启项目,系统的内存可以正常回收了,最终确定是由这个sdk导致了内存泄漏,后续兄数据结构c语言版第二版课后答案弟团队的同学也协助解决了这一问题,重新发了一个正式包。
通过测试发现内存已经可以正常释放。
这里的内存泄漏可以用以下示意图概括:
未被释放的事件监听会导致对应的组件在卸载时并未被释放,因此我们的内存容器对桌面的压强怎么算中会有这些 v前端面试题ue组件。
日常项目开发的过程中,内存优化需要持续关注,我们课件编辑器的内存占用大概100M左右,需要在后续的开发过程中持续优化
-
点击预览按钮之后页面操作卡顿
这是一个非常有趣的案例,课件的预览是在当前页面打开一个弹窗嵌入预览页面的iframe,每次前端开发点击预览之后回到编辑器总会出腾讯nba现或多或少的卡顿现象。
这是容器苗因为预览页和编辑器数据结构实验报告的域名相同,因此打开iframe时共享了同一进程。
iframe 作为升级版的 frame,一般来说都会被认为和上层的 parent 容器处在同一个进程中,他们会拥有父容器的一个子上容器对桌面的压强怎么算下文 BrowserContext。在这种情况下,iframe 当中的 js 运行时便会阻塞外部的 js 运行,特别是当如果数据结构 iframe 中的代码质量不高而导致性能问题时,外层运行的容器会受到相当大的影响。这显然是我们不愿意看到的,因为 webview 中的内容仅仅会作为 IDE 拓展机制的一部分,我们不希望看到我们的外部 UI 和程序被 iframe 阻塞从而导致性能表现不佳。
iframe 线程
幸运的是,Chrom 在 67 版本之容器中有某种酒精含量的酒精溶液后默认开启了 Site Isolation。基于它的描述,如果 iframe 中的域名和当前父域名不同(也就是大家熟悉的跨域情况),那么这个 iframe 中的渲染内容就会被放在两个不同的渲染进程中。而我们只需要将 IDE 主应用的页面挂在域名A下,而同时前端和后端的区别将 iframe 的的页面挂在域名B下,那么这个 iframe 的进程就和主进程分开了。在这种模型下,iframe 和主进程仅仅能通过 postMessage 和 message 事件进行数据通讯。但是在上面的模型中,仍然有容器云一点需要注意。基于 Site Isolation 的特性,同一个页面中如果有多个,拥有同一个域名的多个 iframe腾讯nba 之间是共享进程的,因此他们仍然会互相影响性能。如果某个业务场景需要一个更为独立的 iframe 进程,它必须和其他 iframe 拥有不同的域名。
我们在项目中分别嵌入了百度首页和我们课件渲染页面,发现一级域名相同时iframe和当前页面总是会共缓存视频合并app享进程id,无前端学什么论嵌入页面的性能如何,对当前页面都会有或多或少的影响。因此我们有了以下解缓存的视频在哪决方案:
- 预览页面部署到新的域名上,两者不共享进程
- 通过a标签打开新的页面进行预览,需要注意的是a标签需要加上 rel=”noopener”属性,切断进程联系
<a
v-if="showOpenEntry"
class="intro-step3preview-wrap"
rel="noopener"
target="__blank"
:disabled="!showEditArea"
:style="{color:!showEditArea?'lightgray':'#515a6e'}"
:href="https://juejin.im/post/7104659840429260814/pageShareUrl"
>
<lego-icontype="preview"size="16"/>
</a>
目前的优化中采用了第二种跳转的方式作为临时方案。
-
添加动画时间过长
有这样一个场景是老师需要给多个元素同时添加动画,但是页面需要几秒钟响应,对腾讯视频下载于用户来说就是出现了卡顿。
我们依然通过performan腾讯地图ce排查,确定了动画表单的渲染阻塞了ui的更新,同样涉及到长列表,但此处没有课件列表复杂,而且课件页都渲染了一个固定高度的容器,此处每个动画表单高度都不一样,因此我们采用另一种懒加载的渲染方式:
exportdefault{
data(){
return{
//当前渲染动画数量
nextRenderQuantity:0
};
},
computed:{
animationLength(){
returnthis.animationConfigsUnderActiveTab.length;
},
renderAnimationList(){
//从原数组中切割
returnthis.animationConfigsUnderActiveTab.slice(0,this.nextRenderQuantity);
}
},
watch:{
animationLength:{
handler(){
this.handleRenderProgressive();
},
immediate:true,
}
},
beforeDestroy(){
this.timer&&cancelAnimationFrame(this.timer);
},
methods:{
/**
*动画表单的渐进式渲染,每一帧多渲染一个
*/
handleRenderProgressive(){
this.timer&&cancelAnimationFrame(this.timer);
if(this.nextRenderQuantity<this.animationConfigsUnderActiveTab.length){
this.nextRenderQuantity+=1;
this.timer=requestAnimationFrame(this.handleRenderProgressive);
}
},
}
};
通过 requestAnimationFrame 每一帧添加一个动画,也就是40个元素同时添加动画,需要40x腾讯会议16 = 640ms渲染完表单,而在这个时间之前,页面已经及时作出了响应,老师在使用的时候就不会觉得卡顿了。
优化前后的响应时间从2.35s => 370ms,数据结构与算法优化效果比较显著。
总结:不要让你的js逻辑阻塞了ui的渲染。
-
其他的优化
其他的一些常缓存的视频在哪见的项目优化就不在此赘述了,无论什么样的业务场景,tob还是toc系统,腾讯课堂大家可能都曾在以下优化方向上摸爬滚打过。
- 路由懒加载
- 静态缓存清理资源缓存
- 打包体积优化
- 较大第三方库借助cdn
- 编码优化容器技术:长数组遍历善用 for 循环等
- 可能的预编译优化
深腾讯会议入框架,寻找性能优化方向
Vue 的懒人哲学缓存文件夹名称 vs React 暴力美学
在性能优化的路上越走越偏,我也深深感受到前端工具带来的便利和过分依赖前端框架所带来的所谓的 side ef缓存清理fect。vue 和 react缓存文件夹名称 作为如今最火的两个框架,各自有着其独特的魅力,而性能优化的同时我们始终绕不开框架的底层原理。
Vue 的懒人哲学:
曾经的一次分享中我们提到了vue所谓的懒人哲学 ,也就是说 vue 框前端开发架内部为你做了太多优化工作。
我们知道Vue会对组件中需要追踪的状态,将其转化为getter和setter进行数据代理,构建视图和数前端据层的依赖,这就是ViewModel 这一层。而正是由于vue容器的容积一定比它的体积小精确到原子级别的数据侦听使得其对数据十分敏感,任何数据的改变,vue都能知道这个数据所绑定的视图,在下一次dom缓存视频在手机哪里找 diff时,他能精确知道哪容器中有某种酒精含量的酒精溶液些dom腾讯客服该渲染,哪些保持不动。而vue的这个原理也是他升级Vue3时进行更高效的预编译优化的前提条件,感兴趣的同学可前端是什么工作以跟容器所能容纳什么叫做容器的容积我探讨下曾经的分享,这其中也聊到了 Vue。
zhuanlan.zhihu.com/p/158880026
但是最大问题缓存在于,vue 更新视图恰好不多不少的前提是,你的数据流十分干净,没有多余的数据更新,否则「敏感」的vue会以为更多的组件需要重新渲染,这也是目前我们课件编辑器的数据结构c语言版问题所在,项目体量越来越大,几乎没有几个开腾讯体育发者可以保证自己所维护的状态管理干净透明,而一旦有不合理的数据更新,组数据结构件的重新渲染是无法从中拦截的,因此用 Vue 可以让你「懒」一点,也需要你写代码时「小心」一点。而缓存是什么意思对比react,两腾讯nba者底层设计的不同导致在遇到此类问题时,我们可能需要不一样的思考方向。
现在数据结构严蔚敏在知乎上还能翻到一些尤大关于 react 性能问题的理解。
尤大说的比较通俗易懂,而且也确实直指r前端是什么工作eact框架的要害,其中所提到的 react 把大量优化责任丢给开发者,相信大家都有所感受。
React 的暴力美学:
与Vue不同的是,React对数据天然不敏感,框架不关心你更新了多少数据,乃至更新了多少脏数据,数据与dom结构也没有vue那种依赖关系,数据结构c语言版第二版课后答案你只需要通过 setState 告诉我我应该渲染哪些组件即可。与 Vue 相比,react的处理方式既前端和后端优雅又暴力,而且从开发者的角度来审视,react的这种设计真的减少了太多的心理负担,而作为初接入reac数据结构有哪些t数据结构严蔚敏的开发腾讯会议者来说,你不会因为多更新了数据导缓存文件夹名称致过容器技术多的rerender 而抓耳挠腮,你要做的就是借助框架本身去消除这些副作前端开发需要掌握什么技术用,众所周知react正好提供了这些能力:
- R腾讯地图eat.memo
- PureComponent
- shouldComponentUpdate
你需要的,都能借助框架或者第三方工具做到。
谈到这里,不妨具体说说框架如何避免不必要的渲染问题:
以 react context 为例,如果我们在项目中所有的状态管理都放在一个 context 中,那么在使用时总会引起不必要的渲染。而在开发过程中如何避免,不同开发者都有不同的心得。
constAppContext=React.createContext(null);
constApp=()=>{
const[count,setCount]=useState(0);//经常变的
const[name,setName]=useState('Mike');//不经常变的
return(
<AppContext.Providervalue={{
count,
setCount,
name,
setName
}}>
<ComponentA/>
<ComponentB/>
</AppContext.Provider>
)
}
constComponentA=()=>{
console.log('这是组件A');
constcontext=useContext(Context);
return(
<div>{context.name}</div>
)
}
constComponentB=()=>{
console.log('这是组件B')
constcontext=useContext(Context);
return(
<div>
{context.count}
<button
onClick={()=>{
context.setCount((c)=>c+1)
}}
>
SetCount
</button>
</div>
)
}
在这个 demo中,我们在顶层注入了 Context 做状态管理,同时有一个经常改变的状态count 和一个不经常改变的状态 name,组件A和组件B分别引用了 name 和 count 这两个状态。
我们在点击 SetCount 时,调用了全局上下文 中的方法,同时观测到A B两个组件都会重新渲染,而实际上我们的组件 A只用到了 name 这个状态,是不应该重新渲染的。这里前端和后端的数据流其实非常「干净」,没有多余的引用,如果是 Vue,它会追踪腾讯课堂依赖而避免组件 A 的渲染,react 却没有做到。而作为 react 开发者,如果放任这种不必要的 rerender不管,那正如尤大所说, react 应用的性能确实会遇到瓶颈,好在 react 给了开发者足够的发挥空间,大多开发者缓存视频在手机哪里找遇到此类场景,反手就是一个 context 拆分优雅解决:
constAppContext=React.createContext(null);
constAppContext2=React.createContext(null);
constApp=()=>{
const[count,setCount]=useState(0);
const[name,setName]=useState('Mike');
return(
<AppContext.Providervalue={{
name,
setName
}}>
<AppContext2.Providervalue={{
count,
setCount
}}>
<ComponentA/>
<ComponentB/>
</AppContext2.Provider>
</AppContext.Provider>
)
}
constComponentA=()=>{
console.log('这是组件A');
constcontext=useContext(Context);
return(
<div>{context.name}</div>
)
}
constComponentB=()=>{
console.log('这是组件B')
constcontext=useContext(Context);
return(
<div>
{context.count}
<button
onClick={()=>{
context.setCount((c)=>c+1)
}}
>
SetCount
</button>
</div>
)
}
这里我们将两个状态拆分进不同的 context 中,此时再调用 setCount 方法,就不会影响到组件 A 重新渲染数据结构与算法了。这也是我们实际项目开发中最常见的解决方案。但是项目体量越来越大时,这种模块的拆容器的容积一定比它的体积小分会变得很繁琐,类似腾讯nba Vuex 模块的拆分一样,我们开发一个新功能,也总是不愿意在 vuex 中新开数据结构知识点总结辟一个容器对桌面的压强怎么算模块,写更多的文件以及 getters mutations。所以在这个例子中我们也可以通过 useMemo 来数据结构c语言版第二版课后答案解决:
constComponentA=()=>{
console.log('这是组件A');
constcontext=useContext(Context);
constmemoResult=useMemo(
()=>{
<div>{context.name}</div>
},
[context.name]
)
returnmemoResult;
}
我们在组件 A 中,组件内容用 useMemo 包裹,将其制造为一个缓存对象,这里的前端和后端哪个工资高 useMemo 不去缓存 state,因为我们调用了顶层方法 setCount 引起 state immutable 更新 -> 进而 name 更新(引用地址变化),在顶层组件中缓存 state 其实并没有什么用,所以在这个案例中 us缓存视频在手机哪里找eMemo 只能用来缓存组件。
当然,我们不能每个组件都通过 useMemo 来处理,很多时候只是平添开销。因此 react腾讯 团队所提出的 context selectors 才是解决类似案例的最佳选择:
github.com/reactjs/rfc…
通过 selector 机制,开发腾讯视频下载者在使用 Contex数据结构严蔚敏ts 时,可以数据结构严蔚敏选择需要相应的 Context data,从而规避掉「不关心」的 Context data 变化所触发的渲染。这跟我们手动拆分 context 所实现的功能如出一辙,总的来说优化思路还是比较一致的。
react 更多案例可以参考:
codesandbox.io/s/react-cod…
聊到这里我们发现,当使用框架作为开发工具来解决问题时,如果腾讯地图产生了副作用,react开发者有很多方式可腾讯会议app下载以抵消这个副作用,而相对来说 vue 以及vue生态圈所能提供的解决方案就比较少,正如前面我们遇容器中有某种酒精含量的酒精溶液到的那些bad前端开发 case一样,我们前端开发可能需要从一些比较偏的角度去思考才能解决这类问题。
题外话(个人观点):
个人认为 Vue 做大型项目有着天然的弊端,由于递归实现了精确数据侦听,使得其产生了过多的订阅对象,而正如前面所说,一旦数据流不合理,多余的更新不可逆,而过多的侦听对象对系统内存也是一个考验。令一点就是笔者的自我感受,Vue 项目开发在组件化不如 React 来得清澈透明,单文件组件大了之后,可读性也比较差,而react 有社区加持,有 Redu缓存是什么意思x 和 Saga 进行状态管理,上手曲线虽然略高,但是代码规范度极高,状态管理效果极好,适合团队开发。前端工程师反观 vue, 做小型项目却有着天然优势(为什么?),因此每个项目在前期都要着重分析业务场景,做好腾讯项目规划和技术选型。个人观点,希数据结构知识点总结望各位同腾讯会议app下载学指出不足,理性讨论。
以上就是项目中一些优化实践,不同场景有不同的解决方案,希望大家也可以留言给到一些建议和帮助,