DOM review
在我们开始之前,我们先看看我们用到的DOM的API。
// Get and element by id
const domRoot = document.getElementById("root")
// Create a new element given a tag name
const domInput = document.createElement("input")
// set properties
domInput["type"] = "text";
domInput["value"] = "Hi world";
domInput["className"] = "my-class";
// listen to events
domInput.addEventListener("change", e => alert(e.target.value));
//create a text node
const domText = document.createTextNode("");
// set text node content
domText["nodeValue"] = "Foo";
// Append an element
domRoot.appendChild(domInp);
// Append a text node( same as previous)
domRoot.appendChild(domText);
注意我们使用 properties 而不是 attributes 来设置元素,因此只能允许有效的属性。
Didact Elements
我们使用纯 JS 对象来描述我们需要渲染什么。我们把它们叫做 Didact Elements. 这些元素包含2个属性: type 和 props. type可以是一个string 或者 一个函数,但我们目前只是会使用string。props 是一个可以为空但不为null的对象。 props 可能有children 属性,该属性是一个Didact元素的数组组成。
例如:
const element = {
type: "div",
props: {
id: "container",
children: [
{ type: "input", props: { value: "foo", type: "text" } },
{ type: "a", props: { href: "/bar" },
{ type: "span", props: {} },
]
}
}
用来描述这么一个DOM
<div id="container">
<input value="foo" type="text">
<a href="/bar"></a>
<span></span>
</div>
Didact 元素和React的元素很像。但你通常不会使用JS 对象来创建 React元素,一般你会使用 JSX 或者createElemnt.
Render DOM Elemnts
这一步是渲染一个元素以及它的子元素到 dom上。我们使用 render方法(与ReactDOM.render相同)接收一个element和一个dom容器。该方法也会根据元素中定义的子元素来创建dom的子树。
function render(element, parentDOM) {
const { type, props } = element;
const dom = document.createElement(type);
const childElements = props.children || [];
childElements.forEach(childElement => render(childElement, dom));
parentDom.appendChild(dom);
}
我们仍然少了properties以及event listeners. 我们来使用Object.keys来遍历props的属性名。
function render(element, parentDOM) {
...
const isListener = name => name.startWith("on");
Object.keys(props).filter(isListener).forEach(name=> {
const eventType = name.toLowerCase().substring(2);
dom.addEventListener(eventType, props[name]);
});
const isAttribute = name => !isListener(name) && name != "children";
Object.keys(props).filter(isAttribute).forEach(name => {
dom[name] = props[name];
});
...
}
Render DOM Text Node
render 方法还没有支持的是 text nodes, 首先我们需要定义 text 元如何显示。比如一个描述 Foo的元素应该类似下面这样:
const reactElement = {
type: "span",
props: {
children: ["Foo"]
}
}
但我们看child属性,我里面只有一个string, 如果我们参考 Didact 元素,所有元素都有 type和 props,我们将会有更少的 if, 所以 Didact 的Text元素将有一个type 为"TEXT ELEMENT" 并且世纪的值房再 nodeValue的属性
const textElement = {
type: "span",
props: {
children: [
{
type: "TEXT ELEMENT",
props: { nodeValue: "Foo" }
}
]
}
}
现在我们已经定义了如何渲染它们,不同之处在于其他元素使用createElement渲染,但text元素,使用createTextNode来创建。
const isTextElement = type === "TEXT ELEMENT";
const dom = isTextElement ? document.createTextNode(""): document.createElement(type);