# 手写 Dom dff

vnode.js

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

function createElement(type, props, children) {
  return new Element(type, props, children);
}

module.exports = createElement;
1
2
3
4
5
6
7
8
9
10
11
12
13
  • 这就是一个虚拟 dom╮(╯▽╰)╭
  • 其实就是把一些属性挂载

index.js

import createElement from "./vnode.js";
import domDiff from "./diff.js";

let vDmo1 = createElement(
  "ul",
  {
    className: "list"
  },
  [
    createElement("li", { className: "item" }, ["1"]),
    createElement("li", { className: "item" }, ["2"]),
    createElement("li", { className: "item" }, ["3"])
  ]
);

let vDmo2 = createElement(
  "ul",
  {
    className: "new-list"
  },
  [
    createElement("li", { className: "item" }, ["a"]),
    createElement("li", { className: "item" }, ["2"]),
    createElement("li", { className: "item" }, ["b"])
  ]
);

console.log(domDiff(vDmo1, vDmo2));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
  • 把虚拟 dom 方法和 diff 方法都引进来
  • 创建两个虚拟 dom 进行比较

utils.js

export default {
  isString(type) {
    return typeof type === "string";
  }
};
1
2
3
4
5
  • 一个工具类,用来判断是否是字符串

diff.js

import utils from "./utils.js";

let patches = [];
function domDiff(oldDom, newDom) {
  walk(oldDom, newDom, index);
  return patches;
}

function walk(oldDom, newDom, index) {
  let patch = [];
  // 如果newDom不存在则意味着是删除
  if (!newDom) {
    patch.push({
      type: "REMOVE",
      index
    });
  }
  // 对比文本
  else if (utils.isString(oldDom) && utils.isString(newDom)) {
    if (oldDom !== newDom) {
      patch.push({
        type: "TEXT",
        text: newDom
      });
    }
  } else if (oldDom.type === newDom.type) {
    let attr = diffAttr(oldDom.props, newDom.props);
    if (Object.keys(attr).length) {
      patch.push({
        type: "ATTR",
        attr
      });
    }
    diffChildren(oldDom.children, newDom.children);
  } else {
    pathch.push({
      type: "REPLACE",
      newDom
    });
  }

  if (patch.length) {
    patches[index] = patch;
  }
}

function diffAttr(oldProps, newProps) {
  let attr = {};
  for (let key in oldProps) {
    if (oldProps[key] !== newProps[key]) {
      attr[key] = newProps[key];
    }
  }
  for (let key in newProps) {
    if (!oldProps.hasOwnProperty(key)) {
      attr[key] = newProps[key];
    }
  }
  return attr;
}

function diffChildren(oldChildren, newChildren) {
  oldChildren.forEach((child, i) => {
    walk(child, newChildren[i], ++index);
  });
}

export default domDiff;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68

diff.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>简单的dom diff</title>
  </head>
  <body>
    <script src="./code/index.js" type="module"></script>
  </body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12