近段時間開始寫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