当前位置: 首页 > 科技观察

Vue实现原理+前端性能优化

时间:2023-03-12 22:57:22 科技观察

一、Vue实现原理1、Vue简介当前的大前端时代,是一个纷扰纷争的时代。世界已经分为许多教派,主要以Vue、React和Angular为首。形成前端框架三足鼎立的局面。Vue在前端框架中的地位就像过去的jQuery。由于其简单、开发效率高,已经成为前端工程师的必备技能之一。Vue是一个渐进式JavaScript框架,完美集成了第三方插件和UI组件库。它与jQuery最大的不同在于,Vue无需开发者直接操作DOM节点就可以改变页面渲染内容。基于HTML、CSS、JavaScript,快速上手,开发优雅简洁的应用模块。但是我们一提到Vue,更多的是关注它的用法,而不是去学习它是如何解决前端问题的,这就有些亚健康了。有过前端开发经验的人,肯定在开发过程中遇到过奇怪的问题,然后迷迷糊糊解决了。如果他们再次遇到类似的问题,他们将再次不知所措。作为一名前端工程师,在遇到问题时,能否准确定位问题原因并及时解决,主要取决于我们对前端框架的理解是否足够深入。2.Vue实现原理2.1虚拟DOM(VirtualDOM)随着时代的发展,web应用的页面交互效果越来越复杂,页面功能越来越丰富,需要的状态也越来越多需要维护,DOM操作也越来越复杂。更频繁。DOM操作虽然简单易用,但是会带来维护性差的问题。在程序执行过程中,Watcher会在初始化时将各个节点和状态一一关联映射。setter检测到Data的状态发生变化后,会通知Watcher,Watcher会将这些变化通知给之前记录的DOM。以及与这些状态相关的节点,从而触发页面的渲染过程。组件接收到状态变化后,会通过编译将模板转换为渲染函数Render,并执行渲染函数得到一棵虚拟DOM树。通过比较旧的虚拟DOM和新生成的虚拟DOM树,更新对应的实际DOM节点。,执行页面渲染。几乎所有的主流前端框架都使用虚拟DOM,但是在使用虚拟DOM时,无论是Angular还是React都无法确定哪个状态发生了变化。因此,需要对旧的虚拟DOM和新的虚拟DOM进行一次暴力比较。但是,Vue从1.0版本开始通过细粒度绑定来更新视图。也就是说,当状态发生变化时,Vue可以知道哪些状态需要变化,哪些节点需要变化,从而更新节点。但是,这种细粒度的变化检测有一定的内存开销影响性能,而且项目越复杂,开销越大。Vue2.0版本之后,为了优化性能,引入了虚拟DOM,选择了一个折中方案。它既不需要暴力比较整个新旧虚拟DOM,也不需要通过细粒度绑定来更新视图,即以Components为单位进行Watcher监控,也就是说,即使多个组件中的节点使用某种状态,只需要一个Watcher来监听这种状态的变化。当状态改变时,Watcher通知组件,组件内部通过虚拟DOM来比较和重新渲染节点。2.2常用指令的实现原理指令是指Vue提供的带有“v-”前缀的特性。当指令中表达式的内容发生变化时,会影响DOM内容的变化。Vue.directive全局API可以创建自定义指令和获取全局指令。除了自定义指令,Vue还有一些开发过程中常用的内置指令,比如v-if、v-for等,在解析Vue模板时,指令会被解析成AST,而函数该指令的实现将在使用AST生成字符串的过程中实现。在解析模板时,节点上的指令将被解析并添加到AST的directives属性中。这些指令会将数据发送到VNode。当虚拟DOM渲染页面时,会触发一些钩子函数。当钩子函数被触发后,就表示命令生效了。2.2.1v-if指令原理在应用中使用v-if指令:createif

createelse
在编译阶段生成:(create)?_c('div',[_v("createif")]):_c('div',[_v("createelse")])会根据create时的值选择创建哪个节点代码被执行。2.2.2v-for指令原理在应用中使用v-for指令:{{item}}在编译阶段生成:_l((list),function(item,index){return_c('li',[_v(_s(item))])})_l是renderList的别名,代码执行时,_l函数会循环list变量,调用第二个参数传递的函数传递两个参数:item和index。当调用_c函数时,会执行_v函数创建一个节点。2.2.3自定义指令原理在应用程序中,指令的处理逻辑分别监控创建函数、更新函数和销毁函数。具体实现如下:exportdefault{create:updateDirectives,update:updateDirectives,destory:functionunbindDirectives(vnode){updateDirectives(vnode,emptyNode)}}hook函数触发后,会执行updateDirectives函数,代码如下如下:functionupdateDirectives(oldVnode,vnode){if(oldVnode.data.directives||vnode.data.directives){_update(oldVnode,vnode)}}在这个函数中,不管是否有old虚拟节点,只要有指令,_update函数将被执行。_update函数代码如下:,vnode.context)constdirsWithInsert=[]constdirsWithPostpatch=[]letkey,oldDir,dirfor(keyinnewDirs){oldDir=oldDirs[key]dir=newDirs[key]if(!oldDir){//新命令触发bindcallHook(dir,'bind',vnode,oldVnode)if(dir.def&&dir.def.inserted){dirsWithInsert.push(dir)}}else{//命令已经存在triggerupdatedir.oldValue=oldDir.valuecallHook(dir,'update',vnode,oldVnode)if(dir.def&&dir.def.componentUpdated){dirsWithPostpatch.push(dir)}}}if(dirsWithInsert.length){constcallInsert=()=>{for(leti=0;i{for(leti=0;i{{item}}编译后的内容如下图所示,节点以createElement的语法糖的形式创建。createElement{"ul",{attr:{id:"myId"}},[createElement("li",1),createElement("li",2),createElement("li",3)]}渲染函数后执行生成一棵虚拟DOM树,其总体结构如下:最后,将虚拟DOM树转化为真实的DOM。虚拟DOM的DOM开销对性能的优化主要体现在当节点发生变化时,可以使用diff算法比较变化前后虚拟DOM结构的变化,通过修改节点进行必要的调整属性而不是盲目破坏旧节点创建新节点。这个过程的主要步骤是:用js对象结构来表示DOM树的结构,然后用这棵树构造出真正的DOM树插入到文档中;当状态发生变化时,重建一棵新树并将其与旧树进行比较。记录差异;将记录的差异应用于构建的真实DOM树。需要注意的是,differ算法遵循对等比较的原则,在使用过程中应尽量减少跨层DOM调整。