console
const FeactInstanceMap = {
set(key, value) {
key.__feactInternalInstance = value;
},
get(key) {
return key.__feactInternalInstance;
},
};
function flatten(list, depth) {
depth = typeof depth == 'number' ? depth : Infinity;
if (!depth) {
if (Array.isArray(list)) {
return list.map(function(i) {
return i;
});
}
return list;
}
return _flatten(list, 1);
function _flatten(list, d) {
return list.reduce(function(acc, item) {
if (Array.isArray(item) && d < depth) {
return acc.concat(_flatten(item, d + 1));
} else {
return acc.concat(item);
}
}, []);
}
}
function shouldUpdateFeactComponent(prevElement, nextElement) {
if (prevElement != null && nextElement != null) {
var prevType = typeof prevElement;
var nextType = typeof nextElement;
if (prevType === 'string' || prevType === 'number') {
return nextType === 'string' || nextType === 'number';
} else {
return (
nextType === 'object' && prevElement.type === nextElement.type && prevElement.key === nextElement.key
);
}
}
return false;
}
function flattenChildren(children, init_value) {
let childrenMap = {};
init_value = init_value || '';
for (let i = 0; i < children.length; i++) {
let child = children[i];
let name = `.${i}`;
if (Array.isArray(child)) {
let flattendObject = flattenChildren(child, `.${i}`);
Object.assign(childrenMap, flattendObject);
continue;
}
if (child && child._currentElement && child._currentElement.key) {
name = `.$${child._currentElement.key}`;
}
if (child && child.key) {
name = `.$${child.key}`;
}
name = init_value + name;
childrenMap[name] = child;
}
return childrenMap;
}
class FeactEmptyComponent {
constructor(text) {
this._currentElement = text;
this._stringText = '' + text;
this._domNode = null;
this._hostParent = null;
this._hostContainerInfo = null;
}
mountComponent(hostParent, hostContainerInfo) {
this._hostParent = hostParent;
this._hostContainerInfo = hostContainerInfo;
this._domNode = document.createComment('feact --- empty --- component');
return this._domNode;
}
getHost() {
return this._domNode;
}
receiveComponent() {}
}
class FeactDOMTextComponent {
constructor(text) {
this._currentElement = text;
this._stringText = '' + text;
this._domNode = null;
this._hostParent = null;
this._hostContainerInfo = null;
}
mountComponent(hostParent, hostContainerInfo) {
this._hostParent = hostParent;
this._hostContainerInfo = hostContainerInfo;
this._domNode = document.createTextNode(this._currentElement);
return this._domNode;
}
getHost() {
return this._domNode;
}
receiveComponent(txt) {
this._domNode.data = txt;
}
}
function instantiateFeactComponentArrayItem(ele) {
if (Array.isArray(ele)) {
return ele.map((item, index) => {
return instantiateFeactComponent(item);
});
} else return instantiateFeactComponent(ele);
}
var updateDepth = 0;
var diffQueue = [];
var globalIdCounter = 1;
class FeactDOMComponent {
constructor(element) {
this._currentElement = element;
this._renderedChildren = null;
this._hostNode = null;
this._hostParent = null;
this._rootNodeID = 0;
this._domID = 0;
this._hostContainerInfo = null;
this._wrapperState = null;
this._topLevelWrapper = null;
}
getHost() {
return this._hostNode;
}
mountChildren(propschildren, el) {
var mountImages = [];
var index = 0;
var children = [];
let flattenpropschildren = propschildren;
for (let i = 0; i < flattenpropschildren.length; i++) {
let child = flattenpropschildren[i];
let vInstance = instantiateFeactComponentArrayItem(child);
children.push(vInstance);
}
let flattenddChildrenInst = flatten(children);
for (let j = 0; j < flattenddChildrenInst.length; j++) {
let vInstance = flattenddChildrenInst[j];
vInstance._mountIndex = j;
mountImages.push(FeactReconciler.mountComponent(vInstance, this));
}
this._renderedChildren = flattenChildren(children);
return mountImages;
}
_createInitialChildren(props, el) {
var mountImages = this.mountChildren(props.children, el);
for (var i = 0; i < mountImages.length; i++) {
el.appendChild(mountImages[i]);
}
}
mountComponent(hostParent, hostContainerInfo) {
this._hostParent = hostParent;
this._rootNodeID = globalIdCounter++;
this._domID = hostContainerInfo && hostContainerInfo._idCounter++;
const domElement = document.createElement(this._currentElement.type);
this._updateDOMProperties(null, this._currentElement.props);
this._createInitialChildren(this._currentElement.props, domElement);
FeactInstanceMap.set(domElement, this);
this._hostNode = domElement;
return domElement;
}
receiveComponent(nextElement) {
const prevElement = this._currentElement;
this.updateComponent(prevElement, nextElement);
}
updateComponent(prevElement, nextElement) {
const lastProps = prevElement.props;
const nextProps = nextElement.props;
this._updateDOMProperties(lastProps, nextProps);
this._updateDOMChildren(lastProps, nextProps);
}
updateChildren(prevChildren, nextChildrenE, diffQueue) {
let hostNode = this._hostNode;
let ob = {};
var nextChildren = {};
let nextChildrenElements = flattenChildren(nextChildrenE);
var mountImages = [];
var removedNodes = {};
for (let name in nextChildrenElements) {
let prevChild = prevChildren && prevChildren[name];
var prevElement = prevChild && prevChild._currentElement;
var nextElement = nextChildrenElements[name];
if (prevChild != null && shouldUpdateFeactComponent(prevElement, nextElement)) {
FeactReconciler.receiveComponent(prevChild, nextElement);
nextChildren[name] = prevChild;
} else {
var nextChildInstance = instantiateFeactComponent(nextElement);
nextChildren[name] = nextChildInstance;
var nextChildMountImage = FeactReconciler.mountComponent(nextChildInstance, this);
mountImages.push(nextChildMountImage);
if (prevChild) {
prevChild._instance && prevChild._instance.componentWillUnmount && prevChild._instance.componentWillUnmount();
removedNodes[name] = prevChild.getHost();
}
}
}
for (let name in prevChildren) {
if (prevChildren.hasOwnProperty(name) && !(nextChildren && nextChildren.hasOwnProperty(name))) {
let prevChild = prevChildren[name];
removedNodes[name] = prevChild.getHost();
}
}
var lastIndex = 0;
var nextMountIndex = 0;
var nextIndex = 0;
var lastPlaceNode = null;
debugger;
for (let name in nextChildren) {
let prevChild = prevChildren[name];
let nextChild = nextChildren[name];
if (prevChild == nextChild) {
prevChild._mountIndex < lastIndex &&
diffQueue.push({
parentNode: hostNode,
type: 'MOVE_EXISTING',
fromIndex: prevChild._mountIndex,
toIndex: nextIndex,
node: prevChild.getHost(),
afterNode: lastPlaceNode,
});
lastIndex = Math.max(prevChild._mountIndex, lastIndex);
} else {
if (prevChild) {
lastIndex = Math.max(prevChild._mountIndex, lastIndex);
}
diffQueue.push({
parentNode: hostNode,
type: 'INSERT_NODE',
fromIndex: null,
toIndex: nextIndex,
markup: mountImages[nextMountIndex++],
afterNode: lastPlaceNode,
});
}
nextChild._mountIndex = nextIndex;
nextIndex++;
lastPlaceNode = nextChild.getHost();
}
for (let name in removedNodes) {
if (removedNodes.hasOwnProperty(name)) {
diffQueue.push({
parentNode: hostNode,
type: 'DELETE_NODE',
fromIndex: prevChildren[name]._mountIndex,
toIndex: null,
node: removedNodes[name],
});
}
}
this._renderedChildren = nextChildren;
}
_updateDOMProperties(lastProps, nextProps) {
let eventReg = /^on[\w]*$/;
for (let propskey in nextProps) {
if (propskey.match(eventReg)) {
let eventHanlder = nextProps[propskey];
ReactEventEmitter.putListener(this._rootNodeID, propskey, eventHanlder);
}
}
}
_updateDOMChildren(lastProps, nextProps) {
let hostNode = this._hostNode;
const lastContent = lastProps.children;
const nextContent = nextProps.children;
let diffQueue = [];
this.updateChildren(this._renderedChildren, nextProps.children, diffQueue);
this._patch(diffQueue);
}
_patch(updates) {
console.log(updates);
var initialChildren = {};
var deleteChildren = [];
for (var k = 0; k < updates.length; k++) {
let update = updates[k];
switch (update.type) {
case 'INSERT_NODE':
insertChildAt(update.parentNode, update.markup, getNodeAfter(update.parentNode, update.afterNode));
break;
case 'MOVE_EXISTING':
insertChildAt(update.parentNode, update.node, getNodeAfter(update.parentNode, update.afterNode));
break;
case 'DELETE_NODE':
update.node.remove();
break;
}
}
}
updateTextContent(content) {
const node = this._hostNode;
node.textContent = content;
const firstChild = node.firstChild;
if (firstChild && firstChild === node.lastChild && firstChild.nodeType === 3) {
firstChild.nodeValue = content;
return;
}
node.textContent = content;
}
}
var ReactEventEmitter = {
listenerBank: {},
putListener: function putListener(id, registrationName, listener) {
var bankForRegistrationName = this.listenerBank[registrationName] || (this.listenerBank[registrationName] = {});
bankForRegistrationName[id] = listener;
},
getListener: function getListener(id, registrationName) {
return this.listenerBank[registrationName][id];
},
trapBubbledEvent: function trapBubbledEvent(topLevelEventType, element) {
var eventMap = {
onClick: 'click',
};
var baseEventType = eventMap[topLevelEventType];
element.addEventListener(baseEventType, this.dispatchEvent.bind(this, topLevelEventType));
},
dispatchEvent: function dispatchEvent(eventType, event) {
event.preventDefault();
let internalInst = FeactInstanceMap.get(event.target);
var parentArray = [];
var listenArray = [];
var parent = internalInst;
while (parent) {
parentArray.push(parent);
parent = parent._hostParent;
}
for (let i = 0; i < parentArray.length; i++) {
let instID = parentArray[i]._rootNodeID;
let handler = this.getListener(instID, eventType);
handler && listenArray.push(handler);
}
for (let i = 0; i < listenArray.length; i++) {
listenArray[i](event);
}
},
};
function insertChildAt(parentNode, childNode, index) {
if (typeof index != 'number') {
parentNode.insertBefore(childNode, index);
return;
}
var beforeChild = parentNode.childNodes[index];
beforeChild ? parentNode.insertBefore(childNode, beforeChild) : parentNode.append(childNode);
}
class FeactCompositeComponentWrapper {
constructor(element) {
this._currentElement = element;
this._rootNodeID = 0;
this._compositeType = null;
this._instance = null;
this._hostParent = null;
this._hostContainerInfo = null;
}
getHost() {
return this._renderedComponent.getHost();
}
mountComponent(hostParent, hostContainerInfo) {
this._hostParent = hostParent;
this._hostContainerInfo = hostContainerInfo;
const Component = this._currentElement.type;
const componentInstance = new Component(this._currentElement.props);
this._instance = componentInstance;
FeactInstanceMap.set(componentInstance, this);
if (componentInstance.componentWillMount) {
componentInstance.componentWillMount();
}
const markup = this.performInitialMount();
if (componentInstance.componentDidMount) {
componentInstance.componentDidMount();
}
return markup;
}
performInitialMount() {
const renderedElement = this._instance.render();
const child = instantiateFeactComponent(renderedElement);
this._renderedComponent = child;
return FeactReconciler.mountComponent(child, this);
}
performUpdateIfNecessary() {
this.updateComponent(this._currentElement, this._currentElement);
}
receiveComponent(nextElement) {
const prevElement = this._currentElement;
this.updateComponent(prevElement, nextElement);
}
updateComponent(prevElement, nextElement) {
this._updating = true;
const nextProps = nextElement.props;
const inst = this._instance;
const willReceive = prevElement !== nextElement;
if (willReceive && inst.componentWillReceiveProps) {
inst.componentWillReceiveProps(nextProps);
}
let shouldUpdate = true;
const nextState = this._processPendingState();
if (inst.shouldComponentUpdate) {
shouldUpdate = inst.shouldComponentUpdate(nextProps, nextState);
}
if (shouldUpdate) {
this._performComponentUpdate(nextElement, nextProps, nextState);
} else {
inst.props = nextProps;
}
this._updating = false;
}
unmountComponent() {
const inst = this._instance;
inst.componentWillUnmount && inst.componentWillUnmount();
}
_processPendingState() {
const inst = this._instance;
if (!this._pendingPartialState) {
return inst.state;
}
let nextState = inst.state;
for (let i = 0; i < this._pendingPartialState.length; ++i) {
nextState = Object.assign({}, nextState, this._pendingPartialState[i]);
}
this._pendingPartialState = null;
return nextState;
}
_performComponentUpdate(nextElement, nextProps, nextState) {
this._currentElement = nextElement;
const inst = this._instance;
var hasComponentDidUpdate = !!inst.componentDidUpdate;
var prevProps;
var prevState;
if (hasComponentDidUpdate) {
prevProps = inst.props;
prevState = inst.state;
}
inst.props = nextProps;
inst.state = nextState;
inst.componentWillUpdate && inst.componentWillUpdate(nextProps, nextState);
this._updateRenderedComponent();
inst.componentDidUpdate && inst.componentDidUpdate(prevProps, prevState);
}
_updateRenderedComponent() {
const prevComponentInstance = this._renderedComponent;
const inst = this._instance;
const prevRenderedElement = prevComponentInstance._currentElement;
const nextRenderedElement = inst.render();
if (shouldUpdateFeactComponent(prevRenderedElement, nextRenderedElement)) {
FeactReconciler.receiveComponent(prevComponentInstance, nextRenderedElement);
} else {
var oldHostNode = prevComponentInstance.getHost();
var child = instantiateFeactComponent(nextRenderedElement);
this._renderedComponent = child;
var nextMarkup = FeactReconciler.mountComponent(child, this);
let parent = oldHostNode.parentElement;
parent.innerHTML = '';
parent.append(nextMarkup);
FeactReconciler.unmountComponent(prevComponentInstance);
}
}
}
const TopLevelWrapper = function(props) {
this.props = props;
};
TopLevelWrapper.prototype.render = function() {
return this.props;
};
function instantiateFeactComponent(element) {
if (element == null || element == undefined) {
return new FeactEmptyComponent(element);
} else if (typeof element === 'string') {
return new FeactDOMTextComponent(element);
} else if (typeof element.type === 'string') {
return new FeactDOMComponent(element);
} else if (typeof element.type === 'function') {
return new FeactCompositeComponentWrapper(element);
}
}
const FeactReconciler = {
mountComponent(internalInstance, parentNode, hostContainerInfo) {
return internalInstance.mountComponent(parentNode, hostContainerInfo);
},
receiveComponent(internalInstance, nextElement) {
internalInstance.receiveComponent(nextElement);
},
performUpdateIfNecessary(internalInstance) {
internalInstance.performUpdateIfNecessary();
},
instantiateChildren(internalInstance, propschildren) {
internalInstance.instantiateChildren(propschildren);
},
unmountComponent(internalInstance) {
internalInstance.unmountComponent && internalInstance.unmountComponent();
}
};
function FeactComponent() {}
FeactComponent.prototype.setState = function(partialState) {
const internalInstance = FeactInstanceMap.get(this);
internalInstance._pendingPartialState = internalInstance._pendingPartialState || [];
internalInstance._pendingPartialState.push(partialState);
if (!internalInstance._updating) {
FeactReconciler.performUpdateIfNecessary(internalInstance);
}
};
function mixSpecIntoComponent(Constructor, spec) {
const proto = Constructor.prototype;
for (const key in spec) {
proto[key] = spec[key];
}
}
const Feact = {
createElement(type, props, ...children) {
const element = {
type,
props: props || {},
};
if (props && props.key) {
element.key = props.key;
}
if (children) {
element.props.children = children;
}
return element;
},
createClass(spec) {
function Constructor(props) {
this.props = props;
const initialState = this.getInitialState ? this.getInitialState() : null;
this.state = initialState;
}
Constructor.prototype = new FeactComponent();
mixSpecIntoComponent(Constructor, spec);
return Constructor;
},
render(element, container) {
const prevComponent = getTopLevelComponentInContainer(container);
if (prevComponent) {
return updateRootComponent(prevComponent, element);
} else {
return renderNewRootComponent(element, container);
}
},
};
function getTopLevelComponentInContainer(container) {
return container.__feactComponentInstance;
}
function renderNewRootComponent(element, container) {
const wrapperElement = Feact.createElement(TopLevelWrapper, element);
const componentInstance = new FeactCompositeComponentWrapper(wrapperElement);
const markUp = FeactReconciler.mountComponent(componentInstance, null);
container.__feactComponentInstance = componentInstance._renderedComponent;
container.appendChild(markUp);
ReactEventEmitter.trapBubbledEvent('onClick', document);
return markUp;
}
function updateRootComponent(prevComponent, nextElement) {
FeactReconciler.receiveComponent(prevComponent, nextElement);
}
const MyComponent = Feact.createClass({
componentWillMount() {
this.renderCount = 0;
},
getInitialState() {
return {
message: 'state from getInitialState',
};
},
componentWillReceiveProps(nextProps) {
console.log('will reiceive');
},
render() {
this.renderCount += 1;
return Feact.createElement(
'h1',
null,
'this is render ' +
this.renderCount +
', with state: ' +
this.state.message +
', and this prop: ' +
this.props.a +
'a::::' +
this.props.a,
...this.props.children
);
},
});
function getNodeAfter(parentNode, node) {
if (Array.isArray(node)) {
node = node[1];
}
return node ? node.nextSibling : parentNode.firstChild;
}
const MyComponent123 = Feact.createClass({
componentWillMount() {
this.renderCount = 0;
},
getInitialState() {
return {
message: 'state from getInitialState',
a: 0,
b: 1,
};
},
componentDidMount() {
setTimeout(() => {
this.setState({
message: 'frrffrfr',
a: 1,
b: 0,
});
}, 3000);
},
componentWillReceiveProps(nextProps) {
},
onClickBubble(e) {
console.log('事件冒泡了');
},
changeState(v, e) {
this.setState({
a: v,
});
},
render() {
this.renderCount += 1;
return Feact.createElement(
'h1', {
onClick: this.onClickBubble.bind(this),
}, [
'pure txt',
this.state.a ?
Feact.createElement('div', { key: this.state.a ? 'f' : 'd' }, 'list item1') :
Feact.createElement(
'div', { key: this.state.a ? 'd' : 'f' },
'list item2',
Feact.createElement('div', {}, 'list list item2')
),
this.state.a ?
Feact.createElement(
'div', { key: this.state.a ? 'd' : 'f' },
'list item2',
Feact.createElement('div', {}, 'list list item2')
) :
Feact.createElement('div', { key: this.state.a ? 'f' : 'd' }, 'list item1'),
],
this.state.a ?
Feact.createElement(
'h2', {
onClick: this.onClickBubble.bind(this),
},
'ttt'
) :
null,
this.state.a ?
Feact.createElement(
'p',
null,
'this is render ' +
this.renderCount +
', with state: ' +
this.state.message +
', and this prop: ' +
this.props.prop,
Feact.createElement('p', {}, 'p-third')
) :
Feact.createElement('section', {}, null),
Feact.createElement('button', { onClick: this.changeState.bind(this, 0) }, '恢复状态'),
Feact.createElement('button', { onClick: this.changeState.bind(this, 1) }, '设置状态'),
);
},
});
Feact.render(
Feact.createElement(MyComponent123, { prop: 'first prop' }, Feact.createElement('div', {}, 'fewroot')),
document.getElementById('app')
);
<div id="app"></div>