近段时间开始写vue项目,以及在面试中遇到关于vue原理的问题,现打算开始看看Vue及全家桶的源码,并记录下本身的理解,若有错误,还望大佬们帮忙指出:vue
1.关于vue虚拟dom渲染到真实dom的简单实现面试
// 虚拟DOM元素的类,构建实例对象,用来描述DOM
class Element {
constructor(type, props, children) {
this.type = type; //对应的dom元素名称
this.props = props; //对应的类名,style等等
this.children = children; //对应的子元素
}
}
// 建立虚拟DOM,返回虚拟节点(object)
function createElement(type, props, children) {
return new Element(type, props, children);
}
// render方法将虚拟dom转换为真实dom
function render(domObj){
//根据type渲染为真实的dom元素
let el = document.createElement(domObj.type)
//遍历自身props添加
for(let key in domObj.props) {
//设置属性(只考虑了class,暂时没有考虑到style样式。。。)
el.setAttribute(key, domObj.props[key])
}
//遍历子元素
domObj.children.forEach(child => {
let chileEl = (child instanceof Element) ? render(child) : document.createTextNode(child)
el.appendChild(chileEl)
})
return el;
}
//将元素dom插入页面内
function renderDom(el,target){
target.appendChild(el)
}
// 首先引入对应的方法来建立虚拟DOM
let virtualDom = createElement('ul', {class: 'list'}, [
createElement('li', {class: 'item'}, ['子元素1']),
createElement('li', {class: 'item'}, ['子元素2']),
createElement('li', {class: 'item'}, ['子元素3'])
]);
console.log(virtualDom)
let el = render(virtualDom);
console.log(el);
renderDom(el,document.getElementById('root'))
复制代码
比较两棵DOM树的差别是Virtual DOM算法最核心的部分.简单的说就是新旧虚拟dom 的比较,若是有差别就以新的为准,而后再插入的真实的dom中,从新渲染。 借网络一张图片说明:算法
如图能够看出,树的渲染对比,须要通过新旧虚拟dom的对比,而且只在同层级进行比较: 经过第一步的虚拟dom简单实现能够看出,节点的对比主要为如下四种状况:bash
此节点是否被移除 -> 添加新的节点网络
属性是否被改变 -> 旧属性改成新属性app
文本内容被改变-> 旧内容改成新内容dom
节点要被整个替换 -> 结构彻底不相同 移除整个替换ui
如下是模仿节点被移除:this
/**
* 变化虚拟dom
*/
//keyIndex记录遍历顺序
let keyIndex = 0
// 遍历
function diff(oldEle, newEle) {
let patches = {}
keyIndex = 0
walk(patches, 0, oldEle, newEle)
return patches
}
//分析变化
function walk(patches, index, oldEle, newEle) {
let currentPatches = []
//这里应该有不少的判断类型,这里只处理了删除的状况...
if (!newEle) {
currentPatches.push({ type: 'remove' })
}
else if (oldEle.type == newEle.type) {
//比较儿子们
walkChild(patches, currentPatches, oldEle.children, newEle.children)
}
//判断当前节点是否有改变,有的话把补丁放入补丁集合中
if (currentPatches.length) {
patches[index] = currentPatches
}
}
function walkChild(patches, currentPatches, oldChilds, newChilds) {
if (oldChilds) {
for (let i = 0; i < oldChilds.length; i++) {
let oldChild = oldChilds[i]
let newChild = newChilds[i]
walk(patches, ++keyIndex, oldChild, newChild)
}
}
}
/**
* 将变化的补丁插入到真实dom
*/
let index = 0;
let allPatches;
function patcFunc(trueNode,patch){
allPatches = patch
showNow(trueNode)
}
//操做真实dom
function showNow(trueNode) {
let currentPatches = allPatches[index]
index++
(trueNode.childNodes || []) && trueNode.childNodes.forEach(child => {
showNow(child)
})
if (currentPatches) {
doPatch(trueNode, currentPatches)
}
}
//根据type是移除,则移除dom中的元素
function doPatch(ele, currentPatches) {
currentPatches.forEach(currentPatch => {
if (currentPatch.type == 'remove') {
ele.parentNode.removeChild(ele)
}
})
}
// 首先引入对应的方法来建立虚拟DOM
let virtualDom = createElement('div', {class: 'list'}, [
createElement('div', {class: 'item'}, ['周杰伦']),
createElement('div', {class: 'item'}, ['林俊杰']),
createElement('div', {class: 'item'}, ['王力宏'])
]);
console.log('旧的dom', virtualDom)
//变化
let newDom = createElement('div', {class: 'list'}, [
createElement('div', {class: 'item'}, ['林俊杰']),
createElement('div', {class: 'item'}, [''])
]);
let patch = diff(virtualDom,newDom)
let el = render(virtualDom);
patcFunc(el,patch)
renderDom(el,document.getElementById('root'))
复制代码
总结的说,vue在建立虚拟dom的时候会以一个规定的格式来建立虚拟dom须要的数据,而后当发生改变的时候,新的虚拟dom的数据和旧的虚拟dom的数据会进行对比,按照层级进行对比(diff的比较方式,若是上一层的子节点不同,就直接替换,再也不比较子节点),不相同的便放到一个patch补丁里,而后再将patch补丁里的数据去操做当前真实dom,看是否节点须要改变,删除等等。spa