Vue 模版编译原理

当我们使用Vue编写完一个组件以后,Vue会根据模版编译一个render函数,调用render函数生成虚拟DOM,然后将虚拟DOM映射成真实DOM
当数据发生变化时,Vue会触发更新视图,调用render函数返回新的虚拟DOM,对比新旧虚拟DOM,修改真实DOM,从而更新页面
在此期间,有以下4个关键步骤:

  1. 模版编译。生成渲染函数render
  2. 执行render函数生成虚拟DOM
  3. 首次渲染,根据虚拟DOM生成真实DOM
  4. 状态更新后,使用DIFF算法,比较两个虚拟DOM,生成真实DOM

模版编译
模板编译的目标是生成render函数,Vue在实例化组件的时候会生成虚拟DOM,Vue会判断是否有render函数,有就调用,渲染函数执行后,会得到一份vnode用于虚拟DOM渲染。没有的话就获取template中的元素,进行模版编译,生产渲染函数。每次执行渲染函数,就会生成一份最新的vnode,然后使用最新的vnode进行渲染虚拟DOM,进而生成真实DOM。
模版编译有以下步骤:

  1. 将模版解析为AST(抽象语法树)
  2. 遍历AST标记静态节点
  3. 使用AST生成渲染函数

虚拟DOM可以用来描述真实DOM的JS数据结构,是一个树状结构,每个节点所对应的dom元素,都保存了dom元素的标签名、属性和子节点等信息
虚拟DOM主要有以下作用:

  1. 维护视图与状态的关系,在改变状态后,Vue会生成新的虚拟DOM,然后使用Diff算法对比新旧虚拟DOM,得到区别,然后调用patch函数,达到更新真实DOM的目的
  2. 避免手工操作DOM,提升项目的可维护性
  3. 跨平台性更好(虚拟DOM可以将对DOM的操作抽象为对JS对象的操作,这些操作可以被适配到不同平台上,比如浏览器、移动端、甚至是服务器渲染)
  4. 服务端渲染(SSR)
  5. 原生应用(React Native、Weex)
  6. 小程序(uniapp)

Diff算法过程
大概过程是,从根节点开始对比两颗虚拟DOM树中的各个节点:

  1. 如果新旧节点的引用一致,可以认为没有变化
  2. 比较新旧节点文本,需要修改的话,就更改对应的文本
  3. 如果新节点没有子节点,老节点有子节点,直接删除老的子节点
  4. 如果新节点有子节点,老节点没有子节点,则创建新的子节点
  5. 如果都有子节点,但是两者引用不同,会调用uodateChildren函数比较子节点

updataChildren
该操作可以将老的子节点通过DOM操作转为新节点
一个基本的思想就是利用两层循环,外层循环遍历新虚拟DOM的每个节点,在内层循环遍历老虚拟DOM的节点,如果老虚拟DOM节点中有新虚拟DOM中的节点,就移动到新虚拟DOM相应位置,如果没有就创建一个新的节点放到相应的位置,遍历完成后,如果老的虚拟DOM中还有节点,则直接将剩余的节点全部删除
可以做出相关的优化
在新老虚拟DOM首尾各添加一个指针,每次对比这四个指针对应的节点,如果老的首节点或者尾节点能和新的首节点或者尾节点匹配上,将老的对应的节点直接移动到新的对应的节点位置,如果没有匹配到就继续在老的虚拟DOM中寻找,如果没有就直接建立新的,然后移动新老节点的头部,直到新虚拟DOM中的首尾相遇,如果这时候老的虚拟DOM中还有节点,则直接删除

你可能感兴趣的:(vue.js,javascript,前端)