Skip to content

Latest commit

 

History

History
277 lines (237 loc) · 5.7 KB

第8章.md

File metadata and controls

277 lines (237 loc) · 5.7 KB

HTML Attributes 定义在 HTML 标签上的属性

DOM properties,dom 元素上的属性,很多都相似,但是类在 html 使 class,dom 上是 className

aria-*只有 html 属性

  <input value="foo">
  <!-- el.value就是输入框现在的值,但是el.getAttribute('value')始终是foo不会变,等同于el.defaultValue -->

  <input type="foo">
  <!-- html属性会校验修正不合法的值 -->
  <!-- console.log(el.type)   显示为text  -->

HTML 属性的作用是设置与之对应的 dom 属性初始值

以禁用按钮为例

<button disabled>Button</button>

const  button = {
  type:'button',
  props:{
    disabled:''
  }
}


<button :disabled="false">Button</button>
const  button = {
  type:'button',
  props:{
    disabled:false
  }
}
如果当成htm属性,注意html上的值全部会被当成字符串
el.setAttribute('disabled',false)
等价 el.setAttribute('disabled','false')
按钮会被禁用

解决,优先设置为 dom properties

function mountElement(vnode, container) {
  const el = document.createElement(vnode.type);
  if (vnode.props) {
    for (const key in vnode.props) {
      const value = vnode.props[key];
      if (key in el) {
        if (typeof value === "boolean" && value === "") {
          el[key] = true;
        } else {
          el[key] = value;
        }
      } else {
        el.setAttribute(key, value);
      }
    }
  }
}

设置 class 三种方式,el.setAttribute 性能最优

  • setAttribute
  • el.className
  • el.classList

卸载不能通过 innerHTML 直接清空

  • 没有调用 beforeUnmount、unmounted 等声明周期函数
  • 没有调用自定义指令的卸载钩子
  • 不会移除 dom 上的时间处理函数
function mountElement(vnode, container) {
  const el = (vnode.el = document.createElement(vnode.type));
}

function render(vnode, container) {
  if (vnode) {
    patch(container._vnode, vnode, container);
  } else {
    if (container._vnode) {
      unmount(vnode);
    }
  }
}
function unmount(vnode) {
  const el = vnode.el;
  const parent = el.parent;
  if (parent) parent.removeChild(el);
}

patch

function patch(n1,n2,container) {
  // type都不一致了,没有打补丁的必要
  if(n1&& n1.type !== n2.type) {
     unmount(n1)
     n1 = null
  }
  const {type} = n2
  if(type=== 'string') {
   if(!n1) {
     mountElement(n2,container)
   } else{
     patchElement(n1,n2)
   }
  // 对象,表明是组件
  } else if(type === 'object') {

  }
}

事件伪造

function patchProps(el, key, prevValue, nextValue) {
  if (/^on/.test(key)) {
    let invoker = el._vei;
    const name = key.slice(2).toLowerCase();
    if (nextValue) {
      if (!invoker) {
        invoker = el._vei = (e) => {
          invoker.value(e);
        };
        invoker.value = nextValue;
        el.addEventListener(name, invoker);
      } else {
        invoker.value = nextValue;
      }
    } else if (invoker) {
      el.removeEventListener(name, invoker);
    }
  }
  // ...
}

解决事件覆盖问题

let invokers = el._vei || (el._vei = {});
const name = key.slice(2).toLowerCase();
const invoker = invokers[name];

同一事件,也应该可以绑定多次,传递数组事件

let invoker = (el._vei = (e) => {
  if (Array.isArray(invoker.value)) {
    invoker.value.forEach((fn) => fn(e));
  }
});

解决事件提前执行

if (!invoker) {
  invoker = el._vei = (e) => {
    if (e.timeStamp < el.attached) return;
    invoker.value(e);
  };

  invoker.value = nextValue;
  el.addEventListener(name, invoker);
  el.attached = performance.now();
} else {
  invoker.value = nextValue;
}

patch

function patchElement(n1, n2) {
  const el = (n2.el = n1.el);
  const oldProps = n1.props;
  const newProps = n2.props;

  // 更新props
  for (const key of newProps) {
    if (newProps[key] !== oldProps[key]) {
      patchProps(el, key, oldProps[key], newProps[key]);
    }
  }
  for (const key of oldProps) {
    if (!(key in newProps)) {
      patchProps(el, key, oldProps[key], null);
    }
  }
  patchChildren(n1, n2, el);
}

function patchChildren(n1, n2, container) {
  if (typeof n2.children === "string") {
    if (Array.isArray(n1.children)) {
      n1.children.forEach((c) => unmount(c));
    }
    setElementText(container, n2.children);
  } else if (Array.isArray(n2.children)) {
    //diff算法核心,子节点都是一组子节点
    // 这里用简易算法
    if (Array.isArray(n1.children)) {
      n1.children.forEach((c) => unmount(c));
      n2.children.forEach((c) => patch(null, c, container));
    } else {
      //旧子节点为空或字符串
      setElementText(container, "");
      n2.children.forEach((c) => patch(null, c, container));
    }
  } else {
    // 新子节点不存在
    if (Array.isArray(n1.children)) {
      n1.children.forEach((c) => unmount(c));
    } else if (typeof n1.children === "string") {
      setElementText(container, "");
    }
  }
}

文本节点

const Text = Symbol();
const newVnode = {
  type: Text,
  children: "我是注释内容",
};

function patch(n1, n2, patch) {
  const { type } = n2;
  if (type === Text) {
    if (!n1) {
      const el = (n2.el = document.createTextNode(n2.children));
      insert(el, container);
    } else {
      const el = (n2.el = n1.el);
      el.nodeValue = n2.children;
    }
  }
}

虚拟节点

const Fragment = Symbol();
const vnode = {
  type: Fragment,
  children: [
    { type: "li", children: "text 1" },
    { type: "li", children: "text 2" },
  ],
};

function patch(n1, n2, container) {
  ///
  if ((type = Fragment)) {
    if (!n1) {
      n2.children.forEach((c) => patch(null, c, container));
    } else {
      patchChildren(n1, n2, container);
    }
  }
}