(3)手写mini-vue,简单实现核心的diff算法

66 阅读1分钟

1.需要在mount中定义isMounted记录是否是第一次挂载页面,增加存储老的的vnode的preSubtree

//core/index.js
import { effectWatch } from './reactivity/index.js'
import { mountElement,diff } from './renderer/index.js'
//rootComponent ==> App.js
export function createApp (rootComponent) {

  return {
    //rootContainer == #app
    mount (rootContainer) {
      const context = rootComponent.setup()
      let isMounted = false //添加标识
      let preSubtree; //增加存储老的的vnode
      effectWatch(() => {
        
        if(!isMounted){
          isMounted = true
          rootContainer.innerHTML = ''
          const subTree = rootComponent.render(context)
          console.log(subTree)
          mountElement(subTree, rootContainer)
          preSubtree = subTree //新值oldVnode 
        }else{
          const subTree = rootComponent.render(context)
          //diff算法 获取newVnode oldVnode
          diff(preSubtree,subTree)
          preSubtree = subTree
        }
        
      )
    }
    
  }
}

2.简单实现diff算法

//core/renderer/index.js
//n1 oldnode
//n2 newnode
export function diff (n1, n2) {
  console.log(n1, n2)
  //tag
  if (n1.tag !== n2.tag) {
    n1.el.replaceWith(document.createElement(n2.tag))
  } else {
    const el = n2.el = n1.el
    const { props: newProps } = n2
    const { props: oldProps } = n1
    //props
    //new:{id:'foo',class:'bar',a}
    //old:{id:'foo-1',class:'bar',a}
    //新旧节点名称某个名称不一致
    if (newProps && oldProps) {
      Object.keys(newProps).forEach((key) => {
        const newVal = newProps[key]
        const oldVal = oldProps[key]
        if (newVal !== oldVal) {
          el.setAttribute(key, newVal)
        }
      })
    }
    //如果新节点删除了部分key
    //new:{id:'foo',class:'bar'}
    //old:{id:'foo-1',class:'bar',a}
    if (oldProps) {
      Object.keys(oldProps).forEach((key) => {
        if (!newProps[key]) {
          el.removeAttribute(key)
        }
      })
    }
    //children 暴力算法
    // 1.newChildren ->string  (oldChildren ->string  oldChildren ->array)
    // 1.newChildren ->array  (oldChildren ->string  oldChildren ->array)
    const { children: newChildren } = n2
    const { children: oldChildren } = n1

    if(typeof newChildren === 'string'){
      if(typeof oldChildren === 'string'){
        if(newChildren!==oldChildren){
          el.textContent = newChildren
        }
      }else if(Array.isArray(oldChildren)){
        el.textContent = newChildren
      }
    }else if(Array.isArray(newChildren)){
      if(typeof oldChildren ==='string'){
        el.innerText = ``
        mountElement(n2,el)
      }else if(Array.isArray(oldChildren)){

        const length = Math.min(newChildren.length,oldChildren.length)
        //处理公共的
        for (let i = 0; i < length; i++) {
          const newVnode = newChildren[i];
          const oldVnode = oldChildren[i];
          diff(oldVnode,newVnode)
        }

        if(newChildren.length>length){
          //创建节点
          for (let index = length; index < newChildren.length; index++) {
            const newVnode = newChildren[index];
            mountElement(newVnode)
          }
        }

        if(oldChildren.length>length){
          //删除节点
          for (let index = length; index < oldChildren.length; index++) {
            const oldVnode = oldChildren[index];
            oldVnode.el.parent.removeChild(oldVnode.el)
            
          }
        }
      }
    }

  }

}