DOM-Diff讲解

 前言

d6583396de40d96196e4737cd9f0f119.png

  我是歌谣 最好的种树是十年前 其次是现在 今天继续给大家带来的是DOM-Diff讲解

 环境配置

1f374d87ec8fba81556de943e777c1ae.png

npm init -y
yarn add vite -D

 修改page.json配置端口

572355e5f3646dea76f1920e645369ad.png

{
"name": "react_ts",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "vite --port 3002",
"server":"ts-node-dev ./server/app.ts"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@types/express": "^4.17.17",
"@types/jquery": "^3.5.18",
"express": "^4.18.2",
"jquery": "^3.7.1",
"ts-node-dev": "^2.0.0",
"typescript": "^5.2.2",
"vite": "^4.4.9"
}
}

 目录结构

aebd3d10a6ab994a543722eb355aa36a.png

73c6af84edb76989eb24381664d346b2.png

 domDiff.js

33d28ee4a6b8b011c0b7292b4678aa64.png

import {
ATTR,
TEXT,
REPLACE,
REMOVE
} from './pathTypes';
let patches = {},
vnIndex = 0;
function domDiff (oldVDom, newVDom) {
let index = 0;
vNodeWalk(oldVDom, newVDom, index);
return patches;
}
function vNodeWalk(oldNode,newNode,index){
let vnPatch = [];
if (!newNode) {
vnPatch.push({
type: REMOVE,
index
})
} else if (typeof oldNode === 'string' && typeof newNode === 'string') {
if (oldNode !== newNode) {
vnPatch.push({
type: TEXT,
text: newNode
})
}
} else if (oldNode.type === newNode.type) {
const attrPatch = attrsWalk(oldNode.props, newNode.props);
console.log(attrPatch,"attrPatch is")
if (Object.keys(attrPatch).length > 0) {
vnPatch.push({
type: ATTR,
attrs: attrPatch
});
}
childrenWalk(oldNode.children, newNode.children);
} else {
vnPatch.push({
type: REPLACE,
newNode
})
}
if (vnPatch.length > 0) {
patches[index] = vnPatch;
}
}
function attrsWalk (oldAttrs, newAttrs) {
let attrPatch = {};
for (let key in oldAttrs) {
// 修改属性
if (oldAttrs[key] !== newAttrs[key]) {
attrPatch[key] = newAttrs[key];
}
}
for (let key in newAttrs) {
// 新增
if (!oldAttrs.hasOwnProperty(key)) {
attrPatch[key] = newAttrs[key];
}
}
return attrPatch;
}
function childrenWalk (oldChildren, newChildren) {
oldChildren.map((c, idx) => {
vNodeWalk(c, newChildren[idx], ++ vnIndex);
});
}
export default domDiff;

 dopatch.js

7785ea7d51d45297dc7267543585a9f5.png

import {
ATTR,
TEXT,
REPLACE,
REMOVE
} from './pathTypes';
import { setAttrs, render } from './virtualDom';
import Element from './element';
let finalPatches = {},
rnIndex = 0;
function doPatch (rDom, patches) {
finalPatches = patches;
rNodeWalk(rDom);
}
function rNodeWalk (rNode) {
const rnPatch = finalPatches[rnIndex ++],
childNodes = rNode.childNodes;
[...childNodes].map((c) => {
rNodeWalk(c);
});
if (rnPatch) {
patchAction(rNode, rnPatch);
}
}
function patchAction (rNode, rnPatch) {
rnPatch.map((p) => {
switch (p.type) {
case ATTR:
for (let key in p.attrs) {
const value = p.attrs[key];
if (value) {
setAttrs(rNode, key, value);
} else {
rNode.removeAttribute(key);
}
}
break;
case TEXT:
rNode.textContent = p.text;
break;
case REPLACE:
const newNode = (p.newNode instanceof Element)
? render(p.newNode)
: document.createTextNode(p.newNode);
rNode.parentNode.replaceChild(newNode, rNode);
break;
case REMOVE:
rNode.parentNode.removeChild(rNode);
break;
default:
break;
}
});
}
export default doPatch;
// vNode = virtual Node
// vnPatch = virtual Node patch
// rNode = real Node
// rnPatch = real Node patch

 element.js

687a5f8d4b060efb6d90e13daf2e14a8.png

class Element{
constructor(type,props,children){
this.type=type
this.props=props
this.children=children
}
}
export default Element

 index.js

48a2d767e461cc85da727709f6b36428.png

import { createElement, render, renderDom } from './virtualDom';
import domDiff from './domDiff';
import doPatch from './doPatch';
const vDom1 = createElement('ul', {
class: 'list',
style: 'width: 300px; height: 300px; background-color: orange'
}, [
createElement('li', {
class: 'item',
'data-index': 0
}, [
createElement('p', {
class: 'text'
}, [
'第1个列表项'
])
]),
createElement('li', {
class: 'item',
'data-index': 1
}, [
createElement('p', {
class: 'text'
}, [
createElement('span', {
class: 'title'
}, [])
])
]),
createElement('li', {
class: 'item',
'data-index': 2
}, [
'第3个列表项'
])
]);
const vDom2 = createElement('ul', {
class: 'list-wrap',
style: 'width: 300px; height: 300px; background-color: orange'
}, [
createElement('li', {
class: 'item',
'data-index': 0
}, [
createElement('p', {
class: 'title'
}, [
'特殊列表项'
])
]),
createElement('li', {
class: 'item',
'data-index': 1
}, [
createElement('p', {
class: 'text'
}, [])
]),
createElement('div', {
class: 'item',
'data-index': 2
}, [
'第3个列表项'
])
]);
const rDom = render(vDom1);
renderDom(
rDom,
document.getElementById('app')
);
const patches = domDiff(vDom1, vDom2);
doPatch(rDom, patches);
console.log(patches);

 Virtualdom.js

62da1f2415c5d062fae61cc3566e57ac.png

import Element from "./element";
import domDiff from "./domDiff"
function createElement(type, props, children) {
return new Element(type, props, children)
}
function setAttrs(node,prop,value){
switch(prop){
case 'value':
if(node.tagName==='INPUT'||node.tagName==='TEXTAREA'){
node.value=value
}else{
node.setAttribute(prop,value)
}
break;
case 'style':
node.style.cssText=value
break;
default:
node.setAttribute(prop,value);
break
}
}
function render (vDom) {
const { type, props, children } = vDom,
el = document.createElement(type);
for (let key in props) {
setAttrs(el, key, props[key]);
}
children.map((c) => {
c = c instanceof Element
?
render(c)
:
document.createTextNode(c);
el.appendChild(c);
});
return el;
}
function renderDom(rDom,rootEl){
rootEl.appendChild(rDom)
}
export {
createElement,
render,
setAttrs,
renderDom
}

 运行结果

051a5b1e1460d33e2f7daab344503a39.png

829bc852e27a9011a9500cbbd0704228.png

总结

9562efa16778c2d2deb9bffa25f9fb8c.png

我是歌谣 想加入前后端技术交流群私信我

56535b0767b59cdb8d244aec912b188f.png

点击上方 蓝字 关注我们

下方查看历史文章

1238d0e806d48ed8aaaf22a800aa0a26.png

javascript树形结构化

原生+TS实现todolist效果

Vue3+typesctipt实现todolist效果

关于原始typescript实现todolist笔记(装饰器模式)

关于原生typescript实现todolist

posted @   前端导师歌谣  阅读(4)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
点击右上角即可分享
微信分享提示