vue-snabbdom 虚拟 dom diff 算法学习

193 阅读2分钟

用到的工具函数(下面用到的时候可以回来看)

export default {
  // 判断是否为一个vnode对象
	isVnode(b) {
		return b.sel === '' || b.sel === undefined
	},
  // 判断虚拟节点是否为同一个虚拟节点
	sameNode(node, node2) {
		return node.sel === node2.sel && node.key === node2.key
	},
}

vnode 函数生成

/**
 * @description:用来生成虚拟dom对象
 * @param {*} sel 需要生成的dom
 * @param {*} data 标签的属性,点击事件等
 * @param {*} children 子级节点
 * @param {*} text 子级节点 具有文本对象,children就是空的
 * @param {*} elm 虚拟dom生成的真实dom对象
 * @return {*} 返回一个vnode 对象
 */
export default function (sel, data, children, text, elm) {
	const key = data && data.key ? data.key : ''
	return { sel, data, children, text, elm, key }
}

h 函数的实现(增加了一些参数的判断,从而达到参数占位符)

h 函数就是配合 vnode 对象返回一个 vnode 对象

import vnode from './vnode.js'
import is from '../utils/is'
/**
 * @description:
 * @param {*} sel 需要生成的dom
 * @param {*} b dom的属性 key 等
 * @param {*} c 子级节点
 * @return {*}
 */

export default function (sel, b, c) {
	let children
	let text
	let data
	// 判断是c是否为空值 如果为c存在 b肯定就存在
	if (c !== undefined) {
		// 如果b是真值 直接赋值给data
		if (b !== null) {
			data = b
		}
		if (is.array(c)) {
			children = c
		} else if (is.primitive(c)) {
			text = c
		} else if (c && c.sel) {
			children = [c]
		}
		// c没有传进来 b有可能是children 也有可能是data
	} else if (b !== undefined && b !== null) {

		if (is.array(b)) { // 数组类型 为children
			children = b
		} else if (is.primitive(b)) { //普通类型数据
			text = b
		} else if (b && b.sel) { 	// 如果 b为一个 对象并且 sel存在 直接当做children 证明他和vnode函数返回的数据一样
			children = [b]
		} else {  //以上都不满足的情况下 可以判定为 data属性
			console.log('b==>', b)
			data = b
		}
		// 静态文本类型 为children
	}
	// 最后开始判断是否传入了 children属性
	if (children !== undefined) {
		children.forEach((item, index) => {
			if (is.primitive(item)) {
				children[index] = vnode(undefined, undefined, undefined, item, undefined)
			}
		})
	}
	return vnode(sel, data, children, text, undefined)
}

patch 函数(这里的情况只包1)

  1. newVnode 存在text属性 oldVnode 没有text属性
  2. newVnode 没有text 属性 oldVnode 有text属性 xx xx
import is from '../utils/is'
import vnode from '../core/vnode'
export default function patch(oldVnode, newVnode) {
  // 把root节点格式化为一个虚拟节点对象
	if (is.isVnode(oldVnode)) {
		oldVnode = vnode(undefined, undefined, undefined, undefined, oldVnode)
	}
	if (is.sameNode(oldVnode, newVnode)) {
    // 如果是同一个虚拟节点对象 先不管
	} else {
		// 若果存在文本内容
		if (newVnode.text) {
			if (newVnode.text != oldVnode.text) {
				oldVnode.elm.innerText = newVnode.text
			}
		}else{
      	oldVnode.elm.innerText =''
    }
	}
}


创建一个 index 文件

import h from '../core/h'
import patch from '../core/patch'
const vnode = h('div', 'test')
patch(document.querySelector('.container'), vnode)

到现在我们已经可以生成一个真实节点了 image.png