Skip to content

Commit

Permalink
Add doc-templite , Copy source UI
Browse files Browse the repository at this point in the history
  • Loading branch information
chinanf-boy committed Sep 5, 2018
1 parent dbe51ac commit 4e09a2b
Show file tree
Hide file tree
Showing 7 changed files with 1,132 additions and 1,061 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
node_modules/
.DS_Store
.DS_Store
.doc-templite.js
221 changes: 221 additions & 0 deletions 1.Rendering-DOM-elements.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@


## 1. 渲染DOM元素

> 这个故事是我们一步一步构建自己版本的React的系列文章的一部分:
### 1.1 DOM审查

在我们开始之前,让我们回顾一下我们将使用的DOM API:

``` js
// Get an 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(domInput);
// Append a text node (same as previous)
domRoot.appendChild(domText);
```

> [>>> codepen.io](https://codepen.io/pomber/pen/aWBLJR)
请注意,我们正在设置[元素属性而不是属性](http://stackoverflow.com/questions/6003819/properties-and-attributes-in-html)。这意味着只允许有效的属性。

### 1.2 Didact元素

我们将使用普通的JS对象来描述需要渲染的东西。我们将它们称为`Didact Elements`

这些元素有两个必需的属性:`type``props`

- `type`可以是一个**{字符串string}**或一个**{函数function}**, 但我们将只使用-字符串-,直到我们在稍后的帖子中引入-组件-。

- `props`是可以为空的对象(但不为空)。`props`可能有一个`children`属性,它应该是一个`Didact元素`的数组。

> 我们会很多地使用`Didact Elements`,所以从现在开始我们只会称它们为**{元素element}**, 不要与`HTML element`混淆.
例如,像这样的一个元素:

``` js
const element = {
type: "div",
props: {
id: "container",
children: [
{ type: "input", props: { value: "foo", type: "text" } },
{ type: "a", props: { href: "/bar" } },
{ type: "span", props: {} }
]
}
};
```

描述这个dom:

``` html
<div id="container">
<input value="foo" type="text">
<a href="/bar"></a>
<span></span>
</div>
```

---

`Didact-元素``React-元素`非常相似。

但是通常你在使用`React`时不会创建`React-元素`作为JS对象,

你可能使用`JSX`或者甚至是`createElement`

我们将在`Didact`中做同样的事情,但我们将会在系列下一篇文章中描述-`createElement`-的代码。

---

### 1.3 渲染-DOM-元素

下一步是将元素及其子元素呈现给dom。

我们将使用一个`render`函数(相当于`ReactDOM.render`)接收一个元素和一个`dom容器`

该函数应该创建由`element`定义的`dom子树`并将其附加到`容器`中:

``` js
function render(element, parentDom) {
const { type, props } = element; // 获取类型 和 属性对象
const dom = document.createElement(type); // 创建-类型-element
const childElements = props.children || []; // 获取-孩子
childElements.forEach(childElement => render(childElement, dom)); // 每个孩子 都要加入-爸爸妈妈-的怀抱
//
parentDom.appendChild(dom); // 爸爸妈妈加入爷爷奶奶的怀抱
}
```

我们仍然缺少`属性``事件监听器`。让我们`props``Object.keys`函数`迭代`属性名称并相应地-设置-它们:

``` js
function render(element, parentDom) {
const { type, props } = element;
const dom = document.createElement(type);

const isListener = name => name.startsWith("on");
// 是否开头-on
Object.keys(props).filter(isListener).forEach(name => {
const eventType = name.toLowerCase().substring(2); // 取两位后
dom.addEventListener(eventType, props[name]);
});
// 每一个开头-on 的属性-对应-函数 props[name] - >用-dom-addEvent 接连

const isAttribute = name => !isListener(name) && name != "children";
// 不是-监听事件 和 不能是-孩子

Object.keys(props).filter(isAttribute).forEach(name => {
dom[name] = props[name];
});
// 过滤出来的属性 - 赋予 - > dom
const childElements = props.children || [];
childElements.forEach(childElement => render(childElement, dom));

parentDom.appendChild(dom);
}
```

### 1.4 渲染DOM文本节点

`render`函数不支持的一件事是`文本节点`。首先,我们需要定义文本元素的外观。例如,`<span>Foo</span>``React`中描述的元素如下所示:

``` js
const reactElement = {
type: "span",
props: {
children: ["Foo"] // 是孩子, 但也只是一个字符串
}
};
```

请注意,`children`,只是一个字符串 ,而不是另一个元素对象。

这违背了我们如何定义`Didact元素``children`应该是元素的数组和所有元素应该有`type``props`

如果我们遵循这些规则,我们将来会少一些`if`判断。

因此,`Didact Text Elements``type==“TEXT ELEMENT”`相等,实际文本将位于`nodeValue`属性中。

像这个:

``` js
const textElement = {
type: "span",
props: {
children: [
{
type: "TEXT ELEMENT", // 1
props: { nodeValue: "Foo" } // 2
}
]
}
};
```

现在我们已经规范了文本元素的数据结构,我们需要可以呈现它, 以便与其他元素一样,而区别也就是{`type: "TEXT ELEMENT"`}。

我们应该使用`createTextNode`,而不是使用`createElement`

就是这样,`nodeValue`将会像其他属性一样设置。

``` js
function render(element, parentDom) {
const { type, props } = element;

// Create DOM element
const isTextElement = type === "TEXT ELEMENT"; // 文本类型判定
const dom = isTextElement
? document.createTextNode("")
: document.createElement(type);

// Add event listeners
const isListener = name => name.startsWith("on");
Object.keys(props).filter(isListener).forEach(name => {
const eventType = name.toLowerCase().substring(2);
dom.addEventListener(eventType, props[name]);
});

// Set properties
const isAttribute = name => !isListener(name) && name != "children";
Object.keys(props).filter(isAttribute).forEach(name => {
dom[name] = props[name];
});

// Render children
const childElements = props.children || [];
childElements.forEach(childElement => render(childElement, dom));

// Append to parent
parentDom.appendChild(dom);
}
```

### 1.5 概要

我们创建了一个`render函数`,允许我们将`一个元素{element}及其子元素{children}`呈现给-DOM「`parentDom.appendChild(dom);`」。

接下来我们需要的是`createElement`的简单方法。

我们将在下一篇文章中做到这一点,在那里我们将让`JSX与Didact`一起工作。

如果您想尝试我们迄今为止编写的代码,请检查[codepen](https://codepen.io/pomber/pen/eWbwBq?editors=0010)。你也可以从[github回购中检查这个差异](https://github.com/hexacta/didact/commit/fc4d360d91a1e68f0442d39dbce5b9cca5a08f24)

---

下一篇文章:[Didact: Element creation and JSX {en}](https://engineering.hexacta.com/didact-element-creation-and-jsx-d05171c55c56) |-|_|🌟|[Didact:元素创建和JSX {zh}](#2-%E5%85%83%E7%B4%A0%E5%88%9B%E5%BB%BA%E5%92%8Cjsx)
136 changes: 136 additions & 0 deletions 2.JSX.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@

## 2. 元素创建和JSX

> 这个故事是我们一步一步构建自己版本的React的系列文章的一部分:
### 2.1 JSX

上次我们介绍了[Didact Elements](#1.2-didact元素),它是一种描述我们想要呈现给-DOM-的非常详细的方式`{数据结构}`

在这篇文章中,我们将看到如何使用`JSX`来简化元素的创建。

`JSX`提供了一些语法糖来创建元素。以便代替:

``` js
const element = {
type: "div",
props: {
id: "container",
children: [
{ type: "input", props: { value: "foo", type: "text" } },
{
type: "a",
props: {
href: "/bar",
children: [{ type: "TEXT ELEMENT", props: { nodeValue: "bar" } }]
}
},
{
type: "span",
props: {
onClick: e => alert("Hi"),
children: [{ type: "TEXT ELEMENT", props: { nodeValue: "click me" } }]
}
}
]
}
};
```

我们的代码可以是

``` js
const element = (
<div id="container">
<input value="foo" type="text" />
<a href="/bar">bar</a>
<span onClick={e => alert("Hi")}>click me</span>
</div>
);
```

如果你对`JSX`不熟悉,你可能会想知道最后一个片段是否是有效的`javascript:`它不是。

为了使浏览器的理解,需要的代码由预处理转化为-有效的JS,像babel[了解更多关于JSX阅读这篇文章由贾森·米勒](https://jasonformat.com/wtf-is-jsx/)

例如,`babel`从上面将JSX转换为:

``` js
const element = createElement(
"div",
{ id: "container" },
createElement("input", { value: "foo", type: "text" }),
createElement(
"a",
{ href: "/bar" },
"bar"
),
createElement(
"span",
{ onClick: e => alert("Hi") },
"click me"
)
);
```

> [>> babel repl ](https://babeljs.io/repl/#?babili=false&evaluate=true&lineWrap=false&presets=react&targets=&browsers=&builtIns=false&debug=false&code=%2F**%20%40jsx%20createElement%20*%2F%0A%0Aconst%20element%20%3D%20%28%0A%20%20%3Cdiv%20id%3D%22container%22%3E%0A%20%20%20%20%3Cinput%20value%3D%22foo%22%20type%3D%22text%22%20%2F%3E%0A%20%20%20%20%3Ca%20href%3D%22%2Fbar%22%3Ebar%3C%2Fa%3E%0A%20%20%20%20%3Cspan%20onClick%3D%7Be%20%3D%3E%20alert%28%22Hi%22%29%7D%3Eclick%20me%3C%2Fspan%3E%0A%20%20%3C%2Fdiv%3E%0A%29%3B)
我们需要添加到`Didact`中来支持`JSX`是一个`createElement`功能,这就是其余部分工作由-预处理器-完成的。

函数的第一个参数是`type`元素的第一个参数,第二个参数是元素的对象`props`,以及所有下面的参数`children`

`createElement`需要创建一个`props`对象,将其分配给第二个参数中的所有值,将该`children`属性设置为第二个参数后面的所有参数,然后返回一个对象`{}` - 带有 `{type, props }`。把它放到代码中更容易:

``` js
function createElement(type, config, ...args) {
const props = Object.assign({}, config);// 合并
const hasChildren = args.length > 0; // 孩子?
props.children = hasChildren ? [].concat(...args) : [];
return { type, props }; // Didact元素的数据结构-类型{type}与属性{props}
}
```

除了一件事情之外,这个函数运行良好:`文本元素`

文本-作为字符串-传递给`createElement`函数,`Didact`需要文本元素`type`以及`props`其余元素。

所以我们将`每个arg`转换为一个文本元素-一个规范的`Didact元素:

``` js
const TEXT_ELEMENT = "TEXT ELEMENT"; // 类型

function createElement(type, config, ...args) {
const props = Object.assign({}, config);
const hasChildren = args.length > 0;
const rawChildren = hasChildren ? [].concat(...args) : [];
props.children = rawChildren
.filter(c => c != null && c !== false)
.map(c => c instanceof Object ? c : createTextElement(c));
// 过滤-空-值, 剩下的-不属于-Object的值 -> createTextElement -> 变为 类型为TEXT_ELEMENT- Didact元素
return { type, props };
}

function createTextElement(value) {
// 规范数据
return createElement(TEXT_ELEMENT, { nodeValue: value });
}
```

我还筛选了要排除的子项列表`null,undefined并指出false`.

我们不会呈现这些子项,因此不需要`添加`它们`props.children`


### 2.2 概要

在这篇文章中我们没有给`Didact`增加任何实际的权力,但是我们现在有了改进的开发者体验,

因为我们可以使用`JSX`来定义元素。我已经[更新了上次的codepen](https://codepen.io/pomber/pen/xdmoWE?editors=0010)以包含来自这篇文章的代码。

请注意,`codepen`使用`babel来传输JSX`,开头的注释`/** @jsx createElement */`告诉`babel`使用函数。

您还可以检查[Github提交的更改。](https://github.com/hexacta/didact/commit/15010f8e7b8b54841d1e2dd9eacf7b3c06b1a24b)

---

在下一篇文章中,[Didact: Instances, reconciliation and virtual DOM](https://engineering.hexacta.com/didact-instances-reconciliation-and-virtual-dom-9316d650f1d0) |-|_|🌟| [我们介绍了Didact的虚拟DOM和协调算法以支持DOM更新](#3-%E5%AE%9E%E4%BE%8B-%E5%AF%B9%E6%AF%94%E5%92%8C%E8%99%9A%E6%8B%9Fdom)
Loading

0 comments on commit 4e09a2b

Please sign in to comment.