vue2模版编译 虚拟节点转真实节点追加到dom上

73 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第29天,点击查看活动详情

我们在vue2模版编译render的代码写法 已经生成了虚拟dom了,在这里我们需要这是patch,然后打补丁,我们这里又新建了一个patch.js文件文件我们在这里写方法,请看代码方法

createElement方法

function createElement(vnode) { // 看到名字大家已经猜到了 这是一个新建节点和文字的方法
   const {  tag,props,children,text } = vnode
   if (typeof tag==="string") { // tag是字符串 就是真实节点
      vnode.el = document.createElement(tag) 
      // 这里比较麻烦的事我们要为我们新建的元素去添加他们之前的属性
      updateProps(vnode)
      // 这里需要循环他的children 去创建新的,就是一个递归
      children.map((child)=>{
        vnode.el.appendChild(createElement(child)) // 结果又追加到了父级
      })
   } else {
    vnode.el = document.createTextNode(text) // 给当前vnode添加文本
   }
   return vnode.el // 把新建的root节点返回出去
}

updateProps方法

function updateProps(vnode) { // 这个方法主要是给每个有属性的虚拟节点变为真实节点之后添加属性的
  const el = vnode.el;
  const newProps = vnode.props || {};
  // 遍历对象newProps属性对象
  for (let key in newProps) { /
    if (key==="style") { // style是比较麻烦的 我们还需要循环
        for (let skey in newProps[key].style) {
            el.style[skey] = newProps[key].style[skey]
        }
    } else if (key==="class") { // class 需要单独设置
        el.classNmae = newProps[key]
    } else  { // 其他用setAttribute直接设置
        el.setAttribute(key,newProps[key])
    }
  }
}

patch 是这里最核心的方法我们需要把自己已经转化的好真实dom挂载到页面上

function patch(oldNode,vNode) { // oldNode 是真实dom vNode是虚拟dom
    let el = createElement(vNode); // 虚拟节点变为真实节点
    let parentElement = oldNode.parentNode; // 我们需要找到真实domroot的父级
    parentElement.insertBefore(el,oldNode.nextSibling) // 这个的意思是我们需要把我们新的真实dom追到到,老的真实dom的后面。
    parentElement.removeChild(oldNode) 追加完成删除老的真实dom   
}

这里有人会问,为啥上面那个方法,不能直接删除老的真实dom,然后用body直接追加上去呢,还用这么麻烦,先找父级然后在同级追加。 有一个问题,就是我们root的父级不一定就是body,如果不是body的情况,我们直接删除,然后用body追加是错的,还有就是我们body里会有script标签,如果我们删除了,直接body追加会在script标签后面,会有阻拦的情况。

最后我们需要在哪里去触发patch呢, lifecycle.js 混入的钩子函数的 _update方法里出发

import { patch } from './vdom/patch.js'
function mountComponent(vm) { // 其实是一个生命周期函数
   // 执行就是lifecycleMixin函数混入的 update更新方法
   vm._update(vm._render())
}

function lifecycleMixin(Vue) {
    Vue.prototype._update = function (vnode) {
       console.log(vnode) 
       const vm = this;
       patch(vm.$el,vnode)
    }
}
export {
    mountComponent,
    lifecycleMixin
}

我们看下页面效果

截屏2022-10-18 下午10.56.17.png 完美显示出来了 完结撒花 如果有人想要完整的源码可以留言 谢谢