返回顶部
首页 > 资讯 > 前端开发 > html >如何理解Vue实现原理与前端性能优化
  • 683
分享到

如何理解Vue实现原理与前端性能优化

2024-04-02 19:04:59 683人浏览 独家记忆
摘要

这篇文章主要介绍“如何理解Vue实现原理与前端性能优化”,在日常操作中,相信很多人在如何理解Vue实现原理与前端性能优化问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”如何理解

这篇文章主要介绍“如何理解Vue实现原理与前端性能优化”,在日常操作中,相信很多人在如何理解Vue实现原理与前端性能优化问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”如何理解Vue实现原理与前端性能优化”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

 一、Vue实现原理

1、Vue简介

现在的大前端时代,是一个动荡纷争的时代,江湖中已经分成了很多门派,主要以Vue,React还有angular为首,形成前端框架三足鼎立的局势。Vue在前端框架中的地位就像曾经的Jquery,由于其简单易懂、开发效率高,已经成为了前端工程师必不可少的技能之一。

Vue是一种渐进式javascript框架,完美融合了第三方插件和UI组件库,它和jQuery最大的区别在于,Vue无需开发人员直接操作DOM节点,就可以改变页面渲染内容,在应用开发者具有一定的htmlCSS、JavaScript的基础上,能够快速上手,开发出优雅、简洁的应用程序模块。

如何理解Vue实现原理与前端性能优化

但是我们提及Vue的时候,更多的是关注它的用法,而不是学习它是如何解决前端问题的,这多少有点亚健康。有前端开发经验的人,一定在开发过程中遇到过奇奇怪怪的问题,然后稀里糊涂地解决,倘若再次遇到相似的问题,便再次手足无措,作为一名前端工程师,在遇到问题的时候我们是否能准确定位产生问题的原因并及时解决,主要取决于我们对前端框架的理解是否足够深入。

2、Vue实现原理

2.1 虚拟DOM(Virtual DOM)

随着时代的发展,WEB应用的页面交互效果越来越复杂,页面功能越来越丰富,需要维护的状态越来越多,DOM操作也越来越频繁。DOM操作虽然简单易用,但是会产生不好维护的问题。

如何理解Vue实现原理与前端性能优化

在程序执行的过程中,Watcher初始化时会将每一个节点和状态进行一一关联和映射,setter监听到Data的状态发生改变后,就会通知Watcher,Watcher会将这些变化通知曾经记录过的DOM以及跟这些状态相关的节点,从而触发页面的渲染过程。组件接收到状态变化后,会通过编译将模板转换成渲染函数Render,执行渲染函数就会得到一个虚拟DOM树,通过对比旧的虚拟DOM和新生成的虚拟DOM树,来更新对应的实际DOM节点,执行页面渲染。

如何理解Vue实现原理与前端性能优化

主流前端框架几乎都在使用虚拟DOM,但是在使用虚拟DOM的时候,Angular和React都无法确定具体是哪个状态发生了变化,因此需要在旧的虚拟DOM和新的虚拟DOM之间进行暴力对比,但Vue从1.0版本开始,就通过细粒度的绑定来更新视图,也就是说,当状态发生变化的时候Vue可以知道具体是哪个状态哪些节点需要发生改变,从而对这个节点执行更新,然而这种细粒度的变化侦测会有一些内存开销影响性能,一个项目越复杂,开销就越大。

Vue从2.0版本后,为了优化性能,引入了虚拟DOM,选择了一个折中的方案,既不需要暴力对比整个新旧虚拟DOM,也不需要通过细粒度的绑定来实现视图的更新,即以组件为单位进行Watcher监听,也就是说即便一个组件内有多个节点使用了某个状态,也只需一个Watcher来监听这个状态的变化,当这个状态发生变化时,Watcher通知组件,组件内部通过虚拟DOM的方式去进行节点的对比和重新渲染。

2.2 常用指令实现原理

