processElement处理标签
function processElement(n1, n2, container) {
if (!n1) {
mountElement(n2, container);
}
else {
updateElement(n1, n2);
}
}
mountElement挂载标签
- 创建对应type的dom
- children是string:el.textContent = text
- children是array:对children每一项进行patch
- patchProp搞定属性,区分属性和事件,分别进行设置
- 插入父元素
function mountElement(vnode, container) {
const { shapeFlag, props } = vnode;
const el = (vnode.el = createElement(vnode.type));
if (shapeFlag & 8) {
setElementText(el, vnode.children);
}
else if (shapeFlag & 16) {
mountChildren(vnode.children, el);
}
if (props) {
for (const key in props) {
const nextVal = props[key];
patchProp(el, key, null, nextVal);
}
}
insert(el, container);
}
createElement函数创建dom
function createElement(type) {
const element = document.createElement(type);
return element;
}
设置text
function setElementText(el, text) {
el.textContent = text;
}
对children每一项进行patch
function mountChildren(children, container) {
children.forEach((VNodeChild) => {
patch(null, VNodeChild, container);
});
}
patchProp
function patchProp(el, key, preValue, nextValue) {
if (isOn(key)) {
const invokers = el._vei || (el._vei = {});
const existingInvoker = invokers[key];
if (nextValue && existingInvoker) {
existingInvoker.value = nextValue;
}
else {
const eventName = key.slice(2).toLowerCase();
if (nextValue) {
const invoker = (invokers[key] = nextValue);
el.addEventListener(eventName, invoker);
}
else {
el.removeEventListener(eventName, existingInvoker);
invokers[key] = undefined;
}
}
}
else {
if (nextValue === null || nextValue === "") {
el.removeAttribute(key);
}
else {
el.setAttribute(key, nextValue);
}
}
}
插入父元素
function insert(child, parent, anchor = null) {
if (anchor) {
parent.insertBefore(child, anchor);
}
else {
parent.appendChild(child);
}
}
updateElement更新标签
- 更新props属性
- 更新children子元素
function updateElement(n1, n2, container) {
const oldProps = (n1 && n1.props) || {};
const newProps = n2.props || {};
const el = (n2.el = n1.el);
patchProps(el, oldProps, newProps);
patchChildren(n1, n2, el);
}
patchProps
包含属性和事件
- 被改变的key更新
- 被删除的key删除
function patchProps(el, oldProps, newProps) {
for (const key in newProps) {
const prevProp = oldProps[key];
const nextProp = newProps[key];
if (prevProp !== nextProp) {
hostPatchProp(el, key, prevProp, nextProp);
}
}
for (const key in oldProps) {
const prevProp = oldProps[key];
const nextProp = null;
if (!(key in newProps)) {
hostPatchProp(el, key, prevProp, nextProp);
}
}
}
patchChildren
更新children string文本,setElementText直接替换 array数组,需要遍历做对比,也就是diff
function patchChildren(n1, n2, container) {
const { shapeFlag: prevShapeFlag, children: c1 } = n1;
const { shapeFlag, children: c2 } = n2;
if (shapeFlag & 8) {
if (c2 !== c1) {
setElementText(container, c2);
}
}
else {
if (prevShapeFlag & 16) {
if (shapeFlag & 16) {
patchKeyedChildren(c1, c2, container);
}
}
}
}
setElementText
function setElementText(el, text) {
el.textContent = text;
}
patchKeyedChildren
太长了,可以专门找一片讲diff的文章
function patchKeyedChildren(c1, c2, container) {
let i = 0;
let e1 = c1.length - 1;
let e2 = c2.length - 1;
const isSameVNodeType = (n1, n2) => {
return n1.type === n2.type && n1.key === n2.key;
};
while (i <= e1 && i <= e2) {
const prevChild = c1[i];
const nextChild = c2[i];
if (!isSameVNodeType(prevChild, nextChild)) {
// console.log("两个 child 不相等(从左往右比对)");
// console.log(`prevChild:${prevChild}`);
// console.log(`nextChild:${nextChild}`);
break;
}
// console.log("两个 child 相等,接下来对比着两个 child 节点(从左往右比对)");
patch(prevChild, nextChild, container);
i++;
}
while (i <= e1 && i <= e2) {
const prevChild = c1[e1];
const nextChild = c2[e2];
if (!isSameVNodeType(prevChild, nextChild)) {
// console.log("两个 child 不相等(从右往左比对)");
// console.log(`prevChild:${prevChild}`);
// console.log(`nextChild:${nextChild}`);
break;
}
// console.log("两个 child 相等,接下来对比着两个 child 节点(从右往左比对)");
patch(prevChild, nextChild, container);
e1--;
e2--;
}
if (i > e1 && i <= e2) {
while (i <= e2) {
// console.log(`需要新创建一个 vnode: ${c2[i].key}`);
patch(null, c2[i], container);
i++;
}
}
else if (i > e2 && i <= e1) {
while (i <= e1) {
// console.log(`需要删除当前的 vnode: ${c1[i].key}`);
hostRemove(c1[i].el);
i++;
}
}
else {
let s1 = i;
let s2 = i;
const keyToNewIndexMap = new Map();
for (let i = s2; i <= e2; i++) {
const nextChild = c2[i];
keyToNewIndexMap.set(nextChild.key, i);
}
const toBePatched = e2 - s2 + 1;
const newIndexToOldIndexMap = new Array(toBePatched);
for (let index = 0; index < newIndexToOldIndexMap.length; index++) {
newIndexToOldIndexMap[index] = -1;
}
for (i = s1; i <= e1; i++) {
const prevChild = c1[i];
const newIndex = keyToNewIndexMap.get(prevChild.key);
newIndexToOldIndexMap[newIndex] = i;
if (newIndex === undefined) {
hostRemove(prevChild.el);
}
else {
// console.log("新老节点都存在");
patch(prevChild, c2[newIndex], container);
}
}
for (i = e2; i >= s2; i--) {
const nextChild = c2[i];
if (newIndexToOldIndexMap[i] === -1) {
patch(null, c2[i], container);
}
else {
const anchor = i + 1 >= e2 + 1 ? null : c2[i + 1];
hostInsert(nextChild.el, container, anchor && anchor.el);
}
}
}
}