You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

20 KiB

30虚拟DOM如何通过虚拟DOM更新页面

你好,我是大圣。

上一讲我们主要介绍了Vue项目的首次渲染流程在mountComponent中注册了effect函数这样在组件数据有更新的时候就会通知到组件的update方法进行更新。

Vue中组件更新的方式也是使用了响应式+虚拟DOM的方式这个我们在第一讲中有介绍过Vue 1、Vue 2和Vue 3中更新方式的变化今天我们就来详细剖析一下Vue组件内部如何通过虚拟DOM更新页面的代码细节。

Vue虚拟DOM执行流程

我们从虚拟DOM在Vue的执行流程开始讲起。在Vue中我们使用虚拟DOM来描述页面的组件比如下面的template虽然格式和HTML很像但是在Vue的内部会解析成JavaScript函数这个函数就是用来返回虚拟DOM

<div id="app">
  <p>hello world</p>
  <Rate :value="4"></Rate>
</div>

上面的template会解析成下面的函数最终返回一个JavaScript的对象能够描述这段HTML

function render(){
  return h('div',{id:"app"},children:[
    h('p',{},'hello world'),
    h(Rate,{value:4}),
  ])
}

知道虚拟DOM是什么之后那么它是怎么创建的呢

DOM的创建

我们简单回忆上一讲介绍的mount函数在代码中我们使用createVNode函数创建项目的虚拟DOM可以看到Vue内部的虚拟DOM也就是vnode就是一个对象通过type、props、children等属性描述整个节点

const vnode = createVNode(    
  rootComponent as ConcreteComponent,
  rootProps
)
function _createVNode() {

  // 处理属性和class
  if (props) {
    ...
  }

  // 标记vnode信息
  const shapeFlag = isString(type)
    ? ShapeFlags.ELEMENT
    : __FEATURE_SUSPENSE__ && isSuspense(type)
    ? ShapeFlags.SUSPENSE
    : isTeleport(type)
    ? ShapeFlags.TELEPORT
    : isObject(type)
    ? ShapeFlags.STATEFUL_COMPONENT
    : isFunction(type)
    ? ShapeFlags.FUNCTIONAL_COMPONENT
    : 0

  return createBaseVNode(
    type,
    props,
    children,
    patchFlag,
    dynamicProps,
    shapeFlag,
    isBlockNode,
    true
  )
}

function createBaseVNode(type,props,children,...){
    const vnode = {
    type,
    props,
    key: props && normalizeKey(props),
    ref: props && normalizeRef(props),
    children,
    shapeFlag,
    patchFlag,
    dynamicProps,
     ...
  } as VNode
  // 标准化子节点
  if (needFullChildrenNormalization) {
    normalizeChildren(vnode, children)
  } else if (children) {
    vnode.shapeFlag |= isString(children)
      ? ShapeFlags.TEXT_CHILDREN
      : ShapeFlags.ARRAY_CHILDREN
  }
  return vnode
}componentUpdateFn

createVNode负责创建Vue中的虚拟DOM而上一讲中我们讲过mount函数的核心逻辑就是使用setupComponent执行我们写的