Vue原理(持续更新...)

虚拟DOM和diff

1.背景(历史缺陷)

  • DOM操作比较耗时:为什么说DOM操作很慢 - johnhery - 博客园
  • 以前用jQuery,可以自行控制DOM操作时机。
  • Vue和React都是数据驱动视图。不必手动操作DOM那么这个时候如何有效控制DOM操作?Vue和React是怎么做的? 

2.解决方案:vdom

  • vdom-用js模拟dom结构,计算出最小变更,然后再去操作DOM,不是说不去操作DOM而是说尽量减少对dom的操作

3.用JS模拟dom结构 

4.snabbdom库

        示例:通过h函数生成一个vnode,这个vnode就和上面的js表示的vnode数据结构很像了,      ​​​​

        h函数:创建vnode 

        patch函数 : 初次渲染时通过patch函数将vnode变成真正的dom元素,后续还可以通过patch函数实现dom更新,patch(vnode,null)将执行销毁dom元素

5.vdom总结

  • 用js模拟dom结构(vnode)
  • 新旧vnode对比,得出最小的更新范围,最后更新DOM
  • 数据驱动视图的模式下。能够有效控制DOM操作,减少不必要的dom操作提升性能

6.diff算法

  • ​只比较​​​​同一层级,不跨级比较,也就是每次只比较dom树的一层,是一个层级遍历分析的策略,也就是广度优先,

  • tag(标签)不同,则直接删掉,不再深度比较,尽管下一级的节点可能相同,但是也不管了,直接删除

  • tag和key两者都相同,则认为是相同节点,不再深度比较

7.源码解读

  •  patch函数:
    1. 执行pre hook,
    2. 参数验证:如果第一个参数不是一个vnode而是一个dom元素比如说是一个div,那么此时也就是对应第一渲染的时候,那么就创建一个空的vnode,然后把这个空的vnode和这个dom元素关联在一起
    3. 判断是否是相同的vnode:根据tag和key来判断两者是不是相同的vnode,如果不相同就删掉重建,注意:如果不传入key,那么key就是undefined,如果是同一个vnode,那么就进入patchVnode函数
    4. patchVnode:该函数接受oldVnode和vnode,首先调用prepatch hook,进行一些初始化,然后设置vnode.elem,也就是说我们新创建的虚拟dom本来是没有对应的dom元素的,为了让vnode找到它应该跟哪个dom元素产生关联,我们要给他设置一个elem属性,采用的方式是vnode.elem=oldVnode.elem
    5. patchVnode:如果vnode的子节点是文本节点,那么就简单了,比较一下一样不一样,如果一样就不动,如果不一样,直接把原来的干掉换成新的
    6. patchVnode:如果vnode的子节点不是文本节点,那么有如下逻辑:新旧vnode都有children属性,那么调用updateChildern函数;如果新vnode有旧vnode没有,那旧vnode虽然没有childern,但是可能有text,所以先清除旧vnode的text子节点,然后再把新vnode的children通过调用dom相关的API操作dom增加进去,
    7. updateChildren函数:此时新旧vnode都有children属性,两个children数组,通过双指针算法进行对比,对比规则:1.头头相比,2.尾尾相比,3.新头旧尾,4.旧头新尾,,慢慢的直到全部child节点对比完成,如果命中,就调用patchVnode对vnode进行更新
    8. updateChildren函数:如果一种都没有命中,那就看看新vnode中当前index所指向的child节点的key,在旧vnode的child数组中有没有对应的key存在,如果没有对应上,直接重建,如果对应上了,就拿到这个对应上的节点,再看看标签是否相等,如果不等,就重建,如果相等就patchVnode
  • 由此可以看出key的作用了,如果不加key,那么默认所有的key都是undefined,那么在updateChild阶段,如果出现了四种情况都未命中的情况,那么会频繁的调用pathVnode,因为此时判断是否为同一个节点的条件放宽了,那么patchVnode的活也就变多了,效率就低了

8.diff算法总结

  • patchVnode,更新当前正在对比的Vnode
  • addVnodes,removeVnodes,增加删除DOM
  • updateChildren,对比当前节点的孩子们
  • 细节不重要updateChildren的过程也不重要 
  • vdom核心概念:h函数(传入的是什么,生成的是什么)、vnode(结构是什么)、patch(干什么,传入的是什么参数)、diff(从patch-patchVnode-addVnodes/removeVnodes-updateChildren)、key(在updateChildren中的重要性
  • vdom存在的价值,数据驱动视图,控制DOM操作

模板编译

        Vue的模板,看着像html,但是并不是真正的html,因为他有指令,差值,JS表达式;所以模板到底是什么?

1.前置知识:JS的with语法

        with改变{}内自由变量的查找规则,当作obj属性来查找

        如果找不到匹配的obj属性就会报错

        with要慎用。它打破了作用域规则,易读性变差

2.vue-template-compiler将模板编译成render函数

        首先模板不是html这是肯定的,因为html不是一个图灵完备的语言,不能够执行判断循环之类的逻辑,前端只有js是一个图灵完备的语言,所以如果要执行模板中的各种逻辑代码,那么模板一定是以某种方式转为了js代码,由模板转为js代码的过程即编译模板

3.执行render函数生成vnode

        以属性和动态属性的编译为例,vue拿到我们的模板之后会生成render函数,调用render函数即可得到根据模板生成的vnode,最后通过vnode便可以进行patch和diff

        模板编译的过程应该在开发环境下就完成,算是优化的一部分

4.vue组件中可以用render代替template

5.流程就是:模板到——render函数——vnode——再到渲染和更新

组件 渲染/更新 过程

        黄色区域的render函数,说明模板已经编译好了,然后调用render函数生成虚拟dom树,如果render函数中用到了data中的数据,即触发了该数据的getter函数,那么该数据就会被监听。如果对data中数据进行了修改,那么会触发该数据的setter函数,然后会去确认该数据是否正在监听中,如果是那么模板重新编译,重新生成dom树

1.初次渲染过程

  • 解析模板为render函数(开发环境下完成)
  • 触发响应式,进行数据代理,监听data属性
  • 执行render函数,生成vnode,pathch(elem,vnode)

2.更新过程

  • 修改data,触发setter
  • 重新执行render函数,生成newVnode
  • patch(vnode,newVnode)

3异步渲染

  • 回顾nextTick:页面是异步渲染的,$nextTick待Dom渲染完成后再执行回调,页面渲染时会将data的修改做一个整合,多次data的修改只会渲染一次,因此nextTick也只会调用一次
  • 汇总data的修改,一次性更新视图
  • 减少DOM操作次数,提高性能

Vue-router原理

        稍微复杂的SPA都需要路由。

1.vue-router的路由模式

  • hash模式:
    • hash变化会触发页面跳转,即浏览器的前进后退
    • hash变化不会刷新页面。也是SPA必须的特点
    • hash永远不会提交到server端
    • 主要API,window.onhashchange(event)监听hash的变化,可以通过event获得oldURL和newURL,location.herf修改hash值
  • H5 History模式
    •  用url规范的路由,但是跳转时不刷新页面
    • 会提交到server需要后端的配合,任何路由返回的都是index.html
    • 主要API:history.pushState,window.onpopstate事件监听浏览器的前进和后退
  • 总结
    • hash简单,不需要后端支持

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值