babel
在runtime时, babel转译器将对每一个节点都执行在编译注释(Pragma)中声明的函数。
例如:
before babel: (the code you write)
1 | /** @jsx h */ |
after babel: (the code you run)
1 | var foo = h('div', {id:"foo"}, 'Hello!'); |
也可以在浏览器里打印看一下结果: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
<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>Document</title>
</head>
<body>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<script type="text/jsx" data-presets="es2016,react">
/** @jsx h */
let foo = <div id="foo">Hello!</div>;
const h = (type, attributes, ...args) => {
let children = args.length ? [].concat(...args) : null;
return { nodeName, attributes, children };
}
console.log(foo);
// foo: {
// attributes: {id: "foo"}
// children: ["Hello!"]
// nodeName: "div"
// }
</script>
</body>
</html>
createElement
将jsx中的element转换成 plain object1
2
3
4
5
6
7
8
9
10
11
12
13function 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));
return { type, props };
}
function createTextElement(value) {
return createElement(TEXT_ELEMENT, { nodeValue: value });
}
这里对text node特殊处理了一下,使其不那么特殊, 如此后面处理时省略了许多判断
instance
每个element(jsx转换而来的)对应一个instance,instance包含了dom, element, childInstances。
如果是custom component 则还有一个publicInstance,是组件的实例.
1 | function instantiate(element) { |
createPublicInstance
1 | function createPublicInstance(element, internalInstance) { |
这里的internalInstance其实就是 instance,之所以需要将其绑定到组件实例上是因为
在自定义组件中update需要用到,这里可以先看一下Component的setState实现1
2
3
4
5
6
7
8
9
10setState(partialState) {
this.state = Object.assign({}, this.state, partialState);
updateInstance(this.__internalInstance);
}
function updateInstance(internalInstance) {
const parentDom = internalInstance.dom.parentNode;
const element = internalInstance.element;
reconcile(parentDom, internalInstance, element);
}
reconciliation 对组件进行 diff
下面instance是当前的状态,而element是新的状态,将这两者进行diff,就可以得出如何更新dom节点
1 | function reconcile(parentDom, instance, element) { |
Component
由于之前已经讲过了setState,其他部分就很简单了1
2
3
4
5
6
7
8
9
10
11class Component {
constructor(props) {
this.props = props;
this.state = this.state || {};
}
setState(partialState) {
this.state = Object.assign({}, this.state, partialState);
updateInstance(this.__internalInstance);
}
}
render
1 | function render(element, container) { |
一个完整的例子
1 |
|
总结
本文是对文章Didact: a DIY guide to build your own React 的总结。旨在帮助我在阅读react源码之前先整体把握react的结构组成,帮助我更好的阅读源码。由于react16大量更新了代码,引入了Fiber:Incremental reconciliation,下篇看一下fiber的简单的实现。是同一位作者所写。