手摸手vue中虚拟dom函数和diff算法
snabbdom简介和测试环境搭建
npm i-S snabbdom
npm i -D webpack@5 webpack-cli@3 webpack-dev-server@3
修改package.json中”script”属性的“dev”:“webpack-dev-server”
配置webpack.config.js 文件
// 从https://www.webpackjs.com/官网照着配置
const path = require(‘path’);module.exports = {
// 入口
entry: './src/index.js',
// 出口
output: {
// 虚拟打包路径,就是说文件夹不会真正生成,而是在8080端口虚拟生成
publicPath: 'xuni',
// 打包出来的文件名,不会真正的物理生成
filename: 'bundle.js'
},
devServer: {
// 端口号
port: 8080,
// 静态资源文件夹
contentBase: 'www'
}
};
打包测试
虚拟DOM和h函数
diff是发生在虚拟的DOM层,算出应该如何让最小量更新,最后反映到真正的DOM
h函数用来产生虚拟节点(vnode),可以嵌套使用,从而得到虚拟DOM树
import vnode from './vnode.js';
// 编写一个低配版本的h函数,这个函数必须接受3个参数,缺一不可
// 相当于它的重载功能较弱。
// 也就是说,调用的时候形态必须是下面的三种之一:
// 形态① h('div', {}, '文字')
// 形态② h('div', {}, [])
// 形态③ h('div', {}, h())
export default function (sel, data, c) {
if (arguments.length != 3)
throw new Error('对不起,h函数必须传入3个参数,我们是低配版h函数');
if (typeof c == 'string' || typeof c == 'number'){
return vnode(sel,data,undefined,c,undefined)
}else if(Array.isArray(c)){
let children = []
for (var i =0 ;i < c.length;i++){
if(!(typeof c[i] == 'object' && c[i].hasOwnProperty('sel')))
throw new Error('传入的数组参数中有项不是h函数');
children.push(c[i])
}
return vnode(sel,data,children,undefined,undefined)
}else if(typeof c == 'object' && c.hasOwnProperty('sel')){
let children = [c]
return vnode(sel,data,children,undefined,undefined)
}else{
throw new Error('传入的数组参数中有项不是h函数');
}
};
虚拟节点的属性
- sel
- data
- children
- text
- elm
- key
diff算法
我们要手写h函数(用于产生虚拟节点),vnode函数(节点转化成对象),patch函数(用于上树,判断新旧是否是同个节点),
diff处理新旧节点是否是同个节点
只有同一个虚拟节点,才能进行精细化比较
选择器相同且key相同
- 只进行同层比较,不会进行跨层比较
新旧节点不是同一个节点
创建新节点的时候,所有的子节点需要递归创建。createElements函数(用于创建节点,将vnode创建为DOM,是孤儿节点,上树还需要patch函数,不进行插入)
手写递归创建节点,注意递归主要利用返回值的dom对象,因为标杆在新节点的父子节点比较麻烦
appendChild与insertBefore之前的差别
- appendChild() 方法:可以向节点的子节点列表的末尾添加新的子节点。
比如:appendChild(newchild)括号里可以是创建的标签var newchild = document.createElement - insertBefore() 方法:
可在已有的字节点前中插入一个新的子节点。比如:insertBefore(newchild,rechild)
参数:
(newchild: 要插入的新节点。
rechild: 指定此节点前插入节点 - 相同之处:插入子节点。
- 不同之处:
appendChild是在父节点中的字节点的末尾添加新的节点(相对于父节点来说)。
insertBefore是在已有的节点前添加新的节点(相对于子节点来说的)
对于新旧节点都有children属性的情况下采用精细化比较
四种命中方法(自上往下,命中一种就不会往下继续判断了)
- 新前与旧后
- 新后与旧后
- 新后与旧前
- 新前与旧后
- 循环遍历,移动到oldstartidx之前
新增
如果是旧节点先循环完毕,如果新节点中还有剩余节点,说明他们是要新增的节点。
新前>=新后菜可以新增
删除
多删除
如果是新节点先循环完毕,如果老节点中还有剩余节点,说明他们是要被删除的节点。
旧前>=旧后菜可以删除
复杂
当第三种情况命中的时候,移动新前指向老节点的旧后的后面
当第四种情况命中的时候,注意呀移动节点,移动新前指向老节点的旧前的前面
参考:
解析vue2.0的diff算法
还没有评论,来说两句吧...