指令是指Vue提供的以“v-”前缀的特性,当指令中表达式的内容发生变化时,会连带影响DOM内容发生变化。Vue.directive全局api可以创建自定义指令,并获取全局指令,除了自定义指令,Vue还内置了一些开发过程中常用的指令,如v-if、v-for等。在Vue模板解析时,会将指令解析到AST,使用AST生成字符串的过程中实现指令的功能。

如何理解Vue实现原理与前端性能优化

在解析模板时,会将节点上的指令解析出来并添加到AST的directives属性中,directives将数据发送到Vnode中,在虚拟DOM进行页面渲染时,会触发某些钩子函数,当钩子函数被触发后,就说明指令已生效。

2.2.1 v-if指令原理

在应用程序中使用v-if指令:

<div v-if="create">create if</div>  <div v-else>create else</div>

在编译阶段生成:

(create)   ? _c('div',[_v("create if")])   : _c('div',[_v("create else")])

在代码执行时,会根据create的值来选择创建哪个节点。

2.2.2 v-for指令原理

在应用程序中使用v-for指令:

<li v-for="(item,index) in list">{{item}}</li>

在编译阶段生成:

_l((list), function(item, index){   return _c('li',[    _v(_s(item))   ]) })

_l是renderList的别名,执行代码时,_l函数会循环list变量,调用第二个参数中传递的函数,传递两个参数:item和index,当_c函数被调用时,会执行_v函数,创建一个节点。

2.2.3 自定义指令原理

在应用程序中,指令的处理逻辑分别监听了create函数、update函数以及destory函数,具体实现如下:

export default {   create: updateDirectives,   update: updateDirectives,   destory: function unbindDirectives (vnode){    updateDirectives(vnode, emptyNode)   }  }

钩子函数被触发后,会执行updateDirectives函数,代码如下:

function updateDirectives(oldVnode, vnode){   if (oldVnode.data.directives || vnode.data.directives) {    _update(oldVnode, vnode)   }  }

在该函数中,不论是否存在旧虚拟节点,只要其中存在directives,就会执行_update函数,_update函数代码如下:

