<html>
<head>
<meta charset="utf-8">
<title>React Hello World w/ JSBin</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
/** @jsx createElement */
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 });
}
let rootInstance = null;
function render(element, container) {
const prevInstance = rootInstance; //虚拟DOM的根节点
const nextInstance = reconcile(container, prevInstance, element); //对比DOM diff,并更新真实DOM
rootInstance = nextInstance; // 新的虚拟DOM根节点
}
function reconcile(parentDom, instance, element) {
if (instance == null) {
//虚拟的根节点为空时,使用当前React元素,创建新的虚拟DOM
const newInstance = instantiate(element);
//将真实DOM插入容器
parentDom.appendChild(newInstance.dom);
return newInstance;
} else if (element == null) {
//删除DOM
parentDom.removeChild(instance.dom);
return null;
} if (instance.element.type === element.type) {
//原有虚拟DOM节点类型与要创建的DOM节点类型一致时,可以重用dom以提升性能,只需要更新dom节点属性
updateDomProperties(instance.dom, instance.element.props, element.props);
//对instance子节点进行对比,以保证尽可能的重用DOM
instance.childInstances = reconcileChildren(instance, element);
instance.element = element;
return instance;
} else {
//使用当前React元素,创建新的虚拟DOM
const newInstance = instantiate(element);
//将真实DOM替换容器中的原有DOM
parentDom.replaceChild(newInstance.dom, instance.dom);
return newInstance;
}
}
function reconcileChildren(instance, element) {
// instance 旧
// element 新
const dom = instance.dom;
const childInstances = instance.childInstances;
const nextChildElements = element.props.children || [];
const newChildInstances = []; // 新的孩子数组
//选取新旧子节点数据组中最大的值
const count = Math.max(childInstances.length, nextChildElements.length);
for (let i = 0; i < count; i++) {
const childInstance = childInstances[i];
const childElement = nextChildElements[i];
//调用reconcile创建子节点的虚拟DOM
/*这里存在三种情况:
* (1). childInstance和childElement都存在,则调用reconcile进行diff操作
* (2). childInstance为空而childElement存在,调用调用reconcile创建新的instance
* (3). childInstance存在而childElement为空,则调用reconcile进行删除操作,此时会返回null
*/
const newChildInstance = reconcile(dom, childInstance, childElement);
newChildInstances.push(newChildInstance);
}
return newChildInstances.filter(instance => instance != null); //过滤null
}
function instantiate(element) {
const { type, props } = element;
const isTextElement = type === "TEXT ELEMENT";
const dom = isTextElement
? document.createTextNode("")
: document.createElement(type);
updateDomProperties(dom, [], props); //更新DOM节点的属性、绑定事件
//递归地调用instantiate函数,创建虚拟DOM的子节点
const childElements = props.children || [];
const childInstances = childElements.map(instantiate);
const childDoms = childInstances.map(childInstance => childInstance.dom);
childDoms.forEach(childDom => dom.appendChild(childDom));
const instance = { dom, element, childInstances };
return instance;
}
function updateDomProperties(dom, prevProps, nextProps) {
//判断是否为事件属性
const isEvent = name => name.startsWith("on");
//判断是否为普通属性
const isAttribute = name => !isEvent(name) && name != "children";
//移除原有DOM节点上绑定的事件
Object.keys(prevProps).filter(isEvent).forEach(name => {
const eventType = name.toLowerCase().substring(2);
dom.removeEventListener(eventType, prevProps[name]);
});
//移除原有DOM节点的普通属性
Object.keys(prevProps).filter(isAttribute).forEach(name => {
dom[name] = null;
});
//添加新属性
Object.keys(nextProps).filter(isAttribute).forEach(name => {
dom[name] = nextProps[name];
});
//添加新事件
Object.keys(nextProps).filter(isEvent).forEach(name => {
const eventType = name.toLowerCase().substring(2);
dom.addEventListener(eventType, nextProps[name]);
});
}
const list = [1, 1, 1]
function getElement() {
return (
<div id="container">
<a style="width: 40; height: 20; background-color: #FF0000" onClick={handleClick} >click</a>
{
list.map(item => {
return <div>{item}</div>
})
}
</div>
);
}
function handleClick() {
list.push(1)
render(getElement(), document.getElementById("root"));
}
render(getElement(), document.getElementById("root"));
Output
300px
You can jump to the latest bin by adding /latest
to your URL
Keyboard Shortcuts
Shortcut | Action |
---|---|
ctrl + [num] | Toggle nth panel |
ctrl + 0 | Close focused panel |
ctrl + enter | Re-render output. If console visible: run JS in console |
Ctrl + l | Clear the console |
ctrl + / | Toggle comment on selected lines |
ctrl + ] | Indents selected lines |
ctrl + [ | Unindents selected lines |
tab | Code complete & Emmet expand |
ctrl + shift + L | Beautify code in active panel |
ctrl + s | Save & lock current Bin from further changes |
ctrl + shift + s | Open the share options |
ctrl + y | Archive Bin |
Complete list of JS Bin shortcuts |
JS Bin URLs
URL | Action |
---|---|
/ | Show the full rendered output. This content will update in real time as it's updated from the /edit url. |
/edit | Edit the current bin |
/watch | Follow a Code Casting session |
/embed | Create an embeddable version of the bin |
/latest | Load the very latest bin (/latest goes in place of the revision) |
/[username]/last | View the last edited bin for this user |
/[username]/last/edit | Edit the last edited bin for this user |
/[username]/last/watch | Follow the Code Casting session for the latest bin for this user |
/quiet | Remove analytics and edit button from rendered output |
.js | Load only the JavaScript for a bin |
.css | Load only the CSS for a bin |
Except for username prefixed urls, the url may start with http://jsbin.com/abc and the url fragments can be added to the url to view it differently. |