function _update(oldVnode, vnode) {   const isCreate = oldVnode === emptyNode   const isDestory = vnode === emptyNode   const oldDirs = nORMalizeDirectives(oldVnode.data.directives, oldVnode.context)   const newDirs = normalizeDirectives(vnode.data.directives, vnode.context)   const dirsWithInsert = []   const dirsWithPostpatch = []   let key, oldDir, dir   for (key in newDirs) {    oldDir = oldDirs[key]    dir = newDirs[key]    if (!oldDir) { //新指令触发bind     callHook(dir, 'bind', vnode, oldVnode)     if (dir.def && dir.def.inserted) {      dirsWithInsert.push(dir)     }    } else { //指令已存在触发update     dir.oldValue = oldDir.value     callHook(dir, 'update', vnode, oldVnode)     if (dir.def && dir.def.componentUpdated) {      dirsWithPostpatch.push(dir)     }    }   }   if (dirsWithInsert.length) {    const callInsert = () => {     for (let i = 0; i < dirsWithInsert.length; i++) {      callHook(dirsWithInsert[i], 'inserted', vnode, oldVnode)     }    }    if (isCreate) {     mergeVNodeHook(vnode, 'insert', callInsert)    } else {     callInsert()    }   }   if (dirsWithPostpatch.length) {    mergeVNodeHook(vnode, 'postpatch', () => {     for(let i = 0; i < dirsWithPostpatch.length; i++) {      callHook(dirsWithPostpatch[i], 'componentUpdated', vnode, oldVnode)     }    })   }   if (!isCreate) {    for(key in oldDirs) {     if (!newDirs[key]) {      callHook(oldDirs[key], 'unbind', oldVnode, oldVnode, isDestory)     }    }   }  }

isCreate:判断该虚拟节点是否是一个新建的节点。

isDistory:判断是否删除一个旧虚拟节点。

oldDirs:旧的指令集合,oldVnode中保存的指令。

newDirs:新的指令集合,vnode中保存的指令。

dirsWithInsert:触发inserted指令钩子函数的指令列表。

dirsWithPostpatch:触发componentUpdated钩子函数的指令列表。

通过normalizeDirectives函数将模板中使用的指令从用户注册的自定义指令集合中取出来的结果如下:

{   v-customize: {    def: {inserted: f},    modifiers: {},    name: "customize",    rawName: "v-customize"   }  }

自定义指令的代码为:

Vue.directives('customize', {   inserted: function (el) {    el.customize()   }  })

虚拟DOM在对比和渲染时,会根据不同情景触发不同的钩子函数,当使用虚拟节点创建一个新的实际节点时,会触发create钩子函数,当一个DOM节点插入到父节点时,会触发insert钩子函数。

callHook函数执行钩子函数的方式如下:

function callHook(dir, hook, vnode, oldVnode, isDestory) {   const fn = dir.def && dir.def[hook]   if (fn) {    try {     fn(vnode.elm, dir, vnode, oldVnode, isDestory)    } catch (e) {     handleError(e, vnode.context, `directive ${dir.name} ${hook} hook`)   }   }  }

callHook函数的参数意义分别为:

dir:指令对象。

hook:将要触发的钩子函数名。

vnode:新的虚拟节点。

oldVnode:旧的虚拟节点。

isDestory:判断是否删除一个旧虚拟节点。

虚拟DOM在渲染时会触发的所有钩子函数及其触发机制如下:

如何理解Vue实现原理与前端性能优化

需要注意的是,remove函数是只有一个元素从其父元素中移除时才会触发,如果该元素是被移除元素的子元素,则不会触发remove函数。

二、前端性能优化

前端在一个应用中,主要承担在用户打开一个页面时,发送请求到服务器,接收服务器返回的页面进行渲染并将渲染结果呈现给用户的功能。

如何理解Vue实现原理与前端性能优化

要提高前端性能需要从与用户操作无关的客户端和服务端交互和浏览器解析页面着手,也就是从传输和渲染两方面着手。

1、请求传输

1.1请求维度

基于目前前后端传输广泛使用的Http 1.1协议,可以从压缩请求的大小和减少请求的数量两方面着手进行优化,主要的优化手段如下:

如何理解Vue实现原理与前端性能优化

1.2协议维度

从1.0的短连接到1.1的长连接到HTTP2.0、3.0,做出了很多改变,每次协议的升级对前端性能优化来讲都是一次飞跃。HTTP2.0的新特性如下:

如何理解Vue实现原理与前端性能优化

二进制分帧:HTTP2.0在应用层与传输层之间增加一个二进制分帧层,将所有传输的信息分割为更小的消息和帧,并对它们采用二进制格式的编码,使通信都在一个可以承载任意数量的双向数据流的tcp连接上完成。

压缩头部:使用HPACK算法,规定了在客户端和服务器端会使用并且维护“首部表”来跟踪和存储之前发送的键值对,对于相同的头部,不必再通过请求发送,减少了头部开销。

多路复用:客户端和服务器可以把HTTP消息分解为互不依赖的帧,然后乱序发送,最后再在另一端把它们重新组合起来。

请求优先级:每个流都可以带有一个31bit的优先值:0表示最高优先级;2的31次方-1表示最低优先级。

服务器推送:通过提供push-promise帧来实现真正意义上的浏览器推送,摆脱利用ajax轮询进行伪实时的场景。

2、浏览器渲染

2.1 浏览器单线程解析渲染阻塞

浏览器的主要构成如下:

如何理解Vue实现原理与前端性能优化

它的几个常驻线程如下:

如何理解Vue实现原理与前端性能优化

由于GUI线程和js引擎线程互斥,故衍生了一系列避免渲染过程中发生阻塞的优化方法,如样式文件放头部,脚本文件放在DOM节点最末尾;针对不需要操作DOM的脚本,可以采用动态创建script标签的方式载入;脚本文件加上async或者defer等。

2.2 巨大的DOM开销

在浏览器渲染的过程中,巨大的DOM开销无疑成为了渲染效率是最大瓶颈。通过如下代码可以输出一个空DOM节点,查看它所包含的300余个属性和事件。

let ele = document.createElement("div")  let obj = {}  for (const prop in ele) {    obj[prop]=ele[prop]  }  console.log(obj)

2.2.1重绘与回流

重绘是指当页面展示元素中的一些元素需要更新属性,这些属性只是影响元素的外观、风格,而不会影响布局的,比如background-color。回流是指当页面展示元素中的一部分(或全部)因为元素的规模尺寸、布局、隐藏等改变而需要重新构建。显然,重绘不一定导致回流,回流必然导致重绘。

它们都会带来一定的DOM开销,需要尽力去避免,常见的避免手段有避免触发同步布局事件;对于复杂动画效果,使用绝对定位让其脱离文档流;css3硬件加速(transform、opacity、filters、Will-change)等。

2.2.2虚拟DOM

针对巨大的DOM开销,除了尽力避免重绘和回流,近几年还有一种比较流行的,各大框架比如VUE、react都使用的虚拟DOM的方式。

虚拟DOM是一颗以js对象为基础的树,用对象属性来描述节点,是对DOM的抽象,通过一系列操作将其映射到真实环境。

如何理解Vue实现原理与前端性能优化

用一段代码来模拟展示一下这个过程,首先用户编写模板如下:

<ul id="myId">   <li v-for="item in list">{{item}}</li>  </ul>

编译后的内容如下所示,采用了creatElement语法糖的形式创建节点。

createElement {   "ul",   {    attr:{     id: "myId"    }   },   [    createElement("li", 1),   createElement("li", 2),   createElement("li", 3)   ]  }

经过渲染函数的执行生成虚拟DOM树,其大致结构如下:

如何理解Vue实现原理与前端性能优化

最终将虚拟DOM树转化为真实DOM。

如何理解Vue实现原理与前端性能优化

虚拟DOM对性能的DOM开销的优化主要体现在当节点有变化时,它可以通过differ算法比较变化前后的虚拟DOM结构的变化,通过对节点属性的修改做必要的调整,而不是无脑的销毁旧节点创建新节点。

这个过程的主要步骤是:用js对象结构表示DOM树的结构,然后用这个树构造一个真正的DOM树,插入文档中;当状态变更时,重新构造一棵新树,与旧树进行对比,记录差异;将记录的差异应用到所构建的真正的DOM树上。需要特别注意的是,differ算法遵循同级比较的原则,在使用的过程中要尽量减少跨层级的DOM调整。

到此,关于“如何理解Vue实现原理与前端性能优化”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程网网站,小编会继续努力为大家带来更多实用的文章!

--结束END--

本文标题: 如何理解Vue实现原理与前端性能优化

本文链接: https://lsjlt.com/news/83812.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 如何理解Vue实现原理与前端性能优化
    这篇文章主要介绍“如何理解Vue实现原理与前端性能优化”,在日常操作中,相信很多人在如何理解Vue实现原理与前端性能优化问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”如何理解...
    99+
    2024-04-02
  • 如何理解前端性能优化CRP
    这篇文章主要介绍“如何理解前端性能优化CRP”,在日常操作中,相信很多人在如何理解前端性能优化CRP问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”如何理解前端性能优化CRP”...
    99+
    2024-04-02
  • Android 进阶实现性能优化之OOM与Leakcanary详解原理
    目录Android内存泄漏常见场景以及解决方案资源性对象未关闭注册对象未注销类的静态变量持有大数据单例造成的内存泄漏非静态内部类的静态实例Handler临时性内存泄漏容器中的对象没清...
    99+
    2024-04-02
  • 如何进行Web前端性能优化
    这篇文章给大家介绍如何进行Web前端性能优化 ,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。Yahoo! 的 Exceptional Performance team 在 Web 前端方面作出了卓越的贡献。广为人知的优...
    99+
    2023-06-08
  • 揭秘Vue与Nuxt.js的秘密武器,优化前端性能
    随着 Web 应用程序的复杂性与日俱增,优化前端性能已成为至关重要的任务。Vue 和 Nuxt.js 作为领先的前端框架,提供了强大的工具和技术,可帮助开发者显著提升应用程序的性能。 Vue 的秘密武器 虚拟 DOM: Vue 采用虚拟 ...
    99+
    2024-04-02
  • 如何利用Webpack来优化前端性能
    本篇内容主要讲解“如何利用Webpack来优化前端性能”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“如何利用Webpack来优化前端性能”吧!一、背景随着前端的...
    99+
    2024-04-02
  • 如何提高web前端的性能优化
    如何提高web前端的性能优化,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。前端性能优化是一个不断追求的过程,前面的文章虽然讲解了一些性能优化,但是关于性能优化的路还有很长,很多...
    99+
    2023-06-08
  • React前端框架实现原理的理解
    目录vdomdsl 的编译渲染 vdom组件状态管理react 架构的演变fiber 架构总结vdom react 和 vue 都是基于 vdom 的前端框架,我们先聊下 vdom:...
    99+
    2024-04-02
  • 如何理解并实现索引的原理和优化
    这篇文章主要讲解了“如何理解并实现索引的原理和优化”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何理解并实现索引的原理和优化”吧!Part1为什么Kafk...
    99+
    2024-04-02
  • 前端发开中如何进行性能优化
    小编给大家分享一下前端发开中如何进行性能优化,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!content方面1,减少HTTP请求...
    99+
    2024-04-02
  • MySQL MVCC 原理剖析与性能优化策略
    MySQL 是一种常用的关系型数据库管理系统,被广泛应用于各种应用中。在 MySQL 中,MVCC(Multi-Version Concurrency Control)是一种用于实现并发控制和事务隔离的机制。本文将剖析 MySQL MVCC...
    99+
    2023-10-22
    MySQL 关键词: 性能优化策略 MVCC (Multi-Version Concurrency Control)
  • 怎么理解web渲染引擎与前端优化
    本篇内容介绍了“怎么理解web渲染引擎与前端优化”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!大家知道,大...
    99+
    2024-04-02
  • Vue中前端与后端如何实现交互
    目录Promise的基本使用基本使用多个请求,链式编程Promise的API—实例方法Promise的API—对象方法(直接通过Promise函数名称调用的方...
    99+
    2024-04-02
  • JavaScriptwebpack模块打包器如何优化前端性能
    目录一、webpack的使用背景二、webpack如何优化1. JS代码压缩2.CSS代码压缩3. HTML文件压缩4. 文件大小压缩5. 图片压缩6. Tree Shaking7....
    99+
    2022-11-13
    JavaScript webpack JavaScript webpack优化性能
  • 如何理解Vue前后端数据交互与显示
    目录一、技术概述二、技术详述1. 从接口获取后端数据2. 前端向后端传值的交互3. 显示获取到的数据4. 防止数据联动三、技术问题1. 界面自动刷新2. 获取数据数组出错3. 传值显...
    99+
    2024-04-02
  • 前端面试官常问的问题:如何进行前端性能优化?
    前端性能优化一直是前端开发者们在工作中不可避免要面对的重要问题。在面试中,面试官通常会问及候选人对于前端性能优化的理解和实践经验。本文将详细探讨前端性能优化的重要性、常见的优化方案以及...
    99+
    2024-04-02
  • MySQL性能优化如何实现
    这篇文章将为大家详细讲解有关MySQL性能优化如何实现,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。一般来说,要保证数据库的效率,要做好以下四个方面的工作:数据库表设计S...
    99+
    2024-04-02
  • 软件架构之如何理解前后端分离与前端模块化
    这篇文章主要讲解了“软件架构之如何理解前后端分离与前端模块化”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“软件架构之如何理解前后端分离与前端模块化”吧!前后...
    99+
    2024-04-02
  • 怎么理解并掌握web前端性能优化的重排和重绘
    这篇文章主要介绍“怎么理解并掌握web前端性能优化的重排和重绘”,在日常操作中,相信很多人在怎么理解并掌握web前端性能优化的重排和重绘问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大...
    99+
    2024-04-02
  • 如何提高javascript和css网站前端的性能优化
    本篇文章为大家展示了如何提高javascript和css网站前端的性能优化,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。CSS性能优化1、把样式表置于顶部现把样式表放到文档的< head /&...
    99+
    2023-06-08
软考高级职称资格查询
推荐阅读
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作