From 3bcd5603e95ab07db9fc8781e385729f1e64058f Mon Sep 17 00:00:00 2001 From: "waliang.wang" Date: Wed, 27 Sep 2017 19:28:42 +0800 Subject: [PATCH 01/57] =?UTF-8?q?opti:=20Spin=E6=A8=A1=E5=9D=97=20loading?= =?UTF-8?q?=E5=8A=A8=E6=80=81=E5=8A=A0=E8=BD=BDbug=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dist/React.js | 2 +- dist/ReactIE.js | 2 +- dist/ReactSelection.js | 2 +- dist/ReactShim.js | 2 +- src/diff.js | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/dist/React.js b/dist/React.js index d4863d2e4..6267ca36c 100644 --- a/dist/React.js +++ b/dist/React.js @@ -2301,7 +2301,7 @@ function updateElement(lastVnode, nextVnode, vparent, context, mountQueue) { }); list.length = 0; } else { - if (lastProps[innerHTML]) { + if (lastProps[innerHTML] || lastProps.children.length) { while (dom.firstChild) { dom.removeChild(dom.firstChild); } diff --git a/dist/ReactIE.js b/dist/ReactIE.js index 13fbbeea5..addbbefac 100644 --- a/dist/ReactIE.js +++ b/dist/ReactIE.js @@ -2300,7 +2300,7 @@ function updateElement(lastVnode, nextVnode, vparent, context, mountQueue) { }); list.length = 0; } else { - if (lastProps[innerHTML]) { + if (lastProps[innerHTML] || lastProps.children.length) { while (dom.firstChild) { dom.removeChild(dom.firstChild); } diff --git a/dist/ReactSelection.js b/dist/ReactSelection.js index 13fbbeea5..addbbefac 100644 --- a/dist/ReactSelection.js +++ b/dist/ReactSelection.js @@ -2300,7 +2300,7 @@ function updateElement(lastVnode, nextVnode, vparent, context, mountQueue) { }); list.length = 0; } else { - if (lastProps[innerHTML]) { + if (lastProps[innerHTML] || lastProps.children.length) { while (dom.firstChild) { dom.removeChild(dom.firstChild); } diff --git a/dist/ReactShim.js b/dist/ReactShim.js index 869dbc1f7..defee8f68 100644 --- a/dist/ReactShim.js +++ b/dist/ReactShim.js @@ -2142,7 +2142,7 @@ function updateElement(lastVnode, nextVnode, vparent, context, mountQueue) { }); list.length = 0; } else { - if (lastProps[innerHTML]) { + if (lastProps[innerHTML] || lastProps.children.length) { while (dom.firstChild) { dom.removeChild(dom.firstChild); } diff --git a/src/diff.js b/src/diff.js index 3e7814e0a..fa7782b2d 100644 --- a/src/diff.js +++ b/src/diff.js @@ -478,7 +478,7 @@ function updateElement(lastVnode, nextVnode, vparent, context, mountQueue) { }); list.length = 0; } else { - if (lastProps[innerHTML]) { + if (lastProps[innerHTML] || lastProps.children.length) { while (dom.firstChild) { dom.removeChild(dom.firstChild); } From a98c3647524e85ce7b9a87850e4b84c83f1db7e2 Mon Sep 17 00:00:00 2001 From: "waliang.wang" Date: Thu, 28 Sep 2017 10:45:27 +0800 Subject: [PATCH 02/57] =?UTF-8?q?opti:=20=E4=BF=AE=E6=94=B9.gitignore?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 7c62a4581..365196f57 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ /node_modules/* /md/* /coverage/ -package-lock.json \ No newline at end of file +package-lock.json +node_modules/ \ No newline at end of file From b6009fdae616b39bc502a5ca0223c2b469799dda Mon Sep 17 00:00:00 2001 From: RubyLouvre <1669866773@qq.com> Date: Thu, 28 Sep 2017 12:02:44 +0800 Subject: [PATCH 03/57] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20clearRefsAndMounts?= =?UTF-8?q?=20BUG?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dist/React.js | 54 ++++++------- dist/ReactIE.js | 54 ++++++------- dist/ReactSelection.js | 54 ++++++------- dist/ReactShim.js | 54 ++++++------- index5.html | 68 +++++++++------- src/Component.js | 1 + src/browser.js | 2 +- src/diff.js | 60 +++++++------- src/dispose.js | 6 +- test/modules/ReactComponentLifeCycle-test.jsx | 79 +++++++++++++++++++ test/spec.js | 1 + version.md | 4 +- 12 files changed, 256 insertions(+), 181 deletions(-) create mode 100644 test/modules/ReactComponentLifeCycle-test.jsx diff --git a/dist/React.js b/dist/React.js index 600f2c891..80b43b5ac 100644 --- a/dist/React.js +++ b/dist/React.js @@ -1,5 +1,5 @@ /** - * by 司徒正美 Copyright 2017-09-27 + * by 司徒正美 Copyright 2017-09-28 * IE9+ */ @@ -1026,6 +1026,7 @@ function Component(props, context) { this.__pendingCallbacks = []; this.__pendingStates = []; this.__current = noop; //用于DevTools工具中,通过实例找到生成它的那个虚拟DOM + this.__lifestage = 0; //判断生命周期 /* * this.__dom = dom 用于isMounted或ReactDOM.findDOMNode方法 * this.__hydrating = true 表示组件正在根据虚拟DOM合成真实DOM @@ -1670,10 +1671,10 @@ function disposeElement(vnode) { } function disposeComponent(vnode) { + var instance = vnode._instance; if (instance) { options.beforeUnmount(instance); - var dom = instance.__dom; instance.__current = instance.setState = instance.forceUpdate = noop; if (vnode.ref) { vnode.ref(null); @@ -1682,10 +1683,6 @@ function disposeComponent(vnode) { instance.componentWillUnmount(); } //在执行componentWillUnmount后才将关联的元素节点解绑,防止用户在钩子里调用 findDOMNode方法 - if (dom) { - dom.__component = null; - } - vnode.ref = instance.__dom = vnode._instance = null; disposeVnode(instance.__rendered); } @@ -1891,17 +1888,11 @@ function unstable_renderSubtreeIntoContainer(lastVnode, nextVnode, container, ca } //[Top API] ReactDOM.unmountComponentAtNode function unmountComponentAtNode(container) { - var context = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - var lastVnode = container.__component; if (lastVnode) { - // lastVnode._hostNode = container.firstChild; - var nextVnode = { - type: "#comment", - text: "empty", - vtype: 0 - }; - alignVnode(lastVnode, nextVnode, context, getVParent(container), []); + disposeVnode(lastVnode); + emptyElement(container); + container.__component = null; } } //[Top API] ReactDOM.findDOMNode @@ -2121,7 +2112,6 @@ function mountComponent(lastNode, vnode, vparent, parentContext, mountQueue) { updateInstanceChain(instance, dom); mountQueue.push(instance); - return dom; } function mountStateless(lastNode, vnode, vparent, parentContext, mountQueue) { @@ -2258,7 +2248,6 @@ function _refreshComponent(instance, mountQueue) { instance.lastContext = lastContext; //这里会更新instance的props, context, state var nextRendered = renderComponent.call(instance, nextVnode, nextProps, nextContext, nextState); - if (lastRendered !== nextRendered && parentContext) { dom = alignVnode(lastRendered, nextRendered, vparent, getChildContext(instance, parentContext), mountQueue); } @@ -2266,10 +2255,10 @@ function _refreshComponent(instance, mountQueue) { updateInstanceChain(instance, dom); instance.__lifeStage = 2; + instance.__hydrating = false; if (mountQueue.isChildProcess) { clearRefsAndMounts(mountQueue); } - instance.__hydrating = false; return dom; } @@ -2281,18 +2270,18 @@ function alignVnode(lastVnode, nextVnode, vparent, context, mountQueue) { dom = updateVnode(lastVnode, nextVnode, vparent, context, mountQueue); } else { disposeVnode(lastVnode); - // let innerMountQueue = mountQueue.executor - // ? mountQueue - // : nextVnode.vtype === 2 ? [] : mountQueue; + // let innerMountQueue = mountQueue.isChildProcess + // ? mountQueue + // : nextVnode.vtype === 2 ? [] : mountQueue; dom = mountVnode(null, nextVnode, vparent, context, mountQueue); var p = node.parentNode; if (p) { p.replaceChild(dom, node); removeDOMElement(node); } - // if (innerMountQueue !== mountQueue) { - // clearRefsAndMounts(innerMountQueue); - // } + // if (innerMountQueue !== mountQueue) { + // clearRefsAndMounts(innerMountQueue); + // } } return dom; } @@ -2337,13 +2326,16 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, mountQueue) { nextChildren = flattenChildren(nextVnode), nextLength = nextChildren.length, lastLength = lastChildren.length; + //如果旧数组长度为零 if (nextLength && !lastLength) { - nextChildren.forEach(function (vnode) { + return nextChildren.forEach(function (vnode) { var curNode = mountVnode(null, vnode, lastVnode, context, mountQueue); parentNode.appendChild(curNode); }); - return; + } + if (nextLength === lastLength && lastLength === 1) { + return alignVnode(lastChildren[0], nextChildren[0], lastVnode, context, mountQueue); } var maxLength = Math.max(nextLength, lastLength), insertPoint = parentNode.firstChild, @@ -2498,7 +2490,11 @@ function clearRefsAndMounts(queue) { //先执行所有refs方法(从上到下) clearRefs(); //再执行所有mount/update钩子(从下到上) - queue.forEach(function (instance) { + var i = 0; + while (i < queue.length) { + //queue可能中途加入新元素, 因此不能直接使用queue.forEach(fn) + var instance = queue[i]; + i++; if (!instance.__lifeStage) { if (instance.componentDidMount) { instance.componentDidMount(); @@ -2513,12 +2509,12 @@ function clearRefsAndMounts(queue) { if (ref) { ref(instance.__mergeStates ? instance : null); } - instance.__hydrating = false; + instance.__hydrating = false; //子树已经构建完毕 while (instance.__renderInNextCycle) { _refreshComponent(instance, queue); callUpdate(instance); } - }); + } //再执行所有setState/forceUpdate回调,根据从下到上的顺序执行 queue.sort(mountSorter).forEach(function (instance) { clearArray(instance.__pendingCallbacks).forEach(function (fn) { diff --git a/dist/ReactIE.js b/dist/ReactIE.js index 377d27419..4218f9458 100644 --- a/dist/ReactIE.js +++ b/dist/ReactIE.js @@ -1,5 +1,5 @@ /** - * IE6+,有问题请加QQ 370262116 by 司徒正美 Copyright 2017-09-27 + * IE6+,有问题请加QQ 370262116 by 司徒正美 Copyright 2017-09-28 */ (function (global, factory) { @@ -1025,6 +1025,7 @@ function Component(props, context) { this.__pendingCallbacks = []; this.__pendingStates = []; this.__current = noop; //用于DevTools工具中,通过实例找到生成它的那个虚拟DOM + this.__lifestage = 0; //判断生命周期 /* * this.__dom = dom 用于isMounted或ReactDOM.findDOMNode方法 * this.__hydrating = true 表示组件正在根据虚拟DOM合成真实DOM @@ -1669,10 +1670,10 @@ function disposeElement(vnode) { } function disposeComponent(vnode) { + var instance = vnode._instance; if (instance) { options.beforeUnmount(instance); - var dom = instance.__dom; instance.__current = instance.setState = instance.forceUpdate = noop; if (vnode.ref) { vnode.ref(null); @@ -1681,10 +1682,6 @@ function disposeComponent(vnode) { instance.componentWillUnmount(); } //在执行componentWillUnmount后才将关联的元素节点解绑,防止用户在钩子里调用 findDOMNode方法 - if (dom) { - dom.__component = null; - } - vnode.ref = instance.__dom = vnode._instance = null; disposeVnode(instance.__rendered); } @@ -1890,17 +1887,11 @@ function unstable_renderSubtreeIntoContainer(lastVnode, nextVnode, container, ca } //[Top API] ReactDOM.unmountComponentAtNode function unmountComponentAtNode(container) { - var context = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - var lastVnode = container.__component; if (lastVnode) { - // lastVnode._hostNode = container.firstChild; - var nextVnode = { - type: "#comment", - text: "empty", - vtype: 0 - }; - alignVnode(lastVnode, nextVnode, context, getVParent(container), []); + disposeVnode(lastVnode); + emptyElement(container); + container.__component = null; } } //[Top API] ReactDOM.findDOMNode @@ -2120,7 +2111,6 @@ function mountComponent(lastNode, vnode, vparent, parentContext, mountQueue) { updateInstanceChain(instance, dom); mountQueue.push(instance); - return dom; } function mountStateless(lastNode, vnode, vparent, parentContext, mountQueue) { @@ -2257,7 +2247,6 @@ function _refreshComponent(instance, mountQueue) { instance.lastContext = lastContext; //这里会更新instance的props, context, state var nextRendered = renderComponent.call(instance, nextVnode, nextProps, nextContext, nextState); - if (lastRendered !== nextRendered && parentContext) { dom = alignVnode(lastRendered, nextRendered, vparent, getChildContext(instance, parentContext), mountQueue); } @@ -2265,10 +2254,10 @@ function _refreshComponent(instance, mountQueue) { updateInstanceChain(instance, dom); instance.__lifeStage = 2; + instance.__hydrating = false; if (mountQueue.isChildProcess) { clearRefsAndMounts(mountQueue); } - instance.__hydrating = false; return dom; } @@ -2280,18 +2269,18 @@ function alignVnode(lastVnode, nextVnode, vparent, context, mountQueue) { dom = updateVnode(lastVnode, nextVnode, vparent, context, mountQueue); } else { disposeVnode(lastVnode); - // let innerMountQueue = mountQueue.executor - // ? mountQueue - // : nextVnode.vtype === 2 ? [] : mountQueue; + // let innerMountQueue = mountQueue.isChildProcess + // ? mountQueue + // : nextVnode.vtype === 2 ? [] : mountQueue; dom = mountVnode(null, nextVnode, vparent, context, mountQueue); var p = node.parentNode; if (p) { p.replaceChild(dom, node); removeDOMElement(node); } - // if (innerMountQueue !== mountQueue) { - // clearRefsAndMounts(innerMountQueue); - // } + // if (innerMountQueue !== mountQueue) { + // clearRefsAndMounts(innerMountQueue); + // } } return dom; } @@ -2336,13 +2325,16 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, mountQueue) { nextChildren = flattenChildren(nextVnode), nextLength = nextChildren.length, lastLength = lastChildren.length; + //如果旧数组长度为零 if (nextLength && !lastLength) { - nextChildren.forEach(function (vnode) { + return nextChildren.forEach(function (vnode) { var curNode = mountVnode(null, vnode, lastVnode, context, mountQueue); parentNode.appendChild(curNode); }); - return; + } + if (nextLength === lastLength && lastLength === 1) { + return alignVnode(lastChildren[0], nextChildren[0], lastVnode, context, mountQueue); } var maxLength = Math.max(nextLength, lastLength), insertPoint = parentNode.firstChild, @@ -2497,7 +2489,11 @@ function clearRefsAndMounts(queue) { //先执行所有refs方法(从上到下) clearRefs(); //再执行所有mount/update钩子(从下到上) - queue.forEach(function (instance) { + var i = 0; + while (i < queue.length) { + //queue可能中途加入新元素, 因此不能直接使用queue.forEach(fn) + var instance = queue[i]; + i++; if (!instance.__lifeStage) { if (instance.componentDidMount) { instance.componentDidMount(); @@ -2512,12 +2508,12 @@ function clearRefsAndMounts(queue) { if (ref) { ref(instance.__mergeStates ? instance : null); } - instance.__hydrating = false; + instance.__hydrating = false; //子树已经构建完毕 while (instance.__renderInNextCycle) { _refreshComponent(instance, queue); callUpdate(instance); } - }); + } //再执行所有setState/forceUpdate回调,根据从下到上的顺序执行 queue.sort(mountSorter).forEach(function (instance) { clearArray(instance.__pendingCallbacks).forEach(function (fn) { diff --git a/dist/ReactSelection.js b/dist/ReactSelection.js index 377d27419..4218f9458 100644 --- a/dist/ReactSelection.js +++ b/dist/ReactSelection.js @@ -1,5 +1,5 @@ /** - * IE6+,有问题请加QQ 370262116 by 司徒正美 Copyright 2017-09-27 + * IE6+,有问题请加QQ 370262116 by 司徒正美 Copyright 2017-09-28 */ (function (global, factory) { @@ -1025,6 +1025,7 @@ function Component(props, context) { this.__pendingCallbacks = []; this.__pendingStates = []; this.__current = noop; //用于DevTools工具中,通过实例找到生成它的那个虚拟DOM + this.__lifestage = 0; //判断生命周期 /* * this.__dom = dom 用于isMounted或ReactDOM.findDOMNode方法 * this.__hydrating = true 表示组件正在根据虚拟DOM合成真实DOM @@ -1669,10 +1670,10 @@ function disposeElement(vnode) { } function disposeComponent(vnode) { + var instance = vnode._instance; if (instance) { options.beforeUnmount(instance); - var dom = instance.__dom; instance.__current = instance.setState = instance.forceUpdate = noop; if (vnode.ref) { vnode.ref(null); @@ -1681,10 +1682,6 @@ function disposeComponent(vnode) { instance.componentWillUnmount(); } //在执行componentWillUnmount后才将关联的元素节点解绑,防止用户在钩子里调用 findDOMNode方法 - if (dom) { - dom.__component = null; - } - vnode.ref = instance.__dom = vnode._instance = null; disposeVnode(instance.__rendered); } @@ -1890,17 +1887,11 @@ function unstable_renderSubtreeIntoContainer(lastVnode, nextVnode, container, ca } //[Top API] ReactDOM.unmountComponentAtNode function unmountComponentAtNode(container) { - var context = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - var lastVnode = container.__component; if (lastVnode) { - // lastVnode._hostNode = container.firstChild; - var nextVnode = { - type: "#comment", - text: "empty", - vtype: 0 - }; - alignVnode(lastVnode, nextVnode, context, getVParent(container), []); + disposeVnode(lastVnode); + emptyElement(container); + container.__component = null; } } //[Top API] ReactDOM.findDOMNode @@ -2120,7 +2111,6 @@ function mountComponent(lastNode, vnode, vparent, parentContext, mountQueue) { updateInstanceChain(instance, dom); mountQueue.push(instance); - return dom; } function mountStateless(lastNode, vnode, vparent, parentContext, mountQueue) { @@ -2257,7 +2247,6 @@ function _refreshComponent(instance, mountQueue) { instance.lastContext = lastContext; //这里会更新instance的props, context, state var nextRendered = renderComponent.call(instance, nextVnode, nextProps, nextContext, nextState); - if (lastRendered !== nextRendered && parentContext) { dom = alignVnode(lastRendered, nextRendered, vparent, getChildContext(instance, parentContext), mountQueue); } @@ -2265,10 +2254,10 @@ function _refreshComponent(instance, mountQueue) { updateInstanceChain(instance, dom); instance.__lifeStage = 2; + instance.__hydrating = false; if (mountQueue.isChildProcess) { clearRefsAndMounts(mountQueue); } - instance.__hydrating = false; return dom; } @@ -2280,18 +2269,18 @@ function alignVnode(lastVnode, nextVnode, vparent, context, mountQueue) { dom = updateVnode(lastVnode, nextVnode, vparent, context, mountQueue); } else { disposeVnode(lastVnode); - // let innerMountQueue = mountQueue.executor - // ? mountQueue - // : nextVnode.vtype === 2 ? [] : mountQueue; + // let innerMountQueue = mountQueue.isChildProcess + // ? mountQueue + // : nextVnode.vtype === 2 ? [] : mountQueue; dom = mountVnode(null, nextVnode, vparent, context, mountQueue); var p = node.parentNode; if (p) { p.replaceChild(dom, node); removeDOMElement(node); } - // if (innerMountQueue !== mountQueue) { - // clearRefsAndMounts(innerMountQueue); - // } + // if (innerMountQueue !== mountQueue) { + // clearRefsAndMounts(innerMountQueue); + // } } return dom; } @@ -2336,13 +2325,16 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, mountQueue) { nextChildren = flattenChildren(nextVnode), nextLength = nextChildren.length, lastLength = lastChildren.length; + //如果旧数组长度为零 if (nextLength && !lastLength) { - nextChildren.forEach(function (vnode) { + return nextChildren.forEach(function (vnode) { var curNode = mountVnode(null, vnode, lastVnode, context, mountQueue); parentNode.appendChild(curNode); }); - return; + } + if (nextLength === lastLength && lastLength === 1) { + return alignVnode(lastChildren[0], nextChildren[0], lastVnode, context, mountQueue); } var maxLength = Math.max(nextLength, lastLength), insertPoint = parentNode.firstChild, @@ -2497,7 +2489,11 @@ function clearRefsAndMounts(queue) { //先执行所有refs方法(从上到下) clearRefs(); //再执行所有mount/update钩子(从下到上) - queue.forEach(function (instance) { + var i = 0; + while (i < queue.length) { + //queue可能中途加入新元素, 因此不能直接使用queue.forEach(fn) + var instance = queue[i]; + i++; if (!instance.__lifeStage) { if (instance.componentDidMount) { instance.componentDidMount(); @@ -2512,12 +2508,12 @@ function clearRefsAndMounts(queue) { if (ref) { ref(instance.__mergeStates ? instance : null); } - instance.__hydrating = false; + instance.__hydrating = false; //子树已经构建完毕 while (instance.__renderInNextCycle) { _refreshComponent(instance, queue); callUpdate(instance); } - }); + } //再执行所有setState/forceUpdate回调,根据从下到上的顺序执行 queue.sort(mountSorter).forEach(function (instance) { clearArray(instance.__pendingCallbacks).forEach(function (fn) { diff --git a/dist/ReactShim.js b/dist/ReactShim.js index e209b2650..ada6adab3 100644 --- a/dist/ReactShim.js +++ b/dist/ReactShim.js @@ -1,7 +1,7 @@ /** * 此版本要求浏览器没有createClass, createFactory, PropTypes, isValidElement, * unmountComponentAtNode,unstable_renderSubtreeIntoContainer - * QQ 370262116 by 司徒正美 Copyright 2017-09-27 + * QQ 370262116 by 司徒正美 Copyright 2017-09-28 */ (function (global, factory) { @@ -464,6 +464,7 @@ function Component(props, context) { this.__pendingCallbacks = []; this.__pendingStates = []; this.__current = noop; //用于DevTools工具中,通过实例找到生成它的那个虚拟DOM + this.__lifestage = 0; //判断生命周期 /* * this.__dom = dom 用于isMounted或ReactDOM.findDOMNode方法 * this.__hydrating = true 表示组件正在根据虚拟DOM合成真实DOM @@ -1515,10 +1516,10 @@ function disposeElement(vnode) { } function disposeComponent(vnode) { + var instance = vnode._instance; if (instance) { options.beforeUnmount(instance); - var dom = instance.__dom; instance.__current = instance.setState = instance.forceUpdate = noop; if (vnode.ref) { vnode.ref(null); @@ -1527,10 +1528,6 @@ function disposeComponent(vnode) { instance.componentWillUnmount(); } //在执行componentWillUnmount后才将关联的元素节点解绑,防止用户在钩子里调用 findDOMNode方法 - if (dom) { - dom.__component = null; - } - vnode.ref = instance.__dom = vnode._instance = null; disposeVnode(instance.__rendered); } @@ -1732,17 +1729,11 @@ function render(vnode, container, callback) { //[Top API] ReactDOM.unmountComponentAtNode function unmountComponentAtNode(container) { - var context = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - var lastVnode = container.__component; if (lastVnode) { - // lastVnode._hostNode = container.firstChild; - var nextVnode = { - type: "#comment", - text: "empty", - vtype: 0 - }; - alignVnode(lastVnode, nextVnode, context, getVParent(container), []); + disposeVnode(lastVnode); + emptyElement(container); + container.__component = null; } } //[Top API] ReactDOM.findDOMNode @@ -1962,7 +1953,6 @@ function mountComponent(lastNode, vnode, vparent, parentContext, mountQueue) { updateInstanceChain(instance, dom); mountQueue.push(instance); - return dom; } function mountStateless(lastNode, vnode, vparent, parentContext, mountQueue) { @@ -2099,7 +2089,6 @@ function _refreshComponent(instance, mountQueue) { instance.lastContext = lastContext; //这里会更新instance的props, context, state var nextRendered = renderComponent.call(instance, nextVnode, nextProps, nextContext, nextState); - if (lastRendered !== nextRendered && parentContext) { dom = alignVnode(lastRendered, nextRendered, vparent, getChildContext(instance, parentContext), mountQueue); } @@ -2107,10 +2096,10 @@ function _refreshComponent(instance, mountQueue) { updateInstanceChain(instance, dom); instance.__lifeStage = 2; + instance.__hydrating = false; if (mountQueue.isChildProcess) { clearRefsAndMounts(mountQueue); } - instance.__hydrating = false; return dom; } @@ -2122,18 +2111,18 @@ function alignVnode(lastVnode, nextVnode, vparent, context, mountQueue) { dom = updateVnode(lastVnode, nextVnode, vparent, context, mountQueue); } else { disposeVnode(lastVnode); - // let innerMountQueue = mountQueue.executor - // ? mountQueue - // : nextVnode.vtype === 2 ? [] : mountQueue; + // let innerMountQueue = mountQueue.isChildProcess + // ? mountQueue + // : nextVnode.vtype === 2 ? [] : mountQueue; dom = mountVnode(null, nextVnode, vparent, context, mountQueue); var p = node.parentNode; if (p) { p.replaceChild(dom, node); removeDOMElement(node); } - // if (innerMountQueue !== mountQueue) { - // clearRefsAndMounts(innerMountQueue); - // } + // if (innerMountQueue !== mountQueue) { + // clearRefsAndMounts(innerMountQueue); + // } } return dom; } @@ -2178,13 +2167,16 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, mountQueue) { nextChildren = flattenChildren(nextVnode), nextLength = nextChildren.length, lastLength = lastChildren.length; + //如果旧数组长度为零 if (nextLength && !lastLength) { - nextChildren.forEach(function (vnode) { + return nextChildren.forEach(function (vnode) { var curNode = mountVnode(null, vnode, lastVnode, context, mountQueue); parentNode.appendChild(curNode); }); - return; + } + if (nextLength === lastLength && lastLength === 1) { + return alignVnode(lastChildren[0], nextChildren[0], lastVnode, context, mountQueue); } var maxLength = Math.max(nextLength, lastLength), insertPoint = parentNode.firstChild, @@ -2339,7 +2331,11 @@ function clearRefsAndMounts(queue) { //先执行所有refs方法(从上到下) clearRefs(); //再执行所有mount/update钩子(从下到上) - queue.forEach(function (instance) { + var i = 0; + while (i < queue.length) { + //queue可能中途加入新元素, 因此不能直接使用queue.forEach(fn) + var instance = queue[i]; + i++; if (!instance.__lifeStage) { if (instance.componentDidMount) { instance.componentDidMount(); @@ -2354,12 +2350,12 @@ function clearRefsAndMounts(queue) { if (ref) { ref(instance.__mergeStates ? instance : null); } - instance.__hydrating = false; + instance.__hydrating = false; //子树已经构建完毕 while (instance.__renderInNextCycle) { _refreshComponent(instance, queue); callUpdate(instance); } - }); + } //再执行所有setState/forceUpdate回调,根据从下到上的顺序执行 queue.sort(mountSorter).forEach(function (instance) { clearArray(instance.__pendingCallbacks).forEach(function (fn) { diff --git a/index5.html b/index5.html index bebf4be2d..21d5de080 100644 --- a/index5.html +++ b/index5.html @@ -15,36 +15,50 @@ window.onload = function(){ var div = document.body - class App extends React.Component { - state = {aaa: 1} - componentDidMount(){ - this.setState({aaa: 1}, function(){ - console.log('App setState cb') - }) - console.log('App did mount') - } - componentDidUpdate(){ - console.log('App did update') - } - render() { - return
; - } - } - class A extends React.Component { - state = {aaa: 1} - componentDidMount(){ - - console.log('A did mount') - } - componentDidUpdate(){ - console.log('A did update') + var _testJournal = []; + + class Child extends React.Component { + componentDidMount() { + _testJournal.push('Child:onDOMReady'); + } + + render() { + return
Child
; + } } - render() { - return
{this.state.aaa}
; + + class SwitcherParent extends React.Component { + constructor(props) { + super(props); + _testJournal.push('SwitcherParent:getInitialState'); + this.state = {showHasOnDOMReadyComponent: false}; + } + + componentDidMount() { + _testJournal.push('SwitcherParent:onDOMReady'); + this.switchIt(); + } + + switchIt = () => { + this.setState({showHasOnDOMReadyComponent: true}); + }; + + render() { + console.log('render完了',this.state.showHasOnDOMReadyComponent) + return ( +
+ {this.state.showHasOnDOMReadyComponent ? :
first
} +
+ ); + } } - } + + + - s = ReactDOM.render(, div) + s = ReactDOM.render(, div) + + console.log(_testJournal) } diff --git a/src/Component.js b/src/Component.js index f7fe85fcb..9d2bc1dd7 100644 --- a/src/Component.js +++ b/src/Component.js @@ -19,6 +19,7 @@ export function Component(props, context) { this.__pendingCallbacks = []; this.__pendingStates = []; this.__current = noop;//用于DevTools工具中,通过实例找到生成它的那个虚拟DOM + this.__lifestage = 0; //判断生命周期 /* * this.__dom = dom 用于isMounted或ReactDOM.findDOMNode方法 * this.__hydrating = true 表示组件正在根据虚拟DOM合成真实DOM diff --git a/src/browser.js b/src/browser.js index 2cc1faa58..1860a815a 100644 --- a/src/browser.js +++ b/src/browser.js @@ -56,7 +56,7 @@ export var win = w; export var document = w.document || fakeDoc; var isStandard = "textContent" in document; var fragment = document.createDocumentFragment(); -function emptyElement(node) { +export function emptyElement(node) { var child; while ((child = node.firstChild)) { emptyElement(child); diff --git a/src/diff.js b/src/diff.js index b61981d12..5c13101ee 100644 --- a/src/diff.js +++ b/src/diff.js @@ -11,7 +11,7 @@ import { } from "./util"; import { diffProps } from "./diffProps"; import { disposeVnode } from "./dispose"; -import { createDOMElement, removeDOMElement } from "./browser"; +import { createDOMElement, emptyElement, removeDOMElement } from "./browser"; import { CurrentOwner, flattenChildren } from "./createElement"; import { processFormElement, @@ -38,16 +38,12 @@ export function unstable_renderSubtreeIntoContainer( return renderByAnu(nextVnode, container, callback, parentContext); } //[Top API] ReactDOM.unmountComponentAtNode -export function unmountComponentAtNode(container, context = {}) { +export function unmountComponentAtNode(container) { var lastVnode = container.__component; if (lastVnode) { - // lastVnode._hostNode = container.firstChild; - let nextVnode = { - type: "#comment", - text: "empty", - vtype: 0 - }; - alignVnode(lastVnode, nextVnode, context, getVParent(container), []); + disposeVnode(lastVnode); + emptyElement(container); + container.__component = null; } } //[Top API] ReactDOM.findDOMNode @@ -265,14 +261,14 @@ function mountComponent(lastNode, vnode, vparent, parentContext, mountQueue) { var childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; - + + let dom = mountVnode(lastNode, rendered, vparent, childContext, mountQueue); - + createInstanceChain(instance, vnode, rendered); updateInstanceChain(instance, dom); - + mountQueue.push(instance); - return dom; } function mountStateless(lastNode, vnode, vparent, parentContext, mountQueue) { @@ -376,9 +372,6 @@ function updateComponent(lastVnode, nextVnode, vparent, context, mountQueue) { return instance.__dom; } - - - function _refreshComponent(instance, mountQueue) { let { props: lastProps, @@ -410,7 +403,7 @@ function _refreshComponent(instance, mountQueue) { instance.__forceUpdate = false; return dom; } - + instance.__hydrating = true; instance.__forceUpdate = false; if (instance.componentWillUpdate) { @@ -426,8 +419,7 @@ function _refreshComponent(instance, mountQueue) { nextProps, nextContext, nextState - ); - + ); if(lastRendered!== nextRendered && parentContext){ dom = alignVnode( lastRendered, @@ -441,10 +433,10 @@ function _refreshComponent(instance, mountQueue) { updateInstanceChain(instance, dom); instance.__lifeStage = 2; + instance.__hydrating = false; if(mountQueue.isChildProcess) { clearRefsAndMounts(mountQueue); } - instance.__hydrating = false; return dom; } @@ -456,18 +448,18 @@ export function alignVnode(lastVnode, nextVnode, vparent, context, mountQueue) { dom = updateVnode(lastVnode, nextVnode, vparent, context, mountQueue); } else { disposeVnode(lastVnode); - // let innerMountQueue = mountQueue.executor - // ? mountQueue - // : nextVnode.vtype === 2 ? [] : mountQueue; + // let innerMountQueue = mountQueue.isChildProcess + // ? mountQueue + // : nextVnode.vtype === 2 ? [] : mountQueue; dom = mountVnode(null, nextVnode, vparent, context, mountQueue); let p = node.parentNode; if (p) { p.replaceChild(dom, node); removeDOMElement(node); } - // if (innerMountQueue !== mountQueue) { - // clearRefsAndMounts(innerMountQueue); - // } + // if (innerMountQueue !== mountQueue) { + // clearRefsAndMounts(innerMountQueue); + // } } return dom; } @@ -512,13 +504,16 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, mountQueue) { nextChildren = flattenChildren(nextVnode), nextLength = nextChildren.length, lastLength = lastChildren.length; + //如果旧数组长度为零 if (nextLength && !lastLength) { - nextChildren.forEach(function(vnode) { + return nextChildren.forEach(function(vnode) { let curNode = mountVnode(null, vnode, lastVnode, context, mountQueue); parentNode.appendChild(curNode); }); - return; + } + if(nextLength === lastLength && lastLength ===1){ + return alignVnode(lastChildren[0], nextChildren[0], lastVnode, context, mountQueue); } let maxLength = Math.max(nextLength, lastLength), insertPoint = parentNode.firstChild, @@ -682,7 +677,10 @@ function clearRefsAndMounts(queue) { //先执行所有refs方法(从上到下) clearRefs(); //再执行所有mount/update钩子(从下到上) - queue.forEach(function(instance) { + let i = 0; + while(i < queue.length){//queue可能中途加入新元素, 因此不能直接使用queue.forEach(fn) + var instance = queue[i]; + i++; if (!instance.__lifeStage) { if (instance.componentDidMount) { instance.componentDidMount(); @@ -697,12 +695,12 @@ function clearRefsAndMounts(queue) { if (ref) { ref(instance.__mergeStates ? instance : null); } - instance.__hydrating = false; + instance.__hydrating = false; //子树已经构建完毕 while (instance.__renderInNextCycle) { _refreshComponent(instance, queue); callUpdate(instance); } - }); + } //再执行所有setState/forceUpdate回调,根据从下到上的顺序执行 queue.sort(mountSorter).forEach(function(instance){ clearArray(instance.__pendingCallbacks).forEach(function(fn) { diff --git a/src/dispose.js b/src/dispose.js index 351f0d451..07840f986 100644 --- a/src/dispose.js +++ b/src/dispose.js @@ -38,10 +38,10 @@ function disposeElement(vnode) { } function disposeComponent(vnode) { + let instance = vnode._instance; if (instance) { options.beforeUnmount(instance); - let dom = instance.__dom; instance.__current = instance.setState = instance.forceUpdate = noop; if (vnode.ref) { vnode.ref(null); @@ -50,10 +50,6 @@ function disposeComponent(vnode) { instance.componentWillUnmount(); } //在执行componentWillUnmount后才将关联的元素节点解绑,防止用户在钩子里调用 findDOMNode方法 - if (dom) { - dom.__component = null; - } - vnode.ref = instance.__dom = vnode._instance = null; disposeVnode(instance.__rendered); } diff --git a/test/modules/ReactComponentLifeCycle-test.jsx b/test/modules/ReactComponentLifeCycle-test.jsx new file mode 100644 index 000000000..d40e883cb --- /dev/null +++ b/test/modules/ReactComponentLifeCycle-test.jsx @@ -0,0 +1,79 @@ +import React from "dist/React"; +import getTestDocument from "./getTestDocument"; +import ReactTestUtils from "lib/ReactTestUtils"; + +import ReactDOMServer from "dist/ReactDOMServer"; +//https://github.com/facebook/react/blob/master/src/isomorphic/children/__tests__/ReactChildren-test.js +var ReactDOM = window.ReactDOM || React; + +describe("ReactComponentLifeCycle-test", function() { + this.timeout(200000); + + it('should not reuse an instance when it has been unmounted', () => { + var container = document.createElement('div'); + + class StatefulComponent extends React.Component { + state = {}; + + render() { + return
; + } + } + + var element = ; + var firstInstance = ReactDOM.render(element, container); + ReactDOM.unmountComponentAtNode(container); + var secondInstance = ReactDOM.render(element, container); + expect(firstInstance).not.toBe(secondInstance); + }); + + /** + * If a state update triggers rerendering that in turn fires an onDOMReady, + * that second onDOMReady should not fail. + */ + it('it should fire onDOMReady when already in onDOMReady', () => { + var _testJournal = []; + + class Child extends React.Component { + componentDidMount() { + _testJournal.push('Child:onDOMReady'); + } + + render() { + return
; + } + } + + class SwitcherParent extends React.Component { + constructor(props) { + super(props); + _testJournal.push('SwitcherParent:getInitialState'); + this.state = {showHasOnDOMReadyComponent: false}; + } + + componentDidMount() { + _testJournal.push('SwitcherParent:onDOMReady'); + this.switchIt(); + } + + switchIt = () => { + this.setState({showHasOnDOMReadyComponent: true}); + }; + + render() { + return ( +
+ {this.state.showHasOnDOMReadyComponent ? :
} +
+ ); + } + } + + ReactTestUtils.renderIntoDocument(); + expect(_testJournal).toEqual([ + 'SwitcherParent:getInitialState', + 'SwitcherParent:onDOMReady', + 'Child:onDOMReady', + ]); + }); +}); \ No newline at end of file diff --git a/test/spec.js b/test/spec.js index cc173a954..d65938b5c 100644 --- a/test/spec.js +++ b/test/spec.js @@ -45,6 +45,7 @@ require("./modules/ReactIdentity-test.jsx"); require("./modules/ReactCompositeComponentNestedState-test.jsx"); +require("./modules/ReactComponentLifeCycle-test.jsx"); diff --git a/version.md b/version.md index 3281a5391..7b832f3a9 100644 --- a/version.md +++ b/version.md @@ -11,9 +11,11 @@ >4 第四个参数为上下文对象 >5 第五个参数为任务调度系系统的列队 7. 使用全新的方式获取元素的命名空间 -8. 全新的节点排序算法(diffChildren) +8. 上线全新的节点排序算法(diffChildren) 9. renderByAnu在全局渲染后应该置空CurrentOwner.cur, 防止影响其他虚拟DOM 10. 完善createStringRef方法,应该能抛错与删除无用数据 +11. 上线全新的任务调度系统 +12. 重构unmountComponentAtNode方法 ## 1.1.1 1. 简化createClass From e7314a3f40bc568d220b75e6795e70c66ccee2e8 Mon Sep 17 00:00:00 2001 From: RubyLouvre <1669866773@qq.com> Date: Thu, 28 Sep 2017 14:49:07 +0800 Subject: [PATCH 04/57] =?UTF-8?q?=E9=87=8D=E5=91=BD=E5=90=8D=E5=86=85?= =?UTF-8?q?=E9=83=A8=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Component.js | 2 +- src/diff.js | 104 +++++++++++++++++++++++------------------------ 2 files changed, 53 insertions(+), 53 deletions(-) diff --git a/src/Component.js b/src/Component.js index 9d2bc1dd7..3c1cfd234 100644 --- a/src/Component.js +++ b/src/Component.js @@ -102,7 +102,7 @@ function setStateImpl(state, cb) { this.__renderInNextCycle = true; if (options.async) { //在事件句柄中执行setState会进行合并 - options.enqueueUpdate(this); + options.addTask(this); return; } if (this.__hydrating) { diff --git a/src/diff.js b/src/diff.js index 5c13101ee..47f9e0809 100644 --- a/src/diff.js +++ b/src/diff.js @@ -73,7 +73,7 @@ function renderByAnu(vnode, container, callback, context = {}) { if (!(container && container.getElementsByTagName)) { throw `ReactDOM.render的第二个参数错误`; // eslint-disable-line } - let mountQueue = [], + let updateQueue = [], rootNode, lastVnode = container.__component; if (lastVnode) { @@ -83,12 +83,12 @@ function renderByAnu(vnode, container, callback, context = {}) { vnode, getVParent(container), context, - mountQueue + updateQueue ); } else { - mountQueue.isMainProcess = true; + updateQueue.isMainProcess = true; //如果是后端渲染生成,它的孩子中存在一个拥有data-reactroot属性的元素节点 - rootNode = genVnodes(container, vnode, context, mountQueue); + rootNode = genVnodes(container, vnode, context, updateQueue); } if (rootNode.setAttribute) { @@ -97,7 +97,7 @@ function renderByAnu(vnode, container, callback, context = {}) { var instance = vnode._instance; container.__component = vnode; - clearRefsAndMounts(mountQueue); + clearScheduler(updateQueue); CurrentOwner.cur = null; //防止干扰 var ret = instance || rootNode; if (callback) { @@ -107,7 +107,7 @@ function renderByAnu(vnode, container, callback, context = {}) { return ret; } -function genVnodes(container, vnode, context, mountQueue) { +function genVnodes(container, vnode, context, updateQueue) { let nodes = getNodes(container); let lastNode = null; for (var i = 0, el; (el = nodes[i++]); ) { @@ -118,7 +118,7 @@ function genVnodes(container, vnode, context, mountQueue) { } } return container.appendChild( - mountVnode(lastNode, vnode, getVParent(container), context, mountQueue) + mountVnode(lastNode, vnode, getVParent(container), context, updateQueue) ); } @@ -178,14 +178,14 @@ const formElements = { input: 1 }; -function mountElement(lastNode, vnode, vparent, context, mountQueue) { +function mountElement(lastNode, vnode, vparent, context, updateQueue) { let { type, props, ref } = vnode; let dom = genMountElement(lastNode, vnode, vparent, type); vnode._hostNode = dom; let method = lastNode ? alignChildren : mountChildren; - method(dom, vnode, context, mountQueue); + method(dom, vnode, context, updateQueue); if (vnode.checkProps) { diffProps(props, {}, vnode, {}, dom); @@ -201,16 +201,16 @@ function mountElement(lastNode, vnode, vparent, context, mountQueue) { } //将虚拟DOM转换为真实DOM并插入父元素 -function mountChildren(parentNode, vparent, context, mountQueue) { +function mountChildren(parentNode, vparent, context, updateQueue) { var children = flattenChildren(vparent); for (let i = 0, n = children.length; i < n; i++) { parentNode.appendChild( - mountVnode(null, children[i], vparent, context, mountQueue) + mountVnode(null, children[i], vparent, context, updateQueue) ); } } -function alignChildren(parentNode, vparent, context, mountQueue) { +function alignChildren(parentNode, vparent, context, updateQueue) { let children = flattenChildren(vparent), childNodes = parentNode.childNodes, insertPoint = childNodes[0] || null, @@ -219,7 +219,7 @@ function alignChildren(parentNode, vparent, context, mountQueue) { for (let i = 0; i < n; i++) { let vnode = children[i]; let lastNode = childNodes[j]; - let dom = mountVnode(lastNode, vnode, vparent, context, mountQueue); + let dom = mountVnode(lastNode, vnode, vparent, context, updateQueue); if (dom === lastNode) { j++; } @@ -231,7 +231,7 @@ function alignChildren(parentNode, vparent, context, mountQueue) { } } -function mountComponent(lastNode, vnode, vparent, parentContext, mountQueue) { +function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { let { type, props } = vnode; let lastOwn = CurrentOwner.cur; let componentContext = getContextByTypes(parentContext, type.contextTypes); @@ -263,21 +263,21 @@ function mountComponent(lastNode, vnode, vparent, parentContext, mountQueue) { : parentContext; - let dom = mountVnode(lastNode, rendered, vparent, childContext, mountQueue); + let dom = mountVnode(lastNode, rendered, vparent, childContext, updateQueue); createInstanceChain(instance, vnode, rendered); updateInstanceChain(instance, dom); - mountQueue.push(instance); + updateQueue.push(instance); return dom; } -function mountStateless(lastNode, vnode, vparent, parentContext, mountQueue) { +function mountStateless(lastNode, vnode, vparent, parentContext, updateQueue) { let componentContext = getContextByTypes(parentContext, vnode.type.contextTypes); let instance = new Stateless(vnode.type); let rendered = renderComponent.call(instance, vnode, vnode.props, componentContext); - let dom = mountVnode(lastNode, rendered, vparent, parentContext, mountQueue); + let dom = mountVnode(lastNode, rendered, vparent, parentContext, updateQueue); //用于refreshComponent instance.nextVnode = vnode; @@ -286,7 +286,7 @@ function mountStateless(lastNode, vnode, vparent, parentContext, mountQueue) { createInstanceChain(instance, vnode, rendered); updateInstanceChain(instance, dom); - mountQueue.unshift(instance); + updateQueue.unshift(instance); return dom; } @@ -334,7 +334,7 @@ function Stateless(render) { //Stateless.prototype.render = renderComponent; -function updateComponent(lastVnode, nextVnode, vparent, context, mountQueue) { +function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { var instance = lastVnode._instance; var ref = lastVnode.ref; if (ref && lastVnode.vtype === 2) { @@ -348,8 +348,8 @@ function updateComponent(lastVnode, nextVnode, vparent, context, mountQueue) { instance.componentWillReceiveProps(nextProps, nextContext); instance.__receiving = false; } - if (!mountQueue.executor) { - mountQueue.executor = true; + if (!updateQueue.executor) { + updateQueue.executor = true; } // shouldComponentUpdate为false时不能阻止setState/forceUpdate cb的触发 @@ -359,20 +359,20 @@ function updateComponent(lastVnode, nextVnode, vparent, context, mountQueue) { nextVnode.parentContext = context; nextVnode.vparent = vparent; var queue; - if( mountQueue.isChildProcess ){ - queue = mountQueue; + if( updateQueue.isChildProcess ){ + queue = updateQueue; }else { queue = []; queue.isChildProcess = true; } _refreshComponent(instance, queue); //子组件先执行 - mountQueue.unshift(instance); + updateQueue.unshift(instance); return instance.__dom; } -function _refreshComponent(instance, mountQueue) { +function _refreshComponent(instance, updateQueue) { let { props: lastProps, state: lastState, @@ -426,7 +426,7 @@ function _refreshComponent(instance, mountQueue) { nextRendered, vparent, getChildContext(instance, parentContext), - mountQueue + updateQueue ); } createInstanceChain(instance, nextVnode, nextRendered); @@ -434,37 +434,37 @@ function _refreshComponent(instance, mountQueue) { instance.__lifeStage = 2; instance.__hydrating = false; - if(mountQueue.isChildProcess) { - clearRefsAndMounts(mountQueue); + if(updateQueue.isChildProcess) { + clearScheduler(updateQueue); } return dom; } -export function alignVnode(lastVnode, nextVnode, vparent, context, mountQueue) { +export function alignVnode(lastVnode, nextVnode, vparent, context, updateQueue) { let node = lastVnode._hostNode, dom; if (isSameNode(lastVnode, nextVnode)) { - dom = updateVnode(lastVnode, nextVnode, vparent, context, mountQueue); + dom = updateVnode(lastVnode, nextVnode, vparent, context, updateQueue); } else { disposeVnode(lastVnode); - // let innerMountQueue = mountQueue.isChildProcess - // ? mountQueue - // : nextVnode.vtype === 2 ? [] : mountQueue; - dom = mountVnode(null, nextVnode, vparent, context, mountQueue); + // let innerUpdateQueue = updateQueue.isChildProcess + // ? updateQueue + // : nextVnode.vtype === 2 ? [] : updateQueue; + dom = mountVnode(null, nextVnode, vparent, context, updateQueue); let p = node.parentNode; if (p) { p.replaceChild(dom, node); removeDOMElement(node); } - // if (innerMountQueue !== mountQueue) { - // clearRefsAndMounts(innerMountQueue); + // if (innerUpdateQueue !== updateQueue) { + // clearScheduler(innerUpdateQueue); // } } return dom; } -function updateElement(lastVnode, nextVnode, vparent, context, mountQueue) { +function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { let dom = lastVnode._hostNode; let lastProps = lastVnode.props; let nextProps = nextVnode.props; @@ -481,9 +481,9 @@ function updateElement(lastVnode, nextVnode, vparent, context, mountQueue) { while (dom.firstChild) { dom.removeChild(dom.firstChild); } - mountChildren(dom, nextVnode, context, mountQueue); + mountChildren(dom, nextVnode, context, updateQueue); } else { - diffChildren(lastVnode, nextVnode, dom, context, mountQueue); + diffChildren(lastVnode, nextVnode, dom, context, updateQueue); } } @@ -499,7 +499,7 @@ function updateElement(lastVnode, nextVnode, vparent, context, mountQueue) { return dom; } -function diffChildren(lastVnode, nextVnode, parentNode, context, mountQueue) { +function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { let lastChildren = lastVnode.vchildren, nextChildren = flattenChildren(nextVnode), nextLength = nextChildren.length, @@ -508,12 +508,12 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, mountQueue) { //如果旧数组长度为零 if (nextLength && !lastLength) { return nextChildren.forEach(function(vnode) { - let curNode = mountVnode(null, vnode, lastVnode, context, mountQueue); + let curNode = mountVnode(null, vnode, lastVnode, context, updateQueue); parentNode.appendChild(curNode); }); } if(nextLength === lastLength && lastLength ===1){ - return alignVnode(lastChildren[0], nextChildren[0], lastVnode, context, mountQueue); + return alignVnode(lastChildren[0], nextChildren[0], lastVnode, context, updateQueue); } let maxLength = Math.max(nextLength, lastLength), insertPoint = parentNode.firstChild, @@ -524,7 +524,7 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, mountQueue) { hit, dom, oldDom, - // hasExecutor = mountQueue.executor, + // hasExecutor = updateQueue.executor, nextChild, lastChild; //第一次循环,构建移动指令(actions)与移除名单(removeHits)与命中名单(fuzzyHits) @@ -580,7 +580,7 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, mountQueue) { oldChild = fuzzyHits[hit].shift(); oldDom = oldChild._hostNode; parentNode.insertBefore(oldDom, insertPoint); - dom = updateVnode(oldChild, curChild, lastVnode, context, mountQueue); + dom = updateVnode(oldChild, curChild, lastVnode, context, updateQueue); removeHits[oldChild._i] = true; } else { //为了兼容 react stack reconciliation的执行顺序,添加下面三行, @@ -590,7 +590,7 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, mountQueue) { disposeVnode(removed); } //如果找不到对应的旧节点,创建一个新节点放在这里 - dom = mountVnode(null, curChild, lastVnode, context, mountQueue); + dom = mountVnode(null, curChild, lastVnode, context, updateQueue); parentNode.insertBefore(dom, insertPoint); } } else { @@ -603,7 +603,7 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, mountQueue) { action.next, lastVnode, context, - mountQueue + updateQueue ); } insertPoint = dom.nextSibling; @@ -618,8 +618,8 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, mountQueue) { disposeVnode(el); } }); - // if (!hasExecutor && mountQueue.executor) { - // clearRefsAndMounts(mountQueue); + // if (!hasExecutor && updateQueue.executor) { + // clearScheduler(updateQueue); // } } @@ -672,7 +672,7 @@ function callUpdate(instance) { } } -function clearRefsAndMounts(queue) { +function clearScheduler(queue) { options.beforePatch(); //先执行所有refs方法(从上到下) clearRefs(); @@ -722,10 +722,10 @@ options.flushBatchedUpdates = function(queue) { if (!queue) { queue = dirtyComponents; } - clearRefsAndMounts(queue); + clearScheduler(queue); }; -options.enqueueUpdate = function(instance) { +options.addTask = function(instance) { if (dirtyComponents.indexOf(instance) == -1) { dirtyComponents.push(instance); } From 394a0dd6efa4c2b5533fd19ef3974ea05427d49f Mon Sep 17 00:00:00 2001 From: RubyLouvre <1669866773@qq.com> Date: Thu, 28 Sep 2017 15:02:40 +0800 Subject: [PATCH 05/57] remove pr --- dist/React.js | 108 ++++++++++++++++++++--------------------- dist/ReactIE.js | 108 ++++++++++++++++++++--------------------- dist/ReactSelection.js | 108 ++++++++++++++++++++--------------------- dist/ReactShim.js | 108 ++++++++++++++++++++--------------------- src/diff.js | 2 +- 5 files changed, 217 insertions(+), 217 deletions(-) diff --git a/dist/React.js b/dist/React.js index b6b814687..d082143fb 100644 --- a/dist/React.js +++ b/dist/React.js @@ -1107,7 +1107,7 @@ function setStateImpl(state, cb) { this.__renderInNextCycle = true; if (options.async) { //在事件句柄中执行setState会进行合并 - options.enqueueUpdate(this); + options.addTask(this); return; } if (this.__hydrating) { @@ -1924,16 +1924,16 @@ function renderByAnu(vnode, container, callback) { if (!(container && container.getElementsByTagName)) { throw "ReactDOM.render\u7684\u7B2C\u4E8C\u4E2A\u53C2\u6570\u9519\u8BEF"; // eslint-disable-line } - var mountQueue = [], + var updateQueue = [], rootNode = void 0, lastVnode = container.__component; if (lastVnode) { // lastVnode._hostNode = container.firstChild;?? - rootNode = alignVnode(lastVnode, vnode, getVParent(container), context, mountQueue); + rootNode = alignVnode(lastVnode, vnode, getVParent(container), context, updateQueue); } else { - mountQueue.isMainProcess = true; + updateQueue.isMainProcess = true; //如果是后端渲染生成,它的孩子中存在一个拥有data-reactroot属性的元素节点 - rootNode = genVnodes(container, vnode, context, mountQueue); + rootNode = genVnodes(container, vnode, context, updateQueue); } if (rootNode.setAttribute) { @@ -1942,7 +1942,7 @@ function renderByAnu(vnode, container, callback) { var instance = vnode._instance; container.__component = vnode; - clearRefsAndMounts(mountQueue); + clearScheduler(updateQueue); CurrentOwner.cur = null; //防止干扰 var ret = instance || rootNode; if (callback) { @@ -1952,7 +1952,7 @@ function renderByAnu(vnode, container, callback) { return ret; } -function genVnodes(container, vnode, context, mountQueue) { +function genVnodes(container, vnode, context, updateQueue) { var nodes = getNodes(container); var lastNode = null; for (var i = 0, el; el = nodes[i++];) { @@ -1962,7 +1962,7 @@ function genVnodes(container, vnode, context, mountQueue) { container.removeChild(el); } } - return container.appendChild(mountVnode(lastNode, vnode, getVParent(container), context, mountQueue)); + return container.appendChild(mountVnode(lastNode, vnode, getVParent(container), context, updateQueue)); } var patchStrategy = { @@ -2021,7 +2021,7 @@ var formElements = { input: 1 }; -function mountElement(lastNode, vnode, vparent, context, mountQueue) { +function mountElement(lastNode, vnode, vparent, context, updateQueue) { var type = vnode.type, props = vnode.props, ref = vnode.ref; @@ -2031,7 +2031,7 @@ function mountElement(lastNode, vnode, vparent, context, mountQueue) { vnode._hostNode = dom; var method = lastNode ? alignChildren : mountChildren; - method(dom, vnode, context, mountQueue); + method(dom, vnode, context, updateQueue); if (vnode.checkProps) { diffProps(props, {}, vnode, {}, dom); @@ -2047,14 +2047,14 @@ function mountElement(lastNode, vnode, vparent, context, mountQueue) { } //将虚拟DOM转换为真实DOM并插入父元素 -function mountChildren(parentNode, vparent, context, mountQueue) { +function mountChildren(parentNode, vparent, context, updateQueue) { var children = flattenChildren(vparent); for (var i = 0, n = children.length; i < n; i++) { - parentNode.appendChild(mountVnode(null, children[i], vparent, context, mountQueue)); + parentNode.appendChild(mountVnode(null, children[i], vparent, context, updateQueue)); } } -function alignChildren(parentNode, vparent, context, mountQueue) { +function alignChildren(parentNode, vparent, context, updateQueue) { var children = flattenChildren(vparent), childNodes = parentNode.childNodes, insertPoint = childNodes[0] || null, @@ -2063,7 +2063,7 @@ function alignChildren(parentNode, vparent, context, mountQueue) { for (var i = 0; i < n; i++) { var vnode = children[i]; var lastNode = childNodes[j]; - var dom = mountVnode(lastNode, vnode, vparent, context, mountQueue); + var dom = mountVnode(lastNode, vnode, vparent, context, updateQueue); if (dom === lastNode) { j++; } @@ -2075,7 +2075,7 @@ function alignChildren(parentNode, vparent, context, mountQueue) { } } -function mountComponent(lastNode, vnode, vparent, parentContext, mountQueue) { +function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { var type = vnode.type, props = vnode.props; @@ -2106,21 +2106,21 @@ function mountComponent(lastNode, vnode, vparent, parentContext, mountQueue) { var childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; - var dom = mountVnode(lastNode, rendered, vparent, childContext, mountQueue); + var dom = mountVnode(lastNode, rendered, vparent, childContext, updateQueue); createInstanceChain(instance, vnode, rendered); updateInstanceChain(instance, dom); - mountQueue.push(instance); + updateQueue.push(instance); return dom; } -function mountStateless(lastNode, vnode, vparent, parentContext, mountQueue) { +function mountStateless(lastNode, vnode, vparent, parentContext, updateQueue) { var componentContext = getContextByTypes(parentContext, vnode.type.contextTypes); var instance = new Stateless(vnode.type); var rendered = renderComponent.call(instance, vnode, vnode.props, componentContext); - var dom = mountVnode(lastNode, rendered, vparent, parentContext, mountQueue); + var dom = mountVnode(lastNode, rendered, vparent, parentContext, updateQueue); //用于refreshComponent instance.nextVnode = vnode; @@ -2129,7 +2129,7 @@ function mountStateless(lastNode, vnode, vparent, parentContext, mountQueue) { createInstanceChain(instance, vnode, rendered); updateInstanceChain(instance, dom); - mountQueue.unshift(instance); + updateQueue.unshift(instance); return dom; } @@ -2176,7 +2176,7 @@ function Stateless(render) { //Stateless.prototype.render = renderComponent; -function updateComponent(lastVnode, nextVnode, vparent, context, mountQueue) { +function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { var instance = lastVnode._instance; var ref = lastVnode.ref; if (ref && lastVnode.vtype === 2) { @@ -2190,8 +2190,8 @@ function updateComponent(lastVnode, nextVnode, vparent, context, mountQueue) { instance.componentWillReceiveProps(nextProps, nextContext); instance.__receiving = false; } - if (!mountQueue.executor) { - mountQueue.executor = true; + if (!updateQueue.executor) { + updateQueue.executor = true; } // shouldComponentUpdate为false时不能阻止setState/forceUpdate cb的触发 @@ -2201,20 +2201,20 @@ function updateComponent(lastVnode, nextVnode, vparent, context, mountQueue) { nextVnode.parentContext = context; nextVnode.vparent = vparent; var queue; - if (mountQueue.isChildProcess) { - queue = mountQueue; + if (updateQueue.isChildProcess) { + queue = updateQueue; } else { queue = []; queue.isChildProcess = true; } _refreshComponent(instance, queue); //子组件先执行 - mountQueue.unshift(instance); + updateQueue.unshift(instance); return instance.__dom; } -function _refreshComponent(instance, mountQueue) { +function _refreshComponent(instance, updateQueue) { var lastProps = instance.props, lastState = instance.state, lastContext = instance.context, @@ -2249,44 +2249,44 @@ function _refreshComponent(instance, mountQueue) { //这里会更新instance的props, context, state var nextRendered = renderComponent.call(instance, nextVnode, nextProps, nextContext, nextState); if (lastRendered !== nextRendered && parentContext) { - dom = alignVnode(lastRendered, nextRendered, vparent, getChildContext(instance, parentContext), mountQueue); + dom = alignVnode(lastRendered, nextRendered, vparent, getChildContext(instance, parentContext), updateQueue); } createInstanceChain(instance, nextVnode, nextRendered); updateInstanceChain(instance, dom); instance.__lifeStage = 2; instance.__hydrating = false; - if (mountQueue.isChildProcess) { - clearRefsAndMounts(mountQueue); + if (updateQueue.isChildProcess) { + clearScheduler(updateQueue); } return dom; } -function alignVnode(lastVnode, nextVnode, vparent, context, mountQueue) { +function alignVnode(lastVnode, nextVnode, vparent, context, updateQueue) { var node = lastVnode._hostNode, dom = void 0; if (isSameNode(lastVnode, nextVnode)) { - dom = updateVnode(lastVnode, nextVnode, vparent, context, mountQueue); + dom = updateVnode(lastVnode, nextVnode, vparent, context, updateQueue); } else { disposeVnode(lastVnode); - // let innerMountQueue = mountQueue.isChildProcess - // ? mountQueue - // : nextVnode.vtype === 2 ? [] : mountQueue; - dom = mountVnode(null, nextVnode, vparent, context, mountQueue); + // let innerUpdateQueue = updateQueue.isChildProcess + // ? updateQueue + // : nextVnode.vtype === 2 ? [] : updateQueue; + dom = mountVnode(null, nextVnode, vparent, context, updateQueue); var p = node.parentNode; if (p) { p.replaceChild(dom, node); removeDOMElement(node); } - // if (innerMountQueue !== mountQueue) { - // clearRefsAndMounts(innerMountQueue); + // if (innerUpdateQueue !== updateQueue) { + // clearScheduler(innerUpdateQueue); // } } return dom; } -function updateElement(lastVnode, nextVnode, vparent, context, mountQueue) { +function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { var dom = lastVnode._hostNode; var lastProps = lastVnode.props; var nextProps = nextVnode.props; @@ -2299,13 +2299,13 @@ function updateElement(lastVnode, nextVnode, vparent, context, mountQueue) { }); list.length = 0; } else { - if (lastProps[innerHTML] || lastProps.children.length) { + if (lastProps[innerHTML]) { while (dom.firstChild) { dom.removeChild(dom.firstChild); } - mountChildren(dom, nextVnode, context, mountQueue); + mountChildren(dom, nextVnode, context, updateQueue); } else { - diffChildren(lastVnode, nextVnode, dom, context, mountQueue); + diffChildren(lastVnode, nextVnode, dom, context, updateQueue); } } @@ -2321,7 +2321,7 @@ function updateElement(lastVnode, nextVnode, vparent, context, mountQueue) { return dom; } -function diffChildren(lastVnode, nextVnode, parentNode, context, mountQueue) { +function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { var lastChildren = lastVnode.vchildren, nextChildren = flattenChildren(nextVnode), nextLength = nextChildren.length, @@ -2330,12 +2330,12 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, mountQueue) { //如果旧数组长度为零 if (nextLength && !lastLength) { return nextChildren.forEach(function (vnode) { - var curNode = mountVnode(null, vnode, lastVnode, context, mountQueue); + var curNode = mountVnode(null, vnode, lastVnode, context, updateQueue); parentNode.appendChild(curNode); }); } if (nextLength === lastLength && lastLength === 1) { - return alignVnode(lastChildren[0], nextChildren[0], lastVnode, context, mountQueue); + return alignVnode(lastChildren[0], nextChildren[0], lastVnode, context, updateQueue); } var maxLength = Math.max(nextLength, lastLength), insertPoint = parentNode.firstChild, @@ -2347,7 +2347,7 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, mountQueue) { dom = void 0, oldDom = void 0, - // hasExecutor = mountQueue.executor, + // hasExecutor = updateQueue.executor, nextChild = void 0, lastChild = void 0; //第一次循环,构建移动指令(actions)与移除名单(removeHits)与命中名单(fuzzyHits) @@ -2403,7 +2403,7 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, mountQueue) { oldChild = fuzzyHits[hit].shift(); oldDom = oldChild._hostNode; parentNode.insertBefore(oldDom, insertPoint); - dom = updateVnode(oldChild, curChild, lastVnode, context, mountQueue); + dom = updateVnode(oldChild, curChild, lastVnode, context, updateQueue); removeHits[oldChild._i] = true; } else { //为了兼容 react stack reconciliation的执行顺序,添加下面三行, @@ -2413,7 +2413,7 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, mountQueue) { disposeVnode(removed); } //如果找不到对应的旧节点,创建一个新节点放在这里 - dom = mountVnode(null, curChild, lastVnode, context, mountQueue); + dom = mountVnode(null, curChild, lastVnode, context, updateQueue); parentNode.insertBefore(dom, insertPoint); } } else { @@ -2421,7 +2421,7 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, mountQueue) { if (action.action === "moveAfter") { parentNode.insertBefore(oldDom, insertPoint); } - dom = updateVnode(action.last, action.next, lastVnode, context, mountQueue); + dom = updateVnode(action.last, action.next, lastVnode, context, updateQueue); } insertPoint = dom.nextSibling; } @@ -2435,8 +2435,8 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, mountQueue) { disposeVnode(el); } }); - // if (!hasExecutor && mountQueue.executor) { - // clearRefsAndMounts(mountQueue); + // if (!hasExecutor && updateQueue.executor) { + // clearScheduler(updateQueue); // } } @@ -2485,7 +2485,7 @@ function callUpdate(instance) { } } -function clearRefsAndMounts(queue) { +function clearScheduler(queue) { options.beforePatch(); //先执行所有refs方法(从上到下) clearRefs(); @@ -2537,10 +2537,10 @@ options.flushBatchedUpdates = function (queue) { if (!queue) { queue = dirtyComponents; } - clearRefsAndMounts(queue); + clearScheduler(queue); }; -options.enqueueUpdate = function (instance) { +options.addTask = function (instance) { if (dirtyComponents.indexOf(instance) == -1) { dirtyComponents.push(instance); } diff --git a/dist/ReactIE.js b/dist/ReactIE.js index bb2a1b87b..04727e6f7 100644 --- a/dist/ReactIE.js +++ b/dist/ReactIE.js @@ -1106,7 +1106,7 @@ function setStateImpl(state, cb) { this.__renderInNextCycle = true; if (options.async) { //在事件句柄中执行setState会进行合并 - options.enqueueUpdate(this); + options.addTask(this); return; } if (this.__hydrating) { @@ -1923,16 +1923,16 @@ function renderByAnu(vnode, container, callback) { if (!(container && container.getElementsByTagName)) { throw "ReactDOM.render\u7684\u7B2C\u4E8C\u4E2A\u53C2\u6570\u9519\u8BEF"; // eslint-disable-line } - var mountQueue = [], + var updateQueue = [], rootNode = void 0, lastVnode = container.__component; if (lastVnode) { // lastVnode._hostNode = container.firstChild;?? - rootNode = alignVnode(lastVnode, vnode, getVParent(container), context, mountQueue); + rootNode = alignVnode(lastVnode, vnode, getVParent(container), context, updateQueue); } else { - mountQueue.isMainProcess = true; + updateQueue.isMainProcess = true; //如果是后端渲染生成,它的孩子中存在一个拥有data-reactroot属性的元素节点 - rootNode = genVnodes(container, vnode, context, mountQueue); + rootNode = genVnodes(container, vnode, context, updateQueue); } if (rootNode.setAttribute) { @@ -1941,7 +1941,7 @@ function renderByAnu(vnode, container, callback) { var instance = vnode._instance; container.__component = vnode; - clearRefsAndMounts(mountQueue); + clearScheduler(updateQueue); CurrentOwner.cur = null; //防止干扰 var ret = instance || rootNode; if (callback) { @@ -1951,7 +1951,7 @@ function renderByAnu(vnode, container, callback) { return ret; } -function genVnodes(container, vnode, context, mountQueue) { +function genVnodes(container, vnode, context, updateQueue) { var nodes = getNodes(container); var lastNode = null; for (var i = 0, el; el = nodes[i++];) { @@ -1961,7 +1961,7 @@ function genVnodes(container, vnode, context, mountQueue) { container.removeChild(el); } } - return container.appendChild(mountVnode(lastNode, vnode, getVParent(container), context, mountQueue)); + return container.appendChild(mountVnode(lastNode, vnode, getVParent(container), context, updateQueue)); } var patchStrategy = { @@ -2020,7 +2020,7 @@ var formElements = { input: 1 }; -function mountElement(lastNode, vnode, vparent, context, mountQueue) { +function mountElement(lastNode, vnode, vparent, context, updateQueue) { var type = vnode.type, props = vnode.props, ref = vnode.ref; @@ -2030,7 +2030,7 @@ function mountElement(lastNode, vnode, vparent, context, mountQueue) { vnode._hostNode = dom; var method = lastNode ? alignChildren : mountChildren; - method(dom, vnode, context, mountQueue); + method(dom, vnode, context, updateQueue); if (vnode.checkProps) { diffProps(props, {}, vnode, {}, dom); @@ -2046,14 +2046,14 @@ function mountElement(lastNode, vnode, vparent, context, mountQueue) { } //将虚拟DOM转换为真实DOM并插入父元素 -function mountChildren(parentNode, vparent, context, mountQueue) { +function mountChildren(parentNode, vparent, context, updateQueue) { var children = flattenChildren(vparent); for (var i = 0, n = children.length; i < n; i++) { - parentNode.appendChild(mountVnode(null, children[i], vparent, context, mountQueue)); + parentNode.appendChild(mountVnode(null, children[i], vparent, context, updateQueue)); } } -function alignChildren(parentNode, vparent, context, mountQueue) { +function alignChildren(parentNode, vparent, context, updateQueue) { var children = flattenChildren(vparent), childNodes = parentNode.childNodes, insertPoint = childNodes[0] || null, @@ -2062,7 +2062,7 @@ function alignChildren(parentNode, vparent, context, mountQueue) { for (var i = 0; i < n; i++) { var vnode = children[i]; var lastNode = childNodes[j]; - var dom = mountVnode(lastNode, vnode, vparent, context, mountQueue); + var dom = mountVnode(lastNode, vnode, vparent, context, updateQueue); if (dom === lastNode) { j++; } @@ -2074,7 +2074,7 @@ function alignChildren(parentNode, vparent, context, mountQueue) { } } -function mountComponent(lastNode, vnode, vparent, parentContext, mountQueue) { +function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { var type = vnode.type, props = vnode.props; @@ -2105,21 +2105,21 @@ function mountComponent(lastNode, vnode, vparent, parentContext, mountQueue) { var childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; - var dom = mountVnode(lastNode, rendered, vparent, childContext, mountQueue); + var dom = mountVnode(lastNode, rendered, vparent, childContext, updateQueue); createInstanceChain(instance, vnode, rendered); updateInstanceChain(instance, dom); - mountQueue.push(instance); + updateQueue.push(instance); return dom; } -function mountStateless(lastNode, vnode, vparent, parentContext, mountQueue) { +function mountStateless(lastNode, vnode, vparent, parentContext, updateQueue) { var componentContext = getContextByTypes(parentContext, vnode.type.contextTypes); var instance = new Stateless(vnode.type); var rendered = renderComponent.call(instance, vnode, vnode.props, componentContext); - var dom = mountVnode(lastNode, rendered, vparent, parentContext, mountQueue); + var dom = mountVnode(lastNode, rendered, vparent, parentContext, updateQueue); //用于refreshComponent instance.nextVnode = vnode; @@ -2128,7 +2128,7 @@ function mountStateless(lastNode, vnode, vparent, parentContext, mountQueue) { createInstanceChain(instance, vnode, rendered); updateInstanceChain(instance, dom); - mountQueue.unshift(instance); + updateQueue.unshift(instance); return dom; } @@ -2175,7 +2175,7 @@ function Stateless(render) { //Stateless.prototype.render = renderComponent; -function updateComponent(lastVnode, nextVnode, vparent, context, mountQueue) { +function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { var instance = lastVnode._instance; var ref = lastVnode.ref; if (ref && lastVnode.vtype === 2) { @@ -2189,8 +2189,8 @@ function updateComponent(lastVnode, nextVnode, vparent, context, mountQueue) { instance.componentWillReceiveProps(nextProps, nextContext); instance.__receiving = false; } - if (!mountQueue.executor) { - mountQueue.executor = true; + if (!updateQueue.executor) { + updateQueue.executor = true; } // shouldComponentUpdate为false时不能阻止setState/forceUpdate cb的触发 @@ -2200,20 +2200,20 @@ function updateComponent(lastVnode, nextVnode, vparent, context, mountQueue) { nextVnode.parentContext = context; nextVnode.vparent = vparent; var queue; - if (mountQueue.isChildProcess) { - queue = mountQueue; + if (updateQueue.isChildProcess) { + queue = updateQueue; } else { queue = []; queue.isChildProcess = true; } _refreshComponent(instance, queue); //子组件先执行 - mountQueue.unshift(instance); + updateQueue.unshift(instance); return instance.__dom; } -function _refreshComponent(instance, mountQueue) { +function _refreshComponent(instance, updateQueue) { var lastProps = instance.props, lastState = instance.state, lastContext = instance.context, @@ -2248,44 +2248,44 @@ function _refreshComponent(instance, mountQueue) { //这里会更新instance的props, context, state var nextRendered = renderComponent.call(instance, nextVnode, nextProps, nextContext, nextState); if (lastRendered !== nextRendered && parentContext) { - dom = alignVnode(lastRendered, nextRendered, vparent, getChildContext(instance, parentContext), mountQueue); + dom = alignVnode(lastRendered, nextRendered, vparent, getChildContext(instance, parentContext), updateQueue); } createInstanceChain(instance, nextVnode, nextRendered); updateInstanceChain(instance, dom); instance.__lifeStage = 2; instance.__hydrating = false; - if (mountQueue.isChildProcess) { - clearRefsAndMounts(mountQueue); + if (updateQueue.isChildProcess) { + clearScheduler(updateQueue); } return dom; } -function alignVnode(lastVnode, nextVnode, vparent, context, mountQueue) { +function alignVnode(lastVnode, nextVnode, vparent, context, updateQueue) { var node = lastVnode._hostNode, dom = void 0; if (isSameNode(lastVnode, nextVnode)) { - dom = updateVnode(lastVnode, nextVnode, vparent, context, mountQueue); + dom = updateVnode(lastVnode, nextVnode, vparent, context, updateQueue); } else { disposeVnode(lastVnode); - // let innerMountQueue = mountQueue.isChildProcess - // ? mountQueue - // : nextVnode.vtype === 2 ? [] : mountQueue; - dom = mountVnode(null, nextVnode, vparent, context, mountQueue); + // let innerUpdateQueue = updateQueue.isChildProcess + // ? updateQueue + // : nextVnode.vtype === 2 ? [] : updateQueue; + dom = mountVnode(null, nextVnode, vparent, context, updateQueue); var p = node.parentNode; if (p) { p.replaceChild(dom, node); removeDOMElement(node); } - // if (innerMountQueue !== mountQueue) { - // clearRefsAndMounts(innerMountQueue); + // if (innerUpdateQueue !== updateQueue) { + // clearScheduler(innerUpdateQueue); // } } return dom; } -function updateElement(lastVnode, nextVnode, vparent, context, mountQueue) { +function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { var dom = lastVnode._hostNode; var lastProps = lastVnode.props; var nextProps = nextVnode.props; @@ -2298,13 +2298,13 @@ function updateElement(lastVnode, nextVnode, vparent, context, mountQueue) { }); list.length = 0; } else { - if (lastProps[innerHTML] || lastProps.children.length) { + if (lastProps[innerHTML]) { while (dom.firstChild) { dom.removeChild(dom.firstChild); } - mountChildren(dom, nextVnode, context, mountQueue); + mountChildren(dom, nextVnode, context, updateQueue); } else { - diffChildren(lastVnode, nextVnode, dom, context, mountQueue); + diffChildren(lastVnode, nextVnode, dom, context, updateQueue); } } @@ -2320,7 +2320,7 @@ function updateElement(lastVnode, nextVnode, vparent, context, mountQueue) { return dom; } -function diffChildren(lastVnode, nextVnode, parentNode, context, mountQueue) { +function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { var lastChildren = lastVnode.vchildren, nextChildren = flattenChildren(nextVnode), nextLength = nextChildren.length, @@ -2329,12 +2329,12 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, mountQueue) { //如果旧数组长度为零 if (nextLength && !lastLength) { return nextChildren.forEach(function (vnode) { - var curNode = mountVnode(null, vnode, lastVnode, context, mountQueue); + var curNode = mountVnode(null, vnode, lastVnode, context, updateQueue); parentNode.appendChild(curNode); }); } if (nextLength === lastLength && lastLength === 1) { - return alignVnode(lastChildren[0], nextChildren[0], lastVnode, context, mountQueue); + return alignVnode(lastChildren[0], nextChildren[0], lastVnode, context, updateQueue); } var maxLength = Math.max(nextLength, lastLength), insertPoint = parentNode.firstChild, @@ -2346,7 +2346,7 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, mountQueue) { dom = void 0, oldDom = void 0, - // hasExecutor = mountQueue.executor, + // hasExecutor = updateQueue.executor, nextChild = void 0, lastChild = void 0; //第一次循环,构建移动指令(actions)与移除名单(removeHits)与命中名单(fuzzyHits) @@ -2402,7 +2402,7 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, mountQueue) { oldChild = fuzzyHits[hit].shift(); oldDom = oldChild._hostNode; parentNode.insertBefore(oldDom, insertPoint); - dom = updateVnode(oldChild, curChild, lastVnode, context, mountQueue); + dom = updateVnode(oldChild, curChild, lastVnode, context, updateQueue); removeHits[oldChild._i] = true; } else { //为了兼容 react stack reconciliation的执行顺序,添加下面三行, @@ -2412,7 +2412,7 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, mountQueue) { disposeVnode(removed); } //如果找不到对应的旧节点,创建一个新节点放在这里 - dom = mountVnode(null, curChild, lastVnode, context, mountQueue); + dom = mountVnode(null, curChild, lastVnode, context, updateQueue); parentNode.insertBefore(dom, insertPoint); } } else { @@ -2420,7 +2420,7 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, mountQueue) { if (action.action === "moveAfter") { parentNode.insertBefore(oldDom, insertPoint); } - dom = updateVnode(action.last, action.next, lastVnode, context, mountQueue); + dom = updateVnode(action.last, action.next, lastVnode, context, updateQueue); } insertPoint = dom.nextSibling; } @@ -2434,8 +2434,8 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, mountQueue) { disposeVnode(el); } }); - // if (!hasExecutor && mountQueue.executor) { - // clearRefsAndMounts(mountQueue); + // if (!hasExecutor && updateQueue.executor) { + // clearScheduler(updateQueue); // } } @@ -2484,7 +2484,7 @@ function callUpdate(instance) { } } -function clearRefsAndMounts(queue) { +function clearScheduler(queue) { options.beforePatch(); //先执行所有refs方法(从上到下) clearRefs(); @@ -2536,10 +2536,10 @@ options.flushBatchedUpdates = function (queue) { if (!queue) { queue = dirtyComponents; } - clearRefsAndMounts(queue); + clearScheduler(queue); }; -options.enqueueUpdate = function (instance) { +options.addTask = function (instance) { if (dirtyComponents.indexOf(instance) == -1) { dirtyComponents.push(instance); } diff --git a/dist/ReactSelection.js b/dist/ReactSelection.js index bb2a1b87b..04727e6f7 100644 --- a/dist/ReactSelection.js +++ b/dist/ReactSelection.js @@ -1106,7 +1106,7 @@ function setStateImpl(state, cb) { this.__renderInNextCycle = true; if (options.async) { //在事件句柄中执行setState会进行合并 - options.enqueueUpdate(this); + options.addTask(this); return; } if (this.__hydrating) { @@ -1923,16 +1923,16 @@ function renderByAnu(vnode, container, callback) { if (!(container && container.getElementsByTagName)) { throw "ReactDOM.render\u7684\u7B2C\u4E8C\u4E2A\u53C2\u6570\u9519\u8BEF"; // eslint-disable-line } - var mountQueue = [], + var updateQueue = [], rootNode = void 0, lastVnode = container.__component; if (lastVnode) { // lastVnode._hostNode = container.firstChild;?? - rootNode = alignVnode(lastVnode, vnode, getVParent(container), context, mountQueue); + rootNode = alignVnode(lastVnode, vnode, getVParent(container), context, updateQueue); } else { - mountQueue.isMainProcess = true; + updateQueue.isMainProcess = true; //如果是后端渲染生成,它的孩子中存在一个拥有data-reactroot属性的元素节点 - rootNode = genVnodes(container, vnode, context, mountQueue); + rootNode = genVnodes(container, vnode, context, updateQueue); } if (rootNode.setAttribute) { @@ -1941,7 +1941,7 @@ function renderByAnu(vnode, container, callback) { var instance = vnode._instance; container.__component = vnode; - clearRefsAndMounts(mountQueue); + clearScheduler(updateQueue); CurrentOwner.cur = null; //防止干扰 var ret = instance || rootNode; if (callback) { @@ -1951,7 +1951,7 @@ function renderByAnu(vnode, container, callback) { return ret; } -function genVnodes(container, vnode, context, mountQueue) { +function genVnodes(container, vnode, context, updateQueue) { var nodes = getNodes(container); var lastNode = null; for (var i = 0, el; el = nodes[i++];) { @@ -1961,7 +1961,7 @@ function genVnodes(container, vnode, context, mountQueue) { container.removeChild(el); } } - return container.appendChild(mountVnode(lastNode, vnode, getVParent(container), context, mountQueue)); + return container.appendChild(mountVnode(lastNode, vnode, getVParent(container), context, updateQueue)); } var patchStrategy = { @@ -2020,7 +2020,7 @@ var formElements = { input: 1 }; -function mountElement(lastNode, vnode, vparent, context, mountQueue) { +function mountElement(lastNode, vnode, vparent, context, updateQueue) { var type = vnode.type, props = vnode.props, ref = vnode.ref; @@ -2030,7 +2030,7 @@ function mountElement(lastNode, vnode, vparent, context, mountQueue) { vnode._hostNode = dom; var method = lastNode ? alignChildren : mountChildren; - method(dom, vnode, context, mountQueue); + method(dom, vnode, context, updateQueue); if (vnode.checkProps) { diffProps(props, {}, vnode, {}, dom); @@ -2046,14 +2046,14 @@ function mountElement(lastNode, vnode, vparent, context, mountQueue) { } //将虚拟DOM转换为真实DOM并插入父元素 -function mountChildren(parentNode, vparent, context, mountQueue) { +function mountChildren(parentNode, vparent, context, updateQueue) { var children = flattenChildren(vparent); for (var i = 0, n = children.length; i < n; i++) { - parentNode.appendChild(mountVnode(null, children[i], vparent, context, mountQueue)); + parentNode.appendChild(mountVnode(null, children[i], vparent, context, updateQueue)); } } -function alignChildren(parentNode, vparent, context, mountQueue) { +function alignChildren(parentNode, vparent, context, updateQueue) { var children = flattenChildren(vparent), childNodes = parentNode.childNodes, insertPoint = childNodes[0] || null, @@ -2062,7 +2062,7 @@ function alignChildren(parentNode, vparent, context, mountQueue) { for (var i = 0; i < n; i++) { var vnode = children[i]; var lastNode = childNodes[j]; - var dom = mountVnode(lastNode, vnode, vparent, context, mountQueue); + var dom = mountVnode(lastNode, vnode, vparent, context, updateQueue); if (dom === lastNode) { j++; } @@ -2074,7 +2074,7 @@ function alignChildren(parentNode, vparent, context, mountQueue) { } } -function mountComponent(lastNode, vnode, vparent, parentContext, mountQueue) { +function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { var type = vnode.type, props = vnode.props; @@ -2105,21 +2105,21 @@ function mountComponent(lastNode, vnode, vparent, parentContext, mountQueue) { var childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; - var dom = mountVnode(lastNode, rendered, vparent, childContext, mountQueue); + var dom = mountVnode(lastNode, rendered, vparent, childContext, updateQueue); createInstanceChain(instance, vnode, rendered); updateInstanceChain(instance, dom); - mountQueue.push(instance); + updateQueue.push(instance); return dom; } -function mountStateless(lastNode, vnode, vparent, parentContext, mountQueue) { +function mountStateless(lastNode, vnode, vparent, parentContext, updateQueue) { var componentContext = getContextByTypes(parentContext, vnode.type.contextTypes); var instance = new Stateless(vnode.type); var rendered = renderComponent.call(instance, vnode, vnode.props, componentContext); - var dom = mountVnode(lastNode, rendered, vparent, parentContext, mountQueue); + var dom = mountVnode(lastNode, rendered, vparent, parentContext, updateQueue); //用于refreshComponent instance.nextVnode = vnode; @@ -2128,7 +2128,7 @@ function mountStateless(lastNode, vnode, vparent, parentContext, mountQueue) { createInstanceChain(instance, vnode, rendered); updateInstanceChain(instance, dom); - mountQueue.unshift(instance); + updateQueue.unshift(instance); return dom; } @@ -2175,7 +2175,7 @@ function Stateless(render) { //Stateless.prototype.render = renderComponent; -function updateComponent(lastVnode, nextVnode, vparent, context, mountQueue) { +function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { var instance = lastVnode._instance; var ref = lastVnode.ref; if (ref && lastVnode.vtype === 2) { @@ -2189,8 +2189,8 @@ function updateComponent(lastVnode, nextVnode, vparent, context, mountQueue) { instance.componentWillReceiveProps(nextProps, nextContext); instance.__receiving = false; } - if (!mountQueue.executor) { - mountQueue.executor = true; + if (!updateQueue.executor) { + updateQueue.executor = true; } // shouldComponentUpdate为false时不能阻止setState/forceUpdate cb的触发 @@ -2200,20 +2200,20 @@ function updateComponent(lastVnode, nextVnode, vparent, context, mountQueue) { nextVnode.parentContext = context; nextVnode.vparent = vparent; var queue; - if (mountQueue.isChildProcess) { - queue = mountQueue; + if (updateQueue.isChildProcess) { + queue = updateQueue; } else { queue = []; queue.isChildProcess = true; } _refreshComponent(instance, queue); //子组件先执行 - mountQueue.unshift(instance); + updateQueue.unshift(instance); return instance.__dom; } -function _refreshComponent(instance, mountQueue) { +function _refreshComponent(instance, updateQueue) { var lastProps = instance.props, lastState = instance.state, lastContext = instance.context, @@ -2248,44 +2248,44 @@ function _refreshComponent(instance, mountQueue) { //这里会更新instance的props, context, state var nextRendered = renderComponent.call(instance, nextVnode, nextProps, nextContext, nextState); if (lastRendered !== nextRendered && parentContext) { - dom = alignVnode(lastRendered, nextRendered, vparent, getChildContext(instance, parentContext), mountQueue); + dom = alignVnode(lastRendered, nextRendered, vparent, getChildContext(instance, parentContext), updateQueue); } createInstanceChain(instance, nextVnode, nextRendered); updateInstanceChain(instance, dom); instance.__lifeStage = 2; instance.__hydrating = false; - if (mountQueue.isChildProcess) { - clearRefsAndMounts(mountQueue); + if (updateQueue.isChildProcess) { + clearScheduler(updateQueue); } return dom; } -function alignVnode(lastVnode, nextVnode, vparent, context, mountQueue) { +function alignVnode(lastVnode, nextVnode, vparent, context, updateQueue) { var node = lastVnode._hostNode, dom = void 0; if (isSameNode(lastVnode, nextVnode)) { - dom = updateVnode(lastVnode, nextVnode, vparent, context, mountQueue); + dom = updateVnode(lastVnode, nextVnode, vparent, context, updateQueue); } else { disposeVnode(lastVnode); - // let innerMountQueue = mountQueue.isChildProcess - // ? mountQueue - // : nextVnode.vtype === 2 ? [] : mountQueue; - dom = mountVnode(null, nextVnode, vparent, context, mountQueue); + // let innerUpdateQueue = updateQueue.isChildProcess + // ? updateQueue + // : nextVnode.vtype === 2 ? [] : updateQueue; + dom = mountVnode(null, nextVnode, vparent, context, updateQueue); var p = node.parentNode; if (p) { p.replaceChild(dom, node); removeDOMElement(node); } - // if (innerMountQueue !== mountQueue) { - // clearRefsAndMounts(innerMountQueue); + // if (innerUpdateQueue !== updateQueue) { + // clearScheduler(innerUpdateQueue); // } } return dom; } -function updateElement(lastVnode, nextVnode, vparent, context, mountQueue) { +function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { var dom = lastVnode._hostNode; var lastProps = lastVnode.props; var nextProps = nextVnode.props; @@ -2298,13 +2298,13 @@ function updateElement(lastVnode, nextVnode, vparent, context, mountQueue) { }); list.length = 0; } else { - if (lastProps[innerHTML] || lastProps.children.length) { + if (lastProps[innerHTML]) { while (dom.firstChild) { dom.removeChild(dom.firstChild); } - mountChildren(dom, nextVnode, context, mountQueue); + mountChildren(dom, nextVnode, context, updateQueue); } else { - diffChildren(lastVnode, nextVnode, dom, context, mountQueue); + diffChildren(lastVnode, nextVnode, dom, context, updateQueue); } } @@ -2320,7 +2320,7 @@ function updateElement(lastVnode, nextVnode, vparent, context, mountQueue) { return dom; } -function diffChildren(lastVnode, nextVnode, parentNode, context, mountQueue) { +function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { var lastChildren = lastVnode.vchildren, nextChildren = flattenChildren(nextVnode), nextLength = nextChildren.length, @@ -2329,12 +2329,12 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, mountQueue) { //如果旧数组长度为零 if (nextLength && !lastLength) { return nextChildren.forEach(function (vnode) { - var curNode = mountVnode(null, vnode, lastVnode, context, mountQueue); + var curNode = mountVnode(null, vnode, lastVnode, context, updateQueue); parentNode.appendChild(curNode); }); } if (nextLength === lastLength && lastLength === 1) { - return alignVnode(lastChildren[0], nextChildren[0], lastVnode, context, mountQueue); + return alignVnode(lastChildren[0], nextChildren[0], lastVnode, context, updateQueue); } var maxLength = Math.max(nextLength, lastLength), insertPoint = parentNode.firstChild, @@ -2346,7 +2346,7 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, mountQueue) { dom = void 0, oldDom = void 0, - // hasExecutor = mountQueue.executor, + // hasExecutor = updateQueue.executor, nextChild = void 0, lastChild = void 0; //第一次循环,构建移动指令(actions)与移除名单(removeHits)与命中名单(fuzzyHits) @@ -2402,7 +2402,7 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, mountQueue) { oldChild = fuzzyHits[hit].shift(); oldDom = oldChild._hostNode; parentNode.insertBefore(oldDom, insertPoint); - dom = updateVnode(oldChild, curChild, lastVnode, context, mountQueue); + dom = updateVnode(oldChild, curChild, lastVnode, context, updateQueue); removeHits[oldChild._i] = true; } else { //为了兼容 react stack reconciliation的执行顺序,添加下面三行, @@ -2412,7 +2412,7 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, mountQueue) { disposeVnode(removed); } //如果找不到对应的旧节点,创建一个新节点放在这里 - dom = mountVnode(null, curChild, lastVnode, context, mountQueue); + dom = mountVnode(null, curChild, lastVnode, context, updateQueue); parentNode.insertBefore(dom, insertPoint); } } else { @@ -2420,7 +2420,7 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, mountQueue) { if (action.action === "moveAfter") { parentNode.insertBefore(oldDom, insertPoint); } - dom = updateVnode(action.last, action.next, lastVnode, context, mountQueue); + dom = updateVnode(action.last, action.next, lastVnode, context, updateQueue); } insertPoint = dom.nextSibling; } @@ -2434,8 +2434,8 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, mountQueue) { disposeVnode(el); } }); - // if (!hasExecutor && mountQueue.executor) { - // clearRefsAndMounts(mountQueue); + // if (!hasExecutor && updateQueue.executor) { + // clearScheduler(updateQueue); // } } @@ -2484,7 +2484,7 @@ function callUpdate(instance) { } } -function clearRefsAndMounts(queue) { +function clearScheduler(queue) { options.beforePatch(); //先执行所有refs方法(从上到下) clearRefs(); @@ -2536,10 +2536,10 @@ options.flushBatchedUpdates = function (queue) { if (!queue) { queue = dirtyComponents; } - clearRefsAndMounts(queue); + clearScheduler(queue); }; -options.enqueueUpdate = function (instance) { +options.addTask = function (instance) { if (dirtyComponents.indexOf(instance) == -1) { dirtyComponents.push(instance); } diff --git a/dist/ReactShim.js b/dist/ReactShim.js index d9be0b707..5a74f0d6d 100644 --- a/dist/ReactShim.js +++ b/dist/ReactShim.js @@ -545,7 +545,7 @@ function setStateImpl(state, cb) { this.__renderInNextCycle = true; if (options.async) { //在事件句柄中执行setState会进行合并 - options.enqueueUpdate(this); + options.addTask(this); return; } if (this.__hydrating) { @@ -1765,16 +1765,16 @@ function renderByAnu(vnode, container, callback) { if (!(container && container.getElementsByTagName)) { throw "ReactDOM.render\u7684\u7B2C\u4E8C\u4E2A\u53C2\u6570\u9519\u8BEF"; // eslint-disable-line } - var mountQueue = [], + var updateQueue = [], rootNode = void 0, lastVnode = container.__component; if (lastVnode) { // lastVnode._hostNode = container.firstChild;?? - rootNode = alignVnode(lastVnode, vnode, getVParent(container), context, mountQueue); + rootNode = alignVnode(lastVnode, vnode, getVParent(container), context, updateQueue); } else { - mountQueue.isMainProcess = true; + updateQueue.isMainProcess = true; //如果是后端渲染生成,它的孩子中存在一个拥有data-reactroot属性的元素节点 - rootNode = genVnodes(container, vnode, context, mountQueue); + rootNode = genVnodes(container, vnode, context, updateQueue); } if (rootNode.setAttribute) { @@ -1783,7 +1783,7 @@ function renderByAnu(vnode, container, callback) { var instance = vnode._instance; container.__component = vnode; - clearRefsAndMounts(mountQueue); + clearScheduler(updateQueue); CurrentOwner.cur = null; //防止干扰 var ret = instance || rootNode; if (callback) { @@ -1793,7 +1793,7 @@ function renderByAnu(vnode, container, callback) { return ret; } -function genVnodes(container, vnode, context, mountQueue) { +function genVnodes(container, vnode, context, updateQueue) { var nodes = getNodes(container); var lastNode = null; for (var i = 0, el; el = nodes[i++];) { @@ -1803,7 +1803,7 @@ function genVnodes(container, vnode, context, mountQueue) { container.removeChild(el); } } - return container.appendChild(mountVnode(lastNode, vnode, getVParent(container), context, mountQueue)); + return container.appendChild(mountVnode(lastNode, vnode, getVParent(container), context, updateQueue)); } var patchStrategy = { @@ -1862,7 +1862,7 @@ var formElements = { input: 1 }; -function mountElement(lastNode, vnode, vparent, context, mountQueue) { +function mountElement(lastNode, vnode, vparent, context, updateQueue) { var type = vnode.type, props = vnode.props, ref = vnode.ref; @@ -1872,7 +1872,7 @@ function mountElement(lastNode, vnode, vparent, context, mountQueue) { vnode._hostNode = dom; var method = lastNode ? alignChildren : mountChildren; - method(dom, vnode, context, mountQueue); + method(dom, vnode, context, updateQueue); if (vnode.checkProps) { diffProps(props, {}, vnode, {}, dom); @@ -1888,14 +1888,14 @@ function mountElement(lastNode, vnode, vparent, context, mountQueue) { } //将虚拟DOM转换为真实DOM并插入父元素 -function mountChildren(parentNode, vparent, context, mountQueue) { +function mountChildren(parentNode, vparent, context, updateQueue) { var children = flattenChildren(vparent); for (var i = 0, n = children.length; i < n; i++) { - parentNode.appendChild(mountVnode(null, children[i], vparent, context, mountQueue)); + parentNode.appendChild(mountVnode(null, children[i], vparent, context, updateQueue)); } } -function alignChildren(parentNode, vparent, context, mountQueue) { +function alignChildren(parentNode, vparent, context, updateQueue) { var children = flattenChildren(vparent), childNodes = parentNode.childNodes, insertPoint = childNodes[0] || null, @@ -1904,7 +1904,7 @@ function alignChildren(parentNode, vparent, context, mountQueue) { for (var i = 0; i < n; i++) { var vnode = children[i]; var lastNode = childNodes[j]; - var dom = mountVnode(lastNode, vnode, vparent, context, mountQueue); + var dom = mountVnode(lastNode, vnode, vparent, context, updateQueue); if (dom === lastNode) { j++; } @@ -1916,7 +1916,7 @@ function alignChildren(parentNode, vparent, context, mountQueue) { } } -function mountComponent(lastNode, vnode, vparent, parentContext, mountQueue) { +function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { var type = vnode.type, props = vnode.props; @@ -1947,21 +1947,21 @@ function mountComponent(lastNode, vnode, vparent, parentContext, mountQueue) { var childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; - var dom = mountVnode(lastNode, rendered, vparent, childContext, mountQueue); + var dom = mountVnode(lastNode, rendered, vparent, childContext, updateQueue); createInstanceChain(instance, vnode, rendered); updateInstanceChain(instance, dom); - mountQueue.push(instance); + updateQueue.push(instance); return dom; } -function mountStateless(lastNode, vnode, vparent, parentContext, mountQueue) { +function mountStateless(lastNode, vnode, vparent, parentContext, updateQueue) { var componentContext = getContextByTypes(parentContext, vnode.type.contextTypes); var instance = new Stateless(vnode.type); var rendered = renderComponent.call(instance, vnode, vnode.props, componentContext); - var dom = mountVnode(lastNode, rendered, vparent, parentContext, mountQueue); + var dom = mountVnode(lastNode, rendered, vparent, parentContext, updateQueue); //用于refreshComponent instance.nextVnode = vnode; @@ -1970,7 +1970,7 @@ function mountStateless(lastNode, vnode, vparent, parentContext, mountQueue) { createInstanceChain(instance, vnode, rendered); updateInstanceChain(instance, dom); - mountQueue.unshift(instance); + updateQueue.unshift(instance); return dom; } @@ -2017,7 +2017,7 @@ function Stateless(render) { //Stateless.prototype.render = renderComponent; -function updateComponent(lastVnode, nextVnode, vparent, context, mountQueue) { +function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { var instance = lastVnode._instance; var ref = lastVnode.ref; if (ref && lastVnode.vtype === 2) { @@ -2031,8 +2031,8 @@ function updateComponent(lastVnode, nextVnode, vparent, context, mountQueue) { instance.componentWillReceiveProps(nextProps, nextContext); instance.__receiving = false; } - if (!mountQueue.executor) { - mountQueue.executor = true; + if (!updateQueue.executor) { + updateQueue.executor = true; } // shouldComponentUpdate为false时不能阻止setState/forceUpdate cb的触发 @@ -2042,20 +2042,20 @@ function updateComponent(lastVnode, nextVnode, vparent, context, mountQueue) { nextVnode.parentContext = context; nextVnode.vparent = vparent; var queue; - if (mountQueue.isChildProcess) { - queue = mountQueue; + if (updateQueue.isChildProcess) { + queue = updateQueue; } else { queue = []; queue.isChildProcess = true; } _refreshComponent(instance, queue); //子组件先执行 - mountQueue.unshift(instance); + updateQueue.unshift(instance); return instance.__dom; } -function _refreshComponent(instance, mountQueue) { +function _refreshComponent(instance, updateQueue) { var lastProps = instance.props, lastState = instance.state, lastContext = instance.context, @@ -2090,44 +2090,44 @@ function _refreshComponent(instance, mountQueue) { //这里会更新instance的props, context, state var nextRendered = renderComponent.call(instance, nextVnode, nextProps, nextContext, nextState); if (lastRendered !== nextRendered && parentContext) { - dom = alignVnode(lastRendered, nextRendered, vparent, getChildContext(instance, parentContext), mountQueue); + dom = alignVnode(lastRendered, nextRendered, vparent, getChildContext(instance, parentContext), updateQueue); } createInstanceChain(instance, nextVnode, nextRendered); updateInstanceChain(instance, dom); instance.__lifeStage = 2; instance.__hydrating = false; - if (mountQueue.isChildProcess) { - clearRefsAndMounts(mountQueue); + if (updateQueue.isChildProcess) { + clearScheduler(updateQueue); } return dom; } -function alignVnode(lastVnode, nextVnode, vparent, context, mountQueue) { +function alignVnode(lastVnode, nextVnode, vparent, context, updateQueue) { var node = lastVnode._hostNode, dom = void 0; if (isSameNode(lastVnode, nextVnode)) { - dom = updateVnode(lastVnode, nextVnode, vparent, context, mountQueue); + dom = updateVnode(lastVnode, nextVnode, vparent, context, updateQueue); } else { disposeVnode(lastVnode); - // let innerMountQueue = mountQueue.isChildProcess - // ? mountQueue - // : nextVnode.vtype === 2 ? [] : mountQueue; - dom = mountVnode(null, nextVnode, vparent, context, mountQueue); + // let innerUpdateQueue = updateQueue.isChildProcess + // ? updateQueue + // : nextVnode.vtype === 2 ? [] : updateQueue; + dom = mountVnode(null, nextVnode, vparent, context, updateQueue); var p = node.parentNode; if (p) { p.replaceChild(dom, node); removeDOMElement(node); } - // if (innerMountQueue !== mountQueue) { - // clearRefsAndMounts(innerMountQueue); + // if (innerUpdateQueue !== updateQueue) { + // clearScheduler(innerUpdateQueue); // } } return dom; } -function updateElement(lastVnode, nextVnode, vparent, context, mountQueue) { +function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { var dom = lastVnode._hostNode; var lastProps = lastVnode.props; var nextProps = nextVnode.props; @@ -2140,13 +2140,13 @@ function updateElement(lastVnode, nextVnode, vparent, context, mountQueue) { }); list.length = 0; } else { - if (lastProps[innerHTML] || lastProps.children.length) { + if (lastProps[innerHTML]) { while (dom.firstChild) { dom.removeChild(dom.firstChild); } - mountChildren(dom, nextVnode, context, mountQueue); + mountChildren(dom, nextVnode, context, updateQueue); } else { - diffChildren(lastVnode, nextVnode, dom, context, mountQueue); + diffChildren(lastVnode, nextVnode, dom, context, updateQueue); } } @@ -2162,7 +2162,7 @@ function updateElement(lastVnode, nextVnode, vparent, context, mountQueue) { return dom; } -function diffChildren(lastVnode, nextVnode, parentNode, context, mountQueue) { +function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { var lastChildren = lastVnode.vchildren, nextChildren = flattenChildren(nextVnode), nextLength = nextChildren.length, @@ -2171,12 +2171,12 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, mountQueue) { //如果旧数组长度为零 if (nextLength && !lastLength) { return nextChildren.forEach(function (vnode) { - var curNode = mountVnode(null, vnode, lastVnode, context, mountQueue); + var curNode = mountVnode(null, vnode, lastVnode, context, updateQueue); parentNode.appendChild(curNode); }); } if (nextLength === lastLength && lastLength === 1) { - return alignVnode(lastChildren[0], nextChildren[0], lastVnode, context, mountQueue); + return alignVnode(lastChildren[0], nextChildren[0], lastVnode, context, updateQueue); } var maxLength = Math.max(nextLength, lastLength), insertPoint = parentNode.firstChild, @@ -2188,7 +2188,7 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, mountQueue) { dom = void 0, oldDom = void 0, - // hasExecutor = mountQueue.executor, + // hasExecutor = updateQueue.executor, nextChild = void 0, lastChild = void 0; //第一次循环,构建移动指令(actions)与移除名单(removeHits)与命中名单(fuzzyHits) @@ -2244,7 +2244,7 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, mountQueue) { oldChild = fuzzyHits[hit].shift(); oldDom = oldChild._hostNode; parentNode.insertBefore(oldDom, insertPoint); - dom = updateVnode(oldChild, curChild, lastVnode, context, mountQueue); + dom = updateVnode(oldChild, curChild, lastVnode, context, updateQueue); removeHits[oldChild._i] = true; } else { //为了兼容 react stack reconciliation的执行顺序,添加下面三行, @@ -2254,7 +2254,7 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, mountQueue) { disposeVnode(removed); } //如果找不到对应的旧节点,创建一个新节点放在这里 - dom = mountVnode(null, curChild, lastVnode, context, mountQueue); + dom = mountVnode(null, curChild, lastVnode, context, updateQueue); parentNode.insertBefore(dom, insertPoint); } } else { @@ -2262,7 +2262,7 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, mountQueue) { if (action.action === "moveAfter") { parentNode.insertBefore(oldDom, insertPoint); } - dom = updateVnode(action.last, action.next, lastVnode, context, mountQueue); + dom = updateVnode(action.last, action.next, lastVnode, context, updateQueue); } insertPoint = dom.nextSibling; } @@ -2276,8 +2276,8 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, mountQueue) { disposeVnode(el); } }); - // if (!hasExecutor && mountQueue.executor) { - // clearRefsAndMounts(mountQueue); + // if (!hasExecutor && updateQueue.executor) { + // clearScheduler(updateQueue); // } } @@ -2326,7 +2326,7 @@ function callUpdate(instance) { } } -function clearRefsAndMounts(queue) { +function clearScheduler(queue) { options.beforePatch(); //先执行所有refs方法(从上到下) clearRefs(); @@ -2378,10 +2378,10 @@ options.flushBatchedUpdates = function (queue) { if (!queue) { queue = dirtyComponents; } - clearRefsAndMounts(queue); + clearScheduler(queue); }; -options.enqueueUpdate = function (instance) { +options.addTask = function (instance) { if (dirtyComponents.indexOf(instance) == -1) { dirtyComponents.push(instance); } diff --git a/src/diff.js b/src/diff.js index e3c7a63c8..47f9e0809 100644 --- a/src/diff.js +++ b/src/diff.js @@ -477,7 +477,7 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { }); list.length = 0; } else { - if (lastProps[innerHTML] || lastProps.children.length) { + if (lastProps[innerHTML]) { while (dom.firstChild) { dom.removeChild(dom.firstChild); } From d076f44f9d556e0c558db549d0667f4cff990eed Mon Sep 17 00:00:00 2001 From: RubyLouvre <1669866773@qq.com> Date: Thu, 28 Sep 2017 16:02:46 +0800 Subject: [PATCH 06/57] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=90=8C=E6=AD=A5?= =?UTF-8?q?=E5=88=B0=E5=85=B6=E4=BB=96=E7=9B=AE=E5=BD=95=E7=9A=84=E8=84=9A?= =?UTF-8?q?=E6=9C=AC=EF=BC=8C=E5=8E=BB=E6=8E=89=E4=B8=8D=E7=94=A8=E7=9A=84?= =?UTF-8?q?=E5=8F=98=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build/shim.script.js | 4 +-- build/sync.js | 59 ++++++++++++++++++++++++++++++++++++++++++ dist/React.js | 7 +++-- dist/ReactIE.js | 7 +++-- dist/ReactSelection.js | 7 +++-- dist/ReactShim.js | 7 +++-- package.json | 5 +++- src/React.js | 2 +- src/browser.js | 2 +- src/createElement.js | 2 +- src/diff.js | 5 +++- 11 files changed, 92 insertions(+), 15 deletions(-) create mode 100644 build/sync.js diff --git a/build/shim.script.js b/build/shim.script.js index 8b5f966ad..697173931 100644 --- a/build/shim.script.js +++ b/build/shim.script.js @@ -19,9 +19,9 @@ var text2 = str2 fs.writeFileSync(dir2, text2, { encoding: "utf8" }); //fs.writeFileSync( path.join(__dirname, "../../draft/node_modules/anujs/dist/React.js"), text2, { encoding: "utf8" }); -fs.writeFileSync( path.join(__dirname, "../../antd-test/node_modules/anujs/dist/React.js"), text2, { encoding: "utf8" }); +//fs.writeFileSync( path.join(__dirname, "../../antd-test/node_modules/anujs/dist/React.js"), text2, { encoding: "utf8" }); -//fs.writeFileSync( path.join(__dirname, "../../yo-demo/node_modules/anujs/dist/React.js"), text2, { encoding: "utf8" }); +fs.writeFileSync( path.join(__dirname, "../../yo-demo/node_modules/anujs/dist/React.js"), text2, { encoding: "utf8" }); //fs.writeFileSync( path.join(__dirname, "../../yo-router/node_modules/anujs/dist/React.js"), text2, { encoding: "utf8" }); console.log("对React瘦身完毕"); // eslint-disable-line diff --git a/build/sync.js b/build/sync.js new file mode 100644 index 000000000..8e60d0e25 --- /dev/null +++ b/build/sync.js @@ -0,0 +1,59 @@ +const fs = require("fs-extra"); +const path = require("path"); +const ora = require("ora"); + +const anuPath = path.join(__dirname, "../"); +const qreactPath = path.join(__dirname, "../../qreact"); +const dirs = ["build", "lib", "src", "ssr"]; +const spinner = ora("开始同步").start(); + +function empty(dir) { + return new Promise((resolve, reject) => { + fs.emptyDir(path.join(qreactPath, dir), err => { + if (err) { + reject(err); + } else { + resolve(); + } + }); + }); +} + +function copy(dir) { + return new Promise((resolve, reject) => { + fs.copy(path.join(anuPath, dir), path.join(qreactPath, dir), err => { + if (err) { + reject(err); + } else { + resolve(); + } + }); + }); +} + +function emptyDirs(dirs) { + return Promise.all(dirs.map(dir => empty(dir))); +} + +function copyDirs(dirs) { + return Promise.all(dirs.map(dir => copy(dir))); +} + +function start() { + const emptyPromise = emptyDirs(dirs); + emptyPromise + .then(() => { + spinner.succeed("已清除 QReact 下目录"); + return copyDirs(dirs); + }) + .then(() => { + spinner.succeed("已复制 anujs 至 QReact"); + }) + + .catch(e => { + console.error(e); // eslint-disable-line + spinner.fail("同步失败"); + }); +} + +start(); diff --git a/dist/React.js b/dist/React.js index d082143fb..33ef71ee9 100644 --- a/dist/React.js +++ b/dist/React.js @@ -226,7 +226,7 @@ function createElement(type, config) { if (isFn(type)) { vtype = type.prototype && type.prototype.render ? 2 : 4; } else if (type + "" !== type) { - console.error("createElement第一个参数类型错误"); + console.error("createElement第一个参数类型错误"); // eslint-disable-line } if (config != null) { for (var i in config) { @@ -2237,7 +2237,7 @@ function _refreshComponent(instance, updateQueue) { instance.__forceUpdate = false; return dom; } - + // clearRefs(); instance.__hydrating = true; instance.__forceUpdate = false; if (instance.componentWillUpdate) { @@ -2496,6 +2496,9 @@ function clearScheduler(queue) { var instance = queue[i]; i++; if (!instance.__lifeStage) { + if (pendingRefs.length) { + clearRefs(); + } if (instance.componentDidMount) { instance.componentDidMount(); instance.componentDidMount = null; diff --git a/dist/ReactIE.js b/dist/ReactIE.js index 04727e6f7..f48d2b5d1 100644 --- a/dist/ReactIE.js +++ b/dist/ReactIE.js @@ -225,7 +225,7 @@ function createElement(type, config) { if (isFn(type)) { vtype = type.prototype && type.prototype.render ? 2 : 4; } else if (type + "" !== type) { - console.error("createElement第一个参数类型错误"); + console.error("createElement第一个参数类型错误"); // eslint-disable-line } if (config != null) { for (var i in config) { @@ -2236,7 +2236,7 @@ function _refreshComponent(instance, updateQueue) { instance.__forceUpdate = false; return dom; } - + // clearRefs(); instance.__hydrating = true; instance.__forceUpdate = false; if (instance.componentWillUpdate) { @@ -2495,6 +2495,9 @@ function clearScheduler(queue) { var instance = queue[i]; i++; if (!instance.__lifeStage) { + if (pendingRefs.length) { + clearRefs(); + } if (instance.componentDidMount) { instance.componentDidMount(); instance.componentDidMount = null; diff --git a/dist/ReactSelection.js b/dist/ReactSelection.js index 04727e6f7..f48d2b5d1 100644 --- a/dist/ReactSelection.js +++ b/dist/ReactSelection.js @@ -225,7 +225,7 @@ function createElement(type, config) { if (isFn(type)) { vtype = type.prototype && type.prototype.render ? 2 : 4; } else if (type + "" !== type) { - console.error("createElement第一个参数类型错误"); + console.error("createElement第一个参数类型错误"); // eslint-disable-line } if (config != null) { for (var i in config) { @@ -2236,7 +2236,7 @@ function _refreshComponent(instance, updateQueue) { instance.__forceUpdate = false; return dom; } - + // clearRefs(); instance.__hydrating = true; instance.__forceUpdate = false; if (instance.componentWillUpdate) { @@ -2495,6 +2495,9 @@ function clearScheduler(queue) { var instance = queue[i]; i++; if (!instance.__lifeStage) { + if (pendingRefs.length) { + clearRefs(); + } if (instance.componentDidMount) { instance.componentDidMount(); instance.componentDidMount = null; diff --git a/dist/ReactShim.js b/dist/ReactShim.js index 5a74f0d6d..30a9d41ec 100644 --- a/dist/ReactShim.js +++ b/dist/ReactShim.js @@ -227,7 +227,7 @@ function createElement(type, config) { if (isFn(type)) { vtype = type.prototype && type.prototype.render ? 2 : 4; } else if (type + "" !== type) { - console.error("createElement第一个参数类型错误"); + console.error("createElement第一个参数类型错误"); // eslint-disable-line } if (config != null) { for (var i in config) { @@ -2078,7 +2078,7 @@ function _refreshComponent(instance, updateQueue) { instance.__forceUpdate = false; return dom; } - + // clearRefs(); instance.__hydrating = true; instance.__forceUpdate = false; if (instance.componentWillUpdate) { @@ -2337,6 +2337,9 @@ function clearScheduler(queue) { var instance = queue[i]; i++; if (!instance.__lifeStage) { + if (pendingRefs.length) { + clearRefs(); + } if (instance.componentDidMount) { instance.componentDidMount(); instance.componentDidMount = null; diff --git a/package.json b/package.json index 95c41cdfd..ac5a031fb 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,8 @@ "build": "rollup -c build/rollup.js && rollup -c build/rollup.ie.js && rollup -c build/rollup.selection.js && rollup -c build/rollup.shim.js && rollup -c build/rollup.ssr.js && node build/shim.script.js ", "test": "node node_modules/karma-event-driver-ext", "devtools": "rollup -c build/rollup.tools.js", - "webpack": "^1.14.0" + "webpack": "^1.14.0", + "sync": "node ./build/sync.js" }, "repository": { "type": "git", @@ -44,6 +45,7 @@ "es3ify-webpack-plugin": "0.0.1", "eslint": "^4.2.0", "eslint-plugin-react": "^7.1.0", + "fs-extra": "^4.0.2", "husky": "^0.14.3", "karma": "^1.5", "karma-chai": "^0.1.0", @@ -55,6 +57,7 @@ "karma-spec-reporter": "0.0.30", "karma-webpack": "^2.0.3", "mocha": "^3.2.0", + "ora": "^1.3.0", "rollup": "^0.41.6", "rollup-plugin-babel": "^2.7.1", "rollup-plugin-filesize": "^1.4.2", diff --git a/src/React.js b/src/React.js index 30eb4b8bb..d62742c4f 100644 --- a/src/React.js +++ b/src/React.js @@ -9,7 +9,7 @@ import { cloneElement } from "./cloneElement"; import { PureComponent } from "./PureComponent"; import { createElement } from "./createElement"; -import { render,pendingRefs, findDOMNode, isValidElement, unmountComponentAtNode, unstable_renderSubtreeIntoContainer } from "./diff"; +import { render,findDOMNode, isValidElement, unmountComponentAtNode, unstable_renderSubtreeIntoContainer } from "./diff"; var React = { version: "VERSION", diff --git a/src/browser.js b/src/browser.js index 1860a815a..e2ba62879 100644 --- a/src/browser.js +++ b/src/browser.js @@ -1,4 +1,4 @@ -import { oneObject, recyclables, typeNumber } from "./util"; +import { recyclables, typeNumber } from "./util"; //用于后端的元素节点 export function DOMElement(type) { diff --git a/src/createElement.js b/src/createElement.js index efc4442ac..8292eee6a 100644 --- a/src/createElement.js +++ b/src/createElement.js @@ -24,7 +24,7 @@ export function createElement(type, config, ...children) { ? 2 : 4; } else if (type + "" !== type) { - console.error("createElement第一个参数类型错误"); + console.error("createElement第一个参数类型错误"); // eslint-disable-line } if (config != null) { for (let i in config) { diff --git a/src/diff.js b/src/diff.js index 47f9e0809..19d74001c 100644 --- a/src/diff.js +++ b/src/diff.js @@ -403,7 +403,7 @@ function _refreshComponent(instance, updateQueue) { instance.__forceUpdate = false; return dom; } - + // clearRefs(); instance.__hydrating = true; instance.__forceUpdate = false; if (instance.componentWillUpdate) { @@ -682,6 +682,9 @@ function clearScheduler(queue) { var instance = queue[i]; i++; if (!instance.__lifeStage) { + if(pendingRefs.length){ + clearRefs(); + } if (instance.componentDidMount) { instance.componentDidMount(); instance.componentDidMount = null; From 3788fb6ed2631e543c2c75c558ce8d11bf6eafbf Mon Sep 17 00:00:00 2001 From: RubyLouvre <1669866773@qq.com> Date: Thu, 28 Sep 2017 17:39:40 +0800 Subject: [PATCH 07/57] =?UTF-8?q?=E5=88=86=E7=A6=BB=E5=87=BA=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E8=B0=83=E5=BA=A6=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dist/React.js | 182 +++++++++--------- dist/ReactIE.js | 182 +++++++++--------- dist/ReactSelection.js | 182 +++++++++--------- dist/ReactShim.js | 182 +++++++++--------- index4.html | 142 +++----------- src/diff.js | 101 +--------- src/scheduler.js | 93 +++++++++ test/modules/ReactComponentLifeCycle-test.jsx | 141 ++++++++++++++ test/spec.js | 4 +- 9 files changed, 633 insertions(+), 576 deletions(-) create mode 100644 src/scheduler.js diff --git a/dist/React.js b/dist/React.js index 33ef71ee9..7454c6de2 100644 --- a/dist/React.js +++ b/dist/React.js @@ -1872,6 +1872,93 @@ function getOptionSelected(option, selected) { dom.selected = selected; } +var pendingRefs = []; +function clearRefs() { + var refs = pendingRefs.slice(0); + pendingRefs.length = 0; + refs.forEach(function (fn) { + fn(); + }); +} +function callUpdate(instance) { + if (instance.__lifeStage === 2) { + if (instance.componentDidUpdate) { + instance.__didUpdate = true; + instance.componentDidUpdate(instance.lastProps, instance.lastState, instance.lastContext); + if (!instance.__renderInNextCycle) { + instance.__didUpdate = false; + } + } + options.afterUpdate(instance); + instance.__lifeStage = 1; + } +} + +function drainQueue(queue) { + options.beforePatch(); + //先执行所有refs方法(从上到下) + clearRefs(); + //再执行所有mount/update钩子(从下到上) + var i = 0; + while (i < queue.length) { + //queue可能中途加入新元素, 因此不能直接使用queue.forEach(fn) + var instance = queue[i]; + i++; + if (!instance.__lifeStage) { + if (pendingRefs.length) { + clearRefs(); + } + if (instance.componentDidMount) { + instance.componentDidMount(); + instance.componentDidMount = null; + } + instance.__lifeStage = 1; + options.afterMount(instance); + } else { + callUpdate(instance); + } + var ref = instance.__current.ref; + if (ref) { + ref(instance.__mergeStates ? instance : null); + } + instance.__hydrating = false; //子树已经构建完毕 + while (instance.__renderInNextCycle) { + options._refreshComponent(instance, queue); + callUpdate(instance); + } + } + //再执行所有setState/forceUpdate回调,根据从下到上的顺序执行 + queue.sort(mountSorter).forEach(function (instance) { + clearArray(instance.__pendingCallbacks).forEach(function (fn) { + fn.call(instance); + }); + }); + queue.length = 0; + options.afterPatch(); +} + +//有一个列队, 先放进A组件与A组件回调 +var dirtyComponents = []; +dirtyComponents.isChildProcess = true; + +function mountSorter(c1, c2) { + //让子节点先于父节点执行 + return c2.__mountOrder - c1.__mountOrder; +} + +options.flushBatchedUpdates = function (queue) { + if (!queue) { + queue = dirtyComponents; + } + drainQueue(queue); +}; + +options.addTask = function (instance) { + if (dirtyComponents.indexOf(instance) == -1) { + dirtyComponents.push(instance); + } +}; + //[Top API] React.isValidElement function isValidElement(vnode) { return vnode && vnode.vtype; @@ -1942,7 +2029,7 @@ function renderByAnu(vnode, container, callback) { var instance = vnode._instance; container.__component = vnode; - clearScheduler(updateQueue); + drainQueue(updateQueue); CurrentOwner.cur = null; //防止干扰 var ret = instance || rootNode; if (callback) { @@ -2257,11 +2344,12 @@ function _refreshComponent(instance, updateQueue) { instance.__lifeStage = 2; instance.__hydrating = false; if (updateQueue.isChildProcess) { - clearScheduler(updateQueue); + drainQueue(updateQueue); } return dom; } +options._refreshComponent = _refreshComponent; function alignVnode(lastVnode, nextVnode, vparent, context, updateQueue) { var node = lastVnode._hostNode, @@ -2445,8 +2533,7 @@ function isSameNode(a, b) { return true; } } -//================================= -//******* 构建实例链 ******* + function createInstanceChain(instance, vnode, rendered) { instance.__current = vnode; if (rendered._instance) { @@ -2462,93 +2549,6 @@ function updateInstanceChain(instance, dom) { } } -//******* 调度系统 ******* -var pendingRefs = []; -function clearRefs() { - var refs = pendingRefs.slice(0); - pendingRefs.length = 0; - refs.forEach(function (fn) { - fn(); - }); -} -function callUpdate(instance) { - if (instance.__lifeStage === 2) { - if (instance.componentDidUpdate) { - instance.__didUpdate = true; - instance.componentDidUpdate(instance.lastProps, instance.lastState, instance.lastContext); - if (!instance.__renderInNextCycle) { - instance.__didUpdate = false; - } - } - options.afterUpdate(instance); - instance.__lifeStage = 1; - } -} - -function clearScheduler(queue) { - options.beforePatch(); - //先执行所有refs方法(从上到下) - clearRefs(); - //再执行所有mount/update钩子(从下到上) - var i = 0; - while (i < queue.length) { - //queue可能中途加入新元素, 因此不能直接使用queue.forEach(fn) - var instance = queue[i]; - i++; - if (!instance.__lifeStage) { - if (pendingRefs.length) { - clearRefs(); - } - if (instance.componentDidMount) { - instance.componentDidMount(); - instance.componentDidMount = null; - } - instance.__lifeStage = 1; - options.afterMount(instance); - } else { - callUpdate(instance); - } - var ref = instance.__current.ref; - if (ref) { - ref(instance.__mergeStates ? instance : null); - } - instance.__hydrating = false; //子树已经构建完毕 - while (instance.__renderInNextCycle) { - _refreshComponent(instance, queue); - callUpdate(instance); - } - } - //再执行所有setState/forceUpdate回调,根据从下到上的顺序执行 - queue.sort(mountSorter).forEach(function (instance) { - clearArray(instance.__pendingCallbacks).forEach(function (fn) { - fn.call(instance); - }); - }); - queue.length = 0; - options.afterPatch(); -} - -//有一个列队, 先放进A组件与A组件回调 -var dirtyComponents = []; -dirtyComponents.isChildProcess = true; - -function mountSorter(c1, c2) { - //让子节点先于父节点执行 - return c2.__mountOrder - c1.__mountOrder; -} -options.flushBatchedUpdates = function (queue) { - if (!queue) { - queue = dirtyComponents; - } - clearScheduler(queue); -}; - -options.addTask = function (instance) { - if (dirtyComponents.indexOf(instance) == -1) { - dirtyComponents.push(instance); - } -}; - var React = { version: "1.1.1", render: render, diff --git a/dist/ReactIE.js b/dist/ReactIE.js index f48d2b5d1..c740812b3 100644 --- a/dist/ReactIE.js +++ b/dist/ReactIE.js @@ -1871,6 +1871,93 @@ function getOptionSelected(option, selected) { dom.selected = selected; } +var pendingRefs = []; +function clearRefs() { + var refs = pendingRefs.slice(0); + pendingRefs.length = 0; + refs.forEach(function (fn) { + fn(); + }); +} +function callUpdate(instance) { + if (instance.__lifeStage === 2) { + if (instance.componentDidUpdate) { + instance.__didUpdate = true; + instance.componentDidUpdate(instance.lastProps, instance.lastState, instance.lastContext); + if (!instance.__renderInNextCycle) { + instance.__didUpdate = false; + } + } + options.afterUpdate(instance); + instance.__lifeStage = 1; + } +} + +function drainQueue(queue) { + options.beforePatch(); + //先执行所有refs方法(从上到下) + clearRefs(); + //再执行所有mount/update钩子(从下到上) + var i = 0; + while (i < queue.length) { + //queue可能中途加入新元素, 因此不能直接使用queue.forEach(fn) + var instance = queue[i]; + i++; + if (!instance.__lifeStage) { + if (pendingRefs.length) { + clearRefs(); + } + if (instance.componentDidMount) { + instance.componentDidMount(); + instance.componentDidMount = null; + } + instance.__lifeStage = 1; + options.afterMount(instance); + } else { + callUpdate(instance); + } + var ref = instance.__current.ref; + if (ref) { + ref(instance.__mergeStates ? instance : null); + } + instance.__hydrating = false; //子树已经构建完毕 + while (instance.__renderInNextCycle) { + options._refreshComponent(instance, queue); + callUpdate(instance); + } + } + //再执行所有setState/forceUpdate回调,根据从下到上的顺序执行 + queue.sort(mountSorter).forEach(function (instance) { + clearArray(instance.__pendingCallbacks).forEach(function (fn) { + fn.call(instance); + }); + }); + queue.length = 0; + options.afterPatch(); +} + +//有一个列队, 先放进A组件与A组件回调 +var dirtyComponents = []; +dirtyComponents.isChildProcess = true; + +function mountSorter(c1, c2) { + //让子节点先于父节点执行 + return c2.__mountOrder - c1.__mountOrder; +} + +options.flushBatchedUpdates = function (queue) { + if (!queue) { + queue = dirtyComponents; + } + drainQueue(queue); +}; + +options.addTask = function (instance) { + if (dirtyComponents.indexOf(instance) == -1) { + dirtyComponents.push(instance); + } +}; + //[Top API] React.isValidElement function isValidElement(vnode) { return vnode && vnode.vtype; @@ -1941,7 +2028,7 @@ function renderByAnu(vnode, container, callback) { var instance = vnode._instance; container.__component = vnode; - clearScheduler(updateQueue); + drainQueue(updateQueue); CurrentOwner.cur = null; //防止干扰 var ret = instance || rootNode; if (callback) { @@ -2256,11 +2343,12 @@ function _refreshComponent(instance, updateQueue) { instance.__lifeStage = 2; instance.__hydrating = false; if (updateQueue.isChildProcess) { - clearScheduler(updateQueue); + drainQueue(updateQueue); } return dom; } +options._refreshComponent = _refreshComponent; function alignVnode(lastVnode, nextVnode, vparent, context, updateQueue) { var node = lastVnode._hostNode, @@ -2444,8 +2532,7 @@ function isSameNode(a, b) { return true; } } -//================================= -//******* 构建实例链 ******* + function createInstanceChain(instance, vnode, rendered) { instance.__current = vnode; if (rendered._instance) { @@ -2461,93 +2548,6 @@ function updateInstanceChain(instance, dom) { } } -//******* 调度系统 ******* -var pendingRefs = []; -function clearRefs() { - var refs = pendingRefs.slice(0); - pendingRefs.length = 0; - refs.forEach(function (fn) { - fn(); - }); -} -function callUpdate(instance) { - if (instance.__lifeStage === 2) { - if (instance.componentDidUpdate) { - instance.__didUpdate = true; - instance.componentDidUpdate(instance.lastProps, instance.lastState, instance.lastContext); - if (!instance.__renderInNextCycle) { - instance.__didUpdate = false; - } - } - options.afterUpdate(instance); - instance.__lifeStage = 1; - } -} - -function clearScheduler(queue) { - options.beforePatch(); - //先执行所有refs方法(从上到下) - clearRefs(); - //再执行所有mount/update钩子(从下到上) - var i = 0; - while (i < queue.length) { - //queue可能中途加入新元素, 因此不能直接使用queue.forEach(fn) - var instance = queue[i]; - i++; - if (!instance.__lifeStage) { - if (pendingRefs.length) { - clearRefs(); - } - if (instance.componentDidMount) { - instance.componentDidMount(); - instance.componentDidMount = null; - } - instance.__lifeStage = 1; - options.afterMount(instance); - } else { - callUpdate(instance); - } - var ref = instance.__current.ref; - if (ref) { - ref(instance.__mergeStates ? instance : null); - } - instance.__hydrating = false; //子树已经构建完毕 - while (instance.__renderInNextCycle) { - _refreshComponent(instance, queue); - callUpdate(instance); - } - } - //再执行所有setState/forceUpdate回调,根据从下到上的顺序执行 - queue.sort(mountSorter).forEach(function (instance) { - clearArray(instance.__pendingCallbacks).forEach(function (fn) { - fn.call(instance); - }); - }); - queue.length = 0; - options.afterPatch(); -} - -//有一个列队, 先放进A组件与A组件回调 -var dirtyComponents = []; -dirtyComponents.isChildProcess = true; - -function mountSorter(c1, c2) { - //让子节点先于父节点执行 - return c2.__mountOrder - c1.__mountOrder; -} -options.flushBatchedUpdates = function (queue) { - if (!queue) { - queue = dirtyComponents; - } - clearScheduler(queue); -}; - -options.addTask = function (instance) { - if (dirtyComponents.indexOf(instance) == -1) { - dirtyComponents.push(instance); - } -}; - //IE8中select.value不会在onchange事件中随用户的选中而改变其value值,也不让用户直接修改value 只能通过这个hack改变 var noCheck = false; function setSelectValue(e) { diff --git a/dist/ReactSelection.js b/dist/ReactSelection.js index f48d2b5d1..c740812b3 100644 --- a/dist/ReactSelection.js +++ b/dist/ReactSelection.js @@ -1871,6 +1871,93 @@ function getOptionSelected(option, selected) { dom.selected = selected; } +var pendingRefs = []; +function clearRefs() { + var refs = pendingRefs.slice(0); + pendingRefs.length = 0; + refs.forEach(function (fn) { + fn(); + }); +} +function callUpdate(instance) { + if (instance.__lifeStage === 2) { + if (instance.componentDidUpdate) { + instance.__didUpdate = true; + instance.componentDidUpdate(instance.lastProps, instance.lastState, instance.lastContext); + if (!instance.__renderInNextCycle) { + instance.__didUpdate = false; + } + } + options.afterUpdate(instance); + instance.__lifeStage = 1; + } +} + +function drainQueue(queue) { + options.beforePatch(); + //先执行所有refs方法(从上到下) + clearRefs(); + //再执行所有mount/update钩子(从下到上) + var i = 0; + while (i < queue.length) { + //queue可能中途加入新元素, 因此不能直接使用queue.forEach(fn) + var instance = queue[i]; + i++; + if (!instance.__lifeStage) { + if (pendingRefs.length) { + clearRefs(); + } + if (instance.componentDidMount) { + instance.componentDidMount(); + instance.componentDidMount = null; + } + instance.__lifeStage = 1; + options.afterMount(instance); + } else { + callUpdate(instance); + } + var ref = instance.__current.ref; + if (ref) { + ref(instance.__mergeStates ? instance : null); + } + instance.__hydrating = false; //子树已经构建完毕 + while (instance.__renderInNextCycle) { + options._refreshComponent(instance, queue); + callUpdate(instance); + } + } + //再执行所有setState/forceUpdate回调,根据从下到上的顺序执行 + queue.sort(mountSorter).forEach(function (instance) { + clearArray(instance.__pendingCallbacks).forEach(function (fn) { + fn.call(instance); + }); + }); + queue.length = 0; + options.afterPatch(); +} + +//有一个列队, 先放进A组件与A组件回调 +var dirtyComponents = []; +dirtyComponents.isChildProcess = true; + +function mountSorter(c1, c2) { + //让子节点先于父节点执行 + return c2.__mountOrder - c1.__mountOrder; +} + +options.flushBatchedUpdates = function (queue) { + if (!queue) { + queue = dirtyComponents; + } + drainQueue(queue); +}; + +options.addTask = function (instance) { + if (dirtyComponents.indexOf(instance) == -1) { + dirtyComponents.push(instance); + } +}; + //[Top API] React.isValidElement function isValidElement(vnode) { return vnode && vnode.vtype; @@ -1941,7 +2028,7 @@ function renderByAnu(vnode, container, callback) { var instance = vnode._instance; container.__component = vnode; - clearScheduler(updateQueue); + drainQueue(updateQueue); CurrentOwner.cur = null; //防止干扰 var ret = instance || rootNode; if (callback) { @@ -2256,11 +2343,12 @@ function _refreshComponent(instance, updateQueue) { instance.__lifeStage = 2; instance.__hydrating = false; if (updateQueue.isChildProcess) { - clearScheduler(updateQueue); + drainQueue(updateQueue); } return dom; } +options._refreshComponent = _refreshComponent; function alignVnode(lastVnode, nextVnode, vparent, context, updateQueue) { var node = lastVnode._hostNode, @@ -2444,8 +2532,7 @@ function isSameNode(a, b) { return true; } } -//================================= -//******* 构建实例链 ******* + function createInstanceChain(instance, vnode, rendered) { instance.__current = vnode; if (rendered._instance) { @@ -2461,93 +2548,6 @@ function updateInstanceChain(instance, dom) { } } -//******* 调度系统 ******* -var pendingRefs = []; -function clearRefs() { - var refs = pendingRefs.slice(0); - pendingRefs.length = 0; - refs.forEach(function (fn) { - fn(); - }); -} -function callUpdate(instance) { - if (instance.__lifeStage === 2) { - if (instance.componentDidUpdate) { - instance.__didUpdate = true; - instance.componentDidUpdate(instance.lastProps, instance.lastState, instance.lastContext); - if (!instance.__renderInNextCycle) { - instance.__didUpdate = false; - } - } - options.afterUpdate(instance); - instance.__lifeStage = 1; - } -} - -function clearScheduler(queue) { - options.beforePatch(); - //先执行所有refs方法(从上到下) - clearRefs(); - //再执行所有mount/update钩子(从下到上) - var i = 0; - while (i < queue.length) { - //queue可能中途加入新元素, 因此不能直接使用queue.forEach(fn) - var instance = queue[i]; - i++; - if (!instance.__lifeStage) { - if (pendingRefs.length) { - clearRefs(); - } - if (instance.componentDidMount) { - instance.componentDidMount(); - instance.componentDidMount = null; - } - instance.__lifeStage = 1; - options.afterMount(instance); - } else { - callUpdate(instance); - } - var ref = instance.__current.ref; - if (ref) { - ref(instance.__mergeStates ? instance : null); - } - instance.__hydrating = false; //子树已经构建完毕 - while (instance.__renderInNextCycle) { - _refreshComponent(instance, queue); - callUpdate(instance); - } - } - //再执行所有setState/forceUpdate回调,根据从下到上的顺序执行 - queue.sort(mountSorter).forEach(function (instance) { - clearArray(instance.__pendingCallbacks).forEach(function (fn) { - fn.call(instance); - }); - }); - queue.length = 0; - options.afterPatch(); -} - -//有一个列队, 先放进A组件与A组件回调 -var dirtyComponents = []; -dirtyComponents.isChildProcess = true; - -function mountSorter(c1, c2) { - //让子节点先于父节点执行 - return c2.__mountOrder - c1.__mountOrder; -} -options.flushBatchedUpdates = function (queue) { - if (!queue) { - queue = dirtyComponents; - } - clearScheduler(queue); -}; - -options.addTask = function (instance) { - if (dirtyComponents.indexOf(instance) == -1) { - dirtyComponents.push(instance); - } -}; - //IE8中select.value不会在onchange事件中随用户的选中而改变其value值,也不让用户直接修改value 只能通过这个hack改变 var noCheck = false; function setSelectValue(e) { diff --git a/dist/ReactShim.js b/dist/ReactShim.js index 30a9d41ec..8068581f2 100644 --- a/dist/ReactShim.js +++ b/dist/ReactShim.js @@ -1717,6 +1717,93 @@ function getOptionSelected(option, selected) { dom.selected = selected; } +var pendingRefs = []; +function clearRefs() { + var refs = pendingRefs.slice(0); + pendingRefs.length = 0; + refs.forEach(function (fn) { + fn(); + }); +} +function callUpdate(instance) { + if (instance.__lifeStage === 2) { + if (instance.componentDidUpdate) { + instance.__didUpdate = true; + instance.componentDidUpdate(instance.lastProps, instance.lastState, instance.lastContext); + if (!instance.__renderInNextCycle) { + instance.__didUpdate = false; + } + } + options.afterUpdate(instance); + instance.__lifeStage = 1; + } +} + +function drainQueue(queue) { + options.beforePatch(); + //先执行所有refs方法(从上到下) + clearRefs(); + //再执行所有mount/update钩子(从下到上) + var i = 0; + while (i < queue.length) { + //queue可能中途加入新元素, 因此不能直接使用queue.forEach(fn) + var instance = queue[i]; + i++; + if (!instance.__lifeStage) { + if (pendingRefs.length) { + clearRefs(); + } + if (instance.componentDidMount) { + instance.componentDidMount(); + instance.componentDidMount = null; + } + instance.__lifeStage = 1; + options.afterMount(instance); + } else { + callUpdate(instance); + } + var ref = instance.__current.ref; + if (ref) { + ref(instance.__mergeStates ? instance : null); + } + instance.__hydrating = false; //子树已经构建完毕 + while (instance.__renderInNextCycle) { + options._refreshComponent(instance, queue); + callUpdate(instance); + } + } + //再执行所有setState/forceUpdate回调,根据从下到上的顺序执行 + queue.sort(mountSorter).forEach(function (instance) { + clearArray(instance.__pendingCallbacks).forEach(function (fn) { + fn.call(instance); + }); + }); + queue.length = 0; + options.afterPatch(); +} + +//有一个列队, 先放进A组件与A组件回调 +var dirtyComponents = []; +dirtyComponents.isChildProcess = true; + +function mountSorter(c1, c2) { + //让子节点先于父节点执行 + return c2.__mountOrder - c1.__mountOrder; +} + +options.flushBatchedUpdates = function (queue) { + if (!queue) { + queue = dirtyComponents; + } + drainQueue(queue); +}; + +options.addTask = function (instance) { + if (dirtyComponents.indexOf(instance) == -1) { + dirtyComponents.push(instance); + } +}; + //[Top API] React.isValidElement function isValidElement(vnode) { return vnode && vnode.vtype; @@ -1783,7 +1870,7 @@ function renderByAnu(vnode, container, callback) { var instance = vnode._instance; container.__component = vnode; - clearScheduler(updateQueue); + drainQueue(updateQueue); CurrentOwner.cur = null; //防止干扰 var ret = instance || rootNode; if (callback) { @@ -2098,11 +2185,12 @@ function _refreshComponent(instance, updateQueue) { instance.__lifeStage = 2; instance.__hydrating = false; if (updateQueue.isChildProcess) { - clearScheduler(updateQueue); + drainQueue(updateQueue); } return dom; } +options._refreshComponent = _refreshComponent; function alignVnode(lastVnode, nextVnode, vparent, context, updateQueue) { var node = lastVnode._hostNode, @@ -2286,8 +2374,7 @@ function isSameNode(a, b) { return true; } } -//================================= -//******* 构建实例链 ******* + function createInstanceChain(instance, vnode, rendered) { instance.__current = vnode; if (rendered._instance) { @@ -2303,93 +2390,6 @@ function updateInstanceChain(instance, dom) { } } -//******* 调度系统 ******* -var pendingRefs = []; -function clearRefs() { - var refs = pendingRefs.slice(0); - pendingRefs.length = 0; - refs.forEach(function (fn) { - fn(); - }); -} -function callUpdate(instance) { - if (instance.__lifeStage === 2) { - if (instance.componentDidUpdate) { - instance.__didUpdate = true; - instance.componentDidUpdate(instance.lastProps, instance.lastState, instance.lastContext); - if (!instance.__renderInNextCycle) { - instance.__didUpdate = false; - } - } - options.afterUpdate(instance); - instance.__lifeStage = 1; - } -} - -function clearScheduler(queue) { - options.beforePatch(); - //先执行所有refs方法(从上到下) - clearRefs(); - //再执行所有mount/update钩子(从下到上) - var i = 0; - while (i < queue.length) { - //queue可能中途加入新元素, 因此不能直接使用queue.forEach(fn) - var instance = queue[i]; - i++; - if (!instance.__lifeStage) { - if (pendingRefs.length) { - clearRefs(); - } - if (instance.componentDidMount) { - instance.componentDidMount(); - instance.componentDidMount = null; - } - instance.__lifeStage = 1; - options.afterMount(instance); - } else { - callUpdate(instance); - } - var ref = instance.__current.ref; - if (ref) { - ref(instance.__mergeStates ? instance : null); - } - instance.__hydrating = false; //子树已经构建完毕 - while (instance.__renderInNextCycle) { - _refreshComponent(instance, queue); - callUpdate(instance); - } - } - //再执行所有setState/forceUpdate回调,根据从下到上的顺序执行 - queue.sort(mountSorter).forEach(function (instance) { - clearArray(instance.__pendingCallbacks).forEach(function (fn) { - fn.call(instance); - }); - }); - queue.length = 0; - options.afterPatch(); -} - -//有一个列队, 先放进A组件与A组件回调 -var dirtyComponents = []; -dirtyComponents.isChildProcess = true; - -function mountSorter(c1, c2) { - //让子节点先于父节点执行 - return c2.__mountOrder - c1.__mountOrder; -} -options.flushBatchedUpdates = function (queue) { - if (!queue) { - queue = dirtyComponents; - } - clearScheduler(queue); -}; - -options.addTask = function (instance) { - if (dirtyComponents.indexOf(instance) == -1) { - dirtyComponents.push(instance); - } -}; - var React = { version: "1.1.1", render: render, diff --git a/index4.html b/index4.html index 0ee9e8104..e64cc0716 100644 --- a/index4.html +++ b/index4.html @@ -4,11 +4,11 @@ - - - - + + + + @@ -18,125 +18,33 @@
开发者工具
-
-  ['parent-render', 'blue'],
-  ['getInitialState', 'blue'],
-  ['render', 'dark blue', 'blue'],
-  ['handleHue', 'dark blue', 'blue'],
-  ['parent-handleColor', 'blue'],
-  ['parent-render', 'green'],
-  ['setState-this', 'dark blue', 'blue'],
-  ['setState-args', 'dark blue', 'green'],
-  ['render', 'light green', 'green'],
-  ['after-setState', 'light green', 'green'],
-  ['parent-after-setState', 'green'],
-
+ diff --git a/src/diff.js b/src/diff.js index 19d74001c..c944f5450 100644 --- a/src/diff.js +++ b/src/diff.js @@ -3,7 +3,6 @@ import { options, getNodes, innerHTML, - clearArray, toLowerCase, deprecatedWarn, getChildContext, @@ -17,6 +16,10 @@ import { processFormElement, postUpdateSelectedOptions } from "./ControlledComponent"; +import { + pendingRefs, + drainQueue +} from "./scheduler"; //[Top API] React.isValidElement export function isValidElement(vnode) { @@ -97,7 +100,7 @@ function renderByAnu(vnode, container, callback, context = {}) { var instance = vnode._instance; container.__component = vnode; - clearScheduler(updateQueue); + drainQueue(updateQueue); CurrentOwner.cur = null; //防止干扰 var ret = instance || rootNode; if (callback) { @@ -435,11 +438,12 @@ function _refreshComponent(instance, updateQueue) { instance.__lifeStage = 2; instance.__hydrating = false; if(updateQueue.isChildProcess) { - clearScheduler(updateQueue); + drainQueue(updateQueue); } return dom; } +options._refreshComponent = _refreshComponent; export function alignVnode(lastVnode, nextVnode, vparent, context, updateQueue) { let node = lastVnode._hostNode, @@ -628,8 +632,7 @@ function isSameNode(a, b) { return true; } } -//================================= -//******* 构建实例链 ******* + function createInstanceChain(instance, vnode, rendered) { instance.__current = vnode; if (rendered._instance) { @@ -645,91 +648,3 @@ function updateInstanceChain(instance, dom) { } } -//******* 调度系统 ******* -export const pendingRefs = []; -function clearRefs() { - var refs = pendingRefs.slice(0); - pendingRefs.length = 0; - refs.forEach(function(fn) { - fn(); - }); -} -function callUpdate(instance) { - if (instance.__lifeStage === 2) { - if (instance.componentDidUpdate) { - instance.__didUpdate = true; - instance.componentDidUpdate( - instance.lastProps, - instance.lastState, - instance.lastContext - ); - if (!instance.__renderInNextCycle) { - instance.__didUpdate = false; - } - } - options.afterUpdate(instance); - instance.__lifeStage = 1; - } -} - -function clearScheduler(queue) { - options.beforePatch(); - //先执行所有refs方法(从上到下) - clearRefs(); - //再执行所有mount/update钩子(从下到上) - let i = 0; - while(i < queue.length){//queue可能中途加入新元素, 因此不能直接使用queue.forEach(fn) - var instance = queue[i]; - i++; - if (!instance.__lifeStage) { - if(pendingRefs.length){ - clearRefs(); - } - if (instance.componentDidMount) { - instance.componentDidMount(); - instance.componentDidMount = null; - } - instance.__lifeStage = 1; - options.afterMount(instance); - } else { - callUpdate(instance); - } - var ref = instance.__current.ref; - if (ref) { - ref(instance.__mergeStates ? instance : null); - } - instance.__hydrating = false; //子树已经构建完毕 - while (instance.__renderInNextCycle) { - _refreshComponent(instance, queue); - callUpdate(instance); - } - } - //再执行所有setState/forceUpdate回调,根据从下到上的顺序执行 - queue.sort(mountSorter).forEach(function(instance){ - clearArray(instance.__pendingCallbacks).forEach(function(fn) { - fn.call(instance); - }); - }); - queue.length = 0; - options.afterPatch(); -} - -//有一个列队, 先放进A组件与A组件回调 -var dirtyComponents = []; -dirtyComponents.isChildProcess = true; - -function mountSorter(c1, c2) {//让子节点先于父节点执行 - return c2.__mountOrder - c1.__mountOrder; -} -options.flushBatchedUpdates = function(queue) { - if (!queue) { - queue = dirtyComponents; - } - clearScheduler(queue); -}; - -options.addTask = function(instance) { - if (dirtyComponents.indexOf(instance) == -1) { - dirtyComponents.push(instance); - } -}; diff --git a/src/scheduler.js b/src/scheduler.js new file mode 100644 index 000000000..41a7933d7 --- /dev/null +++ b/src/scheduler.js @@ -0,0 +1,93 @@ +import { + options, + clearArray, +} from "./util"; + +export const pendingRefs = []; +function clearRefs() { + var refs = pendingRefs.slice(0); + pendingRefs.length = 0; + refs.forEach(function(fn) { + fn(); + }); +} +function callUpdate(instance) { + if (instance.__lifeStage === 2) { + if (instance.componentDidUpdate) { + instance.__didUpdate = true; + instance.componentDidUpdate( + instance.lastProps, + instance.lastState, + instance.lastContext + ); + if (!instance.__renderInNextCycle) { + instance.__didUpdate = false; + } + } + options.afterUpdate(instance); + instance.__lifeStage = 1; + } +} + +export function drainQueue(queue) { + options.beforePatch(); + //先执行所有refs方法(从上到下) + clearRefs(); + //再执行所有mount/update钩子(从下到上) + let i = 0; + while(i < queue.length){//queue可能中途加入新元素, 因此不能直接使用queue.forEach(fn) + var instance = queue[i]; + i++; + if (!instance.__lifeStage) { + if(pendingRefs.length){ + clearRefs(); + } + if (instance.componentDidMount) { + instance.componentDidMount(); + instance.componentDidMount = null; + } + instance.__lifeStage = 1; + options.afterMount(instance); + } else { + callUpdate(instance); + } + var ref = instance.__current.ref; + if (ref) { + ref(instance.__mergeStates ? instance : null); + } + instance.__hydrating = false; //子树已经构建完毕 + while (instance.__renderInNextCycle) { + options._refreshComponent(instance, queue); + callUpdate(instance); + } + } + //再执行所有setState/forceUpdate回调,根据从下到上的顺序执行 + queue.sort(mountSorter).forEach(function(instance){ + clearArray(instance.__pendingCallbacks).forEach(function(fn) { + fn.call(instance); + }); + }); + queue.length = 0; + options.afterPatch(); +} + +//有一个列队, 先放进A组件与A组件回调 +var dirtyComponents = []; +dirtyComponents.isChildProcess = true; + +function mountSorter(c1, c2) {//让子节点先于父节点执行 + return c2.__mountOrder - c1.__mountOrder; +} + +options.flushBatchedUpdates = function(queue) { + if (!queue) { + queue = dirtyComponents; + } + drainQueue(queue); +}; + +options.addTask = function(instance) { + if (dirtyComponents.indexOf(instance) == -1) { + dirtyComponents.push(instance); + } +}; \ No newline at end of file diff --git a/test/modules/ReactComponentLifeCycle-test.jsx b/test/modules/ReactComponentLifeCycle-test.jsx index d40e883cb..900887272 100644 --- a/test/modules/ReactComponentLifeCycle-test.jsx +++ b/test/modules/ReactComponentLifeCycle-test.jsx @@ -76,4 +76,145 @@ describe("ReactComponentLifeCycle-test", function() { 'Child:onDOMReady', ]); }); + + // You could assign state here, but not access members of it, unless you + // had provided a getInitialState method. + it('throws when accessing state in componentWillMount', () => { + class StatefulComponent extends React.Component { + componentWillMount() { + void this.state.yada; + } + + render() { + return
; + } + } + + var instance = ; + expect(function() { + instance = ReactTestUtils.renderIntoDocument(instance); + }).toThrow(); + }); + + it('should allow update state inside of componentWillMount', () => { + class StatefulComponent extends React.Component { + componentWillMount() { + this.setState({stateField: 'something'}); + } + + render() { + return
; + } + } + + var instance = ; + expect(function() { + instance = ReactTestUtils.renderIntoDocument(instance); + }).not.toThrow(); + }); + + it('should not allow update state inside of getInitialState', () => { + + class StatefulComponent extends React.Component { + constructor(props, context) { + super(props, context); + + this.state = {stateField: 'somethingelse'}; + this.setState({stateField: 'something'}); + + } + + render() { + return
{this.state.stateField}
; + } + } + var container = document.createElement('div'); + ReactDOM.render(, container); + expect(container.textContent).toBe("somethingelse"); + + }); + + it('should correctly determine if a component is mounted', () => { + class Component extends React.Component { + + componentWillMount() { + console.log(this.isMounted()) + expect(this.isMounted()).toBeFalsy(); + } + componentDidMount() { + console.log(this.isMounted()) + expect(this.isMounted()).toBeTruthy(); + } + render() { + console.log(this.isMounted()) + expect(this.isMounted()).toBeFalsy(); + return
; + } + } + + var element = ; + + var instance = ReactTestUtils.renderIntoDocument(element); + expect(instance.isMounted()).toBeTruthy(); + + + }); + + it('should correctly determine if a null component is mounted', () => { + class Component extends React.Component { + + componentWillMount() { + expect(this.isMounted()).toBeFalsy(); + } + componentDidMount() { + expect(this.isMounted()).toBeTruthy(); + } + render() { + expect(this.isMounted()).toBeFalsy(); + return null; + } + } + + var element = ; + + var instance = ReactTestUtils.renderIntoDocument(element); + expect(instance.isMounted()).toBeTruthy(); + }) + it('isMounted should return false when unmounted', () => { + class Component extends React.Component { + render() { + return
; + } + } + + var container = document.createElement('div'); + var instance = ReactDOM.render(, container); + + // No longer a public API, but we can test that it works internally by + // reaching into the updater. + expect(instance.isMounted()).toBe(true); + + ReactDOM.unmountComponentAtNode(container); + + expect(instance.isMounted()).toBe(false); + }); + it('warns if findDOMNode is used inside render', () => { + + class Component extends React.Component { + state = {isMounted: false}; + componentDidMount() { + this.setState({isMounted: true}); + } + render() { + if (this.state.isMounted) { + expect(ReactDOM.findDOMNode(this).tagName).toBe('DIV'); + } + return
; + } + } + + ReactTestUtils.renderIntoDocument(); + + }); + }); \ No newline at end of file diff --git a/test/spec.js b/test/spec.js index d65938b5c..2168aa955 100644 --- a/test/spec.js +++ b/test/spec.js @@ -1,4 +1,4 @@ - +/* import "./modules/createElement.spec"; import "./modules/util.spec"; @@ -33,7 +33,7 @@ require("./modules/ReactChildren-test.jsx"); require("./modules/createReactClassIntegration-test.jsx"); require("./modules/ReactMultiChild-test.jsx"); - +*/ require("./modules/refs-test.jsx"); require("./modules/refs-destruction-test.jsx"); From 6f60193f76c8f5dbdda9b54aec83435849ebbc79 Mon Sep 17 00:00:00 2001 From: RubyLouvre <1669866773@qq.com> Date: Thu, 28 Sep 2017 20:31:59 +0800 Subject: [PATCH 08/57] =?UTF-8?q?=E6=94=AF=E6=8C=81module-pattern=20compon?= =?UTF-8?q?ent?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dist/React.js | 125 ++++--- dist/ReactIE.js | 125 ++++--- dist/ReactSelection.js | 125 ++++--- dist/ReactShim.js | 125 ++++--- index4.html | 21 +- src/diff.js | 194 ++++++----- src/scheduler.js | 2 +- test/modules/ReactComponentLifeCycle-test.jsx | 313 +++++++++++++++++- test/spec.js | 4 +- 9 files changed, 670 insertions(+), 364 deletions(-) diff --git a/dist/React.js b/dist/React.js index 7454c6de2..fffe49436 100644 --- a/dist/React.js +++ b/dist/React.js @@ -1919,7 +1919,7 @@ function drainQueue(queue) { } var ref = instance.__current.ref; if (ref) { - ref(instance.__mergeStates ? instance : null); + ref(instance.__isStateless ? null : instance); } instance.__hydrating = false; //子树已经构建完毕 while (instance.__renderInNextCycle) { @@ -2056,7 +2056,7 @@ var patchStrategy = { 0: mountText, 1: mountElement, 2: mountComponent, - 4: mountStateless, + 4: mountComponent, 10: updateText, 11: updateElement, 12: updateComponent, @@ -2161,34 +2161,68 @@ function alignChildren(parentNode, vparent, context, updateQueue) { parentNode.removeChild(childNodes[n]); } } - +function alwaysNull() { + return null; +} +function instantiateComponent(type, vtype, props, context) { + var instance; + var lastOwn = CurrentOwner.cur; + if (vtype === 2) { + instance = new type(props, context); + //防止用户没有调用super或没有传够参数 + instance.props = instance.props || props; + instance.context = instance.context || context; + } else { + instance = { + refs: {}, + render: function render() { + return type(this.props, this.context); + }, + __isStateless: 1, + state: null, + props: props, + context: context, + constructor: type, + __pendingCallbacks: [], + __current: noop, + __mergeStates: alwaysNull + }; + CurrentOwner.cur = instance; + var mixin = type(props, context); + if (mixin && isFn(mixin.render)) { + delete instance.__isStateless; + Object.assign(instance, mixin); + } else { + instance.__rendered = mixin; + } + } + CurrentOwner.cur = lastOwn; + return instance; +} function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { var type = vnode.type, + vtype = vnode.vtype, props = vnode.props; - var lastOwn = CurrentOwner.cur; - var componentContext = getContextByTypes(parentContext, type.contextTypes); - var instance = new type(props, componentContext); //互相持有引用 - CurrentOwner.cur = lastOwn; + + var instanceContext = getContextByTypes(parentContext, type.contextTypes); + var instance = instantiateComponent(type, vtype, props, instanceContext); //互相持有引用 + vnode._instance = instance; - //防止用户没有调用super或没有传够参数 - instance.props = instance.props || props; - instance.context = instance.context || componentContext; + //用于refreshComponent instance.nextVnode = vnode; - - vnode.context = componentContext; + vnode.context = instanceContext; vnode.parentContext = parentContext; vnode.vparent = vparent; var state = instance.state; - if (instance.componentWillMount) { instance.componentWillMount(); - state = instance.__mergeStates(props, componentContext); + state = instance.__mergeStates(props, instanceContext); } + var rendered = renderComponent(instance, vnode, props, instanceContext, state, instance.__rendered); - var rendered = renderComponent.call(instance, vnode, props, componentContext, state); instance.__hydrating = true; var childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; @@ -2201,45 +2235,22 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { updateQueue.push(instance); return dom; } -function mountStateless(lastNode, vnode, vparent, parentContext, updateQueue) { - var componentContext = getContextByTypes(parentContext, vnode.type.contextTypes); - var instance = new Stateless(vnode.type); - var rendered = renderComponent.call(instance, vnode, vnode.props, componentContext); - - var dom = mountVnode(lastNode, rendered, vparent, parentContext, updateQueue); - - //用于refreshComponent - instance.nextVnode = vnode; - vnode.context = parentContext; - vnode.vparent = vparent; - - createInstanceChain(instance, vnode, rendered); - updateInstanceChain(instance, dom); - updateQueue.unshift(instance); - - return dom; -} - -function renderComponent(vnode, props, context, state) { +function renderComponent(instance, vnode, props, context, state, rendered) { // 同时给有状态与无状态组件使用 - this.props = props; - this.state = state || null; - this.context = context; + instance.props = props; + instance.state = state; + instance.context = context; //调整全局的 CurrentOwner.cur - var lastOwn = CurrentOwner.cur; - CurrentOwner.cur = this; - - var rendered = this.render(); - //比较罕见的用法,返回一个带render的普通对象 - if (rendered && rendered.render) { - rendered = rendered.render(); + if (!rendered) { + var lastOwn = CurrentOwner.cur; + CurrentOwner.cur = instance; + rendered = instance.render(); + CurrentOwner.cur = lastOwn; } - CurrentOwner.cur = lastOwn; //组件只能返回组件或null - if (rendered === null || rendered === false) { rendered = { type: "#comment", text: "empty", vtype: 0 }; } else if (!rendered || !rendered.vtype) { @@ -2247,22 +2258,10 @@ function renderComponent(vnode, props, context, state) { throw new Error("@" + vnode.type.name + "#render:You may have returned undefined, an array or some other invalid object"); } - vnode._instance = this; - this.__rendered = rendered; - return rendered; -} - -function Stateless(render) { - this.refs = {}; - this.render = function () { - return render(this.props, this.context); - }; - this.__pendingCallbacks = []; - this.__current = noop; + vnode._instance = instance; + return instance.__rendered = rendered; } -//Stateless.prototype.render = renderComponent; - function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { var instance = lastVnode._instance; var ref = lastVnode.ref; @@ -2318,7 +2317,7 @@ function _refreshComponent(instance, updateQueue) { nextVnode._instance = instance; //important - var nextState = instance.__mergeStates ? instance.__mergeStates(nextProps, nextContext) : null; + var nextState = instance.__mergeStates(nextProps, nextContext); if (!instance.__forceUpdate && instance.shouldComponentUpdate && instance.shouldComponentUpdate(nextProps, nextState, nextContext) === false) { instance.__forceUpdate = false; @@ -2334,7 +2333,7 @@ function _refreshComponent(instance, updateQueue) { instance.lastState = lastState; instance.lastContext = lastContext; //这里会更新instance的props, context, state - var nextRendered = renderComponent.call(instance, nextVnode, nextProps, nextContext, nextState); + var nextRendered = renderComponent(instance, nextVnode, nextProps, nextContext, nextState); if (lastRendered !== nextRendered && parentContext) { dom = alignVnode(lastRendered, nextRendered, vparent, getChildContext(instance, parentContext), updateQueue); } diff --git a/dist/ReactIE.js b/dist/ReactIE.js index c740812b3..f99ceb3d0 100644 --- a/dist/ReactIE.js +++ b/dist/ReactIE.js @@ -1918,7 +1918,7 @@ function drainQueue(queue) { } var ref = instance.__current.ref; if (ref) { - ref(instance.__mergeStates ? instance : null); + ref(instance.__isStateless ? null : instance); } instance.__hydrating = false; //子树已经构建完毕 while (instance.__renderInNextCycle) { @@ -2055,7 +2055,7 @@ var patchStrategy = { 0: mountText, 1: mountElement, 2: mountComponent, - 4: mountStateless, + 4: mountComponent, 10: updateText, 11: updateElement, 12: updateComponent, @@ -2160,34 +2160,68 @@ function alignChildren(parentNode, vparent, context, updateQueue) { parentNode.removeChild(childNodes[n]); } } - +function alwaysNull() { + return null; +} +function instantiateComponent(type, vtype, props, context) { + var instance; + var lastOwn = CurrentOwner.cur; + if (vtype === 2) { + instance = new type(props, context); + //防止用户没有调用super或没有传够参数 + instance.props = instance.props || props; + instance.context = instance.context || context; + } else { + instance = { + refs: {}, + render: function render() { + return type(this.props, this.context); + }, + __isStateless: 1, + state: null, + props: props, + context: context, + constructor: type, + __pendingCallbacks: [], + __current: noop, + __mergeStates: alwaysNull + }; + CurrentOwner.cur = instance; + var mixin = type(props, context); + if (mixin && isFn(mixin.render)) { + delete instance.__isStateless; + Object.assign(instance, mixin); + } else { + instance.__rendered = mixin; + } + } + CurrentOwner.cur = lastOwn; + return instance; +} function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { var type = vnode.type, + vtype = vnode.vtype, props = vnode.props; - var lastOwn = CurrentOwner.cur; - var componentContext = getContextByTypes(parentContext, type.contextTypes); - var instance = new type(props, componentContext); //互相持有引用 - CurrentOwner.cur = lastOwn; + + var instanceContext = getContextByTypes(parentContext, type.contextTypes); + var instance = instantiateComponent(type, vtype, props, instanceContext); //互相持有引用 + vnode._instance = instance; - //防止用户没有调用super或没有传够参数 - instance.props = instance.props || props; - instance.context = instance.context || componentContext; + //用于refreshComponent instance.nextVnode = vnode; - - vnode.context = componentContext; + vnode.context = instanceContext; vnode.parentContext = parentContext; vnode.vparent = vparent; var state = instance.state; - if (instance.componentWillMount) { instance.componentWillMount(); - state = instance.__mergeStates(props, componentContext); + state = instance.__mergeStates(props, instanceContext); } + var rendered = renderComponent(instance, vnode, props, instanceContext, state, instance.__rendered); - var rendered = renderComponent.call(instance, vnode, props, componentContext, state); instance.__hydrating = true; var childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; @@ -2200,45 +2234,22 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { updateQueue.push(instance); return dom; } -function mountStateless(lastNode, vnode, vparent, parentContext, updateQueue) { - var componentContext = getContextByTypes(parentContext, vnode.type.contextTypes); - var instance = new Stateless(vnode.type); - var rendered = renderComponent.call(instance, vnode, vnode.props, componentContext); - - var dom = mountVnode(lastNode, rendered, vparent, parentContext, updateQueue); - - //用于refreshComponent - instance.nextVnode = vnode; - vnode.context = parentContext; - vnode.vparent = vparent; - - createInstanceChain(instance, vnode, rendered); - updateInstanceChain(instance, dom); - updateQueue.unshift(instance); - - return dom; -} - -function renderComponent(vnode, props, context, state) { +function renderComponent(instance, vnode, props, context, state, rendered) { // 同时给有状态与无状态组件使用 - this.props = props; - this.state = state || null; - this.context = context; + instance.props = props; + instance.state = state; + instance.context = context; //调整全局的 CurrentOwner.cur - var lastOwn = CurrentOwner.cur; - CurrentOwner.cur = this; - - var rendered = this.render(); - //比较罕见的用法,返回一个带render的普通对象 - if (rendered && rendered.render) { - rendered = rendered.render(); + if (!rendered) { + var lastOwn = CurrentOwner.cur; + CurrentOwner.cur = instance; + rendered = instance.render(); + CurrentOwner.cur = lastOwn; } - CurrentOwner.cur = lastOwn; //组件只能返回组件或null - if (rendered === null || rendered === false) { rendered = { type: "#comment", text: "empty", vtype: 0 }; } else if (!rendered || !rendered.vtype) { @@ -2246,22 +2257,10 @@ function renderComponent(vnode, props, context, state) { throw new Error("@" + vnode.type.name + "#render:You may have returned undefined, an array or some other invalid object"); } - vnode._instance = this; - this.__rendered = rendered; - return rendered; -} - -function Stateless(render) { - this.refs = {}; - this.render = function () { - return render(this.props, this.context); - }; - this.__pendingCallbacks = []; - this.__current = noop; + vnode._instance = instance; + return instance.__rendered = rendered; } -//Stateless.prototype.render = renderComponent; - function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { var instance = lastVnode._instance; var ref = lastVnode.ref; @@ -2317,7 +2316,7 @@ function _refreshComponent(instance, updateQueue) { nextVnode._instance = instance; //important - var nextState = instance.__mergeStates ? instance.__mergeStates(nextProps, nextContext) : null; + var nextState = instance.__mergeStates(nextProps, nextContext); if (!instance.__forceUpdate && instance.shouldComponentUpdate && instance.shouldComponentUpdate(nextProps, nextState, nextContext) === false) { instance.__forceUpdate = false; @@ -2333,7 +2332,7 @@ function _refreshComponent(instance, updateQueue) { instance.lastState = lastState; instance.lastContext = lastContext; //这里会更新instance的props, context, state - var nextRendered = renderComponent.call(instance, nextVnode, nextProps, nextContext, nextState); + var nextRendered = renderComponent(instance, nextVnode, nextProps, nextContext, nextState); if (lastRendered !== nextRendered && parentContext) { dom = alignVnode(lastRendered, nextRendered, vparent, getChildContext(instance, parentContext), updateQueue); } diff --git a/dist/ReactSelection.js b/dist/ReactSelection.js index c740812b3..f99ceb3d0 100644 --- a/dist/ReactSelection.js +++ b/dist/ReactSelection.js @@ -1918,7 +1918,7 @@ function drainQueue(queue) { } var ref = instance.__current.ref; if (ref) { - ref(instance.__mergeStates ? instance : null); + ref(instance.__isStateless ? null : instance); } instance.__hydrating = false; //子树已经构建完毕 while (instance.__renderInNextCycle) { @@ -2055,7 +2055,7 @@ var patchStrategy = { 0: mountText, 1: mountElement, 2: mountComponent, - 4: mountStateless, + 4: mountComponent, 10: updateText, 11: updateElement, 12: updateComponent, @@ -2160,34 +2160,68 @@ function alignChildren(parentNode, vparent, context, updateQueue) { parentNode.removeChild(childNodes[n]); } } - +function alwaysNull() { + return null; +} +function instantiateComponent(type, vtype, props, context) { + var instance; + var lastOwn = CurrentOwner.cur; + if (vtype === 2) { + instance = new type(props, context); + //防止用户没有调用super或没有传够参数 + instance.props = instance.props || props; + instance.context = instance.context || context; + } else { + instance = { + refs: {}, + render: function render() { + return type(this.props, this.context); + }, + __isStateless: 1, + state: null, + props: props, + context: context, + constructor: type, + __pendingCallbacks: [], + __current: noop, + __mergeStates: alwaysNull + }; + CurrentOwner.cur = instance; + var mixin = type(props, context); + if (mixin && isFn(mixin.render)) { + delete instance.__isStateless; + Object.assign(instance, mixin); + } else { + instance.__rendered = mixin; + } + } + CurrentOwner.cur = lastOwn; + return instance; +} function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { var type = vnode.type, + vtype = vnode.vtype, props = vnode.props; - var lastOwn = CurrentOwner.cur; - var componentContext = getContextByTypes(parentContext, type.contextTypes); - var instance = new type(props, componentContext); //互相持有引用 - CurrentOwner.cur = lastOwn; + + var instanceContext = getContextByTypes(parentContext, type.contextTypes); + var instance = instantiateComponent(type, vtype, props, instanceContext); //互相持有引用 + vnode._instance = instance; - //防止用户没有调用super或没有传够参数 - instance.props = instance.props || props; - instance.context = instance.context || componentContext; + //用于refreshComponent instance.nextVnode = vnode; - - vnode.context = componentContext; + vnode.context = instanceContext; vnode.parentContext = parentContext; vnode.vparent = vparent; var state = instance.state; - if (instance.componentWillMount) { instance.componentWillMount(); - state = instance.__mergeStates(props, componentContext); + state = instance.__mergeStates(props, instanceContext); } + var rendered = renderComponent(instance, vnode, props, instanceContext, state, instance.__rendered); - var rendered = renderComponent.call(instance, vnode, props, componentContext, state); instance.__hydrating = true; var childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; @@ -2200,45 +2234,22 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { updateQueue.push(instance); return dom; } -function mountStateless(lastNode, vnode, vparent, parentContext, updateQueue) { - var componentContext = getContextByTypes(parentContext, vnode.type.contextTypes); - var instance = new Stateless(vnode.type); - var rendered = renderComponent.call(instance, vnode, vnode.props, componentContext); - - var dom = mountVnode(lastNode, rendered, vparent, parentContext, updateQueue); - - //用于refreshComponent - instance.nextVnode = vnode; - vnode.context = parentContext; - vnode.vparent = vparent; - - createInstanceChain(instance, vnode, rendered); - updateInstanceChain(instance, dom); - updateQueue.unshift(instance); - - return dom; -} - -function renderComponent(vnode, props, context, state) { +function renderComponent(instance, vnode, props, context, state, rendered) { // 同时给有状态与无状态组件使用 - this.props = props; - this.state = state || null; - this.context = context; + instance.props = props; + instance.state = state; + instance.context = context; //调整全局的 CurrentOwner.cur - var lastOwn = CurrentOwner.cur; - CurrentOwner.cur = this; - - var rendered = this.render(); - //比较罕见的用法,返回一个带render的普通对象 - if (rendered && rendered.render) { - rendered = rendered.render(); + if (!rendered) { + var lastOwn = CurrentOwner.cur; + CurrentOwner.cur = instance; + rendered = instance.render(); + CurrentOwner.cur = lastOwn; } - CurrentOwner.cur = lastOwn; //组件只能返回组件或null - if (rendered === null || rendered === false) { rendered = { type: "#comment", text: "empty", vtype: 0 }; } else if (!rendered || !rendered.vtype) { @@ -2246,22 +2257,10 @@ function renderComponent(vnode, props, context, state) { throw new Error("@" + vnode.type.name + "#render:You may have returned undefined, an array or some other invalid object"); } - vnode._instance = this; - this.__rendered = rendered; - return rendered; -} - -function Stateless(render) { - this.refs = {}; - this.render = function () { - return render(this.props, this.context); - }; - this.__pendingCallbacks = []; - this.__current = noop; + vnode._instance = instance; + return instance.__rendered = rendered; } -//Stateless.prototype.render = renderComponent; - function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { var instance = lastVnode._instance; var ref = lastVnode.ref; @@ -2317,7 +2316,7 @@ function _refreshComponent(instance, updateQueue) { nextVnode._instance = instance; //important - var nextState = instance.__mergeStates ? instance.__mergeStates(nextProps, nextContext) : null; + var nextState = instance.__mergeStates(nextProps, nextContext); if (!instance.__forceUpdate && instance.shouldComponentUpdate && instance.shouldComponentUpdate(nextProps, nextState, nextContext) === false) { instance.__forceUpdate = false; @@ -2333,7 +2332,7 @@ function _refreshComponent(instance, updateQueue) { instance.lastState = lastState; instance.lastContext = lastContext; //这里会更新instance的props, context, state - var nextRendered = renderComponent.call(instance, nextVnode, nextProps, nextContext, nextState); + var nextRendered = renderComponent(instance, nextVnode, nextProps, nextContext, nextState); if (lastRendered !== nextRendered && parentContext) { dom = alignVnode(lastRendered, nextRendered, vparent, getChildContext(instance, parentContext), updateQueue); } diff --git a/dist/ReactShim.js b/dist/ReactShim.js index 8068581f2..16e9d0421 100644 --- a/dist/ReactShim.js +++ b/dist/ReactShim.js @@ -1764,7 +1764,7 @@ function drainQueue(queue) { } var ref = instance.__current.ref; if (ref) { - ref(instance.__mergeStates ? instance : null); + ref(instance.__isStateless ? null : instance); } instance.__hydrating = false; //子树已经构建完毕 while (instance.__renderInNextCycle) { @@ -1897,7 +1897,7 @@ var patchStrategy = { 0: mountText, 1: mountElement, 2: mountComponent, - 4: mountStateless, + 4: mountComponent, 10: updateText, 11: updateElement, 12: updateComponent, @@ -2002,34 +2002,68 @@ function alignChildren(parentNode, vparent, context, updateQueue) { parentNode.removeChild(childNodes[n]); } } - +function alwaysNull() { + return null; +} +function instantiateComponent(type, vtype, props, context) { + var instance; + var lastOwn = CurrentOwner.cur; + if (vtype === 2) { + instance = new type(props, context); + //防止用户没有调用super或没有传够参数 + instance.props = instance.props || props; + instance.context = instance.context || context; + } else { + instance = { + refs: {}, + render: function render() { + return type(this.props, this.context); + }, + __isStateless: 1, + state: null, + props: props, + context: context, + constructor: type, + __pendingCallbacks: [], + __current: noop, + __mergeStates: alwaysNull + }; + CurrentOwner.cur = instance; + var mixin = type(props, context); + if (mixin && isFn(mixin.render)) { + delete instance.__isStateless; + Object.assign(instance, mixin); + } else { + instance.__rendered = mixin; + } + } + CurrentOwner.cur = lastOwn; + return instance; +} function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { var type = vnode.type, + vtype = vnode.vtype, props = vnode.props; - var lastOwn = CurrentOwner.cur; - var componentContext = getContextByTypes(parentContext, type.contextTypes); - var instance = new type(props, componentContext); //互相持有引用 - CurrentOwner.cur = lastOwn; + + var instanceContext = getContextByTypes(parentContext, type.contextTypes); + var instance = instantiateComponent(type, vtype, props, instanceContext); //互相持有引用 + vnode._instance = instance; - //防止用户没有调用super或没有传够参数 - instance.props = instance.props || props; - instance.context = instance.context || componentContext; + //用于refreshComponent instance.nextVnode = vnode; - - vnode.context = componentContext; + vnode.context = instanceContext; vnode.parentContext = parentContext; vnode.vparent = vparent; var state = instance.state; - if (instance.componentWillMount) { instance.componentWillMount(); - state = instance.__mergeStates(props, componentContext); + state = instance.__mergeStates(props, instanceContext); } + var rendered = renderComponent(instance, vnode, props, instanceContext, state, instance.__rendered); - var rendered = renderComponent.call(instance, vnode, props, componentContext, state); instance.__hydrating = true; var childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; @@ -2042,45 +2076,22 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { updateQueue.push(instance); return dom; } -function mountStateless(lastNode, vnode, vparent, parentContext, updateQueue) { - - var componentContext = getContextByTypes(parentContext, vnode.type.contextTypes); - var instance = new Stateless(vnode.type); - var rendered = renderComponent.call(instance, vnode, vnode.props, componentContext); - - var dom = mountVnode(lastNode, rendered, vparent, parentContext, updateQueue); - - //用于refreshComponent - instance.nextVnode = vnode; - vnode.context = parentContext; - vnode.vparent = vparent; - - createInstanceChain(instance, vnode, rendered); - updateInstanceChain(instance, dom); - updateQueue.unshift(instance); - - return dom; -} -function renderComponent(vnode, props, context, state) { +function renderComponent(instance, vnode, props, context, state, rendered) { // 同时给有状态与无状态组件使用 - this.props = props; - this.state = state || null; - this.context = context; + instance.props = props; + instance.state = state; + instance.context = context; //调整全局的 CurrentOwner.cur - var lastOwn = CurrentOwner.cur; - CurrentOwner.cur = this; - - var rendered = this.render(); - //比较罕见的用法,返回一个带render的普通对象 - if (rendered && rendered.render) { - rendered = rendered.render(); + if (!rendered) { + var lastOwn = CurrentOwner.cur; + CurrentOwner.cur = instance; + rendered = instance.render(); + CurrentOwner.cur = lastOwn; } - CurrentOwner.cur = lastOwn; //组件只能返回组件或null - if (rendered === null || rendered === false) { rendered = { type: "#comment", text: "empty", vtype: 0 }; } else if (!rendered || !rendered.vtype) { @@ -2088,22 +2099,10 @@ function renderComponent(vnode, props, context, state) { throw new Error("@" + vnode.type.name + "#render:You may have returned undefined, an array or some other invalid object"); } - vnode._instance = this; - this.__rendered = rendered; - return rendered; -} - -function Stateless(render) { - this.refs = {}; - this.render = function () { - return render(this.props, this.context); - }; - this.__pendingCallbacks = []; - this.__current = noop; + vnode._instance = instance; + return instance.__rendered = rendered; } -//Stateless.prototype.render = renderComponent; - function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { var instance = lastVnode._instance; var ref = lastVnode.ref; @@ -2159,7 +2158,7 @@ function _refreshComponent(instance, updateQueue) { nextVnode._instance = instance; //important - var nextState = instance.__mergeStates ? instance.__mergeStates(nextProps, nextContext) : null; + var nextState = instance.__mergeStates(nextProps, nextContext); if (!instance.__forceUpdate && instance.shouldComponentUpdate && instance.shouldComponentUpdate(nextProps, nextState, nextContext) === false) { instance.__forceUpdate = false; @@ -2175,7 +2174,7 @@ function _refreshComponent(instance, updateQueue) { instance.lastState = lastState; instance.lastContext = lastContext; //这里会更新instance的props, context, state - var nextRendered = renderComponent.call(instance, nextVnode, nextProps, nextContext, nextState); + var nextRendered = renderComponent(instance, nextVnode, nextProps, nextContext, nextState); if (lastRendered !== nextRendered && parentContext) { dom = alignVnode(lastRendered, nextRendered, vparent, getChildContext(instance, parentContext), updateQueue); } diff --git a/index4.html b/index4.html index e64cc0716..a070d52fc 100644 --- a/index4.html +++ b/index4.html @@ -5,7 +5,7 @@ - @@ -27,24 +27,15 @@ var container = document.createElement('div'); document.body.appendChild(container); - class Component extends React.Component { - state = {isMounted: false}; - componentDidMount() { - this.setState({isMounted: true}); - } - render() { - if (this.state.isMounted) { - console.log(ReactDOM.findDOMNode(this).tagName) - } - return
; - } + const log = []; + function StatelessComponent(props) { + return
{props.name}
; } + var el = document.createElement('div'); + ReactDOM.render(, container); - void ReactDOM.render(, container); - - diff --git a/src/diff.js b/src/diff.js index c944f5450..c02acf113 100644 --- a/src/diff.js +++ b/src/diff.js @@ -1,4 +1,5 @@ import { + isFn, noop, options, getNodes, @@ -16,10 +17,7 @@ import { processFormElement, postUpdateSelectedOptions } from "./ControlledComponent"; -import { - pendingRefs, - drainQueue -} from "./scheduler"; +import { pendingRefs, drainQueue } from "./scheduler"; //[Top API] React.isValidElement export function isValidElement(vnode) { @@ -129,7 +127,7 @@ const patchStrategy = { 0: mountText, 1: mountElement, 2: mountComponent, - 4: mountStateless, + 4: mountComponent, 10: updateText, 11: updateElement, 12: updateComponent, @@ -233,110 +231,116 @@ function alignChildren(parentNode, vparent, context, updateQueue) { parentNode.removeChild(childNodes[n]); } } - -function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { - let { type, props } = vnode; +function alwaysNull() { + return null; +} +function instantiateComponent(type, vtype, props, context) { + var instance; let lastOwn = CurrentOwner.cur; - let componentContext = getContextByTypes(parentContext, type.contextTypes); - let instance = new type(props, componentContext); //互相持有引用 + if (vtype === 2) { + instance = new type(props, context); + //防止用户没有调用super或没有传够参数 + instance.props = instance.props || props; + instance.context = instance.context || context; + } else { + instance = { + refs: {}, + render: function() { + return type(this.props, this.context); + }, + __isStateless: 1, + state: null, + props: props, + context: context, + constructor: type, + __pendingCallbacks: [], + __current: noop, + __mergeStates: alwaysNull + }; + CurrentOwner.cur = instance; + var mixin = type(props, context); + if (mixin && isFn(mixin.render)) { + delete instance.__isStateless; + Object.assign(instance, mixin); + } else { + instance.__rendered = mixin; + } + } CurrentOwner.cur = lastOwn; + return instance; +} +function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { + let { type, vtype, props } = vnode; + + let instanceContext = getContextByTypes(parentContext, type.contextTypes); + let instance = instantiateComponent(type, vtype, props, instanceContext); //互相持有引用 + vnode._instance = instance; - //防止用户没有调用super或没有传够参数 - instance.props = instance.props || props; - instance.context = instance.context || componentContext; + //用于refreshComponent instance.nextVnode = vnode; - - vnode.context = componentContext; + vnode.context = instanceContext; vnode.parentContext = parentContext; vnode.vparent = vparent; let state = instance.state; - if (instance.componentWillMount) { instance.componentWillMount(); - state = instance.__mergeStates(props, componentContext); + state = instance.__mergeStates(props, instanceContext); } + let rendered = renderComponent( + instance, + vnode, + props, + instanceContext, + state, + instance.__rendered + ); - let rendered = renderComponent.call(instance, vnode, props, componentContext, state); instance.__hydrating = true; var childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; - - - let dom = mountVnode(lastNode, rendered, vparent, childContext, updateQueue); - - createInstanceChain(instance, vnode, rendered); - updateInstanceChain(instance, dom); - - updateQueue.push(instance); - return dom; -} -function mountStateless(lastNode, vnode, vparent, parentContext, updateQueue) { - - let componentContext = getContextByTypes(parentContext, vnode.type.contextTypes); - let instance = new Stateless(vnode.type); - let rendered = renderComponent.call(instance, vnode, vnode.props, componentContext); - - let dom = mountVnode(lastNode, rendered, vparent, parentContext, updateQueue); - //用于refreshComponent - instance.nextVnode = vnode; - vnode.context = parentContext; - vnode.vparent = vparent; + let dom = mountVnode(lastNode, rendered, vparent, childContext, updateQueue); createInstanceChain(instance, vnode, rendered); updateInstanceChain(instance, dom); - updateQueue.unshift(instance); + updateQueue.push(instance); return dom; } -function renderComponent(vnode, props, context, state) { +function renderComponent(instance, vnode, props, context, state, rendered) { // 同时给有状态与无状态组件使用 - this.props = props; - this.state = state || null; - this.context = context; + instance.props = props; + instance.state = state; + instance.context = context; //调整全局的 CurrentOwner.cur - var lastOwn = CurrentOwner.cur; - CurrentOwner.cur = this; - - let rendered = this.render(); - //比较罕见的用法,返回一个带render的普通对象 - if(rendered && rendered.render){ - rendered = rendered.render(); + if (!rendered) { + var lastOwn = CurrentOwner.cur; + CurrentOwner.cur = instance; + rendered = instance.render(); + CurrentOwner.cur = lastOwn; } - CurrentOwner.cur = lastOwn; //组件只能返回组件或null - if (rendered === null || rendered === false) { rendered = { type: "#comment", text: "empty", vtype: 0 }; - } else if (!rendered || !rendered.vtype) {//true, undefined, array, {} + } else if (!rendered || !rendered.vtype) { + //true, undefined, array, {} throw new Error( - `@${vnode.type.name}#render:You may have returned undefined, an array or some other invalid object` + `@${vnode.type + .name}#render:You may have returned undefined, an array or some other invalid object` ); } - vnode._instance = this; - this.__rendered = rendered; - return rendered; -} - -function Stateless(render) { - this.refs = {}; - this.render = function() { - return render(this.props, this.context); - }; - this.__pendingCallbacks = []; - this.__current = noop; + vnode._instance = instance; + return (instance.__rendered = rendered); } -//Stateless.prototype.render = renderComponent; - function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { var instance = lastVnode._instance; var ref = lastVnode.ref; @@ -361,17 +365,17 @@ function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { nextVnode.context = nextContext; nextVnode.parentContext = context; nextVnode.vparent = vparent; - var queue; - if( updateQueue.isChildProcess ){ + var queue; + if (updateQueue.isChildProcess) { queue = updateQueue; - }else { + } else { queue = []; queue.isChildProcess = true; } _refreshComponent(instance, queue); //子组件先执行 updateQueue.unshift(instance); - + return instance.__dom; } @@ -386,7 +390,6 @@ function _refreshComponent(instance, updateQueue) { instance.__renderInNextCycle = null; let nextVnode = instance.nextVnode; let nextContext = nextVnode.context; - let parentContext = nextVnode.parentContext; let nextProps = nextVnode.props; @@ -394,9 +397,7 @@ function _refreshComponent(instance, updateQueue) { nextVnode._instance = instance; //important - let nextState = instance.__mergeStates - ? instance.__mergeStates(nextProps, nextContext) - : null; + let nextState = instance.__mergeStates(nextProps, nextContext); if ( !instance.__forceUpdate && @@ -416,14 +417,14 @@ function _refreshComponent(instance, updateQueue) { instance.lastState = lastState; instance.lastContext = lastContext; //这里会更新instance的props, context, state - let nextRendered = renderComponent.call( + let nextRendered = renderComponent( instance, nextVnode, nextProps, nextContext, nextState - ); - if(lastRendered!== nextRendered && parentContext){ + ); + if (lastRendered !== nextRendered && parentContext) { dom = alignVnode( lastRendered, nextRendered, @@ -434,10 +435,10 @@ function _refreshComponent(instance, updateQueue) { } createInstanceChain(instance, nextVnode, nextRendered); updateInstanceChain(instance, dom); - + instance.__lifeStage = 2; instance.__hydrating = false; - if(updateQueue.isChildProcess) { + if (updateQueue.isChildProcess) { drainQueue(updateQueue); } @@ -445,7 +446,13 @@ function _refreshComponent(instance, updateQueue) { } options._refreshComponent = _refreshComponent; -export function alignVnode(lastVnode, nextVnode, vparent, context, updateQueue) { +export function alignVnode( + lastVnode, + nextVnode, + vparent, + context, + updateQueue +) { let node = lastVnode._hostNode, dom; if (isSameNode(lastVnode, nextVnode)) { @@ -461,9 +468,9 @@ export function alignVnode(lastVnode, nextVnode, vparent, context, updateQueue) p.replaceChild(dom, node); removeDOMElement(node); } - // if (innerUpdateQueue !== updateQueue) { - // clearScheduler(innerUpdateQueue); - // } + // if (innerUpdateQueue !== updateQueue) { + // clearScheduler(innerUpdateQueue); + // } } return dom; } @@ -508,7 +515,7 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { nextChildren = flattenChildren(nextVnode), nextLength = nextChildren.length, lastLength = lastChildren.length; - + //如果旧数组长度为零 if (nextLength && !lastLength) { return nextChildren.forEach(function(vnode) { @@ -516,8 +523,14 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { parentNode.appendChild(curNode); }); } - if(nextLength === lastLength && lastLength ===1){ - return alignVnode(lastChildren[0], nextChildren[0], lastVnode, context, updateQueue); + if (nextLength === lastLength && lastLength === 1) { + return alignVnode( + lastChildren[0], + nextChildren[0], + lastVnode, + context, + updateQueue + ); } let maxLength = Math.max(nextLength, lastLength), insertPoint = parentNode.firstChild, @@ -537,7 +550,7 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { while (i < maxLength) { nextChild = nextChildren[i]; lastChild = lastChildren[i]; - + if (nextChild && lastChild && isSameNode(lastChild, nextChild)) { // 如果能直接找到,命名90%的情况 actions[i] = { @@ -647,4 +660,3 @@ function updateInstanceChain(instance, dom) { updateInstanceChain(parent, dom); } } - diff --git a/src/scheduler.js b/src/scheduler.js index 41a7933d7..7e3c7de7f 100644 --- a/src/scheduler.js +++ b/src/scheduler.js @@ -53,7 +53,7 @@ export function drainQueue(queue) { } var ref = instance.__current.ref; if (ref) { - ref(instance.__mergeStates ? instance : null); + ref(instance.__isStateless ? null: instance); } instance.__hydrating = false; //子树已经构建完毕 while (instance.__renderInNextCycle) { diff --git a/test/modules/ReactComponentLifeCycle-test.jsx b/test/modules/ReactComponentLifeCycle-test.jsx index 900887272..bbe87b0a3 100644 --- a/test/modules/ReactComponentLifeCycle-test.jsx +++ b/test/modules/ReactComponentLifeCycle-test.jsx @@ -138,15 +138,12 @@ describe("ReactComponentLifeCycle-test", function() { class Component extends React.Component { componentWillMount() { - console.log(this.isMounted()) expect(this.isMounted()).toBeFalsy(); } componentDidMount() { - console.log(this.isMounted()) expect(this.isMounted()).toBeTruthy(); } render() { - console.log(this.isMounted()) expect(this.isMounted()).toBeFalsy(); return
; } @@ -217,4 +214,314 @@ describe("ReactComponentLifeCycle-test", function() { }); + + it('should carry through each of the phases of setup', () => { + var clone = function(o) { + return JSON.parse(JSON.stringify(o)); +}; + +var GET_INIT_STATE_RETURN_VAL = { + hasWillMountCompleted: false, + hasRenderCompleted: false, + hasDidMountCompleted: false, + hasWillUnmountCompleted: false, +}; + +var INIT_RENDER_STATE = { + hasWillMountCompleted: true, + hasRenderCompleted: false, + hasDidMountCompleted: false, + hasWillUnmountCompleted: false, +}; + +var DID_MOUNT_STATE = { + hasWillMountCompleted: true, + hasRenderCompleted: true, + hasDidMountCompleted: false, + hasWillUnmountCompleted: false, +}; + +var NEXT_RENDER_STATE = { + hasWillMountCompleted: true, + hasRenderCompleted: true, + hasDidMountCompleted: true, + hasWillUnmountCompleted: false, +}; + +var WILL_UNMOUNT_STATE = { + hasWillMountCompleted: true, + hasDidMountCompleted: true, + hasRenderCompleted: true, + hasWillUnmountCompleted: false, +}; + +var POST_WILL_UNMOUNT_STATE = { + hasWillMountCompleted: true, + hasDidMountCompleted: true, + hasRenderCompleted: true, + hasWillUnmountCompleted: true, +}; +function getLifeCycleState(instance) { + return instance.isMounted() ? 'MOUNTED' : 'UNMOUNTED'; +} + spyOn(console, 'error'); + + class LifeCycleComponent extends React.Component { + constructor(props, context) { + super(props, context); + this._testJournal = {}; + var initState = { + hasWillMountCompleted: false, + hasDidMountCompleted: false, + hasRenderCompleted: false, + hasWillUnmountCompleted: false, + }; + this._testJournal.returnedFromGetInitialState = clone(initState); + this._testJournal.lifeCycleAtStartOfGetInitialState = getLifeCycleState( + this, + ); + this.state = initState; + } + + componentWillMount() { + this._testJournal.stateAtStartOfWillMount = clone(this.state); + this._testJournal.lifeCycleAtStartOfWillMount = getLifeCycleState(this); + this.state.hasWillMountCompleted = true; + } + + componentDidMount() { + this._testJournal.stateAtStartOfDidMount = clone(this.state); + this._testJournal.lifeCycleAtStartOfDidMount = getLifeCycleState(this); + this.setState({hasDidMountCompleted: true}); + } + + render() { + var isInitialRender = !this.state.hasRenderCompleted; + if (isInitialRender) { + this._testJournal.stateInInitialRender = clone(this.state); + this._testJournal.lifeCycleInInitialRender = getLifeCycleState(this); + } else { + this._testJournal.stateInLaterRender = clone(this.state); + this._testJournal.lifeCycleInLaterRender = getLifeCycleState(this); + } + // you would *NEVER* do anything like this in real code! + this.state.hasRenderCompleted = true; + return ( +
+ I am the inner DIV +
+ ); + } + + componentWillUnmount() { + this._testJournal.stateAtStartOfWillUnmount = clone(this.state); + this._testJournal.lifeCycleAtStartOfWillUnmount = getLifeCycleState( + this, + ); + this.state.hasWillUnmountCompleted = true; + } + } + + // A component that is merely "constructed" (as in "constructor") but not + // yet initialized, or rendered. + // + var container = document.createElement('div'); + var instance = ReactDOM.render(, container); + + // getInitialState + expect(instance._testJournal.returnedFromGetInitialState).toEqual( + GET_INIT_STATE_RETURN_VAL, + ); + expect(instance._testJournal.lifeCycleAtStartOfGetInitialState).toBe( + 'UNMOUNTED', + ); + + // componentWillMount + expect(instance._testJournal.stateAtStartOfWillMount).toEqual( + instance._testJournal.returnedFromGetInitialState, + ); + expect(instance._testJournal.lifeCycleAtStartOfWillMount).toBe('UNMOUNTED'); + + // componentDidMount + expect(instance._testJournal.stateAtStartOfDidMount).toEqual( + DID_MOUNT_STATE, + ); + expect(instance._testJournal.lifeCycleAtStartOfDidMount).toBe('MOUNTED'); + + // initial render + expect(instance._testJournal.stateInInitialRender).toEqual( + INIT_RENDER_STATE, + ); + expect(instance._testJournal.lifeCycleInInitialRender).toBe('UNMOUNTED'); + + expect(getLifeCycleState(instance)).toBe('MOUNTED'); + + // Now *update the component* + instance.forceUpdate(); + + // render 2nd time + expect(instance._testJournal.stateInLaterRender).toEqual(NEXT_RENDER_STATE); + expect(instance._testJournal.lifeCycleInLaterRender).toBe('MOUNTED'); + + expect(getLifeCycleState(instance)).toBe('MOUNTED'); + + ReactDOM.unmountComponentAtNode(container); + + expect(instance._testJournal.stateAtStartOfWillUnmount).toEqual( + WILL_UNMOUNT_STATE, + ); + // componentWillUnmount called right before unmount. + expect(instance._testJournal.lifeCycleAtStartOfWillUnmount).toBe('MOUNTED'); + + // But the current lifecycle of the component is unmounted. + expect(getLifeCycleState(instance)).toBe('UNMOUNTED'); + expect(instance.state).toEqual(POST_WILL_UNMOUNT_STATE); + + + }); + + it('should allow state updates in componentDidMount', () => { + /** + * calls setState in an componentDidMount. + */ + class SetStateInComponentDidMount extends React.Component { + state = { + stateField: this.props.valueToUseInitially, + }; + + componentDidMount() { + this.setState({stateField: this.props.valueToUseInOnDOMReady}); + } + + render() { + return
; + } + } + + var instance = ( + + ); + instance = ReactTestUtils.renderIntoDocument(instance); + expect(instance.state.stateField).toBe('goodbye'); + }); + + it('should call nested lifecycle methods in the right order', () => { + var log; + var logger = function(msg) { + return function() { + // return true for shouldComponentUpdate + log.push(msg); + return true; + }; + }; + class Outer extends React.Component { + componentWillMount = logger('outer componentWillMount'); + componentDidMount = logger('outer componentDidMount'); + componentWillReceiveProps = logger('outer componentWillReceiveProps'); + shouldComponentUpdate = logger('outer shouldComponentUpdate'); + componentWillUpdate = logger('outer componentWillUpdate'); + componentDidUpdate = logger('outer componentDidUpdate'); + componentWillUnmount = logger('outer componentWillUnmount'); + render() { + return
; + } + } + + class Inner extends React.Component { + componentWillMount = logger('inner componentWillMount'); + componentDidMount = logger('inner componentDidMount'); + componentWillReceiveProps = logger('inner componentWillReceiveProps'); + shouldComponentUpdate = logger('inner shouldComponentUpdate'); + componentWillUpdate = logger('inner componentWillUpdate'); + componentDidUpdate = logger('inner componentDidUpdate'); + componentWillUnmount = logger('inner componentWillUnmount'); + render() { + return {this.props.x}; + } + } + + var container = document.createElement('div'); + log = []; + ReactDOM.render(, container); + expect(log).toEqual([ + 'outer componentWillMount', + 'inner componentWillMount', + 'inner componentDidMount', + 'outer componentDidMount', + ]); + + log = []; + ReactDOM.render(, container); + expect(log).toEqual([ + 'outer componentWillReceiveProps', + 'outer shouldComponentUpdate', + 'outer componentWillUpdate', + 'inner componentWillReceiveProps', + 'inner shouldComponentUpdate', + 'inner componentWillUpdate', + 'inner componentDidUpdate', + 'outer componentDidUpdate', + ]); + + log = []; + ReactDOM.unmountComponentAtNode(container); + expect(log).toEqual([ + 'outer componentWillUnmount', + 'inner componentWillUnmount', + ]); + }); + + it('calls effects on module-pattern component', function() { + const log = []; + var PropTypes = React.PropTypes + function Parent() { + return { + render() { + expect(typeof this.props).toBe('object'); + log.push('render'); + return ; + }, + componentWillMount() { + log.push('will mount'); + }, + componentDidMount() { + log.push('did mount'); + }, + componentDidUpdate() { + log.push('did update'); + }, + getChildContext() { + return {x: 2}; + }, + }; + } + Parent.childContextTypes = { + x: PropTypes.number, + }; + function Child(props, context) { + expect(context.x).toBe(2); + return
; + } + Child.contextTypes = { + x: PropTypes.number, + }; + + const div = document.createElement('div'); + ReactDOM.render( c && log.push('ref')} />, div); + ReactDOM.render( c && log.push('ref')} />, div); +console.log(log.join("\n")) + expect(log).toEqual([ + 'will mount', + 'render', + 'did mount', + 'ref', + + 'render', + 'did update', + 'ref', + ]); + }); }); \ No newline at end of file diff --git a/test/spec.js b/test/spec.js index 2168aa955..d65938b5c 100644 --- a/test/spec.js +++ b/test/spec.js @@ -1,4 +1,4 @@ -/* + import "./modules/createElement.spec"; import "./modules/util.spec"; @@ -33,7 +33,7 @@ require("./modules/ReactChildren-test.jsx"); require("./modules/createReactClassIntegration-test.jsx"); require("./modules/ReactMultiChild-test.jsx"); -*/ + require("./modules/refs-test.jsx"); require("./modules/refs-destruction-test.jsx"); From 373a85576c4c687105b82d2df6dd8e79ede9e6a3 Mon Sep 17 00:00:00 2001 From: RubyLouvre <1669866773@qq.com> Date: Fri, 29 Sep 2017 00:18:43 +0800 Subject: [PATCH 09/57] =?UTF-8?q?=E8=B0=83=E6=95=B4=E7=BB=84=E4=BB=B6?= =?UTF-8?q?=E6=94=BE=E5=85=A5=E6=9B=B4=E6=96=B0=E5=88=97=E9=98=9F=E7=9A=84?= =?UTF-8?q?=E9=A1=BA=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dist/React.js | 17 +- dist/ReactIE.js | 17 +- dist/ReactSelection.js | 17 +- dist/ReactShim.js | 17 +- src/Component.js | 5 +- src/diff.js | 11 +- src/scheduler.js | 3 + test/modules/ReactComponentLifeCycle-test.jsx | 1 - test/modules/ReactCompositeComponent-test.jsx | 312 ++++++++++++++++++ test/spec.js | 2 +- 10 files changed, 369 insertions(+), 33 deletions(-) create mode 100644 test/modules/ReactCompositeComponent-test.jsx diff --git a/dist/React.js b/dist/React.js index fffe49436..10774b35f 100644 --- a/dist/React.js +++ b/dist/React.js @@ -1095,7 +1095,9 @@ function setStateImpl(state, cb) { //组件挂载期 //componentWillUpdate中的setState/forceUpdate应该被忽略 if (this.__hydrating) { - //在挂载过程中,子组件在componentWillReceiveProps里调用父组件的setState,延迟到下一周期更新 + //在render方法中调用setState也会被延迟到下一周期更新.这存在两种情况, + //1. 组件直接调用自己的setState + //2. 子组件调用父组件的setState, this.__renderInNextCycle = true; } } else { @@ -1882,6 +1884,9 @@ function clearRefs() { } function callUpdate(instance) { if (instance.__lifeStage === 2) { + if (pendingRefs.length) { + clearRefs(); + } if (instance.componentDidUpdate) { instance.__didUpdate = true; instance.componentDidUpdate(instance.lastProps, instance.lastState, instance.lastContext); @@ -2190,6 +2195,7 @@ function instantiateComponent(type, vtype, props, context) { CurrentOwner.cur = instance; var mixin = type(props, context); if (mixin && isFn(mixin.render)) { + //支持module pattern component delete instance.__isStateless; Object.assign(instance, mixin); } else { @@ -2221,18 +2227,17 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { instance.componentWillMount(); state = instance.__mergeStates(props, instanceContext); } - var rendered = renderComponent(instance, vnode, props, instanceContext, state, instance.__rendered); - instance.__hydrating = true; + var rendered = renderComponent(instance, vnode, props, instanceContext, state, instance.__rendered); + var childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; var dom = mountVnode(lastNode, rendered, vparent, childContext, updateQueue); - + updateQueue.push(instance); createInstanceChain(instance, vnode, rendered); updateInstanceChain(instance, dom); - updateQueue.push(instance); return dom; } @@ -2295,7 +2300,7 @@ function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { } _refreshComponent(instance, queue); //子组件先执行 - updateQueue.unshift(instance); + updateQueue.push(instance); return instance.__dom; } diff --git a/dist/ReactIE.js b/dist/ReactIE.js index f99ceb3d0..a2137213c 100644 --- a/dist/ReactIE.js +++ b/dist/ReactIE.js @@ -1094,7 +1094,9 @@ function setStateImpl(state, cb) { //组件挂载期 //componentWillUpdate中的setState/forceUpdate应该被忽略 if (this.__hydrating) { - //在挂载过程中,子组件在componentWillReceiveProps里调用父组件的setState,延迟到下一周期更新 + //在render方法中调用setState也会被延迟到下一周期更新.这存在两种情况, + //1. 组件直接调用自己的setState + //2. 子组件调用父组件的setState, this.__renderInNextCycle = true; } } else { @@ -1881,6 +1883,9 @@ function clearRefs() { } function callUpdate(instance) { if (instance.__lifeStage === 2) { + if (pendingRefs.length) { + clearRefs(); + } if (instance.componentDidUpdate) { instance.__didUpdate = true; instance.componentDidUpdate(instance.lastProps, instance.lastState, instance.lastContext); @@ -2189,6 +2194,7 @@ function instantiateComponent(type, vtype, props, context) { CurrentOwner.cur = instance; var mixin = type(props, context); if (mixin && isFn(mixin.render)) { + //支持module pattern component delete instance.__isStateless; Object.assign(instance, mixin); } else { @@ -2220,18 +2226,17 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { instance.componentWillMount(); state = instance.__mergeStates(props, instanceContext); } - var rendered = renderComponent(instance, vnode, props, instanceContext, state, instance.__rendered); - instance.__hydrating = true; + var rendered = renderComponent(instance, vnode, props, instanceContext, state, instance.__rendered); + var childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; var dom = mountVnode(lastNode, rendered, vparent, childContext, updateQueue); - + updateQueue.push(instance); createInstanceChain(instance, vnode, rendered); updateInstanceChain(instance, dom); - updateQueue.push(instance); return dom; } @@ -2294,7 +2299,7 @@ function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { } _refreshComponent(instance, queue); //子组件先执行 - updateQueue.unshift(instance); + updateQueue.push(instance); return instance.__dom; } diff --git a/dist/ReactSelection.js b/dist/ReactSelection.js index f99ceb3d0..a2137213c 100644 --- a/dist/ReactSelection.js +++ b/dist/ReactSelection.js @@ -1094,7 +1094,9 @@ function setStateImpl(state, cb) { //组件挂载期 //componentWillUpdate中的setState/forceUpdate应该被忽略 if (this.__hydrating) { - //在挂载过程中,子组件在componentWillReceiveProps里调用父组件的setState,延迟到下一周期更新 + //在render方法中调用setState也会被延迟到下一周期更新.这存在两种情况, + //1. 组件直接调用自己的setState + //2. 子组件调用父组件的setState, this.__renderInNextCycle = true; } } else { @@ -1881,6 +1883,9 @@ function clearRefs() { } function callUpdate(instance) { if (instance.__lifeStage === 2) { + if (pendingRefs.length) { + clearRefs(); + } if (instance.componentDidUpdate) { instance.__didUpdate = true; instance.componentDidUpdate(instance.lastProps, instance.lastState, instance.lastContext); @@ -2189,6 +2194,7 @@ function instantiateComponent(type, vtype, props, context) { CurrentOwner.cur = instance; var mixin = type(props, context); if (mixin && isFn(mixin.render)) { + //支持module pattern component delete instance.__isStateless; Object.assign(instance, mixin); } else { @@ -2220,18 +2226,17 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { instance.componentWillMount(); state = instance.__mergeStates(props, instanceContext); } - var rendered = renderComponent(instance, vnode, props, instanceContext, state, instance.__rendered); - instance.__hydrating = true; + var rendered = renderComponent(instance, vnode, props, instanceContext, state, instance.__rendered); + var childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; var dom = mountVnode(lastNode, rendered, vparent, childContext, updateQueue); - + updateQueue.push(instance); createInstanceChain(instance, vnode, rendered); updateInstanceChain(instance, dom); - updateQueue.push(instance); return dom; } @@ -2294,7 +2299,7 @@ function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { } _refreshComponent(instance, queue); //子组件先执行 - updateQueue.unshift(instance); + updateQueue.push(instance); return instance.__dom; } diff --git a/dist/ReactShim.js b/dist/ReactShim.js index 16e9d0421..db4ea3514 100644 --- a/dist/ReactShim.js +++ b/dist/ReactShim.js @@ -533,7 +533,9 @@ function setStateImpl(state, cb) { //组件挂载期 //componentWillUpdate中的setState/forceUpdate应该被忽略 if (this.__hydrating) { - //在挂载过程中,子组件在componentWillReceiveProps里调用父组件的setState,延迟到下一周期更新 + //在render方法中调用setState也会被延迟到下一周期更新.这存在两种情况, + //1. 组件直接调用自己的setState + //2. 子组件调用父组件的setState, this.__renderInNextCycle = true; } } else { @@ -1727,6 +1729,9 @@ function clearRefs() { } function callUpdate(instance) { if (instance.__lifeStage === 2) { + if (pendingRefs.length) { + clearRefs(); + } if (instance.componentDidUpdate) { instance.__didUpdate = true; instance.componentDidUpdate(instance.lastProps, instance.lastState, instance.lastContext); @@ -2031,6 +2036,7 @@ function instantiateComponent(type, vtype, props, context) { CurrentOwner.cur = instance; var mixin = type(props, context); if (mixin && isFn(mixin.render)) { + //支持module pattern component delete instance.__isStateless; Object.assign(instance, mixin); } else { @@ -2062,18 +2068,17 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { instance.componentWillMount(); state = instance.__mergeStates(props, instanceContext); } - var rendered = renderComponent(instance, vnode, props, instanceContext, state, instance.__rendered); - instance.__hydrating = true; + var rendered = renderComponent(instance, vnode, props, instanceContext, state, instance.__rendered); + var childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; var dom = mountVnode(lastNode, rendered, vparent, childContext, updateQueue); - + updateQueue.push(instance); createInstanceChain(instance, vnode, rendered); updateInstanceChain(instance, dom); - updateQueue.push(instance); return dom; } @@ -2136,7 +2141,7 @@ function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { } _refreshComponent(instance, queue); //子组件先执行 - updateQueue.unshift(instance); + updateQueue.push(instance); return instance.__dom; } diff --git a/src/Component.js b/src/Component.js index 3c1cfd234..544354a46 100644 --- a/src/Component.js +++ b/src/Component.js @@ -90,10 +90,11 @@ function setStateImpl(state, cb) { if (!hasDOM) { //组件挂载期 //componentWillUpdate中的setState/forceUpdate应该被忽略 if (this.__hydrating) { - //在挂载过程中,子组件在componentWillReceiveProps里调用父组件的setState,延迟到下一周期更新 + //在render方法中调用setState也会被延迟到下一周期更新.这存在两种情况, + //1. 组件直接调用自己的setState + //2. 子组件调用父组件的setState, this.__renderInNextCycle = true; } - } else { //组件更新期 if (this.__receiving) { //componentWillReceiveProps中的setState/forceUpdate应该被忽略 diff --git a/src/diff.js b/src/diff.js index c02acf113..ec1cd6566 100644 --- a/src/diff.js +++ b/src/diff.js @@ -259,7 +259,7 @@ function instantiateComponent(type, vtype, props, context) { }; CurrentOwner.cur = instance; var mixin = type(props, context); - if (mixin && isFn(mixin.render)) { + if (mixin && isFn(mixin.render)) {//支持module pattern component delete instance.__isStateless; Object.assign(instance, mixin); } else { @@ -288,6 +288,8 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { instance.componentWillMount(); state = instance.__mergeStates(props, instanceContext); } + instance.__hydrating = true; + let rendered = renderComponent( instance, vnode, @@ -297,18 +299,17 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { instance.__rendered ); - instance.__hydrating = true; var childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; let dom = mountVnode(lastNode, rendered, vparent, childContext, updateQueue); - + updateQueue.push(instance); createInstanceChain(instance, vnode, rendered); updateInstanceChain(instance, dom); - updateQueue.push(instance); + return dom; } @@ -374,7 +375,7 @@ function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { } _refreshComponent(instance, queue); //子组件先执行 - updateQueue.unshift(instance); + updateQueue.push(instance); return instance.__dom; } diff --git a/src/scheduler.js b/src/scheduler.js index 7e3c7de7f..6426bcc03 100644 --- a/src/scheduler.js +++ b/src/scheduler.js @@ -13,6 +13,9 @@ function clearRefs() { } function callUpdate(instance) { if (instance.__lifeStage === 2) { + if(pendingRefs.length){ + clearRefs(); + } if (instance.componentDidUpdate) { instance.__didUpdate = true; instance.componentDidUpdate( diff --git a/test/modules/ReactComponentLifeCycle-test.jsx b/test/modules/ReactComponentLifeCycle-test.jsx index bbe87b0a3..e318181e9 100644 --- a/test/modules/ReactComponentLifeCycle-test.jsx +++ b/test/modules/ReactComponentLifeCycle-test.jsx @@ -512,7 +512,6 @@ function getLifeCycleState(instance) { const div = document.createElement('div'); ReactDOM.render( c && log.push('ref')} />, div); ReactDOM.render( c && log.push('ref')} />, div); -console.log(log.join("\n")) expect(log).toEqual([ 'will mount', 'render', diff --git a/test/modules/ReactCompositeComponent-test.jsx b/test/modules/ReactCompositeComponent-test.jsx new file mode 100644 index 000000000..8a557233d --- /dev/null +++ b/test/modules/ReactCompositeComponent-test.jsx @@ -0,0 +1,312 @@ +import React from "dist/React"; +import getTestDocument from "./getTestDocument"; +import ReactTestUtils from "lib/ReactTestUtils"; + +import ReactDOMServer from "dist/ReactDOMServer"; +//https://github.com/facebook/react/blob/master/src/isomorphic/children/__tests__/ReactChildren-test.js +var ReactDOM = window.ReactDOM || React; + +describe("ReactCompositeComponent", function() { + this.timeout(200000); + + + it("should support module pattern components", () => { + function Child({test}) { + return { + render() { + return
{test}
; + }, + }; + } + + var el = document.createElement("div"); + ReactDOM.render(, el); + + expect(el.textContent).toBe("test"); + }); + + it("should support rendering to different child types over time", () => { + var instance = ReactTestUtils.renderIntoDocument(); + var el = ReactDOM.findDOMNode(instance); + expect(el.tagName).toBe("A"); + + instance._toggleActivatedState(); + el = ReactDOM.findDOMNode(instance); + expect(el.tagName).toBe("B"); + + instance._toggleActivatedState(); + el = ReactDOM.findDOMNode(instance); + expect(el.tagName).toBe("A"); + }); + var MorphingComponent = class extends React.Component { + state = {activated: false}; + + _toggleActivatedState = () => { + this.setState({activated: !this.state.activated}); + }; + + render() { + var toggleActivatedState = this._toggleActivatedState; + return !this.state.activated + ? + : ; + } + }; + it("should react to state changes from callbacks", () => { + var instance = ReactTestUtils.renderIntoDocument(); + var el = ReactDOM.findDOMNode(instance); + expect(el.tagName).toBe("A"); + + ReactTestUtils.Simulate.click(el); + el = ReactDOM.findDOMNode(instance); + expect(el.tagName).toBe("B"); + }); + + + + it('should rewire refs when rendering to different child types', () => { + var instance = ReactTestUtils.renderIntoDocument(); + + expect(ReactDOM.findDOMNode(instance.refs.x).tagName).toBe('A'); + instance._toggleActivatedState(); + expect(ReactDOM.findDOMNode(instance.refs.x).tagName).toBe('B'); + instance._toggleActivatedState(); + expect(ReactDOM.findDOMNode(instance.refs.x).tagName).toBe('A'); + }); + var ChildUpdates = class extends React.Component { + getAnchor = () => { + return this.refs.anch; + }; + + render() { + var className = this.props.anchorClassOn ? 'anchorClass' : ''; + return this.props.renderAnchor + ? + : ; + } + }; + it('should not cache old DOM nodes when switching constructors', () => { + var container = document.createElement('div'); + var instance = ReactDOM.render( + , + container, + ); + ReactDOM.render( + // Warm any cache + , + container, + ); + ReactDOM.render( + // Clear out the anchor + , + container, + ); + ReactDOM.render( + // rerender + , + container, + ); + expect(instance.getAnchor().className).toBe(''); + }); + + it('should use default values for undefined props', () => { + class Component extends React.Component { + static defaultProps = {prop: 'testKey'}; + + render() { + return ; + } + } + + var instance1 = ReactTestUtils.renderIntoDocument(); + expect(instance1.props).toEqual({prop: 'testKey'}); + + var instance2 = ReactTestUtils.renderIntoDocument( + , + ); + expect(instance2.props).toEqual({prop: 'testKey'}); + + var instance3 = ReactTestUtils.renderIntoDocument( + , + ); + expect(instance3.props).toEqual({prop: null}); + }); + + + it('should not mutate passed-in props object', () => { + class Component extends React.Component { + static defaultProps = {prop: 'testKey'}; + + render() { + return ; + } + } + + var inputProps = {}; + var instance1 = ; + instance1 = ReactTestUtils.renderIntoDocument(instance1); + expect(instance1.props.prop).toBe('testKey'); + + // We don't mutate the input, just in case the caller wants to do something + // with it after using it to instantiate a component + expect(inputProps.prop).toBe(void 666); + }); + + it('should warn about `forceUpdate` on unmounted components', () => { + + var container = document.createElement('div'); + document.body.appendChild(container); + + class Component extends React.Component { + render() { + return
; + } + } + + var instance = ; + expect(instance.forceUpdate).toBe(void 666) + + instance = ReactDOM.render(instance, container); + instance.forceUpdate(); + + ReactDOM.unmountComponentAtNode(container); + + instance.forceUpdate(); + }); + + it('should warn about `setState` on unmounted components', () => { + + var container = document.createElement('div'); + document.body.appendChild(container); + + var renders = 0; + + class Component extends React.Component { + state = {value: 0}; + + render() { + renders++; + return
; + } + } + + var instance = ; + expect(instance.setState).toBe(void 666) + + instance = ReactDOM.render(instance, container); + + expect(renders).toBe(1); + + instance.setState({value: 1}); + + + expect(renders).toBe(2); + + ReactDOM.unmountComponentAtNode(container); + instance.setState({value: 2}); + + expect(renders).toBe(2); + + + }); + + it('should silently allow `setState`, not call cb on unmounting components', () => { + var cbCalled = false; + var container = document.createElement('div'); + document.body.appendChild(container); + + class Component extends React.Component { + state = {value: 0}; + + componentWillUnmount() { + expect(() => { + this.setState({value: 2}, function() { + cbCalled = true; + }); + }).not.toThrow(); + } + + render() { + return
; + } + } + + var instance = ReactDOM.render(, container); + instance.setState({value: 1}); + + ReactDOM.unmountComponentAtNode(container); + expect(cbCalled).toBe(false); + }); + + it('should warn about `setState` in render', () => { + spyOn(console, 'error'); + + var container = document.createElement('div'); + + var renderedState = -1; + var renderPasses = 0; + + class Component extends React.Component { + state = {value: 0}; + + render() { + renderPasses++; + renderedState = this.state.value; + if (this.state.value === 0) { + this.setState({value: 1}); + } + return
; + } + } + + + var instance = ReactDOM.render(, container); + + + + // The setState call is queued and then executed as a second pass. This + // behavior is undefined though so we're free to change it to suit the + // implementation details. + expect(renderPasses).toBe(2); + expect(renderedState).toBe(1); + expect(instance.state.value).toBe(1); + + // Forcing a rerender anywhere will cause the update to happen. + var instance2 = ReactDOM.render(, container); + expect(instance).toBe(instance2); + expect(renderedState).toBe(1); + expect(instance2.state.value).toBe(1); + }); + + it('should warn about `setState` in getChildContext', () => { + spyOn(console, 'error'); + + var container = document.createElement('div'); + + var renderPasses = 0; + + class Component extends React.Component { + state = {value: 0}; + + getChildContext() { + if (this.state.value === 0) { + this.setState({value: 4}); + } + } + + render() { + renderPasses++; + return
; + } + } + Component.childContextTypes = {}; + + var instance = ReactDOM.render(, container); + expect(renderPasses).toBe(2); + expect(instance.state.value).toBe(4); + + }); + + + +}); \ No newline at end of file diff --git a/test/spec.js b/test/spec.js index d65938b5c..f476d455b 100644 --- a/test/spec.js +++ b/test/spec.js @@ -34,7 +34,6 @@ require("./modules/ReactChildren-test.jsx"); require("./modules/createReactClassIntegration-test.jsx"); require("./modules/ReactMultiChild-test.jsx"); - require("./modules/refs-test.jsx"); require("./modules/refs-destruction-test.jsx"); require("./modules/ReactUpdates-test.jsx"); @@ -47,6 +46,7 @@ require("./modules/ReactCompositeComponentNestedState-test.jsx"); require("./modules/ReactComponentLifeCycle-test.jsx"); +require("./modules/ReactCompositeComponent-test.jsx"); From 018b965be37666fa290045ca124c413f014ef42b Mon Sep 17 00:00:00 2001 From: RubyLouvre <1669866773@qq.com> Date: Fri, 29 Sep 2017 00:56:28 +0800 Subject: [PATCH 10/57] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=8F=98=E9=87=8F?= =?UTF-8?q?=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dist/React.js | 25 +++--- dist/ReactIE.js | 25 +++--- dist/ReactSelection.js | 25 +++--- dist/ReactShim.js | 25 +++--- src/diff.js | 30 ++++--- test/modules/ReactCompositeComponent-test.jsx | 85 +++++++++++++++++++ 6 files changed, 153 insertions(+), 62 deletions(-) diff --git a/dist/React.js b/dist/React.js index 10774b35f..de307a6b7 100644 --- a/dist/React.js +++ b/dist/React.js @@ -2211,25 +2211,25 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { props = vnode.props; - var instanceContext = getContextByTypes(parentContext, type.contextTypes); - var instance = instantiateComponent(type, vtype, props, instanceContext); //互相持有引用 + var context = getContextByTypes(parentContext, type.contextTypes); + var instance = instantiateComponent(type, vtype, props, context); //互相持有引用 vnode._instance = instance; //用于refreshComponent instance.nextVnode = vnode; - vnode.context = instanceContext; + vnode.context = context; vnode.parentContext = parentContext; vnode.vparent = vparent; var state = instance.state; if (instance.componentWillMount) { instance.componentWillMount(); - state = instance.__mergeStates(props, instanceContext); + state = instance.__mergeStates(props, context); } instance.__hydrating = true; - var rendered = renderComponent(instance, vnode, props, instanceContext, state, instance.__rendered); + var rendered = renderComponent(instance, vnode, props, context, state, instance.__rendered); var childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; @@ -2249,10 +2249,13 @@ function renderComponent(instance, vnode, props, context, state, rendered) { //调整全局的 CurrentOwner.cur if (!rendered) { - var lastOwn = CurrentOwner.cur; - CurrentOwner.cur = instance; - rendered = instance.render(); - CurrentOwner.cur = lastOwn; + try { + var lastOwn = CurrentOwner.cur; + CurrentOwner.cur = instance; + rendered = instance.render(); + } finally { + CurrentOwner.cur = lastOwn; + } } //组件只能返回组件或null @@ -2281,9 +2284,7 @@ function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { instance.componentWillReceiveProps(nextProps, nextContext); instance.__receiving = false; } - if (!updateQueue.executor) { - updateQueue.executor = true; - } + // shouldComponentUpdate为false时不能阻止setState/forceUpdate cb的触发 //用于refreshComponent diff --git a/dist/ReactIE.js b/dist/ReactIE.js index a2137213c..80ab95c55 100644 --- a/dist/ReactIE.js +++ b/dist/ReactIE.js @@ -2210,25 +2210,25 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { props = vnode.props; - var instanceContext = getContextByTypes(parentContext, type.contextTypes); - var instance = instantiateComponent(type, vtype, props, instanceContext); //互相持有引用 + var context = getContextByTypes(parentContext, type.contextTypes); + var instance = instantiateComponent(type, vtype, props, context); //互相持有引用 vnode._instance = instance; //用于refreshComponent instance.nextVnode = vnode; - vnode.context = instanceContext; + vnode.context = context; vnode.parentContext = parentContext; vnode.vparent = vparent; var state = instance.state; if (instance.componentWillMount) { instance.componentWillMount(); - state = instance.__mergeStates(props, instanceContext); + state = instance.__mergeStates(props, context); } instance.__hydrating = true; - var rendered = renderComponent(instance, vnode, props, instanceContext, state, instance.__rendered); + var rendered = renderComponent(instance, vnode, props, context, state, instance.__rendered); var childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; @@ -2248,10 +2248,13 @@ function renderComponent(instance, vnode, props, context, state, rendered) { //调整全局的 CurrentOwner.cur if (!rendered) { - var lastOwn = CurrentOwner.cur; - CurrentOwner.cur = instance; - rendered = instance.render(); - CurrentOwner.cur = lastOwn; + try { + var lastOwn = CurrentOwner.cur; + CurrentOwner.cur = instance; + rendered = instance.render(); + } finally { + CurrentOwner.cur = lastOwn; + } } //组件只能返回组件或null @@ -2280,9 +2283,7 @@ function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { instance.componentWillReceiveProps(nextProps, nextContext); instance.__receiving = false; } - if (!updateQueue.executor) { - updateQueue.executor = true; - } + // shouldComponentUpdate为false时不能阻止setState/forceUpdate cb的触发 //用于refreshComponent diff --git a/dist/ReactSelection.js b/dist/ReactSelection.js index a2137213c..80ab95c55 100644 --- a/dist/ReactSelection.js +++ b/dist/ReactSelection.js @@ -2210,25 +2210,25 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { props = vnode.props; - var instanceContext = getContextByTypes(parentContext, type.contextTypes); - var instance = instantiateComponent(type, vtype, props, instanceContext); //互相持有引用 + var context = getContextByTypes(parentContext, type.contextTypes); + var instance = instantiateComponent(type, vtype, props, context); //互相持有引用 vnode._instance = instance; //用于refreshComponent instance.nextVnode = vnode; - vnode.context = instanceContext; + vnode.context = context; vnode.parentContext = parentContext; vnode.vparent = vparent; var state = instance.state; if (instance.componentWillMount) { instance.componentWillMount(); - state = instance.__mergeStates(props, instanceContext); + state = instance.__mergeStates(props, context); } instance.__hydrating = true; - var rendered = renderComponent(instance, vnode, props, instanceContext, state, instance.__rendered); + var rendered = renderComponent(instance, vnode, props, context, state, instance.__rendered); var childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; @@ -2248,10 +2248,13 @@ function renderComponent(instance, vnode, props, context, state, rendered) { //调整全局的 CurrentOwner.cur if (!rendered) { - var lastOwn = CurrentOwner.cur; - CurrentOwner.cur = instance; - rendered = instance.render(); - CurrentOwner.cur = lastOwn; + try { + var lastOwn = CurrentOwner.cur; + CurrentOwner.cur = instance; + rendered = instance.render(); + } finally { + CurrentOwner.cur = lastOwn; + } } //组件只能返回组件或null @@ -2280,9 +2283,7 @@ function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { instance.componentWillReceiveProps(nextProps, nextContext); instance.__receiving = false; } - if (!updateQueue.executor) { - updateQueue.executor = true; - } + // shouldComponentUpdate为false时不能阻止setState/forceUpdate cb的触发 //用于refreshComponent diff --git a/dist/ReactShim.js b/dist/ReactShim.js index db4ea3514..3a3fa212d 100644 --- a/dist/ReactShim.js +++ b/dist/ReactShim.js @@ -2052,25 +2052,25 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { props = vnode.props; - var instanceContext = getContextByTypes(parentContext, type.contextTypes); - var instance = instantiateComponent(type, vtype, props, instanceContext); //互相持有引用 + var context = getContextByTypes(parentContext, type.contextTypes); + var instance = instantiateComponent(type, vtype, props, context); //互相持有引用 vnode._instance = instance; //用于refreshComponent instance.nextVnode = vnode; - vnode.context = instanceContext; + vnode.context = context; vnode.parentContext = parentContext; vnode.vparent = vparent; var state = instance.state; if (instance.componentWillMount) { instance.componentWillMount(); - state = instance.__mergeStates(props, instanceContext); + state = instance.__mergeStates(props, context); } instance.__hydrating = true; - var rendered = renderComponent(instance, vnode, props, instanceContext, state, instance.__rendered); + var rendered = renderComponent(instance, vnode, props, context, state, instance.__rendered); var childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; @@ -2090,10 +2090,13 @@ function renderComponent(instance, vnode, props, context, state, rendered) { //调整全局的 CurrentOwner.cur if (!rendered) { - var lastOwn = CurrentOwner.cur; - CurrentOwner.cur = instance; - rendered = instance.render(); - CurrentOwner.cur = lastOwn; + try { + var lastOwn = CurrentOwner.cur; + CurrentOwner.cur = instance; + rendered = instance.render(); + } finally { + CurrentOwner.cur = lastOwn; + } } //组件只能返回组件或null @@ -2122,9 +2125,7 @@ function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { instance.componentWillReceiveProps(nextProps, nextContext); instance.__receiving = false; } - if (!updateQueue.executor) { - updateQueue.executor = true; - } + // shouldComponentUpdate为false时不能阻止setState/forceUpdate cb的触发 //用于refreshComponent diff --git a/src/diff.js b/src/diff.js index ec1cd6566..e604c8b9c 100644 --- a/src/diff.js +++ b/src/diff.js @@ -272,21 +272,21 @@ function instantiateComponent(type, vtype, props, context) { function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { let { type, vtype, props } = vnode; - let instanceContext = getContextByTypes(parentContext, type.contextTypes); - let instance = instantiateComponent(type, vtype, props, instanceContext); //互相持有引用 + let context = getContextByTypes(parentContext, type.contextTypes); + let instance = instantiateComponent(type, vtype, props, context); //互相持有引用 vnode._instance = instance; //用于refreshComponent instance.nextVnode = vnode; - vnode.context = instanceContext; + vnode.context = context; vnode.parentContext = parentContext; vnode.vparent = vparent; let state = instance.state; if (instance.componentWillMount) { instance.componentWillMount(); - state = instance.__mergeStates(props, instanceContext); + state = instance.__mergeStates(props, context); } instance.__hydrating = true; @@ -294,7 +294,7 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { instance, vnode, props, - instanceContext, + context, state, instance.__rendered ); @@ -321,10 +321,15 @@ function renderComponent(instance, vnode, props, context, state, rendered) { //调整全局的 CurrentOwner.cur if (!rendered) { - var lastOwn = CurrentOwner.cur; - CurrentOwner.cur = instance; - rendered = instance.render(); - CurrentOwner.cur = lastOwn; + try{ + var lastOwn = CurrentOwner.cur; + CurrentOwner.cur = instance; + rendered = instance.render(); + }finally{ + CurrentOwner.cur = lastOwn; + } + + } //组件只能返回组件或null @@ -356,9 +361,7 @@ function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { instance.componentWillReceiveProps(nextProps, nextContext); instance.__receiving = false; } - if (!updateQueue.executor) { - updateQueue.executor = true; - } + // shouldComponentUpdate为false时不能阻止setState/forceUpdate cb的触发 //用于refreshComponent @@ -399,7 +402,6 @@ function _refreshComponent(instance, updateQueue) { nextVnode._instance = instance; //important let nextState = instance.__mergeStates(nextProps, nextContext); - if ( !instance.__forceUpdate && instance.shouldComponentUpdate && @@ -408,7 +410,7 @@ function _refreshComponent(instance, updateQueue) { instance.__forceUpdate = false; return dom; } - // clearRefs(); + instance.__hydrating = true; instance.__forceUpdate = false; if (instance.componentWillUpdate) { diff --git a/test/modules/ReactCompositeComponent-test.jsx b/test/modules/ReactCompositeComponent-test.jsx index 8a557233d..8c14890bc 100644 --- a/test/modules/ReactCompositeComponent-test.jsx +++ b/test/modules/ReactCompositeComponent-test.jsx @@ -307,6 +307,91 @@ describe("ReactCompositeComponent", function() { }); + it('should call componentWillUnmount before unmounting', () => { + var container = document.createElement('div'); + var innerUnmounted = false; + + class Component extends React.Component { + render() { + return ( +
+ + Text +
+ ); + } + } + + class Inner extends React.Component { + componentWillUnmount() { + innerUnmounted = true; + } + + render() { + return
; + } + } + + ReactDOM.render(, container); + ReactDOM.unmountComponentAtNode(container); + expect(innerUnmounted).toBe(true); + }); + + it('should warn when shouldComponentUpdate() returns undefined', () => { + var container = document.createElement('div'); + class Component extends React.Component { + state = {bogus: false}; + + shouldComponentUpdate() { + return undefined; + } + + render() { + return
{this.state.bogus}
; + } + } + + var instance = ReactDOM.render(, container); + instance.setState({bogus: true}); + expect(container.textContent).toBe("");//布尔会转换为空字符串 + + }); +//https://github.com/facebook/react/blob/master/src/renderers/__tests__/ReactCompositeComponent-test.js#L526 + it('should pass context to children when not owner', () => { + class Parent extends React.Component { + render() { + return ; + } + } + + class Child extends React.Component { + static childContextTypes = { + foo: React.PropTypes.string, + }; + + getChildContext() { + return { + foo: 'bar', + }; + } + + render() { + return React.Children.only(this.props.children); + } + } + + class Grandchild extends React.Component { + static contextTypes = { + foo: React.PropTypes.string, + }; + + render() { + return
{this.context.foo}
; + } + } + var component = ReactTestUtils.renderIntoDocument(); + expect(ReactDOM.findDOMNode(component).innerHTML).toBe('bar'); + }); }); \ No newline at end of file From 096b876a259cec6b737e6dc7bff424c54b66f349 Mon Sep 17 00:00:00 2001 From: RubyLouvre <1669866773@qq.com> Date: Fri, 29 Sep 2017 11:04:21 +0800 Subject: [PATCH 11/57] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=AF=B9=E7=9B=B8?= =?UTF-8?q?=E5=90=8C=E5=BC=95=E7=94=A8=E5=AF=B9=E8=B1=A1=E7=9A=84=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E6=A3=80=E6=B5=8B=E7=AD=96=E7=95=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dist/React.js | 32 +++++++++--- dist/ReactIE.js | 32 +++++++++--- dist/ReactSelection.js | 32 +++++++++--- dist/ReactShim.js | 32 +++++++++--- src/diff.js | 50 ++++++++++++------- test/modules/ReactCompositeComponent-test.jsx | 25 ++++++++++ 6 files changed, 158 insertions(+), 45 deletions(-) diff --git a/dist/React.js b/dist/React.js index de307a6b7..f628ee72b 100644 --- a/dist/React.js +++ b/dist/React.js @@ -1,5 +1,5 @@ /** - * by 司徒正美 Copyright 2017-09-28 + * by 司徒正美 Copyright 2017-09-29 * IE9+ */ @@ -2072,7 +2072,26 @@ function mountVnode(lastNode, vnode) { return patchStrategy[vnode.vtype].apply(null, arguments); } -function updateVnode(lastVnode) { +function updateByContext(vnode) { + var vchildren = vnode.vchildren; + if (vchildren) { + for (var i = 0; i < vchildren.length; i++) { + var el = vchildren[i]; + if (el.vtype === 1) { + if (updateByContext(el)) { + return true; + } + } else if (el.vtype && el.type.contextTypes) { + return true; + } + } + } +} + +function updateVnode(lastVnode, nextVnode) { + if (lastVnode === nextVnode && !updateByContext(lastVnode)) { + return lastVnode._hostNode; + } return patchStrategy[lastVnode.vtype + 10].apply(null, arguments); } @@ -2324,12 +2343,11 @@ function _refreshComponent(instance, updateQueue) { nextVnode._instance = instance; //important var nextState = instance.__mergeStates(nextProps, nextContext); - if (!instance.__forceUpdate && instance.shouldComponentUpdate && instance.shouldComponentUpdate(nextProps, nextState, nextContext) === false) { instance.__forceUpdate = false; return dom; } - // clearRefs(); + instance.__hydrating = true; instance.__forceUpdate = false; if (instance.componentWillUpdate) { @@ -2340,9 +2358,9 @@ function _refreshComponent(instance, updateQueue) { instance.lastContext = lastContext; //这里会更新instance的props, context, state var nextRendered = renderComponent(instance, nextVnode, nextProps, nextContext, nextState); - if (lastRendered !== nextRendered && parentContext) { - dom = alignVnode(lastRendered, nextRendered, vparent, getChildContext(instance, parentContext), updateQueue); - } + + dom = alignVnode(lastRendered, nextRendered, vparent, getChildContext(instance, parentContext), updateQueue); + createInstanceChain(instance, nextVnode, nextRendered); updateInstanceChain(instance, dom); diff --git a/dist/ReactIE.js b/dist/ReactIE.js index 80ab95c55..7fa4f57ee 100644 --- a/dist/ReactIE.js +++ b/dist/ReactIE.js @@ -1,5 +1,5 @@ /** - * IE6+,有问题请加QQ 370262116 by 司徒正美 Copyright 2017-09-28 + * IE6+,有问题请加QQ 370262116 by 司徒正美 Copyright 2017-09-29 */ (function (global, factory) { @@ -2071,7 +2071,26 @@ function mountVnode(lastNode, vnode) { return patchStrategy[vnode.vtype].apply(null, arguments); } -function updateVnode(lastVnode) { +function updateByContext(vnode) { + var vchildren = vnode.vchildren; + if (vchildren) { + for (var i = 0; i < vchildren.length; i++) { + var el = vchildren[i]; + if (el.vtype === 1) { + if (updateByContext(el)) { + return true; + } + } else if (el.vtype && el.type.contextTypes) { + return true; + } + } + } +} + +function updateVnode(lastVnode, nextVnode) { + if (lastVnode === nextVnode && !updateByContext(lastVnode)) { + return lastVnode._hostNode; + } return patchStrategy[lastVnode.vtype + 10].apply(null, arguments); } @@ -2323,12 +2342,11 @@ function _refreshComponent(instance, updateQueue) { nextVnode._instance = instance; //important var nextState = instance.__mergeStates(nextProps, nextContext); - if (!instance.__forceUpdate && instance.shouldComponentUpdate && instance.shouldComponentUpdate(nextProps, nextState, nextContext) === false) { instance.__forceUpdate = false; return dom; } - // clearRefs(); + instance.__hydrating = true; instance.__forceUpdate = false; if (instance.componentWillUpdate) { @@ -2339,9 +2357,9 @@ function _refreshComponent(instance, updateQueue) { instance.lastContext = lastContext; //这里会更新instance的props, context, state var nextRendered = renderComponent(instance, nextVnode, nextProps, nextContext, nextState); - if (lastRendered !== nextRendered && parentContext) { - dom = alignVnode(lastRendered, nextRendered, vparent, getChildContext(instance, parentContext), updateQueue); - } + + dom = alignVnode(lastRendered, nextRendered, vparent, getChildContext(instance, parentContext), updateQueue); + createInstanceChain(instance, nextVnode, nextRendered); updateInstanceChain(instance, dom); diff --git a/dist/ReactSelection.js b/dist/ReactSelection.js index 80ab95c55..7fa4f57ee 100644 --- a/dist/ReactSelection.js +++ b/dist/ReactSelection.js @@ -1,5 +1,5 @@ /** - * IE6+,有问题请加QQ 370262116 by 司徒正美 Copyright 2017-09-28 + * IE6+,有问题请加QQ 370262116 by 司徒正美 Copyright 2017-09-29 */ (function (global, factory) { @@ -2071,7 +2071,26 @@ function mountVnode(lastNode, vnode) { return patchStrategy[vnode.vtype].apply(null, arguments); } -function updateVnode(lastVnode) { +function updateByContext(vnode) { + var vchildren = vnode.vchildren; + if (vchildren) { + for (var i = 0; i < vchildren.length; i++) { + var el = vchildren[i]; + if (el.vtype === 1) { + if (updateByContext(el)) { + return true; + } + } else if (el.vtype && el.type.contextTypes) { + return true; + } + } + } +} + +function updateVnode(lastVnode, nextVnode) { + if (lastVnode === nextVnode && !updateByContext(lastVnode)) { + return lastVnode._hostNode; + } return patchStrategy[lastVnode.vtype + 10].apply(null, arguments); } @@ -2323,12 +2342,11 @@ function _refreshComponent(instance, updateQueue) { nextVnode._instance = instance; //important var nextState = instance.__mergeStates(nextProps, nextContext); - if (!instance.__forceUpdate && instance.shouldComponentUpdate && instance.shouldComponentUpdate(nextProps, nextState, nextContext) === false) { instance.__forceUpdate = false; return dom; } - // clearRefs(); + instance.__hydrating = true; instance.__forceUpdate = false; if (instance.componentWillUpdate) { @@ -2339,9 +2357,9 @@ function _refreshComponent(instance, updateQueue) { instance.lastContext = lastContext; //这里会更新instance的props, context, state var nextRendered = renderComponent(instance, nextVnode, nextProps, nextContext, nextState); - if (lastRendered !== nextRendered && parentContext) { - dom = alignVnode(lastRendered, nextRendered, vparent, getChildContext(instance, parentContext), updateQueue); - } + + dom = alignVnode(lastRendered, nextRendered, vparent, getChildContext(instance, parentContext), updateQueue); + createInstanceChain(instance, nextVnode, nextRendered); updateInstanceChain(instance, dom); diff --git a/dist/ReactShim.js b/dist/ReactShim.js index 3a3fa212d..591cd2f74 100644 --- a/dist/ReactShim.js +++ b/dist/ReactShim.js @@ -1,7 +1,7 @@ /** * 此版本要求浏览器没有createClass, createFactory, PropTypes, isValidElement, * unmountComponentAtNode,unstable_renderSubtreeIntoContainer - * QQ 370262116 by 司徒正美 Copyright 2017-09-28 + * QQ 370262116 by 司徒正美 Copyright 2017-09-29 */ (function (global, factory) { @@ -1913,7 +1913,26 @@ function mountVnode(lastNode, vnode) { return patchStrategy[vnode.vtype].apply(null, arguments); } -function updateVnode(lastVnode) { +function updateByContext(vnode) { + var vchildren = vnode.vchildren; + if (vchildren) { + for (var i = 0; i < vchildren.length; i++) { + var el = vchildren[i]; + if (el.vtype === 1) { + if (updateByContext(el)) { + return true; + } + } else if (el.vtype && el.type.contextTypes) { + return true; + } + } + } +} + +function updateVnode(lastVnode, nextVnode) { + if (lastVnode === nextVnode && !updateByContext(lastVnode)) { + return lastVnode._hostNode; + } return patchStrategy[lastVnode.vtype + 10].apply(null, arguments); } @@ -2165,12 +2184,11 @@ function _refreshComponent(instance, updateQueue) { nextVnode._instance = instance; //important var nextState = instance.__mergeStates(nextProps, nextContext); - if (!instance.__forceUpdate && instance.shouldComponentUpdate && instance.shouldComponentUpdate(nextProps, nextState, nextContext) === false) { instance.__forceUpdate = false; return dom; } - // clearRefs(); + instance.__hydrating = true; instance.__forceUpdate = false; if (instance.componentWillUpdate) { @@ -2181,9 +2199,9 @@ function _refreshComponent(instance, updateQueue) { instance.lastContext = lastContext; //这里会更新instance的props, context, state var nextRendered = renderComponent(instance, nextVnode, nextProps, nextContext, nextState); - if (lastRendered !== nextRendered && parentContext) { - dom = alignVnode(lastRendered, nextRendered, vparent, getChildContext(instance, parentContext), updateQueue); - } + + dom = alignVnode(lastRendered, nextRendered, vparent, getChildContext(instance, parentContext), updateQueue); + createInstanceChain(instance, nextVnode, nextRendered); updateInstanceChain(instance, dom); diff --git a/src/diff.js b/src/diff.js index e604c8b9c..9165cd0b3 100644 --- a/src/diff.js +++ b/src/diff.js @@ -138,7 +138,26 @@ function mountVnode(lastNode, vnode) { return patchStrategy[vnode.vtype].apply(null, arguments); } -function updateVnode(lastVnode) { +function updateByContext(vnode) { + let vchildren = vnode.vchildren; + if (vchildren) { + for (let i = 0; i < vchildren.length; i++) { + let el = vchildren[i]; + if (el.vtype === 1) { + if (updateByContext(el)) { + return true; + } + } else if (el.vtype && el.type.contextTypes) { + return true; + } + } + } +} + +function updateVnode(lastVnode, nextVnode) { + if (lastVnode === nextVnode && !updateByContext(lastVnode)) { + return lastVnode._hostNode; + } return patchStrategy[lastVnode.vtype + 10].apply(null, arguments); } @@ -259,7 +278,8 @@ function instantiateComponent(type, vtype, props, context) { }; CurrentOwner.cur = instance; var mixin = type(props, context); - if (mixin && isFn(mixin.render)) {//支持module pattern component + if (mixin && isFn(mixin.render)) { + //支持module pattern component delete instance.__isStateless; Object.assign(instance, mixin); } else { @@ -299,7 +319,6 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { instance.__rendered ); - var childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; @@ -309,7 +328,6 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { createInstanceChain(instance, vnode, rendered); updateInstanceChain(instance, dom); - return dom; } @@ -321,15 +339,13 @@ function renderComponent(instance, vnode, props, context, state, rendered) { //调整全局的 CurrentOwner.cur if (!rendered) { - try{ + try { var lastOwn = CurrentOwner.cur; CurrentOwner.cur = instance; rendered = instance.render(); - }finally{ + } finally { CurrentOwner.cur = lastOwn; } - - } //组件只能返回组件或null @@ -427,15 +443,15 @@ function _refreshComponent(instance, updateQueue) { nextContext, nextState ); - if (lastRendered !== nextRendered && parentContext) { - dom = alignVnode( - lastRendered, - nextRendered, - vparent, - getChildContext(instance, parentContext), - updateQueue - ); - } + + dom = alignVnode( + lastRendered, + nextRendered, + vparent, + getChildContext(instance, parentContext), + updateQueue + ); + createInstanceChain(instance, nextVnode, nextRendered); updateInstanceChain(instance, dom); diff --git a/test/modules/ReactCompositeComponent-test.jsx b/test/modules/ReactCompositeComponent-test.jsx index 8c14890bc..4b606e4d2 100644 --- a/test/modules/ReactCompositeComponent-test.jsx +++ b/test/modules/ReactCompositeComponent-test.jsx @@ -394,4 +394,29 @@ describe("ReactCompositeComponent", function() { expect(ReactDOM.findDOMNode(component).innerHTML).toBe('bar'); }); + + it('should skip update when rerendering element in container', () => { + class Parent extends React.Component { + render() { + return
{this.props.children}
; + } + } + + var childRenders = 0; + + class Child extends React.Component { + render() { + childRenders++; + return
; + } + } + + var container = document.createElement('div'); + var child = ; + + ReactDOM.render({child}, container); + ReactDOM.render({child}, container); + expect(childRenders).toBe(1); + }); + }); \ No newline at end of file From 103adc00ae3c1ac17454b8dc5562e16bd6ea4ad4 Mon Sep 17 00:00:00 2001 From: RubyLouvre <1669866773@qq.com> Date: Fri, 29 Sep 2017 12:27:41 +0800 Subject: [PATCH 12/57] =?UTF-8?q?=E6=8F=90=E5=8F=96=E5=87=BAinstantiateCom?= =?UTF-8?q?ponent=E6=A8=A1=E5=9D=97=E4=B8=8E=E5=BC=BA=E5=8C=96renderCompon?= =?UTF-8?q?ent=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dist/React.js | 150 +++++++++--------- dist/ReactIE.js | 150 +++++++++--------- dist/ReactSelection.js | 150 +++++++++--------- dist/ReactShim.js | 150 +++++++++--------- src/createElement.js | 6 +- src/diff.js | 128 +++++---------- src/instantiateComponent.js | 45 ++++++ src/scheduler.js | 2 +- version.md | 4 +- ...ct\347\232\204\345\257\271\346\257\224.md" | 2 + 10 files changed, 381 insertions(+), 406 deletions(-) create mode 100644 src/instantiateComponent.js diff --git a/dist/React.js b/dist/React.js index f628ee72b..f924d92ed 100644 --- a/dist/React.js +++ b/dist/React.js @@ -223,7 +223,7 @@ function createElement(type, config) { key = null, ref = null, argsLen = children.length; - if (isFn(type)) { + if (type && type.call) { vtype = type.prototype && type.prototype.render ? 2 : 4; } else if (type + "" !== type) { console.error("createElement第一个参数类型错误"); // eslint-disable-line @@ -420,7 +420,7 @@ var FAKE_SYMBOL = "@@iterator"; function getIteractor(a) { if (typeNumber(a) > 7) { var iteratorFn = REAL_SYMBOL && a[REAL_SYMBOL] || a[FAKE_SYMBOL]; - if (isFn(iteratorFn)) { + if (iteratorFn && iteratorFn.call) { return iteratorFn; } } @@ -1874,6 +1874,49 @@ function getOptionSelected(option, selected) { dom.selected = selected; } +function alwaysNull() { + return null; +} +function instantiateComponent(type, vtype, props, context) { + var instance; + if (vtype === 2) { + instance = new type(props, context); + //防止用户没有调用super或没有传够参数 + instance.props = instance.props || props; + instance.context = instance.context || context; + } else { + instance = { + refs: {}, + render: function render() { + return type(this.props, this.context); + }, + __isStateless: 1, + state: null, + props: props, + context: context, + __pendingCallbacks: [], + __current: noop, + __mergeStates: alwaysNull + }; + var lastOwn = CurrentOwner.cur; + CurrentOwner.cur = instance; + try { + var mixin = type(props, context); + } finally { + CurrentOwner.cur = lastOwn; + } + if (mixin && mixin.render) { + //支持module pattern component + delete instance.__isStateless; + Object.assign(instance, mixin); + } else { + instance.__rendered = mixin; + } + } + + return instance; +} + var pendingRefs = []; function clearRefs() { var refs = pendingRefs.slice(0); @@ -1928,7 +1971,7 @@ function drainQueue(queue) { } instance.__hydrating = false; //子树已经构建完毕 while (instance.__renderInNextCycle) { - options._refreshComponent(instance, queue); + options.refreshComponent(instance, queue); callUpdate(instance); } } @@ -2185,45 +2228,7 @@ function alignChildren(parentNode, vparent, context, updateQueue) { parentNode.removeChild(childNodes[n]); } } -function alwaysNull() { - return null; -} -function instantiateComponent(type, vtype, props, context) { - var instance; - var lastOwn = CurrentOwner.cur; - if (vtype === 2) { - instance = new type(props, context); - //防止用户没有调用super或没有传够参数 - instance.props = instance.props || props; - instance.context = instance.context || context; - } else { - instance = { - refs: {}, - render: function render() { - return type(this.props, this.context); - }, - __isStateless: 1, - state: null, - props: props, - context: context, - constructor: type, - __pendingCallbacks: [], - __current: noop, - __mergeStates: alwaysNull - }; - CurrentOwner.cur = instance; - var mixin = type(props, context); - if (mixin && isFn(mixin.render)) { - //支持module pattern component - delete instance.__isStateless; - Object.assign(instance, mixin); - } else { - instance.__rendered = mixin; - } - } - CurrentOwner.cur = lastOwn; - return instance; -} + function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { var type = vnode.type, vtype = vnode.vtype, @@ -2248,24 +2253,20 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { } instance.__hydrating = true; - var rendered = renderComponent(instance, vnode, props, context, state, instance.__rendered); - - var childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; + var dom = renderComponent(instance, vnode, props, context, state, function (nextRendered, childContext) { + return mountVnode(lastNode, nextRendered, vparent, childContext, updateQueue); + }, instance.__rendered); - var dom = mountVnode(lastNode, rendered, vparent, childContext, updateQueue); updateQueue.push(instance); - createInstanceChain(instance, vnode, rendered); - updateInstanceChain(instance, dom); return dom; } -function renderComponent(instance, vnode, props, context, state, rendered) { - // 同时给有状态与无状态组件使用 - instance.props = props; +function renderComponent(instance, vnode, props, context, state, cb, rendered) { + //更新新属性 instance.state = state; + instance.props = props; instance.context = context; - //调整全局的 CurrentOwner.cur if (!rendered) { try { @@ -2286,7 +2287,17 @@ function renderComponent(instance, vnode, props, context, state, rendered) { } vnode._instance = instance; - return instance.__rendered = rendered; + instance.__rendered = rendered; + + var parentContext = vnode.parentContext; + var childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; + + var dom = cb(rendered, childContext); + + createInstanceChain(instance, vnode, rendered); + updateInstanceChain(instance, dom); + + return dom; } function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { @@ -2303,9 +2314,6 @@ function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { instance.componentWillReceiveProps(nextProps, nextContext); instance.__receiving = false; } - - // shouldComponentUpdate为false时不能阻止setState/forceUpdate cb的触发 - //用于refreshComponent instance.nextVnode = nextVnode; nextVnode.context = nextContext; @@ -2318,14 +2326,14 @@ function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { queue = []; queue.isChildProcess = true; } - _refreshComponent(instance, queue); + refreshComponent(instance, queue); //子组件先执行 updateQueue.push(instance); return instance.__dom; } -function _refreshComponent(instance, updateQueue) { +function refreshComponent(instance, updateQueue) { var lastProps = instance.props, lastState = instance.state, lastContext = instance.context, @@ -2335,8 +2343,6 @@ function _refreshComponent(instance, updateQueue) { instance.__renderInNextCycle = null; var nextVnode = instance.nextVnode; var nextContext = nextVnode.context; - - var parentContext = nextVnode.parentContext; var nextProps = nextVnode.props; var vparent = nextVnode.vparent; @@ -2357,12 +2363,9 @@ function _refreshComponent(instance, updateQueue) { instance.lastState = lastState; instance.lastContext = lastContext; //这里会更新instance的props, context, state - var nextRendered = renderComponent(instance, nextVnode, nextProps, nextContext, nextState); - - dom = alignVnode(lastRendered, nextRendered, vparent, getChildContext(instance, parentContext), updateQueue); - - createInstanceChain(instance, nextVnode, nextRendered); - updateInstanceChain(instance, dom); + dom = renderComponent(instance, nextVnode, nextProps, nextContext, nextState, function (nextRendered, childContext) { + return alignVnode(lastRendered, nextRendered, vparent, childContext, updateQueue); + }); instance.__lifeStage = 2; instance.__hydrating = false; @@ -2372,7 +2375,7 @@ function _refreshComponent(instance, updateQueue) { return dom; } -options._refreshComponent = _refreshComponent; +options.refreshComponent = refreshComponent; function alignVnode(lastVnode, nextVnode, vparent, context, updateQueue) { var node = lastVnode._hostNode, @@ -2381,18 +2384,12 @@ function alignVnode(lastVnode, nextVnode, vparent, context, updateQueue) { dom = updateVnode(lastVnode, nextVnode, vparent, context, updateQueue); } else { disposeVnode(lastVnode); - // let innerUpdateQueue = updateQueue.isChildProcess - // ? updateQueue - // : nextVnode.vtype === 2 ? [] : updateQueue; dom = mountVnode(null, nextVnode, vparent, context, updateQueue); var p = node.parentNode; if (p) { p.replaceChild(dom, node); removeDOMElement(node); } - // if (innerUpdateQueue !== updateQueue) { - // clearScheduler(innerUpdateQueue); - // } } return dom; } @@ -2457,9 +2454,7 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { hit = void 0, dom = void 0, oldDom = void 0, - - // hasExecutor = updateQueue.executor, - nextChild = void 0, + nextChild = void 0, lastChild = void 0; //第一次循环,构建移动指令(actions)与移除名单(removeHits)与命中名单(fuzzyHits) if (nextLength) { @@ -2546,9 +2541,6 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { disposeVnode(el); } }); - // if (!hasExecutor && updateQueue.executor) { - // clearScheduler(updateQueue); - // } } function isSameNode(a, b) { diff --git a/dist/ReactIE.js b/dist/ReactIE.js index 7fa4f57ee..bb100ee6e 100644 --- a/dist/ReactIE.js +++ b/dist/ReactIE.js @@ -222,7 +222,7 @@ function createElement(type, config) { key = null, ref = null, argsLen = children.length; - if (isFn(type)) { + if (type && type.call) { vtype = type.prototype && type.prototype.render ? 2 : 4; } else if (type + "" !== type) { console.error("createElement第一个参数类型错误"); // eslint-disable-line @@ -419,7 +419,7 @@ var FAKE_SYMBOL = "@@iterator"; function getIteractor(a) { if (typeNumber(a) > 7) { var iteratorFn = REAL_SYMBOL && a[REAL_SYMBOL] || a[FAKE_SYMBOL]; - if (isFn(iteratorFn)) { + if (iteratorFn && iteratorFn.call) { return iteratorFn; } } @@ -1873,6 +1873,49 @@ function getOptionSelected(option, selected) { dom.selected = selected; } +function alwaysNull() { + return null; +} +function instantiateComponent(type, vtype, props, context) { + var instance; + if (vtype === 2) { + instance = new type(props, context); + //防止用户没有调用super或没有传够参数 + instance.props = instance.props || props; + instance.context = instance.context || context; + } else { + instance = { + refs: {}, + render: function render() { + return type(this.props, this.context); + }, + __isStateless: 1, + state: null, + props: props, + context: context, + __pendingCallbacks: [], + __current: noop, + __mergeStates: alwaysNull + }; + var lastOwn = CurrentOwner.cur; + CurrentOwner.cur = instance; + try { + var mixin = type(props, context); + } finally { + CurrentOwner.cur = lastOwn; + } + if (mixin && mixin.render) { + //支持module pattern component + delete instance.__isStateless; + Object.assign(instance, mixin); + } else { + instance.__rendered = mixin; + } + } + + return instance; +} + var pendingRefs = []; function clearRefs() { var refs = pendingRefs.slice(0); @@ -1927,7 +1970,7 @@ function drainQueue(queue) { } instance.__hydrating = false; //子树已经构建完毕 while (instance.__renderInNextCycle) { - options._refreshComponent(instance, queue); + options.refreshComponent(instance, queue); callUpdate(instance); } } @@ -2184,45 +2227,7 @@ function alignChildren(parentNode, vparent, context, updateQueue) { parentNode.removeChild(childNodes[n]); } } -function alwaysNull() { - return null; -} -function instantiateComponent(type, vtype, props, context) { - var instance; - var lastOwn = CurrentOwner.cur; - if (vtype === 2) { - instance = new type(props, context); - //防止用户没有调用super或没有传够参数 - instance.props = instance.props || props; - instance.context = instance.context || context; - } else { - instance = { - refs: {}, - render: function render() { - return type(this.props, this.context); - }, - __isStateless: 1, - state: null, - props: props, - context: context, - constructor: type, - __pendingCallbacks: [], - __current: noop, - __mergeStates: alwaysNull - }; - CurrentOwner.cur = instance; - var mixin = type(props, context); - if (mixin && isFn(mixin.render)) { - //支持module pattern component - delete instance.__isStateless; - Object.assign(instance, mixin); - } else { - instance.__rendered = mixin; - } - } - CurrentOwner.cur = lastOwn; - return instance; -} + function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { var type = vnode.type, vtype = vnode.vtype, @@ -2247,24 +2252,20 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { } instance.__hydrating = true; - var rendered = renderComponent(instance, vnode, props, context, state, instance.__rendered); - - var childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; + var dom = renderComponent(instance, vnode, props, context, state, function (nextRendered, childContext) { + return mountVnode(lastNode, nextRendered, vparent, childContext, updateQueue); + }, instance.__rendered); - var dom = mountVnode(lastNode, rendered, vparent, childContext, updateQueue); updateQueue.push(instance); - createInstanceChain(instance, vnode, rendered); - updateInstanceChain(instance, dom); return dom; } -function renderComponent(instance, vnode, props, context, state, rendered) { - // 同时给有状态与无状态组件使用 - instance.props = props; +function renderComponent(instance, vnode, props, context, state, cb, rendered) { + //更新新属性 instance.state = state; + instance.props = props; instance.context = context; - //调整全局的 CurrentOwner.cur if (!rendered) { try { @@ -2285,7 +2286,17 @@ function renderComponent(instance, vnode, props, context, state, rendered) { } vnode._instance = instance; - return instance.__rendered = rendered; + instance.__rendered = rendered; + + var parentContext = vnode.parentContext; + var childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; + + var dom = cb(rendered, childContext); + + createInstanceChain(instance, vnode, rendered); + updateInstanceChain(instance, dom); + + return dom; } function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { @@ -2302,9 +2313,6 @@ function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { instance.componentWillReceiveProps(nextProps, nextContext); instance.__receiving = false; } - - // shouldComponentUpdate为false时不能阻止setState/forceUpdate cb的触发 - //用于refreshComponent instance.nextVnode = nextVnode; nextVnode.context = nextContext; @@ -2317,14 +2325,14 @@ function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { queue = []; queue.isChildProcess = true; } - _refreshComponent(instance, queue); + refreshComponent(instance, queue); //子组件先执行 updateQueue.push(instance); return instance.__dom; } -function _refreshComponent(instance, updateQueue) { +function refreshComponent(instance, updateQueue) { var lastProps = instance.props, lastState = instance.state, lastContext = instance.context, @@ -2334,8 +2342,6 @@ function _refreshComponent(instance, updateQueue) { instance.__renderInNextCycle = null; var nextVnode = instance.nextVnode; var nextContext = nextVnode.context; - - var parentContext = nextVnode.parentContext; var nextProps = nextVnode.props; var vparent = nextVnode.vparent; @@ -2356,12 +2362,9 @@ function _refreshComponent(instance, updateQueue) { instance.lastState = lastState; instance.lastContext = lastContext; //这里会更新instance的props, context, state - var nextRendered = renderComponent(instance, nextVnode, nextProps, nextContext, nextState); - - dom = alignVnode(lastRendered, nextRendered, vparent, getChildContext(instance, parentContext), updateQueue); - - createInstanceChain(instance, nextVnode, nextRendered); - updateInstanceChain(instance, dom); + dom = renderComponent(instance, nextVnode, nextProps, nextContext, nextState, function (nextRendered, childContext) { + return alignVnode(lastRendered, nextRendered, vparent, childContext, updateQueue); + }); instance.__lifeStage = 2; instance.__hydrating = false; @@ -2371,7 +2374,7 @@ function _refreshComponent(instance, updateQueue) { return dom; } -options._refreshComponent = _refreshComponent; +options.refreshComponent = refreshComponent; function alignVnode(lastVnode, nextVnode, vparent, context, updateQueue) { var node = lastVnode._hostNode, @@ -2380,18 +2383,12 @@ function alignVnode(lastVnode, nextVnode, vparent, context, updateQueue) { dom = updateVnode(lastVnode, nextVnode, vparent, context, updateQueue); } else { disposeVnode(lastVnode); - // let innerUpdateQueue = updateQueue.isChildProcess - // ? updateQueue - // : nextVnode.vtype === 2 ? [] : updateQueue; dom = mountVnode(null, nextVnode, vparent, context, updateQueue); var p = node.parentNode; if (p) { p.replaceChild(dom, node); removeDOMElement(node); } - // if (innerUpdateQueue !== updateQueue) { - // clearScheduler(innerUpdateQueue); - // } } return dom; } @@ -2456,9 +2453,7 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { hit = void 0, dom = void 0, oldDom = void 0, - - // hasExecutor = updateQueue.executor, - nextChild = void 0, + nextChild = void 0, lastChild = void 0; //第一次循环,构建移动指令(actions)与移除名单(removeHits)与命中名单(fuzzyHits) if (nextLength) { @@ -2545,9 +2540,6 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { disposeVnode(el); } }); - // if (!hasExecutor && updateQueue.executor) { - // clearScheduler(updateQueue); - // } } function isSameNode(a, b) { diff --git a/dist/ReactSelection.js b/dist/ReactSelection.js index 7fa4f57ee..bb100ee6e 100644 --- a/dist/ReactSelection.js +++ b/dist/ReactSelection.js @@ -222,7 +222,7 @@ function createElement(type, config) { key = null, ref = null, argsLen = children.length; - if (isFn(type)) { + if (type && type.call) { vtype = type.prototype && type.prototype.render ? 2 : 4; } else if (type + "" !== type) { console.error("createElement第一个参数类型错误"); // eslint-disable-line @@ -419,7 +419,7 @@ var FAKE_SYMBOL = "@@iterator"; function getIteractor(a) { if (typeNumber(a) > 7) { var iteratorFn = REAL_SYMBOL && a[REAL_SYMBOL] || a[FAKE_SYMBOL]; - if (isFn(iteratorFn)) { + if (iteratorFn && iteratorFn.call) { return iteratorFn; } } @@ -1873,6 +1873,49 @@ function getOptionSelected(option, selected) { dom.selected = selected; } +function alwaysNull() { + return null; +} +function instantiateComponent(type, vtype, props, context) { + var instance; + if (vtype === 2) { + instance = new type(props, context); + //防止用户没有调用super或没有传够参数 + instance.props = instance.props || props; + instance.context = instance.context || context; + } else { + instance = { + refs: {}, + render: function render() { + return type(this.props, this.context); + }, + __isStateless: 1, + state: null, + props: props, + context: context, + __pendingCallbacks: [], + __current: noop, + __mergeStates: alwaysNull + }; + var lastOwn = CurrentOwner.cur; + CurrentOwner.cur = instance; + try { + var mixin = type(props, context); + } finally { + CurrentOwner.cur = lastOwn; + } + if (mixin && mixin.render) { + //支持module pattern component + delete instance.__isStateless; + Object.assign(instance, mixin); + } else { + instance.__rendered = mixin; + } + } + + return instance; +} + var pendingRefs = []; function clearRefs() { var refs = pendingRefs.slice(0); @@ -1927,7 +1970,7 @@ function drainQueue(queue) { } instance.__hydrating = false; //子树已经构建完毕 while (instance.__renderInNextCycle) { - options._refreshComponent(instance, queue); + options.refreshComponent(instance, queue); callUpdate(instance); } } @@ -2184,45 +2227,7 @@ function alignChildren(parentNode, vparent, context, updateQueue) { parentNode.removeChild(childNodes[n]); } } -function alwaysNull() { - return null; -} -function instantiateComponent(type, vtype, props, context) { - var instance; - var lastOwn = CurrentOwner.cur; - if (vtype === 2) { - instance = new type(props, context); - //防止用户没有调用super或没有传够参数 - instance.props = instance.props || props; - instance.context = instance.context || context; - } else { - instance = { - refs: {}, - render: function render() { - return type(this.props, this.context); - }, - __isStateless: 1, - state: null, - props: props, - context: context, - constructor: type, - __pendingCallbacks: [], - __current: noop, - __mergeStates: alwaysNull - }; - CurrentOwner.cur = instance; - var mixin = type(props, context); - if (mixin && isFn(mixin.render)) { - //支持module pattern component - delete instance.__isStateless; - Object.assign(instance, mixin); - } else { - instance.__rendered = mixin; - } - } - CurrentOwner.cur = lastOwn; - return instance; -} + function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { var type = vnode.type, vtype = vnode.vtype, @@ -2247,24 +2252,20 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { } instance.__hydrating = true; - var rendered = renderComponent(instance, vnode, props, context, state, instance.__rendered); - - var childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; + var dom = renderComponent(instance, vnode, props, context, state, function (nextRendered, childContext) { + return mountVnode(lastNode, nextRendered, vparent, childContext, updateQueue); + }, instance.__rendered); - var dom = mountVnode(lastNode, rendered, vparent, childContext, updateQueue); updateQueue.push(instance); - createInstanceChain(instance, vnode, rendered); - updateInstanceChain(instance, dom); return dom; } -function renderComponent(instance, vnode, props, context, state, rendered) { - // 同时给有状态与无状态组件使用 - instance.props = props; +function renderComponent(instance, vnode, props, context, state, cb, rendered) { + //更新新属性 instance.state = state; + instance.props = props; instance.context = context; - //调整全局的 CurrentOwner.cur if (!rendered) { try { @@ -2285,7 +2286,17 @@ function renderComponent(instance, vnode, props, context, state, rendered) { } vnode._instance = instance; - return instance.__rendered = rendered; + instance.__rendered = rendered; + + var parentContext = vnode.parentContext; + var childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; + + var dom = cb(rendered, childContext); + + createInstanceChain(instance, vnode, rendered); + updateInstanceChain(instance, dom); + + return dom; } function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { @@ -2302,9 +2313,6 @@ function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { instance.componentWillReceiveProps(nextProps, nextContext); instance.__receiving = false; } - - // shouldComponentUpdate为false时不能阻止setState/forceUpdate cb的触发 - //用于refreshComponent instance.nextVnode = nextVnode; nextVnode.context = nextContext; @@ -2317,14 +2325,14 @@ function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { queue = []; queue.isChildProcess = true; } - _refreshComponent(instance, queue); + refreshComponent(instance, queue); //子组件先执行 updateQueue.push(instance); return instance.__dom; } -function _refreshComponent(instance, updateQueue) { +function refreshComponent(instance, updateQueue) { var lastProps = instance.props, lastState = instance.state, lastContext = instance.context, @@ -2334,8 +2342,6 @@ function _refreshComponent(instance, updateQueue) { instance.__renderInNextCycle = null; var nextVnode = instance.nextVnode; var nextContext = nextVnode.context; - - var parentContext = nextVnode.parentContext; var nextProps = nextVnode.props; var vparent = nextVnode.vparent; @@ -2356,12 +2362,9 @@ function _refreshComponent(instance, updateQueue) { instance.lastState = lastState; instance.lastContext = lastContext; //这里会更新instance的props, context, state - var nextRendered = renderComponent(instance, nextVnode, nextProps, nextContext, nextState); - - dom = alignVnode(lastRendered, nextRendered, vparent, getChildContext(instance, parentContext), updateQueue); - - createInstanceChain(instance, nextVnode, nextRendered); - updateInstanceChain(instance, dom); + dom = renderComponent(instance, nextVnode, nextProps, nextContext, nextState, function (nextRendered, childContext) { + return alignVnode(lastRendered, nextRendered, vparent, childContext, updateQueue); + }); instance.__lifeStage = 2; instance.__hydrating = false; @@ -2371,7 +2374,7 @@ function _refreshComponent(instance, updateQueue) { return dom; } -options._refreshComponent = _refreshComponent; +options.refreshComponent = refreshComponent; function alignVnode(lastVnode, nextVnode, vparent, context, updateQueue) { var node = lastVnode._hostNode, @@ -2380,18 +2383,12 @@ function alignVnode(lastVnode, nextVnode, vparent, context, updateQueue) { dom = updateVnode(lastVnode, nextVnode, vparent, context, updateQueue); } else { disposeVnode(lastVnode); - // let innerUpdateQueue = updateQueue.isChildProcess - // ? updateQueue - // : nextVnode.vtype === 2 ? [] : updateQueue; dom = mountVnode(null, nextVnode, vparent, context, updateQueue); var p = node.parentNode; if (p) { p.replaceChild(dom, node); removeDOMElement(node); } - // if (innerUpdateQueue !== updateQueue) { - // clearScheduler(innerUpdateQueue); - // } } return dom; } @@ -2456,9 +2453,7 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { hit = void 0, dom = void 0, oldDom = void 0, - - // hasExecutor = updateQueue.executor, - nextChild = void 0, + nextChild = void 0, lastChild = void 0; //第一次循环,构建移动指令(actions)与移除名单(removeHits)与命中名单(fuzzyHits) if (nextLength) { @@ -2545,9 +2540,6 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { disposeVnode(el); } }); - // if (!hasExecutor && updateQueue.executor) { - // clearScheduler(updateQueue); - // } } function isSameNode(a, b) { diff --git a/dist/ReactShim.js b/dist/ReactShim.js index 591cd2f74..5f15238ba 100644 --- a/dist/ReactShim.js +++ b/dist/ReactShim.js @@ -224,7 +224,7 @@ function createElement(type, config) { key = null, ref = null, argsLen = children.length; - if (isFn(type)) { + if (type && type.call) { vtype = type.prototype && type.prototype.render ? 2 : 4; } else if (type + "" !== type) { console.error("createElement第一个参数类型错误"); // eslint-disable-line @@ -421,7 +421,7 @@ var FAKE_SYMBOL = "@@iterator"; function getIteractor(a) { if (typeNumber(a) > 7) { var iteratorFn = REAL_SYMBOL && a[REAL_SYMBOL] || a[FAKE_SYMBOL]; - if (isFn(iteratorFn)) { + if (iteratorFn && iteratorFn.call) { return iteratorFn; } } @@ -1719,6 +1719,49 @@ function getOptionSelected(option, selected) { dom.selected = selected; } +function alwaysNull() { + return null; +} +function instantiateComponent(type, vtype, props, context) { + var instance; + if (vtype === 2) { + instance = new type(props, context); + //防止用户没有调用super或没有传够参数 + instance.props = instance.props || props; + instance.context = instance.context || context; + } else { + instance = { + refs: {}, + render: function render() { + return type(this.props, this.context); + }, + __isStateless: 1, + state: null, + props: props, + context: context, + __pendingCallbacks: [], + __current: noop, + __mergeStates: alwaysNull + }; + var lastOwn = CurrentOwner.cur; + CurrentOwner.cur = instance; + try { + var mixin = type(props, context); + } finally { + CurrentOwner.cur = lastOwn; + } + if (mixin && mixin.render) { + //支持module pattern component + delete instance.__isStateless; + Object.assign(instance, mixin); + } else { + instance.__rendered = mixin; + } + } + + return instance; +} + var pendingRefs = []; function clearRefs() { var refs = pendingRefs.slice(0); @@ -1773,7 +1816,7 @@ function drainQueue(queue) { } instance.__hydrating = false; //子树已经构建完毕 while (instance.__renderInNextCycle) { - options._refreshComponent(instance, queue); + options.refreshComponent(instance, queue); callUpdate(instance); } } @@ -2026,45 +2069,7 @@ function alignChildren(parentNode, vparent, context, updateQueue) { parentNode.removeChild(childNodes[n]); } } -function alwaysNull() { - return null; -} -function instantiateComponent(type, vtype, props, context) { - var instance; - var lastOwn = CurrentOwner.cur; - if (vtype === 2) { - instance = new type(props, context); - //防止用户没有调用super或没有传够参数 - instance.props = instance.props || props; - instance.context = instance.context || context; - } else { - instance = { - refs: {}, - render: function render() { - return type(this.props, this.context); - }, - __isStateless: 1, - state: null, - props: props, - context: context, - constructor: type, - __pendingCallbacks: [], - __current: noop, - __mergeStates: alwaysNull - }; - CurrentOwner.cur = instance; - var mixin = type(props, context); - if (mixin && isFn(mixin.render)) { - //支持module pattern component - delete instance.__isStateless; - Object.assign(instance, mixin); - } else { - instance.__rendered = mixin; - } - } - CurrentOwner.cur = lastOwn; - return instance; -} + function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { var type = vnode.type, vtype = vnode.vtype, @@ -2089,24 +2094,20 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { } instance.__hydrating = true; - var rendered = renderComponent(instance, vnode, props, context, state, instance.__rendered); - - var childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; + var dom = renderComponent(instance, vnode, props, context, state, function (nextRendered, childContext) { + return mountVnode(lastNode, nextRendered, vparent, childContext, updateQueue); + }, instance.__rendered); - var dom = mountVnode(lastNode, rendered, vparent, childContext, updateQueue); updateQueue.push(instance); - createInstanceChain(instance, vnode, rendered); - updateInstanceChain(instance, dom); return dom; } -function renderComponent(instance, vnode, props, context, state, rendered) { - // 同时给有状态与无状态组件使用 - instance.props = props; +function renderComponent(instance, vnode, props, context, state, cb, rendered) { + //更新新属性 instance.state = state; + instance.props = props; instance.context = context; - //调整全局的 CurrentOwner.cur if (!rendered) { try { @@ -2127,7 +2128,17 @@ function renderComponent(instance, vnode, props, context, state, rendered) { } vnode._instance = instance; - return instance.__rendered = rendered; + instance.__rendered = rendered; + + var parentContext = vnode.parentContext; + var childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; + + var dom = cb(rendered, childContext); + + createInstanceChain(instance, vnode, rendered); + updateInstanceChain(instance, dom); + + return dom; } function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { @@ -2144,9 +2155,6 @@ function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { instance.componentWillReceiveProps(nextProps, nextContext); instance.__receiving = false; } - - // shouldComponentUpdate为false时不能阻止setState/forceUpdate cb的触发 - //用于refreshComponent instance.nextVnode = nextVnode; nextVnode.context = nextContext; @@ -2159,14 +2167,14 @@ function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { queue = []; queue.isChildProcess = true; } - _refreshComponent(instance, queue); + refreshComponent(instance, queue); //子组件先执行 updateQueue.push(instance); return instance.__dom; } -function _refreshComponent(instance, updateQueue) { +function refreshComponent(instance, updateQueue) { var lastProps = instance.props, lastState = instance.state, lastContext = instance.context, @@ -2176,8 +2184,6 @@ function _refreshComponent(instance, updateQueue) { instance.__renderInNextCycle = null; var nextVnode = instance.nextVnode; var nextContext = nextVnode.context; - - var parentContext = nextVnode.parentContext; var nextProps = nextVnode.props; var vparent = nextVnode.vparent; @@ -2198,12 +2204,9 @@ function _refreshComponent(instance, updateQueue) { instance.lastState = lastState; instance.lastContext = lastContext; //这里会更新instance的props, context, state - var nextRendered = renderComponent(instance, nextVnode, nextProps, nextContext, nextState); - - dom = alignVnode(lastRendered, nextRendered, vparent, getChildContext(instance, parentContext), updateQueue); - - createInstanceChain(instance, nextVnode, nextRendered); - updateInstanceChain(instance, dom); + dom = renderComponent(instance, nextVnode, nextProps, nextContext, nextState, function (nextRendered, childContext) { + return alignVnode(lastRendered, nextRendered, vparent, childContext, updateQueue); + }); instance.__lifeStage = 2; instance.__hydrating = false; @@ -2213,7 +2216,7 @@ function _refreshComponent(instance, updateQueue) { return dom; } -options._refreshComponent = _refreshComponent; +options.refreshComponent = refreshComponent; function alignVnode(lastVnode, nextVnode, vparent, context, updateQueue) { var node = lastVnode._hostNode, @@ -2222,18 +2225,12 @@ function alignVnode(lastVnode, nextVnode, vparent, context, updateQueue) { dom = updateVnode(lastVnode, nextVnode, vparent, context, updateQueue); } else { disposeVnode(lastVnode); - // let innerUpdateQueue = updateQueue.isChildProcess - // ? updateQueue - // : nextVnode.vtype === 2 ? [] : updateQueue; dom = mountVnode(null, nextVnode, vparent, context, updateQueue); var p = node.parentNode; if (p) { p.replaceChild(dom, node); removeDOMElement(node); } - // if (innerUpdateQueue !== updateQueue) { - // clearScheduler(innerUpdateQueue); - // } } return dom; } @@ -2298,9 +2295,7 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { hit = void 0, dom = void 0, oldDom = void 0, - - // hasExecutor = updateQueue.executor, - nextChild = void 0, + nextChild = void 0, lastChild = void 0; //第一次循环,构建移动指令(actions)与移除名单(removeHits)与命中名单(fuzzyHits) if (nextLength) { @@ -2387,9 +2382,6 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { disposeVnode(el); } }); - // if (!hasExecutor && updateQueue.executor) { - // clearScheduler(updateQueue); - // } } function isSameNode(a, b) { diff --git a/src/createElement.js b/src/createElement.js index 8292eee6a..e33d4bf19 100644 --- a/src/createElement.js +++ b/src/createElement.js @@ -1,4 +1,4 @@ -import {EMPTY_CHILDREN, typeNumber, isFn} from "./util"; +import {EMPTY_CHILDREN, typeNumber} from "./util"; export var CurrentOwner = { cur: null @@ -19,7 +19,7 @@ export function createElement(type, config, ...children) { key = null, ref = null, argsLen = children.length; - if (isFn(type)) { + if (type && type.call) { vtype = type.prototype && type.prototype.render ? 2 : 4; @@ -228,7 +228,7 @@ var FAKE_SYMBOL = "@@iterator"; function getIteractor(a) { if (typeNumber(a) > 7) { var iteratorFn = (REAL_SYMBOL && a[REAL_SYMBOL]) || a[FAKE_SYMBOL]; - if (isFn(iteratorFn)) { + if (iteratorFn && iteratorFn.call) { return iteratorFn; } } diff --git a/src/diff.js b/src/diff.js index 9165cd0b3..7c0062994 100644 --- a/src/diff.js +++ b/src/diff.js @@ -1,6 +1,4 @@ import { - isFn, - noop, options, getNodes, innerHTML, @@ -17,6 +15,7 @@ import { processFormElement, postUpdateSelectedOptions } from "./ControlledComponent"; +import { instantiateComponent } from "./instantiateComponent"; import { pendingRefs, drainQueue } from "./scheduler"; //[Top API] React.isValidElement @@ -250,45 +249,7 @@ function alignChildren(parentNode, vparent, context, updateQueue) { parentNode.removeChild(childNodes[n]); } } -function alwaysNull() { - return null; -} -function instantiateComponent(type, vtype, props, context) { - var instance; - let lastOwn = CurrentOwner.cur; - if (vtype === 2) { - instance = new type(props, context); - //防止用户没有调用super或没有传够参数 - instance.props = instance.props || props; - instance.context = instance.context || context; - } else { - instance = { - refs: {}, - render: function() { - return type(this.props, this.context); - }, - __isStateless: 1, - state: null, - props: props, - context: context, - constructor: type, - __pendingCallbacks: [], - __current: noop, - __mergeStates: alwaysNull - }; - CurrentOwner.cur = instance; - var mixin = type(props, context); - if (mixin && isFn(mixin.render)) { - //支持module pattern component - delete instance.__isStateless; - Object.assign(instance, mixin); - } else { - instance.__rendered = mixin; - } - } - CurrentOwner.cur = lastOwn; - return instance; -} + function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { let { type, vtype, props } = vnode; @@ -310,33 +271,34 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { } instance.__hydrating = true; - let rendered = renderComponent( + let dom = renderComponent( instance, vnode, props, context, state, + function(nextRendered, childContext) { + return mountVnode( + lastNode, + nextRendered, + vparent, + childContext, + updateQueue + ); + }, instance.__rendered ); - var childContext = rendered.vtype - ? getChildContext(instance, parentContext) - : parentContext; - - let dom = mountVnode(lastNode, rendered, vparent, childContext, updateQueue); updateQueue.push(instance); - createInstanceChain(instance, vnode, rendered); - updateInstanceChain(instance, dom); return dom; } -function renderComponent(instance, vnode, props, context, state, rendered) { - // 同时给有状态与无状态组件使用 - instance.props = props; +function renderComponent(instance, vnode, props, context, state, cb, rendered) { + //更新新属性 instance.state = state; + instance.props = props; instance.context = context; - //调整全局的 CurrentOwner.cur if (!rendered) { try { @@ -360,7 +322,19 @@ function renderComponent(instance, vnode, props, context, state, rendered) { } vnode._instance = instance; - return (instance.__rendered = rendered); + instance.__rendered = rendered; + + let parentContext = vnode.parentContext; + let childContext = rendered.vtype + ? getChildContext(instance, parentContext) + : parentContext; + + let dom = cb(rendered, childContext); + + createInstanceChain(instance, vnode, rendered); + updateInstanceChain(instance, dom); + + return dom; } function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { @@ -377,9 +351,6 @@ function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { instance.componentWillReceiveProps(nextProps, nextContext); instance.__receiving = false; } - - // shouldComponentUpdate为false时不能阻止setState/forceUpdate cb的触发 - //用于refreshComponent instance.nextVnode = nextVnode; nextVnode.context = nextContext; @@ -392,14 +363,14 @@ function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { queue = []; queue.isChildProcess = true; } - _refreshComponent(instance, queue); + refreshComponent(instance, queue); //子组件先执行 updateQueue.push(instance); return instance.__dom; } -function _refreshComponent(instance, updateQueue) { +function refreshComponent(instance, updateQueue) { let { props: lastProps, state: lastState, @@ -410,8 +381,6 @@ function _refreshComponent(instance, updateQueue) { instance.__renderInNextCycle = null; let nextVnode = instance.nextVnode; let nextContext = nextVnode.context; - - let parentContext = nextVnode.parentContext; let nextProps = nextVnode.props; let vparent = nextVnode.vparent; @@ -436,25 +405,23 @@ function _refreshComponent(instance, updateQueue) { instance.lastState = lastState; instance.lastContext = lastContext; //这里会更新instance的props, context, state - let nextRendered = renderComponent( + dom = renderComponent( instance, nextVnode, nextProps, nextContext, - nextState - ); - - dom = alignVnode( - lastRendered, - nextRendered, - vparent, - getChildContext(instance, parentContext), - updateQueue + nextState, + function(nextRendered, childContext) { + return alignVnode( + lastRendered, + nextRendered, + vparent, + childContext, + updateQueue + ); + } ); - createInstanceChain(instance, nextVnode, nextRendered); - updateInstanceChain(instance, dom); - instance.__lifeStage = 2; instance.__hydrating = false; if (updateQueue.isChildProcess) { @@ -463,7 +430,7 @@ function _refreshComponent(instance, updateQueue) { return dom; } -options._refreshComponent = _refreshComponent; +options.refreshComponent = refreshComponent; export function alignVnode( lastVnode, @@ -478,18 +445,12 @@ export function alignVnode( dom = updateVnode(lastVnode, nextVnode, vparent, context, updateQueue); } else { disposeVnode(lastVnode); - // let innerUpdateQueue = updateQueue.isChildProcess - // ? updateQueue - // : nextVnode.vtype === 2 ? [] : updateQueue; dom = mountVnode(null, nextVnode, vparent, context, updateQueue); let p = node.parentNode; if (p) { p.replaceChild(dom, node); removeDOMElement(node); } - // if (innerUpdateQueue !== updateQueue) { - // clearScheduler(innerUpdateQueue); - // } } return dom; } @@ -560,7 +521,6 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { hit, dom, oldDom, - // hasExecutor = updateQueue.executor, nextChild, lastChild; //第一次循环,构建移动指令(actions)与移除名单(removeHits)与命中名单(fuzzyHits) @@ -654,9 +614,7 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { disposeVnode(el); } }); - // if (!hasExecutor && updateQueue.executor) { - // clearScheduler(updateQueue); - // } + } function isSameNode(a, b) { diff --git a/src/instantiateComponent.js b/src/instantiateComponent.js new file mode 100644 index 000000000..d06508779 --- /dev/null +++ b/src/instantiateComponent.js @@ -0,0 +1,45 @@ +import { noop } from "./util"; + +import { CurrentOwner } from "./createElement"; +function alwaysNull() { + return null; +} +export function instantiateComponent(type, vtype, props, context) { + var instance; + if (vtype === 2) { + instance = new type(props, context); + //防止用户没有调用super或没有传够参数 + instance.props = instance.props || props; + instance.context = instance.context || context; + } else { + instance = { + refs: {}, + render: function() { + return type(this.props, this.context); + }, + __isStateless: 1, + state: null, + props: props, + context: context, + __pendingCallbacks: [], + __current: noop, + __mergeStates: alwaysNull + }; + let lastOwn = CurrentOwner.cur; + CurrentOwner.cur = instance; + try { + var mixin = type(props, context); + } finally { + CurrentOwner.cur = lastOwn; + } + if (mixin && mixin.render) { + //支持module pattern component + delete instance.__isStateless; + Object.assign(instance, mixin); + } else { + instance.__rendered = mixin; + } + } + + return instance; +} diff --git a/src/scheduler.js b/src/scheduler.js index 6426bcc03..958105ff2 100644 --- a/src/scheduler.js +++ b/src/scheduler.js @@ -60,7 +60,7 @@ export function drainQueue(queue) { } instance.__hydrating = false; //子树已经构建完毕 while (instance.__renderInNextCycle) { - options._refreshComponent(instance, queue); + options.refreshComponent(instance, queue); callUpdate(instance); } } diff --git a/version.md b/version.md index 7b832f3a9..87f49e770 100644 --- a/version.md +++ b/version.md @@ -2,7 +2,7 @@ 1. 修正 onChange 事件 2. 重构 diffProps 模块的实现 3. 支持组件的isMounted方法 -4. 添加beforeRender, beforePatch, afterPatch钩子 +4. 添加beforePatch , afterPatch钩子 5. 添加lib/ReactInputSelection.js 6. 统一所有操作虚拟DOM的方法的参数(mountXXX, updateXXX, alignXXX系列) >1 第一个参数为旧真实DOM或旧虚拟DOM @@ -16,6 +16,8 @@ 10. 完善createStringRef方法,应该能抛错与删除无用数据 11. 上线全新的任务调度系统 12. 重构unmountComponentAtNode方法 +13. 添加对两个虚拟DOM的引用都相同的情况下,检测子组件的contextType决定是否更新的策略 +14. 无状态组件支持模块模式(返回一个带生命周期钩子的纯对象,这些方法会像有状态组件那样被调用) ## 1.1.1 1. 简化createClass diff --git "a/\344\270\216\345\256\230\346\226\271React\347\232\204\345\257\271\346\257\224.md" "b/\344\270\216\345\256\230\346\226\271React\347\232\204\345\257\271\346\257\224.md" index 896b12ca7..168687c48 100644 --- "a/\344\270\216\345\256\230\346\226\271React\347\232\204\345\257\271\346\257\224.md" +++ "b/\344\270\216\345\256\230\346\226\271React\347\232\204\345\257\271\346\257\224.md" @@ -67,6 +67,8 @@ | cloneElement后字符串ref的正确处理 | ✔️ | ✔️ | ✖️ | | | getDOMNode | ✔️ | ✔️ | ✖️ | | | componentDidUpdate调用setState不会卡死 | ✔️ | ✔️ | ✖️ | | +| 无状态组件的模块模式组件 | ✔️ | ✔️ | ✖️ | | +| 相同虚拟DOM的context穿透更新策略 | ✔️ | ✔️ | ✖️ | | | style对象 | ✔️ | ✔️ | ✖️ | | | onEvent(尤其是onChange/MouseEnter/Focus的支持) | ✔️ | ✔️ | ✖️ | | | onEventCapture | ✔️ | ✔️ | ✖️ | | From 95dce9a56426235ad2f0adf2a299fe48a64f2a45 Mon Sep 17 00:00:00 2001 From: RubyLouvre <1669866773@qq.com> Date: Fri, 29 Sep 2017 16:22:31 +0800 Subject: [PATCH 13/57] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=20updateByContext=20BU?= =?UTF-8?q?G?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build/shim.script.js | 2 +- dist/React.js | 10 +- dist/ReactIE.js | 10 +- dist/ReactSelection.js | 10 +- dist/ReactShim.js | 10 +- src/diff.js | 11 +- test/modules/ReactComponent-test.jsx | 1 - test/modules/ReactCompositeComponent-test.jsx | 116 +++++++++++++++++- 8 files changed, 155 insertions(+), 15 deletions(-) diff --git a/build/shim.script.js b/build/shim.script.js index 697173931..6f1669f16 100644 --- a/build/shim.script.js +++ b/build/shim.script.js @@ -21,7 +21,7 @@ fs.writeFileSync(dir2, text2, { encoding: "utf8" }); //fs.writeFileSync( path.join(__dirname, "../../draft/node_modules/anujs/dist/React.js"), text2, { encoding: "utf8" }); //fs.writeFileSync( path.join(__dirname, "../../antd-test/node_modules/anujs/dist/React.js"), text2, { encoding: "utf8" }); -fs.writeFileSync( path.join(__dirname, "../../yo-demo/node_modules/anujs/dist/React.js"), text2, { encoding: "utf8" }); +//fs.writeFileSync( path.join(__dirname, "../../yo-demo/node_modules/anujs/dist/React.js"), text2, { encoding: "utf8" }); //fs.writeFileSync( path.join(__dirname, "../../yo-router/node_modules/anujs/dist/React.js"), text2, { encoding: "utf8" }); console.log("对React瘦身完毕"); // eslint-disable-line diff --git a/dist/React.js b/dist/React.js index f924d92ed..77216afa7 100644 --- a/dist/React.js +++ b/dist/React.js @@ -2116,6 +2116,9 @@ function mountVnode(lastNode, vnode) { } function updateByContext(vnode) { + if (vnode.type && vnode.type.contextTypes) { + return true; + } var vchildren = vnode.vchildren; if (vchildren) { for (var i = 0; i < vchildren.length; i++) { @@ -2128,6 +2131,11 @@ function updateByContext(vnode) { return true; } } + } else if (vnode._instance) { + var ret = vnode._instance.__rendered; + if (updateByContext(ret)) { + return true; + } } } @@ -2252,7 +2260,6 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { state = instance.__mergeStates(props, context); } instance.__hydrating = true; - var dom = renderComponent(instance, vnode, props, context, state, function (nextRendered, childContext) { return mountVnode(lastNode, nextRendered, vparent, childContext, updateQueue); }, instance.__rendered); @@ -2291,7 +2298,6 @@ function renderComponent(instance, vnode, props, context, state, cb, rendered) { var parentContext = vnode.parentContext; var childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; - var dom = cb(rendered, childContext); createInstanceChain(instance, vnode, rendered); diff --git a/dist/ReactIE.js b/dist/ReactIE.js index bb100ee6e..6cfe6db95 100644 --- a/dist/ReactIE.js +++ b/dist/ReactIE.js @@ -2115,6 +2115,9 @@ function mountVnode(lastNode, vnode) { } function updateByContext(vnode) { + if (vnode.type && vnode.type.contextTypes) { + return true; + } var vchildren = vnode.vchildren; if (vchildren) { for (var i = 0; i < vchildren.length; i++) { @@ -2127,6 +2130,11 @@ function updateByContext(vnode) { return true; } } + } else if (vnode._instance) { + var ret = vnode._instance.__rendered; + if (updateByContext(ret)) { + return true; + } } } @@ -2251,7 +2259,6 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { state = instance.__mergeStates(props, context); } instance.__hydrating = true; - var dom = renderComponent(instance, vnode, props, context, state, function (nextRendered, childContext) { return mountVnode(lastNode, nextRendered, vparent, childContext, updateQueue); }, instance.__rendered); @@ -2290,7 +2297,6 @@ function renderComponent(instance, vnode, props, context, state, cb, rendered) { var parentContext = vnode.parentContext; var childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; - var dom = cb(rendered, childContext); createInstanceChain(instance, vnode, rendered); diff --git a/dist/ReactSelection.js b/dist/ReactSelection.js index bb100ee6e..6cfe6db95 100644 --- a/dist/ReactSelection.js +++ b/dist/ReactSelection.js @@ -2115,6 +2115,9 @@ function mountVnode(lastNode, vnode) { } function updateByContext(vnode) { + if (vnode.type && vnode.type.contextTypes) { + return true; + } var vchildren = vnode.vchildren; if (vchildren) { for (var i = 0; i < vchildren.length; i++) { @@ -2127,6 +2130,11 @@ function updateByContext(vnode) { return true; } } + } else if (vnode._instance) { + var ret = vnode._instance.__rendered; + if (updateByContext(ret)) { + return true; + } } } @@ -2251,7 +2259,6 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { state = instance.__mergeStates(props, context); } instance.__hydrating = true; - var dom = renderComponent(instance, vnode, props, context, state, function (nextRendered, childContext) { return mountVnode(lastNode, nextRendered, vparent, childContext, updateQueue); }, instance.__rendered); @@ -2290,7 +2297,6 @@ function renderComponent(instance, vnode, props, context, state, cb, rendered) { var parentContext = vnode.parentContext; var childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; - var dom = cb(rendered, childContext); createInstanceChain(instance, vnode, rendered); diff --git a/dist/ReactShim.js b/dist/ReactShim.js index 5f15238ba..1c3183582 100644 --- a/dist/ReactShim.js +++ b/dist/ReactShim.js @@ -1957,6 +1957,9 @@ function mountVnode(lastNode, vnode) { } function updateByContext(vnode) { + if (vnode.type && vnode.type.contextTypes) { + return true; + } var vchildren = vnode.vchildren; if (vchildren) { for (var i = 0; i < vchildren.length; i++) { @@ -1969,6 +1972,11 @@ function updateByContext(vnode) { return true; } } + } else if (vnode._instance) { + var ret = vnode._instance.__rendered; + if (updateByContext(ret)) { + return true; + } } } @@ -2093,7 +2101,6 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { state = instance.__mergeStates(props, context); } instance.__hydrating = true; - var dom = renderComponent(instance, vnode, props, context, state, function (nextRendered, childContext) { return mountVnode(lastNode, nextRendered, vparent, childContext, updateQueue); }, instance.__rendered); @@ -2132,7 +2139,6 @@ function renderComponent(instance, vnode, props, context, state, cb, rendered) { var parentContext = vnode.parentContext; var childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; - var dom = cb(rendered, childContext); createInstanceChain(instance, vnode, rendered); diff --git a/src/diff.js b/src/diff.js index 7c0062994..11802d4cd 100644 --- a/src/diff.js +++ b/src/diff.js @@ -138,6 +138,9 @@ function mountVnode(lastNode, vnode) { } function updateByContext(vnode) { + if (vnode.type && vnode.type.contextTypes) { + return true; + } let vchildren = vnode.vchildren; if (vchildren) { for (let i = 0; i < vchildren.length; i++) { @@ -150,6 +153,11 @@ function updateByContext(vnode) { return true; } } + } else if (vnode._instance) { + var ret = vnode._instance.__rendered; + if (updateByContext(ret)) { + return true; + } } } @@ -270,7 +278,6 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { state = instance.__mergeStates(props, context); } instance.__hydrating = true; - let dom = renderComponent( instance, vnode, @@ -328,7 +335,6 @@ function renderComponent(instance, vnode, props, context, state, cb, rendered) { let childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; - let dom = cb(rendered, childContext); createInstanceChain(instance, vnode, rendered); @@ -614,7 +620,6 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { disposeVnode(el); } }); - } function isSameNode(a, b) { diff --git a/test/modules/ReactComponent-test.jsx b/test/modules/ReactComponent-test.jsx index 5f5caf272..0c4992e89 100644 --- a/test/modules/ReactComponent-test.jsx +++ b/test/modules/ReactComponent-test.jsx @@ -5,7 +5,6 @@ import ReactTestUtils from "lib/ReactTestUtils"; import ReactDOMServer from "dist/ReactDOMServer"; //https://github.com/facebook/react/blob/master/src/isomorphic/children/__tests__/ReactChildren-test.js var ReactDOM = window.ReactDOM || React; - describe("ReactComponent", function() { this.timeout(200000); diff --git a/test/modules/ReactCompositeComponent-test.jsx b/test/modules/ReactCompositeComponent-test.jsx index 4b606e4d2..26e69a50b 100644 --- a/test/modules/ReactCompositeComponent-test.jsx +++ b/test/modules/ReactCompositeComponent-test.jsx @@ -5,6 +5,7 @@ import ReactTestUtils from "lib/ReactTestUtils"; import ReactDOMServer from "dist/ReactDOMServer"; //https://github.com/facebook/react/blob/master/src/isomorphic/children/__tests__/ReactChildren-test.js var ReactDOM = window.ReactDOM || React; +var PropTypes = React.PropTypes describe("ReactCompositeComponent", function() { this.timeout(200000); @@ -366,7 +367,7 @@ describe("ReactCompositeComponent", function() { class Child extends React.Component { static childContextTypes = { - foo: React.PropTypes.string, + foo: PropTypes.string, }; getChildContext() { @@ -382,7 +383,7 @@ describe("ReactCompositeComponent", function() { class Grandchild extends React.Component { static contextTypes = { - foo: React.PropTypes.string, + foo: PropTypes.string, }; render() { @@ -419,4 +420,115 @@ describe("ReactCompositeComponent", function() { expect(childRenders).toBe(1); }); + + it('should pass context when re-rendered for static child', () => { + var parentInstance = null; + var childInstance = null; + + class Parent extends React.Component { + static childContextTypes = { + foo: PropTypes.string, + flag: PropTypes.bool, + }; + + state = { + flag: false, + }; + + getChildContext() { + return { + foo: 'bar', + flag: this.state.flag, + }; + } + + render() { + return React.Children.only(this.props.children); + } + } + + class Middle extends React.Component { + render() { + return this.props.children; + } + } + + class Child extends React.Component { + static contextTypes = { + foo: PropTypes.string, + flag: PropTypes.bool, + }; + + render() { + childInstance = this; + return Child; + } + } + + parentInstance = ReactTestUtils.renderIntoDocument( + , + ); + + expect(parentInstance.state.flag).toBe(false); + expect(childInstance.context).toEqual({foo: 'bar', flag: false}); + + parentInstance.setState({flag: true}); + expect(parentInstance.state.flag).toBe(true); + expect(childInstance.context).toEqual({foo: 'bar', flag: true}); + }); + + it('should pass context when re-rendered for static child within a composite component', () => { + class Parent extends React.Component { + static childContextTypes = { + flag: PropTypes.bool, + }; + + state = { + flag: true, + }; + + getChildContext() { + return { + flag: this.state.flag, + }; + } + + render() { + return
{this.props.children}
; + } + } + + class Child extends React.Component { + static contextTypes = { + flag: PropTypes.bool, + }; + + render() { + return
; + } + } + + class Wrapper extends React.Component { + render() { + return ( + + + + ); + } + } + + var wrapper = ReactTestUtils.renderIntoDocument(); + + expect(wrapper.refs.parent.state.flag).toEqual(true); + expect(wrapper.refs.child.context).toEqual({flag: true}); + + // We update while is still a static prop relative to this update + wrapper.refs.parent.setState({flag: false}); + + expect(wrapper.refs.parent.state.flag).toEqual(false); + expect(wrapper.refs.child.context).toEqual({flag: false}); + }); + + }); \ No newline at end of file From f502cd69c8e5902d1af6c488eeebabae8e90f791 Mon Sep 17 00:00:00 2001 From: RubyLouvre <1669866773@qq.com> Date: Sat, 30 Sep 2017 11:29:58 +0800 Subject: [PATCH 14/57] =?UTF-8?q?=E5=90=8C=E6=AD=A5ref=E5=87=BD=E6=95=B0?= =?UTF-8?q?=EF=BC=8C=E6=94=BE=E5=AE=BDshouldComponentUpdate=E7=9A=84?= =?UTF-8?q?=E9=99=90=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dist/React.js | 62 +-- dist/ReactIE.js | 62 +-- dist/ReactSelection.js | 62 +-- dist/ReactShim.js | 62 +-- index5.html | 127 ++++-- index6.html | 120 +++--- index7.html | 189 ++++---- src/diff.js | 61 +-- test/modules/ReactCompositeComponent-test.jsx | 402 +++++++++++++++++- test/modules/component.spec.jsx | 4 +- version.md | 2 + 11 files changed, 821 insertions(+), 332 deletions(-) diff --git a/dist/React.js b/dist/React.js index 77216afa7..a8f6d15f2 100644 --- a/dist/React.js +++ b/dist/React.js @@ -1,5 +1,5 @@ /** - * by 司徒正美 Copyright 2017-09-29 + * by 司徒正美 Copyright 2017-09-30 * IE9+ */ @@ -2110,7 +2110,7 @@ var patchStrategy = { 12: updateComponent, 14: updateComponent }; - +//mountVnode只是转换虚拟DOM为真实DOM,不做插入DOM树操作 function mountVnode(lastNode, vnode) { return patchStrategy[vnode.vtype].apply(null, arguments); } @@ -2191,9 +2191,9 @@ function mountElement(lastNode, vnode, vparent, context, updateQueue) { var dom = genMountElement(lastNode, vnode, vparent, type); vnode._hostNode = dom; - + var children = flattenChildren(vnode); var method = lastNode ? alignChildren : mountChildren; - method(dom, vnode, context, updateQueue); + method(dom, children, vnode, context, updateQueue); if (vnode.checkProps) { diffProps(props, {}, vnode, {}, dom); @@ -2209,16 +2209,14 @@ function mountElement(lastNode, vnode, vparent, context, updateQueue) { } //将虚拟DOM转换为真实DOM并插入父元素 -function mountChildren(parentNode, vparent, context, updateQueue) { - var children = flattenChildren(vparent); +function mountChildren(parentNode, children, vparent, context, updateQueue) { for (var i = 0, n = children.length; i < n; i++) { parentNode.appendChild(mountVnode(null, children[i], vparent, context, updateQueue)); } } -function alignChildren(parentNode, vparent, context, updateQueue) { - var children = flattenChildren(vparent), - childNodes = parentNode.childNodes, +function alignChildren(parentNode, children, vparent, context, updateQueue) { + var childNodes = parentNode.childNodes, insertPoint = childNodes[0] || null, j = 0, n = children.length; @@ -2307,13 +2305,20 @@ function renderComponent(instance, vnode, props, context, state, cb, rendered) { } function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { - var instance = lastVnode._instance; - var ref = lastVnode.ref; - if (ref && lastVnode.vtype === 2) { - lastVnode.ref(null); + var type = lastVnode.type, + ref = lastVnode.ref, + instance = lastVnode._instance, + vtype = lastVnode.vtype; + + + var nextContext = void 0, + nextProps = nextVnode.props, + queue = void 0; + if (type.contextTypes) { + nextContext = getContextByTypes(context, type.contextTypes); + } else { + nextContext = instance.context; //没有定义contextTypes就沿用旧的 } - var nextContext = getContextByTypes(context, nextVnode.type.contextTypes); - var nextProps = nextVnode.props; if (instance.componentWillReceiveProps) { instance.__receiving = true; @@ -2321,11 +2326,17 @@ function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { instance.__receiving = false; } //用于refreshComponent + if (ref && vtype === 2) { + ref(null); + if (nextVnode.ref) { + //更新ref + lastVnode.ref = nextVnode.ref; + } + } instance.nextVnode = nextVnode; nextVnode.context = nextContext; nextVnode.parentContext = context; nextVnode.vparent = vparent; - var queue; if (updateQueue.isChildProcess) { queue = updateQueue; } else { @@ -2355,7 +2366,7 @@ function refreshComponent(instance, updateQueue) { nextVnode._instance = instance; //important var nextState = instance.__mergeStates(nextProps, nextContext); - if (!instance.__forceUpdate && instance.shouldComponentUpdate && instance.shouldComponentUpdate(nextProps, nextState, nextContext) === false) { + if (!instance.__forceUpdate && instance.shouldComponentUpdate && !instance.shouldComponentUpdate(nextProps, nextState, nextContext)) { instance.__forceUpdate = false; return dom; } @@ -2413,11 +2424,12 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { }); list.length = 0; } else { + var vchildren = flattenChildren(nextVnode); if (lastProps[innerHTML]) { while (dom.firstChild) { dom.removeChild(dom.firstChild); } - mountChildren(dom, nextVnode, context, updateQueue); + mountChildren(dom, vchildren, nextVnode, context, updateQueue); } else { diffChildren(lastVnode, nextVnode, dom, context, updateQueue); } @@ -2439,14 +2451,16 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { var lastChildren = lastVnode.vchildren, nextChildren = flattenChildren(nextVnode), nextLength = nextChildren.length, - lastLength = lastChildren.length; - + lastLength = lastChildren.length, + childNodes = parentNode.childNodes; + lastChildren.forEach(function (el, i) { + if (childNodes[i] !== el._hostNode) { + parentNode.replaceChild(el._hostNode, childNodes[i]); + } + }); //如果旧数组长度为零 if (nextLength && !lastLength) { - return nextChildren.forEach(function (vnode) { - var curNode = mountVnode(null, vnode, lastVnode, context, updateQueue); - parentNode.appendChild(curNode); - }); + return mountChildren(parentNode, nextChildren, lastVnode, context, updateQueue); } if (nextLength === lastLength && lastLength === 1) { return alignVnode(lastChildren[0], nextChildren[0], lastVnode, context, updateQueue); diff --git a/dist/ReactIE.js b/dist/ReactIE.js index 6cfe6db95..54d21b8b9 100644 --- a/dist/ReactIE.js +++ b/dist/ReactIE.js @@ -1,5 +1,5 @@ /** - * IE6+,有问题请加QQ 370262116 by 司徒正美 Copyright 2017-09-29 + * IE6+,有问题请加QQ 370262116 by 司徒正美 Copyright 2017-09-30 */ (function (global, factory) { @@ -2109,7 +2109,7 @@ var patchStrategy = { 12: updateComponent, 14: updateComponent }; - +//mountVnode只是转换虚拟DOM为真实DOM,不做插入DOM树操作 function mountVnode(lastNode, vnode) { return patchStrategy[vnode.vtype].apply(null, arguments); } @@ -2190,9 +2190,9 @@ function mountElement(lastNode, vnode, vparent, context, updateQueue) { var dom = genMountElement(lastNode, vnode, vparent, type); vnode._hostNode = dom; - + var children = flattenChildren(vnode); var method = lastNode ? alignChildren : mountChildren; - method(dom, vnode, context, updateQueue); + method(dom, children, vnode, context, updateQueue); if (vnode.checkProps) { diffProps(props, {}, vnode, {}, dom); @@ -2208,16 +2208,14 @@ function mountElement(lastNode, vnode, vparent, context, updateQueue) { } //将虚拟DOM转换为真实DOM并插入父元素 -function mountChildren(parentNode, vparent, context, updateQueue) { - var children = flattenChildren(vparent); +function mountChildren(parentNode, children, vparent, context, updateQueue) { for (var i = 0, n = children.length; i < n; i++) { parentNode.appendChild(mountVnode(null, children[i], vparent, context, updateQueue)); } } -function alignChildren(parentNode, vparent, context, updateQueue) { - var children = flattenChildren(vparent), - childNodes = parentNode.childNodes, +function alignChildren(parentNode, children, vparent, context, updateQueue) { + var childNodes = parentNode.childNodes, insertPoint = childNodes[0] || null, j = 0, n = children.length; @@ -2306,13 +2304,20 @@ function renderComponent(instance, vnode, props, context, state, cb, rendered) { } function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { - var instance = lastVnode._instance; - var ref = lastVnode.ref; - if (ref && lastVnode.vtype === 2) { - lastVnode.ref(null); + var type = lastVnode.type, + ref = lastVnode.ref, + instance = lastVnode._instance, + vtype = lastVnode.vtype; + + + var nextContext = void 0, + nextProps = nextVnode.props, + queue = void 0; + if (type.contextTypes) { + nextContext = getContextByTypes(context, type.contextTypes); + } else { + nextContext = instance.context; //没有定义contextTypes就沿用旧的 } - var nextContext = getContextByTypes(context, nextVnode.type.contextTypes); - var nextProps = nextVnode.props; if (instance.componentWillReceiveProps) { instance.__receiving = true; @@ -2320,11 +2325,17 @@ function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { instance.__receiving = false; } //用于refreshComponent + if (ref && vtype === 2) { + ref(null); + if (nextVnode.ref) { + //更新ref + lastVnode.ref = nextVnode.ref; + } + } instance.nextVnode = nextVnode; nextVnode.context = nextContext; nextVnode.parentContext = context; nextVnode.vparent = vparent; - var queue; if (updateQueue.isChildProcess) { queue = updateQueue; } else { @@ -2354,7 +2365,7 @@ function refreshComponent(instance, updateQueue) { nextVnode._instance = instance; //important var nextState = instance.__mergeStates(nextProps, nextContext); - if (!instance.__forceUpdate && instance.shouldComponentUpdate && instance.shouldComponentUpdate(nextProps, nextState, nextContext) === false) { + if (!instance.__forceUpdate && instance.shouldComponentUpdate && !instance.shouldComponentUpdate(nextProps, nextState, nextContext)) { instance.__forceUpdate = false; return dom; } @@ -2412,11 +2423,12 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { }); list.length = 0; } else { + var vchildren = flattenChildren(nextVnode); if (lastProps[innerHTML]) { while (dom.firstChild) { dom.removeChild(dom.firstChild); } - mountChildren(dom, nextVnode, context, updateQueue); + mountChildren(dom, vchildren, nextVnode, context, updateQueue); } else { diffChildren(lastVnode, nextVnode, dom, context, updateQueue); } @@ -2438,14 +2450,16 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { var lastChildren = lastVnode.vchildren, nextChildren = flattenChildren(nextVnode), nextLength = nextChildren.length, - lastLength = lastChildren.length; - + lastLength = lastChildren.length, + childNodes = parentNode.childNodes; + lastChildren.forEach(function (el, i) { + if (childNodes[i] !== el._hostNode) { + parentNode.replaceChild(el._hostNode, childNodes[i]); + } + }); //如果旧数组长度为零 if (nextLength && !lastLength) { - return nextChildren.forEach(function (vnode) { - var curNode = mountVnode(null, vnode, lastVnode, context, updateQueue); - parentNode.appendChild(curNode); - }); + return mountChildren(parentNode, nextChildren, lastVnode, context, updateQueue); } if (nextLength === lastLength && lastLength === 1) { return alignVnode(lastChildren[0], nextChildren[0], lastVnode, context, updateQueue); diff --git a/dist/ReactSelection.js b/dist/ReactSelection.js index 6cfe6db95..54d21b8b9 100644 --- a/dist/ReactSelection.js +++ b/dist/ReactSelection.js @@ -1,5 +1,5 @@ /** - * IE6+,有问题请加QQ 370262116 by 司徒正美 Copyright 2017-09-29 + * IE6+,有问题请加QQ 370262116 by 司徒正美 Copyright 2017-09-30 */ (function (global, factory) { @@ -2109,7 +2109,7 @@ var patchStrategy = { 12: updateComponent, 14: updateComponent }; - +//mountVnode只是转换虚拟DOM为真实DOM,不做插入DOM树操作 function mountVnode(lastNode, vnode) { return patchStrategy[vnode.vtype].apply(null, arguments); } @@ -2190,9 +2190,9 @@ function mountElement(lastNode, vnode, vparent, context, updateQueue) { var dom = genMountElement(lastNode, vnode, vparent, type); vnode._hostNode = dom; - + var children = flattenChildren(vnode); var method = lastNode ? alignChildren : mountChildren; - method(dom, vnode, context, updateQueue); + method(dom, children, vnode, context, updateQueue); if (vnode.checkProps) { diffProps(props, {}, vnode, {}, dom); @@ -2208,16 +2208,14 @@ function mountElement(lastNode, vnode, vparent, context, updateQueue) { } //将虚拟DOM转换为真实DOM并插入父元素 -function mountChildren(parentNode, vparent, context, updateQueue) { - var children = flattenChildren(vparent); +function mountChildren(parentNode, children, vparent, context, updateQueue) { for (var i = 0, n = children.length; i < n; i++) { parentNode.appendChild(mountVnode(null, children[i], vparent, context, updateQueue)); } } -function alignChildren(parentNode, vparent, context, updateQueue) { - var children = flattenChildren(vparent), - childNodes = parentNode.childNodes, +function alignChildren(parentNode, children, vparent, context, updateQueue) { + var childNodes = parentNode.childNodes, insertPoint = childNodes[0] || null, j = 0, n = children.length; @@ -2306,13 +2304,20 @@ function renderComponent(instance, vnode, props, context, state, cb, rendered) { } function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { - var instance = lastVnode._instance; - var ref = lastVnode.ref; - if (ref && lastVnode.vtype === 2) { - lastVnode.ref(null); + var type = lastVnode.type, + ref = lastVnode.ref, + instance = lastVnode._instance, + vtype = lastVnode.vtype; + + + var nextContext = void 0, + nextProps = nextVnode.props, + queue = void 0; + if (type.contextTypes) { + nextContext = getContextByTypes(context, type.contextTypes); + } else { + nextContext = instance.context; //没有定义contextTypes就沿用旧的 } - var nextContext = getContextByTypes(context, nextVnode.type.contextTypes); - var nextProps = nextVnode.props; if (instance.componentWillReceiveProps) { instance.__receiving = true; @@ -2320,11 +2325,17 @@ function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { instance.__receiving = false; } //用于refreshComponent + if (ref && vtype === 2) { + ref(null); + if (nextVnode.ref) { + //更新ref + lastVnode.ref = nextVnode.ref; + } + } instance.nextVnode = nextVnode; nextVnode.context = nextContext; nextVnode.parentContext = context; nextVnode.vparent = vparent; - var queue; if (updateQueue.isChildProcess) { queue = updateQueue; } else { @@ -2354,7 +2365,7 @@ function refreshComponent(instance, updateQueue) { nextVnode._instance = instance; //important var nextState = instance.__mergeStates(nextProps, nextContext); - if (!instance.__forceUpdate && instance.shouldComponentUpdate && instance.shouldComponentUpdate(nextProps, nextState, nextContext) === false) { + if (!instance.__forceUpdate && instance.shouldComponentUpdate && !instance.shouldComponentUpdate(nextProps, nextState, nextContext)) { instance.__forceUpdate = false; return dom; } @@ -2412,11 +2423,12 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { }); list.length = 0; } else { + var vchildren = flattenChildren(nextVnode); if (lastProps[innerHTML]) { while (dom.firstChild) { dom.removeChild(dom.firstChild); } - mountChildren(dom, nextVnode, context, updateQueue); + mountChildren(dom, vchildren, nextVnode, context, updateQueue); } else { diffChildren(lastVnode, nextVnode, dom, context, updateQueue); } @@ -2438,14 +2450,16 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { var lastChildren = lastVnode.vchildren, nextChildren = flattenChildren(nextVnode), nextLength = nextChildren.length, - lastLength = lastChildren.length; - + lastLength = lastChildren.length, + childNodes = parentNode.childNodes; + lastChildren.forEach(function (el, i) { + if (childNodes[i] !== el._hostNode) { + parentNode.replaceChild(el._hostNode, childNodes[i]); + } + }); //如果旧数组长度为零 if (nextLength && !lastLength) { - return nextChildren.forEach(function (vnode) { - var curNode = mountVnode(null, vnode, lastVnode, context, updateQueue); - parentNode.appendChild(curNode); - }); + return mountChildren(parentNode, nextChildren, lastVnode, context, updateQueue); } if (nextLength === lastLength && lastLength === 1) { return alignVnode(lastChildren[0], nextChildren[0], lastVnode, context, updateQueue); diff --git a/dist/ReactShim.js b/dist/ReactShim.js index 1c3183582..a78704107 100644 --- a/dist/ReactShim.js +++ b/dist/ReactShim.js @@ -1,7 +1,7 @@ /** * 此版本要求浏览器没有createClass, createFactory, PropTypes, isValidElement, * unmountComponentAtNode,unstable_renderSubtreeIntoContainer - * QQ 370262116 by 司徒正美 Copyright 2017-09-29 + * QQ 370262116 by 司徒正美 Copyright 2017-09-30 */ (function (global, factory) { @@ -1951,7 +1951,7 @@ var patchStrategy = { 12: updateComponent, 14: updateComponent }; - +//mountVnode只是转换虚拟DOM为真实DOM,不做插入DOM树操作 function mountVnode(lastNode, vnode) { return patchStrategy[vnode.vtype].apply(null, arguments); } @@ -2032,9 +2032,9 @@ function mountElement(lastNode, vnode, vparent, context, updateQueue) { var dom = genMountElement(lastNode, vnode, vparent, type); vnode._hostNode = dom; - + var children = flattenChildren(vnode); var method = lastNode ? alignChildren : mountChildren; - method(dom, vnode, context, updateQueue); + method(dom, children, vnode, context, updateQueue); if (vnode.checkProps) { diffProps(props, {}, vnode, {}, dom); @@ -2050,16 +2050,14 @@ function mountElement(lastNode, vnode, vparent, context, updateQueue) { } //将虚拟DOM转换为真实DOM并插入父元素 -function mountChildren(parentNode, vparent, context, updateQueue) { - var children = flattenChildren(vparent); +function mountChildren(parentNode, children, vparent, context, updateQueue) { for (var i = 0, n = children.length; i < n; i++) { parentNode.appendChild(mountVnode(null, children[i], vparent, context, updateQueue)); } } -function alignChildren(parentNode, vparent, context, updateQueue) { - var children = flattenChildren(vparent), - childNodes = parentNode.childNodes, +function alignChildren(parentNode, children, vparent, context, updateQueue) { + var childNodes = parentNode.childNodes, insertPoint = childNodes[0] || null, j = 0, n = children.length; @@ -2148,13 +2146,20 @@ function renderComponent(instance, vnode, props, context, state, cb, rendered) { } function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { - var instance = lastVnode._instance; - var ref = lastVnode.ref; - if (ref && lastVnode.vtype === 2) { - lastVnode.ref(null); + var type = lastVnode.type, + ref = lastVnode.ref, + instance = lastVnode._instance, + vtype = lastVnode.vtype; + + + var nextContext = void 0, + nextProps = nextVnode.props, + queue = void 0; + if (type.contextTypes) { + nextContext = getContextByTypes(context, type.contextTypes); + } else { + nextContext = instance.context; //没有定义contextTypes就沿用旧的 } - var nextContext = getContextByTypes(context, nextVnode.type.contextTypes); - var nextProps = nextVnode.props; if (instance.componentWillReceiveProps) { instance.__receiving = true; @@ -2162,11 +2167,17 @@ function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { instance.__receiving = false; } //用于refreshComponent + if (ref && vtype === 2) { + ref(null); + if (nextVnode.ref) { + //更新ref + lastVnode.ref = nextVnode.ref; + } + } instance.nextVnode = nextVnode; nextVnode.context = nextContext; nextVnode.parentContext = context; nextVnode.vparent = vparent; - var queue; if (updateQueue.isChildProcess) { queue = updateQueue; } else { @@ -2196,7 +2207,7 @@ function refreshComponent(instance, updateQueue) { nextVnode._instance = instance; //important var nextState = instance.__mergeStates(nextProps, nextContext); - if (!instance.__forceUpdate && instance.shouldComponentUpdate && instance.shouldComponentUpdate(nextProps, nextState, nextContext) === false) { + if (!instance.__forceUpdate && instance.shouldComponentUpdate && !instance.shouldComponentUpdate(nextProps, nextState, nextContext)) { instance.__forceUpdate = false; return dom; } @@ -2254,11 +2265,12 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { }); list.length = 0; } else { + var vchildren = flattenChildren(nextVnode); if (lastProps[innerHTML]) { while (dom.firstChild) { dom.removeChild(dom.firstChild); } - mountChildren(dom, nextVnode, context, updateQueue); + mountChildren(dom, vchildren, nextVnode, context, updateQueue); } else { diffChildren(lastVnode, nextVnode, dom, context, updateQueue); } @@ -2280,14 +2292,16 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { var lastChildren = lastVnode.vchildren, nextChildren = flattenChildren(nextVnode), nextLength = nextChildren.length, - lastLength = lastChildren.length; - + lastLength = lastChildren.length, + childNodes = parentNode.childNodes; + lastChildren.forEach(function (el, i) { + if (childNodes[i] !== el._hostNode) { + parentNode.replaceChild(el._hostNode, childNodes[i]); + } + }); //如果旧数组长度为零 if (nextLength && !lastLength) { - return nextChildren.forEach(function (vnode) { - var curNode = mountVnode(null, vnode, lastVnode, context, updateQueue); - parentNode.appendChild(curNode); - }); + return mountChildren(parentNode, nextChildren, lastVnode, context, updateQueue); } if (nextLength === lastLength && lastLength === 1) { return alignVnode(lastChildren[0], nextChildren[0], lastVnode, context, updateQueue); diff --git a/index5.html b/index5.html index 21d5de080..d9cc75614 100644 --- a/index5.html +++ b/index5.html @@ -4,61 +4,101 @@ - + - + + - - + + + + @@ -85,5 +84,4 @@ - - + \ No newline at end of file diff --git a/index7.html b/index7.html index 65de080ab..5fd8a2329 100644 --- a/index7.html +++ b/index7.html @@ -4,137 +4,90 @@ - - - + + + + + + - - -
开发者工具
-
     
-    
-
- - +
开发者工具
+
+ + - - \ No newline at end of file + + \ No newline at end of file diff --git a/src/diff.js b/src/diff.js index 11802d4cd..e9d081173 100644 --- a/src/diff.js +++ b/src/diff.js @@ -132,7 +132,7 @@ const patchStrategy = { 12: updateComponent, 14: updateComponent }; - +//mountVnode只是转换虚拟DOM为真实DOM,不做插入DOM树操作 function mountVnode(lastNode, vnode) { return patchStrategy[vnode.vtype].apply(null, arguments); } @@ -210,9 +210,9 @@ function mountElement(lastNode, vnode, vparent, context, updateQueue) { let dom = genMountElement(lastNode, vnode, vparent, type); vnode._hostNode = dom; - + var children = flattenChildren(vnode); let method = lastNode ? alignChildren : mountChildren; - method(dom, vnode, context, updateQueue); + method(dom, children, vnode, context, updateQueue); if (vnode.checkProps) { diffProps(props, {}, vnode, {}, dom); @@ -228,8 +228,7 @@ function mountElement(lastNode, vnode, vparent, context, updateQueue) { } //将虚拟DOM转换为真实DOM并插入父元素 -function mountChildren(parentNode, vparent, context, updateQueue) { - var children = flattenChildren(vparent); +function mountChildren(parentNode, children, vparent, context, updateQueue) { for (let i = 0, n = children.length; i < n; i++) { parentNode.appendChild( mountVnode(null, children[i], vparent, context, updateQueue) @@ -237,9 +236,8 @@ function mountChildren(parentNode, vparent, context, updateQueue) { } } -function alignChildren(parentNode, vparent, context, updateQueue) { - let children = flattenChildren(vparent), - childNodes = parentNode.childNodes, +function alignChildren(parentNode, children, vparent, context, updateQueue) { + let childNodes = parentNode.childNodes, insertPoint = childNodes[0] || null, j = 0, n = children.length; @@ -344,25 +342,31 @@ function renderComponent(instance, vnode, props, context, state, cb, rendered) { } function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { - var instance = lastVnode._instance; - var ref = lastVnode.ref; - if (ref && lastVnode.vtype === 2) { - lastVnode.ref(null); - } - let nextContext = getContextByTypes(context, nextVnode.type.contextTypes); - let nextProps = nextVnode.props; + let { type, ref, _instance: instance, vtype } = lastVnode; + let nextContext, nextProps = nextVnode.props, queue; + if (type.contextTypes) { + nextContext = getContextByTypes(context, type.contextTypes); + } else { + nextContext = instance.context;//没有定义contextTypes就沿用旧的 + } + if (instance.componentWillReceiveProps) { instance.__receiving = true; instance.componentWillReceiveProps(nextProps, nextContext); instance.__receiving = false; } //用于refreshComponent + if (ref && vtype === 2) { + ref(null); + if(nextVnode.ref){//更新ref + lastVnode.ref = nextVnode.ref; + } + } instance.nextVnode = nextVnode; nextVnode.context = nextContext; nextVnode.parentContext = context; nextVnode.vparent = vparent; - var queue; if (updateQueue.isChildProcess) { queue = updateQueue; } else { @@ -396,7 +400,7 @@ function refreshComponent(instance, updateQueue) { if ( !instance.__forceUpdate && instance.shouldComponentUpdate && - instance.shouldComponentUpdate(nextProps, nextState, nextContext) === false + !instance.shouldComponentUpdate(nextProps, nextState, nextContext) ) { instance.__forceUpdate = false; return dom; @@ -474,11 +478,12 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { }); list.length = 0; } else { + var vchildren = flattenChildren(nextVnode); if (lastProps[innerHTML]) { while (dom.firstChild) { dom.removeChild(dom.firstChild); } - mountChildren(dom, nextVnode, context, updateQueue); + mountChildren(dom, vchildren, nextVnode, context, updateQueue); } else { diffChildren(lastVnode, nextVnode, dom, context, updateQueue); } @@ -500,14 +505,22 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { let lastChildren = lastVnode.vchildren, nextChildren = flattenChildren(nextVnode), nextLength = nextChildren.length, - lastLength = lastChildren.length; - + lastLength = lastChildren.length, + childNodes = parentNode.childNodes; + lastChildren.forEach(function(el, i) { + if (childNodes[i] !== el._hostNode) { + parentNode.replaceChild(el._hostNode, childNodes[i]); + } + }); //如果旧数组长度为零 if (nextLength && !lastLength) { - return nextChildren.forEach(function(vnode) { - let curNode = mountVnode(null, vnode, lastVnode, context, updateQueue); - parentNode.appendChild(curNode); - }); + return mountChildren( + parentNode, + nextChildren, + lastVnode, + context, + updateQueue + ); } if (nextLength === lastLength && lastLength === 1) { return alignVnode( diff --git a/test/modules/ReactCompositeComponent-test.jsx b/test/modules/ReactCompositeComponent-test.jsx index 26e69a50b..c9e44102c 100644 --- a/test/modules/ReactCompositeComponent-test.jsx +++ b/test/modules/ReactCompositeComponent-test.jsx @@ -420,7 +420,7 @@ describe("ReactCompositeComponent", function() { expect(childRenders).toBe(1); }); - +//context穿透更新 it('should pass context when re-rendered for static child', () => { var parentInstance = null; var childInstance = null; @@ -476,7 +476,7 @@ describe("ReactCompositeComponent", function() { expect(parentInstance.state.flag).toBe(true); expect(childInstance.context).toEqual({foo: 'bar', flag: true}); }); - +//context穿透更新 it('should pass context when re-rendered for static child within a composite component', () => { class Parent extends React.Component { static childContextTypes = { @@ -531,4 +531,402 @@ describe("ReactCompositeComponent", function() { }); + it('should pass context transitively', () => { + var childInstance = null; + var grandchildInstance = null; + + class Parent extends React.Component { + static childContextTypes = { + foo: PropTypes.string, + depth: PropTypes.number, + }; + + getChildContext() { + return { + foo: 'bar', + depth: 0, + }; + } + + render() { + return ; + } + } + + class Child extends React.Component { + static contextTypes = { + foo: PropTypes.string, + depth: PropTypes.number, + }; + + static childContextTypes = { + depth: PropTypes.number, + }; + + getChildContext() { + return { + depth: this.context.depth + 1, + }; + } + + render() { + childInstance = this; + return ; + } + } + + class Grandchild extends React.Component { + static contextTypes = { + foo: PropTypes.string, + depth: PropTypes.number, + }; + + render() { + grandchildInstance = this; + return
; + } + } + + ReactTestUtils.renderIntoDocument(); + expect(childInstance.context).toEqual({foo: 'bar', depth: 0}); + expect(grandchildInstance.context).toEqual({foo: 'bar', depth: 1}); + }); + + it('should pass context when re-rendered', () => { + var parentInstance = null; + var childInstance = null; + + class Parent extends React.Component { + static childContextTypes = { + foo: PropTypes.string, + depth: PropTypes.number, + }; + + state = { + flag: false, + }; + + getChildContext() { + return { + foo: 'bar', + depth: 0, + }; + } + + render() { + var output = ; + if (!this.state.flag) { + output = Child; + } + return output; + } + } + + class Child extends React.Component { + static contextTypes = { + foo: PropTypes.string, + depth: PropTypes.number, + }; + + render() { + childInstance = this; + return Child; + } + } + + parentInstance = ReactTestUtils.renderIntoDocument(); + expect(childInstance).toBeNull(); + + expect(parentInstance.state.flag).toBe(false); + /* + ReactDOM.unstable_batchedUpdates(function() { + parentInstance.setState({flag: true}); + }); + expect(parentInstance.state.flag).toBe(true); + + expect(childInstance.context).toEqual({foo: 'bar', depth: 0}); + */ + }); + + it('unmasked context propagates through updates', () => { + class Leaf extends React.Component { + static contextTypes = { + foo: PropTypes.string.isRequired, + }; + + componentWillReceiveProps(nextProps, nextContext) { + expect('foo' in nextContext).toBe(true); + } + + shouldComponentUpdate(nextProps, nextState, nextContext) { + expect('foo' in nextContext).toBe(true); + return true; + } + + render() { + return {this.context.foo}; + } + } + + class Intermediary extends React.Component { + componentWillReceiveProps(nextProps, nextContext) { + expect('foo' in nextContext).toBe(false); + } + + shouldComponentUpdate(nextProps, nextState, nextContext) { + expect('foo' in nextContext).toBe(false); + return true; + } + + render() { + return ; + } + } + + class Parent extends React.Component { + static childContextTypes = { + foo: PropTypes.string, + }; + + getChildContext() { + return { + foo: this.props.cntxt, + }; + } + + render() { + return ; + } + } + + var div = document.createElement('div'); + ReactDOM.render(, div); + expect(div.children[0].innerHTML).toBe('noise'); + div.children[0].innerHTML = 'aliens'; + div.children[0].id = 'aliens'; + expect(div.children[0].innerHTML).toBe('aliens'); + expect(div.children[0].id).toBe('aliens'); + ReactDOM.render(, div); + expect(div.children[0].innerHTML).toBe('bar'); + expect(div.children[0].id).toBe('aliens'); + }); + + it('should trigger componentWillReceiveProps for context changes', () => { + var contextChanges = 0; + var propChanges = 0; + + class GrandChild extends React.Component { + static contextTypes = { + foo: PropTypes.string.isRequired, + }; + + componentWillReceiveProps(nextProps, nextContext) { + expect('foo' in nextContext).toBe(true); + + if (nextProps !== this.props) { + propChanges++; + } + + if (nextContext !== this.context) { + contextChanges++; + } + } + + render() { + return {this.props.children}; + } + } + + class ChildWithContext extends React.Component { + static contextTypes = { + foo: PropTypes.string.isRequired, + }; + + componentWillReceiveProps(nextProps, nextContext) { + expect('foo' in nextContext).toBe(true); + + if (nextProps !== this.props) { + propChanges++; + } + + if (nextContext !== this.context) { + contextChanges++; + } + } + + render() { + return
{this.props.children}
; + } + } + + class ChildWithoutContext extends React.Component { + componentWillReceiveProps(nextProps, nextContext) { + expect('foo' in nextContext).toBe(false); + + if (nextProps !== this.props) { + propChanges++; + } + + if (nextContext !== this.context) { + contextChanges++; + } + } + + render() { + return
{this.props.children}
; + } + } + + class Parent extends React.Component { + static childContextTypes = { + foo: PropTypes.string, + }; + + state = { + foo: 'abc', + }; + + getChildContext() { + return { + foo: this.state.foo, + }; + } + + render() { + return
{this.props.children}
; + } + } + + var div = document.createElement('div'); + + var parentInstance = null; + ReactDOM.render( + (parentInstance = inst)}> + + A1 + A2 + + + + B1 + B2 + + , + div, + ); + + parentInstance.setState({ + foo: 'def', + }); + + expect(propChanges).toBe(0); + expect(contextChanges).toBe(3); // ChildWithContext, GrandChild x 2 + }); + + + it('only renders once if updated in componentWillReceiveProps', () => { + var renders = 0; + + class Component extends React.Component { + state = {updated: false}; + + componentWillReceiveProps(props) { + expect(props.update).toBe(1); + expect(renders).toBe(1); + this.setState({updated: true}); + expect(renders).toBe(1); + } + + render() { + renders++; + return
; + } + } + + var container = document.createElement('div'); + var instance = ReactDOM.render(, container); + expect(renders).toBe(1); + expect(instance.state.updated).toBe(false); + ReactDOM.render(, container); + expect(renders).toBe(2); + expect(instance.state.updated).toBe(true); + }); + + it('only renders once if updated in componentWillReceiveProps when batching', () => { + var renders = 0; + + class Component extends React.Component { + state = {updated: false}; + + componentWillReceiveProps(props) { + expect(props.update).toBe(1); + expect(renders).toBe(1); + this.setState({updated: true}); + expect(renders).toBe(1); + } + + render() { + renders++; + return
; + } + } + + var container = document.createElement('div'); + var instance = ReactDOM.render(, container); + expect(renders).toBe(1); + expect(instance.state.updated).toBe(false); + /* + ReactDOM.unstable_batchedUpdates(() => { + ReactDOM.render(, container); + }); + expect(renders).toBe(2); + expect(instance.state.updated).toBe(true); + */ + }); + + it('should update refs if shouldComponentUpdate gives false', () => { + class Static extends React.Component { + shouldComponentUpdate() { + return false; + } + + render() { + return
{this.props.children}
; + } + } + + class Component extends React.Component { + render() { + if (this.props.flipped) { + return ( +
+ B (ignored) + A (ignored) +
+ ); + } else { + return ( +
+ A + B +
+ ); + } + } + } + + var container = document.createElement('div'); + var comp = ReactDOM.render(, container); + //keyA <> instance0 <> static0 <> contentA + //keyB <> instance1 <> static1 <> contentB + expect(ReactDOM.findDOMNode(comp.refs.static0).textContent).toBe('A'); + expect(ReactDOM.findDOMNode(comp.refs.static1).textContent).toBe('B'); + //keyA <> instance0 <> static1 <> contentA + //keyB <> instance1 <> static1 <> contentB + // When flipping the order, the refs should update even though the actual + // contents do not + ReactDOM.render(, container); + expect(ReactDOM.findDOMNode(comp.refs.static0).textContent).toBe('B'); + expect(ReactDOM.findDOMNode(comp.refs.static1).textContent).toBe('A'); + }); }); \ No newline at end of file diff --git a/test/modules/component.spec.jsx b/test/modules/component.spec.jsx index 1bca0557d..5c62dac3d 100644 --- a/test/modules/component.spec.jsx +++ b/test/modules/component.spec.jsx @@ -47,7 +47,7 @@ describe("组件相关", function() { }; } shouldComponentUpdate() { - // console.log('shouldComponentUpdate') + //这里相当于返回false } click() { this.setState( @@ -77,7 +77,7 @@ describe("组件相关", function() { await browser.pause(200).$apply(); expect(s.__current._hostNode.innerHTML).toBe("1"); await browser.click(s.__current._hostNode).pause(200).$apply(); - expect(s.__current._hostNode.innerHTML).toBe("3"); + expect(s.__current._hostNode.innerHTML).toBe("1"); expect(a).toBe(3); }); diff --git a/version.md b/version.md index 87f49e770..9d4393eee 100644 --- a/version.md +++ b/version.md @@ -18,6 +18,8 @@ 12. 重构unmountComponentAtNode方法 13. 添加对两个虚拟DOM的引用都相同的情况下,检测子组件的contextType决定是否更新的策略 14. 无状态组件支持模块模式(返回一个带生命周期钩子的纯对象,这些方法会像有状态组件那样被调用) +15. 放松shouldComponentUpdate的限制,返回任何假值都阻止子孙更新 +16. 修正ref的更新方式 ## 1.1.1 1. 简化createClass From 51fb606f5021d9ab62cd22b48a81d505fa658943 Mon Sep 17 00:00:00 2001 From: RubyLouvre <1669866773@qq.com> Date: Sat, 30 Sep 2017 14:53:58 +0800 Subject: [PATCH 15/57] =?UTF-8?q?props,=20context=E6=98=AF=E4=B8=8D?= =?UTF-8?q?=E5=8F=AF=E5=8F=98=E7=9A=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dist/React.js | 6 +- dist/ReactIE.js | 6 +- dist/ReactSelection.js | 6 +- dist/ReactShim.js | 6 +- lib/shallowCompare.js | 3 + src/instantiateComponent.js | 6 +- src/scheduler.js | 2 +- src/shallowEqual.js | 2 +- test/modules/ReactCompositeComponent-test.jsx | 310 ++++++++++++++++++ 9 files changed, 330 insertions(+), 17 deletions(-) diff --git a/dist/React.js b/dist/React.js index a8f6d15f2..d5b5a98cc 100644 --- a/dist/React.js +++ b/dist/React.js @@ -1881,9 +1881,9 @@ function instantiateComponent(type, vtype, props, context) { var instance; if (vtype === 2) { instance = new type(props, context); - //防止用户没有调用super或没有传够参数 - instance.props = instance.props || props; - instance.context = instance.context || context; + //props, context是不可变的 + instance.props = props; + instance.context = context; } else { instance = { refs: {}, diff --git a/dist/ReactIE.js b/dist/ReactIE.js index 54d21b8b9..31d91b0ca 100644 --- a/dist/ReactIE.js +++ b/dist/ReactIE.js @@ -1880,9 +1880,9 @@ function instantiateComponent(type, vtype, props, context) { var instance; if (vtype === 2) { instance = new type(props, context); - //防止用户没有调用super或没有传够参数 - instance.props = instance.props || props; - instance.context = instance.context || context; + //props, context是不可变的 + instance.props = props; + instance.context = context; } else { instance = { refs: {}, diff --git a/dist/ReactSelection.js b/dist/ReactSelection.js index 54d21b8b9..31d91b0ca 100644 --- a/dist/ReactSelection.js +++ b/dist/ReactSelection.js @@ -1880,9 +1880,9 @@ function instantiateComponent(type, vtype, props, context) { var instance; if (vtype === 2) { instance = new type(props, context); - //防止用户没有调用super或没有传够参数 - instance.props = instance.props || props; - instance.context = instance.context || context; + //props, context是不可变的 + instance.props = props; + instance.context = context; } else { instance = { refs: {}, diff --git a/dist/ReactShim.js b/dist/ReactShim.js index a78704107..07c2cd277 100644 --- a/dist/ReactShim.js +++ b/dist/ReactShim.js @@ -1726,9 +1726,9 @@ function instantiateComponent(type, vtype, props, context) { var instance; if (vtype === 2) { instance = new type(props, context); - //防止用户没有调用super或没有传够参数 - instance.props = instance.props || props; - instance.context = instance.context || context; + //props, context是不可变的 + instance.props = props; + instance.context = context; } else { instance = { refs: {}, diff --git a/lib/shallowCompare.js b/lib/shallowCompare.js index 8aed1f994..96ac03a4b 100644 --- a/lib/shallowCompare.js +++ b/lib/shallowCompare.js @@ -1,6 +1,9 @@ //by 司徒正美 let shallowEqual = require("../src/shallowEqual"); +if(typeof shallowEqual !== "function"){ + shallowEqual = shallowEqual.shallowEqual; +} function shallowCompare(instance, nextProps, nextState) { return !shallowEqual(instance.props, nextProps) || !shallowEqual(instance.state, nextState); diff --git a/src/instantiateComponent.js b/src/instantiateComponent.js index d06508779..f2e6b9042 100644 --- a/src/instantiateComponent.js +++ b/src/instantiateComponent.js @@ -8,9 +8,9 @@ export function instantiateComponent(type, vtype, props, context) { var instance; if (vtype === 2) { instance = new type(props, context); - //防止用户没有调用super或没有传够参数 - instance.props = instance.props || props; - instance.context = instance.context || context; + //props, context是不可变的 + instance.props = props; + instance.context = context; } else { instance = { refs: {}, diff --git a/src/scheduler.js b/src/scheduler.js index 958105ff2..b53724497 100644 --- a/src/scheduler.js +++ b/src/scheduler.js @@ -1,6 +1,6 @@ import { options, - clearArray, + clearArray } from "./util"; export const pendingRefs = []; diff --git a/src/shallowEqual.js b/src/shallowEqual.js index 7ae0faa77..9375e9aa5 100644 --- a/src/shallowEqual.js +++ b/src/shallowEqual.js @@ -25,4 +25,4 @@ export function shallowEqual(objA, objB) { } return true; -} +} \ No newline at end of file diff --git a/test/modules/ReactCompositeComponent-test.jsx b/test/modules/ReactCompositeComponent-test.jsx index c9e44102c..45e74db5c 100644 --- a/test/modules/ReactCompositeComponent-test.jsx +++ b/test/modules/ReactCompositeComponent-test.jsx @@ -3,6 +3,11 @@ import getTestDocument from "./getTestDocument"; import ReactTestUtils from "lib/ReactTestUtils"; import ReactDOMServer from "dist/ReactDOMServer"; +var shallowCompare = require( "../../lib/shallowCompare"); +//shallowCompare = shallowCompare.shallowCompare + + + //https://github.com/facebook/react/blob/master/src/isomorphic/children/__tests__/ReactChildren-test.js var ReactDOM = window.ReactDOM || React; var PropTypes = React.PropTypes @@ -929,4 +934,309 @@ describe("ReactCompositeComponent", function() { expect(ReactDOM.findDOMNode(comp.refs.static0).textContent).toBe('B'); expect(ReactDOM.findDOMNode(comp.refs.static1).textContent).toBe('A'); }); + + it('should allow access to findDOMNode in componentWillUnmount', () => { + var a = null; + var b = null; + + class Component extends React.Component { + componentDidMount() { + a = ReactDOM.findDOMNode(this); + expect(a).not.toBe(null); + } + + componentWillUnmount() { + b = ReactDOM.findDOMNode(this); + expect(b).not.toBe(null); + } + + render() { + return
; + } + } + + var container = document.createElement('div'); + expect(a).toBe(container.firstChild); + ReactDOM.render(, container); + ReactDOM.unmountComponentAtNode(container); + expect(a).toBe(b); + }); + + it('context should be passed down from the parent', () => { + class Parent extends React.Component { + static childContextTypes = { + foo: PropTypes.string, + }; + + getChildContext() { + return { + foo: 'bar', + }; + } + + render() { + return
{this.props.children}
; + } + } + + class Component extends React.Component { + static contextTypes = { + foo: PropTypes.string.isRequired, + }; + + render() { + return
; + } + } + + var div = document.createElement('div'); + ReactDOM.render(, div); + }); + + it('should replace state', () => { + class Moo extends React.Component { + state = {x: 1}; + render() { + return
; + } + } + + var moo = ReactTestUtils.renderIntoDocument(); + // No longer a public API, but we can test that it works internally by + // reaching into the updater. + // moo.updater.enqueueReplaceState(moo, {y: 2}); + // expect('x' in moo.state).toBe(false); + expect(moo.state.y).toBe(void 666); + }); + + it('should support objects with prototypes as state', () => { + var NotActuallyImmutable = function(str) { + this.str = str; + }; + NotActuallyImmutable.prototype.amIImmutable = function() { + return true; + }; + class Moo extends React.Component { + state = new NotActuallyImmutable('first'); + // No longer a public API, but we can test that it works internally by + // reaching into the updater. + _replaceState = function (a){ + this.state = a + this.forceUpdate() + } + render() { + return
; + } + } + + var moo = ReactTestUtils.renderIntoDocument(); + expect(moo.state.str).toBe('first'); + expect(moo.state.amIImmutable()).toBe(true); + + var secondState = new NotActuallyImmutable('second'); + moo._replaceState(secondState); + expect(moo.state.str).toBe('second'); + expect(moo.state.amIImmutable()).toBe(true); + expect(moo.state).toBe(secondState); + + moo.setState({str: 'third'}); + expect(moo.state.str).toBe('third'); + // Here we lose the prototype. + expect(moo.state.amIImmutable).toBe(undefined); + + + + + }); + it('props对象不能在构造器里被重写', () => { + var container = document.createElement('div'); + class Foo extends React.Component { + constructor(props) { + super(props); + this.props = {idx: "xxx"} + } + + render() { + return {this.props.idx}; + } + } + + + + ReactDOM.render(, container); + + expect(container.textContent).toBe("aaa"); + }); + + it('should warn when mutated props are passed', () => { + + var container = document.createElement('div'); + + class Foo extends React.Component { + constructor(props) { + var _props = {idx: props.idx + '!'}; + super(_props); + } + + render() { + return {this.props.idx}; + } + } + + + ReactDOM.render(, container); + + expect(container.textContent).toBe("qwe"); + }); + + it('should only call componentWillUnmount once', () => { + var app; + var count = 0; + + class App extends React.Component { + render() { + if (this.props.stage === 1) { + return ; + } else { + return null; + } + } + } + + class UnunmountableComponent extends React.Component { + componentWillUnmount() { + app.setState({}); + count++; + throw Error('always fails'); + } + + render() { + return
Hello {this.props.name}
; + } + } + + var container = document.createElement('div'); + + var setRef = ref => { + if (ref) { + app = ref; + } + }; + + expect(function() { + ReactDOM.render(, container); + ReactDOM.render(, container); + }).toThrow(); + expect(count).toBe(1); + }); + it('prepares new child before unmounting old', () => { + var log = []; + + class Spy extends React.Component { + componentWillMount() { + log.push(this.props.name + ' componentWillMount'); + } + render() { + log.push(this.props.name + ' render'); + return
; + } + componentDidMount() { + log.push(this.props.name + ' componentDidMount'); + } + componentWillUnmount() { + log.push(this.props.name + ' componentWillUnmount'); + } + } + + class Wrapper extends React.Component { + render() { + return ; + } + } + + var container = document.createElement('div'); + ReactDOM.render(, container); + ReactDOM.render(, container); + + expect(log).toEqual([ + 'A componentWillMount', + 'A render', + 'A componentDidMount', + 'A componentWillUnmount', + 'B componentWillMount', + 'B render', + + 'B componentDidMount' + ]); + }); + + + it('respects a shallow shouldComponentUpdate implementation', () => { + var renderCalls = 0; + class PlasticWrap extends React.Component { + constructor(props, context) { + super(props, context); + this.state = { + color: 'green', + }; + } + + render() { + return ; + } + } + + class Apple extends React.Component { + state = { + cut: false, + slices: 1, + }; + + shouldComponentUpdate(nextProps, nextState) { + return shallowCompare(this, nextProps, nextState); + } + + cut() { + this.setState({ + cut: true, + slices: 10, + }); + } + + eatSlice() { + this.setState({ + slices: this.state.slices - 1, + }); + } + + render() { + renderCalls++; + return
; + } + } + + var container = document.createElement('div'); + var instance = ReactDOM.render(, container); + expect(renderCalls).toBe(1); + + // Do not re-render based on props + instance.setState({color: 'green'}); + expect(renderCalls).toBe(1); + + // Re-render based on props + instance.setState({color: 'red'}); + expect(renderCalls).toBe(2); + + // Re-render base on state + instance.refs.apple.cut(); + expect(renderCalls).toBe(3); + + // No re-render based on state + instance.refs.apple.cut(); + expect(renderCalls).toBe(3); + + // Re-render based on state again + instance.refs.apple.eatSlice(); + expect(renderCalls).toBe(4); + }); }); \ No newline at end of file From 30f519ce852b1cf6af1987715401a6f982cfd827 Mon Sep 17 00:00:00 2001 From: RubyLouvre <1669866773@qq.com> Date: Sat, 30 Sep 2017 15:09:27 +0800 Subject: [PATCH 16/57] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20shallowCompare.js?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/ReactTestUtils.js | 6 ++--- lib/shallowCompare.js | 57 +++++++++++++++++++++++++++++++++++++------ 2 files changed, 52 insertions(+), 11 deletions(-) diff --git a/lib/ReactTestUtils.js b/lib/ReactTestUtils.js index dc4be5a86..66e0fac5a 100644 --- a/lib/ReactTestUtils.js +++ b/lib/ReactTestUtils.js @@ -10,16 +10,16 @@ } })(this, function(React) { function findAllInRenderedTree(inst, test) { - var ret = [] + var ret = []; if (!inst) { return ret; } if(inst.vtype === 0){ - return ret + return ret; }else if(inst.vtype === 1){ var dom = inst._hostNode; if( dom && dom.nodeType === 1 && test(dom)){ - ret.push(dom) + ret.push(dom); } var children = inst.vchildren; for (var i = 0, n = children.length; i < n; i++) { diff --git a/lib/shallowCompare.js b/lib/shallowCompare.js index 96ac03a4b..eea2cb727 100644 --- a/lib/shallowCompare.js +++ b/lib/shallowCompare.js @@ -1,12 +1,53 @@ //by 司徒正美 +(function umd(root, factory) { + if (typeof exports === "object" && typeof module === "object") { + module.exports = factory(); + } else if (typeof define === "function" && define.amd) { + define([], factory); + } else if (typeof exports === "object") { + exports.shallowCompare = factory(); + } else { + root.shallowCompare = factory(); + } +})(this, function() { -let shallowEqual = require("../src/shallowEqual"); -if(typeof shallowEqual !== "function"){ - shallowEqual = shallowEqual.shallowEqual; -} + const hasOwn = Object.prototype.hasOwnProperty; -function shallowCompare(instance, nextProps, nextState) { - return !shallowEqual(instance.props, nextProps) || !shallowEqual(instance.state, nextState); -} + function is(x, y) { + if (x === y) { + return x !== 0 || y !== 0 || 1 / x === 1 / y; + } else { + return x !== x && y !== y; + } + } + //https://github.com/reactjs/react-redux/blob/master/src/utils/shallowEqual.js + function shallowEqual(objA, objB) { + if (is(objA, objB)) { + return true; + } -module.exports = shallowCompare; + if (typeof objA !== "object" || objA === null || + typeof objB !== "object" || objB === null) { + return false; + } + + const keysA = Object.keys(objA); + const keysB = Object.keys(objB); + + if (keysA.length !== keysB.length) { + return false; + } + + for (let i = 0; i < keysA.length; i++) { + if (!hasOwn.call(objB, keysA[i]) || + !is(objA[keysA[i]], objB[keysA[i]])) { + return false; + } + } + + return true; + } + return function shallowCompare(instance, nextProps, nextState) { + return !shallowEqual(instance.props, nextProps) || !shallowEqual(instance.state, nextState); + }; +}); From ab2c89d6234a5b89dfc65d78942e686380d77856 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A5=9D=E9=91=AB=E5=A5=94?= Date: Sat, 30 Sep 2017 16:25:43 +0800 Subject: [PATCH 17/57] add context test --- package.json | 4 +- test/modules/ReactContextValidator-test.js | 334 +++++++++++++++++++++ test/spec.js | 2 + 3 files changed, 339 insertions(+), 1 deletion(-) create mode 100644 test/modules/ReactContextValidator-test.js diff --git a/package.json b/package.json index ac5a031fb..2a2b121ce 100644 --- a/package.json +++ b/package.json @@ -89,5 +89,7 @@ "subjectPatternErrorMsg": "请输入message信息!", "helpMessage": "Commit message 格式错误, \n请查看规范: http://wiki.corp.qunar.com/pages/viewpage.action?pageId=159698767" }, - "dependencies": {} + "dependencies": { + "chai-spies": "^0.7.1" + } } diff --git a/test/modules/ReactContextValidator-test.js b/test/modules/ReactContextValidator-test.js new file mode 100644 index 000000000..178a8b519 --- /dev/null +++ b/test/modules/ReactContextValidator-test.js @@ -0,0 +1,334 @@ +import React from "dist/React"; +import getTestDocument from "./getTestDocument"; +import ReactTestUtils from "lib/ReactTestUtils"; +import PropTypes from 'lib/ReactPropTypes'; +import ReactDOMServer from "dist/ReactDOMServer"; +// const chai = require('chai') +const spy = require('chai-spies') + +chai.use(spy) + + + +// https://github.com/facebook/react/blob/master/src/isomorphic/classic/__tests__/ReactContextValidator-test.js + +var ReactDOM = window.ReactDOM || React; + +describe('ReactContextValidator', () => { + function normalizeCodeLocInfo(str) { + return str && str.replace(/\(at .+?:\d+\)/g, '(at **)'); + } + + // TODO: This behavior creates a runtime dependency on propTypes. We should + // ensure that this is not required for ES6 classes with Flow. + + it('should filter out context not in contextTypes', () => { + class Component extends React.Component { + render() { + return
; + } + } + Component.contextTypes = { + foo: PropTypes.string, + }; + + class ComponentInFooBarContext extends React.Component { + getChildContext() { + return { + foo: 'abc', + bar: 123, + }; + } + + render() { + return ; + } + } + ComponentInFooBarContext.childContextTypes = { + foo: PropTypes.string, + bar: PropTypes.number, + }; + + var instance = ReactTestUtils.renderIntoDocument( + , + ); + expect(instance.refs.child.context).toEqual({foo: 'abc'}); + }); + + it('should pass next context to lifecycles', () => { + var actualComponentWillReceiveProps; + var actualShouldComponentUpdate; + var actualComponentWillUpdate; + + class Parent extends React.Component { + getChildContext() { + return { + foo: this.props.foo, + bar: 'bar', + }; + } + + render() { + return ; + } + } + Parent.childContextTypes = { + foo: PropTypes.string.isRequired, + bar: PropTypes.string.isRequired, + }; + + class Component extends React.Component { + componentWillReceiveProps(nextProps, nextContext) { + actualComponentWillReceiveProps = nextContext; + return true; + } + + shouldComponentUpdate(nextProps, nextState, nextContext) { + actualShouldComponentUpdate = nextContext; + return true; + } + + componentWillUpdate(nextProps, nextState, nextContext) { + actualComponentWillUpdate = nextContext; + } + + render() { + return
; + } + } + Component.contextTypes = { + foo: PropTypes.string, + }; + + var container = document.createElement('div'); + ReactDOM.render(, container); + ReactDOM.render(, container); + expect(actualComponentWillReceiveProps).toEqual({foo: 'def'}); + expect(actualShouldComponentUpdate).toEqual({foo: 'def'}); + expect(actualComponentWillUpdate).toEqual({foo: 'def'}); + }); + + it('should not pass previous context to lifecycles', () => { + var actualComponentDidUpdate; + + class Parent extends React.Component { + getChildContext() { + return { + foo: this.props.foo, + }; + } + + render() { + return ; + } + } + Parent.childContextTypes = { + foo: PropTypes.string.isRequired, + }; + + class Component extends React.Component { + componentDidUpdate(...args) { + actualComponentDidUpdate = args; + } + + render() { + return
; + } + } + Component.contextTypes = { + foo: PropTypes.string, + }; + + var container = document.createElement('div'); + ReactDOM.render(, container); + ReactDOM.render(, container); + expect(actualComponentDidUpdate.length).toBe(3); + }); + + it('should check context types', () => { + + class Component extends React.Component { + render() { + return
; + } + } + Component.contextTypes = { + foo: PropTypes.string.isRequired, + }; + + ReactTestUtils.renderIntoDocument(); + + var spy = chai.spy(); + window.console.error = spy; + + // PropTypes 为空实现所以没有报错 + expect(spy).not.to.have.been.called; + + class ComponentInFooStringContext extends React.Component { + getChildContext() { + return { + foo: this.props.fooValue, + }; + } + + render() { + return ; + } + } + ComponentInFooStringContext.childContextTypes = { + foo: PropTypes.string, + }; + + ReactTestUtils.renderIntoDocument( + , + ); + + // PropTypes 为空实现所以没有报错 + expect(spy).not.to.have.been.called; + + class ComponentInFooNumberContext extends React.Component { + getChildContext() { + return { + foo: this.props.fooValue, + }; + } + + render() { + return ; + } + } + ComponentInFooNumberContext.childContextTypes = { + foo: PropTypes.number, + }; + + ReactTestUtils.renderIntoDocument( + , + ); + + // PropTypes 为空实现所以没有报错 + expect(spy).not.to.have.been.called; + }); + + it('should check child context types', () => { + var spy = chai.spy(); + window.console.error = spy; + + class Component extends React.Component { + getChildContext() { + return this.props.testContext; + } + + render() { + return
; + } + } + Component.childContextTypes = { + foo: PropTypes.string.isRequired, + bar: PropTypes.number, + }; + + ReactTestUtils.renderIntoDocument(); + + // PropTypes 为空实现所以没有报错 + expect(spy).not.to.have.been.called; + + ReactTestUtils.renderIntoDocument(); + + // PropTypes 为空实现所以没有报错 + expect(spy).not.to.have.been.called; + + ReactTestUtils.renderIntoDocument( + , + ); + + ReactTestUtils.renderIntoDocument(); + + // PropTypes 为空实现所以没有报错 + expect(spy).not.to.have.been.called; + }); + + // TODO (bvaughn) Remove this test and the associated behavior in the future. + // It has only been added in Fiber to match the (unintentional) behavior in Stack. + it('should warn (but not error) if getChildContext method is missing', () => { + var spy = chai.spy(); + window.console.error = spy; + + class ComponentA extends React.Component { + static childContextTypes = { + foo: PropTypes.string.isRequired, + }; + render() { + return
; + } + } + class ComponentB extends React.Component { + static childContextTypes = { + foo: PropTypes.string.isRequired, + }; + render() { + return
; + } + } + + ReactTestUtils.renderIntoDocument(); + + // PropTypes 为空实现所以没有报错 + expect(spy).not.to.have.been.called; + + // Warnings should be deduped by component type + ReactTestUtils.renderIntoDocument(); + + // PropTypes 为空实现所以没有报错 + expect(spy).not.to.have.been.called; + + // PropTypes 为空实现所以没有报错 + ReactTestUtils.renderIntoDocument(); + + // PropTypes 为空实现所以没有报错 + expect(spy).not.to.have.been.called; + }); + + // TODO (bvaughn) Remove this test and the associated behavior in the future. + // It has only been added in Fiber to match the (unintentional) behavior in Stack. + it('should pass parent context if getChildContext method is missing', () => { + + class ParentContextProvider extends React.Component { + static childContextTypes = { + foo: PropTypes.number, + }; + getChildContext() { + return { + foo: 'FOO', + }; + } + render() { + return ; + } + } + + class MiddleMissingContext extends React.Component { + static childContextTypes = { + bar: PropTypes.string.isRequired, + }; + render() { + return ; + } + } + + var childContext; + class ChildContextConsumer extends React.Component { + render() { + childContext = this.context; + return
; + } + } + ChildContextConsumer.contextTypes = { + bar: PropTypes.string.isRequired, + foo: PropTypes.string.isRequired, + }; + + ReactTestUtils.renderIntoDocument(); + expect(childContext.bar).toBeUndefined(); + expect(childContext.foo).toBe('FOO'); + }); +}); \ No newline at end of file diff --git a/test/spec.js b/test/spec.js index f476d455b..bf366feea 100644 --- a/test/spec.js +++ b/test/spec.js @@ -48,6 +48,8 @@ require("./modules/ReactComponentLifeCycle-test.jsx"); require("./modules/ReactCompositeComponent-test.jsx"); +require("./modules/ReactContextValidator-test.js"); + From b5bf27028d994261ac8a8b208343c2f3f60ca7ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A5=9D=E9=91=AB=E5=A5=94?= Date: Sat, 30 Sep 2017 16:57:23 +0800 Subject: [PATCH 18/57] add createClass test --- .../createReactClassIntegration-test.js | 440 ++++++++++++++++++ test/spec.js | 63 +-- 2 files changed, 472 insertions(+), 31 deletions(-) create mode 100644 test/modules/createReactClassIntegration-test.js diff --git a/test/modules/createReactClassIntegration-test.js b/test/modules/createReactClassIntegration-test.js new file mode 100644 index 000000000..f8cc43d30 --- /dev/null +++ b/test/modules/createReactClassIntegration-test.js @@ -0,0 +1,440 @@ +/** + * Copyright (c) 2013-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @emails react-core + */ + +'use strict'; +import React from "dist/React"; +import getTestDocument from "./getTestDocument"; +import ReactTestUtils from "lib/ReactTestUtils"; +import createReactClass from "lib/createClass"; +import PropTypes from "lib/ReactPropTypes"; +import ReactDOM from "dist/React"; +import spy from "chai-spies"; + +chai.use(spy) + +describe('create-react-class-integration', () => { + + it('should throw when `render` is not specified', () => { + expect(function() { + createReactClass({}); + }).toThrowError( + '请实现render方法', + ); + }); + + it('should copy prop types onto the Constructor', () => { + var propValidator = function() {} + var TestComponent = createReactClass({ + propTypes: { + value: propValidator, + }, + render: function() { + return
; + }, + }); + + expect(TestComponent.propTypes).toBeDefined(); + expect(TestComponent.propTypes.value).toBe(propValidator); + }); + + it('should warn on invalid prop types', () => { + var spy = chai.spy(); + window.console.error = spy; + createReactClass({ + displayName: 'Component', + propTypes: { + prop: null, + }, + render: function() { + return {this.props.prop}; + }, + }); + expect(spy).to.have.been.called; + // expect(console.error.calls.count()).toBe(1); + // expect(console.error.calls.argsFor(0)[0]).toBe( + // 'Warning: Component: prop type `prop` is invalid; ' + + // 'it must be a function, usually from React.PropTypes.', + // ); + }); + + it('should warn on invalid context types', () => { + var spy = chai.spy(); + window.console.error = spy; + createReactClass({ + displayName: 'Component', + contextTypes: { + prop: null, + }, + render: function() { + return {this.props.prop}; + }, + }); + expect(spy).to.have.been.called; + // expect(console.error.calls.count()).toBe(1); + // expect(console.error.calls.argsFor(0)[0]).toBe( + // 'Warning: Component: context type `prop` is invalid; ' + + // 'it must be a function, usually from React.PropTypes.', + // ); + }); + + it('should throw on invalid child context types', () => { + var spy = chai.spy(); + window.console.error = spy; + createReactClass({ + displayName: 'Component', + childContextTypes: { + prop: null, + }, + render: function() { + return {this.props.prop}; + }, + }); + expect(spy).to.have.been.called; + // expect(console.error.calls.count()).toBe(1); + // expect(console.error.calls.argsFor(0)[0]).toBe( + // 'Warning: Component: child context type `prop` is invalid; ' + + // 'it must be a function, usually from React.PropTypes.', + // ); + }); + + it('should warn when mispelling shouldComponentUpdate', () => { + var spy = chai.spy(); + window.console.error = spy; + + createReactClass({ + componentShouldUpdate: function() { + return false; + }, + render: function() { + return
; + }, + }); + // 未实现相关功能 + expect(spy).not.to.have.been.called; + // expect(console.error.calls.count()).toBe(1); + // expect(console.error.calls.argsFor(0)[0]).toBe( + // 'Warning: A component has a method called componentShouldUpdate(). Did you ' + + // 'mean shouldComponentUpdate()? The name is phrased as a question ' + + // 'because the function is expected to return a value.', + // ); + + createReactClass({ + displayName: 'NamedComponent', + componentShouldUpdate: function() { + return false; + }, + render: function() { + return
; + }, + }); + // 未实现相关功能 + expect(spy).not.to.have.been.called; + // expect(console.error.calls.count()).toBe(2); + // expect(console.error.calls.argsFor(1)[0]).toBe( + // 'Warning: NamedComponent has a method called componentShouldUpdate(). Did you ' + + // 'mean shouldComponentUpdate()? The name is phrased as a question ' + + // 'because the function is expected to return a value.', + // ); + }); + + it('should warn when mispelling componentWillReceiveProps', () => { + var spy = chai.spy(); + window.console.error = spy; + createReactClass({ + componentWillRecieveProps: function() { + return false; + }, + render: function() { + return
; + }, + }); + // 未实现相关功能 + expect(spy).not.to.have.been.called; + // expect(console.error.calls.count()).toBe(1); + // expect(console.error.calls.argsFor(0)[0]).toBe( + // 'Warning: A component has a method called componentWillRecieveProps(). Did you ' + + // 'mean componentWillReceiveProps()?', + // ); + }); + + // TODO: Consider actually moving these to statics or drop this unit test. + + xit('should warn when using deprecated non-static spec keys', () => { + var spy = chai.spy(); + window.console.error = spy; + createReactClass({ + mixins: [{}], + propTypes: { + foo: PropTypes.string, + }, + contextTypes: { + foo: PropTypes.string, + }, + childContextTypes: { + foo: PropTypes.string, + }, + render: function() { + return
; + }, + }); + expect(spy).to.have.been.called.exactly(4); + // expect(console.error.calls.count()).toBe(4); + // expect(console.error.calls.argsFor(0)[0]).toBe( + // 'createClass(...): `mixins` is now a static property and should ' + + // 'be defined inside "statics".', + // ); + // expect(console.error.calls.argsFor(1)[0]).toBe( + // 'createClass(...): `propTypes` is now a static property and should ' + + // 'be defined inside "statics".', + // ); + // expect(console.error.calls.argsFor(2)[0]).toBe( + // 'createClass(...): `contextTypes` is now a static property and ' + + // 'should be defined inside "statics".', + // ); + // expect(console.error.calls.argsFor(3)[0]).toBe( + // 'createClass(...): `childContextTypes` is now a static property and ' + + // 'should be defined inside "statics".', + // ); + }); + + it('should support statics', () => { + var Component = createReactClass({ + statics: { + abc: 'def', + def: 0, + ghi: null, + jkl: 'mno', + pqr: function() { + return this; + }, + }, + + render: function() { + return ; + }, + }); + var instance = ; + instance = ReactTestUtils.renderIntoDocument(instance); + expect(instance.constructor.abc).toBe('def'); + expect(Component.abc).toBe('def'); + expect(instance.constructor.def).toBe(0); + expect(Component.def).toBe(0); + expect(instance.constructor.ghi).toBe(null); + expect(Component.ghi).toBe(null); + expect(instance.constructor.jkl).toBe('mno'); + expect(Component.jkl).toBe('mno'); + expect(instance.constructor.pqr()).toBe(Component); + expect(Component.pqr()).toBe(Component); + }); + + it('should work with object getInitialState() return values', () => { + var Component = createReactClass({ + getInitialState: function() { + return { + occupation: 'clown', + }; + }, + render: function() { + return ; + }, + }); + var instance = ; + instance = ReactTestUtils.renderIntoDocument(instance); + expect(instance.state.occupation).toEqual('clown'); + }); + + it('renders based on context getInitialState', () => { + var Foo = createReactClass({ + contextTypes: { + className: PropTypes.string, + }, + getInitialState() { + return {className: this.context.className}; + }, + render() { + return ; + }, + }); + + var Outer = createReactClass({ + childContextTypes: { + className: PropTypes.string, + }, + getChildContext() { + return {className: 'foo'}; + }, + render() { + return ; + }, + }); + + var container = document.createElement('div'); + ReactDOM.render(, container); + expect(container.firstChild.className).toBe('foo'); + }); + + it('should throw with non-object getInitialState() return values', () => { + [['an array'], 'a string', 1234].forEach(function(state) { + var Component = createReactClass({ + getInitialState: function() { + return state; + }, + render: function() { + return ; + }, + }); + var instance = ; + expect(function() { + instance = ReactTestUtils.renderIntoDocument(instance); + }).toThrowError( + 'Component.getInitialState(): must return an object or null', + ); + }); + }); + + it('should work with a null getInitialState() return value', () => { + var Component = createReactClass({ + getInitialState: function() { + return null; + }, + render: function() { + return ; + }, + }); + expect(() => + ReactTestUtils.renderIntoDocument(), + ).not.toThrow(); + }); + + it('should throw when using legacy factories', () => { + var spy = chai.spy(); + window.console.error = spy; + var Component = createReactClass({ + render() { + return
; + }, + }); + + // 暂时允许该方式调用 + expect(() => Component()).not.toThrow(); + expect(spy).to.have.been.called; + // expect(console.error.calls.count()).toBe(1); + // expect(console.error.calls.argsFor(0)[0]).toBe( + // 'Warning: Something is calling a React component directly. Use a ' + + // 'factory or JSX instead. See: https://fb.me/react-legacyfactory', + // ); + }); + + it('replaceState and callback works', () => { + var ops = []; + var Component = createReactClass({ + getInitialState() { + return {step: 0}; + }, + render() { + ops.push('Render: ' + this.state.step); + return
; + }, + }); + + var instance = ReactTestUtils.renderIntoDocument(); + instance.replaceState({step: 1}, () => { + ops.push('Callback: ' + instance.state.step); + }); + // replaceState 未实现 + expect(ops).not.toEqual(['Render: 0', 'Render: 1', 'Callback: 1']); + }); + + it('isMounted works', () => { + var spy = chai.spy(); + window.console.error = spy; + + var ops = []; + var instance; + var Component = createReactClass({ + displayName: 'MyComponent', + mixins: [ + { + componentWillMount() { + this.log('mixin.componentWillMount'); + }, + componentDidMount() { + this.log('mixin.componentDidMount'); + }, + componentWillUpdate() { + this.log('mixin.componentWillUpdate'); + }, + componentDidUpdate() { + this.log('mixin.componentDidUpdate'); + }, + componentWillUnmount() { + this.log('mixin.componentWillUnmount'); + }, + }, + ], + log(name) { + ops.push(`${name}: ${this.isMounted()}`); + }, + getInitialState() { + this.log('getInitialState'); + return {}; + }, + componentWillMount() { + this.log('componentWillMount'); + }, + componentDidMount() { + this.log('componentDidMount'); + }, + componentWillUpdate() { + this.log('componentWillUpdate'); + }, + componentDidUpdate() { + this.log('componentDidUpdate'); + }, + componentWillUnmount() { + this.log('componentWillUnmount'); + }, + render() { + instance = this; + this.log('render'); + return
; + }, + }); + + var container = document.createElement('div'); + ReactDOM.render(, container); + ReactDOM.render(, container); + ReactDOM.unmountComponentAtNode(container); + instance.log('after unmount'); + expect(ops).toEqual([ + 'getInitialState: false', + 'mixin.componentWillMount: false', + 'componentWillMount: false', + 'render: false', + 'mixin.componentDidMount: true', + 'componentDidMount: true', + 'mixin.componentWillUpdate: true', + 'componentWillUpdate: true', + 'render: true', + 'mixin.componentDidUpdate: true', + 'componentDidUpdate: true', + 'mixin.componentWillUnmount: true', + 'componentWillUnmount: true', + 'after unmount: false', + ]); + + expect(spy).to.have.been.called; + // expect(console.error.calls.count()).toBe(1); + // expect(console.error.calls.argsFor(0)[0]).toEqual( + // 'Warning: MyComponent: isMounted is deprecated. Instead, make sure to ' + + // 'clean up subscriptions and pending requests in componentWillUnmount ' + + // 'to prevent memory leaks.', + // ); + }); +}); \ No newline at end of file diff --git a/test/spec.js b/test/spec.js index bf366feea..f3e426604 100644 --- a/test/spec.js +++ b/test/spec.js @@ -1,54 +1,55 @@ -import "./modules/createElement.spec"; +// import "./modules/createElement.spec"; -import "./modules/util.spec"; -import "./modules/style.spec"; +// import "./modules/util.spec"; +// import "./modules/style.spec"; -import "./modules/classInherit.spec"; -import "./modules/shallow.spec"; -import "./modules/browser.spec"; -import "./modules/cloneElement.spec"; +// import "./modules/classInherit.spec"; +// import "./modules/shallow.spec"; +// import "./modules/browser.spec"; +// import "./modules/cloneElement.spec"; -require("./modules/context.spec.jsx"); +// require("./modules/context.spec.jsx"); -require("./modules/component.spec.jsx"); -require("./modules/lifecycle.spec.jsx"); +// require("./modules/component.spec.jsx"); +// require("./modules/lifecycle.spec.jsx"); -require("./modules/svg.spec.jsx"); +// require("./modules/svg.spec.jsx"); -require("./modules/diffProps.spec.jsx"); +// require("./modules/diffProps.spec.jsx"); -require("./modules/event.spec.jsx"); +// require("./modules/event.spec.jsx"); -require("./modules/node.spec.jsx"); +// require("./modules/node.spec.jsx"); -require("./modules/ref.spec.jsx"); +// require("./modules/ref.spec.jsx"); -require("./modules/redux.spec.jsx"); +// require("./modules/redux.spec.jsx"); -require("./modules/ReactTestUtils-test.jsx"); -require("./modules/ReactComponent-test.jsx"); +// require("./modules/ReactTestUtils-test.jsx"); +// require("./modules/ReactComponent-test.jsx"); -require("./modules/ReactChildren-test.jsx"); +// require("./modules/ReactChildren-test.jsx"); -require("./modules/createReactClassIntegration-test.jsx"); -require("./modules/ReactMultiChild-test.jsx"); +// require("./modules/createReactClassIntegration-test.jsx"); +// require("./modules/ReactMultiChild-test.jsx"); -require("./modules/refs-test.jsx"); -require("./modules/refs-destruction-test.jsx"); -require("./modules/ReactUpdates-test.jsx"); -require("./modules/ReactStatelessComponent-test.jsx"); +// require("./modules/refs-test.jsx"); +// require("./modules/refs-destruction-test.jsx"); +// require("./modules/ReactUpdates-test.jsx"); +// require("./modules/ReactStatelessComponent-test.jsx"); -require("./modules/ReactEmptyComponent-test.jsx"); -require("./modules/ReactIdentity-test.jsx"); -require("./modules/ReactCompositeComponentNestedState-test.jsx"); +// require("./modules/ReactEmptyComponent-test.jsx"); +// require("./modules/ReactIdentity-test.jsx"); +// require("./modules/ReactCompositeComponentNestedState-test.jsx"); -require("./modules/ReactComponentLifeCycle-test.jsx"); +// require("./modules/ReactComponentLifeCycle-test.jsx"); -require("./modules/ReactCompositeComponent-test.jsx"); +// require("./modules/ReactCompositeComponent-test.jsx"); -require("./modules/ReactContextValidator-test.js"); +// require("./modules/ReactContextValidator-test.js"); +require("./modules/createReactClassIntegration-test.js"); From 0f5e7df75e3707b63725c60ec947d4d6f6a5b969 Mon Sep 17 00:00:00 2001 From: RubyLouvre <1669866773@qq.com> Date: Sat, 30 Sep 2017 17:01:09 +0800 Subject: [PATCH 19/57] update --- dist/React.js | 28 +-- dist/ReactIE.js | 28 +-- dist/ReactSelection.js | 28 +-- dist/ReactShim.js | 28 +-- index4.html | 28 ++- index5.html | 163 +++++++----------- lib/shallowCompare.js | 5 +- src/Component.js | 61 +++---- src/PureComponent.js | 2 + src/diff.js | 6 +- test/modules/ReactCompositeComponent-test.jsx | 49 ++++++ 11 files changed, 230 insertions(+), 196 deletions(-) diff --git a/dist/React.js b/dist/React.js index d5b5a98cc..d36d9c0ac 100644 --- a/dist/React.js +++ b/dist/React.js @@ -1052,17 +1052,21 @@ Component.prototype = { }, __mergeStates: function __mergeStates(props, context) { - var n = this.__pendingStates.length; + var pendings = this.__pendingStates, + n = pendings.length; if (n === 0) { return this.state; } - var states = clearArray(this.__pendingStates); - var nextState = extend({}, this.state); + var state = extend({}, this.state); //每次都返回新的state for (var i = 0; i < n; i++) { - var partial = states[i]; - extend(nextState, isFn(partial) ? partial.call(this, nextState, props, context) : partial); + var pending = pendings[i]; + if (isFn(pending)) { + pending = pending.call(this, state, props, context); + } + extend(state, pending); } - return nextState; + pendings.length = 0; + return state; }, render: function render() {} @@ -1093,9 +1097,9 @@ function setStateImpl(state, cb) { } if (!hasDOM) { //组件挂载期 - //componentWillUpdate中的setState/forceUpdate应该被忽略 + //componentWillUpdate中的setState/forceUpdate应该被忽略 if (this.__hydrating) { - //在render方法中调用setState也会被延迟到下一周期更新.这存在两种情况, + //在render方法中调用setState也会被延迟到下一周期更新.这存在两种情况, //1. 组件直接调用自己的setState //2. 子组件调用父组件的setState, this.__renderInNextCycle = true; @@ -1103,7 +1107,7 @@ function setStateImpl(state, cb) { } else { //组件更新期 if (this.__receiving) { - //componentWillReceiveProps中的setState/forceUpdate应该被忽略 + //componentWillReceiveProps中的setState/forceUpdate应该被忽略 return; } this.__renderInNextCycle = true; @@ -2252,13 +2256,11 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { vnode.parentContext = parentContext; vnode.vparent = vparent; - var state = instance.state; if (instance.componentWillMount) { instance.componentWillMount(); - state = instance.__mergeStates(props, context); } instance.__hydrating = true; - var dom = renderComponent(instance, vnode, props, context, state, function (nextRendered, childContext) { + var dom = renderComponent(instance, vnode, props, context, instance.__mergeStates(props, context), function (nextRendered, childContext) { return mountVnode(lastNode, nextRendered, vparent, childContext, updateQueue); }, instance.__rendered); @@ -2319,7 +2321,6 @@ function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { } else { nextContext = instance.context; //没有定义contextTypes就沿用旧的 } - if (instance.componentWillReceiveProps) { instance.__receiving = true; instance.componentWillReceiveProps(nextProps, nextContext); @@ -2370,7 +2371,6 @@ function refreshComponent(instance, updateQueue) { instance.__forceUpdate = false; return dom; } - instance.__hydrating = true; instance.__forceUpdate = false; if (instance.componentWillUpdate) { diff --git a/dist/ReactIE.js b/dist/ReactIE.js index 31d91b0ca..207e57de5 100644 --- a/dist/ReactIE.js +++ b/dist/ReactIE.js @@ -1051,17 +1051,21 @@ Component.prototype = { }, __mergeStates: function __mergeStates(props, context) { - var n = this.__pendingStates.length; + var pendings = this.__pendingStates, + n = pendings.length; if (n === 0) { return this.state; } - var states = clearArray(this.__pendingStates); - var nextState = extend({}, this.state); + var state = extend({}, this.state); //每次都返回新的state for (var i = 0; i < n; i++) { - var partial = states[i]; - extend(nextState, isFn(partial) ? partial.call(this, nextState, props, context) : partial); + var pending = pendings[i]; + if (isFn(pending)) { + pending = pending.call(this, state, props, context); + } + extend(state, pending); } - return nextState; + pendings.length = 0; + return state; }, render: function render() {} @@ -1092,9 +1096,9 @@ function setStateImpl(state, cb) { } if (!hasDOM) { //组件挂载期 - //componentWillUpdate中的setState/forceUpdate应该被忽略 + //componentWillUpdate中的setState/forceUpdate应该被忽略 if (this.__hydrating) { - //在render方法中调用setState也会被延迟到下一周期更新.这存在两种情况, + //在render方法中调用setState也会被延迟到下一周期更新.这存在两种情况, //1. 组件直接调用自己的setState //2. 子组件调用父组件的setState, this.__renderInNextCycle = true; @@ -1102,7 +1106,7 @@ function setStateImpl(state, cb) { } else { //组件更新期 if (this.__receiving) { - //componentWillReceiveProps中的setState/forceUpdate应该被忽略 + //componentWillReceiveProps中的setState/forceUpdate应该被忽略 return; } this.__renderInNextCycle = true; @@ -2251,13 +2255,11 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { vnode.parentContext = parentContext; vnode.vparent = vparent; - var state = instance.state; if (instance.componentWillMount) { instance.componentWillMount(); - state = instance.__mergeStates(props, context); } instance.__hydrating = true; - var dom = renderComponent(instance, vnode, props, context, state, function (nextRendered, childContext) { + var dom = renderComponent(instance, vnode, props, context, instance.__mergeStates(props, context), function (nextRendered, childContext) { return mountVnode(lastNode, nextRendered, vparent, childContext, updateQueue); }, instance.__rendered); @@ -2318,7 +2320,6 @@ function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { } else { nextContext = instance.context; //没有定义contextTypes就沿用旧的 } - if (instance.componentWillReceiveProps) { instance.__receiving = true; instance.componentWillReceiveProps(nextProps, nextContext); @@ -2369,7 +2370,6 @@ function refreshComponent(instance, updateQueue) { instance.__forceUpdate = false; return dom; } - instance.__hydrating = true; instance.__forceUpdate = false; if (instance.componentWillUpdate) { diff --git a/dist/ReactSelection.js b/dist/ReactSelection.js index 31d91b0ca..207e57de5 100644 --- a/dist/ReactSelection.js +++ b/dist/ReactSelection.js @@ -1051,17 +1051,21 @@ Component.prototype = { }, __mergeStates: function __mergeStates(props, context) { - var n = this.__pendingStates.length; + var pendings = this.__pendingStates, + n = pendings.length; if (n === 0) { return this.state; } - var states = clearArray(this.__pendingStates); - var nextState = extend({}, this.state); + var state = extend({}, this.state); //每次都返回新的state for (var i = 0; i < n; i++) { - var partial = states[i]; - extend(nextState, isFn(partial) ? partial.call(this, nextState, props, context) : partial); + var pending = pendings[i]; + if (isFn(pending)) { + pending = pending.call(this, state, props, context); + } + extend(state, pending); } - return nextState; + pendings.length = 0; + return state; }, render: function render() {} @@ -1092,9 +1096,9 @@ function setStateImpl(state, cb) { } if (!hasDOM) { //组件挂载期 - //componentWillUpdate中的setState/forceUpdate应该被忽略 + //componentWillUpdate中的setState/forceUpdate应该被忽略 if (this.__hydrating) { - //在render方法中调用setState也会被延迟到下一周期更新.这存在两种情况, + //在render方法中调用setState也会被延迟到下一周期更新.这存在两种情况, //1. 组件直接调用自己的setState //2. 子组件调用父组件的setState, this.__renderInNextCycle = true; @@ -1102,7 +1106,7 @@ function setStateImpl(state, cb) { } else { //组件更新期 if (this.__receiving) { - //componentWillReceiveProps中的setState/forceUpdate应该被忽略 + //componentWillReceiveProps中的setState/forceUpdate应该被忽略 return; } this.__renderInNextCycle = true; @@ -2251,13 +2255,11 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { vnode.parentContext = parentContext; vnode.vparent = vparent; - var state = instance.state; if (instance.componentWillMount) { instance.componentWillMount(); - state = instance.__mergeStates(props, context); } instance.__hydrating = true; - var dom = renderComponent(instance, vnode, props, context, state, function (nextRendered, childContext) { + var dom = renderComponent(instance, vnode, props, context, instance.__mergeStates(props, context), function (nextRendered, childContext) { return mountVnode(lastNode, nextRendered, vparent, childContext, updateQueue); }, instance.__rendered); @@ -2318,7 +2320,6 @@ function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { } else { nextContext = instance.context; //没有定义contextTypes就沿用旧的 } - if (instance.componentWillReceiveProps) { instance.__receiving = true; instance.componentWillReceiveProps(nextProps, nextContext); @@ -2369,7 +2370,6 @@ function refreshComponent(instance, updateQueue) { instance.__forceUpdate = false; return dom; } - instance.__hydrating = true; instance.__forceUpdate = false; if (instance.componentWillUpdate) { diff --git a/dist/ReactShim.js b/dist/ReactShim.js index 07c2cd277..5379ebd6a 100644 --- a/dist/ReactShim.js +++ b/dist/ReactShim.js @@ -490,17 +490,21 @@ Component.prototype = { }, __mergeStates: function __mergeStates(props, context) { - var n = this.__pendingStates.length; + var pendings = this.__pendingStates, + n = pendings.length; if (n === 0) { return this.state; } - var states = clearArray(this.__pendingStates); - var nextState = extend({}, this.state); + var state = extend({}, this.state); //每次都返回新的state for (var i = 0; i < n; i++) { - var partial = states[i]; - extend(nextState, isFn(partial) ? partial.call(this, nextState, props, context) : partial); + var pending = pendings[i]; + if (isFn(pending)) { + pending = pending.call(this, state, props, context); + } + extend(state, pending); } - return nextState; + pendings.length = 0; + return state; }, render: function render() {} @@ -531,9 +535,9 @@ function setStateImpl(state, cb) { } if (!hasDOM) { //组件挂载期 - //componentWillUpdate中的setState/forceUpdate应该被忽略 + //componentWillUpdate中的setState/forceUpdate应该被忽略 if (this.__hydrating) { - //在render方法中调用setState也会被延迟到下一周期更新.这存在两种情况, + //在render方法中调用setState也会被延迟到下一周期更新.这存在两种情况, //1. 组件直接调用自己的setState //2. 子组件调用父组件的setState, this.__renderInNextCycle = true; @@ -541,7 +545,7 @@ function setStateImpl(state, cb) { } else { //组件更新期 if (this.__receiving) { - //componentWillReceiveProps中的setState/forceUpdate应该被忽略 + //componentWillReceiveProps中的setState/forceUpdate应该被忽略 return; } this.__renderInNextCycle = true; @@ -2093,13 +2097,11 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { vnode.parentContext = parentContext; vnode.vparent = vparent; - var state = instance.state; if (instance.componentWillMount) { instance.componentWillMount(); - state = instance.__mergeStates(props, context); } instance.__hydrating = true; - var dom = renderComponent(instance, vnode, props, context, state, function (nextRendered, childContext) { + var dom = renderComponent(instance, vnode, props, context, instance.__mergeStates(props, context), function (nextRendered, childContext) { return mountVnode(lastNode, nextRendered, vparent, childContext, updateQueue); }, instance.__rendered); @@ -2160,7 +2162,6 @@ function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { } else { nextContext = instance.context; //没有定义contextTypes就沿用旧的 } - if (instance.componentWillReceiveProps) { instance.__receiving = true; instance.componentWillReceiveProps(nextProps, nextContext); @@ -2211,7 +2212,6 @@ function refreshComponent(instance, updateQueue) { instance.__forceUpdate = false; return dom; } - instance.__hydrating = true; instance.__forceUpdate = false; if (instance.componentWillUpdate) { diff --git a/index4.html b/index4.html index a070d52fc..8724e63b6 100644 --- a/index4.html +++ b/index4.html @@ -5,7 +5,7 @@ - @@ -28,12 +28,26 @@ var container = document.createElement('div'); document.body.appendChild(container); const log = []; - function StatelessComponent(props) { - return
{props.name}
; - } - var el = document.createElement('div'); - ReactDOM.render(, container); - + class Component extends React.Component { + constructor(props){ + super(props) + this.state = { + a: 1 + } + } + render() { + return
; + } + } + + var a = new Component() + var oldState = a.state + var newState = { + a: 2 + } + a.setState(newState) + console.log(oldState == a.state) + console.log(newState == a.state) diff --git a/index5.html b/index5.html index d9cc75614..0ce22f763 100644 --- a/index5.html +++ b/index5.html @@ -4,12 +4,14 @@ - - + + + + + --> - + - @@ -46,9 +46,7 @@ state = initialSettings; shouldComponentUpdate(nextProps, nextState) { - var b = shallowCompare(this, nextProps, nextState); - console.log("b", b) - return b + return shallowCompare(this, nextProps, nextState); } render() { @@ -66,11 +64,10 @@ foo: initialSettings.foo, bar: initialSettings.bar, }; - console.log("开始更新") + initialSettings.foo.xx = 1 instance.setState(settings); expect(renderCalls).toBe(1); - console.log(instance.state === settings, instance.state.foo === settings.foo )//官方false true // Re-render because one field changed initialSettings.foo = [1, 2, 3, 4]; @@ -78,12 +75,11 @@ expect(renderCalls).toBe(2); - /* + // Re-render because the object changed instance.setState(getInitialState()); expect(renderCalls).toBe(3); - */ - + diff --git a/lib/shallowCompare.js b/lib/shallowCompare.js index 3e3b1d1cc..7d8616398 100644 --- a/lib/shallowCompare.js +++ b/lib/shallowCompare.js @@ -50,7 +50,6 @@ return function shallowCompare(instance, nextProps, nextState) { var a = shallowEqual(instance.props, nextProps); var b = shallowEqual(instance.state, nextState); - console.log(a, b, "XXX",instance.state , nextState, instance.state == nextState); - return !(a && b); + return !a || !b; }; }); diff --git a/package.json b/package.json index 2a2b121ce..098afb56e 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "babel-preset-react": "^6.24.1", "babel-runtime": "^5.8.38", "chai": "^3.5.0", + "chai-spies": "^0.7.1", "es3ify-webpack-plugin": "0.0.1", "eslint": "^4.2.0", "eslint-plugin-react": "^7.1.0", diff --git a/src/diff.js b/src/diff.js index 2912f85e2..589de2464 100644 --- a/src/diff.js +++ b/src/diff.js @@ -77,7 +77,6 @@ function renderByAnu(vnode, container, callback, context = {}) { rootNode, lastVnode = container.__component; if (lastVnode) { - // lastVnode._hostNode = container.firstChild;?? rootNode = alignVnode( lastVnode, vnode, @@ -271,7 +270,8 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { vnode.vparent = vparent; if (instance.componentWillMount) { - instance.componentWillMount(); + instance.componentWillMount();//这里可能执行了setState + instance.state = instance.__mergeStates(props, context); } instance.__hydrating = true; let dom = renderComponent( @@ -279,7 +279,6 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { vnode, props, context, - instance.__mergeStates(props, context), function(nextRendered, childContext) { return mountVnode( lastNode, @@ -297,9 +296,8 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { return dom; } -function renderComponent(instance, vnode, props, context, state, cb, rendered) { +function renderComponent(instance, vnode, props, context, cb, rendered) { //更新新属性 - instance.state = state; instance.props = props; instance.context = context; //调整全局的 CurrentOwner.cur @@ -394,16 +392,21 @@ function refreshComponent(instance, updateQueue) { nextVnode._instance = instance; //important let nextState = instance.__mergeStates(nextProps, nextContext); + let noUpdate = false; if ( !instance.__forceUpdate && instance.shouldComponentUpdate && !instance.shouldComponentUpdate(nextProps, nextState, nextContext) ) { - instance.__forceUpdate = false; + noUpdate = true; + } + instance.__forceUpdate = false; + instance.state = nextState;//既然setState了,无论shouldComponentUpdate结果如何,用户传给的state对象都会作用到组件上 + if(noUpdate){ return dom; } + instance.__hydrating = true; - instance.__forceUpdate = false; if (instance.componentWillUpdate) { instance.componentWillUpdate(nextProps, nextState, nextContext); } @@ -416,7 +419,6 @@ function refreshComponent(instance, updateQueue) { nextVnode, nextProps, nextContext, - nextState, function(nextRendered, childContext) { return alignVnode( lastRendered, From 52d05c58068739aaead8f6862e9967037634bc47 Mon Sep 17 00:00:00 2001 From: RubyLouvre <1669866773@qq.com> Date: Sat, 30 Sep 2017 17:47:22 +0800 Subject: [PATCH 21/57] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/modules/ReactCompositeComponent-test.jsx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/modules/ReactCompositeComponent-test.jsx b/test/modules/ReactCompositeComponent-test.jsx index fef676143..691368be0 100644 --- a/test/modules/ReactCompositeComponent-test.jsx +++ b/test/modules/ReactCompositeComponent-test.jsx @@ -1287,5 +1287,19 @@ describe("ReactCompositeComponent", function() { instance.setState(getInitialState()); expect(renderCalls).toBe(3); }); + + it('should call setState callback with no arguments', () => { + let mockArgs; + class Component extends React.Component { + componentDidMount() { + this.setState({}, (...args) => (mockArgs = args)); + } + render() { + return false; + } + } + ReactTestUtils.renderIntoDocument(); + expect(mockArgs.length).toEqual(0); + }); }); \ No newline at end of file From 6cc8d500897dc0443f3d810d93948467d3df55ec Mon Sep 17 00:00:00 2001 From: "waliang.wang" Date: Sat, 30 Sep 2017 18:27:01 +0800 Subject: [PATCH 22/57] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0anu=20case?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/modules/EventPluginHub-test.jsx | 33 ++++++ test/modules/ReactChildReconciler-test.jsx | 108 ++++++++++++++++++ ...ctCompositeComponentDOMMinimalism-test.jsx | 74 ++++++++++++ test/spec.js | 6 +- 4 files changed, 218 insertions(+), 3 deletions(-) create mode 100644 test/modules/EventPluginHub-test.jsx create mode 100644 test/modules/ReactChildReconciler-test.jsx create mode 100644 test/modules/ReactCompositeComponentDOMMinimalism-test.jsx diff --git a/test/modules/EventPluginHub-test.jsx b/test/modules/EventPluginHub-test.jsx new file mode 100644 index 000000000..3584adcd0 --- /dev/null +++ b/test/modules/EventPluginHub-test.jsx @@ -0,0 +1,33 @@ +import React from "dist/React"; +import getTestDocument from "./getTestDocument"; +import ReactTestUtils from "lib/ReactTestUtils"; + +import ReactDOMServer from "dist/ReactDOMServer"; +// https://github.com/facebook/react/blob/master/src/renderers/__tests__/EventPluginHub-test.js +var ReactDOM = window.ReactDOM || React; + +describe("isEventSupported", function() { + this.timeout(200000); + + + it('should prevent non-function listeners, at dispatch', () => { + spyOn(console, 'error'); + var node = ReactTestUtils.renderIntoDocument( +
, + ); + expect(function() { + ReactTestUtils.SimulateNative.click(node); + }).toThrowError( + 'Expected `onClick` listener to be a function, instead got a value of `string` type.', + ); + }); + + it('should not prevent null listeners, at dispatch', () => { + var node = ReactTestUtils.renderIntoDocument(
); + expect(function() { + ReactTestUtils.SimulateNative.click(node); + }).not.toThrow('Expected `onClick` listener to be a function, instead got a value of `null` type.'); + }); + + +}); \ No newline at end of file diff --git a/test/modules/ReactChildReconciler-test.jsx b/test/modules/ReactChildReconciler-test.jsx new file mode 100644 index 000000000..f40d39c24 --- /dev/null +++ b/test/modules/ReactChildReconciler-test.jsx @@ -0,0 +1,108 @@ +import React from "dist/React"; +import getTestDocument from "./getTestDocument"; +import ReactTestUtils from "lib/ReactTestUtils"; + +import ReactDOMServer from "dist/ReactDOMServer"; +// https://github.com/facebook/react/blob/master/src/renderers/__tests__/ReactChildReconciler-test.js +var ReactDOM = window.ReactDOM || React; + +describe("ReactChildReconciler", function() { + this.timeout(200000); + + + function normalizeCodeLocInfo(str) { + return str && str.replace(/\(at .+?:\d+\)/g, '(at **)'); + } + + function createIterable(array) { + return { + '@@iterator': function() { + var i = 0; + return { + next() { + const next = { + value: i < array.length ? array[i] : undefined, + done: i === array.length, + }; + i++; + return next; + }, + }; + }, + }; + } + + it('warns for duplicated array keys', () => { + spyOn(console, 'error'); + + class Component extends React.Component { + render() { + return
{[
,
]}
; + } + } + + ReactTestUtils.renderIntoDocument(); + }); + + it('warns for duplicated array keys with component stack info', () => { + spyOn(console, 'error'); + + class Component extends React.Component { + render() { + return
{[
,
]}
; + } + } + + class Parent extends React.Component { + render() { + return React.cloneElement(this.props.child); + } + } + + class GrandParent extends React.Component { + render() { + return } />; + } + } + + ReactTestUtils.renderIntoDocument(); + }); + + it('warns for duplicated iterable keys', () => { + spyOn(console, 'error'); + + class Component extends React.Component { + render() { + return
{createIterable([
,
])}
; + } + } + + ReactTestUtils.renderIntoDocument(); + }); + + it('warns for duplicated iterable keys with component stack info', () => { + spyOn(console, 'error'); + + class Component extends React.Component { + render() { + return
{createIterable([
,
])}
; + } + } + + class Parent extends React.Component { + render() { + return React.cloneElement(this.props.child); + } + } + + class GrandParent extends React.Component { + render() { + return } />; + } + } + + ReactTestUtils.renderIntoDocument(); + }); + + +}); \ No newline at end of file diff --git a/test/modules/ReactCompositeComponentDOMMinimalism-test.jsx b/test/modules/ReactCompositeComponentDOMMinimalism-test.jsx new file mode 100644 index 000000000..951b7dc0f --- /dev/null +++ b/test/modules/ReactCompositeComponentDOMMinimalism-test.jsx @@ -0,0 +1,74 @@ +import React from "dist/React"; +import getTestDocument from "./getTestDocument"; +import ReactTestUtils from "lib/ReactTestUtils"; +// let ReactInstanceMap = require('ReactInstanceMap'); +import ReactDOMServer from "dist/ReactDOMServer"; +// https://github.com/facebook/react/blob/master/src/renderers/__tests__/EventPluginHub-test.js +var ReactDOM = window.ReactDOM || React; + +describe("ReactCompositeComponentDOMMinimalism", function() { + this.timeout(200000); + + var LowerLevelComposite = class extends React.Component { + render() { + return ( +
+ {this.props.children} +
+ ); + } + }; + + var MyCompositeComponent = class extends React.Component { + render() { + return ( + + {this.props.children} + + ); + } + }; + + var expectSingleChildlessDiv = function(instance) { + var el = ReactDOM.findDOMNode(instance); + expect(el.tagName).toBe('DIV'); + expect(el.children.length).toBe(0); + }; + + it('should not render extra nodes for non-interpolated text', () => { + var instance = ( + + A string child + + ); + instance = ReactTestUtils.renderIntoDocument(instance); + expectSingleChildlessDiv(instance); + }); + + it('should not render extra nodes for non-interpolated text', () => { + var instance = ( + + {'Interpolated String Child'} + + ); + instance = ReactTestUtils.renderIntoDocument(instance); + expectSingleChildlessDiv(instance); + }); + + it('should not render extra nodes for non-interpolated text', () => { + var instance = ( + +
    + This text causes no children in ul, just innerHTML +
+
+ ); + instance = ReactTestUtils.renderIntoDocument(instance); + var el = ReactDOM.findDOMNode(instance); + expect(el.tagName).toBe('DIV'); + expect(el.children.length).toBe(1); + expect(el.children[0].tagName).toBe('UL'); + expect(el.children[0].children.length).toBe(0); + }); + +}); \ No newline at end of file diff --git a/test/spec.js b/test/spec.js index f3e426604..0deeb7de9 100644 --- a/test/spec.js +++ b/test/spec.js @@ -50,9 +50,9 @@ // require("./modules/ReactContextValidator-test.js"); require("./modules/createReactClassIntegration-test.js"); - - - +require("./modules/EventPluginHub-test.jsx"); // wang +require("./modules/ReactChildReconciler-test.jsx"); // wang +require("./modules/ReactCompositeComponentDOMMinimalism-test.jsx"); // wang From 69ed44247f988f4db3d17e8c00cac14e40d2a714 Mon Sep 17 00:00:00 2001 From: RubyLouvre <1669866773@qq.com> Date: Sat, 30 Sep 2017 22:18:08 +0800 Subject: [PATCH 23/57] =?UTF-8?q?=E8=B0=83=E6=95=B4=E5=88=B7=E6=96=B0state?= =?UTF-8?q?=E7=9A=84=E6=97=B6=E6=9C=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ReactChildReconciler-test.jsx | 108 +++++ dist/React.js | 24 +- dist/ReactIE.js | 24 +- dist/ReactSelection.js | 24 +- dist/ReactShim.js | 24 +- index5.html | 75 +-- src/diff.js | 25 +- test/matchers.js | 6 +- test/modules/ReactComponentLifeCycle-test.jsx | 9 +- ...ctCompositeComponentDOMMinimalism-test.jsx | 56 +++ .../ReactCompositeComponentState-test.jsx | 317 +++++++++++++ ...test.js => ReactContextValidator-test.jsx} | 47 +- test/modules/ReactEmptyComponent-test.jsx | 2 +- .../createReactClassIntegration-test.js | 440 ------------------ .../createReactClassIntegration-test.jsx | 175 ++++++- test/spec.js | 72 +-- 16 files changed, 778 insertions(+), 650 deletions(-) create mode 100644 ReactChildReconciler-test.jsx create mode 100644 test/modules/ReactCompositeComponentDOMMinimalism-test.jsx create mode 100644 test/modules/ReactCompositeComponentState-test.jsx rename test/modules/{ReactContextValidator-test.js => ReactContextValidator-test.jsx} (92%) delete mode 100644 test/modules/createReactClassIntegration-test.js diff --git a/ReactChildReconciler-test.jsx b/ReactChildReconciler-test.jsx new file mode 100644 index 000000000..f40d39c24 --- /dev/null +++ b/ReactChildReconciler-test.jsx @@ -0,0 +1,108 @@ +import React from "dist/React"; +import getTestDocument from "./getTestDocument"; +import ReactTestUtils from "lib/ReactTestUtils"; + +import ReactDOMServer from "dist/ReactDOMServer"; +// https://github.com/facebook/react/blob/master/src/renderers/__tests__/ReactChildReconciler-test.js +var ReactDOM = window.ReactDOM || React; + +describe("ReactChildReconciler", function() { + this.timeout(200000); + + + function normalizeCodeLocInfo(str) { + return str && str.replace(/\(at .+?:\d+\)/g, '(at **)'); + } + + function createIterable(array) { + return { + '@@iterator': function() { + var i = 0; + return { + next() { + const next = { + value: i < array.length ? array[i] : undefined, + done: i === array.length, + }; + i++; + return next; + }, + }; + }, + }; + } + + it('warns for duplicated array keys', () => { + spyOn(console, 'error'); + + class Component extends React.Component { + render() { + return
{[
,
]}
; + } + } + + ReactTestUtils.renderIntoDocument(); + }); + + it('warns for duplicated array keys with component stack info', () => { + spyOn(console, 'error'); + + class Component extends React.Component { + render() { + return
{[
,
]}
; + } + } + + class Parent extends React.Component { + render() { + return React.cloneElement(this.props.child); + } + } + + class GrandParent extends React.Component { + render() { + return } />; + } + } + + ReactTestUtils.renderIntoDocument(); + }); + + it('warns for duplicated iterable keys', () => { + spyOn(console, 'error'); + + class Component extends React.Component { + render() { + return
{createIterable([
,
])}
; + } + } + + ReactTestUtils.renderIntoDocument(); + }); + + it('warns for duplicated iterable keys with component stack info', () => { + spyOn(console, 'error'); + + class Component extends React.Component { + render() { + return
{createIterable([
,
])}
; + } + } + + class Parent extends React.Component { + render() { + return React.cloneElement(this.props.child); + } + } + + class GrandParent extends React.Component { + render() { + return } />; + } + } + + ReactTestUtils.renderIntoDocument(); + }); + + +}); \ No newline at end of file diff --git a/dist/React.js b/dist/React.js index 116d952aa..480402014 100644 --- a/dist/React.js +++ b/dist/React.js @@ -2260,7 +2260,7 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { instance.state = instance.__mergeStates(props, context); } instance.__hydrating = true; - var dom = renderComponent(instance, vnode, props, context, function (nextRendered, childContext) { + var dom = renderComponent(instance, vnode, function (nextRendered, childContext) { return mountVnode(lastNode, nextRendered, vparent, childContext, updateQueue); }, instance.__rendered); @@ -2269,10 +2269,7 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { return dom; } -function renderComponent(instance, vnode, props, context, cb, rendered) { - //更新新属性 - instance.props = props; - instance.context = context; +function renderComponent(instance, vnode, cb, rendered) { //调整全局的 CurrentOwner.cur if (!rendered) { try { @@ -2366,25 +2363,26 @@ function refreshComponent(instance, updateQueue) { nextVnode._instance = instance; //important var nextState = instance.__mergeStates(nextProps, nextContext); - var noUpdate = false; + var shouldUpdate = true; if (!instance.__forceUpdate && instance.shouldComponentUpdate && !instance.shouldComponentUpdate(nextProps, nextState, nextContext)) { - noUpdate = true; + shouldUpdate = false; + } else if (instance.componentWillUpdate) { + instance.componentWillUpdate(nextProps, nextState, nextContext); } + instance.__forceUpdate = false; + instance.props = nextProps; + instance.context = nextContext; instance.state = nextState; //既然setState了,无论shouldComponentUpdate结果如何,用户传给的state对象都会作用到组件上 - if (noUpdate) { + if (!shouldUpdate) { return dom; } - instance.__hydrating = true; - if (instance.componentWillUpdate) { - instance.componentWillUpdate(nextProps, nextState, nextContext); - } instance.lastProps = lastProps; instance.lastState = lastState; instance.lastContext = lastContext; //这里会更新instance的props, context, state - dom = renderComponent(instance, nextVnode, nextProps, nextContext, function (nextRendered, childContext) { + dom = renderComponent(instance, nextVnode, function (nextRendered, childContext) { return alignVnode(lastRendered, nextRendered, vparent, childContext, updateQueue); }); diff --git a/dist/ReactIE.js b/dist/ReactIE.js index 8aa7ec769..23f213795 100644 --- a/dist/ReactIE.js +++ b/dist/ReactIE.js @@ -2259,7 +2259,7 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { instance.state = instance.__mergeStates(props, context); } instance.__hydrating = true; - var dom = renderComponent(instance, vnode, props, context, function (nextRendered, childContext) { + var dom = renderComponent(instance, vnode, function (nextRendered, childContext) { return mountVnode(lastNode, nextRendered, vparent, childContext, updateQueue); }, instance.__rendered); @@ -2268,10 +2268,7 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { return dom; } -function renderComponent(instance, vnode, props, context, cb, rendered) { - //更新新属性 - instance.props = props; - instance.context = context; +function renderComponent(instance, vnode, cb, rendered) { //调整全局的 CurrentOwner.cur if (!rendered) { try { @@ -2365,25 +2362,26 @@ function refreshComponent(instance, updateQueue) { nextVnode._instance = instance; //important var nextState = instance.__mergeStates(nextProps, nextContext); - var noUpdate = false; + var shouldUpdate = true; if (!instance.__forceUpdate && instance.shouldComponentUpdate && !instance.shouldComponentUpdate(nextProps, nextState, nextContext)) { - noUpdate = true; + shouldUpdate = false; + } else if (instance.componentWillUpdate) { + instance.componentWillUpdate(nextProps, nextState, nextContext); } + instance.__forceUpdate = false; + instance.props = nextProps; + instance.context = nextContext; instance.state = nextState; //既然setState了,无论shouldComponentUpdate结果如何,用户传给的state对象都会作用到组件上 - if (noUpdate) { + if (!shouldUpdate) { return dom; } - instance.__hydrating = true; - if (instance.componentWillUpdate) { - instance.componentWillUpdate(nextProps, nextState, nextContext); - } instance.lastProps = lastProps; instance.lastState = lastState; instance.lastContext = lastContext; //这里会更新instance的props, context, state - dom = renderComponent(instance, nextVnode, nextProps, nextContext, function (nextRendered, childContext) { + dom = renderComponent(instance, nextVnode, function (nextRendered, childContext) { return alignVnode(lastRendered, nextRendered, vparent, childContext, updateQueue); }); diff --git a/dist/ReactSelection.js b/dist/ReactSelection.js index 8aa7ec769..23f213795 100644 --- a/dist/ReactSelection.js +++ b/dist/ReactSelection.js @@ -2259,7 +2259,7 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { instance.state = instance.__mergeStates(props, context); } instance.__hydrating = true; - var dom = renderComponent(instance, vnode, props, context, function (nextRendered, childContext) { + var dom = renderComponent(instance, vnode, function (nextRendered, childContext) { return mountVnode(lastNode, nextRendered, vparent, childContext, updateQueue); }, instance.__rendered); @@ -2268,10 +2268,7 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { return dom; } -function renderComponent(instance, vnode, props, context, cb, rendered) { - //更新新属性 - instance.props = props; - instance.context = context; +function renderComponent(instance, vnode, cb, rendered) { //调整全局的 CurrentOwner.cur if (!rendered) { try { @@ -2365,25 +2362,26 @@ function refreshComponent(instance, updateQueue) { nextVnode._instance = instance; //important var nextState = instance.__mergeStates(nextProps, nextContext); - var noUpdate = false; + var shouldUpdate = true; if (!instance.__forceUpdate && instance.shouldComponentUpdate && !instance.shouldComponentUpdate(nextProps, nextState, nextContext)) { - noUpdate = true; + shouldUpdate = false; + } else if (instance.componentWillUpdate) { + instance.componentWillUpdate(nextProps, nextState, nextContext); } + instance.__forceUpdate = false; + instance.props = nextProps; + instance.context = nextContext; instance.state = nextState; //既然setState了,无论shouldComponentUpdate结果如何,用户传给的state对象都会作用到组件上 - if (noUpdate) { + if (!shouldUpdate) { return dom; } - instance.__hydrating = true; - if (instance.componentWillUpdate) { - instance.componentWillUpdate(nextProps, nextState, nextContext); - } instance.lastProps = lastProps; instance.lastState = lastState; instance.lastContext = lastContext; //这里会更新instance的props, context, state - dom = renderComponent(instance, nextVnode, nextProps, nextContext, function (nextRendered, childContext) { + dom = renderComponent(instance, nextVnode, function (nextRendered, childContext) { return alignVnode(lastRendered, nextRendered, vparent, childContext, updateQueue); }); diff --git a/dist/ReactShim.js b/dist/ReactShim.js index 29b0a72a4..096fea91a 100644 --- a/dist/ReactShim.js +++ b/dist/ReactShim.js @@ -2101,7 +2101,7 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { instance.state = instance.__mergeStates(props, context); } instance.__hydrating = true; - var dom = renderComponent(instance, vnode, props, context, function (nextRendered, childContext) { + var dom = renderComponent(instance, vnode, function (nextRendered, childContext) { return mountVnode(lastNode, nextRendered, vparent, childContext, updateQueue); }, instance.__rendered); @@ -2110,10 +2110,7 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { return dom; } -function renderComponent(instance, vnode, props, context, cb, rendered) { - //更新新属性 - instance.props = props; - instance.context = context; +function renderComponent(instance, vnode, cb, rendered) { //调整全局的 CurrentOwner.cur if (!rendered) { try { @@ -2207,25 +2204,26 @@ function refreshComponent(instance, updateQueue) { nextVnode._instance = instance; //important var nextState = instance.__mergeStates(nextProps, nextContext); - var noUpdate = false; + var shouldUpdate = true; if (!instance.__forceUpdate && instance.shouldComponentUpdate && !instance.shouldComponentUpdate(nextProps, nextState, nextContext)) { - noUpdate = true; + shouldUpdate = false; + } else if (instance.componentWillUpdate) { + instance.componentWillUpdate(nextProps, nextState, nextContext); } + instance.__forceUpdate = false; + instance.props = nextProps; + instance.context = nextContext; instance.state = nextState; //既然setState了,无论shouldComponentUpdate结果如何,用户传给的state对象都会作用到组件上 - if (noUpdate) { + if (!shouldUpdate) { return dom; } - instance.__hydrating = true; - if (instance.componentWillUpdate) { - instance.componentWillUpdate(nextProps, nextState, nextContext); - } instance.lastProps = lastProps; instance.lastState = lastState; instance.lastContext = lastContext; //这里会更新instance的props, context, state - dom = renderComponent(instance, nextVnode, nextProps, nextContext, function (nextRendered, childContext) { + dom = renderComponent(instance, nextVnode, function (nextRendered, childContext) { return alignVnode(lastRendered, nextRendered, vparent, childContext, updateQueue); }); diff --git a/index5.html b/index5.html index 1f724cc4c..7602f0438 100644 --- a/index5.html +++ b/index5.html @@ -8,7 +8,7 @@ - @@ -30,56 +30,27 @@ } } } - - - function getInitialState() { - return { - foo: [1, 2, 3], - bar: {a: 4, b: 5, c: 6}, - }; - } - - var renderCalls = 0; - var initialSettings = getInitialState(); - - class Component extends React.Component { - state = initialSettings; - - shouldComponentUpdate(nextProps, nextState) { - return shallowCompare(this, nextProps, nextState); - } - - render() { - renderCalls++; - return
; - } - } - - // var container = document.createElement('div'); - var instance = ReactDOM.render(, container); - expect(renderCalls).toBe(1); - - // Do not re-render if state is equal - var settings = { - foo: initialSettings.foo, - bar: initialSettings.bar, - }; - - initialSettings.foo.xx = 1 - instance.setState(settings); - expect(renderCalls).toBe(1); - - // Re-render because one field changed - initialSettings.foo = [1, 2, 3, 4]; - instance.setState(initialSettings); - + const log = []; + class Test extends React.Component { + state = { a: 0 }; + render() { + return null; + } + shouldComponentUpdate(nextProps, nextState) { + log.push("scu from " + Object.keys(this.state) + " to " + Object.keys(nextState)); + return false; + } + } - expect(renderCalls).toBe(2); + const test = ReactDOM.render(, container); + test.setState({ b: 0 }); + expect(log.length).toBe(1); + test.setState({ c: 0 }); + expect(log.length).toBe(2); + // expect(log).toEqual(["scu from a to a,b", "scu from a,b to a,b,c"]); - // Re-render because the object changed - instance.setState(getInitialState()); - expect(renderCalls).toBe(3); + console.log(JSON.stringify(log)) @@ -92,13 +63,7 @@
开发者工具
-          A componentWillMount
-          A render
-          A componentDidMount
-          A componentWillUnmount
-          B componentWillMount
-          B render
-          B componentDidMount
+         
         
diff --git a/src/diff.js b/src/diff.js index 589de2464..5c1a71bff 100644 --- a/src/diff.js +++ b/src/diff.js @@ -277,8 +277,6 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { let dom = renderComponent( instance, vnode, - props, - context, function(nextRendered, childContext) { return mountVnode( lastNode, @@ -296,10 +294,7 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { return dom; } -function renderComponent(instance, vnode, props, context, cb, rendered) { - //更新新属性 - instance.props = props; - instance.context = context; +function renderComponent(instance, vnode, cb, rendered) { //调整全局的 CurrentOwner.cur if (!rendered) { try { @@ -392,24 +387,26 @@ function refreshComponent(instance, updateQueue) { nextVnode._instance = instance; //important let nextState = instance.__mergeStates(nextProps, nextContext); - let noUpdate = false; + let shouldUpdate = true; if ( !instance.__forceUpdate && instance.shouldComponentUpdate && !instance.shouldComponentUpdate(nextProps, nextState, nextContext) ) { - noUpdate = true; + shouldUpdate = false; + }else if(instance.componentWillUpdate) { + instance.componentWillUpdate(nextProps, nextState, nextContext); } + + instance.__forceUpdate = false; + instance.props = nextProps; + instance.context = nextContext; instance.state = nextState;//既然setState了,无论shouldComponentUpdate结果如何,用户传给的state对象都会作用到组件上 - if(noUpdate){ + if(!shouldUpdate){ return dom; } - instance.__hydrating = true; - if (instance.componentWillUpdate) { - instance.componentWillUpdate(nextProps, nextState, nextContext); - } instance.lastProps = lastProps; instance.lastState = lastState; instance.lastContext = lastContext; @@ -417,8 +414,6 @@ function refreshComponent(instance, updateQueue) { dom = renderComponent( instance, nextVnode, - nextProps, - nextContext, function(nextRendered, childContext) { return alignVnode( lastRendered, diff --git a/test/matchers.js b/test/matchers.js index c41398013..c1fe93161 100644 --- a/test/matchers.js +++ b/test/matchers.js @@ -12,13 +12,13 @@ if (typeof chai !== "undefined") { }); //实现spyOn功能 - var spy = (window.spyOn = function(obj, method) { + var spy2 = (window.spyOn = function(obj, method) { var orig = obj[method]; if(orig.calls){ orig.calls.reset(); return orig; } - obj[method] = spy.createSpy(orig); + obj[method] = spy2.createSpy(orig); return { and: { callThrough: function() { @@ -27,7 +27,7 @@ if (typeof chai !== "undefined") { } }; }); - spy.createSpy = function(fn) { + spy2.createSpy = function(fn) { function spyFn() { spyFn.calls.push([].slice.call(arguments)); if (fn) { diff --git a/test/modules/ReactComponentLifeCycle-test.jsx b/test/modules/ReactComponentLifeCycle-test.jsx index e318181e9..17eab73ef 100644 --- a/test/modules/ReactComponentLifeCycle-test.jsx +++ b/test/modules/ReactComponentLifeCycle-test.jsx @@ -1,10 +1,12 @@ + + import React from "dist/React"; import getTestDocument from "./getTestDocument"; import ReactTestUtils from "lib/ReactTestUtils"; +import createReactClass from "lib/createClass"; +import PropTypes from "lib/ReactPropTypes"; +import ReactDOM from "dist/React"; -import ReactDOMServer from "dist/ReactDOMServer"; -//https://github.com/facebook/react/blob/master/src/isomorphic/children/__tests__/ReactChildren-test.js -var ReactDOM = window.ReactDOM || React; describe("ReactComponentLifeCycle-test", function() { this.timeout(200000); @@ -476,7 +478,6 @@ function getLifeCycleState(instance) { it('calls effects on module-pattern component', function() { const log = []; - var PropTypes = React.PropTypes function Parent() { return { render() { diff --git a/test/modules/ReactCompositeComponentDOMMinimalism-test.jsx b/test/modules/ReactCompositeComponentDOMMinimalism-test.jsx new file mode 100644 index 000000000..a9ea4e46a --- /dev/null +++ b/test/modules/ReactCompositeComponentDOMMinimalism-test.jsx @@ -0,0 +1,56 @@ +import React from "dist/React"; +import getTestDocument from "./getTestDocument"; +import ReactTestUtils from "lib/ReactTestUtils"; +import createReactClass from "lib/createClass"; +import PropTypes from "lib/ReactPropTypes"; +var ReactDOM = window.ReactDOM || React; + + + +describe("ReactCompositeComponentDOMMinimalism",function() { + this.timeout(200000); + + var LowerLevelComposite = class extends React.Component { + render() { + return
{this.props.children}
; + } + }; + + var MyCompositeComponent = class extends React.Component { + render() { + return {this.props.children}; + } + }; + + var expectSingleChildlessDiv = function(instance) { + var el = ReactDOM.findDOMNode(instance); + expect(el.tagName).toBe("DIV"); + expect(el.children.length).toBe(0); + }; + + it("should not render extra nodes for non-interpolated text", () => { + var instance = A string child; + instance = ReactTestUtils.renderIntoDocument(instance); + expectSingleChildlessDiv(instance); + }); + + it("should not render extra nodes for non-interpolated text", () => { + var instance = {"Interpolated String Child"}; + instance = ReactTestUtils.renderIntoDocument(instance); + expectSingleChildlessDiv(instance); + }); + + it("should not render extra nodes for non-interpolated text", () => { + var instance = ( + +
    This text causes no children in ul, just innerHTML
+
+ ); + instance = ReactTestUtils.renderIntoDocument(instance); + var el = ReactDOM.findDOMNode(instance); + expect(el.tagName).toBe("DIV"); + expect(el.children.length).toBe(1); + expect(el.children[0].tagName).toBe("UL"); + expect(el.children[0].children.length).toBe(0); + }); +}); diff --git a/test/modules/ReactCompositeComponentState-test.jsx b/test/modules/ReactCompositeComponentState-test.jsx new file mode 100644 index 000000000..9f91be737 --- /dev/null +++ b/test/modules/ReactCompositeComponentState-test.jsx @@ -0,0 +1,317 @@ +import React from "dist/React"; +import getTestDocument from "./getTestDocument"; +import ReactTestUtils from "lib/ReactTestUtils"; +import ReactDOMServer from "dist/ReactDOMServer"; +// https://github.com/facebook/react/blob/master/src/renderers/__tests__/EventPluginHub-test.js +var ReactDOM = window.ReactDOM || React; + +describe("ReactCompositeComponentDOMMinimalism", function() { + this.timeout(200000); + + var TestComponent = class extends React.Component { + constructor(props) { + super(props); + this.peekAtState("getInitialState", undefined, props); + this.state = { color: "red" }; + } + + peekAtState = (from, state = this.state, props = this.props) => { + props.stateListener(from, state && state.color); + }; + + peekAtCallback = from => { + return () => this.peekAtState(from); + }; + + setFavoriteColor(nextColor) { + this.setState({ color: nextColor }, this.peekAtCallback("setFavoriteColor")); + } + + render() { + this.peekAtState("render"); + return
{this.state.color}
; + } + + componentWillMount() { + this.peekAtState("componentWillMount-start"); + this.setState(function(state) { + this.peekAtState("before-setState-sunrise", state); + }); + this.setState({ color: "sunrise" }, this.peekAtCallback("setState-sunrise")); + this.setState(function(state) { + this.peekAtState("after-setState-sunrise", state); + }); + this.peekAtState("componentWillMount-after-sunrise"); + this.setState({ color: "orange" }, this.peekAtCallback("setState-orange")); + this.setState(function(state) { + this.peekAtState("after-setState-orange", state); + }); + this.peekAtState("componentWillMount-end"); + } + + componentDidMount() { + this.peekAtState("componentDidMount-start"); + this.setState({ color: "yellow" }, this.peekAtCallback("setState-yellow")); + this.peekAtState("componentDidMount-end"); + } + + componentWillReceiveProps(newProps) { + this.peekAtState("componentWillReceiveProps-start"); + if (newProps.nextColor) { + this.setState(function(state) { + this.peekAtState("before-setState-receiveProps", state); + return { color: newProps.nextColor }; + }); + // No longer a public API, but we can test that it works internally by + // reaching into the updater. + this.setState({ color: undefined }); + this.setState(function(state) { + this.peekAtState("before-setState-again-receiveProps", state); + return { color: newProps.nextColor }; + }, this.peekAtCallback("setState-receiveProps")); + this.setState(function(state) { + this.peekAtState("after-setState-receiveProps", state); + }); + } + this.peekAtState("componentWillReceiveProps-end"); + } + + shouldComponentUpdate(nextProps, nextState) { + this.peekAtState("shouldComponentUpdate-currentState"); + this.peekAtState("shouldComponentUpdate-nextState", nextState); + return true; + } + + componentWillUpdate(nextProps, nextState) { + this.peekAtState("componentWillUpdate-currentState"); + this.peekAtState("componentWillUpdate-nextState", nextState); + } + + componentDidUpdate(prevProps, prevState) { + this.peekAtState("componentDidUpdate-currentState"); + this.peekAtState("componentDidUpdate-prevState", prevState); + } + + componentWillUnmount() { + this.peekAtState("componentWillUnmount"); + } + }; + + it("should support setting state", () => { + return; + var container = document.createElement("div"); + document.body.appendChild(container); + var stateListener = spyOn.createSpy(); + + var instance = ReactDOM.render(, container, function peekAtInitialCallback() { + this.peekAtState("initial-callback"); + }); + ReactDOM.render(, container, instance.peekAtCallback("setProps")); + instance.setFavoriteColor("blue"); + instance.forceUpdate(instance.peekAtCallback("forceUpdate")); + + ReactDOM.unmountComponentAtNode(container); + let expected = [ + // there is no state when getInitialState() is called + ["getInitialState", null], + ["componentWillMount-start", "red"], + // setState()'s only enqueue pending states. + ["componentWillMount-after-sunrise", "red"], + ["componentWillMount-end", "red"], + // pending state queue is processed + ["before-setState-sunrise", "red"], + ["after-setState-sunrise", "sunrise"], + ["after-setState-orange", "orange"], + // pending state has been applied + ["render", "orange"], + ["componentDidMount-start", "orange"], + // setState-sunrise and setState-orange should be called here, + // after the bug in #1740 + // componentDidMount() called setState({color:'yellow'}), which is async. + // The update doesn't happen until the next flush. + + ["componentDidMount-end", "orange"], + ["shouldComponentUpdate-currentState", "orange"], + ["shouldComponentUpdate-nextState", "yellow"], + ["componentWillUpdate-currentState", "orange"], + ["componentWillUpdate-nextState", "yellow"], + ["render", "yellow"], + ["componentDidUpdate-currentState", "yellow"], + ["componentDidUpdate-prevState", "orange"], + ["setState-sunrise", "yellow"], + ["setState-orange", "yellow"], + ["setState-yellow", "yellow"], + ["initial-callback", "yellow"], + ["componentWillReceiveProps-start", "yellow"], + // setState({color:'green'}) only enqueues a pending state. + ["componentWillReceiveProps-end", "yellow"], + // pending state queue is processed + // We keep updates in the queue to support + // replaceState(prevState => newState). + ["before-setState-receiveProps", "yellow"], + ["before-setState-again-receiveProps", void 666], + ["after-setState-receiveProps", "green"], + ["shouldComponentUpdate-currentState", "yellow"], + ["shouldComponentUpdate-nextState", "green"], + ["componentWillUpdate-currentState", "yellow"], + ["componentWillUpdate-nextState", "green"], + // setFavoriteColor('blue') + ["render", "green"], + ["componentDidUpdate-currentState", "green"], + ["componentDidUpdate-prevState", "yellow"], + ["setState-receiveProps", "green"], + ["setProps", "green"], + // setFavoriteColor('blue') + ["shouldComponentUpdate-currentState", "green"], + ["shouldComponentUpdate-nextState", "blue"], + ["componentWillUpdate-currentState", "green"], + ["componentWillUpdate-nextState", "blue"], + ["render", "blue"], + ["componentDidUpdate-currentState", "blue"], + ["componentDidUpdate-prevState", "green"], + ["setFavoriteColor", "blue"], + // forceUpdate() + ["componentWillUpdate-currentState", "blue"], + ["componentWillUpdate-nextState", "blue"], + ["render", "blue"], + ["componentDidUpdate-currentState", "blue"], + ["componentDidUpdate-prevState", "blue"], + ["forceUpdate", "blue"], + // unmountComponent() + // state is available within `componentWillUnmount()` + ["componentWillUnmount", "blue"] + ]; + + expect(stateListener.calls.join("\n")).toEqual(expected.join("\n")); + }); + + it("should call componentDidUpdate of children first", () => {}); + + it("should batch unmounts", () => { + var outer; + + class Inner extends React.Component { + render() { + return
; + } + + componentWillUnmount() { + // This should get silently ignored (maybe with a warning), but it + // shouldn't break React. + outer.setState({ showInner: false }); + } + } + + class Outer extends React.Component { + state = { showInner: true }; + + render() { + return
{this.state.showInner && }
; + } + } + + var container = document.createElement("div"); + outer = ReactDOM.render(, container); + expect(() => { + ReactDOM.unmountComponentAtNode(container); + }).not.toThrow(); + }); + + it("should update state when called from child cWRP", function() { + const log = []; + class Parent extends React.Component { + state = { value: "one" }; + render() { + log.push("parent render " + this.state.value); + return ; + } + } + let updated = false; + class Child extends React.Component { + componentWillReceiveProps() { + if (updated) { + return; + } + log.push("child componentWillReceiveProps " + this.props.value); + this.props.parent.setState({ value: "two" }); + log.push("child componentWillReceiveProps done " + this.props.value); + updated = true; + } + render() { + log.push("child render " + this.props.value); + return
{this.props.value}
; + } + } + var container = document.createElement("div"); + ReactDOM.render(, container); + ReactDOM.render(, container); + expect(log).toEqual([ + "parent render one", + "child render one", + "parent render one", + "child componentWillReceiveProps one", + "child componentWillReceiveProps done one", + "child render one", + "parent render two", + "child render two" + ]); + }); + + it("should merge state when sCU returns false", function() { + const log = []; + class Test extends React.Component { + state = { a: 0 }; + render() { + return null; + } + shouldComponentUpdate(nextProps, nextState) { + log.push("scu from " + Object.keys(this.state) + " to " + Object.keys(nextState)); + return false; + } + } + + const container = document.createElement("div"); + const test = ReactDOM.render(, container); + test.setState({ b: 0 }); + expect(log.length).toBe(1); + test.setState({ c: 0 }); + expect(log.length).toBe(2); + expect(log).toEqual(["scu from a to a,b", "scu from a,b to a,b,c"]); + }); + + it("should treat assigning to this.state inside cWRP as a replaceState, with a warning", () => { + spyOn(console, "error"); + + let ops = []; + class Test extends React.Component { + state = { step: 1, extra: true }; + componentWillReceiveProps() { + this.setState({ step: 2 }, () => { + // Tests that earlier setState callbacks are not dropped + ops.push(`callback -- step: ${this.state.step}, extra: ${!!this.state.extra}`); + }); + // Treat like replaceState + this.state = { step: 3 }; + } + render() { + ops.push(`render -- step: ${this.state.step}, extra: ${!!this.state.extra}`); + return null; + } + } + + // Mount + const container = document.createElement("div"); + ReactDOM.render(, container); + // Update + ReactDOM.render(, container); + + expect(ops).toEqual(["render -- step: 1, extra: true", "render -- step: 2, extra: false", "callback -- step: 2, extra: false"]); + /* + expect(console.error.calls.count()).toEqual(1); + expect(console.error.calls.argsFor(0)[0]).toEqual( + 'Warning: Test.componentWillReceiveProps(): Assigning directly to ' + + "this.state is deprecated (except inside a component's constructor). " + + 'Use setState instead.', + );*/ + }); +}); diff --git a/test/modules/ReactContextValidator-test.js b/test/modules/ReactContextValidator-test.jsx similarity index 92% rename from test/modules/ReactContextValidator-test.js rename to test/modules/ReactContextValidator-test.jsx index 178a8b519..89e6d371f 100644 --- a/test/modules/ReactContextValidator-test.js +++ b/test/modules/ReactContextValidator-test.jsx @@ -3,18 +3,14 @@ import getTestDocument from "./getTestDocument"; import ReactTestUtils from "lib/ReactTestUtils"; import PropTypes from 'lib/ReactPropTypes'; import ReactDOMServer from "dist/ReactDOMServer"; -// const chai = require('chai') -const spy = require('chai-spies') - -chai.use(spy) +var ReactDOM = window.ReactDOM || React; // https://github.com/facebook/react/blob/master/src/isomorphic/classic/__tests__/ReactContextValidator-test.js -var ReactDOM = window.ReactDOM || React; - -describe('ReactContextValidator', () => { +describe('ReactContextValidator', function() { + this.timeout(200000); function normalizeCodeLocInfo(str) { return str && str.replace(/\(at .+?:\d+\)/g, '(at **)'); } @@ -146,7 +142,8 @@ describe('ReactContextValidator', () => { }); it('should check context types', () => { - + spyOn(console, 'error') + class Component extends React.Component { render() { return
; @@ -157,12 +154,9 @@ describe('ReactContextValidator', () => { }; ReactTestUtils.renderIntoDocument(); - - var spy = chai.spy(); - window.console.error = spy; - + // PropTypes 为空实现所以没有报错 - expect(spy).not.to.have.been.called; + expect(console.error.calls.count()).toBe(0); class ComponentInFooStringContext extends React.Component { getChildContext() { @@ -184,7 +178,7 @@ describe('ReactContextValidator', () => { ); // PropTypes 为空实现所以没有报错 - expect(spy).not.to.have.been.called; + expect(console.error.calls.count()).toBe(0); class ComponentInFooNumberContext extends React.Component { getChildContext() { @@ -206,12 +200,11 @@ describe('ReactContextValidator', () => { ); // PropTypes 为空实现所以没有报错 - expect(spy).not.to.have.been.called; + expect(console.error.calls.count()).toBe(0); }); it('should check child context types', () => { - var spy = chai.spy(); - window.console.error = spy; + spyOn(console, 'error') class Component extends React.Component { getChildContext() { @@ -230,12 +223,13 @@ describe('ReactContextValidator', () => { ReactTestUtils.renderIntoDocument(); // PropTypes 为空实现所以没有报错 - expect(spy).not.to.have.been.called; + expect(console.error.calls.count()).toBe(0); ReactTestUtils.renderIntoDocument(); // PropTypes 为空实现所以没有报错 - expect(spy).not.to.have.been.called; + expect(console.error.calls.count()).toBe(0); + ReactTestUtils.renderIntoDocument( , @@ -244,14 +238,14 @@ describe('ReactContextValidator', () => { ReactTestUtils.renderIntoDocument(); // PropTypes 为空实现所以没有报错 - expect(spy).not.to.have.been.called; + expect(console.error.calls.count()).toBe(0); + }); // TODO (bvaughn) Remove this test and the associated behavior in the future. // It has only been added in Fiber to match the (unintentional) behavior in Stack. it('should warn (but not error) if getChildContext method is missing', () => { - var spy = chai.spy(); - window.console.error = spy; + spyOn(console, 'error') class ComponentA extends React.Component { static childContextTypes = { @@ -273,19 +267,22 @@ describe('ReactContextValidator', () => { ReactTestUtils.renderIntoDocument(); // PropTypes 为空实现所以没有报错 - expect(spy).not.to.have.been.called; + expect(console.error.calls.count()).toBe(0); + // Warnings should be deduped by component type ReactTestUtils.renderIntoDocument(); // PropTypes 为空实现所以没有报错 - expect(spy).not.to.have.been.called; + expect(console.error.calls.count()).toBe(0); + // PropTypes 为空实现所以没有报错 ReactTestUtils.renderIntoDocument(); // PropTypes 为空实现所以没有报错 - expect(spy).not.to.have.been.called; + expect(console.error.calls.count()).toBe(0); + }); // TODO (bvaughn) Remove this test and the associated behavior in the future. diff --git a/test/modules/ReactEmptyComponent-test.jsx b/test/modules/ReactEmptyComponent-test.jsx index 16cc059a5..8841b3192 100644 --- a/test/modules/ReactEmptyComponent-test.jsx +++ b/test/modules/ReactEmptyComponent-test.jsx @@ -10,7 +10,7 @@ describe("ReactComponent", function() { this.timeout(200000); - it("should not produce child DOM nodes for null and false", () => { + it("should not produce child DOM nodes for null and false", function() { class Component1 extends React.Component { render() { return null; diff --git a/test/modules/createReactClassIntegration-test.js b/test/modules/createReactClassIntegration-test.js deleted file mode 100644 index f8cc43d30..000000000 --- a/test/modules/createReactClassIntegration-test.js +++ /dev/null @@ -1,440 +0,0 @@ -/** - * Copyright (c) 2013-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @emails react-core - */ - -'use strict'; -import React from "dist/React"; -import getTestDocument from "./getTestDocument"; -import ReactTestUtils from "lib/ReactTestUtils"; -import createReactClass from "lib/createClass"; -import PropTypes from "lib/ReactPropTypes"; -import ReactDOM from "dist/React"; -import spy from "chai-spies"; - -chai.use(spy) - -describe('create-react-class-integration', () => { - - it('should throw when `render` is not specified', () => { - expect(function() { - createReactClass({}); - }).toThrowError( - '请实现render方法', - ); - }); - - it('should copy prop types onto the Constructor', () => { - var propValidator = function() {} - var TestComponent = createReactClass({ - propTypes: { - value: propValidator, - }, - render: function() { - return
; - }, - }); - - expect(TestComponent.propTypes).toBeDefined(); - expect(TestComponent.propTypes.value).toBe(propValidator); - }); - - it('should warn on invalid prop types', () => { - var spy = chai.spy(); - window.console.error = spy; - createReactClass({ - displayName: 'Component', - propTypes: { - prop: null, - }, - render: function() { - return {this.props.prop}; - }, - }); - expect(spy).to.have.been.called; - // expect(console.error.calls.count()).toBe(1); - // expect(console.error.calls.argsFor(0)[0]).toBe( - // 'Warning: Component: prop type `prop` is invalid; ' + - // 'it must be a function, usually from React.PropTypes.', - // ); - }); - - it('should warn on invalid context types', () => { - var spy = chai.spy(); - window.console.error = spy; - createReactClass({ - displayName: 'Component', - contextTypes: { - prop: null, - }, - render: function() { - return {this.props.prop}; - }, - }); - expect(spy).to.have.been.called; - // expect(console.error.calls.count()).toBe(1); - // expect(console.error.calls.argsFor(0)[0]).toBe( - // 'Warning: Component: context type `prop` is invalid; ' + - // 'it must be a function, usually from React.PropTypes.', - // ); - }); - - it('should throw on invalid child context types', () => { - var spy = chai.spy(); - window.console.error = spy; - createReactClass({ - displayName: 'Component', - childContextTypes: { - prop: null, - }, - render: function() { - return {this.props.prop}; - }, - }); - expect(spy).to.have.been.called; - // expect(console.error.calls.count()).toBe(1); - // expect(console.error.calls.argsFor(0)[0]).toBe( - // 'Warning: Component: child context type `prop` is invalid; ' + - // 'it must be a function, usually from React.PropTypes.', - // ); - }); - - it('should warn when mispelling shouldComponentUpdate', () => { - var spy = chai.spy(); - window.console.error = spy; - - createReactClass({ - componentShouldUpdate: function() { - return false; - }, - render: function() { - return
; - }, - }); - // 未实现相关功能 - expect(spy).not.to.have.been.called; - // expect(console.error.calls.count()).toBe(1); - // expect(console.error.calls.argsFor(0)[0]).toBe( - // 'Warning: A component has a method called componentShouldUpdate(). Did you ' + - // 'mean shouldComponentUpdate()? The name is phrased as a question ' + - // 'because the function is expected to return a value.', - // ); - - createReactClass({ - displayName: 'NamedComponent', - componentShouldUpdate: function() { - return false; - }, - render: function() { - return
; - }, - }); - // 未实现相关功能 - expect(spy).not.to.have.been.called; - // expect(console.error.calls.count()).toBe(2); - // expect(console.error.calls.argsFor(1)[0]).toBe( - // 'Warning: NamedComponent has a method called componentShouldUpdate(). Did you ' + - // 'mean shouldComponentUpdate()? The name is phrased as a question ' + - // 'because the function is expected to return a value.', - // ); - }); - - it('should warn when mispelling componentWillReceiveProps', () => { - var spy = chai.spy(); - window.console.error = spy; - createReactClass({ - componentWillRecieveProps: function() { - return false; - }, - render: function() { - return
; - }, - }); - // 未实现相关功能 - expect(spy).not.to.have.been.called; - // expect(console.error.calls.count()).toBe(1); - // expect(console.error.calls.argsFor(0)[0]).toBe( - // 'Warning: A component has a method called componentWillRecieveProps(). Did you ' + - // 'mean componentWillReceiveProps()?', - // ); - }); - - // TODO: Consider actually moving these to statics or drop this unit test. - - xit('should warn when using deprecated non-static spec keys', () => { - var spy = chai.spy(); - window.console.error = spy; - createReactClass({ - mixins: [{}], - propTypes: { - foo: PropTypes.string, - }, - contextTypes: { - foo: PropTypes.string, - }, - childContextTypes: { - foo: PropTypes.string, - }, - render: function() { - return
; - }, - }); - expect(spy).to.have.been.called.exactly(4); - // expect(console.error.calls.count()).toBe(4); - // expect(console.error.calls.argsFor(0)[0]).toBe( - // 'createClass(...): `mixins` is now a static property and should ' + - // 'be defined inside "statics".', - // ); - // expect(console.error.calls.argsFor(1)[0]).toBe( - // 'createClass(...): `propTypes` is now a static property and should ' + - // 'be defined inside "statics".', - // ); - // expect(console.error.calls.argsFor(2)[0]).toBe( - // 'createClass(...): `contextTypes` is now a static property and ' + - // 'should be defined inside "statics".', - // ); - // expect(console.error.calls.argsFor(3)[0]).toBe( - // 'createClass(...): `childContextTypes` is now a static property and ' + - // 'should be defined inside "statics".', - // ); - }); - - it('should support statics', () => { - var Component = createReactClass({ - statics: { - abc: 'def', - def: 0, - ghi: null, - jkl: 'mno', - pqr: function() { - return this; - }, - }, - - render: function() { - return ; - }, - }); - var instance = ; - instance = ReactTestUtils.renderIntoDocument(instance); - expect(instance.constructor.abc).toBe('def'); - expect(Component.abc).toBe('def'); - expect(instance.constructor.def).toBe(0); - expect(Component.def).toBe(0); - expect(instance.constructor.ghi).toBe(null); - expect(Component.ghi).toBe(null); - expect(instance.constructor.jkl).toBe('mno'); - expect(Component.jkl).toBe('mno'); - expect(instance.constructor.pqr()).toBe(Component); - expect(Component.pqr()).toBe(Component); - }); - - it('should work with object getInitialState() return values', () => { - var Component = createReactClass({ - getInitialState: function() { - return { - occupation: 'clown', - }; - }, - render: function() { - return ; - }, - }); - var instance = ; - instance = ReactTestUtils.renderIntoDocument(instance); - expect(instance.state.occupation).toEqual('clown'); - }); - - it('renders based on context getInitialState', () => { - var Foo = createReactClass({ - contextTypes: { - className: PropTypes.string, - }, - getInitialState() { - return {className: this.context.className}; - }, - render() { - return ; - }, - }); - - var Outer = createReactClass({ - childContextTypes: { - className: PropTypes.string, - }, - getChildContext() { - return {className: 'foo'}; - }, - render() { - return ; - }, - }); - - var container = document.createElement('div'); - ReactDOM.render(, container); - expect(container.firstChild.className).toBe('foo'); - }); - - it('should throw with non-object getInitialState() return values', () => { - [['an array'], 'a string', 1234].forEach(function(state) { - var Component = createReactClass({ - getInitialState: function() { - return state; - }, - render: function() { - return ; - }, - }); - var instance = ; - expect(function() { - instance = ReactTestUtils.renderIntoDocument(instance); - }).toThrowError( - 'Component.getInitialState(): must return an object or null', - ); - }); - }); - - it('should work with a null getInitialState() return value', () => { - var Component = createReactClass({ - getInitialState: function() { - return null; - }, - render: function() { - return ; - }, - }); - expect(() => - ReactTestUtils.renderIntoDocument(), - ).not.toThrow(); - }); - - it('should throw when using legacy factories', () => { - var spy = chai.spy(); - window.console.error = spy; - var Component = createReactClass({ - render() { - return
; - }, - }); - - // 暂时允许该方式调用 - expect(() => Component()).not.toThrow(); - expect(spy).to.have.been.called; - // expect(console.error.calls.count()).toBe(1); - // expect(console.error.calls.argsFor(0)[0]).toBe( - // 'Warning: Something is calling a React component directly. Use a ' + - // 'factory or JSX instead. See: https://fb.me/react-legacyfactory', - // ); - }); - - it('replaceState and callback works', () => { - var ops = []; - var Component = createReactClass({ - getInitialState() { - return {step: 0}; - }, - render() { - ops.push('Render: ' + this.state.step); - return
; - }, - }); - - var instance = ReactTestUtils.renderIntoDocument(); - instance.replaceState({step: 1}, () => { - ops.push('Callback: ' + instance.state.step); - }); - // replaceState 未实现 - expect(ops).not.toEqual(['Render: 0', 'Render: 1', 'Callback: 1']); - }); - - it('isMounted works', () => { - var spy = chai.spy(); - window.console.error = spy; - - var ops = []; - var instance; - var Component = createReactClass({ - displayName: 'MyComponent', - mixins: [ - { - componentWillMount() { - this.log('mixin.componentWillMount'); - }, - componentDidMount() { - this.log('mixin.componentDidMount'); - }, - componentWillUpdate() { - this.log('mixin.componentWillUpdate'); - }, - componentDidUpdate() { - this.log('mixin.componentDidUpdate'); - }, - componentWillUnmount() { - this.log('mixin.componentWillUnmount'); - }, - }, - ], - log(name) { - ops.push(`${name}: ${this.isMounted()}`); - }, - getInitialState() { - this.log('getInitialState'); - return {}; - }, - componentWillMount() { - this.log('componentWillMount'); - }, - componentDidMount() { - this.log('componentDidMount'); - }, - componentWillUpdate() { - this.log('componentWillUpdate'); - }, - componentDidUpdate() { - this.log('componentDidUpdate'); - }, - componentWillUnmount() { - this.log('componentWillUnmount'); - }, - render() { - instance = this; - this.log('render'); - return
; - }, - }); - - var container = document.createElement('div'); - ReactDOM.render(, container); - ReactDOM.render(, container); - ReactDOM.unmountComponentAtNode(container); - instance.log('after unmount'); - expect(ops).toEqual([ - 'getInitialState: false', - 'mixin.componentWillMount: false', - 'componentWillMount: false', - 'render: false', - 'mixin.componentDidMount: true', - 'componentDidMount: true', - 'mixin.componentWillUpdate: true', - 'componentWillUpdate: true', - 'render: true', - 'mixin.componentDidUpdate: true', - 'componentDidUpdate: true', - 'mixin.componentWillUnmount: true', - 'componentWillUnmount: true', - 'after unmount: false', - ]); - - expect(spy).to.have.been.called; - // expect(console.error.calls.count()).toBe(1); - // expect(console.error.calls.argsFor(0)[0]).toEqual( - // 'Warning: MyComponent: isMounted is deprecated. Instead, make sure to ' + - // 'clean up subscriptions and pending requests in componentWillUnmount ' + - // 'to prevent memory leaks.', - // ); - }); -}); \ No newline at end of file diff --git a/test/modules/createReactClassIntegration-test.jsx b/test/modules/createReactClassIntegration-test.jsx index d119ef706..4e87233f4 100644 --- a/test/modules/createReactClassIntegration-test.jsx +++ b/test/modules/createReactClassIntegration-test.jsx @@ -1,26 +1,26 @@ + + import React from "dist/React"; import getTestDocument from "./getTestDocument"; import ReactTestUtils from "lib/ReactTestUtils"; - -var createReactClass = React.createClass -var PropTypes = React.PropTypes - -//https://github.com/facebook/react/blob/master/src/isomorphic/children/__tests__/ReactChildren-test.js +import createReactClass from "lib/createClass"; +import PropTypes from "lib/ReactPropTypes"; var ReactDOM = window.ReactDOM || React; -describe("create-react-class-integration", function() { + +describe('create-react-class-integration', function() { this.timeout(200000); - + it('should throw when `render` is not specified', () => { expect(function() { createReactClass({}); }).toThrowError( - 'createClass(...): Class specification must implement a `render` method.', + '请实现render方法', ); }); it('should copy prop types onto the Constructor', () => { - var propValidator = function(){} + var propValidator = function() {} var TestComponent = createReactClass({ propTypes: { value: propValidator, @@ -35,7 +35,7 @@ describe("create-react-class-integration", function() { }); it('should warn on invalid prop types', () => { - spyOn(console, 'error'); + spyOn(console, 'error') createReactClass({ displayName: 'Component', propTypes: { @@ -45,12 +45,15 @@ describe("create-react-class-integration", function() { return {this.props.prop}; }, }); - expect(console.error.calls.count()).toBe(1); - + expect(console.error.calls.count()).toBe(1); + // expect(console.error.calls.argsFor(0)[0]).toBe( + // 'Warning: Component: prop type `prop` is invalid; ' + + // 'it must be a function, usually from React.PropTypes.', + // ); }); it('should warn on invalid context types', () => { - spyOn(console, 'error'); + spyOn(console, 'error') createReactClass({ displayName: 'Component', contextTypes: { @@ -61,9 +64,14 @@ describe("create-react-class-integration", function() { }, }); expect(console.error.calls.count()).toBe(1); + // expect(console.error.calls.argsFor(0)[0]).toBe( + // 'Warning: Component: context type `prop` is invalid; ' + + // 'it must be a function, usually from React.PropTypes.', + // ); }); + it('should throw on invalid child context types', () => { - spyOn(console, 'error'); + spyOn(console, 'error') createReactClass({ displayName: 'Component', childContextTypes: { @@ -74,6 +82,104 @@ describe("create-react-class-integration", function() { }, }); expect(console.error.calls.count()).toBe(1); + // expect(console.error.calls.count()).toBe(1); + // expect(console.error.calls.argsFor(0)[0]).toBe( + // 'Warning: Component: child context type `prop` is invalid; ' + + // 'it must be a function, usually from React.PropTypes.', + // ); + }); + + it('should warn when mispelling shouldComponentUpdate', () => { + spyOn(console, 'error') + + createReactClass({ + componentShouldUpdate: function() { + return false; + }, + render: function() { + return
; + }, + }); + // 未实现相关功能 + // expect(console.error.calls.count()).toBe(1); + // expect(console.error.calls.argsFor(0)[0]).toBe( + // 'Warning: A component has a method called componentShouldUpdate(). Did you ' + + // 'mean shouldComponentUpdate()? The name is phrased as a question ' + + // 'because the function is expected to return a value.', + // ); + + createReactClass({ + displayName: 'NamedComponent', + componentShouldUpdate: function() { + return false; + }, + render: function() { + return
; + }, + }); + // 未实现相关功能 + // expect(console.error.calls.count()).toBe(2); + // expect(console.error.calls.argsFor(1)[0]).toBe( + // 'Warning: NamedComponent has a method called componentShouldUpdate(). Did you ' + + // 'mean shouldComponentUpdate()? The name is phrased as a question ' + + // 'because the function is expected to return a value.', + // ); + }); + + it('should warn when mispelling componentWillReceiveProps', () => { + spyOn(console, 'error') + createReactClass({ + componentWillRecieveProps: function() { + return false; + }, + render: function() { + return
; + }, + }); + // 未实现相关功能 + // expect(console.error.calls.count()).toBe(1); + // expect(console.error.calls.argsFor(0)[0]).toBe( + // 'Warning: A component has a method called componentWillRecieveProps(). Did you ' + + // 'mean componentWillReceiveProps()?', + // ); + }); + + // TODO: Consider actually moving these to statics or drop this unit test. + +it('should warn when using deprecated non-static spec keys', () => { + spyOn(console, 'error') + createReactClass({ + mixins: [{}], + propTypes: { + foo: PropTypes.string, + }, + contextTypes: { + foo: PropTypes.string, + }, + childContextTypes: { + foo: PropTypes.string, + }, + render: function() { + return
; + }, + }); + // expect(console.error.calls.count()).toBe(4); + // expect(console.error.calls.argsFor(0)[0]).toBe( + // 'createClass(...): `mixins` is now a static property and should ' + + // 'be defined inside "statics".', + // ); + // expect(console.error.calls.argsFor(1)[0]).toBe( + // 'createClass(...): `propTypes` is now a static property and should ' + + // 'be defined inside "statics".', + // ); + // expect(console.error.calls.argsFor(2)[0]).toBe( + // 'createClass(...): `contextTypes` is now a static property and ' + + // 'should be defined inside "statics".', + // ); + // expect(console.error.calls.argsFor(3)[0]).toBe( + // 'createClass(...): `childContextTypes` is now a static property and ' + + // 'should be defined inside "statics".', + // ); }); it('should support statics', () => { @@ -106,7 +212,6 @@ describe("create-react-class-integration", function() { expect(Component.pqr()).toBe(Component); }); - it('should work with object getInitialState() return values', () => { var Component = createReactClass({ getInitialState: function() { @@ -172,13 +277,32 @@ describe("create-react-class-integration", function() { }); }); - it('replaceState is deprecated', () => { - spyOn(console, 'error'); + it('should work with a null getInitialState() return value', () => { + var Component = createReactClass({ + getInitialState: function() { + return null; + }, + render: function() { + return ; + }, + }); + expect(() => + ReactTestUtils.renderIntoDocument(), + ).not.toThrow(); + }); + + it('should throw when using legacy factories', () => { + + }); + + it('replaceState and callback works', () => { + var ops = []; var Component = createReactClass({ getInitialState() { return {step: 0}; }, render() { + ops.push('Render: ' + this.state.step); return
; }, }); @@ -187,12 +311,12 @@ describe("create-react-class-integration", function() { instance.replaceState({step: 1}, () => { ops.push('Callback: ' + instance.state.step); }); - expect(console.error.calls.count()).toBe(1); + // replaceState 未实现 + expect(ops).not.toEqual(['Render: 0', 'Render: 1', 'Callback: 1']); }); it('isMounted works', () => { - spyOn(console, 'error'); - + spyOn(console, 'error') var ops = []; var instance; var Component = createReactClass({ @@ -267,6 +391,11 @@ describe("create-react-class-integration", function() { 'after unmount: false', ]); - expect(console.error.calls.count()).toBe(1); - }) -}) \ No newline at end of file + expect(console.error.calls.count()).toBe(0); + // expect(console.error.calls.argsFor(0)[0]).toEqual( + // 'Warning: MyComponent: isMounted is deprecated. Instead, make sure to ' + + // 'clean up subscriptions and pending requests in componentWillUnmount ' + + // 'to prevent memory leaks.', + // ); + }); +}); \ No newline at end of file diff --git a/test/spec.js b/test/spec.js index f3e426604..7e52a4454 100644 --- a/test/spec.js +++ b/test/spec.js @@ -1,55 +1,63 @@ -// import "./modules/createElement.spec"; +import "./modules/createElement.spec"; -// import "./modules/util.spec"; -// import "./modules/style.spec"; +import "./modules/util.spec"; +import "./modules/style.spec"; -// import "./modules/classInherit.spec"; -// import "./modules/shallow.spec"; -// import "./modules/browser.spec"; -// import "./modules/cloneElement.spec"; +import "./modules/classInherit.spec"; +import "./modules/shallow.spec"; +import "./modules/browser.spec"; +import "./modules/cloneElement.spec"; -// require("./modules/context.spec.jsx"); +require("./modules/context.spec.jsx"); -// require("./modules/component.spec.jsx"); -// require("./modules/lifecycle.spec.jsx"); +require("./modules/component.spec.jsx"); +require("./modules/lifecycle.spec.jsx"); -// require("./modules/svg.spec.jsx"); +require("./modules/svg.spec.jsx"); -// require("./modules/diffProps.spec.jsx"); +require("./modules/diffProps.spec.jsx"); -// require("./modules/event.spec.jsx"); +require("./modules/event.spec.jsx"); -// require("./modules/node.spec.jsx"); +require("./modules/node.spec.jsx"); -// require("./modules/ref.spec.jsx"); +require("./modules/ref.spec.jsx"); -// require("./modules/redux.spec.jsx"); +require("./modules/redux.spec.jsx"); -// require("./modules/ReactTestUtils-test.jsx"); -// require("./modules/ReactComponent-test.jsx"); +require("./modules/ReactTestUtils-test.jsx"); +require("./modules/ReactComponent-test.jsx"); -// require("./modules/ReactChildren-test.jsx"); +require("./modules/ReactChildren-test.jsx"); -// require("./modules/createReactClassIntegration-test.jsx"); -// require("./modules/ReactMultiChild-test.jsx"); +//require("./modules/createReactClassIntegration-test.jsx"); +require("./modules/ReactMultiChild-test.jsx"); -// require("./modules/refs-test.jsx"); -// require("./modules/refs-destruction-test.jsx"); -// require("./modules/ReactUpdates-test.jsx"); -// require("./modules/ReactStatelessComponent-test.jsx"); +require("./modules/refs-test.jsx"); +require("./modules/refs-destruction-test.jsx"); +require("./modules/ReactUpdates-test.jsx"); +require("./modules/ReactStatelessComponent-test.jsx"); -// require("./modules/ReactEmptyComponent-test.jsx"); -// require("./modules/ReactIdentity-test.jsx"); -// require("./modules/ReactCompositeComponentNestedState-test.jsx"); +require("./modules/ReactEmptyComponent-test.jsx"); +require("./modules/ReactIdentity-test.jsx"); +require("./modules/ReactCompositeComponentNestedState-test.jsx"); -// require("./modules/ReactComponentLifeCycle-test.jsx"); +require("./modules/ReactComponentLifeCycle-test.jsx"); + +require("./modules/ReactCompositeComponent-test.jsx"); + +require("./modules/ReactContextValidator-test.jsx"); + +require("./modules/createReactClassIntegration-test.jsx"); + +require("./modules/ReactCompositeComponentDOMMinimalism-test.jsx"); + +require("./modules/ReactCompositeComponentState-test.jsx"); + -// require("./modules/ReactCompositeComponent-test.jsx"); -// require("./modules/ReactContextValidator-test.js"); -require("./modules/createReactClassIntegration-test.js"); From a9bd18c8f10f0749d500593e2548230cb9226b8b Mon Sep 17 00:00:00 2001 From: RubyLouvre <1669866773@qq.com> Date: Sun, 1 Oct 2017 09:14:25 +0800 Subject: [PATCH 24/57] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=20=E5=BF=BD=E7=95=A5?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=97=B6=EF=BC=8C=E7=BB=84=E4=BB=B6=E7=9A=84?= =?UTF-8?q?=E8=99=9A=E6=8B=9FDOM=E7=9A=84=E5=9B=9E=E6=BB=9A=E6=9C=BA?= =?UTF-8?q?=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build/shim.script.js | 2 +- dist/React.js | 17 +- dist/ReactIE.js | 17 +- dist/ReactSelection.js | 17 +- dist/ReactShim.js | 17 +- index3.html | 44 +- index4.html | 94 +- index5.html | 12 +- src/Component.js | 2 +- src/diff.js | 18 +- test/modules/ReactCompositeComponent-test.jsx | 2319 +++++++++-------- 11 files changed, 1356 insertions(+), 1203 deletions(-) diff --git a/build/shim.script.js b/build/shim.script.js index 6f1669f16..8b5f966ad 100644 --- a/build/shim.script.js +++ b/build/shim.script.js @@ -19,7 +19,7 @@ var text2 = str2 fs.writeFileSync(dir2, text2, { encoding: "utf8" }); //fs.writeFileSync( path.join(__dirname, "../../draft/node_modules/anujs/dist/React.js"), text2, { encoding: "utf8" }); -//fs.writeFileSync( path.join(__dirname, "../../antd-test/node_modules/anujs/dist/React.js"), text2, { encoding: "utf8" }); +fs.writeFileSync( path.join(__dirname, "../../antd-test/node_modules/anujs/dist/React.js"), text2, { encoding: "utf8" }); //fs.writeFileSync( path.join(__dirname, "../../yo-demo/node_modules/anujs/dist/React.js"), text2, { encoding: "utf8" }); //fs.writeFileSync( path.join(__dirname, "../../yo-router/node_modules/anujs/dist/React.js"), text2, { encoding: "utf8" }); diff --git a/dist/React.js b/dist/React.js index 480402014..5d1b317b2 100644 --- a/dist/React.js +++ b/dist/React.js @@ -1,5 +1,5 @@ /** - * by 司徒正美 Copyright 2017-09-30 + * by 司徒正美 Copyright 2017-10-01 * IE9+ */ @@ -1026,7 +1026,7 @@ function Component(props, context) { this.__pendingCallbacks = []; this.__pendingStates = []; this.__current = noop; //用于DevTools工具中,通过实例找到生成它的那个虚拟DOM - this.__lifestage = 0; //判断生命周期 + this.__lifeStage = 0; //判断生命周期 /* * this.__dom = dom 用于isMounted或ReactDOM.findDOMNode方法 * this.__hydrating = true 表示组件正在根据虚拟DOM合成真实DOM @@ -2290,8 +2290,8 @@ function renderComponent(instance, vnode, cb, rendered) { } vnode._instance = instance; + instance.lastRendered = instance.__rendered; instance.__rendered = rendered; - var parentContext = vnode.parentContext; var childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; var dom = cb(rendered, childContext); @@ -2330,6 +2330,7 @@ function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { lastVnode.ref = nextVnode.ref; } } + instance.lastVnode = lastVnode; instance.nextVnode = nextVnode; nextVnode.context = nextContext; nextVnode.parentContext = context; @@ -2362,6 +2363,7 @@ function refreshComponent(instance, updateQueue) { nextVnode._instance = instance; //important + var nextState = instance.__mergeStates(nextProps, nextContext); var shouldUpdate = true; if (!instance.__forceUpdate && instance.shouldComponentUpdate && !instance.shouldComponentUpdate(nextProps, nextState, nextContext)) { @@ -2374,7 +2376,15 @@ function refreshComponent(instance, updateQueue) { instance.props = nextProps; instance.context = nextContext; instance.state = nextState; //既然setState了,无论shouldComponentUpdate结果如何,用户传给的state对象都会作用到组件上 + if (!shouldUpdate) { + var lastVnode = instance.lastVnode; + for (var i in lastVnode) { + if (!nextVnode.hasOwnProperty(i)) { + nextVnode[i] = lastVnode[i]; + } + } + instance.__current = nextVnode; return dom; } instance.__hydrating = true; @@ -2457,6 +2467,7 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { childNodes = parentNode.childNodes; lastChildren.forEach(function (el, i) { if (childNodes[i] !== el._hostNode) { + parentNode.replaceChild(el._hostNode, childNodes[i]); } }); diff --git a/dist/ReactIE.js b/dist/ReactIE.js index 23f213795..9374b0a0d 100644 --- a/dist/ReactIE.js +++ b/dist/ReactIE.js @@ -1,5 +1,5 @@ /** - * IE6+,有问题请加QQ 370262116 by 司徒正美 Copyright 2017-09-30 + * IE6+,有问题请加QQ 370262116 by 司徒正美 Copyright 2017-10-01 */ (function (global, factory) { @@ -1025,7 +1025,7 @@ function Component(props, context) { this.__pendingCallbacks = []; this.__pendingStates = []; this.__current = noop; //用于DevTools工具中,通过实例找到生成它的那个虚拟DOM - this.__lifestage = 0; //判断生命周期 + this.__lifeStage = 0; //判断生命周期 /* * this.__dom = dom 用于isMounted或ReactDOM.findDOMNode方法 * this.__hydrating = true 表示组件正在根据虚拟DOM合成真实DOM @@ -2289,8 +2289,8 @@ function renderComponent(instance, vnode, cb, rendered) { } vnode._instance = instance; + instance.lastRendered = instance.__rendered; instance.__rendered = rendered; - var parentContext = vnode.parentContext; var childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; var dom = cb(rendered, childContext); @@ -2329,6 +2329,7 @@ function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { lastVnode.ref = nextVnode.ref; } } + instance.lastVnode = lastVnode; instance.nextVnode = nextVnode; nextVnode.context = nextContext; nextVnode.parentContext = context; @@ -2361,6 +2362,7 @@ function refreshComponent(instance, updateQueue) { nextVnode._instance = instance; //important + var nextState = instance.__mergeStates(nextProps, nextContext); var shouldUpdate = true; if (!instance.__forceUpdate && instance.shouldComponentUpdate && !instance.shouldComponentUpdate(nextProps, nextState, nextContext)) { @@ -2373,7 +2375,15 @@ function refreshComponent(instance, updateQueue) { instance.props = nextProps; instance.context = nextContext; instance.state = nextState; //既然setState了,无论shouldComponentUpdate结果如何,用户传给的state对象都会作用到组件上 + if (!shouldUpdate) { + var lastVnode = instance.lastVnode; + for (var i in lastVnode) { + if (!nextVnode.hasOwnProperty(i)) { + nextVnode[i] = lastVnode[i]; + } + } + instance.__current = nextVnode; return dom; } instance.__hydrating = true; @@ -2456,6 +2466,7 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { childNodes = parentNode.childNodes; lastChildren.forEach(function (el, i) { if (childNodes[i] !== el._hostNode) { + parentNode.replaceChild(el._hostNode, childNodes[i]); } }); diff --git a/dist/ReactSelection.js b/dist/ReactSelection.js index 23f213795..9374b0a0d 100644 --- a/dist/ReactSelection.js +++ b/dist/ReactSelection.js @@ -1,5 +1,5 @@ /** - * IE6+,有问题请加QQ 370262116 by 司徒正美 Copyright 2017-09-30 + * IE6+,有问题请加QQ 370262116 by 司徒正美 Copyright 2017-10-01 */ (function (global, factory) { @@ -1025,7 +1025,7 @@ function Component(props, context) { this.__pendingCallbacks = []; this.__pendingStates = []; this.__current = noop; //用于DevTools工具中,通过实例找到生成它的那个虚拟DOM - this.__lifestage = 0; //判断生命周期 + this.__lifeStage = 0; //判断生命周期 /* * this.__dom = dom 用于isMounted或ReactDOM.findDOMNode方法 * this.__hydrating = true 表示组件正在根据虚拟DOM合成真实DOM @@ -2289,8 +2289,8 @@ function renderComponent(instance, vnode, cb, rendered) { } vnode._instance = instance; + instance.lastRendered = instance.__rendered; instance.__rendered = rendered; - var parentContext = vnode.parentContext; var childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; var dom = cb(rendered, childContext); @@ -2329,6 +2329,7 @@ function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { lastVnode.ref = nextVnode.ref; } } + instance.lastVnode = lastVnode; instance.nextVnode = nextVnode; nextVnode.context = nextContext; nextVnode.parentContext = context; @@ -2361,6 +2362,7 @@ function refreshComponent(instance, updateQueue) { nextVnode._instance = instance; //important + var nextState = instance.__mergeStates(nextProps, nextContext); var shouldUpdate = true; if (!instance.__forceUpdate && instance.shouldComponentUpdate && !instance.shouldComponentUpdate(nextProps, nextState, nextContext)) { @@ -2373,7 +2375,15 @@ function refreshComponent(instance, updateQueue) { instance.props = nextProps; instance.context = nextContext; instance.state = nextState; //既然setState了,无论shouldComponentUpdate结果如何,用户传给的state对象都会作用到组件上 + if (!shouldUpdate) { + var lastVnode = instance.lastVnode; + for (var i in lastVnode) { + if (!nextVnode.hasOwnProperty(i)) { + nextVnode[i] = lastVnode[i]; + } + } + instance.__current = nextVnode; return dom; } instance.__hydrating = true; @@ -2456,6 +2466,7 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { childNodes = parentNode.childNodes; lastChildren.forEach(function (el, i) { if (childNodes[i] !== el._hostNode) { + parentNode.replaceChild(el._hostNode, childNodes[i]); } }); diff --git a/dist/ReactShim.js b/dist/ReactShim.js index 096fea91a..f3fc4d67c 100644 --- a/dist/ReactShim.js +++ b/dist/ReactShim.js @@ -1,7 +1,7 @@ /** * 此版本要求浏览器没有createClass, createFactory, PropTypes, isValidElement, * unmountComponentAtNode,unstable_renderSubtreeIntoContainer - * QQ 370262116 by 司徒正美 Copyright 2017-09-30 + * QQ 370262116 by 司徒正美 Copyright 2017-10-01 */ (function (global, factory) { @@ -464,7 +464,7 @@ function Component(props, context) { this.__pendingCallbacks = []; this.__pendingStates = []; this.__current = noop; //用于DevTools工具中,通过实例找到生成它的那个虚拟DOM - this.__lifestage = 0; //判断生命周期 + this.__lifeStage = 0; //判断生命周期 /* * this.__dom = dom 用于isMounted或ReactDOM.findDOMNode方法 * this.__hydrating = true 表示组件正在根据虚拟DOM合成真实DOM @@ -2131,8 +2131,8 @@ function renderComponent(instance, vnode, cb, rendered) { } vnode._instance = instance; + instance.lastRendered = instance.__rendered; instance.__rendered = rendered; - var parentContext = vnode.parentContext; var childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; var dom = cb(rendered, childContext); @@ -2171,6 +2171,7 @@ function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { lastVnode.ref = nextVnode.ref; } } + instance.lastVnode = lastVnode; instance.nextVnode = nextVnode; nextVnode.context = nextContext; nextVnode.parentContext = context; @@ -2203,6 +2204,7 @@ function refreshComponent(instance, updateQueue) { nextVnode._instance = instance; //important + var nextState = instance.__mergeStates(nextProps, nextContext); var shouldUpdate = true; if (!instance.__forceUpdate && instance.shouldComponentUpdate && !instance.shouldComponentUpdate(nextProps, nextState, nextContext)) { @@ -2215,7 +2217,15 @@ function refreshComponent(instance, updateQueue) { instance.props = nextProps; instance.context = nextContext; instance.state = nextState; //既然setState了,无论shouldComponentUpdate结果如何,用户传给的state对象都会作用到组件上 + if (!shouldUpdate) { + var lastVnode = instance.lastVnode; + for (var i in lastVnode) { + if (!nextVnode.hasOwnProperty(i)) { + nextVnode[i] = lastVnode[i]; + } + } + instance.__current = nextVnode; return dom; } instance.__hydrating = true; @@ -2298,6 +2308,7 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { childNodes = parentNode.childNodes; lastChildren.forEach(function (el, i) { if (childNodes[i] !== el._hostNode) { + parentNode.replaceChild(el._hostNode, childNodes[i]); } }); diff --git a/index3.html b/index3.html index 7dc41cd7a..03feada14 100644 --- a/index3.html +++ b/index3.html @@ -9,12 +9,44 @@ diff --git a/index4.html b/index4.html index 8724e63b6..154651584 100644 --- a/index4.html +++ b/index4.html @@ -4,6 +4,8 @@ + + @@ -25,29 +27,85 @@ window.ReactDOM = window.React } + var expect = function(a) { + return { + toBe: function(b) { + console.log(a, b, a === b) + } + } + } var container = document.createElement('div'); + var s document.body.appendChild(container); const log = []; - class Component extends React.Component { - constructor(props){ - super(props) - this.state = { - a: 1 - } - } - render() { - return
; - } + var renderCalls = 0; + class PlasticWrap extends React.Component { + constructor(props, context) { + super(props, context); + this.state = { + color: "green" + }; + } + + render() { + return ; + } } - - var a = new Component() - var oldState = a.state - var newState = { - a: 2 + + class Apple extends React.Component { + state = { + cut: false, + slices: 1 + }; + + shouldComponentUpdate(nextProps, nextState) { + return shallowCompare(this, nextProps, nextState); + } + + cut() { + this.setState({ + cut: true, + slices: 10 + }); + } + + eatSlice() { + this.setState({ + slices: this.state.slices - 1 + }); + } + + render() { + renderCalls++; + return
; + } } - a.setState(newState) - console.log(oldState == a.state) - console.log(newState == a.state) + + var container = document.createElement("div"); + var instance = ReactDOM.render(, container); + expect(renderCalls).toBe(1); + + // Do not re-render based on props + instance.setState({ color: "green" }); + expect(renderCalls).toBe(1); + + // Re-render based on props + instance.setState({ color: "red" }); + expect(renderCalls).toBe(2); + + // Re-render base on state + instance.refs.apple.cut(); + expect(renderCalls).toBe(3); + + // No re-render based on state + instance.refs.apple.cut(); + expect(renderCalls).toBe(3); + + // Re-render based on state again + instance.refs.apple.eatSlice(); + expect(renderCalls).toBe(4); + + diff --git a/index5.html b/index5.html index 7602f0438..29ab969b6 100644 --- a/index5.html +++ b/index5.html @@ -8,8 +8,8 @@ - + + @@ -42,15 +42,15 @@ } } - const test = ReactDOM.render(, container); - test.setState({ b: 0 }); + const s = ReactDOM.render(, container); + s.setState({ b: 0 }); expect(log.length).toBe(1); - test.setState({ c: 0 }); + s.setState({ c: 0 }); expect(log.length).toBe(2); // expect(log).toEqual(["scu from a to a,b", "scu from a,b to a,b,c"]); - console.log(JSON.stringify(log)) + console.log(s) diff --git a/src/Component.js b/src/Component.js index 28d8a9fc7..0a00e303b 100644 --- a/src/Component.js +++ b/src/Component.js @@ -19,7 +19,7 @@ export function Component(props, context) { this.__pendingCallbacks = []; this.__pendingStates = []; this.__current = noop; //用于DevTools工具中,通过实例找到生成它的那个虚拟DOM - this.__lifestage = 0; //判断生命周期 + this.__lifeStage = 0; //判断生命周期 /* * this.__dom = dom 用于isMounted或ReactDOM.findDOMNode方法 * this.__hydrating = true 表示组件正在根据虚拟DOM合成真实DOM diff --git a/src/diff.js b/src/diff.js index 5c1a71bff..fa511df9d 100644 --- a/src/diff.js +++ b/src/diff.js @@ -318,8 +318,8 @@ function renderComponent(instance, vnode, cb, rendered) { } vnode._instance = instance; + instance.lastRendered = instance.__rendered; instance.__rendered = rendered; - let parentContext = vnode.parentContext; let childContext = rendered.vtype ? getChildContext(instance, parentContext) @@ -353,6 +353,7 @@ function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { lastVnode.ref = nextVnode.ref; } } + instance.lastVnode = lastVnode; instance.nextVnode = nextVnode; nextVnode.context = nextContext; nextVnode.parentContext = context; @@ -385,7 +386,8 @@ function refreshComponent(instance, updateQueue) { let vparent = nextVnode.vparent; nextVnode._instance = instance; //important - + + let nextState = instance.__mergeStates(nextProps, nextContext); let shouldUpdate = true; if ( @@ -403,8 +405,16 @@ function refreshComponent(instance, updateQueue) { instance.props = nextProps; instance.context = nextContext; instance.state = nextState;//既然setState了,无论shouldComponentUpdate结果如何,用户传给的state对象都会作用到组件上 + if(!shouldUpdate){ - return dom; + var lastVnode = instance.lastVnode; + for(var i in lastVnode){ + if(!nextVnode.hasOwnProperty(i)){ + nextVnode[i] = lastVnode[i]; + } + } + instance.__current = nextVnode; + return dom; } instance.__hydrating = true; instance.lastProps = lastProps; @@ -502,7 +512,9 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { childNodes = parentNode.childNodes; lastChildren.forEach(function(el, i) { if (childNodes[i] !== el._hostNode) { + parentNode.replaceChild(el._hostNode, childNodes[i]); + } }); //如果旧数组长度为零 diff --git a/test/modules/ReactCompositeComponent-test.jsx b/test/modules/ReactCompositeComponent-test.jsx index 691368be0..e4feb80d3 100644 --- a/test/modules/ReactCompositeComponent-test.jsx +++ b/test/modules/ReactCompositeComponent-test.jsx @@ -3,25 +3,22 @@ import getTestDocument from "./getTestDocument"; import ReactTestUtils from "lib/ReactTestUtils"; import ReactDOMServer from "dist/ReactDOMServer"; -var shallowCompare = require( "../../lib/shallowCompare"); +var shallowCompare = require("../../lib/shallowCompare"); //shallowCompare = shallowCompare.shallowCompare - - //https://github.com/facebook/react/blob/master/src/isomorphic/children/__tests__/ReactChildren-test.js var ReactDOM = window.ReactDOM || React; -var PropTypes = React.PropTypes +var PropTypes = React.PropTypes; describe("ReactCompositeComponent", function() { this.timeout(200000); - it("should support module pattern components", () => { - function Child({test}) { + function Child({ test }) { return { render() { return
{test}
; - }, + } }; } @@ -45,18 +42,16 @@ describe("ReactCompositeComponent", function() { expect(el.tagName).toBe("A"); }); var MorphingComponent = class extends React.Component { - state = {activated: false}; - - _toggleActivatedState = () => { - this.setState({activated: !this.state.activated}); - }; - - render() { - var toggleActivatedState = this._toggleActivatedState; - return !this.state.activated - ? - : ; - } + state = { activated: false }; + + _toggleActivatedState = () => { + this.setState({ activated: !this.state.activated }); + }; + + render() { + var toggleActivatedState = this._toggleActivatedState; + return !this.state.activated ? : ; + } }; it("should react to state changes from callbacks", () => { var instance = ReactTestUtils.renderIntoDocument(); @@ -68,582 +63,566 @@ describe("ReactCompositeComponent", function() { expect(el.tagName).toBe("B"); }); + it("should rewire refs when rendering to different child types", () => { + var instance = ReactTestUtils.renderIntoDocument(); + expect(ReactDOM.findDOMNode(instance.refs.x).tagName).toBe("A"); + instance._toggleActivatedState(); + expect(ReactDOM.findDOMNode(instance.refs.x).tagName).toBe("B"); + instance._toggleActivatedState(); + expect(ReactDOM.findDOMNode(instance.refs.x).tagName).toBe("A"); + }); + var ChildUpdates = class extends React.Component { + getAnchor = () => { + return this.refs.anch; + }; - it('should rewire refs when rendering to different child types', () => { - var instance = ReactTestUtils.renderIntoDocument(); - - expect(ReactDOM.findDOMNode(instance.refs.x).tagName).toBe('A'); - instance._toggleActivatedState(); - expect(ReactDOM.findDOMNode(instance.refs.x).tagName).toBe('B'); - instance._toggleActivatedState(); - expect(ReactDOM.findDOMNode(instance.refs.x).tagName).toBe('A'); - }); - var ChildUpdates = class extends React.Component { - getAnchor = () => { - return this.refs.anch; + render() { + var className = this.props.anchorClassOn ? "anchorClass" : ""; + return this.props.renderAnchor ? : ; + } }; + it("should not cache old DOM nodes when switching constructors", () => { + var container = document.createElement("div"); + var instance = ReactDOM.render(, container); + ReactDOM.render( + // Warm any cache + , + container + ); + ReactDOM.render( + // Clear out the anchor + , + container + ); + ReactDOM.render( + // rerender + , + container + ); + expect(instance.getAnchor().className).toBe(""); + }); - render() { - var className = this.props.anchorClassOn ? 'anchorClass' : ''; - return this.props.renderAnchor - ? - : ; - } - }; - it('should not cache old DOM nodes when switching constructors', () => { - var container = document.createElement('div'); - var instance = ReactDOM.render( - , - container, - ); - ReactDOM.render( - // Warm any cache - , - container, - ); - ReactDOM.render( - // Clear out the anchor - , - container, - ); - ReactDOM.render( - // rerender - , - container, - ); - expect(instance.getAnchor().className).toBe(''); - }); - - it('should use default values for undefined props', () => { - class Component extends React.Component { - static defaultProps = {prop: 'testKey'}; - - render() { - return ; - } - } - - var instance1 = ReactTestUtils.renderIntoDocument(); - expect(instance1.props).toEqual({prop: 'testKey'}); - - var instance2 = ReactTestUtils.renderIntoDocument( - , - ); - expect(instance2.props).toEqual({prop: 'testKey'}); - - var instance3 = ReactTestUtils.renderIntoDocument( - , - ); - expect(instance3.props).toEqual({prop: null}); - }); - - - it('should not mutate passed-in props object', () => { - class Component extends React.Component { - static defaultProps = {prop: 'testKey'}; - - render() { - return ; - } - } - - var inputProps = {}; - var instance1 = ; - instance1 = ReactTestUtils.renderIntoDocument(instance1); - expect(instance1.props.prop).toBe('testKey'); - - // We don't mutate the input, just in case the caller wants to do something - // with it after using it to instantiate a component - expect(inputProps.prop).toBe(void 666); - }); - - it('should warn about `forceUpdate` on unmounted components', () => { - - var container = document.createElement('div'); - document.body.appendChild(container); - - class Component extends React.Component { - render() { - return
; - } - } - - var instance = ; - expect(instance.forceUpdate).toBe(void 666) - - instance = ReactDOM.render(instance, container); - instance.forceUpdate(); - - ReactDOM.unmountComponentAtNode(container); - - instance.forceUpdate(); - }); - - it('should warn about `setState` on unmounted components', () => { - - var container = document.createElement('div'); - document.body.appendChild(container); - - var renders = 0; - - class Component extends React.Component { - state = {value: 0}; - - render() { - renders++; - return
; - } - } + it("should use default values for undefined props", () => { + class Component extends React.Component { + static defaultProps = { prop: "testKey" }; - var instance = ; - expect(instance.setState).toBe(void 666) + render() { + return ; + } + } - instance = ReactDOM.render(instance, container); + var instance1 = ReactTestUtils.renderIntoDocument(); + expect(instance1.props).toEqual({ prop: "testKey" }); - expect(renders).toBe(1); + var instance2 = ReactTestUtils.renderIntoDocument(); + expect(instance2.props).toEqual({ prop: "testKey" }); - instance.setState({value: 1}); + var instance3 = ReactTestUtils.renderIntoDocument(); + expect(instance3.props).toEqual({ prop: null }); + }); + it("should not mutate passed-in props object", () => { + class Component extends React.Component { + static defaultProps = { prop: "testKey" }; - expect(renders).toBe(2); + render() { + return ; + } + } - ReactDOM.unmountComponentAtNode(container); - instance.setState({value: 2}); + var inputProps = {}; + var instance1 = ; + instance1 = ReactTestUtils.renderIntoDocument(instance1); + expect(instance1.props.prop).toBe("testKey"); - expect(renders).toBe(2); + // We don't mutate the input, just in case the caller wants to do something + // with it after using it to instantiate a component + expect(inputProps.prop).toBe(void 666); + }); + + it("should warn about `forceUpdate` on unmounted components", () => { + var container = document.createElement("div"); + document.body.appendChild(container); + + class Component extends React.Component { + render() { + return
; + } + } + + var instance = ; + expect(instance.forceUpdate).toBe(void 666); + instance = ReactDOM.render(instance, container); + instance.forceUpdate(); - }); + ReactDOM.unmountComponentAtNode(container); - it('should silently allow `setState`, not call cb on unmounting components', () => { - var cbCalled = false; - var container = document.createElement('div'); - document.body.appendChild(container); + instance.forceUpdate(); + }); - class Component extends React.Component { - state = {value: 0}; + it("should warn about `setState` on unmounted components", () => { + var container = document.createElement("div"); + document.body.appendChild(container); - componentWillUnmount() { - expect(() => { - this.setState({value: 2}, function() { - cbCalled = true; - }); - }).not.toThrow(); - } + var renders = 0; - render() { - return
; - } - } + class Component extends React.Component { + state = { value: 0 }; - var instance = ReactDOM.render(, container); - instance.setState({value: 1}); + render() { + renders++; + return
; + } + } - ReactDOM.unmountComponentAtNode(container); - expect(cbCalled).toBe(false); - }); + var instance = ; + expect(instance.setState).toBe(void 666); - it('should warn about `setState` in render', () => { - spyOn(console, 'error'); + instance = ReactDOM.render(instance, container); - var container = document.createElement('div'); + expect(renders).toBe(1); - var renderedState = -1; - var renderPasses = 0; + instance.setState({ value: 1 }); - class Component extends React.Component { - state = {value: 0}; + expect(renders).toBe(2); - render() { - renderPasses++; - renderedState = this.state.value; - if (this.state.value === 0) { - this.setState({value: 1}); + ReactDOM.unmountComponentAtNode(container); + instance.setState({ value: 2 }); + + expect(renders).toBe(2); + }); + + it("should silently allow `setState`, not call cb on unmounting components", () => { + var cbCalled = false; + var container = document.createElement("div"); + document.body.appendChild(container); + + class Component extends React.Component { + state = { value: 0 }; + + componentWillUnmount() { + expect(() => { + this.setState({ value: 2 }, function() { + cbCalled = true; + }); + }).not.toThrow(); + } + + render() { + return
; + } } - return
; - } - } + var instance = ReactDOM.render(, container); + instance.setState({ value: 1 }); - var instance = ReactDOM.render(, container); + ReactDOM.unmountComponentAtNode(container); + expect(cbCalled).toBe(false); + }); + it("should warn about `setState` in render", () => { + spyOn(console, "error"); + var container = document.createElement("div"); - // The setState call is queued and then executed as a second pass. This - // behavior is undefined though so we're free to change it to suit the - // implementation details. - expect(renderPasses).toBe(2); - expect(renderedState).toBe(1); - expect(instance.state.value).toBe(1); + var renderedState = -1; + var renderPasses = 0; - // Forcing a rerender anywhere will cause the update to happen. - var instance2 = ReactDOM.render(, container); - expect(instance).toBe(instance2); - expect(renderedState).toBe(1); - expect(instance2.state.value).toBe(1); - }); + class Component extends React.Component { + state = { value: 0 }; - it('should warn about `setState` in getChildContext', () => { - spyOn(console, 'error'); + render() { + renderPasses++; + renderedState = this.state.value; + if (this.state.value === 0) { + this.setState({ value: 1 }); + } + return
; + } + } - var container = document.createElement('div'); + var instance = ReactDOM.render(, container); - var renderPasses = 0; + // The setState call is queued and then executed as a second pass. This + // behavior is undefined though so we're free to change it to suit the + // implementation details. + expect(renderPasses).toBe(2); + expect(renderedState).toBe(1); + expect(instance.state.value).toBe(1); - class Component extends React.Component { - state = {value: 0}; + // Forcing a rerender anywhere will cause the update to happen. + var instance2 = ReactDOM.render(, container); + expect(instance).toBe(instance2); + expect(renderedState).toBe(1); + expect(instance2.state.value).toBe(1); + }); - getChildContext() { - if (this.state.value === 0) { - this.setState({value: 4}); + it("should warn about `setState` in getChildContext", () => { + spyOn(console, "error"); + + var container = document.createElement("div"); + + var renderPasses = 0; + + class Component extends React.Component { + state = { value: 0 }; + + getChildContext() { + if (this.state.value === 0) { + this.setState({ value: 4 }); + } + } + + render() { + renderPasses++; + return
; + } } - } - - render() { - renderPasses++; - return
; - } - } - Component.childContextTypes = {}; - - var instance = ReactDOM.render(, container); - expect(renderPasses).toBe(2); - expect(instance.state.value).toBe(4); - - }); - - it('should call componentWillUnmount before unmounting', () => { - var container = document.createElement('div'); - var innerUnmounted = false; - - class Component extends React.Component { - render() { - return ( -
- - Text -
- ); - } - } - - class Inner extends React.Component { - componentWillUnmount() { - innerUnmounted = true; - } - - render() { - return
; - } - } - - ReactDOM.render(, container); - ReactDOM.unmountComponentAtNode(container); - expect(innerUnmounted).toBe(true); - }); - - it('should warn when shouldComponentUpdate() returns undefined', () => { - var container = document.createElement('div'); - class Component extends React.Component { - state = {bogus: false}; - - shouldComponentUpdate() { - return undefined; - } - - render() { - return
{this.state.bogus}
; - } - } - - var instance = ReactDOM.render(, container); - instance.setState({bogus: true}); - expect(container.textContent).toBe("");//布尔会转换为空字符串 - - }); -//https://github.com/facebook/react/blob/master/src/renderers/__tests__/ReactCompositeComponent-test.js#L526 - it('should pass context to children when not owner', () => { - class Parent extends React.Component { - render() { - return ; - } - } - - class Child extends React.Component { - static childContextTypes = { - foo: PropTypes.string, - }; - - getChildContext() { - return { - foo: 'bar', - }; - } - - render() { - return React.Children.only(this.props.children); - } - } - - class Grandchild extends React.Component { - static contextTypes = { - foo: PropTypes.string, - }; - - render() { - return
{this.context.foo}
; - } - } - - var component = ReactTestUtils.renderIntoDocument(); - expect(ReactDOM.findDOMNode(component).innerHTML).toBe('bar'); - }); - - - it('should skip update when rerendering element in container', () => { - class Parent extends React.Component { - render() { - return
{this.props.children}
; - } - } - - var childRenders = 0; - - class Child extends React.Component { - render() { - childRenders++; - return
; - } - } - - var container = document.createElement('div'); - var child = ; - - ReactDOM.render({child}, container); - ReactDOM.render({child}, container); - expect(childRenders).toBe(1); - }); - -//context穿透更新 - it('should pass context when re-rendered for static child', () => { - var parentInstance = null; - var childInstance = null; - - class Parent extends React.Component { - static childContextTypes = { - foo: PropTypes.string, - flag: PropTypes.bool, - }; - - state = { - flag: false, - }; - - getChildContext() { - return { - foo: 'bar', - flag: this.state.flag, - }; - } - - render() { - return React.Children.only(this.props.children); - } - } - - class Middle extends React.Component { - render() { - return this.props.children; - } - } - - class Child extends React.Component { - static contextTypes = { - foo: PropTypes.string, - flag: PropTypes.bool, - }; - - render() { - childInstance = this; - return Child; - } - } - - parentInstance = ReactTestUtils.renderIntoDocument( - , - ); - - expect(parentInstance.state.flag).toBe(false); - expect(childInstance.context).toEqual({foo: 'bar', flag: false}); - - parentInstance.setState({flag: true}); - expect(parentInstance.state.flag).toBe(true); - expect(childInstance.context).toEqual({foo: 'bar', flag: true}); - }); -//context穿透更新 - it('should pass context when re-rendered for static child within a composite component', () => { - class Parent extends React.Component { - static childContextTypes = { - flag: PropTypes.bool, - }; - - state = { - flag: true, - }; - - getChildContext() { - return { - flag: this.state.flag, - }; - } - - render() { - return
{this.props.children}
; - } - } - - class Child extends React.Component { - static contextTypes = { - flag: PropTypes.bool, - }; - - render() { - return
; - } - } - - class Wrapper extends React.Component { - render() { - return ( - - - + Component.childContextTypes = {}; + + var instance = ReactDOM.render(, container); + expect(renderPasses).toBe(2); + expect(instance.state.value).toBe(4); + }); + + it("should call componentWillUnmount before unmounting", () => { + var container = document.createElement("div"); + var innerUnmounted = false; + + class Component extends React.Component { + render() { + return ( +
+ + Text +
+ ); + } + } + + class Inner extends React.Component { + componentWillUnmount() { + innerUnmounted = true; + } + + render() { + return
; + } + } + + ReactDOM.render(, container); + ReactDOM.unmountComponentAtNode(container); + expect(innerUnmounted).toBe(true); + }); + + it("should warn when shouldComponentUpdate() returns undefined", () => { + var container = document.createElement("div"); + class Component extends React.Component { + state = { bogus: false }; + + shouldComponentUpdate() { + return undefined; + } + + render() { + return
{this.state.bogus}
; + } + } + + var instance = ReactDOM.render(, container); + instance.setState({ bogus: true }); + expect(container.textContent).toBe(""); //布尔会转换为空字符串 + }); + //https://github.com/facebook/react/blob/master/src/renderers/__tests__/ReactCompositeComponent-test.js#L526 + it("should pass context to children when not owner", () => { + class Parent extends React.Component { + render() { + return ( + + + + ); + } + } + + class Child extends React.Component { + static childContextTypes = { + foo: PropTypes.string + }; + + getChildContext() { + return { + foo: "bar" + }; + } + + render() { + return React.Children.only(this.props.children); + } + } + + class Grandchild extends React.Component { + static contextTypes = { + foo: PropTypes.string + }; + + render() { + return
{this.context.foo}
; + } + } + + var component = ReactTestUtils.renderIntoDocument(); + expect(ReactDOM.findDOMNode(component).innerHTML).toBe("bar"); + }); + + it("should skip update when rerendering element in container", () => { + class Parent extends React.Component { + render() { + return
{this.props.children}
; + } + } + + var childRenders = 0; + + class Child extends React.Component { + render() { + childRenders++; + return
; + } + } + + var container = document.createElement("div"); + var child = ; + + ReactDOM.render({child}, container); + ReactDOM.render({child}, container); + expect(childRenders).toBe(1); + }); + + //context穿透更新 + it("should pass context when re-rendered for static child", () => { + var parentInstance = null; + var childInstance = null; + + class Parent extends React.Component { + static childContextTypes = { + foo: PropTypes.string, + flag: PropTypes.bool + }; + + state = { + flag: false + }; + + getChildContext() { + return { + foo: "bar", + flag: this.state.flag + }; + } + + render() { + return React.Children.only(this.props.children); + } + } + + class Middle extends React.Component { + render() { + return this.props.children; + } + } + + class Child extends React.Component { + static contextTypes = { + foo: PropTypes.string, + flag: PropTypes.bool + }; + + render() { + childInstance = this; + return Child; + } + } + + parentInstance = ReactTestUtils.renderIntoDocument( + + + + + ); - } - } - var wrapper = ReactTestUtils.renderIntoDocument(); + expect(parentInstance.state.flag).toBe(false); + expect(childInstance.context).toEqual({ foo: "bar", flag: false }); + + parentInstance.setState({ flag: true }); + expect(parentInstance.state.flag).toBe(true); + expect(childInstance.context).toEqual({ foo: "bar", flag: true }); + }); + //context穿透更新 + it("should pass context when re-rendered for static child within a composite component", () => { + class Parent extends React.Component { + static childContextTypes = { + flag: PropTypes.bool + }; - expect(wrapper.refs.parent.state.flag).toEqual(true); - expect(wrapper.refs.child.context).toEqual({flag: true}); + state = { + flag: true + }; - // We update while is still a static prop relative to this update - wrapper.refs.parent.setState({flag: false}); + getChildContext() { + return { + flag: this.state.flag + }; + } - expect(wrapper.refs.parent.state.flag).toEqual(false); - expect(wrapper.refs.child.context).toEqual({flag: false}); - }); + render() { + return
{this.props.children}
; + } + } + class Child extends React.Component { + static contextTypes = { + flag: PropTypes.bool + }; - it('should pass context transitively', () => { - var childInstance = null; - var grandchildInstance = null; + render() { + return
; + } + } - class Parent extends React.Component { - static childContextTypes = { - foo: PropTypes.string, - depth: PropTypes.number, - }; + class Wrapper extends React.Component { + render() { + return ( + + + + ); + } + } - getChildContext() { - return { - foo: 'bar', - depth: 0, - }; - } - - render() { - return ; - } - } - - class Child extends React.Component { - static contextTypes = { - foo: PropTypes.string, - depth: PropTypes.number, - }; - - static childContextTypes = { - depth: PropTypes.number, - }; - - getChildContext() { - return { - depth: this.context.depth + 1, - }; - } - - render() { - childInstance = this; - return ; - } - } - - class Grandchild extends React.Component { - static contextTypes = { - foo: PropTypes.string, - depth: PropTypes.number, - }; - - render() { - grandchildInstance = this; - return
; - } - } - - ReactTestUtils.renderIntoDocument(); - expect(childInstance.context).toEqual({foo: 'bar', depth: 0}); - expect(grandchildInstance.context).toEqual({foo: 'bar', depth: 1}); - }); - - it('should pass context when re-rendered', () => { - var parentInstance = null; - var childInstance = null; - - class Parent extends React.Component { - static childContextTypes = { - foo: PropTypes.string, - depth: PropTypes.number, - }; - - state = { - flag: false, - }; - - getChildContext() { - return { - foo: 'bar', - depth: 0, - }; - } + var wrapper = ReactTestUtils.renderIntoDocument(); - render() { - var output = ; - if (!this.state.flag) { - output = Child; + expect(wrapper.refs.parent.state.flag).toEqual(true); + expect(wrapper.refs.child.context).toEqual({ flag: true }); + + // We update while is still a static prop relative to this update + wrapper.refs.parent.setState({ flag: false }); + + expect(wrapper.refs.parent.state.flag).toEqual(false); + expect(wrapper.refs.child.context).toEqual({ flag: false }); + }); + + it("should pass context transitively", () => { + var childInstance = null; + var grandchildInstance = null; + + class Parent extends React.Component { + static childContextTypes = { + foo: PropTypes.string, + depth: PropTypes.number + }; + + getChildContext() { + return { + foo: "bar", + depth: 0 + }; + } + + render() { + return ; + } + } + + class Child extends React.Component { + static contextTypes = { + foo: PropTypes.string, + depth: PropTypes.number + }; + + static childContextTypes = { + depth: PropTypes.number + }; + + getChildContext() { + return { + depth: this.context.depth + 1 + }; + } + + render() { + childInstance = this; + return ; + } + } + + class Grandchild extends React.Component { + static contextTypes = { + foo: PropTypes.string, + depth: PropTypes.number + }; + + render() { + grandchildInstance = this; + return
; + } + } + + ReactTestUtils.renderIntoDocument(); + expect(childInstance.context).toEqual({ foo: "bar", depth: 0 }); + expect(grandchildInstance.context).toEqual({ foo: "bar", depth: 1 }); + }); + + it("should pass context when re-rendered", () => { + var parentInstance = null; + var childInstance = null; + + class Parent extends React.Component { + static childContextTypes = { + foo: PropTypes.string, + depth: PropTypes.number + }; + + state = { + flag: false + }; + + getChildContext() { + return { + foo: "bar", + depth: 0 + }; + } + + render() { + var output = ; + if (!this.state.flag) { + output = Child; + } + return output; + } + } + + class Child extends React.Component { + static contextTypes = { + foo: PropTypes.string, + depth: PropTypes.number + }; + + render() { + childInstance = this; + return Child; + } } - return output; - } - } - - class Child extends React.Component { - static contextTypes = { - foo: PropTypes.string, - depth: PropTypes.number, - }; - - render() { - childInstance = this; - return Child; - } - } - - parentInstance = ReactTestUtils.renderIntoDocument(); - expect(childInstance).toBeNull(); - - expect(parentInstance.state.flag).toBe(false); - /* + + parentInstance = ReactTestUtils.renderIntoDocument(); + expect(childInstance).toBeNull(); + + expect(parentInstance.state.flag).toBe(false); + /* ReactDOM.unstable_batchedUpdates(function() { parentInstance.setState({flag: true}); }); @@ -651,655 +630,683 @@ describe("ReactCompositeComponent", function() { expect(childInstance.context).toEqual({foo: 'bar', depth: 0}); */ - }); - - it('unmasked context propagates through updates', () => { - class Leaf extends React.Component { - static contextTypes = { - foo: PropTypes.string.isRequired, - }; - - componentWillReceiveProps(nextProps, nextContext) { - expect('foo' in nextContext).toBe(true); - } - - shouldComponentUpdate(nextProps, nextState, nextContext) { - expect('foo' in nextContext).toBe(true); - return true; - } - - render() { - return {this.context.foo}; - } - } - - class Intermediary extends React.Component { - componentWillReceiveProps(nextProps, nextContext) { - expect('foo' in nextContext).toBe(false); - } - - shouldComponentUpdate(nextProps, nextState, nextContext) { - expect('foo' in nextContext).toBe(false); - return true; - } - - render() { - return ; - } - } - - class Parent extends React.Component { - static childContextTypes = { - foo: PropTypes.string, - }; - - getChildContext() { - return { - foo: this.props.cntxt, - }; - } - - render() { - return ; - } - } - - var div = document.createElement('div'); - ReactDOM.render(, div); - expect(div.children[0].innerHTML).toBe('noise'); - div.children[0].innerHTML = 'aliens'; - div.children[0].id = 'aliens'; - expect(div.children[0].innerHTML).toBe('aliens'); - expect(div.children[0].id).toBe('aliens'); - ReactDOM.render(, div); - expect(div.children[0].innerHTML).toBe('bar'); - expect(div.children[0].id).toBe('aliens'); - }); - - it('should trigger componentWillReceiveProps for context changes', () => { - var contextChanges = 0; - var propChanges = 0; - - class GrandChild extends React.Component { - static contextTypes = { - foo: PropTypes.string.isRequired, - }; - - componentWillReceiveProps(nextProps, nextContext) { - expect('foo' in nextContext).toBe(true); - - if (nextProps !== this.props) { - propChanges++; - } + }); + + it("unmasked context propagates through updates", () => { + class Leaf extends React.Component { + static contextTypes = { + foo: PropTypes.string.isRequired + }; + + componentWillReceiveProps(nextProps, nextContext) { + expect("foo" in nextContext).toBe(true); + } + + shouldComponentUpdate(nextProps, nextState, nextContext) { + expect("foo" in nextContext).toBe(true); + return true; + } - if (nextContext !== this.context) { - contextChanges++; + render() { + return {this.context.foo}; + } } - } - render() { - return {this.props.children}; - } - } + class Intermediary extends React.Component { + componentWillReceiveProps(nextProps, nextContext) { + expect("foo" in nextContext).toBe(false); + } - class ChildWithContext extends React.Component { - static contextTypes = { - foo: PropTypes.string.isRequired, - }; + shouldComponentUpdate(nextProps, nextState, nextContext) { + expect("foo" in nextContext).toBe(false); + return true; + } + + render() { + return ; + } + } + + class Parent extends React.Component { + static childContextTypes = { + foo: PropTypes.string + }; - componentWillReceiveProps(nextProps, nextContext) { - expect('foo' in nextContext).toBe(true); + getChildContext() { + return { + foo: this.props.cntxt + }; + } - if (nextProps !== this.props) { - propChanges++; + render() { + return ; + } } - if (nextContext !== this.context) { - contextChanges++; + var div = document.createElement("div"); + ReactDOM.render(, div); + expect(div.children[0].innerHTML).toBe("noise"); + div.children[0].innerHTML = "aliens"; + div.children[0].id = "aliens"; + expect(div.children[0].innerHTML).toBe("aliens"); + expect(div.children[0].id).toBe("aliens"); + ReactDOM.render(, div); + expect(div.children[0].innerHTML).toBe("bar"); + expect(div.children[0].id).toBe("aliens"); + }); + + it("should trigger componentWillReceiveProps for context changes", () => { + var contextChanges = 0; + var propChanges = 0; + + class GrandChild extends React.Component { + static contextTypes = { + foo: PropTypes.string.isRequired + }; + + componentWillReceiveProps(nextProps, nextContext) { + expect("foo" in nextContext).toBe(true); + + if (nextProps !== this.props) { + propChanges++; + } + + if (nextContext !== this.context) { + contextChanges++; + } + } + + render() { + return {this.props.children}; + } } - } - render() { - return
{this.props.children}
; - } - } + class ChildWithContext extends React.Component { + static contextTypes = { + foo: PropTypes.string.isRequired + }; + + componentWillReceiveProps(nextProps, nextContext) { + expect("foo" in nextContext).toBe(true); + + if (nextProps !== this.props) { + propChanges++; + } - class ChildWithoutContext extends React.Component { - componentWillReceiveProps(nextProps, nextContext) { - expect('foo' in nextContext).toBe(false); + if (nextContext !== this.context) { + contextChanges++; + } + } - if (nextProps !== this.props) { - propChanges++; + render() { + return
{this.props.children}
; + } } - if (nextContext !== this.context) { - contextChanges++; + class ChildWithoutContext extends React.Component { + componentWillReceiveProps(nextProps, nextContext) { + expect("foo" in nextContext).toBe(false); + + if (nextProps !== this.props) { + propChanges++; + } + + if (nextContext !== this.context) { + contextChanges++; + } + } + + render() { + return
{this.props.children}
; + } } - } - render() { - return
{this.props.children}
; - } - } + class Parent extends React.Component { + static childContextTypes = { + foo: PropTypes.string + }; - class Parent extends React.Component { - static childContextTypes = { - foo: PropTypes.string, - }; + state = { + foo: "abc" + }; - state = { - foo: 'abc', - }; + getChildContext() { + return { + foo: this.state.foo + }; + } - getChildContext() { - return { - foo: this.state.foo, - }; - } - - render() { - return
{this.props.children}
; - } - } - - var div = document.createElement('div'); - - var parentInstance = null; - ReactDOM.render( - (parentInstance = inst)}> - - A1 - A2 - - - - B1 - B2 - - , - div, - ); - - parentInstance.setState({ - foo: 'def', + render() { + return
{this.props.children}
; + } + } + + var div = document.createElement("div"); + + var parentInstance = null; + ReactDOM.render( + (parentInstance = inst)}> + + A1 + A2 + + + + B1 + B2 + + , + div + ); + + parentInstance.setState({ + foo: "def" + }); + + expect(propChanges).toBe(0); + expect(contextChanges).toBe(3); // ChildWithContext, GrandChild x 2 }); - expect(propChanges).toBe(0); - expect(contextChanges).toBe(3); // ChildWithContext, GrandChild x 2 - }); + it("only renders once if updated in componentWillReceiveProps", () => { + var renders = 0; + class Component extends React.Component { + state = { updated: false }; - it('only renders once if updated in componentWillReceiveProps', () => { - var renders = 0; + componentWillReceiveProps(props) { + expect(props.update).toBe(1); + expect(renders).toBe(1); + this.setState({ updated: true }); + expect(renders).toBe(1); + } - class Component extends React.Component { - state = {updated: false}; + render() { + renders++; + return
; + } + } - componentWillReceiveProps(props) { - expect(props.update).toBe(1); - expect(renders).toBe(1); - this.setState({updated: true}); + var container = document.createElement("div"); + var instance = ReactDOM.render(, container); expect(renders).toBe(1); - } - - render() { - renders++; - return
; - } - } - - var container = document.createElement('div'); - var instance = ReactDOM.render(, container); - expect(renders).toBe(1); - expect(instance.state.updated).toBe(false); - ReactDOM.render(, container); - expect(renders).toBe(2); - expect(instance.state.updated).toBe(true); - }); + expect(instance.state.updated).toBe(false); + ReactDOM.render(, container); + expect(renders).toBe(2); + expect(instance.state.updated).toBe(true); + }); - it('only renders once if updated in componentWillReceiveProps when batching', () => { - var renders = 0; + it("only renders once if updated in componentWillReceiveProps when batching", () => { + var renders = 0; - class Component extends React.Component { - state = {updated: false}; + class Component extends React.Component { + state = { updated: false }; - componentWillReceiveProps(props) { - expect(props.update).toBe(1); - expect(renders).toBe(1); - this.setState({updated: true}); + componentWillReceiveProps(props) { + expect(props.update).toBe(1); + expect(renders).toBe(1); + this.setState({ updated: true }); + expect(renders).toBe(1); + } + + render() { + renders++; + return
; + } + } + + var container = document.createElement("div"); + var instance = ReactDOM.render(, container); expect(renders).toBe(1); - } - - render() { - renders++; - return
; - } - } - - var container = document.createElement('div'); - var instance = ReactDOM.render(, container); - expect(renders).toBe(1); - expect(instance.state.updated).toBe(false); - /* + expect(instance.state.updated).toBe(false); + /* ReactDOM.unstable_batchedUpdates(() => { ReactDOM.render(, container); }); expect(renders).toBe(2); expect(instance.state.updated).toBe(true); */ - }); - - it('should update refs if shouldComponentUpdate gives false', () => { - class Static extends React.Component { - shouldComponentUpdate() { - return false; - } - - render() { - return
{this.props.children}
; - } - } - - class Component extends React.Component { - render() { - if (this.props.flipped) { - return ( -
- B (ignored) - A (ignored) -
- ); - } else { - return ( -
- A - B -
- ); + }); + + it("should update refs if shouldComponentUpdate gives false", () => { + class Static extends React.Component { + shouldComponentUpdate() { + return false; + } + + render() { + return
{this.props.children}
; + } + } + + class Component extends React.Component { + render() { + if (this.props.flipped) { + return ( +
+ + B (ignored) + + + A (ignored) + +
+ ); + } else { + return ( +
+ + A + + + B + +
+ ); + } + } + } + + var container = document.createElement("div"); + var comp = ReactDOM.render(, container); + //keyA <> instance0 <> static0 <> contentA + //keyB <> instance1 <> static1 <> contentB + expect(ReactDOM.findDOMNode(comp.refs.static0).textContent).toBe("A"); + expect(ReactDOM.findDOMNode(comp.refs.static1).textContent).toBe("B"); + //keyA <> instance0 <> static1 <> contentA + //keyB <> instance1 <> static1 <> contentB + // When flipping the order, the refs should update even though the actual + // contents do not + ReactDOM.render(, container); + expect(ReactDOM.findDOMNode(comp.refs.static0).textContent).toBe("B"); + expect(ReactDOM.findDOMNode(comp.refs.static1).textContent).toBe("A"); + }); + + it("should allow access to findDOMNode in componentWillUnmount", () => { + var a = null; + var b = null; + + class Component extends React.Component { + componentDidMount() { + a = ReactDOM.findDOMNode(this); + expect(a).not.toBe(null); + } + + componentWillUnmount() { + b = ReactDOM.findDOMNode(this); + expect(b).not.toBe(null); + } + + render() { + return
; + } + } + + var container = document.createElement("div"); + expect(a).toBe(container.firstChild); + ReactDOM.render(, container); + ReactDOM.unmountComponentAtNode(container); + expect(a).toBe(b); + }); + + it("context should be passed down from the parent", () => { + class Parent extends React.Component { + static childContextTypes = { + foo: PropTypes.string + }; + + getChildContext() { + return { + foo: "bar" + }; + } + + render() { + return
{this.props.children}
; + } + } + + class Component extends React.Component { + static contextTypes = { + foo: PropTypes.string.isRequired + }; + + render() { + return
; + } + } + + var div = document.createElement("div"); + ReactDOM.render( + + + , + div + ); + }); + + it("should replace state", () => { + class Moo extends React.Component { + state = { x: 1 }; + render() { + return
; + } + } + + var moo = ReactTestUtils.renderIntoDocument(); + // No longer a public API, but we can test that it works internally by + // reaching into the updater. + // moo.updater.enqueueReplaceState(moo, {y: 2}); + // expect('x' in moo.state).toBe(false); + expect(moo.state.y).toBe(void 666); + }); + + it("should support objects with prototypes as state", () => { + var NotActuallyImmutable = function(str) { + this.str = str; + }; + NotActuallyImmutable.prototype.amIImmutable = function() { + return true; + }; + class Moo extends React.Component { + state = new NotActuallyImmutable("first"); + // No longer a public API, but we can test that it works internally by + // reaching into the updater. + _replaceState = function(a) { + this.state = a; + this.forceUpdate(); + }; + render() { + return
; + } + } + + var moo = ReactTestUtils.renderIntoDocument(); + expect(moo.state.str).toBe("first"); + expect(moo.state.amIImmutable()).toBe(true); + + var secondState = new NotActuallyImmutable("second"); + moo._replaceState(secondState); + expect(moo.state.str).toBe("second"); + expect(moo.state.amIImmutable()).toBe(true); + expect(moo.state).toBe(secondState); + + moo.setState({ str: "third" }); + expect(moo.state.str).toBe("third"); + // Here we lose the prototype. + expect(moo.state.amIImmutable).toBe(undefined); + }); + it("props对象不能在构造器里被重写", () => { + var container = document.createElement("div"); + class Foo extends React.Component { + constructor(props) { + super(props); + this.props = { idx: "xxx" }; + } + + render() { + return {this.props.idx}; + } + } + + ReactDOM.render(, container); + + expect(container.textContent).toBe("aaa"); + }); + + it("should warn when mutated props are passed", () => { + var container = document.createElement("div"); + + class Foo extends React.Component { + constructor(props) { + var _props = { idx: props.idx + "!" }; + super(_props); + } + + render() { + return {this.props.idx}; + } + } + + ReactDOM.render(, container); + + expect(container.textContent).toBe("qwe"); + }); + + it("should only call componentWillUnmount once", () => { + var app; + var count = 0; + + class App extends React.Component { + render() { + if (this.props.stage === 1) { + return ; + } else { + return null; + } + } } - } - } - - var container = document.createElement('div'); - var comp = ReactDOM.render(, container); - //keyA <> instance0 <> static0 <> contentA - //keyB <> instance1 <> static1 <> contentB - expect(ReactDOM.findDOMNode(comp.refs.static0).textContent).toBe('A'); - expect(ReactDOM.findDOMNode(comp.refs.static1).textContent).toBe('B'); - //keyA <> instance0 <> static1 <> contentA - //keyB <> instance1 <> static1 <> contentB - // When flipping the order, the refs should update even though the actual - // contents do not - ReactDOM.render(, container); - expect(ReactDOM.findDOMNode(comp.refs.static0).textContent).toBe('B'); - expect(ReactDOM.findDOMNode(comp.refs.static1).textContent).toBe('A'); - }); - - it('should allow access to findDOMNode in componentWillUnmount', () => { - var a = null; - var b = null; - - class Component extends React.Component { - componentDidMount() { - a = ReactDOM.findDOMNode(this); - expect(a).not.toBe(null); - } - - componentWillUnmount() { - b = ReactDOM.findDOMNode(this); - expect(b).not.toBe(null); - } - - render() { - return
; - } - } - - var container = document.createElement('div'); - expect(a).toBe(container.firstChild); - ReactDOM.render(, container); - ReactDOM.unmountComponentAtNode(container); - expect(a).toBe(b); - }); - - it('context should be passed down from the parent', () => { - class Parent extends React.Component { - static childContextTypes = { - foo: PropTypes.string, - }; - - getChildContext() { - return { - foo: 'bar', + + class UnunmountableComponent extends React.Component { + componentWillUnmount() { + app.setState({}); + count++; + throw Error("always fails"); + } + + render() { + return
Hello {this.props.name}
; + } + } + + var container = document.createElement("div"); + + var setRef = ref => { + if (ref) { + app = ref; + } }; - } - - render() { - return
{this.props.children}
; - } - } - - class Component extends React.Component { - static contextTypes = { - foo: PropTypes.string.isRequired, - }; - - render() { - return
; - } - } - - var div = document.createElement('div'); - ReactDOM.render(, div); - }); - - it('should replace state', () => { - class Moo extends React.Component { - state = {x: 1}; - render() { - return
; - } - } - - var moo = ReactTestUtils.renderIntoDocument(); - // No longer a public API, but we can test that it works internally by - // reaching into the updater. - // moo.updater.enqueueReplaceState(moo, {y: 2}); - // expect('x' in moo.state).toBe(false); - expect(moo.state.y).toBe(void 666); - }); - - it('should support objects with prototypes as state', () => { - var NotActuallyImmutable = function(str) { - this.str = str; - }; - NotActuallyImmutable.prototype.amIImmutable = function() { - return true; - }; - class Moo extends React.Component { - state = new NotActuallyImmutable('first'); - // No longer a public API, but we can test that it works internally by - // reaching into the updater. - _replaceState = function (a){ - this.state = a - this.forceUpdate() - } - render() { - return
; - } - } - - var moo = ReactTestUtils.renderIntoDocument(); - expect(moo.state.str).toBe('first'); - expect(moo.state.amIImmutable()).toBe(true); - - var secondState = new NotActuallyImmutable('second'); - moo._replaceState(secondState); - expect(moo.state.str).toBe('second'); - expect(moo.state.amIImmutable()).toBe(true); - expect(moo.state).toBe(secondState); - - moo.setState({str: 'third'}); - expect(moo.state.str).toBe('third'); - // Here we lose the prototype. - expect(moo.state.amIImmutable).toBe(undefined); - - - - - }); - it('props对象不能在构造器里被重写', () => { - var container = document.createElement('div'); - class Foo extends React.Component { - constructor(props) { - super(props); - this.props = {idx: "xxx"} - } - - render() { - return {this.props.idx}; - } - } - - - - ReactDOM.render(, container); - - expect(container.textContent).toBe("aaa"); - }); - - it('should warn when mutated props are passed', () => { - - var container = document.createElement('div'); - - class Foo extends React.Component { - constructor(props) { - var _props = {idx: props.idx + '!'}; - super(_props); - } - - render() { - return {this.props.idx}; - } - } - - - ReactDOM.render(, container); - - expect(container.textContent).toBe("qwe"); - }); - - it('should only call componentWillUnmount once', () => { - var app; - var count = 0; - - class App extends React.Component { - render() { - if (this.props.stage === 1) { - return ; - } else { - return null; + + expect(function() { + ReactDOM.render(, container); + ReactDOM.render(, container); + }).toThrow(); + expect(count).toBe(1); + }); + it("prepares new child before unmounting old", () => { + var log = []; + + class Spy extends React.Component { + componentWillMount() { + log.push(this.props.name + " componentWillMount"); + } + render() { + log.push(this.props.name + " render"); + return
; + } + componentDidMount() { + log.push(this.props.name + " componentDidMount"); + } + componentWillUnmount() { + log.push(this.props.name + " componentWillUnmount"); + } } - } - } - - class UnunmountableComponent extends React.Component { - componentWillUnmount() { - app.setState({}); - count++; - throw Error('always fails'); - } - - render() { - return
Hello {this.props.name}
; - } - } - - var container = document.createElement('div'); - - var setRef = ref => { - if (ref) { - app = ref; - } - }; - expect(function() { - ReactDOM.render(, container); - ReactDOM.render(, container); - }).toThrow(); - expect(count).toBe(1); - }); - it('prepares new child before unmounting old', () => { - var log = []; - - class Spy extends React.Component { - componentWillMount() { - log.push(this.props.name + ' componentWillMount'); - } - render() { - log.push(this.props.name + ' render'); - return
; - } - componentDidMount() { - log.push(this.props.name + ' componentDidMount'); - } - componentWillUnmount() { - log.push(this.props.name + ' componentWillUnmount'); - } - } - - class Wrapper extends React.Component { - render() { - return ; - } - } - - var container = document.createElement('div'); - ReactDOM.render(, container); - ReactDOM.render(, container); - - expect(log).toEqual([ - 'A componentWillMount', - 'A render', - 'A componentDidMount', - 'A componentWillUnmount', - 'B componentWillMount', - 'B render', - - 'B componentDidMount' - ]); - }); - - - it('respects a shallow shouldComponentUpdate implementation', () => { - var renderCalls = 0; - class PlasticWrap extends React.Component { - constructor(props, context) { - super(props, context); - this.state = { - color: 'green', + class Wrapper extends React.Component { + render() { + return ; + } + } + + var container = document.createElement("div"); + ReactDOM.render(, container); + ReactDOM.render(, container); + + expect(log).toEqual(["A componentWillMount", "A render", "A componentDidMount", "A componentWillUnmount", "B componentWillMount", "B render", "B componentDidMount"]); + }); + + it("respects a shallow shouldComponentUpdate implementation", () => { + var renderCalls = 0; + class PlasticWrap extends React.Component { + constructor(props, context) { + super(props, context); + this.state = { + color: "green" + }; + } + + render() { + return ; + } + } + + class Apple extends React.Component { + state = { + cut: false, + slices: 1 + }; + + shouldComponentUpdate(nextProps, nextState) { + return shallowCompare(this, nextProps, nextState); + } + + cut() { + this.setState({ + cut: true, + slices: 10 + }); + } + + eatSlice() { + this.setState({ + slices: this.state.slices - 1 + }); + } + + render() { + renderCalls++; + return
; + } + } + + var container = document.createElement("div"); + var instance = ReactDOM.render(, container); + expect(renderCalls).toBe(1); + + // Do not re-render based on props + instance.setState({ color: "green" }); + expect(renderCalls).toBe(1); + + // Re-render based on props + instance.setState({ color: "red" }); + expect(renderCalls).toBe(2); + + // Re-render base on state + instance.refs.apple.cut(); + expect(renderCalls).toBe(3); + + // No re-render based on state + instance.refs.apple.cut(); + expect(renderCalls).toBe(3); + + // Re-render based on state again + instance.refs.apple.eatSlice(); + expect(renderCalls).toBe(4); + }); + + it("does not do a deep comparison for a shallow shouldComponentUpdate implementation", () => { + function getInitialState() { + return { + foo: [1, 2, 3], + bar: { a: 4, b: 5, c: 6 } + }; + } + + var renderCalls = 0; + var initialSettings = getInitialState(); + + class Component extends React.Component { + state = initialSettings; + + shouldComponentUpdate(nextProps, nextState) { + var a = shallowCompare(this, nextProps, nextState); + console.log(a, "!!!"); + return a; + } + + render() { + renderCalls++; + return
; + } + } + + var container = document.createElement("div"); + var instance = ReactDOM.render(, container); + expect(renderCalls).toBe(1); + + // Do not re-render if state is equal + var settings = { + foo: initialSettings.foo, + bar: initialSettings.bar }; - } - - render() { - return ; - } - } - - class Apple extends React.Component { - state = { - cut: false, - slices: 1, - }; - - shouldComponentUpdate(nextProps, nextState) { - return shallowCompare(this, nextProps, nextState); - } - - cut() { - this.setState({ - cut: true, - slices: 10, - }); - } + instance.setState(settings); + expect(renderCalls).toBe(1); - eatSlice() { - this.setState({ - slices: this.state.slices - 1, - }); - } - - render() { - renderCalls++; - return
; - } - } - - var container = document.createElement('div'); - var instance = ReactDOM.render(, container); - expect(renderCalls).toBe(1); - - // Do not re-render based on props - instance.setState({color: 'green'}); - expect(renderCalls).toBe(1); - - // Re-render based on props - instance.setState({color: 'red'}); - expect(renderCalls).toBe(2); - - // Re-render base on state - instance.refs.apple.cut(); - expect(renderCalls).toBe(3); - - // No re-render based on state - instance.refs.apple.cut(); - expect(renderCalls).toBe(3); - - // Re-render based on state again - instance.refs.apple.eatSlice(); - expect(renderCalls).toBe(4); - }); - - it('does not do a deep comparison for a shallow shouldComponentUpdate implementation', () => { - function getInitialState() { - return { - foo: [1, 2, 3], - bar: {a: 4, b: 5, c: 6}, - }; - } - - var renderCalls = 0; - var initialSettings = getInitialState(); - - class Component extends React.Component { - state = initialSettings; - - shouldComponentUpdate(nextProps, nextState) { - var a = shallowCompare(this, nextProps, nextState); - console.log(a, '!!!') - return a - } - - render() { - renderCalls++; - return
; - } - } - - var container = document.createElement('div'); - var instance = ReactDOM.render(, container); - expect(renderCalls).toBe(1); - - // Do not re-render if state is equal - var settings = { - foo: initialSettings.foo, - bar: initialSettings.bar, - }; - instance.setState(settings); - expect(renderCalls).toBe(1); - - // Re-render because one field changed - initialSettings.foo = [1, 2, 3]; - instance.setState(initialSettings); - expect(renderCalls).toBe(2); - - // Re-render because the object changed - instance.setState(getInitialState()); - expect(renderCalls).toBe(3); - }); - - it('should call setState callback with no arguments', () => { - let mockArgs; - class Component extends React.Component { - componentDidMount() { - this.setState({}, (...args) => (mockArgs = args)); - } - render() { - return false; - } - } - - ReactTestUtils.renderIntoDocument(); - expect(mockArgs.length).toEqual(0); - }); -}); \ No newline at end of file + // Re-render because one field changed + initialSettings.foo = [1, 2, 3]; + instance.setState(initialSettings); + expect(renderCalls).toBe(2); + + // Re-render because the object changed + instance.setState(getInitialState()); + expect(renderCalls).toBe(3); + }); + + it("should call setState callback with no arguments", () => { + let mockArgs; + class Component extends React.Component { + componentDidMount() { + this.setState({}, (...args) => (mockArgs = args)); + } + render() { + return false; + } + } + + ReactTestUtils.renderIntoDocument(); + expect(mockArgs.length).toEqual(0); + }); + + it("确保子组件即便更新被阻止,新虚拟DOM也要移值旧的虚拟DOM的_hostNode过来", () => { + class Component extends React.Component { + constructor(props) { + super(props); + this.state = { + a: 1 + }; + } + render() { + return ; + } + } + class Child extends React.Component { + constructor(props) { + super(props); + this.state = { + a: 1 + }; + } + shouldComponentUpdate() { + return false; + } + render() { + return
{new Date() - 0}
; + } + } + var b = ; + var container = document.createElement("div") + var s = ReactDOM.render(b, container); + expect(!!s.__rendered._hostNode).toBe(true) + s.setState({a:2}) + expect(!!s.__rendered._hostNode).toBe(true) + }); +}); From cf8dfbe48dcb1ee0b03c2750d4dd0227bdc81911 Mon Sep 17 00:00:00 2001 From: RubyLouvre <1669866773@qq.com> Date: Sun, 1 Oct 2017 09:15:22 +0800 Subject: [PATCH 25/57] 1.1.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 098afb56e..a640aa757 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "anujs", - "version": "1.1.1", + "version": "1.1.2", "description": "a mini React-like framework", "main": "dist/React.js", "scripts": { From 2c354d7a96d5aae71103f1da6142f6d04d9e13da Mon Sep 17 00:00:00 2001 From: RubyLouvre <1669866773@qq.com> Date: Sun, 1 Oct 2017 09:18:22 +0800 Subject: [PATCH 26/57] =?UTF-8?q?=E6=9B=B4=E6=96=B0version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- version.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/version.md b/version.md index 9d4393eee..cf83de296 100644 --- a/version.md +++ b/version.md @@ -5,11 +5,13 @@ 4. 添加beforePatch , afterPatch钩子 5. 添加lib/ReactInputSelection.js 6. 统一所有操作虚拟DOM的方法的参数(mountXXX, updateXXX, alignXXX系列) + >1 第一个参数为旧真实DOM或旧虚拟DOM >2 第二个参数为新虚拟DOM >3 第三个参数为父虚拟DOM(可能不存在,那么后面直接跟第四,第五) >4 第四个参数为上下文对象 >5 第五个参数为任务调度系系统的列队 + 7. 使用全新的方式获取元素的命名空间 8. 上线全新的节点排序算法(diffChildren) 9. renderByAnu在全局渲染后应该置空CurrentOwner.cur, 防止影响其他虚拟DOM @@ -20,6 +22,7 @@ 14. 无状态组件支持模块模式(返回一个带生命周期钩子的纯对象,这些方法会像有状态组件那样被调用) 15. 放松shouldComponentUpdate的限制,返回任何假值都阻止子孙更新 16. 修正ref的更新方式 +17. shouldComponentUpdate返回假值时,当前的虚拟DOM应该吸纳旧虚拟DOM的有用信息 ## 1.1.1 1. 简化createClass From 1abebcd1558006b1c082396c3ddc090f744c8ea6 Mon Sep 17 00:00:00 2001 From: RubyLouvre <1669866773@qq.com> Date: Sun, 1 Oct 2017 09:21:15 +0800 Subject: [PATCH 27/57] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dist/React.js | 2 +- dist/ReactIE.js | 2 +- dist/ReactSelection.js | 2 +- dist/ReactShim.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dist/React.js b/dist/React.js index 5d1b317b2..d9335fbea 100644 --- a/dist/React.js +++ b/dist/React.js @@ -2598,7 +2598,7 @@ function updateInstanceChain(instance, dom) { } var React = { - version: "1.1.1", + version: "1.1.2", render: render, options: options, PropTypes: PropTypes, diff --git a/dist/ReactIE.js b/dist/ReactIE.js index 9374b0a0d..0ebd59fed 100644 --- a/dist/ReactIE.js +++ b/dist/ReactIE.js @@ -2715,7 +2715,7 @@ if (msie < 9) { } var React = { - version: "1.1.1", + version: "1.1.2", render: render, options: options, PropTypes: PropTypes, diff --git a/dist/ReactSelection.js b/dist/ReactSelection.js index 9374b0a0d..0ebd59fed 100644 --- a/dist/ReactSelection.js +++ b/dist/ReactSelection.js @@ -2715,7 +2715,7 @@ if (msie < 9) { } var React = { - version: "1.1.1", + version: "1.1.2", render: render, options: options, PropTypes: PropTypes, diff --git a/dist/ReactShim.js b/dist/ReactShim.js index f3fc4d67c..2c6522543 100644 --- a/dist/ReactShim.js +++ b/dist/ReactShim.js @@ -2439,7 +2439,7 @@ function updateInstanceChain(instance, dom) { } var React = { - version: "1.1.1", + version: "1.1.2", render: render, options: options, Children: Children, //支持react-redux From e8204800e5c39262830d8e7d0da583f2bb86dc51 Mon Sep 17 00:00:00 2001 From: RubyLouvre <1669866773@qq.com> Date: Sun, 1 Oct 2017 15:47:56 +0800 Subject: [PATCH 28/57] =?UTF-8?q?=E4=BD=BF=E7=94=A8=E4=B8=80=E4=B8=AAupdat?= =?UTF-8?q?er=E5=AF=B9=E8=B1=A1=E5=B0=81=E8=A3=85=E6=89=80=E6=9C=89?= =?UTF-8?q?=E5=86=85=E9=83=A8=E5=8F=98=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dist/React.js | 399 +++++++++--------- dist/ReactIE.js | 399 +++++++++--------- dist/ReactSelection.js | 399 +++++++++--------- dist/ReactShim.js | 399 +++++++++--------- index4.html | 154 ++++--- index5.html | 103 ++--- index6.html | 4 +- lib/ReactTestUtils.js | 21 +- src/Component.js | 73 ++-- src/diff.js | 201 ++++----- src/dispose.js | 8 +- src/event.js | 2 +- src/instantiateComponent.js | 108 ++++- src/scheduler.js | 54 +-- test/modules/ReactCompositeComponent-test.jsx | 4 +- test/modules/ReactStatelessComponent-test.jsx | 4 +- test/modules/component.spec.jsx | 26 +- test/modules/event.spec.jsx | 4 +- test/modules/node.spec.jsx | 58 +-- test/modules/util.spec.js | 3 +- 20 files changed, 1282 insertions(+), 1141 deletions(-) diff --git a/dist/React.js b/dist/React.js index d9335fbea..01e9cdea2 100644 --- a/dist/React.js +++ b/dist/React.js @@ -726,7 +726,7 @@ function dispatchEvent(e, type, end) { triggerEventFlow(paths.reverse(), bubble, e); } options.async = false; - options.flushBatchedUpdates(); + options.flushUpdaters(); } function collectPaths(from, end) { @@ -1014,19 +1014,19 @@ var PropTypes = { * @param {any} props * @param {any} context */ -var mountOrder = 1; +//var mountOrder = 1; function Component(props, context) { //防止用户在构造器生成JSX CurrentOwner.cur = this; - this.__mountOrder = mountOrder++; + // this.__mountOrder = mountOrder++; this.context = context; this.props = props; this.refs = {}; this.state = null; - this.__pendingCallbacks = []; - this.__pendingStates = []; - this.__current = noop; //用于DevTools工具中,通过实例找到生成它的那个虚拟DOM - this.__lifeStage = 0; //判断生命周期 + // this.__pendingCallbacks = []; + // this.__pendingStates = []; + // this.__current = noop; //用于DevTools工具中,通过实例找到生成它的那个虚拟DOM + // this.__lifeStage = 0; //判断生命周期 /* * this.__dom = dom 用于isMounted或ReactDOM.findDOMNode方法 * this.__hydrating = true 表示组件正在根据虚拟DOM合成真实DOM @@ -1041,88 +1041,72 @@ Component.prototype = { deprecatedWarn("replaceState"); }, setState: function setState(state, cb) { - debounceSetState(this, state, cb); + debounceSetState(this.updater, state, cb); }, isMounted: function isMounted() { deprecatedWarn("isMounted"); - return !!this.__dom; + return !!(this.updater || {})._hostNode; }, forceUpdate: function forceUpdate(cb) { - debounceSetState(this, true, cb); - }, - - __mergeStates: function __mergeStates(props, context) { - var pendings = this.__pendingStates, - n = pendings.length; - if (n === 0) { - return this.state; - } - var state = extend({}, this.state); //每次都返回新的state - for (var i = 0; i < n; i++) { - var pending = pendings[i]; - if (isFn(pending)) { - pending = pending.call(this, state, props, context); - } - extend(state, pending); - } - pendings.length = 0; - return state; + debounceSetState(this.updater, true, cb); }, - render: function render() {} }; -function debounceSetState(a, b, c) { - if (a.__didUpdate) { +function debounceSetState(updater, state, cb) { + if (!updater) { + return; + } + if (updater._didUpdate) { //如果用户在componentDidUpdate中使用setState,要防止其卡死 setTimeout(function () { - a.__didUpdate = false; - setStateImpl.call(a, b, c); + updater._didUpdate = false; + setStateImpl(updater, state, cb); }, 300); return; } - setStateImpl.call(a, b, c); + setStateImpl(updater, state, cb); } -function setStateImpl(state, cb) { +function setStateImpl(updater, state, cb) { if (isFn(cb)) { - this.__pendingCallbacks.push(cb); + updater._pendingCallbacks.push(cb); } - var hasDOM = this.__dom; + var hasDOM = updater._hostNode; if (state === true) { //forceUpdate - this.__forceUpdate = true; + updater._forceUpdate = true; } else { //setState - this.__pendingStates.push(state); + updater._pendingStates.push(state); } if (!hasDOM) { //组件挂载期 //componentWillUpdate中的setState/forceUpdate应该被忽略 - if (this.__hydrating) { + if (updater._hydrating) { //在render方法中调用setState也会被延迟到下一周期更新.这存在两种情况, //1. 组件直接调用自己的setState //2. 子组件调用父组件的setState, - this.__renderInNextCycle = true; + updater._renderInNextCycle = true; } } else { //组件更新期 - if (this.__receiving) { + if (updater._receiving) { //componentWillReceiveProps中的setState/forceUpdate应该被忽略 return; } - this.__renderInNextCycle = true; + updater._renderInNextCycle = true; if (options.async) { //在事件句柄中执行setState会进行合并 - options.addTask(this); + options.enqueueUpdater(updater); return; } - if (this.__hydrating) { + if (updater._hydrating) { // 在componentDidMount里调用自己的setState,延迟到下一周期更新 // 在更新过程中, 子组件在componentWillReceiveProps里调用父组件的setState,延迟到下一周期更新 return; } // 不在生命周期钩子内执行setState - options.flushBatchedUpdates([this]); + options.flushUpdaters([updater]); } } @@ -1654,7 +1638,7 @@ var disposeStrategy = { function disposeStateless(vnode) { var instance = vnode._instance; if (instance) { - disposeVnode(instance.__rendered); + disposeVnode(instance.updater.rendered); vnode._instance = null; } } @@ -1681,7 +1665,7 @@ function disposeComponent(vnode) { var instance = vnode._instance; if (instance) { options.beforeUnmount(instance); - instance.__current = instance.setState = instance.forceUpdate = noop; + instance.setState = instance.forceUpdate = noop; if (vnode.ref) { vnode.ref(null); } @@ -1689,8 +1673,8 @@ function disposeComponent(vnode) { instance.componentWillUnmount(); } //在执行componentWillUnmount后才将关联的元素节点解绑,防止用户在钩子里调用 findDOMNode方法 - vnode.ref = instance.__dom = vnode._instance = null; - disposeVnode(instance.__rendered); + disposeVnode(instance.updater.rendered); + vnode.ref = vnode._instance = instance.updater = null; } } @@ -1878,16 +1862,101 @@ function getOptionSelected(option, selected) { dom.selected = selected; } +var mountOrder = 1; function alwaysNull() { return null; } -function instantiateComponent(type, vtype, props, context) { +function Updater(instance, vnode, props, context) { + //防止用户在构造器生成JSX + vnode._instance = instance; + instance.updater = this; + this._mountOrder = mountOrder++; + this._instance = instance; + this._pendingCallbacks = []; + this._pendingStates = []; + this._lifeStage = 0; //判断生命周期 + //update总是保存最新的数据,如state, props, context, parentContext, vparent + this.nextVnode = vnode; + this.props = props; + this.context = context; + if (instance.__isStateless) { + this.mergeStates = alwaysNull; + } +} + +Updater.prototype = { + mergeStates: function mergeStates() { + var instance = this._instance, + pendings = this._pendingStates, + state = instance.state, + n = pendings.length; + if (n === 0) { + return state; + } + var nextState = Object.assign({}, state); //每次都返回新的state + for (var i = 0; i < n; i++) { + var pending = pendings[i]; + if (pending && pending.call) { + pending = pending.call(instance, nextState, this.props); + } + Object.assign(nextState, pending); + } + pendings.length = 0; + return nextState; + }, + + renderComponent: function renderComponent(cb, rendered, parentInstance) { + var vnode = this.nextVnode; + var instance = this._instance; + var parentContext = this.parentContext; + //调整全局的 CurrentOwner.cur + if (!rendered) { + try { + var lastOwn = CurrentOwner.cur; + CurrentOwner.cur = instance; + rendered = instance.render(); + } finally { + CurrentOwner.cur = lastOwn; + } + } + + //组件只能返回组件或null + if (rendered === null || rendered === false) { + rendered = { type: "#comment", text: "empty", vtype: 0 }; + } else if (!rendered || !rendered.vtype) { + //true, undefined, array, {} + throw new Error("@" + vnode.type.name + "#render:You may have returned undefined, an array or some other invalid object"); + } + this.lastRendered = this.rendered; + this.rendered = rendered; + var childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; + var dom = cb(rendered, this.vparent, childContext); + if (parentInstance) { + this._parentInstance = parentInstance; + } + + updateInstanceChain(this, dom); + + return dom; + } +}; + +function updateInstanceChain(updater, dom) { + updater.nextVnode._hostNode = updater._hostNode = dom; //同步到生成组件的那个虚拟DOM中 + var parent = updater._parentInstance; + if (parent) { + updateInstanceChain(parent.updater, dom); + } +} + +function instantiateComponent(type, vnode, props, context) { var instance; - if (vtype === 2) { + if (vnode.vtype === 2) { instance = new type(props, context); //props, context是不可变的 instance.props = props; instance.context = context; + new Updater(instance, vnode, props, context); } else { instance = { refs: {}, @@ -1895,13 +1964,10 @@ function instantiateComponent(type, vtype, props, context) { return type(this.props, this.context); }, __isStateless: 1, - state: null, props: props, - context: context, - __pendingCallbacks: [], - __current: noop, - __mergeStates: alwaysNull + context: context }; + var updater = new Updater(instance, vnode, props, context); var lastOwn = CurrentOwner.cur; CurrentOwner.cur = instance; try { @@ -1914,7 +1980,7 @@ function instantiateComponent(type, vtype, props, context) { delete instance.__isStateless; Object.assign(instance, mixin); } else { - instance.__rendered = mixin; + updater.rendered = mixin; } } @@ -1929,20 +1995,20 @@ function clearRefs() { fn(); }); } -function callUpdate(instance) { - if (instance.__lifeStage === 2) { +function callUpdate(updater, instance) { + if (updater._lifeStage === 2) { if (pendingRefs.length) { clearRefs(); } if (instance.componentDidUpdate) { - instance.__didUpdate = true; - instance.componentDidUpdate(instance.lastProps, instance.lastState, instance.lastContext); - if (!instance.__renderInNextCycle) { - instance.__didUpdate = false; + updater._didUpdate = true; + instance.componentDidUpdate(updater.lastProps, updater.lastState, updater.lastContext); + if (!updater._renderInNextCycle) { + updater._didUpdate = false; } } options.afterUpdate(instance); - instance.__lifeStage = 1; + updater._lifeStage = 1; } } @@ -1954,9 +2020,10 @@ function drainQueue(queue) { var i = 0; while (i < queue.length) { //queue可能中途加入新元素, 因此不能直接使用queue.forEach(fn) - var instance = queue[i]; + var updater = queue[i], + instance = updater._instance; i++; - if (!instance.__lifeStage) { + if (!updater._lifeStage) { if (pendingRefs.length) { clearRefs(); } @@ -1964,25 +2031,25 @@ function drainQueue(queue) { instance.componentDidMount(); instance.componentDidMount = null; } - instance.__lifeStage = 1; + updater._lifeStage = 1; options.afterMount(instance); } else { - callUpdate(instance); + callUpdate(updater, instance); } - var ref = instance.__current.ref; + var ref = updater.nextVnode.ref; if (ref) { ref(instance.__isStateless ? null : instance); } - instance.__hydrating = false; //子树已经构建完毕 - while (instance.__renderInNextCycle) { - options.refreshComponent(instance, queue); - callUpdate(instance); + updater._hydrating = false; //子树已经构建完毕 + while (updater._renderInNextCycle) { + options.refreshComponent(updater, queue); + callUpdate(updater, instance); } } //再执行所有setState/forceUpdate回调,根据从下到上的顺序执行 - queue.sort(mountSorter).forEach(function (instance) { - clearArray(instance.__pendingCallbacks).forEach(function (fn) { - fn.call(instance); + queue.sort(mountSorter).forEach(function (updater) { + clearArray(updater._pendingCallbacks).forEach(function (fn) { + fn.call(updater._instance); }); }); queue.length = 0; @@ -1993,21 +2060,21 @@ function drainQueue(queue) { var dirtyComponents = []; dirtyComponents.isChildProcess = true; -function mountSorter(c1, c2) { +function mountSorter(u1, u2) { //让子节点先于父节点执行 - return c2.__mountOrder - c1.__mountOrder; + return u2._mountOrder - u1._mountOrder; } -options.flushBatchedUpdates = function (queue) { +options.flushUpdaters = function (queue) { if (!queue) { queue = dirtyComponents; } drainQueue(queue); }; -options.addTask = function (instance) { - if (dirtyComponents.indexOf(instance) == -1) { - dirtyComponents.push(instance); +options.enqueueUpdater = function (updater) { + if (dirtyComponents.indexOf(updater) == -1) { + dirtyComponents.push(updater); } }; @@ -2042,7 +2109,7 @@ function findDOMNode(ref) { if (ref.nodeType === 1) { return ref; } - return ref.__dom || null; + return ref.updater ? ref.updater._hostNode : ref._hostNode || null; } // 用于辅助XML元素的生成(svg, math), // 它们需要根据父节点的tagName与namespaceURI,知道自己是存在什么文档中 @@ -2117,7 +2184,6 @@ var patchStrategy = { function mountVnode(lastNode, vnode) { return patchStrategy[vnode.vtype].apply(null, arguments); } - function updateByContext(vnode) { if (vnode.type && vnode.type.contextTypes) { return true; @@ -2135,7 +2201,7 @@ function updateByContext(vnode) { } } } else if (vnode._instance) { - var ret = vnode._instance.__rendered; + var ret = vnode._instance.updater.rendered; if (updateByContext(ret)) { return true; } @@ -2238,89 +2304,54 @@ function alignChildren(parentNode, children, vparent, context, updateQueue) { } } -function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { +function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue, parentInstance) { var type = vnode.type, - vtype = vnode.vtype, props = vnode.props; - var context = getContextByTypes(parentContext, type.contextTypes); - var instance = instantiateComponent(type, vtype, props, context); //互相持有引用 + var instance = instantiateComponent(type, vnode, props, context); //互相持有引用 + var updater = instance.updater; - vnode._instance = instance; - - //用于refreshComponent - instance.nextVnode = vnode; - vnode.context = context; - vnode.parentContext = parentContext; - vnode.vparent = vparent; + updater.vparent = vparent; + updater.parentContext = parentContext; if (instance.componentWillMount) { instance.componentWillMount(); //这里可能执行了setState - instance.state = instance.__mergeStates(props, context); + instance.state = updater.mergeStates(); } - instance.__hydrating = true; - var dom = renderComponent(instance, vnode, function (nextRendered, childContext) { - return mountVnode(lastNode, nextRendered, vparent, childContext, updateQueue); - }, instance.__rendered); - - updateQueue.push(instance); - return dom; -} - -function renderComponent(instance, vnode, cb, rendered) { - //调整全局的 CurrentOwner.cur - if (!rendered) { - try { - var lastOwn = CurrentOwner.cur; - CurrentOwner.cur = instance; - rendered = instance.render(); - } finally { - CurrentOwner.cur = lastOwn; - } - } - - //组件只能返回组件或null - if (rendered === null || rendered === false) { - rendered = { type: "#comment", text: "empty", vtype: 0 }; - } else if (!rendered || !rendered.vtype) { - //true, undefined, array, {} - throw new Error("@" + vnode.type.name + "#render:You may have returned undefined, an array or some other invalid object"); - } + updater._hydrating = true; - vnode._instance = instance; - instance.lastRendered = instance.__rendered; - instance.__rendered = rendered; - var parentContext = vnode.parentContext; - var childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; - var dom = cb(rendered, childContext); + var dom = updater.renderComponent(function (nextRendered, vparent, childContext) { + return mountVnode(lastNode, nextRendered, vparent, childContext, updateQueue, instance //作为parentInstance往下传 + ); + }, updater.rendered, parentInstance); - createInstanceChain(instance, vnode, rendered); - updateInstanceChain(instance, dom); + updateQueue.push(updater); return dom; } -function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { +function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQueue) { var type = lastVnode.type, ref = lastVnode.ref, instance = lastVnode._instance, vtype = lastVnode.vtype; - var nextContext = void 0, nextProps = nextVnode.props, queue = void 0; + var updater = instance.updater; if (type.contextTypes) { - nextContext = getContextByTypes(context, type.contextTypes); + nextContext = getContextByTypes(parentContext, type.contextTypes); } else { nextContext = instance.context; //没有定义contextTypes就沿用旧的 } + if (instance.componentWillReceiveProps) { - instance.__receiving = true; + updater._receiving = true; instance.componentWillReceiveProps(nextProps, nextContext); - instance.__receiving = false; + updater._receiving = false; } //用于refreshComponent if (ref && vtype === 2) { @@ -2330,74 +2361,79 @@ function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { lastVnode.ref = nextVnode.ref; } } - instance.lastVnode = lastVnode; - instance.nextVnode = nextVnode; - nextVnode.context = nextContext; - nextVnode.parentContext = context; - nextVnode.vparent = vparent; + //updater上总是保持新的数据 + updater.lastVnode = lastVnode; + updater.nextVnode = nextVnode; + + updater.context = nextContext; + updater.props = nextProps; + updater.vparent = vparent; + updater.parentContext = parentContext; + if (updateQueue.isChildProcess) { queue = updateQueue; } else { queue = []; queue.isChildProcess = true; } - refreshComponent(instance, queue); + refreshComponent(updater, queue); //子组件先执行 - updateQueue.push(instance); + updateQueue.push(updater); - return instance.__dom; + return updater._hostNode; } -function refreshComponent(instance, updateQueue) { - var lastProps = instance.props, - lastState = instance.state, - lastContext = instance.context, - lastRendered = instance.__rendered, - dom = instance.__dom; +function refreshComponent(updater, updateQueue) { + var instance = updater._instance, + dom = updater._hostNode, + nextContext = updater.context, + nextProps = updater.props, + nextVnode = updater.nextVnode, + lastVnode = updater.lastVnode; - instance.__renderInNextCycle = null; - var nextVnode = instance.nextVnode; - var nextContext = nextVnode.context; - var nextProps = nextVnode.props; - var vparent = nextVnode.vparent; nextVnode._instance = instance; //important + updater._renderInNextCycle = null; - var nextState = instance.__mergeStates(nextProps, nextContext); + var nextState = updater.mergeStates(); var shouldUpdate = true; - if (!instance.__forceUpdate && instance.shouldComponentUpdate && !instance.shouldComponentUpdate(nextProps, nextState, nextContext)) { + if (!updater._forceUpdate && instance.shouldComponentUpdate && !instance.shouldComponentUpdate(nextProps, nextState, nextContext)) { shouldUpdate = false; } else if (instance.componentWillUpdate) { instance.componentWillUpdate(nextProps, nextState, nextContext); } + var lastProps = instance.props, + lastContext = instance.context, + lastState = instance.state; + + + updater._forceUpdate = false; - instance.__forceUpdate = false; instance.props = nextProps; instance.context = nextContext; instance.state = nextState; //既然setState了,无论shouldComponentUpdate结果如何,用户传给的state对象都会作用到组件上 if (!shouldUpdate) { - var lastVnode = instance.lastVnode; for (var i in lastVnode) { if (!nextVnode.hasOwnProperty(i)) { nextVnode[i] = lastVnode[i]; } } - instance.__current = nextVnode; return dom; } - instance.__hydrating = true; - instance.lastProps = lastProps; - instance.lastState = lastState; - instance.lastContext = lastContext; + updater._hydrating = true; + updater.lastProps = lastProps; + updater.lastState = lastState; + updater.lastContext = lastContext; + + var lastRendered = updater.rendered; //这里会更新instance的props, context, state - dom = renderComponent(instance, nextVnode, function (nextRendered, childContext) { - return alignVnode(lastRendered, nextRendered, vparent, childContext, updateQueue); + dom = updater.renderComponent(function (nextRendered, vparent, childContext) { + return alignVnode(lastRendered, nextRendered, vparent, childContext, updateQueue, instance); }); - - instance.__lifeStage = 2; - instance.__hydrating = false; + updater._lifeStage = 2; + updater._hydrating = false; if (updateQueue.isChildProcess) { drainQueue(updateQueue); } @@ -2406,14 +2442,14 @@ function refreshComponent(instance, updateQueue) { } options.refreshComponent = refreshComponent; -function alignVnode(lastVnode, nextVnode, vparent, context, updateQueue) { +function alignVnode(lastVnode, nextVnode, vparent, context, updateQueue, parentInstance) { var node = lastVnode._hostNode, dom = void 0; if (isSameNode(lastVnode, nextVnode)) { dom = updateVnode(lastVnode, nextVnode, vparent, context, updateQueue); } else { disposeVnode(lastVnode); - dom = mountVnode(null, nextVnode, vparent, context, updateQueue); + dom = mountVnode(null, nextVnode, vparent, context, updateQueue, parentInstance); var p = node.parentNode; if (p) { p.replaceChild(dom, node); @@ -2582,21 +2618,6 @@ function isSameNode(a, b) { } } -function createInstanceChain(instance, vnode, rendered) { - instance.__current = vnode; - if (rendered._instance) { - rendered._instance.__parentInstance = instance; - } -} - -function updateInstanceChain(instance, dom) { - instance.__dom = instance.__current._hostNode = dom; - var parent = instance.__parentInstance; - if (parent) { - updateInstanceChain(parent, dom); - } -} - var React = { version: "1.1.2", render: render, diff --git a/dist/ReactIE.js b/dist/ReactIE.js index 0ebd59fed..0b07deb08 100644 --- a/dist/ReactIE.js +++ b/dist/ReactIE.js @@ -725,7 +725,7 @@ function dispatchEvent(e, type, end) { triggerEventFlow(paths.reverse(), bubble, e); } options.async = false; - options.flushBatchedUpdates(); + options.flushUpdaters(); } function collectPaths(from, end) { @@ -1013,19 +1013,19 @@ var PropTypes = { * @param {any} props * @param {any} context */ -var mountOrder = 1; +//var mountOrder = 1; function Component(props, context) { //防止用户在构造器生成JSX CurrentOwner.cur = this; - this.__mountOrder = mountOrder++; + // this.__mountOrder = mountOrder++; this.context = context; this.props = props; this.refs = {}; this.state = null; - this.__pendingCallbacks = []; - this.__pendingStates = []; - this.__current = noop; //用于DevTools工具中,通过实例找到生成它的那个虚拟DOM - this.__lifeStage = 0; //判断生命周期 + // this.__pendingCallbacks = []; + // this.__pendingStates = []; + // this.__current = noop; //用于DevTools工具中,通过实例找到生成它的那个虚拟DOM + // this.__lifeStage = 0; //判断生命周期 /* * this.__dom = dom 用于isMounted或ReactDOM.findDOMNode方法 * this.__hydrating = true 表示组件正在根据虚拟DOM合成真实DOM @@ -1040,88 +1040,72 @@ Component.prototype = { deprecatedWarn("replaceState"); }, setState: function setState(state, cb) { - debounceSetState(this, state, cb); + debounceSetState(this.updater, state, cb); }, isMounted: function isMounted() { deprecatedWarn("isMounted"); - return !!this.__dom; + return !!(this.updater || {})._hostNode; }, forceUpdate: function forceUpdate(cb) { - debounceSetState(this, true, cb); - }, - - __mergeStates: function __mergeStates(props, context) { - var pendings = this.__pendingStates, - n = pendings.length; - if (n === 0) { - return this.state; - } - var state = extend({}, this.state); //每次都返回新的state - for (var i = 0; i < n; i++) { - var pending = pendings[i]; - if (isFn(pending)) { - pending = pending.call(this, state, props, context); - } - extend(state, pending); - } - pendings.length = 0; - return state; + debounceSetState(this.updater, true, cb); }, - render: function render() {} }; -function debounceSetState(a, b, c) { - if (a.__didUpdate) { +function debounceSetState(updater, state, cb) { + if (!updater) { + return; + } + if (updater._didUpdate) { //如果用户在componentDidUpdate中使用setState,要防止其卡死 setTimeout(function () { - a.__didUpdate = false; - setStateImpl.call(a, b, c); + updater._didUpdate = false; + setStateImpl(updater, state, cb); }, 300); return; } - setStateImpl.call(a, b, c); + setStateImpl(updater, state, cb); } -function setStateImpl(state, cb) { +function setStateImpl(updater, state, cb) { if (isFn(cb)) { - this.__pendingCallbacks.push(cb); + updater._pendingCallbacks.push(cb); } - var hasDOM = this.__dom; + var hasDOM = updater._hostNode; if (state === true) { //forceUpdate - this.__forceUpdate = true; + updater._forceUpdate = true; } else { //setState - this.__pendingStates.push(state); + updater._pendingStates.push(state); } if (!hasDOM) { //组件挂载期 //componentWillUpdate中的setState/forceUpdate应该被忽略 - if (this.__hydrating) { + if (updater._hydrating) { //在render方法中调用setState也会被延迟到下一周期更新.这存在两种情况, //1. 组件直接调用自己的setState //2. 子组件调用父组件的setState, - this.__renderInNextCycle = true; + updater._renderInNextCycle = true; } } else { //组件更新期 - if (this.__receiving) { + if (updater._receiving) { //componentWillReceiveProps中的setState/forceUpdate应该被忽略 return; } - this.__renderInNextCycle = true; + updater._renderInNextCycle = true; if (options.async) { //在事件句柄中执行setState会进行合并 - options.addTask(this); + options.enqueueUpdater(updater); return; } - if (this.__hydrating) { + if (updater._hydrating) { // 在componentDidMount里调用自己的setState,延迟到下一周期更新 // 在更新过程中, 子组件在componentWillReceiveProps里调用父组件的setState,延迟到下一周期更新 return; } // 不在生命周期钩子内执行setState - options.flushBatchedUpdates([this]); + options.flushUpdaters([updater]); } } @@ -1653,7 +1637,7 @@ var disposeStrategy = { function disposeStateless(vnode) { var instance = vnode._instance; if (instance) { - disposeVnode(instance.__rendered); + disposeVnode(instance.updater.rendered); vnode._instance = null; } } @@ -1680,7 +1664,7 @@ function disposeComponent(vnode) { var instance = vnode._instance; if (instance) { options.beforeUnmount(instance); - instance.__current = instance.setState = instance.forceUpdate = noop; + instance.setState = instance.forceUpdate = noop; if (vnode.ref) { vnode.ref(null); } @@ -1688,8 +1672,8 @@ function disposeComponent(vnode) { instance.componentWillUnmount(); } //在执行componentWillUnmount后才将关联的元素节点解绑,防止用户在钩子里调用 findDOMNode方法 - vnode.ref = instance.__dom = vnode._instance = null; - disposeVnode(instance.__rendered); + disposeVnode(instance.updater.rendered); + vnode.ref = vnode._instance = instance.updater = null; } } @@ -1877,16 +1861,101 @@ function getOptionSelected(option, selected) { dom.selected = selected; } +var mountOrder = 1; function alwaysNull() { return null; } -function instantiateComponent(type, vtype, props, context) { +function Updater(instance, vnode, props, context) { + //防止用户在构造器生成JSX + vnode._instance = instance; + instance.updater = this; + this._mountOrder = mountOrder++; + this._instance = instance; + this._pendingCallbacks = []; + this._pendingStates = []; + this._lifeStage = 0; //判断生命周期 + //update总是保存最新的数据,如state, props, context, parentContext, vparent + this.nextVnode = vnode; + this.props = props; + this.context = context; + if (instance.__isStateless) { + this.mergeStates = alwaysNull; + } +} + +Updater.prototype = { + mergeStates: function mergeStates() { + var instance = this._instance, + pendings = this._pendingStates, + state = instance.state, + n = pendings.length; + if (n === 0) { + return state; + } + var nextState = Object.assign({}, state); //每次都返回新的state + for (var i = 0; i < n; i++) { + var pending = pendings[i]; + if (pending && pending.call) { + pending = pending.call(instance, nextState, this.props); + } + Object.assign(nextState, pending); + } + pendings.length = 0; + return nextState; + }, + + renderComponent: function renderComponent(cb, rendered, parentInstance) { + var vnode = this.nextVnode; + var instance = this._instance; + var parentContext = this.parentContext; + //调整全局的 CurrentOwner.cur + if (!rendered) { + try { + var lastOwn = CurrentOwner.cur; + CurrentOwner.cur = instance; + rendered = instance.render(); + } finally { + CurrentOwner.cur = lastOwn; + } + } + + //组件只能返回组件或null + if (rendered === null || rendered === false) { + rendered = { type: "#comment", text: "empty", vtype: 0 }; + } else if (!rendered || !rendered.vtype) { + //true, undefined, array, {} + throw new Error("@" + vnode.type.name + "#render:You may have returned undefined, an array or some other invalid object"); + } + this.lastRendered = this.rendered; + this.rendered = rendered; + var childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; + var dom = cb(rendered, this.vparent, childContext); + if (parentInstance) { + this._parentInstance = parentInstance; + } + + updateInstanceChain(this, dom); + + return dom; + } +}; + +function updateInstanceChain(updater, dom) { + updater.nextVnode._hostNode = updater._hostNode = dom; //同步到生成组件的那个虚拟DOM中 + var parent = updater._parentInstance; + if (parent) { + updateInstanceChain(parent.updater, dom); + } +} + +function instantiateComponent(type, vnode, props, context) { var instance; - if (vtype === 2) { + if (vnode.vtype === 2) { instance = new type(props, context); //props, context是不可变的 instance.props = props; instance.context = context; + new Updater(instance, vnode, props, context); } else { instance = { refs: {}, @@ -1894,13 +1963,10 @@ function instantiateComponent(type, vtype, props, context) { return type(this.props, this.context); }, __isStateless: 1, - state: null, props: props, - context: context, - __pendingCallbacks: [], - __current: noop, - __mergeStates: alwaysNull + context: context }; + var updater = new Updater(instance, vnode, props, context); var lastOwn = CurrentOwner.cur; CurrentOwner.cur = instance; try { @@ -1913,7 +1979,7 @@ function instantiateComponent(type, vtype, props, context) { delete instance.__isStateless; Object.assign(instance, mixin); } else { - instance.__rendered = mixin; + updater.rendered = mixin; } } @@ -1928,20 +1994,20 @@ function clearRefs() { fn(); }); } -function callUpdate(instance) { - if (instance.__lifeStage === 2) { +function callUpdate(updater, instance) { + if (updater._lifeStage === 2) { if (pendingRefs.length) { clearRefs(); } if (instance.componentDidUpdate) { - instance.__didUpdate = true; - instance.componentDidUpdate(instance.lastProps, instance.lastState, instance.lastContext); - if (!instance.__renderInNextCycle) { - instance.__didUpdate = false; + updater._didUpdate = true; + instance.componentDidUpdate(updater.lastProps, updater.lastState, updater.lastContext); + if (!updater._renderInNextCycle) { + updater._didUpdate = false; } } options.afterUpdate(instance); - instance.__lifeStage = 1; + updater._lifeStage = 1; } } @@ -1953,9 +2019,10 @@ function drainQueue(queue) { var i = 0; while (i < queue.length) { //queue可能中途加入新元素, 因此不能直接使用queue.forEach(fn) - var instance = queue[i]; + var updater = queue[i], + instance = updater._instance; i++; - if (!instance.__lifeStage) { + if (!updater._lifeStage) { if (pendingRefs.length) { clearRefs(); } @@ -1963,25 +2030,25 @@ function drainQueue(queue) { instance.componentDidMount(); instance.componentDidMount = null; } - instance.__lifeStage = 1; + updater._lifeStage = 1; options.afterMount(instance); } else { - callUpdate(instance); + callUpdate(updater, instance); } - var ref = instance.__current.ref; + var ref = updater.nextVnode.ref; if (ref) { ref(instance.__isStateless ? null : instance); } - instance.__hydrating = false; //子树已经构建完毕 - while (instance.__renderInNextCycle) { - options.refreshComponent(instance, queue); - callUpdate(instance); + updater._hydrating = false; //子树已经构建完毕 + while (updater._renderInNextCycle) { + options.refreshComponent(updater, queue); + callUpdate(updater, instance); } } //再执行所有setState/forceUpdate回调,根据从下到上的顺序执行 - queue.sort(mountSorter).forEach(function (instance) { - clearArray(instance.__pendingCallbacks).forEach(function (fn) { - fn.call(instance); + queue.sort(mountSorter).forEach(function (updater) { + clearArray(updater._pendingCallbacks).forEach(function (fn) { + fn.call(updater._instance); }); }); queue.length = 0; @@ -1992,21 +2059,21 @@ function drainQueue(queue) { var dirtyComponents = []; dirtyComponents.isChildProcess = true; -function mountSorter(c1, c2) { +function mountSorter(u1, u2) { //让子节点先于父节点执行 - return c2.__mountOrder - c1.__mountOrder; + return u2._mountOrder - u1._mountOrder; } -options.flushBatchedUpdates = function (queue) { +options.flushUpdaters = function (queue) { if (!queue) { queue = dirtyComponents; } drainQueue(queue); }; -options.addTask = function (instance) { - if (dirtyComponents.indexOf(instance) == -1) { - dirtyComponents.push(instance); +options.enqueueUpdater = function (updater) { + if (dirtyComponents.indexOf(updater) == -1) { + dirtyComponents.push(updater); } }; @@ -2041,7 +2108,7 @@ function findDOMNode(ref) { if (ref.nodeType === 1) { return ref; } - return ref.__dom || null; + return ref.updater ? ref.updater._hostNode : ref._hostNode || null; } // 用于辅助XML元素的生成(svg, math), // 它们需要根据父节点的tagName与namespaceURI,知道自己是存在什么文档中 @@ -2116,7 +2183,6 @@ var patchStrategy = { function mountVnode(lastNode, vnode) { return patchStrategy[vnode.vtype].apply(null, arguments); } - function updateByContext(vnode) { if (vnode.type && vnode.type.contextTypes) { return true; @@ -2134,7 +2200,7 @@ function updateByContext(vnode) { } } } else if (vnode._instance) { - var ret = vnode._instance.__rendered; + var ret = vnode._instance.updater.rendered; if (updateByContext(ret)) { return true; } @@ -2237,89 +2303,54 @@ function alignChildren(parentNode, children, vparent, context, updateQueue) { } } -function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { +function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue, parentInstance) { var type = vnode.type, - vtype = vnode.vtype, props = vnode.props; - var context = getContextByTypes(parentContext, type.contextTypes); - var instance = instantiateComponent(type, vtype, props, context); //互相持有引用 + var instance = instantiateComponent(type, vnode, props, context); //互相持有引用 + var updater = instance.updater; - vnode._instance = instance; - - //用于refreshComponent - instance.nextVnode = vnode; - vnode.context = context; - vnode.parentContext = parentContext; - vnode.vparent = vparent; + updater.vparent = vparent; + updater.parentContext = parentContext; if (instance.componentWillMount) { instance.componentWillMount(); //这里可能执行了setState - instance.state = instance.__mergeStates(props, context); + instance.state = updater.mergeStates(); } - instance.__hydrating = true; - var dom = renderComponent(instance, vnode, function (nextRendered, childContext) { - return mountVnode(lastNode, nextRendered, vparent, childContext, updateQueue); - }, instance.__rendered); - - updateQueue.push(instance); - return dom; -} - -function renderComponent(instance, vnode, cb, rendered) { - //调整全局的 CurrentOwner.cur - if (!rendered) { - try { - var lastOwn = CurrentOwner.cur; - CurrentOwner.cur = instance; - rendered = instance.render(); - } finally { - CurrentOwner.cur = lastOwn; - } - } - - //组件只能返回组件或null - if (rendered === null || rendered === false) { - rendered = { type: "#comment", text: "empty", vtype: 0 }; - } else if (!rendered || !rendered.vtype) { - //true, undefined, array, {} - throw new Error("@" + vnode.type.name + "#render:You may have returned undefined, an array or some other invalid object"); - } + updater._hydrating = true; - vnode._instance = instance; - instance.lastRendered = instance.__rendered; - instance.__rendered = rendered; - var parentContext = vnode.parentContext; - var childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; - var dom = cb(rendered, childContext); + var dom = updater.renderComponent(function (nextRendered, vparent, childContext) { + return mountVnode(lastNode, nextRendered, vparent, childContext, updateQueue, instance //作为parentInstance往下传 + ); + }, updater.rendered, parentInstance); - createInstanceChain(instance, vnode, rendered); - updateInstanceChain(instance, dom); + updateQueue.push(updater); return dom; } -function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { +function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQueue) { var type = lastVnode.type, ref = lastVnode.ref, instance = lastVnode._instance, vtype = lastVnode.vtype; - var nextContext = void 0, nextProps = nextVnode.props, queue = void 0; + var updater = instance.updater; if (type.contextTypes) { - nextContext = getContextByTypes(context, type.contextTypes); + nextContext = getContextByTypes(parentContext, type.contextTypes); } else { nextContext = instance.context; //没有定义contextTypes就沿用旧的 } + if (instance.componentWillReceiveProps) { - instance.__receiving = true; + updater._receiving = true; instance.componentWillReceiveProps(nextProps, nextContext); - instance.__receiving = false; + updater._receiving = false; } //用于refreshComponent if (ref && vtype === 2) { @@ -2329,74 +2360,79 @@ function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { lastVnode.ref = nextVnode.ref; } } - instance.lastVnode = lastVnode; - instance.nextVnode = nextVnode; - nextVnode.context = nextContext; - nextVnode.parentContext = context; - nextVnode.vparent = vparent; + //updater上总是保持新的数据 + updater.lastVnode = lastVnode; + updater.nextVnode = nextVnode; + + updater.context = nextContext; + updater.props = nextProps; + updater.vparent = vparent; + updater.parentContext = parentContext; + if (updateQueue.isChildProcess) { queue = updateQueue; } else { queue = []; queue.isChildProcess = true; } - refreshComponent(instance, queue); + refreshComponent(updater, queue); //子组件先执行 - updateQueue.push(instance); + updateQueue.push(updater); - return instance.__dom; + return updater._hostNode; } -function refreshComponent(instance, updateQueue) { - var lastProps = instance.props, - lastState = instance.state, - lastContext = instance.context, - lastRendered = instance.__rendered, - dom = instance.__dom; +function refreshComponent(updater, updateQueue) { + var instance = updater._instance, + dom = updater._hostNode, + nextContext = updater.context, + nextProps = updater.props, + nextVnode = updater.nextVnode, + lastVnode = updater.lastVnode; - instance.__renderInNextCycle = null; - var nextVnode = instance.nextVnode; - var nextContext = nextVnode.context; - var nextProps = nextVnode.props; - var vparent = nextVnode.vparent; nextVnode._instance = instance; //important + updater._renderInNextCycle = null; - var nextState = instance.__mergeStates(nextProps, nextContext); + var nextState = updater.mergeStates(); var shouldUpdate = true; - if (!instance.__forceUpdate && instance.shouldComponentUpdate && !instance.shouldComponentUpdate(nextProps, nextState, nextContext)) { + if (!updater._forceUpdate && instance.shouldComponentUpdate && !instance.shouldComponentUpdate(nextProps, nextState, nextContext)) { shouldUpdate = false; } else if (instance.componentWillUpdate) { instance.componentWillUpdate(nextProps, nextState, nextContext); } + var lastProps = instance.props, + lastContext = instance.context, + lastState = instance.state; + + + updater._forceUpdate = false; - instance.__forceUpdate = false; instance.props = nextProps; instance.context = nextContext; instance.state = nextState; //既然setState了,无论shouldComponentUpdate结果如何,用户传给的state对象都会作用到组件上 if (!shouldUpdate) { - var lastVnode = instance.lastVnode; for (var i in lastVnode) { if (!nextVnode.hasOwnProperty(i)) { nextVnode[i] = lastVnode[i]; } } - instance.__current = nextVnode; return dom; } - instance.__hydrating = true; - instance.lastProps = lastProps; - instance.lastState = lastState; - instance.lastContext = lastContext; + updater._hydrating = true; + updater.lastProps = lastProps; + updater.lastState = lastState; + updater.lastContext = lastContext; + + var lastRendered = updater.rendered; //这里会更新instance的props, context, state - dom = renderComponent(instance, nextVnode, function (nextRendered, childContext) { - return alignVnode(lastRendered, nextRendered, vparent, childContext, updateQueue); + dom = updater.renderComponent(function (nextRendered, vparent, childContext) { + return alignVnode(lastRendered, nextRendered, vparent, childContext, updateQueue, instance); }); - - instance.__lifeStage = 2; - instance.__hydrating = false; + updater._lifeStage = 2; + updater._hydrating = false; if (updateQueue.isChildProcess) { drainQueue(updateQueue); } @@ -2405,14 +2441,14 @@ function refreshComponent(instance, updateQueue) { } options.refreshComponent = refreshComponent; -function alignVnode(lastVnode, nextVnode, vparent, context, updateQueue) { +function alignVnode(lastVnode, nextVnode, vparent, context, updateQueue, parentInstance) { var node = lastVnode._hostNode, dom = void 0; if (isSameNode(lastVnode, nextVnode)) { dom = updateVnode(lastVnode, nextVnode, vparent, context, updateQueue); } else { disposeVnode(lastVnode); - dom = mountVnode(null, nextVnode, vparent, context, updateQueue); + dom = mountVnode(null, nextVnode, vparent, context, updateQueue, parentInstance); var p = node.parentNode; if (p) { p.replaceChild(dom, node); @@ -2581,21 +2617,6 @@ function isSameNode(a, b) { } } -function createInstanceChain(instance, vnode, rendered) { - instance.__current = vnode; - if (rendered._instance) { - rendered._instance.__parentInstance = instance; - } -} - -function updateInstanceChain(instance, dom) { - instance.__dom = instance.__current._hostNode = dom; - var parent = instance.__parentInstance; - if (parent) { - updateInstanceChain(parent, dom); - } -} - //IE8中select.value不会在onchange事件中随用户的选中而改变其value值,也不让用户直接修改value 只能通过这个hack改变 var noCheck = false; function setSelectValue(e) { diff --git a/dist/ReactSelection.js b/dist/ReactSelection.js index 0ebd59fed..0b07deb08 100644 --- a/dist/ReactSelection.js +++ b/dist/ReactSelection.js @@ -725,7 +725,7 @@ function dispatchEvent(e, type, end) { triggerEventFlow(paths.reverse(), bubble, e); } options.async = false; - options.flushBatchedUpdates(); + options.flushUpdaters(); } function collectPaths(from, end) { @@ -1013,19 +1013,19 @@ var PropTypes = { * @param {any} props * @param {any} context */ -var mountOrder = 1; +//var mountOrder = 1; function Component(props, context) { //防止用户在构造器生成JSX CurrentOwner.cur = this; - this.__mountOrder = mountOrder++; + // this.__mountOrder = mountOrder++; this.context = context; this.props = props; this.refs = {}; this.state = null; - this.__pendingCallbacks = []; - this.__pendingStates = []; - this.__current = noop; //用于DevTools工具中,通过实例找到生成它的那个虚拟DOM - this.__lifeStage = 0; //判断生命周期 + // this.__pendingCallbacks = []; + // this.__pendingStates = []; + // this.__current = noop; //用于DevTools工具中,通过实例找到生成它的那个虚拟DOM + // this.__lifeStage = 0; //判断生命周期 /* * this.__dom = dom 用于isMounted或ReactDOM.findDOMNode方法 * this.__hydrating = true 表示组件正在根据虚拟DOM合成真实DOM @@ -1040,88 +1040,72 @@ Component.prototype = { deprecatedWarn("replaceState"); }, setState: function setState(state, cb) { - debounceSetState(this, state, cb); + debounceSetState(this.updater, state, cb); }, isMounted: function isMounted() { deprecatedWarn("isMounted"); - return !!this.__dom; + return !!(this.updater || {})._hostNode; }, forceUpdate: function forceUpdate(cb) { - debounceSetState(this, true, cb); - }, - - __mergeStates: function __mergeStates(props, context) { - var pendings = this.__pendingStates, - n = pendings.length; - if (n === 0) { - return this.state; - } - var state = extend({}, this.state); //每次都返回新的state - for (var i = 0; i < n; i++) { - var pending = pendings[i]; - if (isFn(pending)) { - pending = pending.call(this, state, props, context); - } - extend(state, pending); - } - pendings.length = 0; - return state; + debounceSetState(this.updater, true, cb); }, - render: function render() {} }; -function debounceSetState(a, b, c) { - if (a.__didUpdate) { +function debounceSetState(updater, state, cb) { + if (!updater) { + return; + } + if (updater._didUpdate) { //如果用户在componentDidUpdate中使用setState,要防止其卡死 setTimeout(function () { - a.__didUpdate = false; - setStateImpl.call(a, b, c); + updater._didUpdate = false; + setStateImpl(updater, state, cb); }, 300); return; } - setStateImpl.call(a, b, c); + setStateImpl(updater, state, cb); } -function setStateImpl(state, cb) { +function setStateImpl(updater, state, cb) { if (isFn(cb)) { - this.__pendingCallbacks.push(cb); + updater._pendingCallbacks.push(cb); } - var hasDOM = this.__dom; + var hasDOM = updater._hostNode; if (state === true) { //forceUpdate - this.__forceUpdate = true; + updater._forceUpdate = true; } else { //setState - this.__pendingStates.push(state); + updater._pendingStates.push(state); } if (!hasDOM) { //组件挂载期 //componentWillUpdate中的setState/forceUpdate应该被忽略 - if (this.__hydrating) { + if (updater._hydrating) { //在render方法中调用setState也会被延迟到下一周期更新.这存在两种情况, //1. 组件直接调用自己的setState //2. 子组件调用父组件的setState, - this.__renderInNextCycle = true; + updater._renderInNextCycle = true; } } else { //组件更新期 - if (this.__receiving) { + if (updater._receiving) { //componentWillReceiveProps中的setState/forceUpdate应该被忽略 return; } - this.__renderInNextCycle = true; + updater._renderInNextCycle = true; if (options.async) { //在事件句柄中执行setState会进行合并 - options.addTask(this); + options.enqueueUpdater(updater); return; } - if (this.__hydrating) { + if (updater._hydrating) { // 在componentDidMount里调用自己的setState,延迟到下一周期更新 // 在更新过程中, 子组件在componentWillReceiveProps里调用父组件的setState,延迟到下一周期更新 return; } // 不在生命周期钩子内执行setState - options.flushBatchedUpdates([this]); + options.flushUpdaters([updater]); } } @@ -1653,7 +1637,7 @@ var disposeStrategy = { function disposeStateless(vnode) { var instance = vnode._instance; if (instance) { - disposeVnode(instance.__rendered); + disposeVnode(instance.updater.rendered); vnode._instance = null; } } @@ -1680,7 +1664,7 @@ function disposeComponent(vnode) { var instance = vnode._instance; if (instance) { options.beforeUnmount(instance); - instance.__current = instance.setState = instance.forceUpdate = noop; + instance.setState = instance.forceUpdate = noop; if (vnode.ref) { vnode.ref(null); } @@ -1688,8 +1672,8 @@ function disposeComponent(vnode) { instance.componentWillUnmount(); } //在执行componentWillUnmount后才将关联的元素节点解绑,防止用户在钩子里调用 findDOMNode方法 - vnode.ref = instance.__dom = vnode._instance = null; - disposeVnode(instance.__rendered); + disposeVnode(instance.updater.rendered); + vnode.ref = vnode._instance = instance.updater = null; } } @@ -1877,16 +1861,101 @@ function getOptionSelected(option, selected) { dom.selected = selected; } +var mountOrder = 1; function alwaysNull() { return null; } -function instantiateComponent(type, vtype, props, context) { +function Updater(instance, vnode, props, context) { + //防止用户在构造器生成JSX + vnode._instance = instance; + instance.updater = this; + this._mountOrder = mountOrder++; + this._instance = instance; + this._pendingCallbacks = []; + this._pendingStates = []; + this._lifeStage = 0; //判断生命周期 + //update总是保存最新的数据,如state, props, context, parentContext, vparent + this.nextVnode = vnode; + this.props = props; + this.context = context; + if (instance.__isStateless) { + this.mergeStates = alwaysNull; + } +} + +Updater.prototype = { + mergeStates: function mergeStates() { + var instance = this._instance, + pendings = this._pendingStates, + state = instance.state, + n = pendings.length; + if (n === 0) { + return state; + } + var nextState = Object.assign({}, state); //每次都返回新的state + for (var i = 0; i < n; i++) { + var pending = pendings[i]; + if (pending && pending.call) { + pending = pending.call(instance, nextState, this.props); + } + Object.assign(nextState, pending); + } + pendings.length = 0; + return nextState; + }, + + renderComponent: function renderComponent(cb, rendered, parentInstance) { + var vnode = this.nextVnode; + var instance = this._instance; + var parentContext = this.parentContext; + //调整全局的 CurrentOwner.cur + if (!rendered) { + try { + var lastOwn = CurrentOwner.cur; + CurrentOwner.cur = instance; + rendered = instance.render(); + } finally { + CurrentOwner.cur = lastOwn; + } + } + + //组件只能返回组件或null + if (rendered === null || rendered === false) { + rendered = { type: "#comment", text: "empty", vtype: 0 }; + } else if (!rendered || !rendered.vtype) { + //true, undefined, array, {} + throw new Error("@" + vnode.type.name + "#render:You may have returned undefined, an array or some other invalid object"); + } + this.lastRendered = this.rendered; + this.rendered = rendered; + var childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; + var dom = cb(rendered, this.vparent, childContext); + if (parentInstance) { + this._parentInstance = parentInstance; + } + + updateInstanceChain(this, dom); + + return dom; + } +}; + +function updateInstanceChain(updater, dom) { + updater.nextVnode._hostNode = updater._hostNode = dom; //同步到生成组件的那个虚拟DOM中 + var parent = updater._parentInstance; + if (parent) { + updateInstanceChain(parent.updater, dom); + } +} + +function instantiateComponent(type, vnode, props, context) { var instance; - if (vtype === 2) { + if (vnode.vtype === 2) { instance = new type(props, context); //props, context是不可变的 instance.props = props; instance.context = context; + new Updater(instance, vnode, props, context); } else { instance = { refs: {}, @@ -1894,13 +1963,10 @@ function instantiateComponent(type, vtype, props, context) { return type(this.props, this.context); }, __isStateless: 1, - state: null, props: props, - context: context, - __pendingCallbacks: [], - __current: noop, - __mergeStates: alwaysNull + context: context }; + var updater = new Updater(instance, vnode, props, context); var lastOwn = CurrentOwner.cur; CurrentOwner.cur = instance; try { @@ -1913,7 +1979,7 @@ function instantiateComponent(type, vtype, props, context) { delete instance.__isStateless; Object.assign(instance, mixin); } else { - instance.__rendered = mixin; + updater.rendered = mixin; } } @@ -1928,20 +1994,20 @@ function clearRefs() { fn(); }); } -function callUpdate(instance) { - if (instance.__lifeStage === 2) { +function callUpdate(updater, instance) { + if (updater._lifeStage === 2) { if (pendingRefs.length) { clearRefs(); } if (instance.componentDidUpdate) { - instance.__didUpdate = true; - instance.componentDidUpdate(instance.lastProps, instance.lastState, instance.lastContext); - if (!instance.__renderInNextCycle) { - instance.__didUpdate = false; + updater._didUpdate = true; + instance.componentDidUpdate(updater.lastProps, updater.lastState, updater.lastContext); + if (!updater._renderInNextCycle) { + updater._didUpdate = false; } } options.afterUpdate(instance); - instance.__lifeStage = 1; + updater._lifeStage = 1; } } @@ -1953,9 +2019,10 @@ function drainQueue(queue) { var i = 0; while (i < queue.length) { //queue可能中途加入新元素, 因此不能直接使用queue.forEach(fn) - var instance = queue[i]; + var updater = queue[i], + instance = updater._instance; i++; - if (!instance.__lifeStage) { + if (!updater._lifeStage) { if (pendingRefs.length) { clearRefs(); } @@ -1963,25 +2030,25 @@ function drainQueue(queue) { instance.componentDidMount(); instance.componentDidMount = null; } - instance.__lifeStage = 1; + updater._lifeStage = 1; options.afterMount(instance); } else { - callUpdate(instance); + callUpdate(updater, instance); } - var ref = instance.__current.ref; + var ref = updater.nextVnode.ref; if (ref) { ref(instance.__isStateless ? null : instance); } - instance.__hydrating = false; //子树已经构建完毕 - while (instance.__renderInNextCycle) { - options.refreshComponent(instance, queue); - callUpdate(instance); + updater._hydrating = false; //子树已经构建完毕 + while (updater._renderInNextCycle) { + options.refreshComponent(updater, queue); + callUpdate(updater, instance); } } //再执行所有setState/forceUpdate回调,根据从下到上的顺序执行 - queue.sort(mountSorter).forEach(function (instance) { - clearArray(instance.__pendingCallbacks).forEach(function (fn) { - fn.call(instance); + queue.sort(mountSorter).forEach(function (updater) { + clearArray(updater._pendingCallbacks).forEach(function (fn) { + fn.call(updater._instance); }); }); queue.length = 0; @@ -1992,21 +2059,21 @@ function drainQueue(queue) { var dirtyComponents = []; dirtyComponents.isChildProcess = true; -function mountSorter(c1, c2) { +function mountSorter(u1, u2) { //让子节点先于父节点执行 - return c2.__mountOrder - c1.__mountOrder; + return u2._mountOrder - u1._mountOrder; } -options.flushBatchedUpdates = function (queue) { +options.flushUpdaters = function (queue) { if (!queue) { queue = dirtyComponents; } drainQueue(queue); }; -options.addTask = function (instance) { - if (dirtyComponents.indexOf(instance) == -1) { - dirtyComponents.push(instance); +options.enqueueUpdater = function (updater) { + if (dirtyComponents.indexOf(updater) == -1) { + dirtyComponents.push(updater); } }; @@ -2041,7 +2108,7 @@ function findDOMNode(ref) { if (ref.nodeType === 1) { return ref; } - return ref.__dom || null; + return ref.updater ? ref.updater._hostNode : ref._hostNode || null; } // 用于辅助XML元素的生成(svg, math), // 它们需要根据父节点的tagName与namespaceURI,知道自己是存在什么文档中 @@ -2116,7 +2183,6 @@ var patchStrategy = { function mountVnode(lastNode, vnode) { return patchStrategy[vnode.vtype].apply(null, arguments); } - function updateByContext(vnode) { if (vnode.type && vnode.type.contextTypes) { return true; @@ -2134,7 +2200,7 @@ function updateByContext(vnode) { } } } else if (vnode._instance) { - var ret = vnode._instance.__rendered; + var ret = vnode._instance.updater.rendered; if (updateByContext(ret)) { return true; } @@ -2237,89 +2303,54 @@ function alignChildren(parentNode, children, vparent, context, updateQueue) { } } -function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { +function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue, parentInstance) { var type = vnode.type, - vtype = vnode.vtype, props = vnode.props; - var context = getContextByTypes(parentContext, type.contextTypes); - var instance = instantiateComponent(type, vtype, props, context); //互相持有引用 + var instance = instantiateComponent(type, vnode, props, context); //互相持有引用 + var updater = instance.updater; - vnode._instance = instance; - - //用于refreshComponent - instance.nextVnode = vnode; - vnode.context = context; - vnode.parentContext = parentContext; - vnode.vparent = vparent; + updater.vparent = vparent; + updater.parentContext = parentContext; if (instance.componentWillMount) { instance.componentWillMount(); //这里可能执行了setState - instance.state = instance.__mergeStates(props, context); + instance.state = updater.mergeStates(); } - instance.__hydrating = true; - var dom = renderComponent(instance, vnode, function (nextRendered, childContext) { - return mountVnode(lastNode, nextRendered, vparent, childContext, updateQueue); - }, instance.__rendered); - - updateQueue.push(instance); - return dom; -} - -function renderComponent(instance, vnode, cb, rendered) { - //调整全局的 CurrentOwner.cur - if (!rendered) { - try { - var lastOwn = CurrentOwner.cur; - CurrentOwner.cur = instance; - rendered = instance.render(); - } finally { - CurrentOwner.cur = lastOwn; - } - } - - //组件只能返回组件或null - if (rendered === null || rendered === false) { - rendered = { type: "#comment", text: "empty", vtype: 0 }; - } else if (!rendered || !rendered.vtype) { - //true, undefined, array, {} - throw new Error("@" + vnode.type.name + "#render:You may have returned undefined, an array or some other invalid object"); - } + updater._hydrating = true; - vnode._instance = instance; - instance.lastRendered = instance.__rendered; - instance.__rendered = rendered; - var parentContext = vnode.parentContext; - var childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; - var dom = cb(rendered, childContext); + var dom = updater.renderComponent(function (nextRendered, vparent, childContext) { + return mountVnode(lastNode, nextRendered, vparent, childContext, updateQueue, instance //作为parentInstance往下传 + ); + }, updater.rendered, parentInstance); - createInstanceChain(instance, vnode, rendered); - updateInstanceChain(instance, dom); + updateQueue.push(updater); return dom; } -function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { +function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQueue) { var type = lastVnode.type, ref = lastVnode.ref, instance = lastVnode._instance, vtype = lastVnode.vtype; - var nextContext = void 0, nextProps = nextVnode.props, queue = void 0; + var updater = instance.updater; if (type.contextTypes) { - nextContext = getContextByTypes(context, type.contextTypes); + nextContext = getContextByTypes(parentContext, type.contextTypes); } else { nextContext = instance.context; //没有定义contextTypes就沿用旧的 } + if (instance.componentWillReceiveProps) { - instance.__receiving = true; + updater._receiving = true; instance.componentWillReceiveProps(nextProps, nextContext); - instance.__receiving = false; + updater._receiving = false; } //用于refreshComponent if (ref && vtype === 2) { @@ -2329,74 +2360,79 @@ function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { lastVnode.ref = nextVnode.ref; } } - instance.lastVnode = lastVnode; - instance.nextVnode = nextVnode; - nextVnode.context = nextContext; - nextVnode.parentContext = context; - nextVnode.vparent = vparent; + //updater上总是保持新的数据 + updater.lastVnode = lastVnode; + updater.nextVnode = nextVnode; + + updater.context = nextContext; + updater.props = nextProps; + updater.vparent = vparent; + updater.parentContext = parentContext; + if (updateQueue.isChildProcess) { queue = updateQueue; } else { queue = []; queue.isChildProcess = true; } - refreshComponent(instance, queue); + refreshComponent(updater, queue); //子组件先执行 - updateQueue.push(instance); + updateQueue.push(updater); - return instance.__dom; + return updater._hostNode; } -function refreshComponent(instance, updateQueue) { - var lastProps = instance.props, - lastState = instance.state, - lastContext = instance.context, - lastRendered = instance.__rendered, - dom = instance.__dom; +function refreshComponent(updater, updateQueue) { + var instance = updater._instance, + dom = updater._hostNode, + nextContext = updater.context, + nextProps = updater.props, + nextVnode = updater.nextVnode, + lastVnode = updater.lastVnode; - instance.__renderInNextCycle = null; - var nextVnode = instance.nextVnode; - var nextContext = nextVnode.context; - var nextProps = nextVnode.props; - var vparent = nextVnode.vparent; nextVnode._instance = instance; //important + updater._renderInNextCycle = null; - var nextState = instance.__mergeStates(nextProps, nextContext); + var nextState = updater.mergeStates(); var shouldUpdate = true; - if (!instance.__forceUpdate && instance.shouldComponentUpdate && !instance.shouldComponentUpdate(nextProps, nextState, nextContext)) { + if (!updater._forceUpdate && instance.shouldComponentUpdate && !instance.shouldComponentUpdate(nextProps, nextState, nextContext)) { shouldUpdate = false; } else if (instance.componentWillUpdate) { instance.componentWillUpdate(nextProps, nextState, nextContext); } + var lastProps = instance.props, + lastContext = instance.context, + lastState = instance.state; + + + updater._forceUpdate = false; - instance.__forceUpdate = false; instance.props = nextProps; instance.context = nextContext; instance.state = nextState; //既然setState了,无论shouldComponentUpdate结果如何,用户传给的state对象都会作用到组件上 if (!shouldUpdate) { - var lastVnode = instance.lastVnode; for (var i in lastVnode) { if (!nextVnode.hasOwnProperty(i)) { nextVnode[i] = lastVnode[i]; } } - instance.__current = nextVnode; return dom; } - instance.__hydrating = true; - instance.lastProps = lastProps; - instance.lastState = lastState; - instance.lastContext = lastContext; + updater._hydrating = true; + updater.lastProps = lastProps; + updater.lastState = lastState; + updater.lastContext = lastContext; + + var lastRendered = updater.rendered; //这里会更新instance的props, context, state - dom = renderComponent(instance, nextVnode, function (nextRendered, childContext) { - return alignVnode(lastRendered, nextRendered, vparent, childContext, updateQueue); + dom = updater.renderComponent(function (nextRendered, vparent, childContext) { + return alignVnode(lastRendered, nextRendered, vparent, childContext, updateQueue, instance); }); - - instance.__lifeStage = 2; - instance.__hydrating = false; + updater._lifeStage = 2; + updater._hydrating = false; if (updateQueue.isChildProcess) { drainQueue(updateQueue); } @@ -2405,14 +2441,14 @@ function refreshComponent(instance, updateQueue) { } options.refreshComponent = refreshComponent; -function alignVnode(lastVnode, nextVnode, vparent, context, updateQueue) { +function alignVnode(lastVnode, nextVnode, vparent, context, updateQueue, parentInstance) { var node = lastVnode._hostNode, dom = void 0; if (isSameNode(lastVnode, nextVnode)) { dom = updateVnode(lastVnode, nextVnode, vparent, context, updateQueue); } else { disposeVnode(lastVnode); - dom = mountVnode(null, nextVnode, vparent, context, updateQueue); + dom = mountVnode(null, nextVnode, vparent, context, updateQueue, parentInstance); var p = node.parentNode; if (p) { p.replaceChild(dom, node); @@ -2581,21 +2617,6 @@ function isSameNode(a, b) { } } -function createInstanceChain(instance, vnode, rendered) { - instance.__current = vnode; - if (rendered._instance) { - rendered._instance.__parentInstance = instance; - } -} - -function updateInstanceChain(instance, dom) { - instance.__dom = instance.__current._hostNode = dom; - var parent = instance.__parentInstance; - if (parent) { - updateInstanceChain(parent, dom); - } -} - //IE8中select.value不会在onchange事件中随用户的选中而改变其value值,也不让用户直接修改value 只能通过这个hack改变 var noCheck = false; function setSelectValue(e) { diff --git a/dist/ReactShim.js b/dist/ReactShim.js index 2c6522543..dd4d8b5b8 100644 --- a/dist/ReactShim.js +++ b/dist/ReactShim.js @@ -452,19 +452,19 @@ function callIteractor(iteratorFn, children) { * @param {any} props * @param {any} context */ -var mountOrder = 1; +//var mountOrder = 1; function Component(props, context) { //防止用户在构造器生成JSX CurrentOwner.cur = this; - this.__mountOrder = mountOrder++; + // this.__mountOrder = mountOrder++; this.context = context; this.props = props; this.refs = {}; this.state = null; - this.__pendingCallbacks = []; - this.__pendingStates = []; - this.__current = noop; //用于DevTools工具中,通过实例找到生成它的那个虚拟DOM - this.__lifeStage = 0; //判断生命周期 + // this.__pendingCallbacks = []; + // this.__pendingStates = []; + // this.__current = noop; //用于DevTools工具中,通过实例找到生成它的那个虚拟DOM + // this.__lifeStage = 0; //判断生命周期 /* * this.__dom = dom 用于isMounted或ReactDOM.findDOMNode方法 * this.__hydrating = true 表示组件正在根据虚拟DOM合成真实DOM @@ -479,88 +479,72 @@ Component.prototype = { deprecatedWarn("replaceState"); }, setState: function setState(state, cb) { - debounceSetState(this, state, cb); + debounceSetState(this.updater, state, cb); }, isMounted: function isMounted() { deprecatedWarn("isMounted"); - return !!this.__dom; + return !!(this.updater || {})._hostNode; }, forceUpdate: function forceUpdate(cb) { - debounceSetState(this, true, cb); - }, - - __mergeStates: function __mergeStates(props, context) { - var pendings = this.__pendingStates, - n = pendings.length; - if (n === 0) { - return this.state; - } - var state = extend({}, this.state); //每次都返回新的state - for (var i = 0; i < n; i++) { - var pending = pendings[i]; - if (isFn(pending)) { - pending = pending.call(this, state, props, context); - } - extend(state, pending); - } - pendings.length = 0; - return state; + debounceSetState(this.updater, true, cb); }, - render: function render() {} }; -function debounceSetState(a, b, c) { - if (a.__didUpdate) { +function debounceSetState(updater, state, cb) { + if (!updater) { + return; + } + if (updater._didUpdate) { //如果用户在componentDidUpdate中使用setState,要防止其卡死 setTimeout(function () { - a.__didUpdate = false; - setStateImpl.call(a, b, c); + updater._didUpdate = false; + setStateImpl(updater, state, cb); }, 300); return; } - setStateImpl.call(a, b, c); + setStateImpl(updater, state, cb); } -function setStateImpl(state, cb) { +function setStateImpl(updater, state, cb) { if (isFn(cb)) { - this.__pendingCallbacks.push(cb); + updater._pendingCallbacks.push(cb); } - var hasDOM = this.__dom; + var hasDOM = updater._hostNode; if (state === true) { //forceUpdate - this.__forceUpdate = true; + updater._forceUpdate = true; } else { //setState - this.__pendingStates.push(state); + updater._pendingStates.push(state); } if (!hasDOM) { //组件挂载期 //componentWillUpdate中的setState/forceUpdate应该被忽略 - if (this.__hydrating) { + if (updater._hydrating) { //在render方法中调用setState也会被延迟到下一周期更新.这存在两种情况, //1. 组件直接调用自己的setState //2. 子组件调用父组件的setState, - this.__renderInNextCycle = true; + updater._renderInNextCycle = true; } } else { //组件更新期 - if (this.__receiving) { + if (updater._receiving) { //componentWillReceiveProps中的setState/forceUpdate应该被忽略 return; } - this.__renderInNextCycle = true; + updater._renderInNextCycle = true; if (options.async) { //在事件句柄中执行setState会进行合并 - options.addTask(this); + options.enqueueUpdater(updater); return; } - if (this.__hydrating) { + if (updater._hydrating) { // 在componentDidMount里调用自己的setState,延迟到下一周期更新 // 在更新过程中, 子组件在componentWillReceiveProps里调用父组件的setState,延迟到下一周期更新 return; } // 不在生命周期钩子内执行setState - options.flushBatchedUpdates([this]); + options.flushUpdaters([updater]); } } @@ -950,7 +934,7 @@ function dispatchEvent(e, type, end) { triggerEventFlow(paths.reverse(), bubble, e); } options.async = false; - options.flushBatchedUpdates(); + options.flushUpdaters(); } function collectPaths(from, end) { @@ -1499,7 +1483,7 @@ var disposeStrategy = { function disposeStateless(vnode) { var instance = vnode._instance; if (instance) { - disposeVnode(instance.__rendered); + disposeVnode(instance.updater.rendered); vnode._instance = null; } } @@ -1526,7 +1510,7 @@ function disposeComponent(vnode) { var instance = vnode._instance; if (instance) { options.beforeUnmount(instance); - instance.__current = instance.setState = instance.forceUpdate = noop; + instance.setState = instance.forceUpdate = noop; if (vnode.ref) { vnode.ref(null); } @@ -1534,8 +1518,8 @@ function disposeComponent(vnode) { instance.componentWillUnmount(); } //在执行componentWillUnmount后才将关联的元素节点解绑,防止用户在钩子里调用 findDOMNode方法 - vnode.ref = instance.__dom = vnode._instance = null; - disposeVnode(instance.__rendered); + disposeVnode(instance.updater.rendered); + vnode.ref = vnode._instance = instance.updater = null; } } @@ -1723,16 +1707,101 @@ function getOptionSelected(option, selected) { dom.selected = selected; } +var mountOrder = 1; function alwaysNull() { return null; } -function instantiateComponent(type, vtype, props, context) { +function Updater(instance, vnode, props, context) { + //防止用户在构造器生成JSX + vnode._instance = instance; + instance.updater = this; + this._mountOrder = mountOrder++; + this._instance = instance; + this._pendingCallbacks = []; + this._pendingStates = []; + this._lifeStage = 0; //判断生命周期 + //update总是保存最新的数据,如state, props, context, parentContext, vparent + this.nextVnode = vnode; + this.props = props; + this.context = context; + if (instance.__isStateless) { + this.mergeStates = alwaysNull; + } +} + +Updater.prototype = { + mergeStates: function mergeStates() { + var instance = this._instance, + pendings = this._pendingStates, + state = instance.state, + n = pendings.length; + if (n === 0) { + return state; + } + var nextState = Object.assign({}, state); //每次都返回新的state + for (var i = 0; i < n; i++) { + var pending = pendings[i]; + if (pending && pending.call) { + pending = pending.call(instance, nextState, this.props); + } + Object.assign(nextState, pending); + } + pendings.length = 0; + return nextState; + }, + + renderComponent: function renderComponent(cb, rendered, parentInstance) { + var vnode = this.nextVnode; + var instance = this._instance; + var parentContext = this.parentContext; + //调整全局的 CurrentOwner.cur + if (!rendered) { + try { + var lastOwn = CurrentOwner.cur; + CurrentOwner.cur = instance; + rendered = instance.render(); + } finally { + CurrentOwner.cur = lastOwn; + } + } + + //组件只能返回组件或null + if (rendered === null || rendered === false) { + rendered = { type: "#comment", text: "empty", vtype: 0 }; + } else if (!rendered || !rendered.vtype) { + //true, undefined, array, {} + throw new Error("@" + vnode.type.name + "#render:You may have returned undefined, an array or some other invalid object"); + } + this.lastRendered = this.rendered; + this.rendered = rendered; + var childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; + var dom = cb(rendered, this.vparent, childContext); + if (parentInstance) { + this._parentInstance = parentInstance; + } + + updateInstanceChain(this, dom); + + return dom; + } +}; + +function updateInstanceChain(updater, dom) { + updater.nextVnode._hostNode = updater._hostNode = dom; //同步到生成组件的那个虚拟DOM中 + var parent = updater._parentInstance; + if (parent) { + updateInstanceChain(parent.updater, dom); + } +} + +function instantiateComponent(type, vnode, props, context) { var instance; - if (vtype === 2) { + if (vnode.vtype === 2) { instance = new type(props, context); //props, context是不可变的 instance.props = props; instance.context = context; + new Updater(instance, vnode, props, context); } else { instance = { refs: {}, @@ -1740,13 +1809,10 @@ function instantiateComponent(type, vtype, props, context) { return type(this.props, this.context); }, __isStateless: 1, - state: null, props: props, - context: context, - __pendingCallbacks: [], - __current: noop, - __mergeStates: alwaysNull + context: context }; + var updater = new Updater(instance, vnode, props, context); var lastOwn = CurrentOwner.cur; CurrentOwner.cur = instance; try { @@ -1759,7 +1825,7 @@ function instantiateComponent(type, vtype, props, context) { delete instance.__isStateless; Object.assign(instance, mixin); } else { - instance.__rendered = mixin; + updater.rendered = mixin; } } @@ -1774,20 +1840,20 @@ function clearRefs() { fn(); }); } -function callUpdate(instance) { - if (instance.__lifeStage === 2) { +function callUpdate(updater, instance) { + if (updater._lifeStage === 2) { if (pendingRefs.length) { clearRefs(); } if (instance.componentDidUpdate) { - instance.__didUpdate = true; - instance.componentDidUpdate(instance.lastProps, instance.lastState, instance.lastContext); - if (!instance.__renderInNextCycle) { - instance.__didUpdate = false; + updater._didUpdate = true; + instance.componentDidUpdate(updater.lastProps, updater.lastState, updater.lastContext); + if (!updater._renderInNextCycle) { + updater._didUpdate = false; } } options.afterUpdate(instance); - instance.__lifeStage = 1; + updater._lifeStage = 1; } } @@ -1799,9 +1865,10 @@ function drainQueue(queue) { var i = 0; while (i < queue.length) { //queue可能中途加入新元素, 因此不能直接使用queue.forEach(fn) - var instance = queue[i]; + var updater = queue[i], + instance = updater._instance; i++; - if (!instance.__lifeStage) { + if (!updater._lifeStage) { if (pendingRefs.length) { clearRefs(); } @@ -1809,25 +1876,25 @@ function drainQueue(queue) { instance.componentDidMount(); instance.componentDidMount = null; } - instance.__lifeStage = 1; + updater._lifeStage = 1; options.afterMount(instance); } else { - callUpdate(instance); + callUpdate(updater, instance); } - var ref = instance.__current.ref; + var ref = updater.nextVnode.ref; if (ref) { ref(instance.__isStateless ? null : instance); } - instance.__hydrating = false; //子树已经构建完毕 - while (instance.__renderInNextCycle) { - options.refreshComponent(instance, queue); - callUpdate(instance); + updater._hydrating = false; //子树已经构建完毕 + while (updater._renderInNextCycle) { + options.refreshComponent(updater, queue); + callUpdate(updater, instance); } } //再执行所有setState/forceUpdate回调,根据从下到上的顺序执行 - queue.sort(mountSorter).forEach(function (instance) { - clearArray(instance.__pendingCallbacks).forEach(function (fn) { - fn.call(instance); + queue.sort(mountSorter).forEach(function (updater) { + clearArray(updater._pendingCallbacks).forEach(function (fn) { + fn.call(updater._instance); }); }); queue.length = 0; @@ -1838,21 +1905,21 @@ function drainQueue(queue) { var dirtyComponents = []; dirtyComponents.isChildProcess = true; -function mountSorter(c1, c2) { +function mountSorter(u1, u2) { //让子节点先于父节点执行 - return c2.__mountOrder - c1.__mountOrder; + return u2._mountOrder - u1._mountOrder; } -options.flushBatchedUpdates = function (queue) { +options.flushUpdaters = function (queue) { if (!queue) { queue = dirtyComponents; } drainQueue(queue); }; -options.addTask = function (instance) { - if (dirtyComponents.indexOf(instance) == -1) { - dirtyComponents.push(instance); +options.enqueueUpdater = function (updater) { + if (dirtyComponents.indexOf(updater) == -1) { + dirtyComponents.push(updater); } }; @@ -1883,7 +1950,7 @@ function findDOMNode(ref) { if (ref.nodeType === 1) { return ref; } - return ref.__dom || null; + return ref.updater ? ref.updater._hostNode : ref._hostNode || null; } // 用于辅助XML元素的生成(svg, math), // 它们需要根据父节点的tagName与namespaceURI,知道自己是存在什么文档中 @@ -1958,7 +2025,6 @@ var patchStrategy = { function mountVnode(lastNode, vnode) { return patchStrategy[vnode.vtype].apply(null, arguments); } - function updateByContext(vnode) { if (vnode.type && vnode.type.contextTypes) { return true; @@ -1976,7 +2042,7 @@ function updateByContext(vnode) { } } } else if (vnode._instance) { - var ret = vnode._instance.__rendered; + var ret = vnode._instance.updater.rendered; if (updateByContext(ret)) { return true; } @@ -2079,89 +2145,54 @@ function alignChildren(parentNode, children, vparent, context, updateQueue) { } } -function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { +function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue, parentInstance) { var type = vnode.type, - vtype = vnode.vtype, props = vnode.props; - var context = getContextByTypes(parentContext, type.contextTypes); - var instance = instantiateComponent(type, vtype, props, context); //互相持有引用 + var instance = instantiateComponent(type, vnode, props, context); //互相持有引用 + var updater = instance.updater; - vnode._instance = instance; - - //用于refreshComponent - instance.nextVnode = vnode; - vnode.context = context; - vnode.parentContext = parentContext; - vnode.vparent = vparent; + updater.vparent = vparent; + updater.parentContext = parentContext; if (instance.componentWillMount) { instance.componentWillMount(); //这里可能执行了setState - instance.state = instance.__mergeStates(props, context); + instance.state = updater.mergeStates(); } - instance.__hydrating = true; - var dom = renderComponent(instance, vnode, function (nextRendered, childContext) { - return mountVnode(lastNode, nextRendered, vparent, childContext, updateQueue); - }, instance.__rendered); - - updateQueue.push(instance); - return dom; -} - -function renderComponent(instance, vnode, cb, rendered) { - //调整全局的 CurrentOwner.cur - if (!rendered) { - try { - var lastOwn = CurrentOwner.cur; - CurrentOwner.cur = instance; - rendered = instance.render(); - } finally { - CurrentOwner.cur = lastOwn; - } - } - - //组件只能返回组件或null - if (rendered === null || rendered === false) { - rendered = { type: "#comment", text: "empty", vtype: 0 }; - } else if (!rendered || !rendered.vtype) { - //true, undefined, array, {} - throw new Error("@" + vnode.type.name + "#render:You may have returned undefined, an array or some other invalid object"); - } + updater._hydrating = true; - vnode._instance = instance; - instance.lastRendered = instance.__rendered; - instance.__rendered = rendered; - var parentContext = vnode.parentContext; - var childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; - var dom = cb(rendered, childContext); + var dom = updater.renderComponent(function (nextRendered, vparent, childContext) { + return mountVnode(lastNode, nextRendered, vparent, childContext, updateQueue, instance //作为parentInstance往下传 + ); + }, updater.rendered, parentInstance); - createInstanceChain(instance, vnode, rendered); - updateInstanceChain(instance, dom); + updateQueue.push(updater); return dom; } -function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { +function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQueue) { var type = lastVnode.type, ref = lastVnode.ref, instance = lastVnode._instance, vtype = lastVnode.vtype; - var nextContext = void 0, nextProps = nextVnode.props, queue = void 0; + var updater = instance.updater; if (type.contextTypes) { - nextContext = getContextByTypes(context, type.contextTypes); + nextContext = getContextByTypes(parentContext, type.contextTypes); } else { nextContext = instance.context; //没有定义contextTypes就沿用旧的 } + if (instance.componentWillReceiveProps) { - instance.__receiving = true; + updater._receiving = true; instance.componentWillReceiveProps(nextProps, nextContext); - instance.__receiving = false; + updater._receiving = false; } //用于refreshComponent if (ref && vtype === 2) { @@ -2171,74 +2202,79 @@ function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { lastVnode.ref = nextVnode.ref; } } - instance.lastVnode = lastVnode; - instance.nextVnode = nextVnode; - nextVnode.context = nextContext; - nextVnode.parentContext = context; - nextVnode.vparent = vparent; + //updater上总是保持新的数据 + updater.lastVnode = lastVnode; + updater.nextVnode = nextVnode; + + updater.context = nextContext; + updater.props = nextProps; + updater.vparent = vparent; + updater.parentContext = parentContext; + if (updateQueue.isChildProcess) { queue = updateQueue; } else { queue = []; queue.isChildProcess = true; } - refreshComponent(instance, queue); + refreshComponent(updater, queue); //子组件先执行 - updateQueue.push(instance); + updateQueue.push(updater); - return instance.__dom; + return updater._hostNode; } -function refreshComponent(instance, updateQueue) { - var lastProps = instance.props, - lastState = instance.state, - lastContext = instance.context, - lastRendered = instance.__rendered, - dom = instance.__dom; +function refreshComponent(updater, updateQueue) { + var instance = updater._instance, + dom = updater._hostNode, + nextContext = updater.context, + nextProps = updater.props, + nextVnode = updater.nextVnode, + lastVnode = updater.lastVnode; - instance.__renderInNextCycle = null; - var nextVnode = instance.nextVnode; - var nextContext = nextVnode.context; - var nextProps = nextVnode.props; - var vparent = nextVnode.vparent; nextVnode._instance = instance; //important + updater._renderInNextCycle = null; - var nextState = instance.__mergeStates(nextProps, nextContext); + var nextState = updater.mergeStates(); var shouldUpdate = true; - if (!instance.__forceUpdate && instance.shouldComponentUpdate && !instance.shouldComponentUpdate(nextProps, nextState, nextContext)) { + if (!updater._forceUpdate && instance.shouldComponentUpdate && !instance.shouldComponentUpdate(nextProps, nextState, nextContext)) { shouldUpdate = false; } else if (instance.componentWillUpdate) { instance.componentWillUpdate(nextProps, nextState, nextContext); } + var lastProps = instance.props, + lastContext = instance.context, + lastState = instance.state; + + + updater._forceUpdate = false; - instance.__forceUpdate = false; instance.props = nextProps; instance.context = nextContext; instance.state = nextState; //既然setState了,无论shouldComponentUpdate结果如何,用户传给的state对象都会作用到组件上 if (!shouldUpdate) { - var lastVnode = instance.lastVnode; for (var i in lastVnode) { if (!nextVnode.hasOwnProperty(i)) { nextVnode[i] = lastVnode[i]; } } - instance.__current = nextVnode; return dom; } - instance.__hydrating = true; - instance.lastProps = lastProps; - instance.lastState = lastState; - instance.lastContext = lastContext; + updater._hydrating = true; + updater.lastProps = lastProps; + updater.lastState = lastState; + updater.lastContext = lastContext; + + var lastRendered = updater.rendered; //这里会更新instance的props, context, state - dom = renderComponent(instance, nextVnode, function (nextRendered, childContext) { - return alignVnode(lastRendered, nextRendered, vparent, childContext, updateQueue); + dom = updater.renderComponent(function (nextRendered, vparent, childContext) { + return alignVnode(lastRendered, nextRendered, vparent, childContext, updateQueue, instance); }); - - instance.__lifeStage = 2; - instance.__hydrating = false; + updater._lifeStage = 2; + updater._hydrating = false; if (updateQueue.isChildProcess) { drainQueue(updateQueue); } @@ -2247,14 +2283,14 @@ function refreshComponent(instance, updateQueue) { } options.refreshComponent = refreshComponent; -function alignVnode(lastVnode, nextVnode, vparent, context, updateQueue) { +function alignVnode(lastVnode, nextVnode, vparent, context, updateQueue, parentInstance) { var node = lastVnode._hostNode, dom = void 0; if (isSameNode(lastVnode, nextVnode)) { dom = updateVnode(lastVnode, nextVnode, vparent, context, updateQueue); } else { disposeVnode(lastVnode); - dom = mountVnode(null, nextVnode, vparent, context, updateQueue); + dom = mountVnode(null, nextVnode, vparent, context, updateQueue, parentInstance); var p = node.parentNode; if (p) { p.replaceChild(dom, node); @@ -2423,21 +2459,6 @@ function isSameNode(a, b) { } } -function createInstanceChain(instance, vnode, rendered) { - instance.__current = vnode; - if (rendered._instance) { - rendered._instance.__parentInstance = instance; - } -} - -function updateInstanceChain(instance, dom) { - instance.__dom = instance.__current._hostNode = dom; - var parent = instance.__parentInstance; - if (parent) { - updateInstanceChain(parent, dom); - } -} - var React = { version: "1.1.2", render: render, diff --git a/index4.html b/index4.html index 154651584..c0096f9c2 100644 --- a/index4.html +++ b/index4.html @@ -35,78 +35,94 @@ } } var container = document.createElement('div'); - var s + document.body.appendChild(container); - const log = []; - var renderCalls = 0; - class PlasticWrap extends React.Component { - constructor(props, context) { - super(props, context); - this.state = { - color: "green" - }; - } - - render() { - return ; - } - } - - class Apple extends React.Component { - state = { - cut: false, - slices: 1 - }; - - shouldComponentUpdate(nextProps, nextState) { - return shallowCompare(this, nextProps, nextState); - } - - cut() { - this.setState({ - cut: true, - slices: 10 - }); - } - - eatSlice() { - this.setState({ - slices: this.state.slices - 1 - }); - } - - render() { - renderCalls++; - return
; - } - } - - var container = document.createElement("div"); - var instance = ReactDOM.render(, container); - expect(renderCalls).toBe(1); - - // Do not re-render based on props - instance.setState({ color: "green" }); - expect(renderCalls).toBe(1); - - // Re-render based on props - instance.setState({ color: "red" }); - expect(renderCalls).toBe(2); - - // Re-render base on state - instance.refs.apple.cut(); - expect(renderCalls).toBe(3); - - // No re-render based on state - instance.refs.apple.cut(); - expect(renderCalls).toBe(3); - - // Re-render based on state again - instance.refs.apple.eatSlice(); - expect(renderCalls).toBe(4); - + class ParentComponent extends React.Component { + state = {color: 'blue'}; + + handleColor = color => { + this.props.logger('parent-handleColor', this.state.color); + this.setState({color: color}, function() { + this.props.logger('parent-after-setState', this.state.color); + }); + }; + + render() { + this.props.logger('parent-render', this.state.color); + return ( + + ); + } + } + + class ChildComponent extends React.Component { + constructor(props) { + super(props); + props.logger('getInitialState', props.color); + this.state = {hue: 'dark ' + props.color}; + } + + handleHue = (shade, color) => { + console.log(this.state, '方法') + this.props.logger('handleHue', this.state.hue, this.props.color); + this.props.onSelectColor(color); + this.setState( + function(state, props) { + this.props.logger( + 'setState-this', + this.state.hue, + this.props.color, + ); + this.props.logger('setState-args', state.hue, props.color); + return {hue: shade + ' ' + props.color}; + }, + function() { + this.props.logger( + 'after-setState', + this.state.hue, + this.props.color, + ); + }, + ); + }; + + render() { + console.log(this.state, 'render') + this.props.logger('render', this.state.hue, this.props.color); + return ( +
+ + + + +
+ ); + } + } + + var container = document.createElement('div'); + document.body.appendChild(container); + var list = [] + function logger(){ + list.push([].slice.call(arguments, 0)) + } + void ReactDOM.render(, container); + // click "light green" + //container.childNodes[0].childNodes[3].click() diff --git a/index5.html b/index5.html index 29ab969b6..df9134b60 100644 --- a/index5.html +++ b/index5.html @@ -8,56 +8,12 @@ - - + - - - + @@ -67,7 +23,58 @@
- + + + \ No newline at end of file diff --git a/index6.html b/index6.html index ea1140f2a..75a0696ea 100644 --- a/index6.html +++ b/index6.html @@ -6,8 +6,8 @@ - + + diff --git a/lib/ReactTestUtils.js b/lib/ReactTestUtils.js index 66e0fac5a..557c42249 100644 --- a/lib/ReactTestUtils.js +++ b/lib/ReactTestUtils.js @@ -14,9 +14,9 @@ if (!inst) { return ret; } - if(inst.vtype === 0){ + if(inst.vtype === 0){//如果是文本,注释 return ret; - }else if(inst.vtype === 1){ + }else if(inst.vtype === 1){//如果是元素虚拟DOM var dom = inst._hostNode; if( dom && dom.nodeType === 1 && test(dom)){ ret.push(dom); @@ -26,8 +26,16 @@ var el = children[i]; ret = ret.concat(findAllInRenderedTree(el, test)); } - } else if (inst.vtype || inst.__rendered) { - var rendered = inst._instance ? inst._instance.__rendered : inst.__rendered; + } else if (inst.vtype > 1) {//如果是组件虚拟DOM + var componentInstance = inst._instance; + var rendered = getRendered(componentInstance); + // var rendered = inst._instance ? inst._instance.__rendered || inst._instance.updater.rendered : inst.__rendered; + if (rendered) { + //如果是实例 + ret = ret.concat(findAllInRenderedTree(rendered, test)); + } + }else if(inst.refs){//组件实例都带有refs对象 + rendered = getRendered(inst); if (rendered) { //如果是实例 ret = ret.concat(findAllInRenderedTree(rendered, test)); @@ -36,7 +44,10 @@ return ret; } - + function getRendered(instance){ + //1.1.2之前,组件实例有一个__rendered属性,1.1.2时,就将私有属性统统塞到updater对象上 + return instance.updater ? instance.updater.rendered: instance.__rendered; + } var ReactTestUtils = { renderIntoDocument: function(element) { var div = document.createElement("div"); diff --git a/src/Component.js b/src/Component.js index 0a00e303b..eabcd667a 100644 --- a/src/Component.js +++ b/src/Component.js @@ -7,19 +7,19 @@ import { CurrentOwner } from "./createElement"; * @param {any} props * @param {any} context */ -var mountOrder = 1; +//var mountOrder = 1; export function Component(props, context) { //防止用户在构造器生成JSX CurrentOwner.cur = this; - this.__mountOrder = mountOrder++; + // this.__mountOrder = mountOrder++; this.context = context; this.props = props; this.refs = {}; this.state = null; - this.__pendingCallbacks = []; - this.__pendingStates = []; - this.__current = noop; //用于DevTools工具中,通过实例找到生成它的那个虚拟DOM - this.__lifeStage = 0; //判断生命周期 + // this.__pendingCallbacks = []; + // this.__pendingStates = []; + // this.__current = noop; //用于DevTools工具中,通过实例找到生成它的那个虚拟DOM + // this.__lifeStage = 0; //判断生命周期 /* * this.__dom = dom 用于isMounted或ReactDOM.findDOMNode方法 * this.__hydrating = true 表示组件正在根据虚拟DOM合成真实DOM @@ -35,86 +35,71 @@ Component.prototype = { }, setState(state, cb) { - debounceSetState(this, state, cb); + debounceSetState(this.updater, state, cb); }, isMounted() { deprecatedWarn("isMounted"); - return !!this.__dom; + return !!(this.updater || {})._hostNode; }, forceUpdate(cb) { - debounceSetState(this, true, cb); + debounceSetState(this.updater, true, cb); }, - __mergeStates: function(props, context) { - let pendings = this.__pendingStates, - n = pendings.length; - if (n === 0) { - return this.state; - } - let state = extend({}, this.state);//每次都返回新的state - for (let i = 0; i < n; i++) { - let pending = pendings[i]; - if (isFn(pending)) { - pending = pending.call(this, state, props, context); - } - extend(state, pending); - } - pendings.length = 0; - return state; - }, - render() {} }; -function debounceSetState(a, b, c) { - if (a.__didUpdate) { +function debounceSetState(updater, state, cb) { + if(!updater){ + return; + } + if (updater._didUpdate) { //如果用户在componentDidUpdate中使用setState,要防止其卡死 setTimeout(function() { - a.__didUpdate = false; - setStateImpl.call(a, b, c); + updater._didUpdate = false; + setStateImpl(updater, state, cb); }, 300); return; } - setStateImpl.call(a, b, c); + setStateImpl(updater, state, cb); } -function setStateImpl(state, cb) { +function setStateImpl(updater, state, cb) { if (isFn(cb)) { - this.__pendingCallbacks.push(cb); + updater._pendingCallbacks.push(cb); } - let hasDOM = this.__dom; + let hasDOM = updater._hostNode; if (state === true) { //forceUpdate - this.__forceUpdate = true; + updater._forceUpdate = true; } else { //setState - this.__pendingStates.push(state); + updater._pendingStates.push(state); } if (!hasDOM) { //组件挂载期 //componentWillUpdate中的setState/forceUpdate应该被忽略 - if (this.__hydrating) { + if (updater._hydrating) { //在render方法中调用setState也会被延迟到下一周期更新.这存在两种情况, //1. 组件直接调用自己的setState //2. 子组件调用父组件的setState, - this.__renderInNextCycle = true; + updater._renderInNextCycle = true; } } else { //组件更新期 - if (this.__receiving) { + if (updater._receiving) { //componentWillReceiveProps中的setState/forceUpdate应该被忽略 return; } - this.__renderInNextCycle = true; + updater._renderInNextCycle = true; if (options.async) { //在事件句柄中执行setState会进行合并 - options.addTask(this); + options.enqueueUpdater(updater); return; } - if (this.__hydrating) { + if (updater._hydrating) { // 在componentDidMount里调用自己的setState,延迟到下一周期更新 // 在更新过程中, 子组件在componentWillReceiveProps里调用父组件的setState,延迟到下一周期更新 return; } // 不在生命周期钩子内执行setState - options.flushBatchedUpdates([this]); + options.flushUpdaters([updater]); } } diff --git a/src/diff.js b/src/diff.js index fa511df9d..2f9ef9f07 100644 --- a/src/diff.js +++ b/src/diff.js @@ -4,7 +4,6 @@ import { innerHTML, toLowerCase, deprecatedWarn, - getChildContext, getContextByTypes } from "./util"; import { diffProps } from "./diffProps"; @@ -54,7 +53,7 @@ export function findDOMNode(ref) { if (ref.nodeType === 1) { return ref; } - return ref.__dom || null; + return ref.updater ? ref.updater._hostNode : (ref._hostNode || null); } // 用于辅助XML元素的生成(svg, math), // 它们需要根据父节点的tagName与namespaceURI,知道自己是存在什么文档中 @@ -135,7 +134,6 @@ const patchStrategy = { function mountVnode(lastNode, vnode) { return patchStrategy[vnode.vtype].apply(null, arguments); } - function updateByContext(vnode) { if (vnode.type && vnode.type.contextTypes) { return true; @@ -153,12 +151,12 @@ function updateByContext(vnode) { } } } else if (vnode._instance) { - var ret = vnode._instance.__rendered; + var ret = vnode._instance.updater.rendered; if (updateByContext(ret)) { return true; } } -} +} function updateVnode(lastVnode, nextVnode) { if (lastVnode === nextVnode && !updateByContext(lastVnode)) { @@ -255,96 +253,56 @@ function alignChildren(parentNode, children, vparent, context, updateQueue) { } } -function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue) { - let { type, vtype, props } = vnode; - +function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue, parentInstance) { + let { type, props } = vnode; let context = getContextByTypes(parentContext, type.contextTypes); - let instance = instantiateComponent(type, vtype, props, context); //互相持有引用 + let instance = instantiateComponent(type, vnode, props, context); //互相持有引用 + let updater = instance.updater; - vnode._instance = instance; - - //用于refreshComponent - instance.nextVnode = vnode; - vnode.context = context; - vnode.parentContext = parentContext; - vnode.vparent = vparent; + updater.vparent = vparent; + updater.parentContext = parentContext; if (instance.componentWillMount) { instance.componentWillMount();//这里可能执行了setState - instance.state = instance.__mergeStates(props, context); + instance.state = updater.mergeStates(); } - instance.__hydrating = true; - let dom = renderComponent( - instance, - vnode, - function(nextRendered, childContext) { + + updater._hydrating = true; + + let dom = updater.renderComponent( + function(nextRendered, vparent, childContext) { return mountVnode( lastNode, nextRendered, vparent, childContext, - updateQueue + updateQueue, + instance //作为parentInstance往下传 ); }, - instance.__rendered + updater.rendered, + parentInstance ); - updateQueue.push(instance); - - return dom; -} - -function renderComponent(instance, vnode, cb, rendered) { - //调整全局的 CurrentOwner.cur - if (!rendered) { - try { - var lastOwn = CurrentOwner.cur; - CurrentOwner.cur = instance; - rendered = instance.render(); - } finally { - CurrentOwner.cur = lastOwn; - } - } - - //组件只能返回组件或null - if (rendered === null || rendered === false) { - rendered = { type: "#comment", text: "empty", vtype: 0 }; - } else if (!rendered || !rendered.vtype) { - //true, undefined, array, {} - throw new Error( - `@${vnode.type - .name}#render:You may have returned undefined, an array or some other invalid object` - ); - } - - vnode._instance = instance; - instance.lastRendered = instance.__rendered; - instance.__rendered = rendered; - let parentContext = vnode.parentContext; - let childContext = rendered.vtype - ? getChildContext(instance, parentContext) - : parentContext; - let dom = cb(rendered, childContext); - - createInstanceChain(instance, vnode, rendered); - updateInstanceChain(instance, dom); + updateQueue.push(updater); return dom; } -function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { +function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQueue) { let { type, ref, _instance: instance, vtype } = lastVnode; - let nextContext, nextProps = nextVnode.props, queue; + var updater = instance.updater; if (type.contextTypes) { - nextContext = getContextByTypes(context, type.contextTypes); + nextContext = getContextByTypes(parentContext, type.contextTypes); } else { nextContext = instance.context;//没有定义contextTypes就沿用旧的 } + if (instance.componentWillReceiveProps) { - instance.__receiving = true; + updater._receiving = true; instance.componentWillReceiveProps(nextProps, nextContext); - instance.__receiving = false; + updater._receiving = false; } //用于refreshComponent if (ref && vtype === 2) { @@ -353,45 +311,46 @@ function updateComponent(lastVnode, nextVnode, vparent, context, updateQueue) { lastVnode.ref = nextVnode.ref; } } - instance.lastVnode = lastVnode; - instance.nextVnode = nextVnode; - nextVnode.context = nextContext; - nextVnode.parentContext = context; - nextVnode.vparent = vparent; + //updater上总是保持新的数据 + updater.lastVnode = lastVnode; + updater.nextVnode = nextVnode; + + updater.context = nextContext; + updater.props = nextProps; + updater.vparent = vparent; + updater.parentContext = parentContext; + if (updateQueue.isChildProcess) { queue = updateQueue; } else { queue = []; queue.isChildProcess = true; } - refreshComponent(instance, queue); + refreshComponent(updater, queue); //子组件先执行 - updateQueue.push(instance); + updateQueue.push(updater); - return instance.__dom; + return updater._hostNode; } -function refreshComponent(instance, updateQueue) { +function refreshComponent(updater, updateQueue) { let { - props: lastProps, - state: lastState, - context: lastContext, - __rendered: lastRendered, - __dom: dom - } = instance; - instance.__renderInNextCycle = null; - let nextVnode = instance.nextVnode; - let nextContext = nextVnode.context; - let nextProps = nextVnode.props; - let vparent = nextVnode.vparent; - + _instance:instance, + _hostNode: dom, + context: nextContext, + props: nextProps, + nextVnode, + lastVnode + } = updater; + nextVnode._instance = instance; //important + + updater._renderInNextCycle = null; - - let nextState = instance.__mergeStates(nextProps, nextContext); + let nextState = updater.mergeStates(); let shouldUpdate = true; if ( - !instance.__forceUpdate && + !updater._forceUpdate && instance.shouldComponentUpdate && !instance.shouldComponentUpdate(nextProps, nextState, nextContext) ) { @@ -399,44 +358,47 @@ function refreshComponent(instance, updateQueue) { }else if(instance.componentWillUpdate) { instance.componentWillUpdate(nextProps, nextState, nextContext); } - + let { + props: lastProps, + context: lastContext, + state: lastState + } = instance; - instance.__forceUpdate = false; + updater._forceUpdate = false; + instance.props = nextProps; instance.context = nextContext; instance.state = nextState;//既然setState了,无论shouldComponentUpdate结果如何,用户传给的state对象都会作用到组件上 if(!shouldUpdate){ - var lastVnode = instance.lastVnode; - for(var i in lastVnode){ + for(let i in lastVnode){ if(!nextVnode.hasOwnProperty(i)){ nextVnode[i] = lastVnode[i]; } } - instance.__current = nextVnode; - return dom; + return dom; } - instance.__hydrating = true; - instance.lastProps = lastProps; - instance.lastState = lastState; - instance.lastContext = lastContext; + updater._hydrating = true; + updater.lastProps = lastProps; + updater.lastState = lastState; + updater.lastContext = lastContext; + + let lastRendered = updater.rendered; //这里会更新instance的props, context, state - dom = renderComponent( - instance, - nextVnode, - function(nextRendered, childContext) { + dom = updater.renderComponent( + function(nextRendered,vparent, childContext) { return alignVnode( lastRendered, nextRendered, vparent, childContext, - updateQueue + updateQueue, + instance ); } ); - - instance.__lifeStage = 2; - instance.__hydrating = false; + updater._lifeStage = 2; + updater._hydrating = false; if (updateQueue.isChildProcess) { drainQueue(updateQueue); } @@ -450,7 +412,8 @@ export function alignVnode( nextVnode, vparent, context, - updateQueue + updateQueue, + parentInstance ) { let node = lastVnode._hostNode, dom; @@ -458,7 +421,7 @@ export function alignVnode( dom = updateVnode(lastVnode, nextVnode, vparent, context, updateQueue); } else { disposeVnode(lastVnode); - dom = mountVnode(null, nextVnode, vparent, context, updateQueue); + dom = mountVnode(null, nextVnode, vparent, context, updateQueue, parentInstance); let p = node.parentNode; if (p) { p.replaceChild(dom, node); @@ -646,17 +609,3 @@ function isSameNode(a, b) { } } -function createInstanceChain(instance, vnode, rendered) { - instance.__current = vnode; - if (rendered._instance) { - rendered._instance.__parentInstance = instance; - } -} - -function updateInstanceChain(instance, dom) { - instance.__dom = instance.__current._hostNode = dom; - var parent = instance.__parentInstance; - if (parent) { - updateInstanceChain(parent, dom); - } -} diff --git a/src/dispose.js b/src/dispose.js index 07840f986..bc451219d 100644 --- a/src/dispose.js +++ b/src/dispose.js @@ -17,7 +17,7 @@ var disposeStrategy = { function disposeStateless(vnode) { var instance = vnode._instance; if (instance) { - disposeVnode(instance.__rendered); + disposeVnode(instance.updater.rendered); vnode._instance = null; } } @@ -42,7 +42,7 @@ function disposeComponent(vnode) { let instance = vnode._instance; if (instance) { options.beforeUnmount(instance); - instance.__current = instance.setState = instance.forceUpdate = noop; + instance.setState = instance.forceUpdate = noop; if (vnode.ref) { vnode.ref(null); } @@ -50,7 +50,7 @@ function disposeComponent(vnode) { instance.componentWillUnmount(); } //在执行componentWillUnmount后才将关联的元素节点解绑,防止用户在钩子里调用 findDOMNode方法 - vnode.ref = instance.__dom = vnode._instance = null; - disposeVnode(instance.__rendered); + disposeVnode(instance.updater.rendered); + vnode.ref = vnode._instance = instance.updater = null; } } diff --git a/src/event.js b/src/event.js index b6971f688..387095ca2 100644 --- a/src/event.js +++ b/src/event.js @@ -46,7 +46,7 @@ export function dispatchEvent(e, type, end) { triggerEventFlow(paths.reverse(), bubble, e); } options.async = false; - options.flushBatchedUpdates(); + options.flushUpdaters(); } function collectPaths(from, end) { diff --git a/src/instantiateComponent.js b/src/instantiateComponent.js index f2e6b9042..038821859 100644 --- a/src/instantiateComponent.js +++ b/src/instantiateComponent.js @@ -1,16 +1,107 @@ -import { noop } from "./util"; - +var mountOrder = 1; +import { getChildContext,options } from "../src/util"; import { CurrentOwner } from "./createElement"; function alwaysNull() { return null; } -export function instantiateComponent(type, vtype, props, context) { +function Updater(instance, vnode, props, context) { + //防止用户在构造器生成JSX + vnode._instance = instance; + instance.updater = this; + this._mountOrder = mountOrder++; + this._instance = instance; + this._pendingCallbacks = []; + this._pendingStates = []; + this._lifeStage = 0; //判断生命周期 + //update总是保存最新的数据,如state, props, context, parentContext, vparent + this.nextVnode = vnode; + this.props = props; + this.context = context; + if(instance.__isStateless){ + this.mergeStates = alwaysNull; + } +} + +Updater.prototype = { + mergeStates: function(){ + let instance = this._instance, + pendings = this._pendingStates, + state = instance.state, + n = pendings.length; + if (n === 0) { + return state; + } + let nextState = Object.assign({}, state);//每次都返回新的state + for (let i = 0; i < n; i++) { + let pending = pendings[i]; + if (pending && pending.call) { + pending = pending.call(instance, nextState, this.props); + } + Object.assign(nextState, pending); + } + pendings.length = 0; + return nextState; + }, + + + renderComponent: function( cb, rendered, parentInstance) { + let vnode = this.nextVnode; + let instance = this._instance; + let parentContext = this.parentContext; + //调整全局的 CurrentOwner.cur + if (!rendered) { + try { + var lastOwn = CurrentOwner.cur; + CurrentOwner.cur = instance; + rendered = instance.render(); + } finally { + CurrentOwner.cur = lastOwn; + } + } + + //组件只能返回组件或null + if (rendered === null || rendered === false) { + rendered = { type: "#comment", text: "empty", vtype: 0 }; + } else if (!rendered || !rendered.vtype) { + //true, undefined, array, {} + throw new Error( + `@${vnode.type + .name}#render:You may have returned undefined, an array or some other invalid object` + ); + } + this.lastRendered = this.rendered; + this.rendered = rendered; + let childContext = rendered.vtype + ? getChildContext(instance, parentContext) + : parentContext; + let dom = cb(rendered, this.vparent, childContext); + if(parentInstance){ + this._parentInstance = parentInstance; + } + + updateInstanceChain(this, dom); + + return dom; + } +}; + + +function updateInstanceChain(updater, dom) { + updater.nextVnode._hostNode = updater._hostNode = dom;//同步到生成组件的那个虚拟DOM中 + var parent = updater._parentInstance; + if (parent) { + updateInstanceChain(parent.updater, dom); + } +} + +export function instantiateComponent(type, vnode, props, context) { var instance; - if (vtype === 2) { + if (vnode.vtype === 2) { instance = new type(props, context); //props, context是不可变的 instance.props = props; instance.context = context; + new Updater(instance, vnode, props, context); } else { instance = { refs: {}, @@ -18,13 +109,10 @@ export function instantiateComponent(type, vtype, props, context) { return type(this.props, this.context); }, __isStateless: 1, - state: null, props: props, - context: context, - __pendingCallbacks: [], - __current: noop, - __mergeStates: alwaysNull + context: context }; + var updater = new Updater(instance, vnode, props, context); let lastOwn = CurrentOwner.cur; CurrentOwner.cur = instance; try { @@ -37,7 +125,7 @@ export function instantiateComponent(type, vtype, props, context) { delete instance.__isStateless; Object.assign(instance, mixin); } else { - instance.__rendered = mixin; + updater.rendered = mixin; } } diff --git a/src/scheduler.js b/src/scheduler.js index b53724497..2d0716e9a 100644 --- a/src/scheduler.js +++ b/src/scheduler.js @@ -11,24 +11,24 @@ function clearRefs() { fn(); }); } -function callUpdate(instance) { - if (instance.__lifeStage === 2) { +function callUpdate(updater, instance) { + if (updater._lifeStage === 2) { if(pendingRefs.length){ clearRefs(); } if (instance.componentDidUpdate) { - instance.__didUpdate = true; + updater._didUpdate = true; instance.componentDidUpdate( - instance.lastProps, - instance.lastState, - instance.lastContext + updater.lastProps, + updater.lastState, + updater.lastContext ); - if (!instance.__renderInNextCycle) { - instance.__didUpdate = false; + if (!updater._renderInNextCycle) { + updater._didUpdate = false; } } options.afterUpdate(instance); - instance.__lifeStage = 1; + updater._lifeStage = 1; } } @@ -39,9 +39,9 @@ export function drainQueue(queue) { //再执行所有mount/update钩子(从下到上) let i = 0; while(i < queue.length){//queue可能中途加入新元素, 因此不能直接使用queue.forEach(fn) - var instance = queue[i]; + var updater = queue[i], instance = updater._instance; i++; - if (!instance.__lifeStage) { + if (!updater._lifeStage) { if(pendingRefs.length){ clearRefs(); } @@ -49,25 +49,25 @@ export function drainQueue(queue) { instance.componentDidMount(); instance.componentDidMount = null; } - instance.__lifeStage = 1; + updater._lifeStage = 1; options.afterMount(instance); } else { - callUpdate(instance); + callUpdate(updater, instance); } - var ref = instance.__current.ref; + var ref = updater.nextVnode.ref; if (ref) { ref(instance.__isStateless ? null: instance); } - instance.__hydrating = false; //子树已经构建完毕 - while (instance.__renderInNextCycle) { - options.refreshComponent(instance, queue); - callUpdate(instance); + updater._hydrating = false; //子树已经构建完毕 + while (updater._renderInNextCycle) { + options.refreshComponent(updater, queue); + callUpdate(updater, instance); } } //再执行所有setState/forceUpdate回调,根据从下到上的顺序执行 - queue.sort(mountSorter).forEach(function(instance){ - clearArray(instance.__pendingCallbacks).forEach(function(fn) { - fn.call(instance); + queue.sort(mountSorter).forEach(function(updater){ + clearArray(updater._pendingCallbacks).forEach(function(fn) { + fn.call(updater._instance); }); }); queue.length = 0; @@ -78,19 +78,19 @@ export function drainQueue(queue) { var dirtyComponents = []; dirtyComponents.isChildProcess = true; -function mountSorter(c1, c2) {//让子节点先于父节点执行 - return c2.__mountOrder - c1.__mountOrder; +function mountSorter(u1, u2) {//让子节点先于父节点执行 + return u2._mountOrder - u1._mountOrder; } -options.flushBatchedUpdates = function(queue) { +options.flushUpdaters = function(queue) { if (!queue) { queue = dirtyComponents; } drainQueue(queue); }; -options.addTask = function(instance) { - if (dirtyComponents.indexOf(instance) == -1) { - dirtyComponents.push(instance); +options.enqueueUpdater = function(updater) { + if (dirtyComponents.indexOf(updater) == -1) { + dirtyComponents.push(updater); } }; \ No newline at end of file diff --git a/test/modules/ReactCompositeComponent-test.jsx b/test/modules/ReactCompositeComponent-test.jsx index e4feb80d3..cf24264de 100644 --- a/test/modules/ReactCompositeComponent-test.jsx +++ b/test/modules/ReactCompositeComponent-test.jsx @@ -1305,8 +1305,8 @@ describe("ReactCompositeComponent", function() { var b = ; var container = document.createElement("div") var s = ReactDOM.render(b, container); - expect(!!s.__rendered._hostNode).toBe(true) + expect(!!s.updater._hostNode).toBe(true) s.setState({a:2}) - expect(!!s.__rendered._hostNode).toBe(true) + expect(!!s.updater._hostNode).toBe(true) }); }); diff --git a/test/modules/ReactStatelessComponent-test.jsx b/test/modules/ReactStatelessComponent-test.jsx index 3c1c15296..0b6db5f3e 100644 --- a/test/modules/ReactStatelessComponent-test.jsx +++ b/test/modules/ReactStatelessComponent-test.jsx @@ -103,7 +103,7 @@ describe("ReactStatelessComponent", function() { var s = ReactTestUtils.renderIntoDocument(); - expect(s.__current._hostNode.textContent).toBe("3") + expect(s.updater._hostNode.textContent).toBe("3") }); it('should support default props and prop types', () => { @@ -115,7 +115,7 @@ describe("ReactStatelessComponent", function() { spyOn(console, 'error'); var s = ReactTestUtils.renderIntoDocument(); - expect(s.__current._hostNode.textContent).toBe("2") + expect(s.updater._hostNode.textContent).toBe("2") }); it('should receive context', () => { diff --git a/test/modules/component.spec.jsx b/test/modules/component.spec.jsx index 5c62dac3d..ce71974f9 100644 --- a/test/modules/component.spec.jsx +++ b/test/modules/component.spec.jsx @@ -34,7 +34,7 @@ describe("组件相关", function() { var s = React.render(, div); await browser.pause(200).$apply(); - expect(s.__current._hostNode.innerHTML).toBe("Hello Sebastian"); + expect(s.updater._hostNode.innerHTML).toBe("Hello Sebastian"); }); it("setState", async () => { @@ -75,9 +75,9 @@ describe("组件相关", function() { var s = React.render(, div); await browser.pause(200).$apply(); - expect(s.__current._hostNode.innerHTML).toBe("1"); - await browser.click(s.__current._hostNode).pause(200).$apply(); - expect(s.__current._hostNode.innerHTML).toBe("1"); + expect(s.updater._hostNode.innerHTML).toBe("1"); + await browser.click(s.updater._hostNode).pause(200).$apply(); + expect(s.updater._hostNode.innerHTML).toBe("1"); expect(a).toBe(3); }); @@ -120,9 +120,9 @@ describe("组件相关", function() { var s = React.render(, div); await browser.pause(200).$apply(); - expect(s.__current._hostNode.innerHTML).toBe("1"); - await browser.click(s.__current._hostNode).pause(200).$apply(); - expect(s.__current._hostNode.innerHTML).toBe("1"); + expect(s.updater._hostNode.innerHTML).toBe("1"); + await browser.click(s.updater._hostNode).pause(200).$apply(); + expect(s.updater._hostNode.innerHTML).toBe("1"); expect(a).toBe(3); }); it("PureComponent", async () => { @@ -150,9 +150,9 @@ describe("组件相关", function() { var s = React.render(, div); await browser.pause(200).$apply(); - expect(s.__current._hostNode.innerHTML).toBe("7"); - await browser.click(s.__current._hostNode).pause(200).$apply(); - expect(s.__current._hostNode.innerHTML).toBe("7"); + expect(s.updater._hostNode.innerHTML).toBe("7"); + await browser.click(s.updater._hostNode).pause(200).$apply(); + expect(s.updater._hostNode.innerHTML).toBe("7"); }); it("PureComponent2", async () => { class A extends React.PureComponent { @@ -180,10 +180,10 @@ describe("组件相关", function() { var s = React.render(, div); await browser.pause(100).$apply(); - expect(s.__current._hostNode.innerHTML).toBe("7"); + expect(s.updater._hostNode.innerHTML).toBe("7"); - await browser.click(s.__current._hostNode).pause(200).$apply(); - expect(s.__current._hostNode.innerHTML).toBe("9"); + await browser.click(s.updater._hostNode).pause(200).$apply(); + expect(s.updater._hostNode.innerHTML).toBe("9"); }); it("子组件是无状态组件", async () => { function Select(props) { diff --git a/test/modules/event.spec.jsx b/test/modules/event.spec.jsx index e75124f77..69be2aced 100644 --- a/test/modules/event.spec.jsx +++ b/test/modules/event.spec.jsx @@ -59,13 +59,13 @@ describe('事件系统模块', function () { .$apply() expect(s.state.aaa).toBe(111) await browser - .click(s.__current._hostNode) + .click(s.updater._hostNode) .pause(100) .$apply() expect(s.state.aaa).toBe(112) await browser - .click(s.__current._hostNode) + .click(s.updater._hostNode) .pause(100) .$apply() diff --git a/test/modules/node.spec.jsx b/test/modules/node.spec.jsx index 8c378c1ae..f6ffd89c4 100644 --- a/test/modules/node.spec.jsx +++ b/test/modules/node.spec.jsx @@ -131,7 +131,7 @@ describe('node模块', function () { .pause(100) .$apply() var index = 0 - expect(s.__current.getDOMNode().nodeName).toBe('DIV') + expect(s.updater._hostNode.nodeName).toBe('DIV') s.forceUpdate(function () { index++ }) @@ -251,15 +251,15 @@ describe('node模块', function () { var s = React.render( -
- ); - } + + function Bar(props) { + return React.cloneElement(props.children, {className: props.className}) + } + function Foo(props) { + return props.className === "a" ? :

} - App.defaultProps = { - initialValue: '请输入内容' - }; - container.innerHTML = 'remove' - - - - var s = React.render(, container) + var myNodeA = ReactDOM.render(, container); + console.log(myNodeA.updater._hostNode) + + var myNodeA = ReactDOM.render(, container); + console.log(myNodeA.updater._hostNode) + // console.log(myNodeB) + // expect(myNodeA === myNodeB).toBe(true); + // var b = ReactDOM.findDOMNode(myNodeB); diff --git a/src/diff.js b/src/diff.js index a5105fe66..c6d39685e 100644 --- a/src/diff.js +++ b/src/diff.js @@ -38,7 +38,8 @@ export function findDOMNode(ref) { if (ref.nodeType === 1) { return ref; } - return ref.updater ? ref.updater._hostNode : ref._hostNode || null; + + return ref.updater ? ref.updater._hostNode : ref._hostNode || null; } // 用于辅助XML元素的生成(svg, math), // 它们需要根据父节点的tagName与namespaceURI,知道自己是存在什么文档中 @@ -48,14 +49,7 @@ function getVParent(container) { namespaceURI: container.namespaceURI }; } -function replaceChild(parentNode, newChild, oldChild){ - if(newChild !== oldChild && parentNode.nodeType === 1){ - if(!oldChild){ - return parentNode.appendChild(newChild ); - } - parentNode.replaceChild(newChild, oldChild ); - } -} + // ReactDOM.render的内部实现 function renderByAnu(vnode, container, callback, context = {}) { if (!isValidElement(vnode)) { @@ -308,13 +302,14 @@ function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQue updater.parentContext = parentContext; // nextVnode._instance = instance; //不能放这里 - if (updateQueue.isChildProcess) { + /* if (updateQueue.isMainProcess) { queue = updateQueue; - } else { queue = []; - queue.isChildProcess = true; - } - refreshComponent(updater, queue); + console.log("转换为子"); + }else{ + queue = updateQueue; + }*/ + refreshComponent(updater, updateQueue); //子组件先执行 updateQueue.push(updater); return updater._hostNode; @@ -367,7 +362,7 @@ function refreshComponent(updater, updateQueue) { }); updater._lifeStage = 2; updater._hydrating = false; - if (updateQueue.isChildProcess) { + if (!updateQueue.isMainProcess) { drainQueue(updateQueue); } @@ -528,8 +523,8 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { } oldChild = action.last; if(oldChild.vtype > 1 && !oldChild._instance){ - dom = mountVnode(null, action.next, lastVnode, context, updateQueue); - parentNode.insertBefore(dom, insertPoint); + //正处于动画过程中 + nextChildren[j] = oldChild; }else{ dom = updateVnode(action.last, action.next, lastVnode, context, updateQueue); diff --git a/src/scheduler.js b/src/scheduler.js index 1453f57e3..68d87cc46 100644 --- a/src/scheduler.js +++ b/src/scheduler.js @@ -76,7 +76,6 @@ export function drainQueue(queue) { //有一个列队, 先放进A组件与A组件回调 var dirtyComponents = []; -dirtyComponents.isChildProcess = true; function mountSorter(u1, u2) {//让子节点先于父节点执行 return u2._mountIndex - u1._mountIndex; diff --git a/test/spec.js b/test/spec.js index 17365d497..3b46b265a 100644 --- a/test/spec.js +++ b/test/spec.js @@ -62,6 +62,7 @@ require("./modules/ReactDOM-test.jsx"); require("./modules/findDOMNode-test.jsx"); +require("./modules/ReactES6Class-test.jsx"); From 8c445b27a962d0578a0d00047456292ae8aedccb Mon Sep 17 00:00:00 2001 From: RubyLouvre <1669866773@qq.com> Date: Thu, 5 Oct 2017 10:49:15 +0800 Subject: [PATCH 35/57] =?UTF-8?q?React.Children.forEach=E4=B8=8D=E6=98=AF?= =?UTF-8?q?=E4=BC=9A=E5=A4=84=E7=90=86null,=20void=200?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Children.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Children.js b/src/Children.js index 544da7f52..e16f3f1ee 100644 --- a/src/Children.js +++ b/src/Children.js @@ -41,7 +41,9 @@ export const Children = { return ret; }, forEach(children, callback, context) { - _flattenChildren(children, false).forEach(callback, context); + if(children != null){ + _flattenChildren(children, false).forEach(callback, context); + } }, toArray: function(children) { From 2ac92b46a6f0c72d1d4531cf9e979ec74ed9f633 Mon Sep 17 00:00:00 2001 From: RubyLouvre <1669866773@qq.com> Date: Fri, 6 Oct 2017 21:11:44 +0800 Subject: [PATCH 36/57] =?UTF-8?q?=E7=AE=80=E5=8C=96diffChildren=E6=96=B9?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dist/React.js | 162 ++++++++++++-------------- dist/ReactIE.js | 162 ++++++++++++-------------- dist/ReactSelection.js | 162 ++++++++++++-------------- dist/ReactShim.js | 162 ++++++++++++-------------- src/diff.js | 139 ++++++++++------------ src/instantiateComponent.js | 12 +- src/scheduler.js | 11 +- test/modules/ReactChildren-test.jsx | 11 +- test/modules/ReactMultiChild-test.jsx | 2 +- 9 files changed, 397 insertions(+), 426 deletions(-) diff --git a/dist/React.js b/dist/React.js index 685b50bd9..dbd5c217b 100644 --- a/dist/React.js +++ b/dist/React.js @@ -1,5 +1,5 @@ /** - * by 司徒正美 Copyright 2017-10-03 + * by 司徒正美 Copyright 2017-10-06 * IE9+ */ @@ -522,7 +522,9 @@ var Children = { return ret; }, forEach: function forEach(children, callback, context) { - _flattenChildren(children, false).forEach(callback, context); + if (children != null) { + _flattenChildren(children, false).forEach(callback, context); + } }, @@ -1641,7 +1643,6 @@ function Updater(instance, vnode) { this.mergeStates = alwaysNull; } } - Updater.prototype = { mergeStates: function mergeStates() { var instance = this._instance, @@ -1690,11 +1691,16 @@ Updater.prototype = { this.rendered = rendered; var childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; var dom = cb(rendered, this.vparent, childContext); - - updateChains[this._mountOrder].forEach(function (el) { + if (!dom) { + throw ["必须返回节点", rendered]; + } + var list = updateChains[this._mountOrder]; + if (!list) { + list = updateChains[this._mountOrder] = [this]; + } + list.forEach(function (el) { el.vnode._hostNode = el._hostNode = dom; }); - return dom; } }; @@ -1990,10 +1996,10 @@ function clearRefs() { }); } function callUpdate(updater, instance) { + if (pendingRefs.length) { + clearRefs(); + } if (updater._lifeStage === 2) { - if (pendingRefs.length) { - clearRefs(); - } if (instance.componentDidUpdate) { updater._didUpdate = true; instance.componentDidUpdate(updater.lastProps, updater.lastState, updater.lastContext); @@ -2002,6 +2008,7 @@ function callUpdate(updater, instance) { } } options.afterUpdate(instance); + updater._hydrating = false; updater._lifeStage = 1; } } @@ -2027,6 +2034,7 @@ function drainQueue(queue) { } updater._lifeStage = 1; options.afterMount(instance); + updater._hydrating = false; } else { callUpdate(updater, instance); } @@ -2034,8 +2042,9 @@ function drainQueue(queue) { if (ref) { ref(instance.__isStateless ? null : instance); } - updater._hydrating = false; //子树已经构建完毕 + // updater._hydrating = false; //子树已经构建完毕 while (updater._renderInNextCycle) { + options.refreshComponent(updater, queue); callUpdate(updater, instance); } @@ -2231,6 +2240,7 @@ function genMountElement(lastNode, vnode, vparent, type) { return lastNode; } else { var dom = createDOMElement(vnode, vparent); + if (lastNode) { while (lastNode.firstChild) { dom.appendChild(lastNode.firstChild); @@ -2254,10 +2264,10 @@ function mountElement(lastNode, vnode, vparent, context, updateQueue) { var dom = genMountElement(lastNode, vnode, vparent, type); vnode._hostNode = dom; + var children = flattenChildren(vnode); var method = lastNode ? alignChildren : mountChildren; method(dom, children, vnode, context, updateQueue); - if (vnode.checkProps) { diffProps(props, {}, vnode, {}, dom); } @@ -2273,8 +2283,10 @@ function mountElement(lastNode, vnode, vparent, context, updateQueue) { //将虚拟DOM转换为真实DOM并插入父元素 function mountChildren(parentNode, children, vparent, context, updateQueue) { + parentNode.vchildren = children; for (var i = 0, n = children.length; i < n; i++) { - parentNode.appendChild(mountVnode(null, children[i], vparent, context, updateQueue)); + var vnode = children[i]; + parentNode.appendChild(mountVnode(null, vnode, vparent, context, updateQueue)); } } @@ -2283,6 +2295,7 @@ function alignChildren(parentNode, children, vparent, context, updateQueue) { insertPoint = childNodes[0] || null, j = 0, n = children.length; + parentNode.vchildren = children; for (var i = 0; i < n; i++) { var vnode = children[i]; var lastNode = childNodes[j]; @@ -2302,7 +2315,6 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue, pa var type = vnode.type, props = vnode.props; - var context = getContextByTypes(parentContext, type.contextTypes); var instance = instantiateComponent(type, vnode, props, context); //互相持有引用 var updater = instance.updater; @@ -2320,7 +2332,6 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue, pa } updater._hydrating = true; - var dom = updater.renderComponent(function (nextRendered, vparent, childContext) { return mountVnode(lastNode, nextRendered, vparent, childContext, updateQueue, updater //作为parentUpater往下传 ); @@ -2332,9 +2343,6 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue, pa } function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQueue) { - if (!lastVnode._instance) { - console.log(lastVnode); - } var type = lastVnode.type, ref = lastVnode.ref, instance = lastVnode._instance, @@ -2370,17 +2378,18 @@ function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQue updater.vparent = vparent; updater.parentContext = parentContext; // nextVnode._instance = instance; //不能放这里 - - /* if (updateQueue.isMainProcess) { + if (updateQueue.isMainProcess) { queue = updateQueue; queue = []; - console.log("转换为子"); - }else{ + } else { queue = updateQueue; - }*/ - refreshComponent(updater, updateQueue); + } + refreshComponent(updater, queue); //子组件先执行 updateQueue.push(updater); + if (!updater._hostNode) { + console.log("出问题了", updater, lastVnode); + } return updater._hostNode; } @@ -2409,39 +2418,30 @@ function refreshComponent(updater, updateQueue) { updater._forceUpdate = false; - instance.state = nextState; //既然setState了,无论shouldComponentUpdate结果如何,用户传给的state对象都会作用到组件上 - + instance.context = nextContext; if (!shouldUpdate) { - if (lastVnode && lastVnode !== vnode) { - for (var i in lastVnode) { - if (!vnode[i]) { - vnode[i] = lastVnode[i]; - } - } - lastVnode._instance = instance; - if (lastVnode.props.children) { - vnode.props.children = lastVnode.props.children; - } - } return dom; } instance.props = nextProps; - instance.context = nextContext; updater._hydrating = true; updater.lastProps = lastProps; updater.lastState = lastState; updater.lastContext = lastContext; var lastRendered = updater.rendered; - //这里会更新instance的props, context, state + dom = updater.renderComponent(function (nextRendered, vparent, childContext) { return alignVnode(lastRendered, nextRendered, vparent, childContext, updateQueue, updater); }); + + updater.lastVnode = vnode; updater._lifeStage = 2; - updater._hydrating = false; + // updater._hydrating = false; if (!updateQueue.isMainProcess) { drainQueue(updateQueue); + } else { + updater._hydrating = false; } return dom; @@ -2466,6 +2466,10 @@ function alignVnode(lastVnode, nextVnode, vparent, context, updateQueue, parentU function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { var dom = lastVnode._hostNode; + if (dom === null) { + console.log("此节点已经被移除", vparent); + return null; + } var lastProps = lastVnode.props; var nextProps = nextVnode.props; var ref = nextVnode.ref; @@ -2484,7 +2488,7 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { } mountChildren(dom, vchildren, nextVnode, context, updateQueue); } else { - diffChildren(lastVnode, nextVnode, dom, context, updateQueue); + diffChildren(lastVnode, nextVnode, dom, context, updateQueue, vparent); } } @@ -2501,8 +2505,8 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { } function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { - var lastChildren = lastVnode.vchildren, - nextChildren = flattenChildren(nextVnode), + var lastChildren = parentNode.vchildren; // lastVnode.vchildren; + var nextChildren = flattenChildren(nextVnode), nextLength = nextChildren.length, lastLength = lastChildren.length, dom = void 0; @@ -2524,23 +2528,18 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { actions = [], i = 0, hit = void 0, - oldDom = void 0, nextChild = void 0, lastChild = void 0; //第一次循环,构建移动指令(actions)与移除名单(removeHits)与命中名单(fuzzyHits) + if (nextLength) { actions.length = nextLength; while (i < maxLength) { nextChild = nextChildren[i]; lastChild = lastChildren[i]; - if (nextChild && lastChild && isSameNode(lastChild, nextChild)) { // 如果能直接找到,命名90%的情况 - actions[i] = { - last: lastChild, - next: nextChild, - directive: "update" - }; + actions[i] = [lastChild, nextChild]; removeHits[i] = true; } else { if (nextChild) { @@ -2548,11 +2547,7 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { if (fuzzyHits[hit] && fuzzyHits[hit].length) { var oldChild = fuzzyHits[hit].shift(); // 如果命中旧的节点,将旧的节点移动新节点的位置,向后移动 - actions[i] = { - last: oldChild, - next: nextChild, - directive: "moveAfter" - }; + actions[i] = [oldChild, nextChild, "moveAfter"]; removeHits[oldChild._i] = true; } } @@ -2574,45 +2569,42 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { for (var j = 0, n = actions.length; j < n; j++) { var action = actions[j]; if (!action) { - var curChild = nextChildren[j]; - hit = curChild.type + (curChild.key || ""); + nextChild = nextChildren[j]; + hit = nextChild.type + (nextChild.key || ""); if (fuzzyHits[hit] && fuzzyHits[hit].length) { - oldChild = fuzzyHits[hit].shift(); - oldDom = oldChild._hostNode; - parentNode.insertBefore(oldDom, insertPoint); - - if (oldChild.vtype > 1 && !oldChild._instance) { - console.log("有问题"); - } - dom = updateVnode(oldChild, curChild, lastVnode, context, updateQueue); - removeHits[oldChild._i] = true; - } else { - //为了兼容 react stack reconciliation的执行顺序,添加下面三行, - //在插入节点前,将原位置上节点对应的组件先移除 - var removed = lastChildren[j]; - if (removed && !removed._disposed && !removeHits[j]) { - disposeVnode(removed); - } - //如果找不到对应的旧节点,创建一个新节点放在这里 - dom = mountVnode(null, curChild, lastVnode, context, updateQueue); + lastChild = fuzzyHits[hit].shift(); + action = [lastChild, nextChild, "moveAfter"]; + } + } + if (action) { + lastChild = action[0]; + nextChild = action[1]; + dom = lastChild._hostNode; + if (action[2]) { parentNode.insertBefore(dom, insertPoint); } - } else { - oldDom = action.last._hostNode; - if (action.action === "moveAfter") { - parentNode.insertBefore(oldDom, insertPoint); + insertPoint = updateVnode(lastChild, nextChild, lastVnode, context, updateQueue); + if (!nextChild._hostNode) { + nextChildren[j] = lastChild; } - oldChild = action.last; - if (oldChild.vtype > 1 && !oldChild._instance) { - //正处于动画过程中 - nextChildren[j] = oldChild; - } else { - dom = updateVnode(action.last, action.next, lastVnode, context, updateQueue); + removeHits[lastChild._i] = true; + } else { + //为了兼容 react stack reconciliation的执行顺序,添加下面三行, + //在插入节点前,将原位置上节点对应的组件先移除 + var removed = lastChildren[j]; + if (removed && !removed._disposed && !removeHits[j]) { + disposeVnode(removed); } + //如果找不到对应的旧节点,创建一个新节点放在这里 + dom = mountVnode(null, nextChild, lastVnode, context, updateQueue); + parentNode.insertBefore(dom, insertPoint); + insertPoint = dom; } - insertPoint = dom.nextSibling; + insertPoint = insertPoint.nextSibling; } + parentNode.vchildren = nextChildren; + //移除 lastChildren.forEach(function (el, i) { if (!removeHits[i]) { diff --git a/dist/ReactIE.js b/dist/ReactIE.js index 92419b133..d0fe6af8b 100644 --- a/dist/ReactIE.js +++ b/dist/ReactIE.js @@ -1,5 +1,5 @@ /** - * IE6+,有问题请加QQ 370262116 by 司徒正美 Copyright 2017-10-03 + * IE6+,有问题请加QQ 370262116 by 司徒正美 Copyright 2017-10-06 */ (function (global, factory) { @@ -521,7 +521,9 @@ var Children = { return ret; }, forEach: function forEach(children, callback, context) { - _flattenChildren(children, false).forEach(callback, context); + if (children != null) { + _flattenChildren(children, false).forEach(callback, context); + } }, @@ -1640,7 +1642,6 @@ function Updater(instance, vnode) { this.mergeStates = alwaysNull; } } - Updater.prototype = { mergeStates: function mergeStates() { var instance = this._instance, @@ -1689,11 +1690,16 @@ Updater.prototype = { this.rendered = rendered; var childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; var dom = cb(rendered, this.vparent, childContext); - - updateChains[this._mountOrder].forEach(function (el) { + if (!dom) { + throw ["必须返回节点", rendered]; + } + var list = updateChains[this._mountOrder]; + if (!list) { + list = updateChains[this._mountOrder] = [this]; + } + list.forEach(function (el) { el.vnode._hostNode = el._hostNode = dom; }); - return dom; } }; @@ -1989,10 +1995,10 @@ function clearRefs() { }); } function callUpdate(updater, instance) { + if (pendingRefs.length) { + clearRefs(); + } if (updater._lifeStage === 2) { - if (pendingRefs.length) { - clearRefs(); - } if (instance.componentDidUpdate) { updater._didUpdate = true; instance.componentDidUpdate(updater.lastProps, updater.lastState, updater.lastContext); @@ -2001,6 +2007,7 @@ function callUpdate(updater, instance) { } } options.afterUpdate(instance); + updater._hydrating = false; updater._lifeStage = 1; } } @@ -2026,6 +2033,7 @@ function drainQueue(queue) { } updater._lifeStage = 1; options.afterMount(instance); + updater._hydrating = false; } else { callUpdate(updater, instance); } @@ -2033,8 +2041,9 @@ function drainQueue(queue) { if (ref) { ref(instance.__isStateless ? null : instance); } - updater._hydrating = false; //子树已经构建完毕 + // updater._hydrating = false; //子树已经构建完毕 while (updater._renderInNextCycle) { + options.refreshComponent(updater, queue); callUpdate(updater, instance); } @@ -2230,6 +2239,7 @@ function genMountElement(lastNode, vnode, vparent, type) { return lastNode; } else { var dom = createDOMElement(vnode, vparent); + if (lastNode) { while (lastNode.firstChild) { dom.appendChild(lastNode.firstChild); @@ -2253,10 +2263,10 @@ function mountElement(lastNode, vnode, vparent, context, updateQueue) { var dom = genMountElement(lastNode, vnode, vparent, type); vnode._hostNode = dom; + var children = flattenChildren(vnode); var method = lastNode ? alignChildren : mountChildren; method(dom, children, vnode, context, updateQueue); - if (vnode.checkProps) { diffProps(props, {}, vnode, {}, dom); } @@ -2272,8 +2282,10 @@ function mountElement(lastNode, vnode, vparent, context, updateQueue) { //将虚拟DOM转换为真实DOM并插入父元素 function mountChildren(parentNode, children, vparent, context, updateQueue) { + parentNode.vchildren = children; for (var i = 0, n = children.length; i < n; i++) { - parentNode.appendChild(mountVnode(null, children[i], vparent, context, updateQueue)); + var vnode = children[i]; + parentNode.appendChild(mountVnode(null, vnode, vparent, context, updateQueue)); } } @@ -2282,6 +2294,7 @@ function alignChildren(parentNode, children, vparent, context, updateQueue) { insertPoint = childNodes[0] || null, j = 0, n = children.length; + parentNode.vchildren = children; for (var i = 0; i < n; i++) { var vnode = children[i]; var lastNode = childNodes[j]; @@ -2301,7 +2314,6 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue, pa var type = vnode.type, props = vnode.props; - var context = getContextByTypes(parentContext, type.contextTypes); var instance = instantiateComponent(type, vnode, props, context); //互相持有引用 var updater = instance.updater; @@ -2319,7 +2331,6 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue, pa } updater._hydrating = true; - var dom = updater.renderComponent(function (nextRendered, vparent, childContext) { return mountVnode(lastNode, nextRendered, vparent, childContext, updateQueue, updater //作为parentUpater往下传 ); @@ -2331,9 +2342,6 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue, pa } function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQueue) { - if (!lastVnode._instance) { - console.log(lastVnode); - } var type = lastVnode.type, ref = lastVnode.ref, instance = lastVnode._instance, @@ -2369,17 +2377,18 @@ function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQue updater.vparent = vparent; updater.parentContext = parentContext; // nextVnode._instance = instance; //不能放这里 - - /* if (updateQueue.isMainProcess) { + if (updateQueue.isMainProcess) { queue = updateQueue; queue = []; - console.log("转换为子"); - }else{ + } else { queue = updateQueue; - }*/ - refreshComponent(updater, updateQueue); + } + refreshComponent(updater, queue); //子组件先执行 updateQueue.push(updater); + if (!updater._hostNode) { + console.log("出问题了", updater, lastVnode); + } return updater._hostNode; } @@ -2408,39 +2417,30 @@ function refreshComponent(updater, updateQueue) { updater._forceUpdate = false; - instance.state = nextState; //既然setState了,无论shouldComponentUpdate结果如何,用户传给的state对象都会作用到组件上 - + instance.context = nextContext; if (!shouldUpdate) { - if (lastVnode && lastVnode !== vnode) { - for (var i in lastVnode) { - if (!vnode[i]) { - vnode[i] = lastVnode[i]; - } - } - lastVnode._instance = instance; - if (lastVnode.props.children) { - vnode.props.children = lastVnode.props.children; - } - } return dom; } instance.props = nextProps; - instance.context = nextContext; updater._hydrating = true; updater.lastProps = lastProps; updater.lastState = lastState; updater.lastContext = lastContext; var lastRendered = updater.rendered; - //这里会更新instance的props, context, state + dom = updater.renderComponent(function (nextRendered, vparent, childContext) { return alignVnode(lastRendered, nextRendered, vparent, childContext, updateQueue, updater); }); + + updater.lastVnode = vnode; updater._lifeStage = 2; - updater._hydrating = false; + // updater._hydrating = false; if (!updateQueue.isMainProcess) { drainQueue(updateQueue); + } else { + updater._hydrating = false; } return dom; @@ -2465,6 +2465,10 @@ function alignVnode(lastVnode, nextVnode, vparent, context, updateQueue, parentU function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { var dom = lastVnode._hostNode; + if (dom === null) { + console.log("此节点已经被移除", vparent); + return null; + } var lastProps = lastVnode.props; var nextProps = nextVnode.props; var ref = nextVnode.ref; @@ -2483,7 +2487,7 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { } mountChildren(dom, vchildren, nextVnode, context, updateQueue); } else { - diffChildren(lastVnode, nextVnode, dom, context, updateQueue); + diffChildren(lastVnode, nextVnode, dom, context, updateQueue, vparent); } } @@ -2500,8 +2504,8 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { } function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { - var lastChildren = lastVnode.vchildren, - nextChildren = flattenChildren(nextVnode), + var lastChildren = parentNode.vchildren; // lastVnode.vchildren; + var nextChildren = flattenChildren(nextVnode), nextLength = nextChildren.length, lastLength = lastChildren.length, dom = void 0; @@ -2523,23 +2527,18 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { actions = [], i = 0, hit = void 0, - oldDom = void 0, nextChild = void 0, lastChild = void 0; //第一次循环,构建移动指令(actions)与移除名单(removeHits)与命中名单(fuzzyHits) + if (nextLength) { actions.length = nextLength; while (i < maxLength) { nextChild = nextChildren[i]; lastChild = lastChildren[i]; - if (nextChild && lastChild && isSameNode(lastChild, nextChild)) { // 如果能直接找到,命名90%的情况 - actions[i] = { - last: lastChild, - next: nextChild, - directive: "update" - }; + actions[i] = [lastChild, nextChild]; removeHits[i] = true; } else { if (nextChild) { @@ -2547,11 +2546,7 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { if (fuzzyHits[hit] && fuzzyHits[hit].length) { var oldChild = fuzzyHits[hit].shift(); // 如果命中旧的节点,将旧的节点移动新节点的位置,向后移动 - actions[i] = { - last: oldChild, - next: nextChild, - directive: "moveAfter" - }; + actions[i] = [oldChild, nextChild, "moveAfter"]; removeHits[oldChild._i] = true; } } @@ -2573,45 +2568,42 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { for (var j = 0, n = actions.length; j < n; j++) { var action = actions[j]; if (!action) { - var curChild = nextChildren[j]; - hit = curChild.type + (curChild.key || ""); + nextChild = nextChildren[j]; + hit = nextChild.type + (nextChild.key || ""); if (fuzzyHits[hit] && fuzzyHits[hit].length) { - oldChild = fuzzyHits[hit].shift(); - oldDom = oldChild._hostNode; - parentNode.insertBefore(oldDom, insertPoint); - - if (oldChild.vtype > 1 && !oldChild._instance) { - console.log("有问题"); - } - dom = updateVnode(oldChild, curChild, lastVnode, context, updateQueue); - removeHits[oldChild._i] = true; - } else { - //为了兼容 react stack reconciliation的执行顺序,添加下面三行, - //在插入节点前,将原位置上节点对应的组件先移除 - var removed = lastChildren[j]; - if (removed && !removed._disposed && !removeHits[j]) { - disposeVnode(removed); - } - //如果找不到对应的旧节点,创建一个新节点放在这里 - dom = mountVnode(null, curChild, lastVnode, context, updateQueue); + lastChild = fuzzyHits[hit].shift(); + action = [lastChild, nextChild, "moveAfter"]; + } + } + if (action) { + lastChild = action[0]; + nextChild = action[1]; + dom = lastChild._hostNode; + if (action[2]) { parentNode.insertBefore(dom, insertPoint); } - } else { - oldDom = action.last._hostNode; - if (action.action === "moveAfter") { - parentNode.insertBefore(oldDom, insertPoint); + insertPoint = updateVnode(lastChild, nextChild, lastVnode, context, updateQueue); + if (!nextChild._hostNode) { + nextChildren[j] = lastChild; } - oldChild = action.last; - if (oldChild.vtype > 1 && !oldChild._instance) { - //正处于动画过程中 - nextChildren[j] = oldChild; - } else { - dom = updateVnode(action.last, action.next, lastVnode, context, updateQueue); + removeHits[lastChild._i] = true; + } else { + //为了兼容 react stack reconciliation的执行顺序,添加下面三行, + //在插入节点前,将原位置上节点对应的组件先移除 + var removed = lastChildren[j]; + if (removed && !removed._disposed && !removeHits[j]) { + disposeVnode(removed); } + //如果找不到对应的旧节点,创建一个新节点放在这里 + dom = mountVnode(null, nextChild, lastVnode, context, updateQueue); + parentNode.insertBefore(dom, insertPoint); + insertPoint = dom; } - insertPoint = dom.nextSibling; + insertPoint = insertPoint.nextSibling; } + parentNode.vchildren = nextChildren; + //移除 lastChildren.forEach(function (el, i) { if (!removeHits[i]) { diff --git a/dist/ReactSelection.js b/dist/ReactSelection.js index 92419b133..d0fe6af8b 100644 --- a/dist/ReactSelection.js +++ b/dist/ReactSelection.js @@ -1,5 +1,5 @@ /** - * IE6+,有问题请加QQ 370262116 by 司徒正美 Copyright 2017-10-03 + * IE6+,有问题请加QQ 370262116 by 司徒正美 Copyright 2017-10-06 */ (function (global, factory) { @@ -521,7 +521,9 @@ var Children = { return ret; }, forEach: function forEach(children, callback, context) { - _flattenChildren(children, false).forEach(callback, context); + if (children != null) { + _flattenChildren(children, false).forEach(callback, context); + } }, @@ -1640,7 +1642,6 @@ function Updater(instance, vnode) { this.mergeStates = alwaysNull; } } - Updater.prototype = { mergeStates: function mergeStates() { var instance = this._instance, @@ -1689,11 +1690,16 @@ Updater.prototype = { this.rendered = rendered; var childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; var dom = cb(rendered, this.vparent, childContext); - - updateChains[this._mountOrder].forEach(function (el) { + if (!dom) { + throw ["必须返回节点", rendered]; + } + var list = updateChains[this._mountOrder]; + if (!list) { + list = updateChains[this._mountOrder] = [this]; + } + list.forEach(function (el) { el.vnode._hostNode = el._hostNode = dom; }); - return dom; } }; @@ -1989,10 +1995,10 @@ function clearRefs() { }); } function callUpdate(updater, instance) { + if (pendingRefs.length) { + clearRefs(); + } if (updater._lifeStage === 2) { - if (pendingRefs.length) { - clearRefs(); - } if (instance.componentDidUpdate) { updater._didUpdate = true; instance.componentDidUpdate(updater.lastProps, updater.lastState, updater.lastContext); @@ -2001,6 +2007,7 @@ function callUpdate(updater, instance) { } } options.afterUpdate(instance); + updater._hydrating = false; updater._lifeStage = 1; } } @@ -2026,6 +2033,7 @@ function drainQueue(queue) { } updater._lifeStage = 1; options.afterMount(instance); + updater._hydrating = false; } else { callUpdate(updater, instance); } @@ -2033,8 +2041,9 @@ function drainQueue(queue) { if (ref) { ref(instance.__isStateless ? null : instance); } - updater._hydrating = false; //子树已经构建完毕 + // updater._hydrating = false; //子树已经构建完毕 while (updater._renderInNextCycle) { + options.refreshComponent(updater, queue); callUpdate(updater, instance); } @@ -2230,6 +2239,7 @@ function genMountElement(lastNode, vnode, vparent, type) { return lastNode; } else { var dom = createDOMElement(vnode, vparent); + if (lastNode) { while (lastNode.firstChild) { dom.appendChild(lastNode.firstChild); @@ -2253,10 +2263,10 @@ function mountElement(lastNode, vnode, vparent, context, updateQueue) { var dom = genMountElement(lastNode, vnode, vparent, type); vnode._hostNode = dom; + var children = flattenChildren(vnode); var method = lastNode ? alignChildren : mountChildren; method(dom, children, vnode, context, updateQueue); - if (vnode.checkProps) { diffProps(props, {}, vnode, {}, dom); } @@ -2272,8 +2282,10 @@ function mountElement(lastNode, vnode, vparent, context, updateQueue) { //将虚拟DOM转换为真实DOM并插入父元素 function mountChildren(parentNode, children, vparent, context, updateQueue) { + parentNode.vchildren = children; for (var i = 0, n = children.length; i < n; i++) { - parentNode.appendChild(mountVnode(null, children[i], vparent, context, updateQueue)); + var vnode = children[i]; + parentNode.appendChild(mountVnode(null, vnode, vparent, context, updateQueue)); } } @@ -2282,6 +2294,7 @@ function alignChildren(parentNode, children, vparent, context, updateQueue) { insertPoint = childNodes[0] || null, j = 0, n = children.length; + parentNode.vchildren = children; for (var i = 0; i < n; i++) { var vnode = children[i]; var lastNode = childNodes[j]; @@ -2301,7 +2314,6 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue, pa var type = vnode.type, props = vnode.props; - var context = getContextByTypes(parentContext, type.contextTypes); var instance = instantiateComponent(type, vnode, props, context); //互相持有引用 var updater = instance.updater; @@ -2319,7 +2331,6 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue, pa } updater._hydrating = true; - var dom = updater.renderComponent(function (nextRendered, vparent, childContext) { return mountVnode(lastNode, nextRendered, vparent, childContext, updateQueue, updater //作为parentUpater往下传 ); @@ -2331,9 +2342,6 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue, pa } function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQueue) { - if (!lastVnode._instance) { - console.log(lastVnode); - } var type = lastVnode.type, ref = lastVnode.ref, instance = lastVnode._instance, @@ -2369,17 +2377,18 @@ function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQue updater.vparent = vparent; updater.parentContext = parentContext; // nextVnode._instance = instance; //不能放这里 - - /* if (updateQueue.isMainProcess) { + if (updateQueue.isMainProcess) { queue = updateQueue; queue = []; - console.log("转换为子"); - }else{ + } else { queue = updateQueue; - }*/ - refreshComponent(updater, updateQueue); + } + refreshComponent(updater, queue); //子组件先执行 updateQueue.push(updater); + if (!updater._hostNode) { + console.log("出问题了", updater, lastVnode); + } return updater._hostNode; } @@ -2408,39 +2417,30 @@ function refreshComponent(updater, updateQueue) { updater._forceUpdate = false; - instance.state = nextState; //既然setState了,无论shouldComponentUpdate结果如何,用户传给的state对象都会作用到组件上 - + instance.context = nextContext; if (!shouldUpdate) { - if (lastVnode && lastVnode !== vnode) { - for (var i in lastVnode) { - if (!vnode[i]) { - vnode[i] = lastVnode[i]; - } - } - lastVnode._instance = instance; - if (lastVnode.props.children) { - vnode.props.children = lastVnode.props.children; - } - } return dom; } instance.props = nextProps; - instance.context = nextContext; updater._hydrating = true; updater.lastProps = lastProps; updater.lastState = lastState; updater.lastContext = lastContext; var lastRendered = updater.rendered; - //这里会更新instance的props, context, state + dom = updater.renderComponent(function (nextRendered, vparent, childContext) { return alignVnode(lastRendered, nextRendered, vparent, childContext, updateQueue, updater); }); + + updater.lastVnode = vnode; updater._lifeStage = 2; - updater._hydrating = false; + // updater._hydrating = false; if (!updateQueue.isMainProcess) { drainQueue(updateQueue); + } else { + updater._hydrating = false; } return dom; @@ -2465,6 +2465,10 @@ function alignVnode(lastVnode, nextVnode, vparent, context, updateQueue, parentU function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { var dom = lastVnode._hostNode; + if (dom === null) { + console.log("此节点已经被移除", vparent); + return null; + } var lastProps = lastVnode.props; var nextProps = nextVnode.props; var ref = nextVnode.ref; @@ -2483,7 +2487,7 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { } mountChildren(dom, vchildren, nextVnode, context, updateQueue); } else { - diffChildren(lastVnode, nextVnode, dom, context, updateQueue); + diffChildren(lastVnode, nextVnode, dom, context, updateQueue, vparent); } } @@ -2500,8 +2504,8 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { } function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { - var lastChildren = lastVnode.vchildren, - nextChildren = flattenChildren(nextVnode), + var lastChildren = parentNode.vchildren; // lastVnode.vchildren; + var nextChildren = flattenChildren(nextVnode), nextLength = nextChildren.length, lastLength = lastChildren.length, dom = void 0; @@ -2523,23 +2527,18 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { actions = [], i = 0, hit = void 0, - oldDom = void 0, nextChild = void 0, lastChild = void 0; //第一次循环,构建移动指令(actions)与移除名单(removeHits)与命中名单(fuzzyHits) + if (nextLength) { actions.length = nextLength; while (i < maxLength) { nextChild = nextChildren[i]; lastChild = lastChildren[i]; - if (nextChild && lastChild && isSameNode(lastChild, nextChild)) { // 如果能直接找到,命名90%的情况 - actions[i] = { - last: lastChild, - next: nextChild, - directive: "update" - }; + actions[i] = [lastChild, nextChild]; removeHits[i] = true; } else { if (nextChild) { @@ -2547,11 +2546,7 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { if (fuzzyHits[hit] && fuzzyHits[hit].length) { var oldChild = fuzzyHits[hit].shift(); // 如果命中旧的节点,将旧的节点移动新节点的位置,向后移动 - actions[i] = { - last: oldChild, - next: nextChild, - directive: "moveAfter" - }; + actions[i] = [oldChild, nextChild, "moveAfter"]; removeHits[oldChild._i] = true; } } @@ -2573,45 +2568,42 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { for (var j = 0, n = actions.length; j < n; j++) { var action = actions[j]; if (!action) { - var curChild = nextChildren[j]; - hit = curChild.type + (curChild.key || ""); + nextChild = nextChildren[j]; + hit = nextChild.type + (nextChild.key || ""); if (fuzzyHits[hit] && fuzzyHits[hit].length) { - oldChild = fuzzyHits[hit].shift(); - oldDom = oldChild._hostNode; - parentNode.insertBefore(oldDom, insertPoint); - - if (oldChild.vtype > 1 && !oldChild._instance) { - console.log("有问题"); - } - dom = updateVnode(oldChild, curChild, lastVnode, context, updateQueue); - removeHits[oldChild._i] = true; - } else { - //为了兼容 react stack reconciliation的执行顺序,添加下面三行, - //在插入节点前,将原位置上节点对应的组件先移除 - var removed = lastChildren[j]; - if (removed && !removed._disposed && !removeHits[j]) { - disposeVnode(removed); - } - //如果找不到对应的旧节点,创建一个新节点放在这里 - dom = mountVnode(null, curChild, lastVnode, context, updateQueue); + lastChild = fuzzyHits[hit].shift(); + action = [lastChild, nextChild, "moveAfter"]; + } + } + if (action) { + lastChild = action[0]; + nextChild = action[1]; + dom = lastChild._hostNode; + if (action[2]) { parentNode.insertBefore(dom, insertPoint); } - } else { - oldDom = action.last._hostNode; - if (action.action === "moveAfter") { - parentNode.insertBefore(oldDom, insertPoint); + insertPoint = updateVnode(lastChild, nextChild, lastVnode, context, updateQueue); + if (!nextChild._hostNode) { + nextChildren[j] = lastChild; } - oldChild = action.last; - if (oldChild.vtype > 1 && !oldChild._instance) { - //正处于动画过程中 - nextChildren[j] = oldChild; - } else { - dom = updateVnode(action.last, action.next, lastVnode, context, updateQueue); + removeHits[lastChild._i] = true; + } else { + //为了兼容 react stack reconciliation的执行顺序,添加下面三行, + //在插入节点前,将原位置上节点对应的组件先移除 + var removed = lastChildren[j]; + if (removed && !removed._disposed && !removeHits[j]) { + disposeVnode(removed); } + //如果找不到对应的旧节点,创建一个新节点放在这里 + dom = mountVnode(null, nextChild, lastVnode, context, updateQueue); + parentNode.insertBefore(dom, insertPoint); + insertPoint = dom; } - insertPoint = dom.nextSibling; + insertPoint = insertPoint.nextSibling; } + parentNode.vchildren = nextChildren; + //移除 lastChildren.forEach(function (el, i) { if (!removeHits[i]) { diff --git a/dist/ReactShim.js b/dist/ReactShim.js index d81688894..c3fb6e3b6 100644 --- a/dist/ReactShim.js +++ b/dist/ReactShim.js @@ -1,7 +1,7 @@ /** * 此版本要求浏览器没有createClass, createFactory, PropTypes, isValidElement, * unmountComponentAtNode,unstable_renderSubtreeIntoContainer - * QQ 370262116 by 司徒正美 Copyright 2017-10-03 + * QQ 370262116 by 司徒正美 Copyright 2017-10-06 */ (function (global, factory) { @@ -613,7 +613,9 @@ var Children = { return ret; }, forEach: function forEach(children, callback, context) { - _flattenChildren(children, false).forEach(callback, context); + if (children != null) { + _flattenChildren(children, false).forEach(callback, context); + } }, @@ -1486,7 +1488,6 @@ function Updater(instance, vnode) { this.mergeStates = alwaysNull; } } - Updater.prototype = { mergeStates: function mergeStates() { var instance = this._instance, @@ -1535,11 +1536,16 @@ Updater.prototype = { this.rendered = rendered; var childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; var dom = cb(rendered, this.vparent, childContext); - - updateChains[this._mountOrder].forEach(function (el) { + if (!dom) { + throw ["必须返回节点", rendered]; + } + var list = updateChains[this._mountOrder]; + if (!list) { + list = updateChains[this._mountOrder] = [this]; + } + list.forEach(function (el) { el.vnode._hostNode = el._hostNode = dom; }); - return dom; } }; @@ -1835,10 +1841,10 @@ function clearRefs() { }); } function callUpdate(updater, instance) { + if (pendingRefs.length) { + clearRefs(); + } if (updater._lifeStage === 2) { - if (pendingRefs.length) { - clearRefs(); - } if (instance.componentDidUpdate) { updater._didUpdate = true; instance.componentDidUpdate(updater.lastProps, updater.lastState, updater.lastContext); @@ -1847,6 +1853,7 @@ function callUpdate(updater, instance) { } } options.afterUpdate(instance); + updater._hydrating = false; updater._lifeStage = 1; } } @@ -1872,6 +1879,7 @@ function drainQueue(queue) { } updater._lifeStage = 1; options.afterMount(instance); + updater._hydrating = false; } else { callUpdate(updater, instance); } @@ -1879,8 +1887,9 @@ function drainQueue(queue) { if (ref) { ref(instance.__isStateless ? null : instance); } - updater._hydrating = false; //子树已经构建完毕 + // updater._hydrating = false; //子树已经构建完毕 while (updater._renderInNextCycle) { + options.refreshComponent(updater, queue); callUpdate(updater, instance); } @@ -2072,6 +2081,7 @@ function genMountElement(lastNode, vnode, vparent, type) { return lastNode; } else { var dom = createDOMElement(vnode, vparent); + if (lastNode) { while (lastNode.firstChild) { dom.appendChild(lastNode.firstChild); @@ -2095,10 +2105,10 @@ function mountElement(lastNode, vnode, vparent, context, updateQueue) { var dom = genMountElement(lastNode, vnode, vparent, type); vnode._hostNode = dom; + var children = flattenChildren(vnode); var method = lastNode ? alignChildren : mountChildren; method(dom, children, vnode, context, updateQueue); - if (vnode.checkProps) { diffProps(props, {}, vnode, {}, dom); } @@ -2114,8 +2124,10 @@ function mountElement(lastNode, vnode, vparent, context, updateQueue) { //将虚拟DOM转换为真实DOM并插入父元素 function mountChildren(parentNode, children, vparent, context, updateQueue) { + parentNode.vchildren = children; for (var i = 0, n = children.length; i < n; i++) { - parentNode.appendChild(mountVnode(null, children[i], vparent, context, updateQueue)); + var vnode = children[i]; + parentNode.appendChild(mountVnode(null, vnode, vparent, context, updateQueue)); } } @@ -2124,6 +2136,7 @@ function alignChildren(parentNode, children, vparent, context, updateQueue) { insertPoint = childNodes[0] || null, j = 0, n = children.length; + parentNode.vchildren = children; for (var i = 0; i < n; i++) { var vnode = children[i]; var lastNode = childNodes[j]; @@ -2143,7 +2156,6 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue, pa var type = vnode.type, props = vnode.props; - var context = getContextByTypes(parentContext, type.contextTypes); var instance = instantiateComponent(type, vnode, props, context); //互相持有引用 var updater = instance.updater; @@ -2161,7 +2173,6 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue, pa } updater._hydrating = true; - var dom = updater.renderComponent(function (nextRendered, vparent, childContext) { return mountVnode(lastNode, nextRendered, vparent, childContext, updateQueue, updater //作为parentUpater往下传 ); @@ -2173,9 +2184,6 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue, pa } function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQueue) { - if (!lastVnode._instance) { - console.log(lastVnode); - } var type = lastVnode.type, ref = lastVnode.ref, instance = lastVnode._instance, @@ -2211,17 +2219,18 @@ function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQue updater.vparent = vparent; updater.parentContext = parentContext; // nextVnode._instance = instance; //不能放这里 - - /* if (updateQueue.isMainProcess) { + if (updateQueue.isMainProcess) { queue = updateQueue; queue = []; - console.log("转换为子"); - }else{ + } else { queue = updateQueue; - }*/ - refreshComponent(updater, updateQueue); + } + refreshComponent(updater, queue); //子组件先执行 updateQueue.push(updater); + if (!updater._hostNode) { + console.log("出问题了", updater, lastVnode); + } return updater._hostNode; } @@ -2250,39 +2259,30 @@ function refreshComponent(updater, updateQueue) { updater._forceUpdate = false; - instance.state = nextState; //既然setState了,无论shouldComponentUpdate结果如何,用户传给的state对象都会作用到组件上 - + instance.context = nextContext; if (!shouldUpdate) { - if (lastVnode && lastVnode !== vnode) { - for (var i in lastVnode) { - if (!vnode[i]) { - vnode[i] = lastVnode[i]; - } - } - lastVnode._instance = instance; - if (lastVnode.props.children) { - vnode.props.children = lastVnode.props.children; - } - } return dom; } instance.props = nextProps; - instance.context = nextContext; updater._hydrating = true; updater.lastProps = lastProps; updater.lastState = lastState; updater.lastContext = lastContext; var lastRendered = updater.rendered; - //这里会更新instance的props, context, state + dom = updater.renderComponent(function (nextRendered, vparent, childContext) { return alignVnode(lastRendered, nextRendered, vparent, childContext, updateQueue, updater); }); + + updater.lastVnode = vnode; updater._lifeStage = 2; - updater._hydrating = false; + // updater._hydrating = false; if (!updateQueue.isMainProcess) { drainQueue(updateQueue); + } else { + updater._hydrating = false; } return dom; @@ -2307,6 +2307,10 @@ function alignVnode(lastVnode, nextVnode, vparent, context, updateQueue, parentU function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { var dom = lastVnode._hostNode; + if (dom === null) { + console.log("此节点已经被移除", vparent); + return null; + } var lastProps = lastVnode.props; var nextProps = nextVnode.props; var ref = nextVnode.ref; @@ -2325,7 +2329,7 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { } mountChildren(dom, vchildren, nextVnode, context, updateQueue); } else { - diffChildren(lastVnode, nextVnode, dom, context, updateQueue); + diffChildren(lastVnode, nextVnode, dom, context, updateQueue, vparent); } } @@ -2342,8 +2346,8 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { } function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { - var lastChildren = lastVnode.vchildren, - nextChildren = flattenChildren(nextVnode), + var lastChildren = parentNode.vchildren; // lastVnode.vchildren; + var nextChildren = flattenChildren(nextVnode), nextLength = nextChildren.length, lastLength = lastChildren.length, dom = void 0; @@ -2365,23 +2369,18 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { actions = [], i = 0, hit = void 0, - oldDom = void 0, nextChild = void 0, lastChild = void 0; //第一次循环,构建移动指令(actions)与移除名单(removeHits)与命中名单(fuzzyHits) + if (nextLength) { actions.length = nextLength; while (i < maxLength) { nextChild = nextChildren[i]; lastChild = lastChildren[i]; - if (nextChild && lastChild && isSameNode(lastChild, nextChild)) { // 如果能直接找到,命名90%的情况 - actions[i] = { - last: lastChild, - next: nextChild, - directive: "update" - }; + actions[i] = [lastChild, nextChild]; removeHits[i] = true; } else { if (nextChild) { @@ -2389,11 +2388,7 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { if (fuzzyHits[hit] && fuzzyHits[hit].length) { var oldChild = fuzzyHits[hit].shift(); // 如果命中旧的节点,将旧的节点移动新节点的位置,向后移动 - actions[i] = { - last: oldChild, - next: nextChild, - directive: "moveAfter" - }; + actions[i] = [oldChild, nextChild, "moveAfter"]; removeHits[oldChild._i] = true; } } @@ -2415,45 +2410,42 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { for (var j = 0, n = actions.length; j < n; j++) { var action = actions[j]; if (!action) { - var curChild = nextChildren[j]; - hit = curChild.type + (curChild.key || ""); + nextChild = nextChildren[j]; + hit = nextChild.type + (nextChild.key || ""); if (fuzzyHits[hit] && fuzzyHits[hit].length) { - oldChild = fuzzyHits[hit].shift(); - oldDom = oldChild._hostNode; - parentNode.insertBefore(oldDom, insertPoint); - - if (oldChild.vtype > 1 && !oldChild._instance) { - console.log("有问题"); - } - dom = updateVnode(oldChild, curChild, lastVnode, context, updateQueue); - removeHits[oldChild._i] = true; - } else { - //为了兼容 react stack reconciliation的执行顺序,添加下面三行, - //在插入节点前,将原位置上节点对应的组件先移除 - var removed = lastChildren[j]; - if (removed && !removed._disposed && !removeHits[j]) { - disposeVnode(removed); - } - //如果找不到对应的旧节点,创建一个新节点放在这里 - dom = mountVnode(null, curChild, lastVnode, context, updateQueue); + lastChild = fuzzyHits[hit].shift(); + action = [lastChild, nextChild, "moveAfter"]; + } + } + if (action) { + lastChild = action[0]; + nextChild = action[1]; + dom = lastChild._hostNode; + if (action[2]) { parentNode.insertBefore(dom, insertPoint); } - } else { - oldDom = action.last._hostNode; - if (action.action === "moveAfter") { - parentNode.insertBefore(oldDom, insertPoint); + insertPoint = updateVnode(lastChild, nextChild, lastVnode, context, updateQueue); + if (!nextChild._hostNode) { + nextChildren[j] = lastChild; } - oldChild = action.last; - if (oldChild.vtype > 1 && !oldChild._instance) { - //正处于动画过程中 - nextChildren[j] = oldChild; - } else { - dom = updateVnode(action.last, action.next, lastVnode, context, updateQueue); + removeHits[lastChild._i] = true; + } else { + //为了兼容 react stack reconciliation的执行顺序,添加下面三行, + //在插入节点前,将原位置上节点对应的组件先移除 + var removed = lastChildren[j]; + if (removed && !removed._disposed && !removeHits[j]) { + disposeVnode(removed); } + //如果找不到对应的旧节点,创建一个新节点放在这里 + dom = mountVnode(null, nextChild, lastVnode, context, updateQueue); + parentNode.insertBefore(dom, insertPoint); + insertPoint = dom; } - insertPoint = dom.nextSibling; + insertPoint = insertPoint.nextSibling; } + parentNode.vchildren = nextChildren; + //移除 lastChildren.forEach(function (el, i) { if (!removeHits[i]) { diff --git a/src/diff.js b/src/diff.js index c6d39685e..a9fab55c8 100644 --- a/src/diff.js +++ b/src/diff.js @@ -165,6 +165,7 @@ function genMountElement(lastNode, vnode, vparent, type) { return lastNode; } else { let dom = createDOMElement(vnode, vparent); + if (lastNode) { while (lastNode.firstChild) { dom.appendChild(lastNode.firstChild); @@ -185,10 +186,10 @@ function mountElement(lastNode, vnode, vparent, context, updateQueue) { let dom = genMountElement(lastNode, vnode, vparent, type); vnode._hostNode = dom; + var children = flattenChildren(vnode); let method = lastNode ? alignChildren : mountChildren; method(dom, children, vnode, context, updateQueue); - if (vnode.checkProps) { diffProps(props, {}, vnode, {}, dom); } @@ -204,8 +205,10 @@ function mountElement(lastNode, vnode, vparent, context, updateQueue) { //将虚拟DOM转换为真实DOM并插入父元素 function mountChildren(parentNode, children, vparent, context, updateQueue) { + parentNode.vchildren = children; for (let i = 0, n = children.length; i < n; i++) { - parentNode.appendChild(mountVnode(null, children[i], vparent, context, updateQueue)); + var vnode = children[i]; + parentNode.appendChild(mountVnode(null, vnode, vparent, context, updateQueue)); } } @@ -214,6 +217,7 @@ function alignChildren(parentNode, children, vparent, context, updateQueue) { insertPoint = childNodes[0] || null, j = 0, n = children.length; + parentNode.vchildren = children; for (let i = 0; i < n; i++) { let vnode = children[i]; let lastNode = childNodes[j]; @@ -231,7 +235,6 @@ function alignChildren(parentNode, children, vparent, context, updateQueue) { function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue, parentUpdater) { let { type, props } = vnode; - let context = getContextByTypes(parentContext, type.contextTypes); let instance = instantiateComponent(type, vnode, props, context); //互相持有引用 let updater = instance.updater; @@ -249,7 +252,6 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue, pa } updater._hydrating = true; - let dom = updater.renderComponent(function(nextRendered, vparent, childContext) { return mountVnode( lastNode, @@ -259,18 +261,15 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue, pa updateQueue, updater //作为parentUpater往下传 ); + }, updater.rendered); updateQueue.push(updater); - return dom; } function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQueue) { - if(!lastVnode._instance){ - console.log(lastVnode); - } let { type, ref, _instance: instance, vtype } = lastVnode; let nextContext,queue, nextProps = nextVnode.props, @@ -301,20 +300,22 @@ function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQue updater.vparent = vparent; updater.parentContext = parentContext; // nextVnode._instance = instance; //不能放这里 - - /* if (updateQueue.isMainProcess) { + if (updateQueue.isMainProcess) { queue = updateQueue; queue = []; - console.log("转换为子"); }else{ queue = updateQueue; - }*/ - refreshComponent(updater, updateQueue); + } + refreshComponent(updater, queue); //子组件先执行 updateQueue.push(updater); + if(!updater._hostNode){ + console.log("出问题了",updater, lastVnode); + } return updater._hostNode; } + function refreshComponent(updater, updateQueue) { let { _instance: instance, _hostNode: dom, context: nextContext, props: nextProps, vnode, lastVnode } = updater; @@ -331,39 +332,31 @@ function refreshComponent(updater, updateQueue) { let { props: lastProps, context: lastContext, state: lastState } = instance; updater._forceUpdate = false; - instance.state = nextState; //既然setState了,无论shouldComponentUpdate结果如何,用户传给的state对象都会作用到组件上 - + instance.context = nextContext; if (!shouldUpdate) { - if (lastVnode && lastVnode !== vnode) { - for(var i in lastVnode){ - if(!vnode[i]){ - vnode[i] = lastVnode[i]; - } - } - lastVnode._instance = instance; - if(lastVnode.props.children){ - vnode.props.children = lastVnode.props.children; - } - } return dom; } instance.props = nextProps; - instance.context = nextContext; updater._hydrating = true; updater.lastProps = lastProps; updater.lastState = lastState; updater.lastContext = lastContext; let lastRendered = updater.rendered; - //这里会更新instance的props, context, state + dom = updater.renderComponent(function(nextRendered, vparent, childContext) { return alignVnode(lastRendered, nextRendered, vparent, childContext, updateQueue, updater); }); + + + updater.lastVnode = vnode; updater._lifeStage = 2; - updater._hydrating = false; + // updater._hydrating = false; if (!updateQueue.isMainProcess) { drainQueue(updateQueue); + }else{ + updater._hydrating = false; } return dom; @@ -386,6 +379,10 @@ export function alignVnode(lastVnode, nextVnode, vparent, context, updateQueue, function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { let dom = lastVnode._hostNode; + if(dom === null){ + console.log("此节点已经被移除",vparent); + return null; + } let lastProps = lastVnode.props; let nextProps = nextVnode.props; let ref = nextVnode.ref; @@ -404,7 +401,7 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { } mountChildren(dom, vchildren, nextVnode, context, updateQueue); } else { - diffChildren(lastVnode, nextVnode, dom, context, updateQueue); + diffChildren(lastVnode, nextVnode, dom, context, updateQueue, vparent); } } @@ -420,11 +417,13 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { return dom; } + function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { - let lastChildren = lastVnode.vchildren, - nextChildren = flattenChildren(nextVnode), + let lastChildren = parentNode.vchildren;// lastVnode.vchildren; + let nextChildren = flattenChildren(nextVnode), nextLength = nextChildren.length, lastLength = lastChildren.length, dom; + //如果旧数组长度为零, 直接添加 if (nextLength && !lastLength) { @@ -443,23 +442,18 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { actions = [], i = 0, hit, - oldDom, nextChild, lastChild; //第一次循环,构建移动指令(actions)与移除名单(removeHits)与命中名单(fuzzyHits) + if (nextLength) { actions.length = nextLength; while (i < maxLength) { nextChild = nextChildren[i]; lastChild = lastChildren[i]; - if (nextChild && lastChild && isSameNode(lastChild, nextChild)) { // 如果能直接找到,命名90%的情况 - actions[i] = { - last: lastChild, - next: nextChild, - directive: "update" - }; + actions[i] = [lastChild,nextChild]; removeHits[i] = true; } else { if (nextChild) { @@ -467,11 +461,7 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { if (fuzzyHits[hit] && fuzzyHits[hit].length) { var oldChild = fuzzyHits[hit].shift(); // 如果命中旧的节点,将旧的节点移动新节点的位置,向后移动 - actions[i] = { - last: oldChild, - next: nextChild, - directive: "moveAfter" - }; + actions[i] = [ oldChild, nextChild, "moveAfter" ]; removeHits[oldChild._i] = true; } } @@ -493,46 +483,42 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { for (let j = 0, n = actions.length; j < n; j++) { let action = actions[j]; if (!action) { - let curChild = nextChildren[j]; - hit = curChild.type + (curChild.key || ""); + nextChild = nextChildren[j]; + hit = nextChild.type + (nextChild.key || ""); if (fuzzyHits[hit] && fuzzyHits[hit].length) { - oldChild = fuzzyHits[hit].shift(); - oldDom = oldChild._hostNode; - parentNode.insertBefore(oldDom, insertPoint); - - if(oldChild.vtype > 1 && !oldChild._instance){ - console.log("有问题"); - } - dom = updateVnode(oldChild, curChild, lastVnode, context, updateQueue); - removeHits[oldChild._i] = true; - } else { - //为了兼容 react stack reconciliation的执行顺序,添加下面三行, - //在插入节点前,将原位置上节点对应的组件先移除 - var removed = lastChildren[j]; - if (removed && !removed._disposed && !removeHits[j]) { - disposeVnode(removed); - } - //如果找不到对应的旧节点,创建一个新节点放在这里 - dom = mountVnode(null, curChild, lastVnode, context, updateQueue); + lastChild = fuzzyHits[hit].shift(); + action = [lastChild,nextChild, "moveAfter"]; + } + } + if(action){ + lastChild = action[0]; + nextChild = action[1]; + dom = lastChild._hostNode; + if (action[2]) { parentNode.insertBefore(dom, insertPoint); } - } else { - oldDom = action.last._hostNode; - if (action.action === "moveAfter") { - parentNode.insertBefore(oldDom, insertPoint); + insertPoint = updateVnode(lastChild, nextChild, lastVnode, context, updateQueue); + if(!nextChild._hostNode){ + nextChildren[j] = lastChild; } - oldChild = action.last; - if(oldChild.vtype > 1 && !oldChild._instance){ - //正处于动画过程中 - nextChildren[j] = oldChild; - }else{ - dom = updateVnode(action.last, action.next, lastVnode, context, updateQueue); - + removeHits[lastChild._i] = true; + } else{ + //为了兼容 react stack reconciliation的执行顺序,添加下面三行, + //在插入节点前,将原位置上节点对应的组件先移除 + var removed = lastChildren[j]; + if (removed && !removed._disposed && !removeHits[j]) { + disposeVnode(removed); } + //如果找不到对应的旧节点,创建一个新节点放在这里 + dom = mountVnode(null, nextChild, lastVnode, context, updateQueue); + parentNode.insertBefore(dom, insertPoint); + insertPoint = dom; } - insertPoint = dom.nextSibling; + insertPoint = insertPoint.nextSibling; } + parentNode.vchildren = nextChildren; + //移除 lastChildren.forEach(function(el, i) { if (!removeHits[i]) { @@ -543,6 +529,7 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { disposeVnode(el); } }); + } function isSameNode(a, b) { diff --git a/src/instantiateComponent.js b/src/instantiateComponent.js index 75e249bc8..d79052b80 100644 --- a/src/instantiateComponent.js +++ b/src/instantiateComponent.js @@ -23,7 +23,6 @@ function Updater(instance, vnode) { this.mergeStates = alwaysNull; } } - Updater.prototype = { mergeStates: function() { let instance = this._instance, @@ -73,11 +72,16 @@ Updater.prototype = { this.rendered = rendered; let childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; let dom = cb(rendered, this.vparent, childContext); - - updateChains[this._mountOrder].forEach(function(el) { + if(!dom){ + throw ["必须返回节点",rendered]; + } + let list = updateChains[this._mountOrder]; + if(!list){ + list = updateChains[this._mountOrder] = [this]; + } + list.forEach(function(el) { el.vnode._hostNode = el._hostNode = dom; }); - return dom; } }; diff --git a/src/scheduler.js b/src/scheduler.js index 68d87cc46..41de880d3 100644 --- a/src/scheduler.js +++ b/src/scheduler.js @@ -12,10 +12,10 @@ function clearRefs() { }); } function callUpdate(updater, instance) { + if(pendingRefs.length){ + clearRefs(); + } if (updater._lifeStage === 2) { - if(pendingRefs.length){ - clearRefs(); - } if (instance.componentDidUpdate) { updater._didUpdate = true; instance.componentDidUpdate( @@ -28,6 +28,7 @@ function callUpdate(updater, instance) { } } options.afterUpdate(instance); + updater._hydrating = false; updater._lifeStage = 1; } } @@ -51,6 +52,7 @@ export function drainQueue(queue) { } updater._lifeStage = 1; options.afterMount(instance); + updater._hydrating = false; } else { callUpdate(updater, instance); } @@ -58,8 +60,9 @@ export function drainQueue(queue) { if (ref) { ref(instance.__isStateless ? null: instance); } - updater._hydrating = false; //子树已经构建完毕 + // updater._hydrating = false; //子树已经构建完毕 while (updater._renderInNextCycle) { + options.refreshComponent(updater, queue); callUpdate(updater, instance); } diff --git a/test/modules/ReactChildren-test.jsx b/test/modules/ReactChildren-test.jsx index 6b8da125e..4c7949673 100644 --- a/test/modules/ReactChildren-test.jsx +++ b/test/modules/ReactChildren-test.jsx @@ -126,7 +126,16 @@ describe("ReactChildren", function() {

, ]); }); - + it('React.Children.forEach不处理null void 0', () => { + var i = 0 + React.Children.forEach(null, function(){ + i++ + }) + React.Children.forEach(void 0, function(){ + i++ + }) + expect(i).toBe(0) + }) it('should traverse children of different kinds', () => { var div =
; var span = ; diff --git a/test/modules/ReactMultiChild-test.jsx b/test/modules/ReactMultiChild-test.jsx index b8304e907..36e842e83 100644 --- a/test/modules/ReactMultiChild-test.jsx +++ b/test/modules/ReactMultiChild-test.jsx @@ -10,7 +10,7 @@ var PropTypes = React.PropTypes; //https://github.com/facebook/react/blob/master/src/isomorphic/children/__tests__/ReactChildren-test.js var ReactDOM = window.ReactDOM || React; -describe("reconciliation", function() { +describe("ReactMultiChild", function() { this.timeout(200000); it("should update children when possible", () => { var container = document.createElement("div"); From d252828b04415a7aa5badf592d3145695b9dc979 Mon Sep 17 00:00:00 2001 From: RubyLouvre <1669866773@qq.com> Date: Fri, 6 Oct 2017 21:22:49 +0800 Subject: [PATCH 37/57] =?UTF-8?q?=E7=AE=80=E5=8C=96updateElement=E6=96=B9?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dist/React.js | 13 ++++--------- dist/ReactIE.js | 13 ++++--------- dist/ReactSelection.js | 13 ++++--------- dist/ReactShim.js | 13 ++++--------- src/diff.js | 13 ++++--------- 5 files changed, 20 insertions(+), 45 deletions(-) diff --git a/dist/React.js b/dist/React.js index dbd5c217b..b52da4270 100644 --- a/dist/React.js +++ b/dist/React.js @@ -2481,15 +2481,10 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { }); list.length = 0; } else { - var vchildren = flattenChildren(nextVnode); if (lastProps[innerHTML]) { - while (dom.firstChild) { - dom.removeChild(dom.firstChild); - } - mountChildren(dom, vchildren, nextVnode, context, updateQueue); - } else { - diffChildren(lastVnode, nextVnode, dom, context, updateQueue, vparent); + dom.vchildren = []; } + diffChildren(lastVnode, nextVnode, dom, context, updateQueue); } if (lastVnode.checkProps || nextVnode.checkProps) { @@ -2505,8 +2500,8 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { } function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { - var lastChildren = parentNode.vchildren; // lastVnode.vchildren; - var nextChildren = flattenChildren(nextVnode), + var lastChildren = parentNode.vchildren, + nextChildren = flattenChildren(nextVnode), nextLength = nextChildren.length, lastLength = lastChildren.length, dom = void 0; diff --git a/dist/ReactIE.js b/dist/ReactIE.js index d0fe6af8b..feb8a5a4f 100644 --- a/dist/ReactIE.js +++ b/dist/ReactIE.js @@ -2480,15 +2480,10 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { }); list.length = 0; } else { - var vchildren = flattenChildren(nextVnode); if (lastProps[innerHTML]) { - while (dom.firstChild) { - dom.removeChild(dom.firstChild); - } - mountChildren(dom, vchildren, nextVnode, context, updateQueue); - } else { - diffChildren(lastVnode, nextVnode, dom, context, updateQueue, vparent); + dom.vchildren = []; } + diffChildren(lastVnode, nextVnode, dom, context, updateQueue); } if (lastVnode.checkProps || nextVnode.checkProps) { @@ -2504,8 +2499,8 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { } function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { - var lastChildren = parentNode.vchildren; // lastVnode.vchildren; - var nextChildren = flattenChildren(nextVnode), + var lastChildren = parentNode.vchildren, + nextChildren = flattenChildren(nextVnode), nextLength = nextChildren.length, lastLength = lastChildren.length, dom = void 0; diff --git a/dist/ReactSelection.js b/dist/ReactSelection.js index d0fe6af8b..feb8a5a4f 100644 --- a/dist/ReactSelection.js +++ b/dist/ReactSelection.js @@ -2480,15 +2480,10 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { }); list.length = 0; } else { - var vchildren = flattenChildren(nextVnode); if (lastProps[innerHTML]) { - while (dom.firstChild) { - dom.removeChild(dom.firstChild); - } - mountChildren(dom, vchildren, nextVnode, context, updateQueue); - } else { - diffChildren(lastVnode, nextVnode, dom, context, updateQueue, vparent); + dom.vchildren = []; } + diffChildren(lastVnode, nextVnode, dom, context, updateQueue); } if (lastVnode.checkProps || nextVnode.checkProps) { @@ -2504,8 +2499,8 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { } function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { - var lastChildren = parentNode.vchildren; // lastVnode.vchildren; - var nextChildren = flattenChildren(nextVnode), + var lastChildren = parentNode.vchildren, + nextChildren = flattenChildren(nextVnode), nextLength = nextChildren.length, lastLength = lastChildren.length, dom = void 0; diff --git a/dist/ReactShim.js b/dist/ReactShim.js index c3fb6e3b6..5fa12027c 100644 --- a/dist/ReactShim.js +++ b/dist/ReactShim.js @@ -2322,15 +2322,10 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { }); list.length = 0; } else { - var vchildren = flattenChildren(nextVnode); if (lastProps[innerHTML]) { - while (dom.firstChild) { - dom.removeChild(dom.firstChild); - } - mountChildren(dom, vchildren, nextVnode, context, updateQueue); - } else { - diffChildren(lastVnode, nextVnode, dom, context, updateQueue, vparent); + dom.vchildren = []; } + diffChildren(lastVnode, nextVnode, dom, context, updateQueue); } if (lastVnode.checkProps || nextVnode.checkProps) { @@ -2346,8 +2341,8 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { } function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { - var lastChildren = parentNode.vchildren; // lastVnode.vchildren; - var nextChildren = flattenChildren(nextVnode), + var lastChildren = parentNode.vchildren, + nextChildren = flattenChildren(nextVnode), nextLength = nextChildren.length, lastLength = lastChildren.length, dom = void 0; diff --git a/src/diff.js b/src/diff.js index a9fab55c8..a8be78b83 100644 --- a/src/diff.js +++ b/src/diff.js @@ -394,15 +394,10 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { }); list.length = 0; } else { - var vchildren = flattenChildren(nextVnode); if (lastProps[innerHTML]) { - while (dom.firstChild) { - dom.removeChild(dom.firstChild); - } - mountChildren(dom, vchildren, nextVnode, context, updateQueue); - } else { - diffChildren(lastVnode, nextVnode, dom, context, updateQueue, vparent); + dom.vchildren = []; } + diffChildren(lastVnode, nextVnode, dom, context, updateQueue); } if (lastVnode.checkProps || nextVnode.checkProps) { @@ -419,8 +414,8 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { - let lastChildren = parentNode.vchildren;// lastVnode.vchildren; - let nextChildren = flattenChildren(nextVnode), + let lastChildren = parentNode.vchildren, + nextChildren = flattenChildren(nextVnode), nextLength = nextChildren.length, lastLength = lastChildren.length, dom; From 9625705d970852020680607342b7e38b05970a54 Mon Sep 17 00:00:00 2001 From: RubyLouvre <1669866773@qq.com> Date: Sat, 7 Oct 2017 00:23:48 +0800 Subject: [PATCH 38/57] =?UTF-8?q?=E6=8A=BD=E8=B1=A1ref=E7=9A=84=E6=93=8D?= =?UTF-8?q?=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dist/React.js | 226 ++++++++++++++-------------- dist/ReactIE.js | 39 +++-- dist/ReactSelection.js | 39 +++-- dist/ReactShim.js | 39 +++-- lib/ReactTestUtils.js | 1 + src/React.js | 2 + src/createElement.js | 2 +- src/diff.js | 89 +++++------ test/modules/ref.spec.jsx | 301 +++++++++++++++++++++++--------------- 9 files changed, 431 insertions(+), 307 deletions(-) diff --git a/dist/React.js b/dist/React.js index b52da4270..282886eae 100644 --- a/dist/React.js +++ b/dist/React.js @@ -1265,6 +1265,99 @@ fn$1.shouldComponentUpdate = function shallowCompare(nextProps, nextState) { }; fn$1.isPureComponent = true; +var pendingRefs = []; +function clearRefs() { + var refs = pendingRefs.slice(0); + pendingRefs.length = 0; + refs.forEach(function (fn) { + fn(); + }); +} +function callUpdate(updater, instance) { + if (pendingRefs.length) { + clearRefs(); + } + if (updater._lifeStage === 2) { + if (instance.componentDidUpdate) { + updater._didUpdate = true; + instance.componentDidUpdate(updater.lastProps, updater.lastState, updater.lastContext); + if (!updater._renderInNextCycle) { + updater._didUpdate = false; + } + } + options.afterUpdate(instance); + updater._hydrating = false; + updater._lifeStage = 1; + } +} + +function drainQueue(queue) { + options.beforePatch(); + //先执行所有refs方法(从上到下) + clearRefs(); + //再执行所有mount/update钩子(从下到上) + var i = 0; + while (i < queue.length) { + //queue可能中途加入新元素, 因此不能直接使用queue.forEach(fn) + var updater = queue[i], + instance = updater._instance; + i++; + if (!updater._lifeStage) { + if (pendingRefs.length) { + clearRefs(); + } + if (instance.componentDidMount) { + instance.componentDidMount(); + instance.componentDidMount = null; + } + updater._lifeStage = 1; + options.afterMount(instance); + updater._hydrating = false; + } else { + callUpdate(updater, instance); + } + var ref = updater.vnode.ref; + if (ref) { + ref(instance.__isStateless ? null : instance); + } + // updater._hydrating = false; //子树已经构建完毕 + while (updater._renderInNextCycle) { + + options.refreshComponent(updater, queue); + callUpdate(updater, instance); + } + } + //再执行所有setState/forceUpdate回调,根据从下到上的顺序执行 + queue.sort(mountSorter).forEach(function (updater) { + clearArray(updater._pendingCallbacks).forEach(function (fn) { + fn.call(updater._instance); + }); + }); + queue.length = 0; + options.afterPatch(); +} + +//有一个列队, 先放进A组件与A组件回调 +var dirtyComponents = []; + +function mountSorter(u1, u2) { + //让子节点先于父节点执行 + return u2._mountIndex - u1._mountIndex; +} + +options.flushUpdaters = function (queue) { + if (!queue) { + queue = dirtyComponents; + } + drainQueue(queue); +}; + +options.enqueueUpdater = function (updater) { + if (dirtyComponents.indexOf(updater) == -1) { + dirtyComponents.push(updater); + } +}; + var rnumber = /^-?\d+(\.\d+)?$/; /** * 为元素样子设置样式 @@ -1987,99 +2080,6 @@ function getOptionSelected(option, selected) { dom.selected = selected; } -var pendingRefs = []; -function clearRefs() { - var refs = pendingRefs.slice(0); - pendingRefs.length = 0; - refs.forEach(function (fn) { - fn(); - }); -} -function callUpdate(updater, instance) { - if (pendingRefs.length) { - clearRefs(); - } - if (updater._lifeStage === 2) { - if (instance.componentDidUpdate) { - updater._didUpdate = true; - instance.componentDidUpdate(updater.lastProps, updater.lastState, updater.lastContext); - if (!updater._renderInNextCycle) { - updater._didUpdate = false; - } - } - options.afterUpdate(instance); - updater._hydrating = false; - updater._lifeStage = 1; - } -} - -function drainQueue(queue) { - options.beforePatch(); - //先执行所有refs方法(从上到下) - clearRefs(); - //再执行所有mount/update钩子(从下到上) - var i = 0; - while (i < queue.length) { - //queue可能中途加入新元素, 因此不能直接使用queue.forEach(fn) - var updater = queue[i], - instance = updater._instance; - i++; - if (!updater._lifeStage) { - if (pendingRefs.length) { - clearRefs(); - } - if (instance.componentDidMount) { - instance.componentDidMount(); - instance.componentDidMount = null; - } - updater._lifeStage = 1; - options.afterMount(instance); - updater._hydrating = false; - } else { - callUpdate(updater, instance); - } - var ref = updater.vnode.ref; - if (ref) { - ref(instance.__isStateless ? null : instance); - } - // updater._hydrating = false; //子树已经构建完毕 - while (updater._renderInNextCycle) { - - options.refreshComponent(updater, queue); - callUpdate(updater, instance); - } - } - //再执行所有setState/forceUpdate回调,根据从下到上的顺序执行 - queue.sort(mountSorter).forEach(function (updater) { - clearArray(updater._pendingCallbacks).forEach(function (fn) { - fn.call(updater._instance); - }); - }); - queue.length = 0; - options.afterPatch(); -} - -//有一个列队, 先放进A组件与A组件回调 -var dirtyComponents = []; - -function mountSorter(u1, u2) { - //让子节点先于父节点执行 - return u2._mountIndex - u1._mountIndex; -} - -options.flushUpdaters = function (queue) { - if (!queue) { - queue = dirtyComponents; - } - drainQueue(queue); -}; - -options.enqueueUpdater = function (updater) { - if (dirtyComponents.indexOf(updater) == -1) { - dirtyComponents.push(updater); - } -}; - //[Top API] React.isValidElement function isValidElement(vnode) { return vnode && vnode.vtype; @@ -2365,10 +2365,9 @@ function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQue } //用于refreshComponent if (ref && vtype === 2) { - ref(null); - if (nextVnode.ref) { - lastVnode.ref = nextVnode.ref; - } + var nextRef = nextVnode.ref; + detachRef(ref, nextRef); + lastVnode.ref = nextRef; } //updater上总是保持新的数据 updater.lastVnode = lastVnode; @@ -2465,14 +2464,19 @@ function alignVnode(lastVnode, nextVnode, vparent, context, updateQueue, parentU } function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { - var dom = lastVnode._hostNode; + var lastProps = lastVnode.props, + dom = lastVnode._hostNode, + ref = lastVnode.ref, + checkProps = lastVnode.checkProps; + if (dom === null) { console.log("此节点已经被移除", vparent); return null; } - var lastProps = lastVnode.props; - var nextProps = nextVnode.props; - var ref = nextVnode.ref; + + var nextProps = nextVnode.props, + nextRef = nextVnode.ref; + nextVnode._hostNode = dom; if (nextProps[innerHTML]) { var list = lastVnode.vchildren || []; @@ -2487,18 +2491,27 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { diffChildren(lastVnode, nextVnode, dom, context, updateQueue); } - if (lastVnode.checkProps || nextVnode.checkProps) { + if (checkProps || nextVnode.checkProps) { diffProps(nextProps, lastProps, nextVnode, lastVnode, dom); } if (nextVnode.type === "select") { postUpdateSelectedOptions(nextVnode); } - if (ref) { - pendingRefs.push(ref.bind(0, dom)); - } + detachRef(ref, nextRef, dom); + return dom; } - +function detachRef(ref, nextRef, dom) { + if (nextRef) { + var refsChanged = !ref.string && !nextRef.string ? ref !== nextRef : ref.string !== nextRef.string; + if (refsChanged) { + ref(null); + } + dom && nextRef(dom); + } else if (ref) { + ref(null); + } +} function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { var lastChildren = parentNode.vchildren, nextChildren = flattenChildren(nextVnode), @@ -2624,6 +2637,7 @@ var React = { options: options, PropTypes: PropTypes, Children: Children, //为了react-redux + pendingRefs: pendingRefs, Component: Component, eventSystem: eventSystem, findDOMNode: findDOMNode, diff --git a/dist/ReactIE.js b/dist/ReactIE.js index feb8a5a4f..ec83ed4d9 100644 --- a/dist/ReactIE.js +++ b/dist/ReactIE.js @@ -2364,10 +2364,9 @@ function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQue } //用于refreshComponent if (ref && vtype === 2) { - ref(null); - if (nextVnode.ref) { - lastVnode.ref = nextVnode.ref; - } + var nextRef = nextVnode.ref; + detachRef(ref, nextRef); + lastVnode.ref = nextRef; } //updater上总是保持新的数据 updater.lastVnode = lastVnode; @@ -2464,14 +2463,19 @@ function alignVnode(lastVnode, nextVnode, vparent, context, updateQueue, parentU } function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { - var dom = lastVnode._hostNode; + var lastProps = lastVnode.props, + dom = lastVnode._hostNode, + ref = lastVnode.ref, + checkProps = lastVnode.checkProps; + if (dom === null) { console.log("此节点已经被移除", vparent); return null; } - var lastProps = lastVnode.props; - var nextProps = nextVnode.props; - var ref = nextVnode.ref; + + var nextProps = nextVnode.props, + nextRef = nextVnode.ref; + nextVnode._hostNode = dom; if (nextProps[innerHTML]) { var list = lastVnode.vchildren || []; @@ -2486,18 +2490,27 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { diffChildren(lastVnode, nextVnode, dom, context, updateQueue); } - if (lastVnode.checkProps || nextVnode.checkProps) { + if (checkProps || nextVnode.checkProps) { diffProps(nextProps, lastProps, nextVnode, lastVnode, dom); } if (nextVnode.type === "select") { postUpdateSelectedOptions(nextVnode); } - if (ref) { - pendingRefs.push(ref.bind(0, dom)); - } + detachRef(ref, nextRef, dom); + return dom; } - +function detachRef(ref, nextRef, dom) { + if (nextRef) { + var refsChanged = !ref.string && !nextRef.string ? ref !== nextRef : ref.string !== nextRef.string; + if (refsChanged) { + ref(null); + } + dom && nextRef(dom); + } else if (ref) { + ref(null); + } +} function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { var lastChildren = parentNode.vchildren, nextChildren = flattenChildren(nextVnode), diff --git a/dist/ReactSelection.js b/dist/ReactSelection.js index feb8a5a4f..ec83ed4d9 100644 --- a/dist/ReactSelection.js +++ b/dist/ReactSelection.js @@ -2364,10 +2364,9 @@ function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQue } //用于refreshComponent if (ref && vtype === 2) { - ref(null); - if (nextVnode.ref) { - lastVnode.ref = nextVnode.ref; - } + var nextRef = nextVnode.ref; + detachRef(ref, nextRef); + lastVnode.ref = nextRef; } //updater上总是保持新的数据 updater.lastVnode = lastVnode; @@ -2464,14 +2463,19 @@ function alignVnode(lastVnode, nextVnode, vparent, context, updateQueue, parentU } function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { - var dom = lastVnode._hostNode; + var lastProps = lastVnode.props, + dom = lastVnode._hostNode, + ref = lastVnode.ref, + checkProps = lastVnode.checkProps; + if (dom === null) { console.log("此节点已经被移除", vparent); return null; } - var lastProps = lastVnode.props; - var nextProps = nextVnode.props; - var ref = nextVnode.ref; + + var nextProps = nextVnode.props, + nextRef = nextVnode.ref; + nextVnode._hostNode = dom; if (nextProps[innerHTML]) { var list = lastVnode.vchildren || []; @@ -2486,18 +2490,27 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { diffChildren(lastVnode, nextVnode, dom, context, updateQueue); } - if (lastVnode.checkProps || nextVnode.checkProps) { + if (checkProps || nextVnode.checkProps) { diffProps(nextProps, lastProps, nextVnode, lastVnode, dom); } if (nextVnode.type === "select") { postUpdateSelectedOptions(nextVnode); } - if (ref) { - pendingRefs.push(ref.bind(0, dom)); - } + detachRef(ref, nextRef, dom); + return dom; } - +function detachRef(ref, nextRef, dom) { + if (nextRef) { + var refsChanged = !ref.string && !nextRef.string ? ref !== nextRef : ref.string !== nextRef.string; + if (refsChanged) { + ref(null); + } + dom && nextRef(dom); + } else if (ref) { + ref(null); + } +} function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { var lastChildren = parentNode.vchildren, nextChildren = flattenChildren(nextVnode), diff --git a/dist/ReactShim.js b/dist/ReactShim.js index 5fa12027c..5060c0df3 100644 --- a/dist/ReactShim.js +++ b/dist/ReactShim.js @@ -2206,10 +2206,9 @@ function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQue } //用于refreshComponent if (ref && vtype === 2) { - ref(null); - if (nextVnode.ref) { - lastVnode.ref = nextVnode.ref; - } + var nextRef = nextVnode.ref; + detachRef(ref, nextRef); + lastVnode.ref = nextRef; } //updater上总是保持新的数据 updater.lastVnode = lastVnode; @@ -2306,14 +2305,19 @@ function alignVnode(lastVnode, nextVnode, vparent, context, updateQueue, parentU } function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { - var dom = lastVnode._hostNode; + var lastProps = lastVnode.props, + dom = lastVnode._hostNode, + ref = lastVnode.ref, + checkProps = lastVnode.checkProps; + if (dom === null) { console.log("此节点已经被移除", vparent); return null; } - var lastProps = lastVnode.props; - var nextProps = nextVnode.props; - var ref = nextVnode.ref; + + var nextProps = nextVnode.props, + nextRef = nextVnode.ref; + nextVnode._hostNode = dom; if (nextProps[innerHTML]) { var list = lastVnode.vchildren || []; @@ -2328,18 +2332,27 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { diffChildren(lastVnode, nextVnode, dom, context, updateQueue); } - if (lastVnode.checkProps || nextVnode.checkProps) { + if (checkProps || nextVnode.checkProps) { diffProps(nextProps, lastProps, nextVnode, lastVnode, dom); } if (nextVnode.type === "select") { postUpdateSelectedOptions(nextVnode); } - if (ref) { - pendingRefs.push(ref.bind(0, dom)); - } + detachRef(ref, nextRef, dom); + return dom; } - +function detachRef(ref, nextRef, dom) { + if (nextRef) { + var refsChanged = !ref.string && !nextRef.string ? ref !== nextRef : ref.string !== nextRef.string; + if (refsChanged) { + ref(null); + } + dom && nextRef(dom); + } else if (ref) { + ref(null); + } +} function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { var lastChildren = parentNode.vchildren, nextChildren = flattenChildren(nextVnode), diff --git a/lib/ReactTestUtils.js b/lib/ReactTestUtils.js index 557c42249..12f866bca 100644 --- a/lib/ReactTestUtils.js +++ b/lib/ReactTestUtils.js @@ -263,6 +263,7 @@ Simulate: {}, SimulateNative: {} }; + // ReactTestUtils.Simulate.click(element, options) "click,change,keyDown,keyUp,KeyPress,mouseDown,mouseUp,mouseMove".replace(/\w+/g, function(name){ ReactTestUtils.Simulate[name] = function(node, opts){ if(!node || node.nodeType !==1){ diff --git a/src/React.js b/src/React.js index d62742c4f..0d2c1d78b 100644 --- a/src/React.js +++ b/src/React.js @@ -8,6 +8,7 @@ import { createClass } from "./createClass"; import { cloneElement } from "./cloneElement"; import { PureComponent } from "./PureComponent"; import { createElement } from "./createElement"; +import { pendingRefs } from "./scheduler"; import { render,findDOMNode, isValidElement, unmountComponentAtNode, unstable_renderSubtreeIntoContainer } from "./diff"; @@ -17,6 +18,7 @@ var React = { options, PropTypes, Children, //为了react-redux + pendingRefs, Component, eventSystem, findDOMNode, diff --git a/src/createElement.js b/src/createElement.js index b56b42b1f..73c7766fa 100644 --- a/src/createElement.js +++ b/src/createElement.js @@ -79,7 +79,7 @@ function createStringRef(owner, ref) { if (dom) { if (dom.nodeType) { dom.getDOMNode = getDOMNode; - } + } owner.refs[ref] = dom; }else{ delete owner.refs[ref]; diff --git a/src/diff.js b/src/diff.js index a8be78b83..13e192ef8 100644 --- a/src/diff.js +++ b/src/diff.js @@ -38,8 +38,8 @@ export function findDOMNode(ref) { if (ref.nodeType === 1) { return ref; } - - return ref.updater ? ref.updater._hostNode : ref._hostNode || null; + + return ref.updater ? ref.updater._hostNode : ref._hostNode || null; } // 用于辅助XML元素的生成(svg, math), // 它们需要根据父节点的tagName与namespaceURI,知道自己是存在什么文档中 @@ -62,7 +62,7 @@ function renderByAnu(vnode, container, callback, context = {}) { rootNode, lastVnode = container.__component; if (lastVnode) { - rootNode = alignVnode(lastVnode, vnode, getVParent(container), context, updateQueue); + rootNode = alignVnode(lastVnode, vnode, getVParent(container), context, updateQueue); } else { updateQueue.isMainProcess = true; //如果是后端渲染生成,它的孩子中存在一个拥有data-reactroot属性的元素节点 @@ -186,7 +186,7 @@ function mountElement(lastNode, vnode, vparent, context, updateQueue) { let dom = genMountElement(lastNode, vnode, vparent, type); vnode._hostNode = dom; - + var children = flattenChildren(vnode); let method = lastNode ? alignChildren : mountChildren; method(dom, children, vnode, context, updateQueue); @@ -261,7 +261,6 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue, pa updateQueue, updater //作为parentUpater往下传 ); - }, updater.rendered); updateQueue.push(updater); @@ -271,7 +270,8 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue, pa function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQueue) { let { type, ref, _instance: instance, vtype } = lastVnode; - let nextContext,queue, + let nextContext, + queue, nextProps = nextVnode.props, updater = instance.updater; if (type.contextTypes) { @@ -287,10 +287,9 @@ function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQue } //用于refreshComponent if (ref && vtype === 2) { - ref(null); - if (nextVnode.ref) { - lastVnode.ref = nextVnode.ref; - } + var nextRef = nextVnode.ref; + detachRef(ref, nextRef); + lastVnode.ref = nextRef; } //updater上总是保持新的数据 updater.lastVnode = lastVnode; @@ -303,19 +302,18 @@ function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQue if (updateQueue.isMainProcess) { queue = updateQueue; queue = []; - }else{ + } else { queue = updateQueue; } refreshComponent(updater, queue); //子组件先执行 updateQueue.push(updater); - if(!updater._hostNode){ - console.log("出问题了",updater, lastVnode); + if (!updater._hostNode) { + console.log("出问题了", updater, lastVnode); } return updater._hostNode; } - function refreshComponent(updater, updateQueue) { let { _instance: instance, _hostNode: dom, context: nextContext, props: nextProps, vnode, lastVnode } = updater; @@ -348,14 +346,13 @@ function refreshComponent(updater, updateQueue) { dom = updater.renderComponent(function(nextRendered, vparent, childContext) { return alignVnode(lastRendered, nextRendered, vparent, childContext, updateQueue, updater); }); - - + updater.lastVnode = vnode; updater._lifeStage = 2; // updater._hydrating = false; if (!updateQueue.isMainProcess) { drainQueue(updateQueue); - }else{ + } else { updater._hydrating = false; } @@ -369,7 +366,9 @@ export function alignVnode(lastVnode, nextVnode, vparent, context, updateQueue, dom = updateVnode(lastVnode, nextVnode, vparent, context, updateQueue); } else { disposeVnode(lastVnode); - var node = lastVnode._hostNode, parent = node.parentNode, next = node.nextSibling; + var node = lastVnode._hostNode, + parent = node.parentNode, + next = node.nextSibling; removeDOMElement(node); dom = mountVnode(null, nextVnode, vparent, context, updateQueue, parentUpdater); parent.insertBefore(dom, next); @@ -378,14 +377,13 @@ export function alignVnode(lastVnode, nextVnode, vparent, context, updateQueue, } function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { - let dom = lastVnode._hostNode; - if(dom === null){ - console.log("此节点已经被移除",vparent); + let { props: lastProps, _hostNode: dom, ref, checkProps } = lastVnode; + if (dom === null) { + console.log("此节点已经被移除", vparent); return null; } - let lastProps = lastVnode.props; - let nextProps = nextVnode.props; - let ref = nextVnode.ref; + + let { props: nextProps, ref: nextRef } = nextVnode; nextVnode._hostNode = dom; if (nextProps[innerHTML]) { var list = lastVnode.vchildren || []; @@ -400,29 +398,37 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { diffChildren(lastVnode, nextVnode, dom, context, updateQueue); } - if (lastVnode.checkProps || nextVnode.checkProps) { + if (checkProps || nextVnode.checkProps) { diffProps(nextProps, lastProps, nextVnode, lastVnode, dom); } if (nextVnode.type === "select") { postUpdateSelectedOptions(nextVnode); } - if (ref) { - pendingRefs.push(ref.bind(0, dom)); - } + detachRef(ref, nextRef, dom); + return dom; } - - +function detachRef(ref, nextRef, dom){ + if (nextRef) { + var refsChanged = !ref.string && !nextRef.string ? ref !== nextRef:ref.string !== nextRef.string; + if(refsChanged){ + ref(null); + } + dom && nextRef(dom); + } else if(ref){ + ref(null); + } +} function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { - let lastChildren = parentNode.vchildren, + let lastChildren = parentNode.vchildren, nextChildren = flattenChildren(nextVnode), nextLength = nextChildren.length, - lastLength = lastChildren.length, dom; + lastLength = lastChildren.length, + dom; - //如果旧数组长度为零, 直接添加 if (nextLength && !lastLength) { - emptyElement(parentNode); + emptyElement(parentNode); return mountChildren(parentNode, nextChildren, lastVnode, context, updateQueue); } if (nextLength === lastLength && lastLength === 1) { @@ -448,7 +454,7 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { lastChild = lastChildren[i]; if (nextChild && lastChild && isSameNode(lastChild, nextChild)) { // 如果能直接找到,命名90%的情况 - actions[i] = [lastChild,nextChild]; + actions[i] = [lastChild, nextChild]; removeHits[i] = true; } else { if (nextChild) { @@ -456,7 +462,7 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { if (fuzzyHits[hit] && fuzzyHits[hit].length) { var oldChild = fuzzyHits[hit].shift(); // 如果命中旧的节点,将旧的节点移动新节点的位置,向后移动 - actions[i] = [ oldChild, nextChild, "moveAfter" ]; + actions[i] = [oldChild, nextChild, "moveAfter"]; removeHits[oldChild._i] = true; } } @@ -482,10 +488,10 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { hit = nextChild.type + (nextChild.key || ""); if (fuzzyHits[hit] && fuzzyHits[hit].length) { lastChild = fuzzyHits[hit].shift(); - action = [lastChild,nextChild, "moveAfter"]; - } + action = [lastChild, nextChild, "moveAfter"]; + } } - if(action){ + if (action) { lastChild = action[0]; nextChild = action[1]; dom = lastChild._hostNode; @@ -493,11 +499,11 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { parentNode.insertBefore(dom, insertPoint); } insertPoint = updateVnode(lastChild, nextChild, lastVnode, context, updateQueue); - if(!nextChild._hostNode){ + if (!nextChild._hostNode) { nextChildren[j] = lastChild; } removeHits[lastChild._i] = true; - } else{ + } else { //为了兼容 react stack reconciliation的执行顺序,添加下面三行, //在插入节点前,将原位置上节点对应的组件先移除 var removed = lastChildren[j]; @@ -524,7 +530,6 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { disposeVnode(el); } }); - } function isSameNode(a, b) { diff --git a/test/modules/ref.spec.jsx b/test/modules/ref.spec.jsx index f8fd15a2d..504d15bbe 100644 --- a/test/modules/ref.spec.jsx +++ b/test/modules/ref.spec.jsx @@ -1,31 +1,28 @@ -import { beforeHook, afterHook, browser } from 'karma-event-driver-ext/cjs/event-driver-hooks'; -import React from 'dist/React' +import React from "dist/React"; +import ReactTestUtils from "lib/ReactTestUtils"; +//https://github.com/facebook/react/blob/master/src/isomorphic/children/__tests__/ReactChildren-test.js +var ReactDOM = window.ReactDOM || React; -describe('ref', function () { +describe("ref", function () { this.timeout(200000); - before(async () => { - await beforeHook() - }) - after(async () => { - await afterHook(false) - }) + var body = document.body, - div + div; beforeEach(function () { - div = document.createElement('div') - body.appendChild(div) - }) + div = document.createElement("div"); + body.appendChild(div); + }); afterEach(function () { - body.removeChild(div) - }) - it('patchRef', async () => { + body.removeChild(div); + }); + it("patchRef", function() { class App extends React.Component { constructor(props) { - super(props) + super(props); this.handleClick = this .handleClick - .bind(this) + .bind(this); } handleClick() { @@ -48,74 +45,66 @@ describe('ref', function () {
); } - }; - - var s = React.render(, div) - await browser - .pause(100) - .$apply() - var dom = s.refs.a + } + + var s = React.render(, div); + + var dom = s.refs.a; + ReactTestUtils.Simulate.click(dom); - await browser - .click(dom) - .pause(100) - .$apply() - expect(document.activeElement).toBe(s.myTextInput) - expect(s.myTextInput).toBeDefined() + expect(document.activeElement).toBe(s.myTextInput); + expect(s.myTextInput).toBeDefined(); - }) + }); - it('patchRef Component', async () => { + it("patchRef Component", function() { class App extends React.Component { render() { - return
+ return
; } } - var index = 1 + var index = 1; class A extends React.Component { componentWillReceiveProps() { - index = 0 - this.forceUpdate() + index = 0; + this.forceUpdate(); } render() { return index ? 111 - : 111 + : 111; } } - var s = React.render(, div) - await browser - .pause(100) - .$apply() - expect(s.refs.a).toInstanceOf(A) + var s = ReactTestUtils.renderIntoDocument(); + + expect(s.refs.a).toInstanceOf(A); - }) - - it('没有组件的情况', async () => { + }); + it("没有组件的情况", function() { + var index = 0; function ref(a) { - expect(a.tagName).toBe('DIV') + index ++; + expect(a.tagName).toBe("DIV"); } - var s = React.render(
, div) - await browser - .pause(100) - .$apply() + var s = ReactTestUtils.renderIntoDocument(
); + expect(index).toBe(1); - }) - it('should invoke refs in Component.render()', async () => { - var i = 0 + }); + it("should invoke refs in Component.render()", function() { + var i = 0; let outer = function (a) { expect(a).toBe(div.firstChild); - i++ - } + i++; + }; let inner = function (a) { expect(a).toBe(div.firstChild.firstChild); - i++ - } + i++; + }; class Foo extends React.Component { render() { return ( @@ -125,20 +114,18 @@ describe('ref', function () { ); } } - var s = React.render(, div); - await browser - .pause(100) - .$apply() + ReactDOM.render(, div); + - expect(i).toBe(2) + expect(i).toBe(2); }); - it('rener方法在存在其他组件,那么组件以innerHTML方式引用子节点,子节点有ref', async () => { + it("rener方法在存在其他组件,那么组件以innerHTML方式引用子节点,子节点有ref", function() { class App extends React.Component { constructor(props) { super(props); this.state = { - tip: 'g-up-tips', - text: 'xxxx' + tip: "g-up-tips", + text: "xxxx" }; } @@ -148,7 +135,7 @@ describe('ref', function () { child -
+
; } } class Child extends React.Component { @@ -158,31 +145,29 @@ describe('ref', function () { } render() { - return
{this.props.children}
+ return
{this.props.children}
; } } - var s = React.render(, div); - await browser - .pause(100) - .$apply() - expect(Object.keys(s.refs).sort()).toEqual(['child', 'inner', 'parent']) + var s = ReactTestUtils.renderIntoDocument(); + + expect(Object.keys(s.refs).sort()).toEqual(["child", "inner", "parent"]); - }) - it('用户在构造器里生成虚拟DOM', async () => { - var a + }); + it("用户在构造器里生成虚拟DOM", function() { + var a; class App extends React.Component { constructor(props) { super(props); - this.sliderLeftJSX = this.renderSlider('btnLeft'); + this.sliderLeftJSX = this.renderSlider("btnLeft"); this.state = {}; } - renderSlider(which = 'btnLeft') { + renderSlider(which = "btnLeft") { return ( { this[which] = dom; }} />); } componentDidMount() { - a = !!this.btnLeft + a = !!this.btnLeft; } render() { @@ -195,45 +180,40 @@ describe('ref', function () { ); } } - var s = React.render(, div); - await browser - .pause(100) - .$apply() + var s = ReactTestUtils.renderIntoDocument(); + - expect(a).toBe(true) + expect(a).toBe(true); - }) + }); - it('Stateless组件也会被执行', async () => { - var b + it("Stateless组件也会被执行", function() { + var b; function App() { return (
StateLess
); } - var s = React.render( { - b = a - }} />, div); - await browser - .pause(100) - .$apply() + function refFn(a){ + b = a; + } + ReactTestUtils.renderIntoDocument(); + + expect(b).toBe(null); - expect(b).toBe(null) + }); + it("ReactDOM.render中的元素也会被执行", function() { + var b; + function refFn(a){ + b = a; + } + ReactTestUtils.renderIntoDocument(

); - }) - it('ReactDOM.render中的元素也会被执行', async () => { - var b - var s = React.render(

{ - b = a - }} />, div); - await browser - .pause(100) - .$apply() - expect(b && b.tagName).toBe('H1') + expect(b && b.tagName).toBe("H1"); - }) - it('带ref的组件被子组件cloneElement', async () => { + }); + it("带ref的组件被子组件cloneElement", function() { class Select extends React.Component { constructor(props) { super(props); @@ -243,12 +223,12 @@ describe('ref', function () { } render() { return React.createElement(SelectTrigger, { - ref: 'trigger' + ref: "trigger" }, React.createElement( - 'div', + "div", { - ref: 'root' - }, "xxxx")) + ref: "root" + }, "xxxx")); } } class SelectTrigger extends React.Component { @@ -259,7 +239,7 @@ describe('ref', function () { }; } render() { - return React.createElement(Trigger, Object.assign({ title: 'xxx' }, this.props), this.props.children) + return React.createElement(Trigger, Object.assign({ title: "xxx" }, this.props), this.props.children); } } class Trigger extends React.Component { @@ -273,14 +253,97 @@ describe('ref', function () { var props = this.props; var children = props.children; var child = React.Children.only(children); - return React.cloneElement(child, { className: '5555' }) + return React.cloneElement(child, { className: "5555" }); } } var s = React.render(, div); + await browser + .click(el) + .pause(100) + .$apply(); - }) - it('模拟mouseenter,mouseleave', async() => { - var aaa = '' + expect(logIndex).toBe(1); + }); + it("模拟mouseenter,mouseleave", async () => { + var aaa = ""; class App extends React.Component { constructor(props) { - super(props) + super(props); this.state = { aaa: { a: 7 } - } + }; } mouseover() { - aaa += 'aaa ' - + aaa += "aaa "; } mouseout(e) { - aaa += 'bbb ' - + aaa += "bbb "; } render() { - return
-
-
-
+ return ( +
+
+
+
+ ); } } - var s = ReactDOM.render( - , div) + var s = ReactDOM.render(, div); await browser .pause(100) - .moveToObject('#mouse3') + .moveToObject("#mouse3") .pause(100) - .moveToObject('#mouse4') - .$apply() + .moveToObject("#mouse4") + .$apply(); - expect(aaa.trim()).toBe('aaa bbb') - - }) - it('捕获', async() => { - var aaa = '' + expect(aaa.trim()).toBe("aaa bbb"); + }); + it("捕获", async () => { + var aaa = ""; class App extends React.PureComponent { constructor(props) { - super(props) + super(props); this.state = { aaa: { a: 7 } - } + }; } click() { - aaa += 'aaa ' - + aaa += "aaa "; } click2(e) { - aaa += 'bbb ' - e.preventDefault() - e.stopPropagation() + aaa += "bbb "; + e.preventDefault(); + e.stopPropagation(); } click3(e) { - aaa += 'ccc ' + aaa += "ccc "; } render() { - return
-

=========

-
-

=====

-
{this.state.aaa.a}
+ return ( +
+

=========

+
+

=====

+
+ {this.state.aaa.a} +
+
-
+ ); } } - var s = ReactDOM.render( - , div) + var s = ReactDOM.render(, div); await browser .pause(100) - .click('#capture') + .click("#capture") .pause(100) - .$apply() + .$apply(); - expect(aaa.trim()).toBe('aaa bbb') - - }) - it('让focus能冒泡', async() => { - var aaa = '' + expect(aaa.trim()).toBe("aaa bbb"); + }); + it("让focus能冒泡", async () => { + var aaa = ""; class App extends React.Component { constructor(props) { - super(props) + super(props); this.state = { aaa: { a: 7 } - } + }; } onFocus1() { - aaa += 'aaa ' - + aaa += "aaa "; } onFocus2(e) { - aaa += 'bbb ' - + aaa += "bbb "; } render() { - return
+ return (
222 + width: 200, + height: 200 + }} + > +
+ 222 +
-
- + ); } } - var s = ReactDOM.render( - , div) + var s = ReactDOM.render(, div); await browser .pause(100) - .click('#focus2') + .click("#focus2") .pause(100) - .$apply() + .$apply(); - expect(aaa.trim()).toBe('aaa bbb') - - }) - it('测试事件对象的属性', function () { + expect(aaa.trim()).toBe("aaa bbb"); + }); + it("测试事件对象的属性", function() { var obj = { - type: 'change', + type: "change", srcElement: 1 - } - var e = new SyntheticEvent(obj) - expect(e.type).toBe('change') - expect(e.timeStamp).toA('number') - expect(e.target).toBe(1) - expect(e.nativeEvent).toBe(obj) - e.stopImmediatePropagation() - expect(e._stopPropagation).toBe(true) - expect(e.toString()).toBe('[object Event]') - var e2 = new SyntheticEvent(e) - expect(e2).toBe(e) - - var p = new DOMElement - p.addEventListener = false - addEvent(p, 'type', 'xxx') - }) - - it('合并点击事件中的setState', async() => { - - var list = [] + }; + var e = new SyntheticEvent(obj); + expect(e.type).toBe("change"); + expect(e.timeStamp).toA("number"); + expect(e.target).toBe(1); + expect(e.nativeEvent).toBe(obj); + e.stopImmediatePropagation(); + expect(e._stopPropagation).toBe(true); + expect(e.toString()).toBe("[object Event]"); + var e2 = new SyntheticEvent(e); + expect(e2).toBe(e); + + var p = new DOMElement(); + p.addEventListener = false; + addEvent(p, "type", "xxx"); + }); + + it("合并点击事件中的setState", async () => { + var list = []; class App extends React.Component { constructor(props) { super(props); @@ -351,58 +358,51 @@ describe('事件系统模块', function () { } render() { - list.push('render ' + this.state.path) - return
- {this.state.path} -
; + list.push("render " + this.state.path); + return ( +
+ + {this.state.path} + +
+ ); } onClick() { - this - .setState({ - path: 'click' - }, function () { - list.push('click....') - }) - this.setState({ - path: 'click2' - }, function () { - list.push('click2....') - }) + this.setState( + { + path: "click" + }, + function() { + list.push("click...."); + } + ); + this.setState( + { + path: "click2" + }, + function() { + list.push("click2...."); + } + ); } componentWillUpdate() { - list.push('will update') + list.push("will update"); } componentDidUpdate() { - list.push('did update') + list.push("did update"); } } - ReactDOM - .render( - , div, function () { - list.push('ReactDOM cb') - }) + ReactDOM.render(, div, function() { + list.push("ReactDOM cb"); + }); await browser .pause(100) - .click('#click2time') + .click("#click2time") .pause(100) - .$apply() + .$apply(); - expect(list).toEqual([ - "render 111", - "ReactDOM cb", - "will update", - "render click2", - "did update", - "click....", - "click2...." - ]) - - }) - -}) \ No newline at end of file + expect(list).toEqual(["render 111", "ReactDOM cb", "will update", "render click2", "did update", "click....", "click2...."]); + }); +}); diff --git a/test/modules/ref.spec.jsx b/test/modules/ref.spec.jsx index 504d15bbe..a606b0dbc 100644 --- a/test/modules/ref.spec.jsx +++ b/test/modules/ref.spec.jsx @@ -296,7 +296,22 @@ describe("ref", function () { expect(s.refs.aaa).toBe(void 666); expect(typeof a).toBe("object"); }); - + it("为元素添加ref", function() { + + class Foo extends React.Component { + render() { + return ( +
+ {this.props.a ? : } +
+ ); + } + } + var s = ReactDOM.render(, div); + expect(s.refs).toEqual({}); + ReactDOM.render(, div); + expect(typeof s.refs.aaa).toBe("object"); + }); it("相同位置上的元素节点的ref函数不一样", function() { var log = []; class Foo extends React.Component { diff --git a/version.md b/version.md index cf83de296..ad6a4eec7 100644 --- a/version.md +++ b/version.md @@ -1,3 +1,11 @@ +## 1.1.3 +1. 抽象出一个Update类,用于封装组件实例上的所有私有数据 +2. 抽象出一个instantiateComponente用于同时实例化有状态与无状态组件,从此再没有mountStateless, updateStateless方法 +3. 使用detachRef安全清空无用的ref数据 +4. 修正checkbox点一下会触发两次onChange的BUG +5. 添加ReceiveComponent检测机制,如果context,props一样,那么就不会执行receive, render, update等钩子 +6. 修改检测空对象的逻辑 + ## 1.1.2 1. 修正 onChange 事件 2. 重构 diffProps 模块的实现 From 94e1b92c63ed2991b2d032c1ff0a4ca12160588f Mon Sep 17 00:00:00 2001 From: RubyLouvre <1669866773@qq.com> Date: Sun, 8 Oct 2017 16:47:24 +0800 Subject: [PATCH 40/57] =?UTF-8?q?=E6=8A=BD=E8=B1=A1=E5=87=BARefs=E6=A8=A1?= =?UTF-8?q?=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dist/React.js | 338 ++--- dist/ReactIE.js | 224 +-- dist/ReactSelection.js | 224 +-- dist/ReactShim.js | 224 +-- index3.html | 77 +- index4.html | 171 ++- index5.html | 97 +- index6.html | 184 ++- index7.html | 67 +- src/React.js | 2 +- src/Refs.js | 61 + src/{instantiateComponent.js => Updater.js} | 44 +- src/cloneElement.js | 9 +- src/createElement.js | 30 +- src/diff.js | 64 +- src/dispose.js | 2 +- src/event.js | 2 - src/scheduler.js | 67 +- test/modules/ReactComponent-test.jsx | 824 ++++++----- ...eactCompositeComponentNestedState-test.jsx | 5 +- test/modules/lifecycle.spec.jsx | 1278 ++++++++--------- test/modules/ref.spec.jsx | 61 + test/spec.js | 1 + 23 files changed, 2126 insertions(+), 1930 deletions(-) create mode 100644 src/Refs.js rename src/{instantiateComponent.js => Updater.js} (81%) diff --git a/dist/React.js b/dist/React.js index 4b3a5d165..0d1ea8d52 100644 --- a/dist/React.js +++ b/dist/React.js @@ -1,5 +1,5 @@ /** - * by 司徒正美 Copyright 2017-10-07 + * by 司徒正美 Copyright 2017-10-08 * IE9+ */ @@ -200,6 +200,64 @@ var recyclables = { "#comment": [] }; +//fix 0.14对此方法的改动,之前refs里面保存的是虚拟DOM +function getDOMNode() { + return this; +} +function errRef() { + throw "ref位置错误"; +} +var pendingRefs = []; +var Refs = { + currentOwner: null, + clearRefs: function clearRefs() { + var refs = pendingRefs.splice(0, pendingRefs.length); + refs.forEach(function (fn) { + fn(); + }); + }, + detachRef: function detachRef(ref, nextRef, dom) { + ref = ref || getDOMNode; + nextRef = nextRef || getDOMNode; + if (ref === nextRef) { + return; + } + if (ref) { + if (ref.string && nextRef.string ? ref.string !== nextRef.string : ref !== getDOMNode) { + ref(null); + // pendingRefs.push(ref.bind(0,null)); + } + } + if (dom && nextRef !== getDOMNode) { + nextRef(dom); + // pendingRefs.push(nextRef.bind(0,dom)); + } + }, + createInstanceRef: function createInstanceRef(updater, ref) { + updater._ref = function () { + if (ref) { + var inst = updater._instance; + ref(inst.__isStateless ? null : inst); + } + updater._ref = getDOMNode; + }; + }, + createStringRef: function createStringRef(owner, ref) { + var stringRef = owner === null ? errRef : function (dom) { + if (dom) { + if (dom.nodeType) { + dom.getDOMNode = getDOMNode; + } + owner.refs[ref] = dom; + } else { + delete owner.refs[ref]; + } + }; + stringRef.string = ref; + return stringRef; + } +}; + var CurrentOwner = { cur: null }; @@ -267,32 +325,11 @@ function createElement(type, config) { return new Vnode(type, key, ref, props, vtype, checkProps); } -//fix 0.14对此方法的改动,之前refs里面保存的是虚拟DOM -function getDOMNode() { - return this; -} -function errRef() { - throw "ref位置错误"; -} -function createStringRef(owner, ref) { - var stringRef = owner === null ? errRef : function (dom) { - if (dom) { - if (dom.nodeType) { - dom.getDOMNode = getDOMNode; - } - owner.refs[ref] = dom; - } else { - delete owner.refs[ref]; - } - }; - stringRef.string = ref; - return stringRef; -} function Vnode(type, key, ref, props, vtype, checkProps) { this.type = type; this.props = props; this.vtype = vtype; - var owner = CurrentOwner.cur; + var owner = Refs.currentOwner; this._owner = owner; if (key) { @@ -305,10 +342,10 @@ function Vnode(type, key, ref, props, vtype, checkProps) { var refType = typeNumber(ref); if (refType === 4 || refType === 3) { //string, number - this.ref = createStringRef(owner, ref + ""); + this.ref = Refs.createStringRef(owner, ref + ""); } else if (refType === 5) { if (ref.string) { - var ref2 = createStringRef(owner, ref.string); + var ref2 = Refs.createStringRef(owner, ref.string); this.ref = function (dom) { ref(dom); ref2(dom); @@ -452,7 +489,7 @@ function cloneElement(vnode, props) { return Object.assign({}, vnode); } var owner = vnode._owner, - lastOwn = CurrentOwner.cur, + lastOwn = Refs.currentOwner, old = vnode.props, configs = {}; if (props) { @@ -467,7 +504,7 @@ function cloneElement(vnode, props) { } else { configs = old; } - CurrentOwner.cur = owner; + Refs.currentOwner = owner; var args = [].slice.call(arguments, 0), argsLength = args.length; @@ -477,7 +514,7 @@ function cloneElement(vnode, props) { args.push(configs.children); } var ret = createElement.apply(null, args); - CurrentOwner.cur = lastOwn; + Refs.currentOwner = lastOwn; return ret; } @@ -777,8 +814,6 @@ function addGlobalEvent(name) { function addEvent(el, type, fn, bool) { if (el.addEventListener) { - // Unable to preventDefault inside passive event listener due to target being - // treated as passive el.addEventListener(type, fn, bool || false); } else if (el.attachEvent) { el.attachEvent("on" + type, fn); @@ -1266,99 +1301,6 @@ fn$1.shouldComponentUpdate = function shallowCompare(nextProps, nextState) { }; fn$1.isPureComponent = true; -var pendingRefs = []; -function clearRefs() { - var refs = pendingRefs.slice(0); - pendingRefs.length = 0; - refs.forEach(function (fn) { - fn(); - }); -} -function callUpdate(updater, instance) { - if (pendingRefs.length) { - clearRefs(); - } - if (updater._lifeStage === 2) { - if (instance.componentDidUpdate) { - updater._didUpdate = true; - instance.componentDidUpdate(updater.lastProps, updater.lastState, updater.lastContext); - if (!updater._renderInNextCycle) { - updater._didUpdate = false; - } - } - options.afterUpdate(instance); - updater._hydrating = false; - updater._lifeStage = 1; - } -} - -function drainQueue(queue) { - options.beforePatch(); - //先执行所有refs方法(从上到下) - clearRefs(); - //再执行所有mount/update钩子(从下到上) - var i = 0; - while (i < queue.length) { - //queue可能中途加入新元素, 因此不能直接使用queue.forEach(fn) - var updater = queue[i], - instance = updater._instance; - i++; - if (!updater._lifeStage) { - if (pendingRefs.length) { - clearRefs(); - } - if (instance.componentDidMount) { - instance.componentDidMount(); - instance.componentDidMount = null; - } - updater._lifeStage = 1; - options.afterMount(instance); - updater._hydrating = false; - } else { - callUpdate(updater, instance); - } - var ref = updater.vnode.ref; - if (ref) { - ref(instance.__isStateless ? null : instance); - } - // updater._hydrating = false; //子树已经构建完毕 - while (updater._renderInNextCycle) { - - options.refreshComponent(updater, queue); - callUpdate(updater, instance); - } - } - //再执行所有setState/forceUpdate回调,根据从下到上的顺序执行 - queue.sort(mountSorter).forEach(function (updater) { - clearArray(updater._pendingCallbacks).forEach(function (fn) { - fn.call(updater._instance); - }); - }); - queue.length = 0; - options.afterPatch(); -} - -//有一个列队, 先放进A组件与A组件回调 -var dirtyComponents = []; - -function mountSorter(u1, u2) { - //让子节点先于父节点执行 - return u2._mountIndex - u1._mountIndex; -} - -options.flushUpdaters = function (queue) { - if (!queue) { - queue = dirtyComponents; - } - drainQueue(queue); -}; - -options.enqueueUpdater = function (updater) { - if (dirtyComponents.indexOf(updater) == -1) { - dirtyComponents.push(updater); - } -}; - var rnumber = /^-?\d+(\.\d+)?$/; /** * 为元素样子设置样式 @@ -1726,6 +1668,7 @@ function Updater(instance, vnode) { this._mountIndex = this._mountOrder; this._instance = instance; this._pendingCallbacks = []; + this._ref = noop; this._pendingStates = []; this._lifeStage = 0; //判断生命周期 //update总是保存最新的数据,如state, props, context, parentContext, vparent @@ -1737,6 +1680,7 @@ function Updater(instance, vnode) { this.mergeStates = alwaysNull; } } + Updater.prototype = { mergeStates: function mergeStates() { var instance = this._instance, @@ -1757,7 +1701,6 @@ Updater.prototype = { pendings.length = 0; return nextState; }, - renderComponent: function renderComponent(cb, rendered) { var vnode = this.vnode, parentContext = this.parentContext, @@ -1766,8 +1709,8 @@ Updater.prototype = { if (!rendered) { try { - var lastOwn = CurrentOwner.cur; - CurrentOwner.cur = instance; + var lastOwn = Refs.currentOwner; + Refs.currentOwner = instance; if (this.willReceive === false) { rendered = this.rendered; delete this.willReceive; @@ -1775,7 +1718,7 @@ Updater.prototype = { rendered = instance.render(); } } finally { - CurrentOwner.cur = lastOwn; + Refs.currentOwner = lastOwn; } } @@ -1820,12 +1763,12 @@ function instantiateComponent(type, vnode, props, context) { updater.displayName = type.displayName || type.name; if (isStateless) { - var lastOwn = CurrentOwner.cur; - CurrentOwner.cur = instance; + var lastOwn = Refs.currentOwner; + Refs.currentOwner = instance; try { var mixin = instance.render(); } finally { - CurrentOwner.cur = lastOwn; + Refs.currentOwner = lastOwn; } if (mixin && mixin.render) { //支持module pattern component @@ -2086,6 +2029,84 @@ function getOptionSelected(option, selected) { dom.selected = selected; } +function callUpdate(updater, instance) { + Refs.clearRefs(); + if (updater._lifeStage === 2) { + updater._didUpdate = true; + instance.componentDidUpdate(); + if (!updater._renderInNextCycle) { + updater._didUpdate = false; + } + updater._lifeStage = 1; + updater._hydrating = false; + options.afterUpdate(instance); + } + updater._ref(); +} + +function drainQueue(queue) { + options.beforePatch(); + //先执行所有refs方法(从上到下) + Refs.clearRefs(); + //再执行所有mount/update钩子(从下到上) + var i = 0; + while (i < queue.length) { + //queue可能中途加入新元素, 因此不能直接使用queue.forEach(fn) + var updater = queue[i], + instance = updater._instance; + i++; + if (!updater._lifeStage) { + Refs.clearRefs(); + instance.newStageAddedRefs = instance.newStageAddedRefs || {}; + if (instance.componentDidMount) { + instance.componentDidMount(); + instance.componentDidMount = null; + } + updater._lifeStage = 1; + updater._hydrating = false; + updater._ref(); + options.afterMount(instance); + } else { + callUpdate(updater, instance); + } + //如果组件在componentDidMount中调用setState + if (updater._renderInNextCycle) { + options.refreshComponent(updater, queue); + // callUpdate(updater, instance); + } + } + //再执行所有setState/forceUpdate回调,根据从下到上的顺序执行 + queue.sort(mountSorter).forEach(function (updater) { + clearArray(updater._pendingCallbacks).forEach(function (fn) { + fn.call(updater._instance); + }); + }); + queue.length = 0; + options.afterPatch(); +} + +var dirtyComponents = []; +function mountSorter(u1, u2) { + //按文档顺序执行 + return u1._mountIndex - u2._mountIndex; +} + +options.flushUpdaters = function (queue) { + if (!queue) { + queue = dirtyComponents; + if (queue.length) { + queue.sort(mountSorter); + } + } + drainQueue(queue); +}; + +options.enqueueUpdater = function (updater) { + if (dirtyComponents.indexOf(updater) == -1) { + dirtyComponents.push(updater); + } +}; + //[Top API] React.isValidElement function isValidElement(vnode) { return vnode && vnode.vtype; @@ -2157,7 +2178,7 @@ function renderByAnu(vnode, container, callback) { var instance = vnode._instance; container.__component = vnode; drainQueue(updateQueue); - CurrentOwner.cur = null; //防止干扰 + Refs.currentOwner = null; //防止干扰 var ret = instance || rootNode; if (callback) { callback.call(ret); //坑 @@ -2220,7 +2241,6 @@ function genMountElement(lastNode, vnode, vparent, type) { return lastNode; } else { var dom = createDOMElement(vnode, vparent); - if (lastNode) { while (lastNode.firstChild) { dom.appendChild(lastNode.firstChild); @@ -2293,7 +2313,8 @@ function alignChildren(parentNode, children, vparent, context, updateQueue) { function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue, parentUpdater) { var type = vnode.type, - props = vnode.props; + props = vnode.props, + ref = vnode.ref; var context = getContextByTypes(parentContext, type.contextTypes); var instance = instantiateComponent(type, vnode, props, context); //互相持有引用 @@ -2316,7 +2337,7 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue, pa return mountVnode(lastNode, nextRendered, vparent, childContext, updateQueue, updater //作为parentUpater往下传 ); }, updater.rendered); - + Refs.createInstanceRef(updater, ref); updateQueue.push(updater); return dom; @@ -2325,8 +2346,7 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue, pa function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQueue) { var type = lastVnode.type, ref = lastVnode.ref, - instance = lastVnode._instance, - vtype = lastVnode.vtype; + instance = lastVnode._instance; var nextContext = void 0, queue = void 0, @@ -2346,12 +2366,12 @@ function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQue instance.componentWillReceiveProps(nextProps, nextContext); updater._receiving = false; } - //用于refreshComponent - if (ref && vtype === 2) { + if (!instance.__isStateless) { var nextRef = nextVnode.ref; - detachRef(ref, nextRef); - lastVnode.ref = nextRef; + ref && Refs.detachRef(ref, nextRef); + Refs.createInstanceRef(updater, nextRef); } + //updater上总是保持新的数据 updater.lastVnode = lastVnode; updater.vnode = nextVnode; @@ -2405,14 +2425,11 @@ function refreshComponent(updater, updateQueue) { instance.state = nextState; //既然setState了,无论shouldComponentUpdate结果如何,用户传给的state对象都会作用到组件上 instance.context = nextContext; if (!shouldUpdate) { + updateQueue.push(updater); return dom; } instance.props = nextProps; updater._hydrating = true; - updater.lastProps = lastProps; - updater.lastState = lastState; - updater.lastContext = lastContext; - var lastRendered = updater.rendered; dom = updater.renderComponent(function (nextRendered, vparent, childContext) { @@ -2421,13 +2438,22 @@ function refreshComponent(updater, updateQueue) { updater.lastVnode = vnode; updater._lifeStage = 2; - // updater._hydrating = false; - if (!updateQueue.isMainProcess) { - drainQueue(updateQueue); + var hookName = "componentDidUpdate"; + var didUpdateHook = instance[hookName]; + if (didUpdateHook) { + instance[hookName] = function () { + didUpdateHook.call(this, lastProps, lastState, lastContext); + this[hookName] = didUpdateHook; + }; } else { - updater._hydrating = false; + //临时添加一个一次性的空钩子 + instance[hookName] = function () { + delete instance[hookName]; + }; } + // updater._hydrating = false; + updateQueue.push(updater); return dom; } options.refreshComponent = refreshComponent; @@ -2458,7 +2484,6 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { console.log("此节点已经被移除", vparent); return null; } - var nextProps = nextVnode.props, nextRef = nextVnode.ref; @@ -2482,21 +2507,10 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { if (nextVnode.type === "select") { postUpdateSelectedOptions(nextVnode); } - detachRef(ref, nextRef, dom); - + Refs.detachRef(ref, nextRef, dom); return dom; } -function detachRef(ref, nextRef, dom) { - ref = ref || noop; - if (nextRef) { - if (!ref.string && !nextRef.string ? ref !== nextRef : ref.string !== nextRef.string) { - ref(null); - } - dom && nextRef(dom); - } else { - ref(null); - } -} + function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { var lastChildren = parentNode.vchildren, nextChildren = flattenChildren(nextVnode), diff --git a/dist/ReactIE.js b/dist/ReactIE.js index 5c1043601..ce1b025fa 100644 --- a/dist/ReactIE.js +++ b/dist/ReactIE.js @@ -1,5 +1,5 @@ /** - * IE6+,有问题请加QQ 370262116 by 司徒正美 Copyright 2017-10-07 + * IE6+,有问题请加QQ 370262116 by 司徒正美 Copyright 2017-10-08 */ (function (global, factory) { @@ -199,6 +199,64 @@ var recyclables = { "#comment": [] }; +//fix 0.14对此方法的改动,之前refs里面保存的是虚拟DOM +function getDOMNode() { + return this; +} +function errRef() { + throw "ref位置错误"; +} +var pendingRefs = []; +var Refs = { + currentOwner: null, + clearRefs: function clearRefs() { + var refs = pendingRefs.splice(0, pendingRefs.length); + refs.forEach(function (fn) { + fn(); + }); + }, + detachRef: function detachRef(ref, nextRef, dom) { + ref = ref || getDOMNode; + nextRef = nextRef || getDOMNode; + if (ref === nextRef) { + return; + } + if (ref) { + if (ref.string && nextRef.string ? ref.string !== nextRef.string : ref !== getDOMNode) { + ref(null); + // pendingRefs.push(ref.bind(0,null)); + } + } + if (dom && nextRef !== getDOMNode) { + nextRef(dom); + // pendingRefs.push(nextRef.bind(0,dom)); + } + }, + createInstanceRef: function createInstanceRef(updater, ref) { + updater._ref = function () { + if (ref) { + var inst = updater._instance; + ref(inst.__isStateless ? null : inst); + } + updater._ref = getDOMNode; + }; + }, + createStringRef: function createStringRef(owner, ref) { + var stringRef = owner === null ? errRef : function (dom) { + if (dom) { + if (dom.nodeType) { + dom.getDOMNode = getDOMNode; + } + owner.refs[ref] = dom; + } else { + delete owner.refs[ref]; + } + }; + stringRef.string = ref; + return stringRef; + } +}; + var CurrentOwner = { cur: null }; @@ -266,32 +324,11 @@ function createElement(type, config) { return new Vnode(type, key, ref, props, vtype, checkProps); } -//fix 0.14对此方法的改动,之前refs里面保存的是虚拟DOM -function getDOMNode() { - return this; -} -function errRef() { - throw "ref位置错误"; -} -function createStringRef(owner, ref) { - var stringRef = owner === null ? errRef : function (dom) { - if (dom) { - if (dom.nodeType) { - dom.getDOMNode = getDOMNode; - } - owner.refs[ref] = dom; - } else { - delete owner.refs[ref]; - } - }; - stringRef.string = ref; - return stringRef; -} function Vnode(type, key, ref, props, vtype, checkProps) { this.type = type; this.props = props; this.vtype = vtype; - var owner = CurrentOwner.cur; + var owner = Refs.currentOwner; this._owner = owner; if (key) { @@ -304,10 +341,10 @@ function Vnode(type, key, ref, props, vtype, checkProps) { var refType = typeNumber(ref); if (refType === 4 || refType === 3) { //string, number - this.ref = createStringRef(owner, ref + ""); + this.ref = Refs.createStringRef(owner, ref + ""); } else if (refType === 5) { if (ref.string) { - var ref2 = createStringRef(owner, ref.string); + var ref2 = Refs.createStringRef(owner, ref.string); this.ref = function (dom) { ref(dom); ref2(dom); @@ -451,7 +488,7 @@ function cloneElement(vnode, props) { return Object.assign({}, vnode); } var owner = vnode._owner, - lastOwn = CurrentOwner.cur, + lastOwn = Refs.currentOwner, old = vnode.props, configs = {}; if (props) { @@ -466,7 +503,7 @@ function cloneElement(vnode, props) { } else { configs = old; } - CurrentOwner.cur = owner; + Refs.currentOwner = owner; var args = [].slice.call(arguments, 0), argsLength = args.length; @@ -476,7 +513,7 @@ function cloneElement(vnode, props) { args.push(configs.children); } var ret = createElement.apply(null, args); - CurrentOwner.cur = lastOwn; + Refs.currentOwner = lastOwn; return ret; } @@ -776,8 +813,6 @@ function addGlobalEvent(name) { function addEvent(el, type, fn, bool) { if (el.addEventListener) { - // Unable to preventDefault inside passive event listener due to target being - // treated as passive el.addEventListener(type, fn, bool || false); } else if (el.attachEvent) { el.attachEvent("on" + type, fn); @@ -1632,6 +1667,7 @@ function Updater(instance, vnode) { this._mountIndex = this._mountOrder; this._instance = instance; this._pendingCallbacks = []; + this._ref = noop; this._pendingStates = []; this._lifeStage = 0; //判断生命周期 //update总是保存最新的数据,如state, props, context, parentContext, vparent @@ -1643,6 +1679,7 @@ function Updater(instance, vnode) { this.mergeStates = alwaysNull; } } + Updater.prototype = { mergeStates: function mergeStates() { var instance = this._instance, @@ -1663,7 +1700,6 @@ Updater.prototype = { pendings.length = 0; return nextState; }, - renderComponent: function renderComponent(cb, rendered) { var vnode = this.vnode, parentContext = this.parentContext, @@ -1672,8 +1708,8 @@ Updater.prototype = { if (!rendered) { try { - var lastOwn = CurrentOwner.cur; - CurrentOwner.cur = instance; + var lastOwn = Refs.currentOwner; + Refs.currentOwner = instance; if (this.willReceive === false) { rendered = this.rendered; delete this.willReceive; @@ -1681,7 +1717,7 @@ Updater.prototype = { rendered = instance.render(); } } finally { - CurrentOwner.cur = lastOwn; + Refs.currentOwner = lastOwn; } } @@ -1726,12 +1762,12 @@ function instantiateComponent(type, vnode, props, context) { updater.displayName = type.displayName || type.name; if (isStateless) { - var lastOwn = CurrentOwner.cur; - CurrentOwner.cur = instance; + var lastOwn = Refs.currentOwner; + Refs.currentOwner = instance; try { var mixin = instance.render(); } finally { - CurrentOwner.cur = lastOwn; + Refs.currentOwner = lastOwn; } if (mixin && mixin.render) { //支持module pattern component @@ -1992,36 +2028,25 @@ function getOptionSelected(option, selected) { dom.selected = selected; } -var pendingRefs = []; -function clearRefs() { - var refs = pendingRefs.slice(0); - pendingRefs.length = 0; - refs.forEach(function (fn) { - fn(); - }); -} function callUpdate(updater, instance) { - if (pendingRefs.length) { - clearRefs(); - } + Refs.clearRefs(); if (updater._lifeStage === 2) { - if (instance.componentDidUpdate) { - updater._didUpdate = true; - instance.componentDidUpdate(updater.lastProps, updater.lastState, updater.lastContext); - if (!updater._renderInNextCycle) { - updater._didUpdate = false; - } + updater._didUpdate = true; + instance.componentDidUpdate(); + if (!updater._renderInNextCycle) { + updater._didUpdate = false; } - options.afterUpdate(instance); - updater._hydrating = false; updater._lifeStage = 1; + updater._hydrating = false; + options.afterUpdate(instance); } + updater._ref(); } function drainQueue(queue) { options.beforePatch(); //先执行所有refs方法(从上到下) - clearRefs(); + Refs.clearRefs(); //再执行所有mount/update钩子(从下到上) var i = 0; while (i < queue.length) { @@ -2030,28 +2055,23 @@ function drainQueue(queue) { instance = updater._instance; i++; if (!updater._lifeStage) { - if (pendingRefs.length) { - clearRefs(); - } + Refs.clearRefs(); + instance.newStageAddedRefs = instance.newStageAddedRefs || {}; if (instance.componentDidMount) { instance.componentDidMount(); instance.componentDidMount = null; } updater._lifeStage = 1; - options.afterMount(instance); updater._hydrating = false; + updater._ref(); + options.afterMount(instance); } else { callUpdate(updater, instance); } - var ref = updater.vnode.ref; - if (ref) { - ref(instance.__isStateless ? null : instance); - } - // updater._hydrating = false; //子树已经构建完毕 - while (updater._renderInNextCycle) { - + //如果组件在componentDidMount中调用setState + if (updater._renderInNextCycle) { options.refreshComponent(updater, queue); - callUpdate(updater, instance); + // callUpdate(updater, instance); } } //再执行所有setState/forceUpdate回调,根据从下到上的顺序执行 @@ -2064,17 +2084,18 @@ function drainQueue(queue) { options.afterPatch(); } -//有一个列队, 先放进A组件与A组件回调 var dirtyComponents = []; - function mountSorter(u1, u2) { - //让子节点先于父节点执行 - return u2._mountIndex - u1._mountIndex; + //按文档顺序执行 + return u1._mountIndex - u2._mountIndex; } options.flushUpdaters = function (queue) { if (!queue) { queue = dirtyComponents; + if (queue.length) { + queue.sort(mountSorter); + } } drainQueue(queue); }; @@ -2156,7 +2177,7 @@ function renderByAnu(vnode, container, callback) { var instance = vnode._instance; container.__component = vnode; drainQueue(updateQueue); - CurrentOwner.cur = null; //防止干扰 + Refs.currentOwner = null; //防止干扰 var ret = instance || rootNode; if (callback) { callback.call(ret); //坑 @@ -2219,7 +2240,6 @@ function genMountElement(lastNode, vnode, vparent, type) { return lastNode; } else { var dom = createDOMElement(vnode, vparent); - if (lastNode) { while (lastNode.firstChild) { dom.appendChild(lastNode.firstChild); @@ -2292,7 +2312,8 @@ function alignChildren(parentNode, children, vparent, context, updateQueue) { function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue, parentUpdater) { var type = vnode.type, - props = vnode.props; + props = vnode.props, + ref = vnode.ref; var context = getContextByTypes(parentContext, type.contextTypes); var instance = instantiateComponent(type, vnode, props, context); //互相持有引用 @@ -2315,7 +2336,7 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue, pa return mountVnode(lastNode, nextRendered, vparent, childContext, updateQueue, updater //作为parentUpater往下传 ); }, updater.rendered); - + Refs.createInstanceRef(updater, ref); updateQueue.push(updater); return dom; @@ -2324,8 +2345,7 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue, pa function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQueue) { var type = lastVnode.type, ref = lastVnode.ref, - instance = lastVnode._instance, - vtype = lastVnode.vtype; + instance = lastVnode._instance; var nextContext = void 0, queue = void 0, @@ -2345,12 +2365,12 @@ function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQue instance.componentWillReceiveProps(nextProps, nextContext); updater._receiving = false; } - //用于refreshComponent - if (ref && vtype === 2) { + if (!instance.__isStateless) { var nextRef = nextVnode.ref; - detachRef(ref, nextRef); - lastVnode.ref = nextRef; + ref && Refs.detachRef(ref, nextRef); + Refs.createInstanceRef(updater, nextRef); } + //updater上总是保持新的数据 updater.lastVnode = lastVnode; updater.vnode = nextVnode; @@ -2404,14 +2424,11 @@ function refreshComponent(updater, updateQueue) { instance.state = nextState; //既然setState了,无论shouldComponentUpdate结果如何,用户传给的state对象都会作用到组件上 instance.context = nextContext; if (!shouldUpdate) { + updateQueue.push(updater); return dom; } instance.props = nextProps; updater._hydrating = true; - updater.lastProps = lastProps; - updater.lastState = lastState; - updater.lastContext = lastContext; - var lastRendered = updater.rendered; dom = updater.renderComponent(function (nextRendered, vparent, childContext) { @@ -2420,13 +2437,22 @@ function refreshComponent(updater, updateQueue) { updater.lastVnode = vnode; updater._lifeStage = 2; - // updater._hydrating = false; - if (!updateQueue.isMainProcess) { - drainQueue(updateQueue); + var hookName = "componentDidUpdate"; + var didUpdateHook = instance[hookName]; + if (didUpdateHook) { + instance[hookName] = function () { + didUpdateHook.call(this, lastProps, lastState, lastContext); + this[hookName] = didUpdateHook; + }; } else { - updater._hydrating = false; + //临时添加一个一次性的空钩子 + instance[hookName] = function () { + delete instance[hookName]; + }; } + // updater._hydrating = false; + updateQueue.push(updater); return dom; } options.refreshComponent = refreshComponent; @@ -2457,7 +2483,6 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { console.log("此节点已经被移除", vparent); return null; } - var nextProps = nextVnode.props, nextRef = nextVnode.ref; @@ -2481,21 +2506,10 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { if (nextVnode.type === "select") { postUpdateSelectedOptions(nextVnode); } - detachRef(ref, nextRef, dom); - + Refs.detachRef(ref, nextRef, dom); return dom; } -function detachRef(ref, nextRef, dom) { - ref = ref || noop; - if (nextRef) { - if (!ref.string && !nextRef.string ? ref !== nextRef : ref.string !== nextRef.string) { - ref(null); - } - dom && nextRef(dom); - } else { - ref(null); - } -} + function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { var lastChildren = parentNode.vchildren, nextChildren = flattenChildren(nextVnode), diff --git a/dist/ReactSelection.js b/dist/ReactSelection.js index 5c1043601..ce1b025fa 100644 --- a/dist/ReactSelection.js +++ b/dist/ReactSelection.js @@ -1,5 +1,5 @@ /** - * IE6+,有问题请加QQ 370262116 by 司徒正美 Copyright 2017-10-07 + * IE6+,有问题请加QQ 370262116 by 司徒正美 Copyright 2017-10-08 */ (function (global, factory) { @@ -199,6 +199,64 @@ var recyclables = { "#comment": [] }; +//fix 0.14对此方法的改动,之前refs里面保存的是虚拟DOM +function getDOMNode() { + return this; +} +function errRef() { + throw "ref位置错误"; +} +var pendingRefs = []; +var Refs = { + currentOwner: null, + clearRefs: function clearRefs() { + var refs = pendingRefs.splice(0, pendingRefs.length); + refs.forEach(function (fn) { + fn(); + }); + }, + detachRef: function detachRef(ref, nextRef, dom) { + ref = ref || getDOMNode; + nextRef = nextRef || getDOMNode; + if (ref === nextRef) { + return; + } + if (ref) { + if (ref.string && nextRef.string ? ref.string !== nextRef.string : ref !== getDOMNode) { + ref(null); + // pendingRefs.push(ref.bind(0,null)); + } + } + if (dom && nextRef !== getDOMNode) { + nextRef(dom); + // pendingRefs.push(nextRef.bind(0,dom)); + } + }, + createInstanceRef: function createInstanceRef(updater, ref) { + updater._ref = function () { + if (ref) { + var inst = updater._instance; + ref(inst.__isStateless ? null : inst); + } + updater._ref = getDOMNode; + }; + }, + createStringRef: function createStringRef(owner, ref) { + var stringRef = owner === null ? errRef : function (dom) { + if (dom) { + if (dom.nodeType) { + dom.getDOMNode = getDOMNode; + } + owner.refs[ref] = dom; + } else { + delete owner.refs[ref]; + } + }; + stringRef.string = ref; + return stringRef; + } +}; + var CurrentOwner = { cur: null }; @@ -266,32 +324,11 @@ function createElement(type, config) { return new Vnode(type, key, ref, props, vtype, checkProps); } -//fix 0.14对此方法的改动,之前refs里面保存的是虚拟DOM -function getDOMNode() { - return this; -} -function errRef() { - throw "ref位置错误"; -} -function createStringRef(owner, ref) { - var stringRef = owner === null ? errRef : function (dom) { - if (dom) { - if (dom.nodeType) { - dom.getDOMNode = getDOMNode; - } - owner.refs[ref] = dom; - } else { - delete owner.refs[ref]; - } - }; - stringRef.string = ref; - return stringRef; -} function Vnode(type, key, ref, props, vtype, checkProps) { this.type = type; this.props = props; this.vtype = vtype; - var owner = CurrentOwner.cur; + var owner = Refs.currentOwner; this._owner = owner; if (key) { @@ -304,10 +341,10 @@ function Vnode(type, key, ref, props, vtype, checkProps) { var refType = typeNumber(ref); if (refType === 4 || refType === 3) { //string, number - this.ref = createStringRef(owner, ref + ""); + this.ref = Refs.createStringRef(owner, ref + ""); } else if (refType === 5) { if (ref.string) { - var ref2 = createStringRef(owner, ref.string); + var ref2 = Refs.createStringRef(owner, ref.string); this.ref = function (dom) { ref(dom); ref2(dom); @@ -451,7 +488,7 @@ function cloneElement(vnode, props) { return Object.assign({}, vnode); } var owner = vnode._owner, - lastOwn = CurrentOwner.cur, + lastOwn = Refs.currentOwner, old = vnode.props, configs = {}; if (props) { @@ -466,7 +503,7 @@ function cloneElement(vnode, props) { } else { configs = old; } - CurrentOwner.cur = owner; + Refs.currentOwner = owner; var args = [].slice.call(arguments, 0), argsLength = args.length; @@ -476,7 +513,7 @@ function cloneElement(vnode, props) { args.push(configs.children); } var ret = createElement.apply(null, args); - CurrentOwner.cur = lastOwn; + Refs.currentOwner = lastOwn; return ret; } @@ -776,8 +813,6 @@ function addGlobalEvent(name) { function addEvent(el, type, fn, bool) { if (el.addEventListener) { - // Unable to preventDefault inside passive event listener due to target being - // treated as passive el.addEventListener(type, fn, bool || false); } else if (el.attachEvent) { el.attachEvent("on" + type, fn); @@ -1632,6 +1667,7 @@ function Updater(instance, vnode) { this._mountIndex = this._mountOrder; this._instance = instance; this._pendingCallbacks = []; + this._ref = noop; this._pendingStates = []; this._lifeStage = 0; //判断生命周期 //update总是保存最新的数据,如state, props, context, parentContext, vparent @@ -1643,6 +1679,7 @@ function Updater(instance, vnode) { this.mergeStates = alwaysNull; } } + Updater.prototype = { mergeStates: function mergeStates() { var instance = this._instance, @@ -1663,7 +1700,6 @@ Updater.prototype = { pendings.length = 0; return nextState; }, - renderComponent: function renderComponent(cb, rendered) { var vnode = this.vnode, parentContext = this.parentContext, @@ -1672,8 +1708,8 @@ Updater.prototype = { if (!rendered) { try { - var lastOwn = CurrentOwner.cur; - CurrentOwner.cur = instance; + var lastOwn = Refs.currentOwner; + Refs.currentOwner = instance; if (this.willReceive === false) { rendered = this.rendered; delete this.willReceive; @@ -1681,7 +1717,7 @@ Updater.prototype = { rendered = instance.render(); } } finally { - CurrentOwner.cur = lastOwn; + Refs.currentOwner = lastOwn; } } @@ -1726,12 +1762,12 @@ function instantiateComponent(type, vnode, props, context) { updater.displayName = type.displayName || type.name; if (isStateless) { - var lastOwn = CurrentOwner.cur; - CurrentOwner.cur = instance; + var lastOwn = Refs.currentOwner; + Refs.currentOwner = instance; try { var mixin = instance.render(); } finally { - CurrentOwner.cur = lastOwn; + Refs.currentOwner = lastOwn; } if (mixin && mixin.render) { //支持module pattern component @@ -1992,36 +2028,25 @@ function getOptionSelected(option, selected) { dom.selected = selected; } -var pendingRefs = []; -function clearRefs() { - var refs = pendingRefs.slice(0); - pendingRefs.length = 0; - refs.forEach(function (fn) { - fn(); - }); -} function callUpdate(updater, instance) { - if (pendingRefs.length) { - clearRefs(); - } + Refs.clearRefs(); if (updater._lifeStage === 2) { - if (instance.componentDidUpdate) { - updater._didUpdate = true; - instance.componentDidUpdate(updater.lastProps, updater.lastState, updater.lastContext); - if (!updater._renderInNextCycle) { - updater._didUpdate = false; - } + updater._didUpdate = true; + instance.componentDidUpdate(); + if (!updater._renderInNextCycle) { + updater._didUpdate = false; } - options.afterUpdate(instance); - updater._hydrating = false; updater._lifeStage = 1; + updater._hydrating = false; + options.afterUpdate(instance); } + updater._ref(); } function drainQueue(queue) { options.beforePatch(); //先执行所有refs方法(从上到下) - clearRefs(); + Refs.clearRefs(); //再执行所有mount/update钩子(从下到上) var i = 0; while (i < queue.length) { @@ -2030,28 +2055,23 @@ function drainQueue(queue) { instance = updater._instance; i++; if (!updater._lifeStage) { - if (pendingRefs.length) { - clearRefs(); - } + Refs.clearRefs(); + instance.newStageAddedRefs = instance.newStageAddedRefs || {}; if (instance.componentDidMount) { instance.componentDidMount(); instance.componentDidMount = null; } updater._lifeStage = 1; - options.afterMount(instance); updater._hydrating = false; + updater._ref(); + options.afterMount(instance); } else { callUpdate(updater, instance); } - var ref = updater.vnode.ref; - if (ref) { - ref(instance.__isStateless ? null : instance); - } - // updater._hydrating = false; //子树已经构建完毕 - while (updater._renderInNextCycle) { - + //如果组件在componentDidMount中调用setState + if (updater._renderInNextCycle) { options.refreshComponent(updater, queue); - callUpdate(updater, instance); + // callUpdate(updater, instance); } } //再执行所有setState/forceUpdate回调,根据从下到上的顺序执行 @@ -2064,17 +2084,18 @@ function drainQueue(queue) { options.afterPatch(); } -//有一个列队, 先放进A组件与A组件回调 var dirtyComponents = []; - function mountSorter(u1, u2) { - //让子节点先于父节点执行 - return u2._mountIndex - u1._mountIndex; + //按文档顺序执行 + return u1._mountIndex - u2._mountIndex; } options.flushUpdaters = function (queue) { if (!queue) { queue = dirtyComponents; + if (queue.length) { + queue.sort(mountSorter); + } } drainQueue(queue); }; @@ -2156,7 +2177,7 @@ function renderByAnu(vnode, container, callback) { var instance = vnode._instance; container.__component = vnode; drainQueue(updateQueue); - CurrentOwner.cur = null; //防止干扰 + Refs.currentOwner = null; //防止干扰 var ret = instance || rootNode; if (callback) { callback.call(ret); //坑 @@ -2219,7 +2240,6 @@ function genMountElement(lastNode, vnode, vparent, type) { return lastNode; } else { var dom = createDOMElement(vnode, vparent); - if (lastNode) { while (lastNode.firstChild) { dom.appendChild(lastNode.firstChild); @@ -2292,7 +2312,8 @@ function alignChildren(parentNode, children, vparent, context, updateQueue) { function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue, parentUpdater) { var type = vnode.type, - props = vnode.props; + props = vnode.props, + ref = vnode.ref; var context = getContextByTypes(parentContext, type.contextTypes); var instance = instantiateComponent(type, vnode, props, context); //互相持有引用 @@ -2315,7 +2336,7 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue, pa return mountVnode(lastNode, nextRendered, vparent, childContext, updateQueue, updater //作为parentUpater往下传 ); }, updater.rendered); - + Refs.createInstanceRef(updater, ref); updateQueue.push(updater); return dom; @@ -2324,8 +2345,7 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue, pa function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQueue) { var type = lastVnode.type, ref = lastVnode.ref, - instance = lastVnode._instance, - vtype = lastVnode.vtype; + instance = lastVnode._instance; var nextContext = void 0, queue = void 0, @@ -2345,12 +2365,12 @@ function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQue instance.componentWillReceiveProps(nextProps, nextContext); updater._receiving = false; } - //用于refreshComponent - if (ref && vtype === 2) { + if (!instance.__isStateless) { var nextRef = nextVnode.ref; - detachRef(ref, nextRef); - lastVnode.ref = nextRef; + ref && Refs.detachRef(ref, nextRef); + Refs.createInstanceRef(updater, nextRef); } + //updater上总是保持新的数据 updater.lastVnode = lastVnode; updater.vnode = nextVnode; @@ -2404,14 +2424,11 @@ function refreshComponent(updater, updateQueue) { instance.state = nextState; //既然setState了,无论shouldComponentUpdate结果如何,用户传给的state对象都会作用到组件上 instance.context = nextContext; if (!shouldUpdate) { + updateQueue.push(updater); return dom; } instance.props = nextProps; updater._hydrating = true; - updater.lastProps = lastProps; - updater.lastState = lastState; - updater.lastContext = lastContext; - var lastRendered = updater.rendered; dom = updater.renderComponent(function (nextRendered, vparent, childContext) { @@ -2420,13 +2437,22 @@ function refreshComponent(updater, updateQueue) { updater.lastVnode = vnode; updater._lifeStage = 2; - // updater._hydrating = false; - if (!updateQueue.isMainProcess) { - drainQueue(updateQueue); + var hookName = "componentDidUpdate"; + var didUpdateHook = instance[hookName]; + if (didUpdateHook) { + instance[hookName] = function () { + didUpdateHook.call(this, lastProps, lastState, lastContext); + this[hookName] = didUpdateHook; + }; } else { - updater._hydrating = false; + //临时添加一个一次性的空钩子 + instance[hookName] = function () { + delete instance[hookName]; + }; } + // updater._hydrating = false; + updateQueue.push(updater); return dom; } options.refreshComponent = refreshComponent; @@ -2457,7 +2483,6 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { console.log("此节点已经被移除", vparent); return null; } - var nextProps = nextVnode.props, nextRef = nextVnode.ref; @@ -2481,21 +2506,10 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { if (nextVnode.type === "select") { postUpdateSelectedOptions(nextVnode); } - detachRef(ref, nextRef, dom); - + Refs.detachRef(ref, nextRef, dom); return dom; } -function detachRef(ref, nextRef, dom) { - ref = ref || noop; - if (nextRef) { - if (!ref.string && !nextRef.string ? ref !== nextRef : ref.string !== nextRef.string) { - ref(null); - } - dom && nextRef(dom); - } else { - ref(null); - } -} + function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { var lastChildren = parentNode.vchildren, nextChildren = flattenChildren(nextVnode), diff --git a/dist/ReactShim.js b/dist/ReactShim.js index 27959d084..e3dbbfd2f 100644 --- a/dist/ReactShim.js +++ b/dist/ReactShim.js @@ -1,7 +1,7 @@ /** * 此版本要求浏览器没有createClass, createFactory, PropTypes, isValidElement, * unmountComponentAtNode,unstable_renderSubtreeIntoContainer - * QQ 370262116 by 司徒正美 Copyright 2017-10-07 + * QQ 370262116 by 司徒正美 Copyright 2017-10-08 */ (function (global, factory) { @@ -201,6 +201,64 @@ var recyclables = { "#comment": [] }; +//fix 0.14对此方法的改动,之前refs里面保存的是虚拟DOM +function getDOMNode() { + return this; +} +function errRef() { + throw "ref位置错误"; +} +var pendingRefs = []; +var Refs = { + currentOwner: null, + clearRefs: function clearRefs() { + var refs = pendingRefs.splice(0, pendingRefs.length); + refs.forEach(function (fn) { + fn(); + }); + }, + detachRef: function detachRef(ref, nextRef, dom) { + ref = ref || getDOMNode; + nextRef = nextRef || getDOMNode; + if (ref === nextRef) { + return; + } + if (ref) { + if (ref.string && nextRef.string ? ref.string !== nextRef.string : ref !== getDOMNode) { + ref(null); + // pendingRefs.push(ref.bind(0,null)); + } + } + if (dom && nextRef !== getDOMNode) { + nextRef(dom); + // pendingRefs.push(nextRef.bind(0,dom)); + } + }, + createInstanceRef: function createInstanceRef(updater, ref) { + updater._ref = function () { + if (ref) { + var inst = updater._instance; + ref(inst.__isStateless ? null : inst); + } + updater._ref = getDOMNode; + }; + }, + createStringRef: function createStringRef(owner, ref) { + var stringRef = owner === null ? errRef : function (dom) { + if (dom) { + if (dom.nodeType) { + dom.getDOMNode = getDOMNode; + } + owner.refs[ref] = dom; + } else { + delete owner.refs[ref]; + } + }; + stringRef.string = ref; + return stringRef; + } +}; + var CurrentOwner = { cur: null }; @@ -268,32 +326,11 @@ function createElement(type, config) { return new Vnode(type, key, ref, props, vtype, checkProps); } -//fix 0.14对此方法的改动,之前refs里面保存的是虚拟DOM -function getDOMNode() { - return this; -} -function errRef() { - throw "ref位置错误"; -} -function createStringRef(owner, ref) { - var stringRef = owner === null ? errRef : function (dom) { - if (dom) { - if (dom.nodeType) { - dom.getDOMNode = getDOMNode; - } - owner.refs[ref] = dom; - } else { - delete owner.refs[ref]; - } - }; - stringRef.string = ref; - return stringRef; -} function Vnode(type, key, ref, props, vtype, checkProps) { this.type = type; this.props = props; this.vtype = vtype; - var owner = CurrentOwner.cur; + var owner = Refs.currentOwner; this._owner = owner; if (key) { @@ -306,10 +343,10 @@ function Vnode(type, key, ref, props, vtype, checkProps) { var refType = typeNumber(ref); if (refType === 4 || refType === 3) { //string, number - this.ref = createStringRef(owner, ref + ""); + this.ref = Refs.createStringRef(owner, ref + ""); } else if (refType === 5) { if (ref.string) { - var ref2 = createStringRef(owner, ref.string); + var ref2 = Refs.createStringRef(owner, ref.string); this.ref = function (dom) { ref(dom); ref2(dom); @@ -543,7 +580,7 @@ function cloneElement(vnode, props) { return Object.assign({}, vnode); } var owner = vnode._owner, - lastOwn = CurrentOwner.cur, + lastOwn = Refs.currentOwner, old = vnode.props, configs = {}; if (props) { @@ -558,7 +595,7 @@ function cloneElement(vnode, props) { } else { configs = old; } - CurrentOwner.cur = owner; + Refs.currentOwner = owner; var args = [].slice.call(arguments, 0), argsLength = args.length; @@ -568,7 +605,7 @@ function cloneElement(vnode, props) { args.push(configs.children); } var ret = createElement.apply(null, args); - CurrentOwner.cur = lastOwn; + Refs.currentOwner = lastOwn; return ret; } @@ -973,8 +1010,6 @@ function addGlobalEvent(name) { function addEvent(el, type, fn, bool) { if (el.addEventListener) { - // Unable to preventDefault inside passive event listener due to target being - // treated as passive el.addEventListener(type, fn, bool || false); } else if (el.attachEvent) { @@ -1478,6 +1513,7 @@ function Updater(instance, vnode) { this._mountIndex = this._mountOrder; this._instance = instance; this._pendingCallbacks = []; + this._ref = noop; this._pendingStates = []; this._lifeStage = 0; //判断生命周期 //update总是保存最新的数据,如state, props, context, parentContext, vparent @@ -1489,6 +1525,7 @@ function Updater(instance, vnode) { this.mergeStates = alwaysNull; } } + Updater.prototype = { mergeStates: function mergeStates() { var instance = this._instance, @@ -1509,7 +1546,6 @@ Updater.prototype = { pendings.length = 0; return nextState; }, - renderComponent: function renderComponent(cb, rendered) { var vnode = this.vnode, parentContext = this.parentContext, @@ -1518,8 +1554,8 @@ Updater.prototype = { if (!rendered) { try { - var lastOwn = CurrentOwner.cur; - CurrentOwner.cur = instance; + var lastOwn = Refs.currentOwner; + Refs.currentOwner = instance; if (this.willReceive === false) { rendered = this.rendered; delete this.willReceive; @@ -1527,7 +1563,7 @@ Updater.prototype = { rendered = instance.render(); } } finally { - CurrentOwner.cur = lastOwn; + Refs.currentOwner = lastOwn; } } @@ -1572,12 +1608,12 @@ function instantiateComponent(type, vnode, props, context) { updater.displayName = type.displayName || type.name; if (isStateless) { - var lastOwn = CurrentOwner.cur; - CurrentOwner.cur = instance; + var lastOwn = Refs.currentOwner; + Refs.currentOwner = instance; try { var mixin = instance.render(); } finally { - CurrentOwner.cur = lastOwn; + Refs.currentOwner = lastOwn; } if (mixin && mixin.render) { //支持module pattern component @@ -1838,36 +1874,25 @@ function getOptionSelected(option, selected) { dom.selected = selected; } -var pendingRefs = []; -function clearRefs() { - var refs = pendingRefs.slice(0); - pendingRefs.length = 0; - refs.forEach(function (fn) { - fn(); - }); -} function callUpdate(updater, instance) { - if (pendingRefs.length) { - clearRefs(); - } + Refs.clearRefs(); if (updater._lifeStage === 2) { - if (instance.componentDidUpdate) { - updater._didUpdate = true; - instance.componentDidUpdate(updater.lastProps, updater.lastState, updater.lastContext); - if (!updater._renderInNextCycle) { - updater._didUpdate = false; - } + updater._didUpdate = true; + instance.componentDidUpdate(); + if (!updater._renderInNextCycle) { + updater._didUpdate = false; } - options.afterUpdate(instance); - updater._hydrating = false; updater._lifeStage = 1; + updater._hydrating = false; + options.afterUpdate(instance); } + updater._ref(); } function drainQueue(queue) { options.beforePatch(); //先执行所有refs方法(从上到下) - clearRefs(); + Refs.clearRefs(); //再执行所有mount/update钩子(从下到上) var i = 0; while (i < queue.length) { @@ -1876,28 +1901,23 @@ function drainQueue(queue) { instance = updater._instance; i++; if (!updater._lifeStage) { - if (pendingRefs.length) { - clearRefs(); - } + Refs.clearRefs(); + instance.newStageAddedRefs = instance.newStageAddedRefs || {}; if (instance.componentDidMount) { instance.componentDidMount(); instance.componentDidMount = null; } updater._lifeStage = 1; - options.afterMount(instance); updater._hydrating = false; + updater._ref(); + options.afterMount(instance); } else { callUpdate(updater, instance); } - var ref = updater.vnode.ref; - if (ref) { - ref(instance.__isStateless ? null : instance); - } - // updater._hydrating = false; //子树已经构建完毕 - while (updater._renderInNextCycle) { - + //如果组件在componentDidMount中调用setState + if (updater._renderInNextCycle) { options.refreshComponent(updater, queue); - callUpdate(updater, instance); + // callUpdate(updater, instance); } } //再执行所有setState/forceUpdate回调,根据从下到上的顺序执行 @@ -1910,17 +1930,18 @@ function drainQueue(queue) { options.afterPatch(); } -//有一个列队, 先放进A组件与A组件回调 var dirtyComponents = []; - function mountSorter(u1, u2) { - //让子节点先于父节点执行 - return u2._mountIndex - u1._mountIndex; + //按文档顺序执行 + return u1._mountIndex - u2._mountIndex; } options.flushUpdaters = function (queue) { if (!queue) { queue = dirtyComponents; + if (queue.length) { + queue.sort(mountSorter); + } } drainQueue(queue); }; @@ -1998,7 +2019,7 @@ function renderByAnu(vnode, container, callback) { var instance = vnode._instance; container.__component = vnode; drainQueue(updateQueue); - CurrentOwner.cur = null; //防止干扰 + Refs.currentOwner = null; //防止干扰 var ret = instance || rootNode; if (callback) { callback.call(ret); //坑 @@ -2061,7 +2082,6 @@ function genMountElement(lastNode, vnode, vparent, type) { return lastNode; } else { var dom = createDOMElement(vnode, vparent); - if (lastNode) { while (lastNode.firstChild) { dom.appendChild(lastNode.firstChild); @@ -2134,7 +2154,8 @@ function alignChildren(parentNode, children, vparent, context, updateQueue) { function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue, parentUpdater) { var type = vnode.type, - props = vnode.props; + props = vnode.props, + ref = vnode.ref; var context = getContextByTypes(parentContext, type.contextTypes); var instance = instantiateComponent(type, vnode, props, context); //互相持有引用 @@ -2157,7 +2178,7 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue, pa return mountVnode(lastNode, nextRendered, vparent, childContext, updateQueue, updater //作为parentUpater往下传 ); }, updater.rendered); - + Refs.createInstanceRef(updater, ref); updateQueue.push(updater); return dom; @@ -2166,8 +2187,7 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue, pa function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQueue) { var type = lastVnode.type, ref = lastVnode.ref, - instance = lastVnode._instance, - vtype = lastVnode.vtype; + instance = lastVnode._instance; var nextContext = void 0, queue = void 0, @@ -2187,12 +2207,12 @@ function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQue instance.componentWillReceiveProps(nextProps, nextContext); updater._receiving = false; } - //用于refreshComponent - if (ref && vtype === 2) { + if (!instance.__isStateless) { var nextRef = nextVnode.ref; - detachRef(ref, nextRef); - lastVnode.ref = nextRef; + ref && Refs.detachRef(ref, nextRef); + Refs.createInstanceRef(updater, nextRef); } + //updater上总是保持新的数据 updater.lastVnode = lastVnode; updater.vnode = nextVnode; @@ -2246,14 +2266,11 @@ function refreshComponent(updater, updateQueue) { instance.state = nextState; //既然setState了,无论shouldComponentUpdate结果如何,用户传给的state对象都会作用到组件上 instance.context = nextContext; if (!shouldUpdate) { + updateQueue.push(updater); return dom; } instance.props = nextProps; updater._hydrating = true; - updater.lastProps = lastProps; - updater.lastState = lastState; - updater.lastContext = lastContext; - var lastRendered = updater.rendered; dom = updater.renderComponent(function (nextRendered, vparent, childContext) { @@ -2262,13 +2279,22 @@ function refreshComponent(updater, updateQueue) { updater.lastVnode = vnode; updater._lifeStage = 2; - // updater._hydrating = false; - if (!updateQueue.isMainProcess) { - drainQueue(updateQueue); + var hookName = "componentDidUpdate"; + var didUpdateHook = instance[hookName]; + if (didUpdateHook) { + instance[hookName] = function () { + didUpdateHook.call(this, lastProps, lastState, lastContext); + this[hookName] = didUpdateHook; + }; } else { - updater._hydrating = false; + //临时添加一个一次性的空钩子 + instance[hookName] = function () { + delete instance[hookName]; + }; } + // updater._hydrating = false; + updateQueue.push(updater); return dom; } options.refreshComponent = refreshComponent; @@ -2299,7 +2325,6 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { console.log("此节点已经被移除", vparent); return null; } - var nextProps = nextVnode.props, nextRef = nextVnode.ref; @@ -2323,21 +2348,10 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { if (nextVnode.type === "select") { postUpdateSelectedOptions(nextVnode); } - detachRef(ref, nextRef, dom); - + Refs.detachRef(ref, nextRef, dom); return dom; } -function detachRef(ref, nextRef, dom) { - ref = ref || noop; - if (nextRef) { - if (!ref.string && !nextRef.string ? ref !== nextRef : ref.string !== nextRef.string) { - ref(null); - } - dom && nextRef(dom); - } else { - ref(null); - } -} + function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { var lastChildren = parentNode.vchildren, nextChildren = flattenChildren(nextVnode), diff --git a/index3.html b/index3.html index 5d09f3b5d..1a50da148 100644 --- a/index3.html +++ b/index3.html @@ -4,6 +4,8 @@ + @@ -12,7 +14,16 @@ -
开发者工具
+
+            'will mount',
+            'render',
+            'did mount',
+            'ref',
+      
+            'render',
+            'did update',
+            'ref',
+    
diff --git a/index4.html b/index4.html index 016026f25..6e16dba7c 100644 --- a/index4.html +++ b/index4.html @@ -4,77 +4,112 @@ + - - - - - - - - - - + + + + + + + - - -
开发者工具
-
- - + + - - \ No newline at end of file + + \ No newline at end of file diff --git a/index5.html b/index5.html index a3de1fbc1..3cf5ea3d4 100644 --- a/index5.html +++ b/index5.html @@ -40,61 +40,58 @@ } } - - class A extends React.Component{ - constructor(props){ - super(props) - this.state = { - a: 100 - } - } - render(){ - console.log(this.state.a) - return

随便{this.state.a}

+ class Static extends React.Component { + shouldComponentUpdate() { + return false; } - } - class B extends React.Component{ - constructor(props){ - super(props) - this.state = { - b: props.a - } - } - componentWillReceiveProps(props){ - console.log("B", props.a) - this.state.b = props.a - + render() { + return
{this.props.children}
; } - shouldComponentUpdate(){ - console.log("=====",!!this.state.b) - return this.state.b - } - render(){ - console.log("B render", this.state.b) - return {this.props.children} - } - } - - class C extends React.Component{ - constructor(props){ - super(props) - this.state = { - c: 111 + } + + class Component extends React.Component { + render() { + if (this.props.flipped) { + return ( +
+ + B (ignored) + + + A (ignored) + +
+ ); + } else { + return ( +
+ + A + + + B + +
+ ); } } - - render(){ - window.c = this - return {this.state.c} - } - } - - var instance = ReactDOM.render(
, container); - console.log(instance.updater._hostNode) - instance.setState({a: 0}) - - console.log(instance.updater._hostNode) + } + + var container = document.createElement("div"); + var comp = ReactDOM.render(, container); + //keyA <> instance0 <> static0 <> contentA + //keyB <> instance1 <> static1 <> contentB + expect(ReactDOM.findDOMNode(comp.refs.static0).textContent).toBe("A"); + expect(ReactDOM.findDOMNode(comp.refs.static1).textContent).toBe("B"); + //keyA <> instance0 <> static1 <> contentA + //keyB <> instance1 <> static1 <> contentB + // When flipping the order, the refs should update even though the actual + // contents do not + ReactDOM.render(, container); + console.log(comp.refs) + expect(ReactDOM.findDOMNode(comp.refs.static0).textContent).toBe("B"); + expect(ReactDOM.findDOMNode(comp.refs.static1).textContent).toBe("A"); // console.log(myNodeB) diff --git a/index6.html b/index6.html index 75a0696ea..9501c9218 100644 --- a/index6.html +++ b/index6.html @@ -4,83 +4,133 @@ - + - - + - + -
开发者工具
+
+            "start mount",
+            "inner 1 render",
+            "inner 2 render",
+            "inner 1 componentDidMount",
+            "ref 1 got instance 1",
+            "inner 2 componentDidMount",
+            "ref 2 got instance 2",
+            "outer componentDidMount",
+            "start update",
+      
+            // Stack resets refs before rendering
+            "ref 1 got null",
+            "inner 1 render",
+            "ref 2 got null",
+            "inner 2 render",
+      
+            "inner 1 componentDidUpdate",
+            "ref 1 got instance 1",
+            "inner 2 componentDidUpdate",
+            "ref 2 got instance 2",
+            "outer componentDidUpdate",
+            "start unmount",
+            "outer componentWillUnmount",
+            "ref 1 got null",
+            "inner 1 componentWillUnmount",
+            "ref 2 got null",
+            "inner 2 componentWillUnmount"
+          
- + diff --git a/index7.html b/index7.html index 5fd8a2329..75dcd288b 100644 --- a/index7.html +++ b/index7.html @@ -6,8 +6,8 @@ - - + @@ -28,55 +28,26 @@ } } } - var k = 0 - class Static extends React.Component { - shouldComponentUpdate() { - // return false; - return true - } - componentDidUpdate() { - console.log('更新') - } - render() { - console.log(this.props.children, '孩子') - return
{this.props.children}
; - } - } - - class Component extends React.Component { + class A extends React.Component { render() { - if (this.props.flipped) { - console.log('第二次') - return ( -
- BB - AA -
- ); - } else { - console.log('第一次') - return ( -
- A - B -
- ); - } + return
; } - } - - // var container = document.createElement('div'); - var comp = ReactDOM.render(, container); - console.log(Object.assign({},comp.refs)) - expect(ReactDOM.findDOMNode(comp.refs.static0).textContent).toBe('A'); - expect(ReactDOM.findDOMNode(comp.refs.static1).textContent).toBe('B'); + } - ReactDOM.render(, container); - console.log(comp.refs) - console.log(comp.refs.static0) - console.log(comp.refs.static1) - expect(ReactDOM.findDOMNode(comp.refs.static0).textContent).toBe('B'); - expect(ReactDOM.findDOMNode(comp.refs.static1).textContent).toBe('A'); + var el = React.createElement(A, {}); + expect(el.vtype).toBe(2); + console.log(el) + el = React.createElement(function() {}, {}); + expect(el.vtype).toBe(4); + var obj = new A().render(); + expect(obj.props.children).toBe(void 666); + expect(obj.props.id).toBe("aaa"); + expect(obj.props).toBe({ + id: "aaa" + }); + expect(obj.type).toBe("div"); + expect(obj.key == null).toBe(true); + expect(typeof obj._owner).toBe("object"); } diff --git a/src/React.js b/src/React.js index 0d2c1d78b..24ee7320e 100644 --- a/src/React.js +++ b/src/React.js @@ -8,7 +8,7 @@ import { createClass } from "./createClass"; import { cloneElement } from "./cloneElement"; import { PureComponent } from "./PureComponent"; import { createElement } from "./createElement"; -import { pendingRefs } from "./scheduler"; +import { pendingRefs } from "./Refs"; import { render,findDOMNode, isValidElement, unmountComponentAtNode, unstable_renderSubtreeIntoContainer } from "./diff"; diff --git a/src/Refs.js b/src/Refs.js new file mode 100644 index 000000000..15da916cf --- /dev/null +++ b/src/Refs.js @@ -0,0 +1,61 @@ + +//fix 0.14对此方法的改动,之前refs里面保存的是虚拟DOM +function getDOMNode() { + return this; +} +function errRef() { + throw "ref位置错误"; +} +export var pendingRefs = []; +export var Refs = { + currentOwner: null, + clearRefs: function(){ + var refs = pendingRefs.splice(0,pendingRefs.length ); + refs.forEach(function(fn) { + fn(); + }); + }, + detachRef:function(ref, nextRef, dom) { + ref = ref || getDOMNode; + nextRef = nextRef || getDOMNode; + if(ref === nextRef) { + return; + } + if(ref){ + if (ref.string && nextRef.string ? ref.string !== nextRef.string: ref !== getDOMNode ) { + ref(null); + // pendingRefs.push(ref.bind(0,null)); + } + } + if(dom && nextRef !== getDOMNode){ + nextRef(dom); + // pendingRefs.push(nextRef.bind(0,dom)); + } + }, + createInstanceRef: function(updater, ref){ + updater._ref = function(){ + if(ref){ + var inst = updater._instance; + ref(inst.__isStateless ? null: inst); + } + updater._ref = getDOMNode; + }; + }, + createStringRef: function (owner, ref) { + var stringRef = owner === null + ? errRef + : function (dom) { + if (dom) { + if (dom.nodeType) { + dom.getDOMNode = getDOMNode; + } + owner.refs[ref] = dom; + }else{ + delete owner.refs[ref]; + } + }; + stringRef.string = ref; + return stringRef; + } +}; + diff --git a/src/instantiateComponent.js b/src/Updater.js similarity index 81% rename from src/instantiateComponent.js rename to src/Updater.js index e2bf72cfd..ad6516135 100644 --- a/src/instantiateComponent.js +++ b/src/Updater.js @@ -1,17 +1,18 @@ var mountOrder = 1; -import { getChildContext } from "../src/util"; -import { CurrentOwner } from "./createElement"; +import { getChildContext, noop } from "../src/util"; +import { Refs } from "./Refs"; function alwaysNull() { return null; } export var updateChains = {}; -function Updater(instance, vnode) { +export function Updater(instance, vnode) { vnode._instance = instance; instance.updater = this; this._mountOrder = mountOrder++; - this._mountIndex = this._mountOrder; + this._mountIndex = this._mountOrder; this._instance = instance; this._pendingCallbacks = []; + this._ref = noop; this._pendingStates = []; this._lifeStage = 0; //判断生命周期 //update总是保存最新的数据,如state, props, context, parentContext, vparent @@ -23,8 +24,9 @@ function Updater(instance, vnode) { this.mergeStates = alwaysNull; } } + Updater.prototype = { - mergeStates: function() { + mergeStates() { let instance = this._instance, pendings = this._pendingStates, state = instance.state, @@ -44,25 +46,21 @@ Updater.prototype = { return nextState; }, - renderComponent: function(cb, rendered) { - let { - vnode, - parentContext, - _instance: instance - } = this; + renderComponent(cb, rendered) { + let { vnode, parentContext, _instance: instance } = this; //调整全局的 CurrentOwner.cur if (!rendered) { try { - var lastOwn = CurrentOwner.cur; - CurrentOwner.cur = instance; - if(this.willReceive === false){ + var lastOwn = Refs.currentOwner; + Refs.currentOwner = instance; + if (this.willReceive === false) { rendered = this.rendered; delete this.willReceive; - }else{ - rendered = instance.render(); + } else { + rendered = instance.render(); } } finally { - CurrentOwner.cur = lastOwn; + Refs.currentOwner = lastOwn; } } @@ -77,11 +75,11 @@ Updater.prototype = { this.rendered = rendered; let childContext = rendered.vtype ? getChildContext(instance, parentContext) : parentContext; let dom = cb(rendered, this.vparent, childContext); - if(!dom){ - throw ["必须返回节点",rendered]; + if (!dom) { + throw ["必须返回节点", rendered]; } let list = updateChains[this._mountOrder]; - if(!list){ + if (!list) { list = updateChains[this._mountOrder] = [this]; } list.forEach(function(el) { @@ -109,12 +107,12 @@ export function instantiateComponent(type, vnode, props, context) { updater.displayName = type.displayName || type.name; if (isStateless) { - let lastOwn = CurrentOwner.cur; - CurrentOwner.cur = instance; + let lastOwn = Refs.currentOwner; + Refs.currentOwner = instance; try { var mixin = instance.render(); } finally { - CurrentOwner.cur = lastOwn; + Refs.currentOwner = lastOwn; } if (mixin && mixin.render) { //支持module pattern component diff --git a/src/cloneElement.js b/src/cloneElement.js index 637c37ad2..8bc5bd260 100644 --- a/src/cloneElement.js +++ b/src/cloneElement.js @@ -1,11 +1,12 @@ -import { createElement, CurrentOwner } from "./createElement"; +import { createElement } from "./createElement"; +import { Refs} from "./Refs"; export function cloneElement(vnode, props) { if (!vnode.vtype) { return Object.assign({}, vnode); } let owner = vnode._owner, - lastOwn = CurrentOwner.cur, + lastOwn = Refs.currentOwner, old = vnode.props, configs = { }; if (props) { @@ -20,7 +21,7 @@ export function cloneElement(vnode, props) { }else{ configs = old; } - CurrentOwner.cur = owner; + Refs.currentOwner = owner; let args = [].slice.call(arguments, 0), argsLength = args.length; @@ -30,6 +31,6 @@ export function cloneElement(vnode, props) { args.push(configs.children); } let ret = createElement.apply(null, args); - CurrentOwner.cur = lastOwn; + Refs.currentOwner = lastOwn; return ret; } diff --git a/src/createElement.js b/src/createElement.js index 73c7766fa..dd20545e8 100644 --- a/src/createElement.js +++ b/src/createElement.js @@ -1,4 +1,5 @@ import {EMPTY_CHILDREN, typeNumber} from "./util"; +import {Refs} from "./Refs"; export var CurrentOwner = { cur: null @@ -65,34 +66,11 @@ export function createElement(type, config, ...children) { return new Vnode(type, key, ref, props, vtype, checkProps); } -//fix 0.14对此方法的改动,之前refs里面保存的是虚拟DOM -function getDOMNode() { - return this; -} -function errRef() { - throw "ref位置错误"; -} -function createStringRef(owner, ref) { - var stringRef = owner === null - ? errRef - : function (dom) { - if (dom) { - if (dom.nodeType) { - dom.getDOMNode = getDOMNode; - } - owner.refs[ref] = dom; - }else{ - delete owner.refs[ref]; - } - }; - stringRef.string = ref; - return stringRef; -} function Vnode(type, key, ref, props, vtype, checkProps) { this.type = type; this.props = props; this.vtype = vtype; - var owner = CurrentOwner.cur; + var owner = Refs.currentOwner; this._owner = owner; if (key) { @@ -105,10 +83,10 @@ function Vnode(type, key, ref, props, vtype, checkProps) { let refType = typeNumber(ref); if (refType === 4 || refType === 3) { //string, number - this.ref = createStringRef(owner, ref+""); + this.ref = Refs.createStringRef(owner, ref+""); } else if (refType === 5) { if (ref.string) { - var ref2 = createStringRef(owner, ref.string); + var ref2 = Refs.createStringRef(owner, ref.string); this.ref = function (dom) { ref(dom); ref2(dom); diff --git a/src/diff.js b/src/diff.js index 16ad32e68..09e7d9b60 100644 --- a/src/diff.js +++ b/src/diff.js @@ -1,11 +1,12 @@ -import { noop, options, getNodes, innerHTML, toLowerCase, deprecatedWarn, getContextByTypes } from "./util"; +import { options, getNodes, innerHTML, toLowerCase, deprecatedWarn, getContextByTypes } from "./util"; import { diffProps } from "./diffProps"; import { disposeVnode } from "./dispose"; import { createDOMElement, emptyElement, removeDOMElement } from "./browser"; -import { CurrentOwner, flattenChildren } from "./createElement"; +import { flattenChildren } from "./createElement"; import { processFormElement, postUpdateSelectedOptions } from "./ControlledComponent"; -import { instantiateComponent, updateChains } from "./instantiateComponent"; -import { pendingRefs, drainQueue } from "./scheduler"; +import { instantiateComponent, updateChains } from "./Updater"; +import { drainQueue } from "./scheduler"; +import { Refs, pendingRefs } from "./Refs"; //[Top API] React.isValidElement export function isValidElement(vnode) { @@ -76,7 +77,7 @@ function renderByAnu(vnode, container, callback, context = {}) { var instance = vnode._instance; container.__component = vnode; drainQueue(updateQueue); - CurrentOwner.cur = null; //防止干扰 + Refs.currentOwner = null; //防止干扰 var ret = instance || rootNode; if (callback) { callback.call(ret); //坑 @@ -139,7 +140,6 @@ function genMountElement(lastNode, vnode, vparent, type) { return lastNode; } else { let dom = createDOMElement(vnode, vparent); - if (lastNode) { while (lastNode.firstChild) { dom.appendChild(lastNode.firstChild); @@ -208,7 +208,7 @@ function alignChildren(parentNode, children, vparent, context, updateQueue) { } function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue, parentUpdater) { - let { type, props } = vnode; + let { type, props, ref } = vnode; let context = getContextByTypes(parentContext, type.contextTypes); let instance = instantiateComponent(type, vnode, props, context); //互相持有引用 let updater = instance.updater; @@ -236,14 +236,14 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue, pa updater //作为parentUpater往下传 ); }, updater.rendered); - + Refs.createInstanceRef(updater, ref); updateQueue.push(updater); return dom; } function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQueue) { - let { type, ref, _instance: instance, vtype } = lastVnode; + let { type, ref, _instance: instance } = lastVnode; let nextContext, queue, nextProps = nextVnode.props, @@ -262,12 +262,12 @@ function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQue instance.componentWillReceiveProps(nextProps, nextContext); updater._receiving = false; } - //用于refreshComponent - if (ref && vtype === 2) { + if (!instance.__isStateless) { var nextRef = nextVnode.ref; - detachRef(ref, nextRef); - lastVnode.ref = nextRef; + ref && Refs.detachRef(ref, nextRef); + Refs.createInstanceRef(updater, nextRef); } + //updater上总是保持新的数据 updater.lastVnode = lastVnode; updater.vnode = nextVnode; @@ -312,14 +312,11 @@ function refreshComponent(updater, updateQueue) { instance.state = nextState; //既然setState了,无论shouldComponentUpdate结果如何,用户传给的state对象都会作用到组件上 instance.context = nextContext; if (!shouldUpdate) { + updateQueue.push(updater); return dom; } instance.props = nextProps; updater._hydrating = true; - updater.lastProps = lastProps; - updater.lastState = lastState; - updater.lastContext = lastContext; - let lastRendered = updater.rendered; dom = updater.renderComponent(function(nextRendered, vparent, childContext) { @@ -328,13 +325,22 @@ function refreshComponent(updater, updateQueue) { updater.lastVnode = vnode; updater._lifeStage = 2; - // updater._hydrating = false; - if (!updateQueue.isMainProcess) { - drainQueue(updateQueue); + let hookName = "componentDidUpdate"; + let didUpdateHook = instance[hookName]; + if (didUpdateHook) { + instance[hookName] = function() { + didUpdateHook.call(this, lastProps, lastState, lastContext); + this[hookName] = didUpdateHook; + }; } else { - updater._hydrating = false; + //临时添加一个一次性的空钩子 + instance[hookName] = function() { + delete instance[hookName]; + }; } + // updater._hydrating = false; + updateQueue.push(updater); return dom; } options.refreshComponent = refreshComponent; @@ -361,7 +367,6 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { console.log("此节点已经被移除", vparent); return null; } - let { props: nextProps, ref: nextRef } = nextVnode; nextVnode._hostNode = dom; if (nextProps[innerHTML]) { @@ -383,21 +388,10 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { if (nextVnode.type === "select") { postUpdateSelectedOptions(nextVnode); } - detachRef(ref, nextRef, dom); - + Refs.detachRef(ref, nextRef, dom); return dom; } -function detachRef(ref, nextRef, dom) { - ref = ref || noop; - if (nextRef) { - if (!ref.string && !nextRef.string ? ref !== nextRef : ref.string !== nextRef.string) { - ref(null); - } - dom && nextRef(dom); - }else{ - ref(null); - } -} + function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { let lastChildren = parentNode.vchildren, nextChildren = flattenChildren(nextVnode), diff --git a/src/dispose.js b/src/dispose.js index 931929002..2a8e531c7 100644 --- a/src/dispose.js +++ b/src/dispose.js @@ -1,6 +1,6 @@ import { options, noop, innerHTML } from "./util"; import { removeDOMElement } from "./browser"; -import { updateChains } from "./instantiateComponent"; +import { updateChains } from "./Updater"; export function disposeVnode(vnode) { if (!vnode || vnode._disposed) { diff --git a/src/event.js b/src/event.js index 782970586..244c4e82c 100644 --- a/src/event.js +++ b/src/event.js @@ -88,8 +88,6 @@ export function addGlobalEvent(name) { export function addEvent(el, type, fn, bool) { if (el.addEventListener) { - // Unable to preventDefault inside passive event listener due to target being - // treated as passive el.addEventListener( type, fn, diff --git a/src/scheduler.js b/src/scheduler.js index 41de880d3..87e158f90 100644 --- a/src/scheduler.js +++ b/src/scheduler.js @@ -2,69 +2,53 @@ import { options, clearArray } from "./util"; +import { + Refs +} from "./Refs"; + -export const pendingRefs = []; -function clearRefs() { - var refs = pendingRefs.slice(0); - pendingRefs.length = 0; - refs.forEach(function(fn) { - fn(); - }); -} function callUpdate(updater, instance) { - if(pendingRefs.length){ - clearRefs(); - } - if (updater._lifeStage === 2) { - if (instance.componentDidUpdate) { - updater._didUpdate = true; - instance.componentDidUpdate( - updater.lastProps, - updater.lastState, - updater.lastContext - ); - if (!updater._renderInNextCycle) { - updater._didUpdate = false; - } + Refs.clearRefs(); + if (updater._lifeStage === 2) { + updater._didUpdate = true; + instance.componentDidUpdate(); + if (!updater._renderInNextCycle) { + updater._didUpdate = false; } - options.afterUpdate(instance); - updater._hydrating = false; updater._lifeStage = 1; + updater._hydrating = false; + options.afterUpdate(instance); } + updater._ref(); } export function drainQueue(queue) { options.beforePatch(); //先执行所有refs方法(从上到下) - clearRefs(); + Refs.clearRefs(); //再执行所有mount/update钩子(从下到上) let i = 0; while(i < queue.length){//queue可能中途加入新元素, 因此不能直接使用queue.forEach(fn) var updater = queue[i], instance = updater._instance; i++; if (!updater._lifeStage) { - if(pendingRefs.length){ - clearRefs(); - } + Refs.clearRefs(); + instance.newStageAddedRefs = instance.newStageAddedRefs || {}; if (instance.componentDidMount) { instance.componentDidMount(); instance.componentDidMount = null; } updater._lifeStage = 1; - options.afterMount(instance); updater._hydrating = false; + updater._ref(); + options.afterMount(instance); } else { callUpdate(updater, instance); } - var ref = updater.vnode.ref; - if (ref) { - ref(instance.__isStateless ? null: instance); - } - // updater._hydrating = false; //子树已经构建完毕 - while (updater._renderInNextCycle) { - + //如果组件在componentDidMount中调用setState + if (updater._renderInNextCycle) { options.refreshComponent(updater, queue); - callUpdate(updater, instance); + // callUpdate(updater, instance); } } //再执行所有setState/forceUpdate回调,根据从下到上的顺序执行 @@ -77,16 +61,17 @@ export function drainQueue(queue) { options.afterPatch(); } -//有一个列队, 先放进A组件与A组件回调 var dirtyComponents = []; - -function mountSorter(u1, u2) {//让子节点先于父节点执行 - return u2._mountIndex - u1._mountIndex; +function mountSorter(u1, u2) {//按文档顺序执行 + return u1._mountIndex - u2._mountIndex; } options.flushUpdaters = function(queue) { if (!queue) { queue = dirtyComponents; + if(queue.length) { + queue.sort(mountSorter); + } } drainQueue(queue); }; diff --git a/test/modules/ReactComponent-test.jsx b/test/modules/ReactComponent-test.jsx index 0c4992e89..bcca2fc6d 100644 --- a/test/modules/ReactComponent-test.jsx +++ b/test/modules/ReactComponent-test.jsx @@ -6,432 +6,418 @@ import ReactDOMServer from "dist/ReactDOMServer"; //https://github.com/facebook/react/blob/master/src/isomorphic/children/__tests__/ReactChildren-test.js var ReactDOM = window.ReactDOM || React; describe("ReactComponent", function() { - this.timeout(200000); - - it("should throw on invalid render targets", () => { - var container = document.createElement("div"); - // jQuery objects are basically arrays; people often pass them in by mistake - expect(function() { - ReactDOM.render(
, [container]); - }).toThrowError(/Target container is not a DOM element./); - - expect(function() { - ReactDOM.render(
, null); - }).toThrowError(/Target container is not a DOM element./); - }); - - it("should throw when supplying a ref outside of render method", () => { - var instance =
- expect(function() { - instance = ReactTestUtils.renderIntoDocument(instance); - }).toThrow() - }); - - it("should warn when children are mutated during render", () => { - function Wrapper(props) { - props.children[1] =

; // Mutation is illegal - return

{props.children}
; - } - - var instance = ReactTestUtils.renderIntoDocument( - - - - - - ); - expect( - ReactTestUtils.scryRenderedDOMComponentsWithTag(instance, "p").length - ).toBe(1); - }); - - it("should warn when children are mutated during update", () => { - class Wrapper extends React.Component { - componentDidMount() { - this.props.children[1] =

; // Mutation is illegal - this.forceUpdate(); - } - - render() { - return

{this.props.children}
; - } - } - - var instance = ReactTestUtils.renderIntoDocument( - - - - - - ); - expect( - ReactTestUtils.scryRenderedDOMComponentsWithTag(instance, "p").length - ).toBe(1); - }); - - it("should support refs on owned components", () => { - var innerObj = {}; - var outerObj = {}; - - class Wrapper extends React.Component { - getObject = () => { - return this.props.object; - }; - - render() { - return
{this.props.children}
; - } - } - - class Component extends React.Component { - render() { - var inner = ; - var outer = ( - - {inner} - + this.timeout(200000); + + it("should throw on invalid render targets", () => { + var container = document.createElement("div"); + // jQuery objects are basically arrays; people often pass them in by mistake + expect(function() { + ReactDOM.render(
, [container]); + }).toThrowError(/Target container is not a DOM element./); + + expect(function() { + ReactDOM.render(
, null); + }).toThrowError(/Target container is not a DOM element./); + }); + + it("should throw when supplying a ref outside of render method", () => { + var instance =
; + expect(function() { + instance = ReactTestUtils.renderIntoDocument(instance); + }).toThrow(); + }); + + it("should warn when children are mutated during render", () => { + function Wrapper(props) { + props.children[1] =

; // Mutation is illegal + return

{props.children}
; + } + + var instance = ReactTestUtils.renderIntoDocument( + + + + + ); - return outer; - } - - componentDidMount() { - expect(this.refs.inner.getObject()).toEqual(innerObj); - expect(this.refs.outer.getObject()).toEqual(outerObj); - } - } - - ReactTestUtils.renderIntoDocument(); - }); - - it("should not have refs on unmounted components", () => { - class Parent extends React.Component { - render() { - return ( - -
- + expect(ReactTestUtils.scryRenderedDOMComponentsWithTag(instance, "p").length).toBe(1); + }); + + it("should warn when children are mutated during update", () => { + class Wrapper extends React.Component { + componentDidMount() { + this.props.children[1] =

; // Mutation is illegal + this.forceUpdate(); + } + + render() { + return

{this.props.children}
; + } + } + + var instance = ReactTestUtils.renderIntoDocument( + + + + + ); - } - - componentDidMount() { - expect(this.refs && this.refs.test).toEqual(undefined); - } - } - - class Child extends React.Component { - render() { - return
; - } - } - - ReactTestUtils.renderIntoDocument(} />); - }); - - it("should support new-style refs", () => { - var innerObj = {}; - var outerObj = {}; - - class Wrapper extends React.Component { - getObject = () => { - return this.props.object; - }; - - render() { - return
{this.props.children}
; - } - } - - var mounted = false; - - class Component extends React.Component { - render() { - var inner = ( - (this.innerRef = c)} /> + expect(ReactTestUtils.scryRenderedDOMComponentsWithTag(instance, "p").length).toBe(1); + }); + + it("should support refs on owned components", () => { + var innerObj = {}; + var outerObj = {}; + + class Wrapper extends React.Component { + getObject = () => { + return this.props.object; + }; + + render() { + return
{this.props.children}
; + } + } + + class Component extends React.Component { + render() { + var inner = ; + var outer = ( + + {inner} + + ); + return outer; + } + + componentDidMount() { + expect(this.refs.inner.getObject()).toEqual(innerObj); + expect(this.refs.outer.getObject()).toEqual(outerObj); + } + } + + ReactTestUtils.renderIntoDocument(); + }); + + it("should not have refs on unmounted components", () => { + class Parent extends React.Component { + render() { + return ( + +
+ + ); + } + + componentDidMount() { + expect(this.refs && this.refs.test).toEqual(undefined); + } + } + + class Child extends React.Component { + render() { + return
; + } + } + + ReactTestUtils.renderIntoDocument(} />); + }); + + it("should support new-style refs", () => { + var innerObj = {}; + var outerObj = {}; + + class Wrapper extends React.Component { + getObject = () => { + return this.props.object; + }; + + render() { + return
{this.props.children}
; + } + } + + var mounted = false; + + class Component extends React.Component { + render() { + var inner = (this.innerRef = c)} />; + var outer = ( + (this.outerRef = c)}> + {inner} + + ); + return outer; + } + + componentDidMount() { + expect(this.innerRef.getObject()).toEqual(innerObj); + expect(this.outerRef.getObject()).toEqual(outerObj); + mounted = true; + } + } + + ReactTestUtils.renderIntoDocument(); + expect(mounted).toBe(true); + }); + + it("should support new-style refs with mixed-up owners", () => { + class Wrapper extends React.Component { + getTitle = () => { + return this.props.title; + }; + + render() { + return this.props.getContent(); + } + } + + var mounted = false; + + class Component extends React.Component { + getInner = () => { + // (With old-style refs, it's impossible to get a ref to this div + // because Wrapper is the current owner when this function is called.) + return
(this.innerRef = c)} />; + }; + + render() { + return (this.wrapperRef = c)} getContent={this.getInner} />; + } + + componentDidMount() { + // Check .props.title to make sure we got the right elements back + expect(this.wrapperRef.getTitle()).toBe("wrapper"); + expect(ReactDOM.findDOMNode(this.innerRef).className).toBe("inner"); + mounted = true; + } + } + + ReactTestUtils.renderIntoDocument(); + expect(mounted).toBe(true); + }); + + it("should call refs at the correct time", () => { + var log = []; + + class Inner extends React.Component { + render() { + log.push(`inner ${this.props.id} render`); + return
; + } + + componentDidMount() { + log.push(`inner ${this.props.id} componentDidMount`); + } + + componentDidUpdate() { + log.push(`inner ${this.props.id} componentDidUpdate`); + } + + componentWillUnmount() { + log.push(`inner ${this.props.id} componentWillUnmount`); + } + } + + class Outer extends React.Component { + render() { + return ( +
+ { + log.push(`ref 1 got ${c ? `instance ${c.props.id}` : "null"}`); + }} + /> + { + log.push(`ref 2 got ${c ? `instance ${c.props.id}` : "null"}`); + }} + /> +
+ ); + } + + componentDidMount() { + log.push("outer componentDidMount"); + } + + componentDidUpdate() { + log.push("outer componentDidUpdate"); + } + + componentWillUnmount() { + log.push("outer componentWillUnmount"); + } + } + + // mount, update, unmount + var container = document.createElement("div"); + log.push("start mount"); + ReactDOM.render(, container); + log.push("start update"); + ReactDOM.render(, container); + log.push("start unmount"); + ReactDOM.unmountComponentAtNode(container); + + /* eslint-disable indent */ + expect(log).toEqual([ + "start mount", + "inner 1 render", + "inner 2 render", + "inner 1 componentDidMount", + "ref 1 got instance 1", + "inner 2 componentDidMount", + "ref 2 got instance 2", + "outer componentDidMount", + "start update", + + // Stack resets refs before rendering + "ref 1 got null", + "inner 1 render", + "ref 2 got null", + "inner 2 render", + + "inner 1 componentDidUpdate", + "ref 1 got instance 1", + "inner 2 componentDidUpdate", + "ref 2 got instance 2", + "outer componentDidUpdate", + "start unmount", + "outer componentWillUnmount", + "ref 1 got null", + "inner 1 componentWillUnmount", + "ref 2 got null", + "inner 2 componentWillUnmount" + ]); + }); + + it("throws usefully when rendering badly-typed elements", () => { + spyOn(console, "error"); + + var X = undefined; + expect(() => ReactTestUtils.renderIntoDocument()).toThrowError( + "Element type is invalid: expected a string (for built-in components) " + + "or a class/function (for composite components) but got: undefined. " + + "You likely forgot to export your component from the file it's " + + "defined in." ); - var outer = ( - (this.outerRef = c)}> - {inner} - - ); - return outer; - } - - componentDidMount() { - expect(this.innerRef.getObject()).toEqual(innerObj); - expect(this.outerRef.getObject()).toEqual(outerObj); - mounted = true; - } - } - - ReactTestUtils.renderIntoDocument(); - expect(mounted).toBe(true); - }); - - it("should support new-style refs with mixed-up owners", () => { - class Wrapper extends React.Component { - getTitle = () => { - return this.props.title; - }; - - render() { - return this.props.getContent(); - } - } - - var mounted = false; - - class Component extends React.Component { - getInner = () => { - // (With old-style refs, it's impossible to get a ref to this div - // because Wrapper is the current owner when this function is called.) - return
(this.innerRef = c)} />; - }; - - render() { - return ( - (this.wrapperRef = c)} - getContent={this.getInner} - /> + + var Y = null; + expect(() => ReactTestUtils.renderIntoDocument()).toThrowError( + "Element type is invalid: expected a string (for built-in components) " + "or a class/function (for composite components) but got: null." ); - } - - componentDidMount() { - // Check .props.title to make sure we got the right elements back - expect(this.wrapperRef.getTitle()).toBe("wrapper"); - expect(ReactDOM.findDOMNode(this.innerRef).className).toBe("inner"); - mounted = true; - } - } - - ReactTestUtils.renderIntoDocument(); - expect(mounted).toBe(true); - }); - it("should call refs at the correct time", () => { - - var log = []; - - class Inner extends React.Component { - render() { - log.push(`inner ${this.props.id} render`); - return
; - } - - componentDidMount() { - log.push(`inner ${this.props.id} componentDidMount`); - } - - componentDidUpdate() { - log.push(`inner ${this.props.id} componentDidUpdate`); - } - - componentWillUnmount() { - log.push(`inner ${this.props.id} componentWillUnmount`); - } - } - - class Outer extends React.Component { - render() { - return ( -
- { - log.push(`ref 1 got ${c ? `instance ${c.props.id}` : "null"}`); - }} - /> - { - log.push(`ref 2 got ${c ? `instance ${c.props.id}` : "null"}`); - }} - /> -
+ + // One warning for each element creation + expect(console.error.calls.count()).toBe(2); + }); + + it("includes owner name in the error about badly-typed elements", () => { + spyOn(console, "error"); + + var X = undefined; + + function Indirection(props) { + return
{props.children}
; + } + + function Bar() { + return ( + + + + ); + } + + function Foo() { + return ; + } + + expect(() => ReactTestUtils.renderIntoDocument()).toThrowError( + "Element type is invalid: expected a string (for built-in components) " + + "or a class/function (for composite components) but got: undefined. " + + "You likely forgot to export your component from the file it's " + + "defined in.\n\nCheck the render method of `Bar`." ); - } - - componentDidMount() { - log.push("outer componentDidMount"); - } - - componentDidUpdate() { - log.push("outer componentDidUpdate"); - } - - componentWillUnmount() { - log.push("outer componentWillUnmount"); - } - } - - // mount, update, unmount - var el = document.createElement("div"); - log.push("start mount"); - ReactDOM.render(, el); - log.push("start update"); - ReactDOM.render(, el); - log.push("start unmount"); - ReactDOM.unmountComponentAtNode(el); - - /* eslint-disable indent */ - expect(log).toEqual([ - "start mount", - "inner 1 render", - "inner 2 render", - "inner 1 componentDidMount", - "ref 1 got instance 1", - "inner 2 componentDidMount", - "ref 2 got instance 2", - "outer componentDidMount", - "start update", - - // Stack resets refs before rendering - "ref 1 got null", - "inner 1 render", - "ref 2 got null", - "inner 2 render", - - "inner 1 componentDidUpdate", - "ref 1 got instance 1", - "inner 2 componentDidUpdate", - "ref 2 got instance 2", - "outer componentDidUpdate", - "start unmount", - "outer componentWillUnmount", - "ref 1 got null", - "inner 1 componentWillUnmount", - "ref 2 got null", - "inner 2 componentWillUnmount" - ]); - /* eslint-enable indent */ - }); - - it('throws usefully when rendering badly-typed elements', () => { - spyOn(console, 'error'); - - var X = undefined; - expect(() => ReactTestUtils.renderIntoDocument()).toThrowError( - 'Element type is invalid: expected a string (for built-in components) ' + - 'or a class/function (for composite components) but got: undefined. ' + - "You likely forgot to export your component from the file it's " + - 'defined in.', - ); - - var Y = null; - expect(() => ReactTestUtils.renderIntoDocument()).toThrowError( - 'Element type is invalid: expected a string (for built-in components) ' + - 'or a class/function (for composite components) but got: null.', - ); - - // One warning for each element creation - expect(console.error.calls.count()).toBe(2); - }); - - it('includes owner name in the error about badly-typed elements', () => { - spyOn(console, 'error'); - - var X = undefined; - - function Indirection(props) { - return
{props.children}
; - } - - function Bar() { - return ; - } - - function Foo() { - return ; - } - - expect(() => ReactTestUtils.renderIntoDocument()).toThrowError( - 'Element type is invalid: expected a string (for built-in components) ' + - 'or a class/function (for composite components) but got: undefined. ' + - "You likely forgot to export your component from the file it's " + - 'defined in.\n\nCheck the render method of `Bar`.', - ); - - // One warning for each element creation - expect(console.error.calls.count()).toBe(1); - }); - - - it('throws if a plain object is used as a child', () => { - var children = { - x: , - y: , - z: , - }; - var element =
{[children]}
; - var container = document.createElement('div'); - var ex; - try { - ReactDOM.render(element, container); - } catch (e) { - ex = e; - } - expect(ex).toBeDefined(); - - }); -it('throws if a plain object even if it is in an owner', () => { - class Foo extends React.Component { - render() { + + // One warning for each element creation + expect(console.error.calls.count()).toBe(1); + }); + + it("throws if a plain object is used as a child", () => { var children = { - a: , - b: , - c: , + x: , + y: , + z: }; - return
{[children]}
; - } - } - var container = document.createElement('div'); - var ex; - try { - ReactDOM.render(, container); - } catch (e) { - ex = e; - } - expect(ex).toBeDefined(); - - }); - - it('throws if a plain object is used as a child when using SSR', async () => { - var children = { - x: , - y: , - z: , - }; - var element =
{[children]}
; - var ex; - try { - ReactDOMServer.renderToString(element); - } catch (e) { - ex = e; - console.warn(e) - } - expect(ex).toBeDefined(); - }); - - it('throws if a plain object even if it is in an owner when using SSR', async () => { - class Foo extends React.Component { - render() { + var element =
{[children]}
; + var container = document.createElement("div"); + var ex; + try { + ReactDOM.render(element, container); + } catch (e) { + ex = e; + } + expect(ex).toBeDefined(); + }); + it("throws if a plain object even if it is in an owner", () => { + class Foo extends React.Component { + render() { + var children = { + a: , + b: , + c: + }; + return
{[children]}
; + } + } + var container = document.createElement("div"); + var ex; + try { + ReactDOM.render(, container); + } catch (e) { + ex = e; + } + expect(ex).toBeDefined(); + }); + + it("throws if a plain object is used as a child when using SSR", async () => { var children = { - a: , - b: , - c: , + x: , + y: , + z: }; - return
{[children]}
; - } - } - var container = document.createElement('div'); - var ex; - try { - ReactDOMServer.renderToString(, container); - } catch (e) { - ex = e; - console.warn(e) - } - expect(ex).toBeDefined(); - - }); + var element =
{[children]}
; + var ex; + try { + ReactDOMServer.renderToString(element); + } catch (e) { + ex = e; + console.warn(e); + } + expect(ex).toBeDefined(); + }); + + it("throws if a plain object even if it is in an owner when using SSR", async () => { + class Foo extends React.Component { + render() { + var children = { + a: , + b: , + c: + }; + return
{[children]}
; + } + } + var container = document.createElement("div"); + var ex; + try { + ReactDOMServer.renderToString(, container); + } catch (e) { + ex = e; + console.warn(e); + } + expect(ex).toBeDefined(); + }); }); diff --git a/test/modules/ReactCompositeComponentNestedState-test.jsx b/test/modules/ReactCompositeComponentNestedState-test.jsx index 7dd0732b5..cb5cd444b 100644 --- a/test/modules/ReactCompositeComponentNestedState-test.jsx +++ b/test/modules/ReactCompositeComponentNestedState-test.jsx @@ -106,8 +106,9 @@ describe("ReactCompositeComponentNestedState-state", function() { ['setState-this', 'dark blue', 'blue'], ['setState-args', 'dark blue', 'green'], ['render', 'light green', 'green'], - ['after-setState', 'light green', 'green'], - ['parent-after-setState', 'green'] + ['parent-after-setState', 'green'], + ['after-setState', 'light green', 'green'] + ]); }); diff --git a/test/modules/lifecycle.spec.jsx b/test/modules/lifecycle.spec.jsx index 74ddd044c..97cf378e3 100644 --- a/test/modules/lifecycle.spec.jsx +++ b/test/modules/lifecycle.spec.jsx @@ -1,655 +1,653 @@ -import { - beforeHook, - afterHook, - browser -} from "karma-event-driver-ext/cjs/event-driver-hooks"; + import React from "dist/React"; -import { SyntheticEvent, addEvent } from "src/event"; -import { DOMElement } from "src/browser"; +import ReactTestUtils from "lib/ReactTestUtils"; describe("生命周期例子", function() { - this.timeout(200000); - before(async () => { - await beforeHook(); - }); - after(async () => { - await afterHook(false); - }); - var body = document.body, - div; - beforeEach(function() { - div = document.createElement("div"); - body.appendChild(div); - }); - afterEach(function() { - body.removeChild(div); - }); - it("如果在componentDidMount中调用setState方法\n那么setState的所有回调,\n都会延迟到componentDidUpdate中执行", async () => { - var list = []; - class App extends React.Component { - constructor(props) { - super(props); - this.state = { - aaa: "aaa" - }; - } - componentWillMount() { - this.setState( - { - aaa: "bbb" - }, - function() { - list.push("1111"); - } - ); - } - componentDidMount() { - this.setState( - { - aaa: "cccc" - }, - function() { - list.push("2222"); - } - ); - this.setState( - { - aaa: "dddd" - }, - function() { - list.push("3333"); - } - ); - list.push("did mount"); - } - - componentWillUpdate() { - list.push("will update"); - } - componentDidUpdate() { - list.push("did update"); - } - render() { - list.push(this.state.aaa); - return
{this.state.aaa}
; - } - } - - var s = ReactDOM.render(, div); - await browser.pause(300).$apply(); - expect(list.join("-")).toBe( - "bbb-did mount-will update-dddd-did update-1111-2222-3333" - ); - }); - it("父组件没有DidMount之时被子组件在willMount钩子里调用其setState", async () => { - var list = []; - class App extends React.Component { - constructor(props) { - super(props); - this.state = { - aaa: "app render" - }; - } - componentWillMount() { - list.push("app will mount"); - } - componentDidMount() { - list.push("app did mount"); - } - - componentWillUpdate() { - list.push("app will update"); - } - componentDidUpdate() { - list.push("app did update"); - } - render() { - list.push(this.state.aaa); - return ( - - ); - } - } - class A extends React.Component { - componentWillMount() { - this.props.parent.setState({ - aaa: "app new render" - }); - this.props.parent.setState({ - aaa: "app new render2" - }); - } - componentWillReceiveProps() { - list.push("child receive"); - } - render() { - return

A

; - } - } - var s = ReactDOM.render(, div); - await browser.pause(300).$apply(); - expect(list.join("-")).toBe( - "app will mount-app render-app did mount-app will update-app new render2-child receive-app did update" - ); - }); - - it("父组件DidMount之时被子组件在componentWillReceiveProps钩子里调用其setState\n父组件的再次render会待到这次render完才调起", async () => { - var list = []; - class App extends React.Component { - constructor(props) { - super(props); - this.state = { - aaa: "app render" - }; - } - componentWillMount() { - list.push("app will mount"); - } - componentDidMount() { - this.setState({ - aaa: "app render1" - }); - list.push("app did mount"); - } - componentDidUpdate() { - list.push("app did update"); - } - render() { - list.push(this.state.aaa); - return ( -
- ); - } - } - var a = 1; - class C extends React.Component { - render() { - list.push("C render"); - return

C

; - } - } - class A extends React.Component { - componentWillReceiveProps() { - if (a < 2) { - this.props.parent.setState( - { - aaa: "child call app render " + ++a - }, - function() { - list.push("componentWillReceiveProps 1"); - } - ); - this.props.parent.setState( - { - aaa: "child call app render " + ++a - }, - function() { - list.push("componentWillReceiveProps 2"); - } - ); + this.timeout(200000); + + var body = document.body, + div; + beforeEach(function() { + div = document.createElement("div"); + body.appendChild(div); + }); + afterEach(function() { + body.removeChild(div); + }); + it("如果在componentDidMount中调用setState方法\n那么setState的所有回调,\n都会延迟到componentDidUpdate中执行", function() { + var list = []; + class App extends React.Component { + constructor(props) { + super(props); + this.state = { + aaa: "aaa" + }; + } + componentWillMount() { + this.setState( + { + aaa: "bbb" + }, + function() { + list.push("1111"); + } + ); + } + componentDidMount() { + this.setState( + { + aaa: "cccc" + }, + function() { + list.push("2222"); + } + ); + this.setState( + { + aaa: "dddd" + }, + function() { + list.push("3333"); + } + ); + list.push("did mount"); + } + + componentWillUpdate() { + list.push("will update"); + } + componentDidUpdate() { + list.push("did update"); + } + render() { + list.push(this.state.aaa); + return
{this.state.aaa}
; + } } - } - componentWillUpdate() { - list.push("child will update"); - } - render() { - list.push("child render"); - return

A

; - } - } - React.render(, div); - await browser.pause(200).$apply(); - - var list2 = [ - "app will mount", - "app render", - "child render", - "C render", - "app did mount", - "app render1", - "child will update", - "child render", - "C render", - "app did update", - "child call app render 3", - "child will update", - "child render", - "C render", - "app did update", - "componentWillReceiveProps 1", - "componentWillReceiveProps 2" - ]; - expect(list.join("-")).toBe(list2.join("-")); - }); - - it("第一次渲染时不会触发componentWillUpdate", async () => { - var a = 1; - class ReceivePropsComponent extends React.Component { - componentWillUpdate() { - a = 2; - } - render() { - return
; - } - } - - React.render(, div); - await browser.pause(200).$apply(); - expect(a).toBe(1); - }); - - it("先执行子组件的mount钩子再到父组件的mount钩子", async () => { - let log = []; - - class Inner extends React.Component { - componentDidMount() { - log.push("inner"); - } - - render() { - return
; - } - } - - class Outer extends React.Component { - componentDidMount() { - log.push("outer"); - } - - render(props) { - return ; - } - } - - React.render(, div); - await browser.pause(200).$apply(); - expect(log.join("-")).toBe("inner-outer"); - }); - it("在componentWillMount中使用setState", async () => { - var list = []; - class App extends React.Component { - constructor(props) { - super(props); - this.state = { - aaa: 111 - }; - } - componentWillMount() { - this.setState( - { - aaa: 222 - }, - function() { - list.push("555"); - } - ); - this.setState( - { - aaa: 333 - }, - function() { - list.push("666"); - } - ); - } - render() { - list.push(this.state.aaa); - return

{this.state.aaa}

; - } - } - - var s = React.render(, div); - await browser.pause(200).$apply(); - expect(list.join("-")).toBe("333-555-666"); - expect(div.textContent || div.innerText).toBe("333"); - }); - it("在componentWillMount中使用setState", async () => { - var list = []; - class App extends React.Component { - constructor(props) { - super(props); - this.state = { - aaa: 111 - }; - } - componentWillMount() { - this.setState( - { - aaa: 222 - }, - function() { - list.push("555"); - } - ); - this.setState( - { - aaa: 333 - }, - function() { - list.push("666"); - } - ); - } - render() { - list.push(this.state.aaa); - return

{this.state.aaa}

; - } - } - - var s = React.render(, div); - await browser.pause(200).$apply(); - expect(list.join("-")).toBe("333-555-666"); - expect(div.textContent || div.innerText).toBe("333"); - }); - it("在componentDidMount中使用setState,会导致willMount, DidMout中的回调都延后", async () => { - var list = []; - class App extends React.Component { - constructor(props) { - super(props); - this.state = { - aaa: 111 - }; - } - - componentWillMount() { - this.setState( - { - aaa: 222 - }, - function() { - list.push("555"); - } - ); - this.setState( - { - aaa: 333 - }, - function() { - list.push("666"); - } - ); - } - componentDidMount() { - this.setState( - { - aaa: 444 - }, - function() { - list.push("777"); - } - ); - } - - render() { - list.push(this.state.aaa); - return

{this.state.aaa}

; - } - } - - var s = React.render(, div); - await browser.pause(200).$apply(); - expect(list.join("-")).toBe("333-444-555-666-777"); - expect(div.textContent || div.innerText).toBe("444"); - }); - - it("ReactDOM的回调总在最后", async () => { - var list = []; - class App extends React.Component { - constructor(props) { - super(props); - this.state = { - path: "111" - }; - } - componentWillMount() { - this.setState( - { - path: "222" - }, - function() { - list.push("componentWillMount cb"); - } - ); - this.setState( - { - path: "2222" - }, - function() { - list.push("componentWillMount cb2"); - } - ); - } - render() { - list.push("render " + this.state.path); - return ( -
- - {this.state.path} - - -
- ); - } - componentDidMount() { - this.setState( - { - path: "eeee" - }, - function() { - list.push("componentDidMount cb"); - } + + var s = ReactDOM.render(, div); + expect(list.join("-")).toBe( + "bbb-did mount-will update-dddd-did update-1111-2222-3333" ); - } - componentWillUpdate() { - list.push("will update"); - } - componentDidUpdate() { - list.push("did update"); - } - } - class Child extends React.Component { - componentWillMount() { - this.props.parent.setState( - { - path: "child" - }, - function() { - list.push("child setState"); - } + }); + it("父组件没有DidMount之时被子组件在willMount钩子里调用其setState", function() { + var list = []; + class App extends React.Component { + constructor(props) { + super(props); + this.state = { + aaa: "app render" + }; + } + componentWillMount() { + list.push("app will mount"); + } + componentDidMount() { + list.push("app did mount"); + } + + componentWillUpdate() { + list.push("app will update"); + } + componentDidUpdate() { + list.push("app did update"); + } + render() { + list.push(this.state.aaa); + return ( +
+ ); + } + } + class A extends React.Component { + componentWillMount() { + this.props.parent.setState({ + aaa: "app new render" + }); + this.props.parent.setState({ + aaa: "app new render2" + }); + } + componentWillReceiveProps() { + list.push("child receive"); + } + render() { + return

A

; + } + } + var s = ReactDOM.render(, div); + expect(list.join("-")).toBe( + "app will mount-app render-app did mount-app will update-app new render2-child receive-app did update" ); - } - render() { - list.push("child render"); - return

33333

; - } - } - ReactDOM.render(, div, function() { - list.push("ReactDOM cb"); }); - expect(list).toEqual([ - "render 2222", - "child render", - "will update", - "render eeee", - "child render", - "did update", - "componentWillMount cb", - "componentWillMount cb2", - "child setState", - "componentDidMount cb", - "ReactDOM cb" - ]); - }); - - it("should update state when called from child cWRP", async () => { - const log = []; - class Parent extends React.Component { - constructor() { - super(), - (this.state = { - value: "one" - }); - } - render() { - log.push("parent render " + this.state.value); - return ; - } - } - let updated = false; - class Child extends React.Component { - componentWillReceiveProps() { - if (updated) { - return; + it("父组件DidMount之时被子组件在componentWillReceiveProps钩子里调用其setState\n父组件的再次render会待到这次render完才调起", function() { + var list = []; + class App extends React.Component { + constructor(props) { + super(props); + this.state = { + aaa: "app render" + }; + } + componentWillMount() { + list.push("app will mount"); + } + componentDidMount() { + this.setState({ + aaa: "app render1" + }); + list.push("app did mount"); + } + componentDidUpdate() { + list.push("app did update"); + } + render() { + list.push(this.state.aaa); + return ( +
+ ); + } } - log.push("child componentWillReceiveProps " + this.props.value); - this.props.parent.setState({ value: "two" }); - log.push("child componentWillReceiveProps done " + this.props.value); - updated = true; - } - render() { - log.push("child render " + this.props.value); - return
{this.props.value}
; - } - } - var container = document.createElement("div"); - React.render(, container); - - React.render(, container); - - expect(log).toEqual([ - "parent render one", - "child render one", - "parent render one", - "child componentWillReceiveProps one", - "child componentWillReceiveProps done one", - "child render one", - "parent render two", - "child render two" - ]); - }); - - it("在componentWillUnmount中setState应该不起作用", async () => { - class Issue extends React.PureComponent { - constructor(props) { - super(props); - this.state = { - status: "normal" - }; - } - - componentWillUnmount() { - this.setState({ status: "unmount" }); - } - - render() { - const { status } = this.state; - - if (status === "unmount") { - throw new Error("Issue unmounted"); + var a = 1; + class C extends React.Component { + render() { + list.push("C render"); + return

C

; + } } + class A extends React.Component { + componentWillReceiveProps() { + if (a < 2) { + this.props.parent.setState( + { + aaa: "child call app render " + ++a + }, + function() { + list.push("componentWillReceiveProps 1"); + } + ); + this.props.parent.setState( + { + aaa: "child call app render " + ++a + }, + function() { + list.push("componentWillReceiveProps 2"); + } + ); + } + } + componentWillUpdate() { + list.push("child will update"); + } + render() { + list.push("child render"); + return

A

; + } + } + React.render(, div); + + var list2 = [ + "app will mount", + "app render", + "child render", + "C render", + "app did mount", + "app render1", + "child will update", + "child render", + "C render", + "app did update", + "child call app render 3", + "child will update", + "child render", + "C render", + "app did update", + "componentWillReceiveProps 1", + "componentWillReceiveProps 2" + ]; + expect(list.join("-")).toBe(list2.join("-")); + }); - return Issue status: {status}; - } - } - - class App extends React.PureComponent { - constructor(props) { - super(props); - this.state = { - showIssue: true - }; - } - - render() { - const { showIssue } = this.state; - - return ( -
- {showIssue && } - -
- ); - } - } - var s = React.render(, div); - await browser.pause(200).$apply(); - expect(div.getElementsByTagName("span").length).toBe(1); - await browser - .click(s.refs.button) - .pause(100) - .$apply(); - expect(div.getElementsByTagName("span").length).toBe(0); - }); - - it("forceUpdate在componentDidMount中使用", async () => { - var list = []; - class App extends React.Component { - constructor(props) { - super(props); - this.state = { - aaa: "aaa" - }; - } - componentWillMount() { - this.setState( - { - aaa: "bbb" - }, - function() { - list.push("1111"); - } - ); - } - componentDidMount() { - this.state.aaa = "cccc"; - this.forceUpdate(function() { - list.push("2222"); + it("第一次渲染时不会触发componentWillUpdate", function() { + var a = 1; + class ReceivePropsComponent extends React.Component { + componentWillUpdate() { + a = 2; + } + render() { + return
; + } + } + + React.render(, div); + expect(a).toBe(1); + }); + + it("先执行子组件的mount钩子再到父组件的mount钩子", function() { + let log = []; + + class Inner extends React.Component { + componentDidMount() { + log.push("inner"); + } + + render() { + return
; + } + } + + class Outer extends React.Component { + componentDidMount() { + log.push("outer"); + } + + render(props) { + return ; + } + } + + React.render(, div); + expect(log.join("-")).toBe("inner-outer"); + }); + it("在componentWillMount中使用setState", function() { + var list = []; + class App extends React.Component { + constructor(props) { + super(props); + this.state = { + aaa: 111 + }; + } + componentWillMount() { + this.setState( + { + aaa: 222 + }, + function() { + list.push("555"); + } + ); + this.setState( + { + aaa: 333 + }, + function() { + list.push("666"); + } + ); + } + render() { + list.push(this.state.aaa); + return

{this.state.aaa}

; + } + } + + var s = React.render(, div); + + expect(list.join("-")).toBe("333-555-666"); + expect(div.textContent || div.innerText).toBe("333"); + }); + it("在componentWillMount中使用setState", function() { + var list = []; + class App extends React.Component { + constructor(props) { + super(props); + this.state = { + aaa: 111 + }; + } + componentWillMount() { + this.setState( + { + aaa: 222 + }, + function() { + list.push("555"); + } + ); + this.setState( + { + aaa: 333 + }, + function() { + list.push("666"); + } + ); + } + render() { + list.push(this.state.aaa); + return

{this.state.aaa}

; + } + } + + var s = React.render(, div); + expect(list.join("-")).toBe("333-555-666"); + expect(div.textContent || div.innerText).toBe("333"); + }); + it("在componentDidMount中使用setState,会导致willMount, DidMout中的回调都延后",function() { + var list = []; + class App extends React.Component { + constructor(props) { + super(props); + this.state = { + aaa: 111 + }; + } + + componentWillMount() { + this.setState( + { + aaa: 222 + }, + function() { + list.push("555"); + } + ); + this.setState( + { + aaa: 333 + }, + function() { + list.push("666"); + } + ); + } + componentDidMount() { + this.setState( + { + aaa: 444 + }, + function() { + list.push("777"); + } + ); + } + + render() { + list.push(this.state.aaa); + return

{this.state.aaa}

; + } + } + + var s = React.render(, div); + expect(list.join("-")).toBe("333-444-555-666-777"); + expect(div.textContent || div.innerText).toBe("444"); + }); + + it("ReactDOM的回调总在最后",function() { + var list = []; + class App extends React.Component { + constructor(props) { + super(props); + this.state = { + path: "111" + }; + } + componentWillMount() { + this.setState( + { + path: "222" + }, + function() { + list.push("componentWillMount cb"); + } + ); + this.setState( + { + path: "2222" + }, + function() { + list.push("componentWillMount cb2"); + } + ); + } + render() { + list.push("render " + this.state.path); + return ( +
+ + {this.state.path} + + +
+ ); + } + componentDidMount() { + this.setState( + { + path: "eeee" + }, + function() { + list.push("componentDidMount cb"); + } + ); + } + componentWillUpdate() { + list.push("will update"); + } + componentDidUpdate() { + list.push("did update"); + } + } + class Child extends React.Component { + componentWillMount() { + this.props.parent.setState( + { + path: "child" + }, + function() { + list.push("child setState"); + } + ); + } + render() { + list.push("child render"); + return

33333

; + } + } + ReactDOM.render(, div, function() { + list.push("ReactDOM cb"); }); - this.state.aaa = "dddd"; - this.forceUpdate(function() { - list.push("3333"); + + expect(list).toEqual([ + "render 2222", + "child render", + "will update", + "render eeee", + "child render", + "did update", + "componentWillMount cb", + "componentWillMount cb2", + "child setState", + "componentDidMount cb", + "ReactDOM cb" + ]); + }); + + + it("在componentWillUnmount中setState应该不起作用", function() { + class Issue extends React.PureComponent { + constructor(props) { + super(props); + this.state = { + status: "normal" + }; + } + + componentWillUnmount() { + this.setState({ status: "unmount" }); + } + + render() { + const { status } = this.state; + + if (status === "unmount") { + throw new Error("Issue unmounted"); + } + + return Issue status: {status}; + } + } + + class App extends React.PureComponent { + constructor(props) { + super(props); + this.state = { + showIssue: true + }; + } + + render() { + const { showIssue } = this.state; + + return ( +
+ {showIssue && } + +
+ ); + } + } + var s = React.render(, div); + + expect(div.getElementsByTagName("span").length).toBe(1); + ReactTestUtils.Simulate.click(s.refs.button); + + expect(div.getElementsByTagName("span").length).toBe(0); + }); + + it("forceUpdate在componentDidMount中使用",function(){ + var list = []; + class App extends React.Component { + constructor(props) { + super(props); + this.state = { + aaa: "aaa" + }; + } + componentWillMount() { + this.setState( + { + aaa: "bbb" + }, + function() { + list.push("1111"); + } + ); + } + componentDidMount() { + this.state.aaa = "cccc"; + this.forceUpdate(function() { + list.push("2222"); + }); + this.state.aaa = "dddd"; + this.forceUpdate(function() { + list.push("3333"); + }); + list.push("did mount"); + } + componentWillUpdate() { + list.push("app will update"); + } + componentDidUpdate() { + list.push("app did update"); + } + + render() { + list.push("render " + this.state.aaa); + return
{this.state.aaa}
; + } + } + ReactDOM.render(, div, function() { + list.push("ReactDOM cb"); }); - list.push("did mount"); - } - componentWillUpdate() { - list.push("app will update"); - } - componentDidUpdate() { - list.push("app did update"); - } - - render() { - list.push("render " + this.state.aaa); - return
{this.state.aaa}
; - } - } - ReactDOM.render(, div, function() { - list.push("ReactDOM cb"); + expect(list).toEqual([ + "render bbb", + "did mount", + "app will update", + "render dddd", + "app did update", + "1111", + "2222", + "3333", + "ReactDOM cb" + ]); + }); + it("事件回调里执行多个组件的setState,不会按触发时的顺序执行,而是按文档顺序执行",function(){ + var list = []; + class App extends React.Component { + constructor(props) { + super(props); + this.handleClick = this.handleClick.bind(this); + } + handleClick() { + this.refs.c.setState({ + text:"第1" + },function(){ + list.push("c的回调"); + }); + this.refs.a.setState({ + text:"第2" + }, function(){ + list.push("a的回调"); + }); + this.refs.b.setState({ + text:"第3" + },function(){ + list.push("b的回调"); + }); + } + render() { + return
+ aaa + bbb + ccc +
; + } + } + class Child extends React.Component { + constructor(props) { + super(props); + this.state = { + text: props.children + }; + } + componentWillUpdate(){ + list.push(this.props.name+" will update"); + } + componentDidUpdate(){ + list.push(this.props.name+" did update"); + } + render(){ + return {this.state.text}; + } + } + var s = ReactDOM.render(, div); + ReactTestUtils.Simulate.click(s.refs.kk); + expect(list).toEqual([ + "a will update", + "b will update", + "c will update", + "a did update", + "b did update", + "c did update", + "a的回调", + "b的回调", + "c的回调" + ]); }); - expect(list).toEqual([ - "render bbb", - "did mount", - "app will update", - "render dddd", - "app did update", - "1111", - "2222", - "3333", - "ReactDOM cb" - ]); - }); }); diff --git a/test/modules/ref.spec.jsx b/test/modules/ref.spec.jsx index a606b0dbc..39ae9c835 100644 --- a/test/modules/ref.spec.jsx +++ b/test/modules/ref.spec.jsx @@ -359,6 +359,67 @@ describe("ref", function () { expect(log[0].nodeName).toBe("SPAN"); expect(log[2].nodeName).toBe("B"); }); + it("组件虚拟DOM的ref总在componentDidMount/Update后才执行", function() { + var list = []; + class Static extends React.Component { + componentDidMount(){ + list.push("static did mount"); + } + componentWillUpdate(){ + list.push("static will update"); + } + componentDidUpdate(){ + list.push("static did update"); + } + render() { + list.push("static render"); + return
{this.props.children}
; + } + } + + class Component extends React.Component { + render() { + if (this.props.flipped) { + return ( +
+ + B + + +
+ ); + } else { + return ( +
+ + A + +
+ ); + } + } + } + + var container = document.createElement("div"); + ReactDOM.render(, container); + + ReactDOM.render(, container); + expect(list).toEqual([ + "static render", + "static did mount", + "instance 111", + "null 111", + "static will update", + "static render", + "static did update", + "instance 222", + ]); + + }); }); \ No newline at end of file diff --git a/test/spec.js b/test/spec.js index 3b46b265a..16dd704c9 100644 --- a/test/spec.js +++ b/test/spec.js @@ -27,6 +27,7 @@ require("./modules/ref.spec.jsx"); require("./modules/redux.spec.jsx"); require("./modules/ReactTestUtils-test.jsx"); + require("./modules/ReactComponent-test.jsx"); require("./modules/ReactChildren-test.jsx"); From 090eec7d3e660669848d6cb19500df6e0dedf01c Mon Sep 17 00:00:00 2001 From: RubyLouvre <1669866773@qq.com> Date: Sun, 8 Oct 2017 17:26:28 +0800 Subject: [PATCH 41/57] =?UTF-8?q?=E7=AE=80=E5=8C=96=20drainQueue=E7=9A=84?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dist/React.js | 61 +++++++++++++++++++++++------------------- dist/ReactIE.js | 61 +++++++++++++++++++++++------------------- dist/ReactSelection.js | 61 +++++++++++++++++++++++------------------- dist/ReactShim.js | 61 +++++++++++++++++++++++------------------- src/Updater.js | 1 + src/diff.js | 30 ++++++++++----------- src/scheduler.js | 33 +++++++++++++---------- 7 files changed, 167 insertions(+), 141 deletions(-) diff --git a/dist/React.js b/dist/React.js index 0d1ea8d52..7f41c2f41 100644 --- a/dist/React.js +++ b/dist/React.js @@ -1669,6 +1669,7 @@ function Updater(instance, vnode) { this._instance = instance; this._pendingCallbacks = []; this._ref = noop; + this._didHook = noop; this._pendingStates = []; this._lifeStage = 0; //判断生命周期 //update总是保存最新的数据,如state, props, context, parentContext, vparent @@ -2029,20 +2030,20 @@ function getOptionSelected(option, selected) { dom.selected = selected; } +/* function callUpdate(updater, instance) { Refs.clearRefs(); - if (updater._lifeStage === 2) { + if (updater._lifeStage === 2) { updater._didUpdate = true; - instance.componentDidUpdate(); + instance._didUpdate(); + updater._lifeStage = 1; + updater._hydrating = false; if (!updater._renderInNextCycle) { updater._didUpdate = false; } - updater._lifeStage = 1; - updater._hydrating = false; - options.afterUpdate(instance); } updater._ref(); -} +}*/ function drainQueue(queue) { options.beforePatch(); @@ -2052,23 +2053,27 @@ function drainQueue(queue) { var i = 0; while (i < queue.length) { //queue可能中途加入新元素, 因此不能直接使用queue.forEach(fn) - var updater = queue[i], - instance = updater._instance; + var updater = queue[i]; //, instance = updater._instance; i++; + Refs.clearRefs(); + updater._didUpdate = updater._lifeStage === 2; + updater._didHook(); + updater._lifeStage = 1; + updater._hydrating = false; + if (!updater._renderInNextCycle) { + updater._didUpdate = false; + } + updater._ref(); + /* if (!updater._lifeStage) { Refs.clearRefs(); - instance.newStageAddedRefs = instance.newStageAddedRefs || {}; - if (instance.componentDidMount) { - instance.componentDidMount(); - instance.componentDidMount = null; - } + updater._didHook(); updater._lifeStage = 1; updater._hydrating = false; updater._ref(); - options.afterMount(instance); } else { callUpdate(updater, instance); - } + }*/ //如果组件在componentDidMount中调用setState if (updater._renderInNextCycle) { options.refreshComponent(updater, queue); @@ -2338,6 +2343,12 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue, pa ); }, updater.rendered); Refs.createInstanceRef(updater, ref); + var userHook = instance.componentDidMount; + updater._didHook = function () { + userHook && userHook.call(instance); + updater._didHook = noop; + options.afterMount(instance); + }; updateQueue.push(updater); return dom; @@ -2438,19 +2449,13 @@ function refreshComponent(updater, updateQueue) { updater.lastVnode = vnode; updater._lifeStage = 2; - var hookName = "componentDidUpdate"; - var didUpdateHook = instance[hookName]; - if (didUpdateHook) { - instance[hookName] = function () { - didUpdateHook.call(this, lastProps, lastState, lastContext); - this[hookName] = didUpdateHook; - }; - } else { - //临时添加一个一次性的空钩子 - instance[hookName] = function () { - delete instance[hookName]; - }; - } + var userHook = instance.componentDidUpdate; + + updater._didHook = function () { + userHook && userHook.call(instance, lastProps, lastState, lastContext); + updater._didHook = noop; + options.afterUpdate(instance); + }; // updater._hydrating = false; updateQueue.push(updater); diff --git a/dist/ReactIE.js b/dist/ReactIE.js index ce1b025fa..ed15135c4 100644 --- a/dist/ReactIE.js +++ b/dist/ReactIE.js @@ -1668,6 +1668,7 @@ function Updater(instance, vnode) { this._instance = instance; this._pendingCallbacks = []; this._ref = noop; + this._didHook = noop; this._pendingStates = []; this._lifeStage = 0; //判断生命周期 //update总是保存最新的数据,如state, props, context, parentContext, vparent @@ -2028,20 +2029,20 @@ function getOptionSelected(option, selected) { dom.selected = selected; } +/* function callUpdate(updater, instance) { Refs.clearRefs(); - if (updater._lifeStage === 2) { + if (updater._lifeStage === 2) { updater._didUpdate = true; - instance.componentDidUpdate(); + instance._didUpdate(); + updater._lifeStage = 1; + updater._hydrating = false; if (!updater._renderInNextCycle) { updater._didUpdate = false; } - updater._lifeStage = 1; - updater._hydrating = false; - options.afterUpdate(instance); } updater._ref(); -} +}*/ function drainQueue(queue) { options.beforePatch(); @@ -2051,23 +2052,27 @@ function drainQueue(queue) { var i = 0; while (i < queue.length) { //queue可能中途加入新元素, 因此不能直接使用queue.forEach(fn) - var updater = queue[i], - instance = updater._instance; + var updater = queue[i]; //, instance = updater._instance; i++; + Refs.clearRefs(); + updater._didUpdate = updater._lifeStage === 2; + updater._didHook(); + updater._lifeStage = 1; + updater._hydrating = false; + if (!updater._renderInNextCycle) { + updater._didUpdate = false; + } + updater._ref(); + /* if (!updater._lifeStage) { Refs.clearRefs(); - instance.newStageAddedRefs = instance.newStageAddedRefs || {}; - if (instance.componentDidMount) { - instance.componentDidMount(); - instance.componentDidMount = null; - } + updater._didHook(); updater._lifeStage = 1; updater._hydrating = false; updater._ref(); - options.afterMount(instance); } else { callUpdate(updater, instance); - } + }*/ //如果组件在componentDidMount中调用setState if (updater._renderInNextCycle) { options.refreshComponent(updater, queue); @@ -2337,6 +2342,12 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue, pa ); }, updater.rendered); Refs.createInstanceRef(updater, ref); + var userHook = instance.componentDidMount; + updater._didHook = function () { + userHook && userHook.call(instance); + updater._didHook = noop; + options.afterMount(instance); + }; updateQueue.push(updater); return dom; @@ -2437,19 +2448,13 @@ function refreshComponent(updater, updateQueue) { updater.lastVnode = vnode; updater._lifeStage = 2; - var hookName = "componentDidUpdate"; - var didUpdateHook = instance[hookName]; - if (didUpdateHook) { - instance[hookName] = function () { - didUpdateHook.call(this, lastProps, lastState, lastContext); - this[hookName] = didUpdateHook; - }; - } else { - //临时添加一个一次性的空钩子 - instance[hookName] = function () { - delete instance[hookName]; - }; - } + var userHook = instance.componentDidUpdate; + + updater._didHook = function () { + userHook && userHook.call(instance, lastProps, lastState, lastContext); + updater._didHook = noop; + options.afterUpdate(instance); + }; // updater._hydrating = false; updateQueue.push(updater); diff --git a/dist/ReactSelection.js b/dist/ReactSelection.js index ce1b025fa..ed15135c4 100644 --- a/dist/ReactSelection.js +++ b/dist/ReactSelection.js @@ -1668,6 +1668,7 @@ function Updater(instance, vnode) { this._instance = instance; this._pendingCallbacks = []; this._ref = noop; + this._didHook = noop; this._pendingStates = []; this._lifeStage = 0; //判断生命周期 //update总是保存最新的数据,如state, props, context, parentContext, vparent @@ -2028,20 +2029,20 @@ function getOptionSelected(option, selected) { dom.selected = selected; } +/* function callUpdate(updater, instance) { Refs.clearRefs(); - if (updater._lifeStage === 2) { + if (updater._lifeStage === 2) { updater._didUpdate = true; - instance.componentDidUpdate(); + instance._didUpdate(); + updater._lifeStage = 1; + updater._hydrating = false; if (!updater._renderInNextCycle) { updater._didUpdate = false; } - updater._lifeStage = 1; - updater._hydrating = false; - options.afterUpdate(instance); } updater._ref(); -} +}*/ function drainQueue(queue) { options.beforePatch(); @@ -2051,23 +2052,27 @@ function drainQueue(queue) { var i = 0; while (i < queue.length) { //queue可能中途加入新元素, 因此不能直接使用queue.forEach(fn) - var updater = queue[i], - instance = updater._instance; + var updater = queue[i]; //, instance = updater._instance; i++; + Refs.clearRefs(); + updater._didUpdate = updater._lifeStage === 2; + updater._didHook(); + updater._lifeStage = 1; + updater._hydrating = false; + if (!updater._renderInNextCycle) { + updater._didUpdate = false; + } + updater._ref(); + /* if (!updater._lifeStage) { Refs.clearRefs(); - instance.newStageAddedRefs = instance.newStageAddedRefs || {}; - if (instance.componentDidMount) { - instance.componentDidMount(); - instance.componentDidMount = null; - } + updater._didHook(); updater._lifeStage = 1; updater._hydrating = false; updater._ref(); - options.afterMount(instance); } else { callUpdate(updater, instance); - } + }*/ //如果组件在componentDidMount中调用setState if (updater._renderInNextCycle) { options.refreshComponent(updater, queue); @@ -2337,6 +2342,12 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue, pa ); }, updater.rendered); Refs.createInstanceRef(updater, ref); + var userHook = instance.componentDidMount; + updater._didHook = function () { + userHook && userHook.call(instance); + updater._didHook = noop; + options.afterMount(instance); + }; updateQueue.push(updater); return dom; @@ -2437,19 +2448,13 @@ function refreshComponent(updater, updateQueue) { updater.lastVnode = vnode; updater._lifeStage = 2; - var hookName = "componentDidUpdate"; - var didUpdateHook = instance[hookName]; - if (didUpdateHook) { - instance[hookName] = function () { - didUpdateHook.call(this, lastProps, lastState, lastContext); - this[hookName] = didUpdateHook; - }; - } else { - //临时添加一个一次性的空钩子 - instance[hookName] = function () { - delete instance[hookName]; - }; - } + var userHook = instance.componentDidUpdate; + + updater._didHook = function () { + userHook && userHook.call(instance, lastProps, lastState, lastContext); + updater._didHook = noop; + options.afterUpdate(instance); + }; // updater._hydrating = false; updateQueue.push(updater); diff --git a/dist/ReactShim.js b/dist/ReactShim.js index e3dbbfd2f..0cc96e317 100644 --- a/dist/ReactShim.js +++ b/dist/ReactShim.js @@ -1514,6 +1514,7 @@ function Updater(instance, vnode) { this._instance = instance; this._pendingCallbacks = []; this._ref = noop; + this._didHook = noop; this._pendingStates = []; this._lifeStage = 0; //判断生命周期 //update总是保存最新的数据,如state, props, context, parentContext, vparent @@ -1874,20 +1875,20 @@ function getOptionSelected(option, selected) { dom.selected = selected; } +/* function callUpdate(updater, instance) { Refs.clearRefs(); - if (updater._lifeStage === 2) { + if (updater._lifeStage === 2) { updater._didUpdate = true; - instance.componentDidUpdate(); + instance._didUpdate(); + updater._lifeStage = 1; + updater._hydrating = false; if (!updater._renderInNextCycle) { updater._didUpdate = false; } - updater._lifeStage = 1; - updater._hydrating = false; - options.afterUpdate(instance); } updater._ref(); -} +}*/ function drainQueue(queue) { options.beforePatch(); @@ -1897,23 +1898,27 @@ function drainQueue(queue) { var i = 0; while (i < queue.length) { //queue可能中途加入新元素, 因此不能直接使用queue.forEach(fn) - var updater = queue[i], - instance = updater._instance; + var updater = queue[i]; //, instance = updater._instance; i++; + Refs.clearRefs(); + updater._didUpdate = updater._lifeStage === 2; + updater._didHook(); + updater._lifeStage = 1; + updater._hydrating = false; + if (!updater._renderInNextCycle) { + updater._didUpdate = false; + } + updater._ref(); + /* if (!updater._lifeStage) { Refs.clearRefs(); - instance.newStageAddedRefs = instance.newStageAddedRefs || {}; - if (instance.componentDidMount) { - instance.componentDidMount(); - instance.componentDidMount = null; - } + updater._didHook(); updater._lifeStage = 1; updater._hydrating = false; updater._ref(); - options.afterMount(instance); } else { callUpdate(updater, instance); - } + }*/ //如果组件在componentDidMount中调用setState if (updater._renderInNextCycle) { options.refreshComponent(updater, queue); @@ -2179,6 +2184,12 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue, pa ); }, updater.rendered); Refs.createInstanceRef(updater, ref); + var userHook = instance.componentDidMount; + updater._didHook = function () { + userHook && userHook.call(instance); + updater._didHook = noop; + options.afterMount(instance); + }; updateQueue.push(updater); return dom; @@ -2279,19 +2290,13 @@ function refreshComponent(updater, updateQueue) { updater.lastVnode = vnode; updater._lifeStage = 2; - var hookName = "componentDidUpdate"; - var didUpdateHook = instance[hookName]; - if (didUpdateHook) { - instance[hookName] = function () { - didUpdateHook.call(this, lastProps, lastState, lastContext); - this[hookName] = didUpdateHook; - }; - } else { - //临时添加一个一次性的空钩子 - instance[hookName] = function () { - delete instance[hookName]; - }; - } + var userHook = instance.componentDidUpdate; + + updater._didHook = function () { + userHook && userHook.call(instance, lastProps, lastState, lastContext); + updater._didHook = noop; + options.afterUpdate(instance); + }; // updater._hydrating = false; updateQueue.push(updater); diff --git a/src/Updater.js b/src/Updater.js index ad6516135..b9f461d4d 100644 --- a/src/Updater.js +++ b/src/Updater.js @@ -13,6 +13,7 @@ export function Updater(instance, vnode) { this._instance = instance; this._pendingCallbacks = []; this._ref = noop; + this._didHook = noop; this._pendingStates = []; this._lifeStage = 0; //判断生命周期 //update总是保存最新的数据,如state, props, context, parentContext, vparent diff --git a/src/diff.js b/src/diff.js index 09e7d9b60..6022df89a 100644 --- a/src/diff.js +++ b/src/diff.js @@ -1,4 +1,4 @@ -import { options, getNodes, innerHTML, toLowerCase, deprecatedWarn, getContextByTypes } from "./util"; +import { noop, options, getNodes, innerHTML, toLowerCase, deprecatedWarn, getContextByTypes } from "./util"; import { diffProps } from "./diffProps"; import { disposeVnode } from "./dispose"; import { createDOMElement, emptyElement, removeDOMElement } from "./browser"; @@ -237,6 +237,12 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue, pa ); }, updater.rendered); Refs.createInstanceRef(updater, ref); + let userHook = instance.componentDidMount; + updater._didHook = function() { + userHook && userHook.call(instance); + updater._didHook = noop; + options.afterMount(instance); + }; updateQueue.push(updater); return dom; @@ -325,20 +331,14 @@ function refreshComponent(updater, updateQueue) { updater.lastVnode = vnode; updater._lifeStage = 2; - let hookName = "componentDidUpdate"; - let didUpdateHook = instance[hookName]; - if (didUpdateHook) { - instance[hookName] = function() { - didUpdateHook.call(this, lastProps, lastState, lastContext); - this[hookName] = didUpdateHook; - }; - } else { - //临时添加一个一次性的空钩子 - instance[hookName] = function() { - delete instance[hookName]; - }; - } - + let userHook = instance.componentDidUpdate; + + updater._didHook = function() { + userHook && userHook.call(instance, lastProps, lastState, lastContext); + updater._didHook = noop; + options.afterUpdate(instance); + }; + // updater._hydrating = false; updateQueue.push(updater); return dom; diff --git a/src/scheduler.js b/src/scheduler.js index 87e158f90..5c553abcd 100644 --- a/src/scheduler.js +++ b/src/scheduler.js @@ -6,21 +6,20 @@ import { Refs } from "./Refs"; - +/* function callUpdate(updater, instance) { Refs.clearRefs(); if (updater._lifeStage === 2) { updater._didUpdate = true; - instance.componentDidUpdate(); + instance._didUpdate(); + updater._lifeStage = 1; + updater._hydrating = false; if (!updater._renderInNextCycle) { updater._didUpdate = false; } - updater._lifeStage = 1; - updater._hydrating = false; - options.afterUpdate(instance); } updater._ref(); -} +}*/ export function drainQueue(queue) { options.beforePatch(); @@ -29,27 +28,33 @@ export function drainQueue(queue) { //再执行所有mount/update钩子(从下到上) let i = 0; while(i < queue.length){//queue可能中途加入新元素, 因此不能直接使用queue.forEach(fn) - var updater = queue[i], instance = updater._instance; + var updater = queue[i];//, instance = updater._instance; i++; + Refs.clearRefs(); + updater._didUpdate = updater._lifeStage === 2; + updater._didHook(); + updater._lifeStage = 1; + updater._hydrating = false; + if (!updater._renderInNextCycle) { + updater._didUpdate = false; + } + updater._ref(); + /* if (!updater._lifeStage) { Refs.clearRefs(); - instance.newStageAddedRefs = instance.newStageAddedRefs || {}; - if (instance.componentDidMount) { - instance.componentDidMount(); - instance.componentDidMount = null; - } + updater._didHook(); updater._lifeStage = 1; updater._hydrating = false; updater._ref(); - options.afterMount(instance); } else { callUpdate(updater, instance); - } + }*/ //如果组件在componentDidMount中调用setState if (updater._renderInNextCycle) { options.refreshComponent(updater, queue); // callUpdate(updater, instance); } + } //再执行所有setState/forceUpdate回调,根据从下到上的顺序执行 queue.sort(mountSorter).forEach(function(updater){ From d2225152a225fc2debcb3fa667aa40ae6ed87490 Mon Sep 17 00:00:00 2001 From: RubyLouvre <1669866773@qq.com> Date: Sun, 8 Oct 2017 21:08:07 +0800 Subject: [PATCH 42/57] =?UTF-8?q?=E5=8E=BB=E6=8E=89=E6=97=A0=E7=94=A8?= =?UTF-8?q?=E7=9A=84=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build/shim.script.js | 4 +-- dist/React.js | 62 ++++++++---------------------------------- dist/ReactIE.js | 62 ++++++++---------------------------------- dist/ReactSelection.js | 62 ++++++++---------------------------------- dist/ReactShim.js | 62 ++++++++---------------------------------- src/Refs.js | 2 -- src/Updater.js | 4 +-- src/diff.js | 18 ++++-------- src/dispose.js | 3 +- src/scheduler.js | 36 ++++-------------------- 10 files changed, 63 insertions(+), 252 deletions(-) diff --git a/build/shim.script.js b/build/shim.script.js index 8b5f966ad..eec93bce3 100644 --- a/build/shim.script.js +++ b/build/shim.script.js @@ -18,8 +18,8 @@ var text2 = str2 .replace(/\/\/freeze_start([\s\S]+?)freeze_end/, ""); fs.writeFileSync(dir2, text2, { encoding: "utf8" }); -//fs.writeFileSync( path.join(__dirname, "../../draft/node_modules/anujs/dist/React.js"), text2, { encoding: "utf8" }); -fs.writeFileSync( path.join(__dirname, "../../antd-test/node_modules/anujs/dist/React.js"), text2, { encoding: "utf8" }); +//fs.writeFileSync( path.join(__dirname, "../../animate/node_modules/anujs/dist/React.js"), text2, { encoding: "utf8" }); +//fs.writeFileSync( path.join(__dirname, "../../antd-test/node_modules/anujs/dist/React.js"), text2, { encoding: "utf8" }); //fs.writeFileSync( path.join(__dirname, "../../yo-demo/node_modules/anujs/dist/React.js"), text2, { encoding: "utf8" }); //fs.writeFileSync( path.join(__dirname, "../../yo-router/node_modules/anujs/dist/React.js"), text2, { encoding: "utf8" }); diff --git a/dist/React.js b/dist/React.js index 7f41c2f41..41ad0fa52 100644 --- a/dist/React.js +++ b/dist/React.js @@ -225,12 +225,10 @@ var Refs = { if (ref) { if (ref.string && nextRef.string ? ref.string !== nextRef.string : ref !== getDOMNode) { ref(null); - // pendingRefs.push(ref.bind(0,null)); } } if (dom && nextRef !== getDOMNode) { nextRef(dom); - // pendingRefs.push(nextRef.bind(0,dom)); } }, createInstanceRef: function createInstanceRef(updater, ref) { @@ -1709,9 +1707,9 @@ Updater.prototype = { //调整全局的 CurrentOwner.cur if (!rendered) { + var lastOwn = Refs.currentOwner; + Refs.currentOwner = instance; try { - var lastOwn = Refs.currentOwner; - Refs.currentOwner = instance; if (this.willReceive === false) { rendered = this.rendered; delete this.willReceive; @@ -1822,7 +1820,6 @@ function disposeElement(vnode) { } function disposeComponent(vnode) { - var instance = vnode._instance; if (instance) { options.beforeUnmount(instance); @@ -1842,7 +1839,7 @@ function disposeComponent(vnode) { } //在执行componentWillUnmount后才将关联的元素节点解绑,防止用户在钩子里调用 findDOMNode方法 disposeVnode(updater.rendered); - vnode.ref = vnode._instance = instance.updater = null; + updater._renderInNextCycle = vnode._instance = instance.updater = null; } } @@ -2030,54 +2027,28 @@ function getOptionSelected(option, selected) { dom.selected = selected; } -/* -function callUpdate(updater, instance) { - Refs.clearRefs(); - if (updater._lifeStage === 2) { - updater._didUpdate = true; - instance._didUpdate(); - updater._lifeStage = 1; - updater._hydrating = false; - if (!updater._renderInNextCycle) { - updater._didUpdate = false; - } - } - updater._ref(); -}*/ - function drainQueue(queue) { options.beforePatch(); //先执行所有refs方法(从上到下) - Refs.clearRefs(); - //再执行所有mount/update钩子(从下到上) + Refs.clearRefs(); //假如一个组件实例也没有,也要把所有元素虚拟DOM的ref执行 + var i = 0; while (i < queue.length) { //queue可能中途加入新元素, 因此不能直接使用queue.forEach(fn) - var updater = queue[i]; //, instance = updater._instance; + var updater = queue[i]; i++; Refs.clearRefs(); updater._didUpdate = updater._lifeStage === 2; - updater._didHook(); + updater._didHook(); //执行所有mount/update钩子(从下到上) updater._lifeStage = 1; updater._hydrating = false; if (!updater._renderInNextCycle) { updater._didUpdate = false; } - updater._ref(); - /* - if (!updater._lifeStage) { - Refs.clearRefs(); - updater._didHook(); - updater._lifeStage = 1; - updater._hydrating = false; - updater._ref(); - } else { - callUpdate(updater, instance); - }*/ + updater._ref(); //执行组件虚拟DOM的ref //如果组件在componentDidMount中调用setState if (updater._renderInNextCycle) { options.refreshComponent(updater, queue); - // callUpdate(updater, instance); } } //再执行所有setState/forceUpdate回调,根据从下到上的顺序执行 @@ -2267,9 +2238,7 @@ function mountElement(lastNode, vnode, vparent, context, updateQueue) { ref = vnode.ref; var dom = genMountElement(lastNode, vnode, vparent, type); - vnode._hostNode = dom; - var children = flattenChildren(vnode); var method = lastNode ? alignChildren : mountChildren; method(dom, children, vnode, context, updateQueue); @@ -2396,13 +2365,13 @@ function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQue return alignVnode(updater.rendered, nextRendered, vparent, childContext, updateQueue, updater); }); } - if (updateQueue.isMainProcess) { + /* if (updateQueue.isMainProcess) { queue = updateQueue; queue = []; } else { queue = updateQueue; - } - refreshComponent(updater, queue); + }*/ + refreshComponent(updater, updateQueue); //子组件先执行 updateQueue.push(updater); return updater._hostNode; @@ -2457,7 +2426,6 @@ function refreshComponent(updater, updateQueue) { options.afterUpdate(instance); }; - // updater._hydrating = false; updateQueue.push(updater); return dom; } @@ -2484,11 +2452,6 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { dom = lastVnode._hostNode, ref = lastVnode.ref, checkProps = lastVnode.checkProps; - - if (dom === null) { - console.log("此节点已经被移除", vparent); - return null; - } var nextProps = nextVnode.props, nextRef = nextVnode.ref; @@ -2530,8 +2493,7 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { } if (nextLength === lastLength && lastLength === 1) { lastChildren[0]._hostNode = parentNode.firstChild; - dom = alignVnode(lastChildren[0], nextChildren[0], lastVnode, context, updateQueue); - return; + return alignVnode(lastChildren[0], nextChildren[0], lastVnode, context, updateQueue); } var maxLength = Math.max(nextLength, lastLength), insertPoint = parentNode.firstChild, diff --git a/dist/ReactIE.js b/dist/ReactIE.js index ed15135c4..43052c37f 100644 --- a/dist/ReactIE.js +++ b/dist/ReactIE.js @@ -224,12 +224,10 @@ var Refs = { if (ref) { if (ref.string && nextRef.string ? ref.string !== nextRef.string : ref !== getDOMNode) { ref(null); - // pendingRefs.push(ref.bind(0,null)); } } if (dom && nextRef !== getDOMNode) { nextRef(dom); - // pendingRefs.push(nextRef.bind(0,dom)); } }, createInstanceRef: function createInstanceRef(updater, ref) { @@ -1708,9 +1706,9 @@ Updater.prototype = { //调整全局的 CurrentOwner.cur if (!rendered) { + var lastOwn = Refs.currentOwner; + Refs.currentOwner = instance; try { - var lastOwn = Refs.currentOwner; - Refs.currentOwner = instance; if (this.willReceive === false) { rendered = this.rendered; delete this.willReceive; @@ -1821,7 +1819,6 @@ function disposeElement(vnode) { } function disposeComponent(vnode) { - var instance = vnode._instance; if (instance) { options.beforeUnmount(instance); @@ -1841,7 +1838,7 @@ function disposeComponent(vnode) { } //在执行componentWillUnmount后才将关联的元素节点解绑,防止用户在钩子里调用 findDOMNode方法 disposeVnode(updater.rendered); - vnode.ref = vnode._instance = instance.updater = null; + updater._renderInNextCycle = vnode._instance = instance.updater = null; } } @@ -2029,54 +2026,28 @@ function getOptionSelected(option, selected) { dom.selected = selected; } -/* -function callUpdate(updater, instance) { - Refs.clearRefs(); - if (updater._lifeStage === 2) { - updater._didUpdate = true; - instance._didUpdate(); - updater._lifeStage = 1; - updater._hydrating = false; - if (!updater._renderInNextCycle) { - updater._didUpdate = false; - } - } - updater._ref(); -}*/ - function drainQueue(queue) { options.beforePatch(); //先执行所有refs方法(从上到下) - Refs.clearRefs(); - //再执行所有mount/update钩子(从下到上) + Refs.clearRefs(); //假如一个组件实例也没有,也要把所有元素虚拟DOM的ref执行 + var i = 0; while (i < queue.length) { //queue可能中途加入新元素, 因此不能直接使用queue.forEach(fn) - var updater = queue[i]; //, instance = updater._instance; + var updater = queue[i]; i++; Refs.clearRefs(); updater._didUpdate = updater._lifeStage === 2; - updater._didHook(); + updater._didHook(); //执行所有mount/update钩子(从下到上) updater._lifeStage = 1; updater._hydrating = false; if (!updater._renderInNextCycle) { updater._didUpdate = false; } - updater._ref(); - /* - if (!updater._lifeStage) { - Refs.clearRefs(); - updater._didHook(); - updater._lifeStage = 1; - updater._hydrating = false; - updater._ref(); - } else { - callUpdate(updater, instance); - }*/ + updater._ref(); //执行组件虚拟DOM的ref //如果组件在componentDidMount中调用setState if (updater._renderInNextCycle) { options.refreshComponent(updater, queue); - // callUpdate(updater, instance); } } //再执行所有setState/forceUpdate回调,根据从下到上的顺序执行 @@ -2266,9 +2237,7 @@ function mountElement(lastNode, vnode, vparent, context, updateQueue) { ref = vnode.ref; var dom = genMountElement(lastNode, vnode, vparent, type); - vnode._hostNode = dom; - var children = flattenChildren(vnode); var method = lastNode ? alignChildren : mountChildren; method(dom, children, vnode, context, updateQueue); @@ -2395,13 +2364,13 @@ function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQue return alignVnode(updater.rendered, nextRendered, vparent, childContext, updateQueue, updater); }); } - if (updateQueue.isMainProcess) { + /* if (updateQueue.isMainProcess) { queue = updateQueue; queue = []; } else { queue = updateQueue; - } - refreshComponent(updater, queue); + }*/ + refreshComponent(updater, updateQueue); //子组件先执行 updateQueue.push(updater); return updater._hostNode; @@ -2456,7 +2425,6 @@ function refreshComponent(updater, updateQueue) { options.afterUpdate(instance); }; - // updater._hydrating = false; updateQueue.push(updater); return dom; } @@ -2483,11 +2451,6 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { dom = lastVnode._hostNode, ref = lastVnode.ref, checkProps = lastVnode.checkProps; - - if (dom === null) { - console.log("此节点已经被移除", vparent); - return null; - } var nextProps = nextVnode.props, nextRef = nextVnode.ref; @@ -2529,8 +2492,7 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { } if (nextLength === lastLength && lastLength === 1) { lastChildren[0]._hostNode = parentNode.firstChild; - dom = alignVnode(lastChildren[0], nextChildren[0], lastVnode, context, updateQueue); - return; + return alignVnode(lastChildren[0], nextChildren[0], lastVnode, context, updateQueue); } var maxLength = Math.max(nextLength, lastLength), insertPoint = parentNode.firstChild, diff --git a/dist/ReactSelection.js b/dist/ReactSelection.js index ed15135c4..43052c37f 100644 --- a/dist/ReactSelection.js +++ b/dist/ReactSelection.js @@ -224,12 +224,10 @@ var Refs = { if (ref) { if (ref.string && nextRef.string ? ref.string !== nextRef.string : ref !== getDOMNode) { ref(null); - // pendingRefs.push(ref.bind(0,null)); } } if (dom && nextRef !== getDOMNode) { nextRef(dom); - // pendingRefs.push(nextRef.bind(0,dom)); } }, createInstanceRef: function createInstanceRef(updater, ref) { @@ -1708,9 +1706,9 @@ Updater.prototype = { //调整全局的 CurrentOwner.cur if (!rendered) { + var lastOwn = Refs.currentOwner; + Refs.currentOwner = instance; try { - var lastOwn = Refs.currentOwner; - Refs.currentOwner = instance; if (this.willReceive === false) { rendered = this.rendered; delete this.willReceive; @@ -1821,7 +1819,6 @@ function disposeElement(vnode) { } function disposeComponent(vnode) { - var instance = vnode._instance; if (instance) { options.beforeUnmount(instance); @@ -1841,7 +1838,7 @@ function disposeComponent(vnode) { } //在执行componentWillUnmount后才将关联的元素节点解绑,防止用户在钩子里调用 findDOMNode方法 disposeVnode(updater.rendered); - vnode.ref = vnode._instance = instance.updater = null; + updater._renderInNextCycle = vnode._instance = instance.updater = null; } } @@ -2029,54 +2026,28 @@ function getOptionSelected(option, selected) { dom.selected = selected; } -/* -function callUpdate(updater, instance) { - Refs.clearRefs(); - if (updater._lifeStage === 2) { - updater._didUpdate = true; - instance._didUpdate(); - updater._lifeStage = 1; - updater._hydrating = false; - if (!updater._renderInNextCycle) { - updater._didUpdate = false; - } - } - updater._ref(); -}*/ - function drainQueue(queue) { options.beforePatch(); //先执行所有refs方法(从上到下) - Refs.clearRefs(); - //再执行所有mount/update钩子(从下到上) + Refs.clearRefs(); //假如一个组件实例也没有,也要把所有元素虚拟DOM的ref执行 + var i = 0; while (i < queue.length) { //queue可能中途加入新元素, 因此不能直接使用queue.forEach(fn) - var updater = queue[i]; //, instance = updater._instance; + var updater = queue[i]; i++; Refs.clearRefs(); updater._didUpdate = updater._lifeStage === 2; - updater._didHook(); + updater._didHook(); //执行所有mount/update钩子(从下到上) updater._lifeStage = 1; updater._hydrating = false; if (!updater._renderInNextCycle) { updater._didUpdate = false; } - updater._ref(); - /* - if (!updater._lifeStage) { - Refs.clearRefs(); - updater._didHook(); - updater._lifeStage = 1; - updater._hydrating = false; - updater._ref(); - } else { - callUpdate(updater, instance); - }*/ + updater._ref(); //执行组件虚拟DOM的ref //如果组件在componentDidMount中调用setState if (updater._renderInNextCycle) { options.refreshComponent(updater, queue); - // callUpdate(updater, instance); } } //再执行所有setState/forceUpdate回调,根据从下到上的顺序执行 @@ -2266,9 +2237,7 @@ function mountElement(lastNode, vnode, vparent, context, updateQueue) { ref = vnode.ref; var dom = genMountElement(lastNode, vnode, vparent, type); - vnode._hostNode = dom; - var children = flattenChildren(vnode); var method = lastNode ? alignChildren : mountChildren; method(dom, children, vnode, context, updateQueue); @@ -2395,13 +2364,13 @@ function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQue return alignVnode(updater.rendered, nextRendered, vparent, childContext, updateQueue, updater); }); } - if (updateQueue.isMainProcess) { + /* if (updateQueue.isMainProcess) { queue = updateQueue; queue = []; } else { queue = updateQueue; - } - refreshComponent(updater, queue); + }*/ + refreshComponent(updater, updateQueue); //子组件先执行 updateQueue.push(updater); return updater._hostNode; @@ -2456,7 +2425,6 @@ function refreshComponent(updater, updateQueue) { options.afterUpdate(instance); }; - // updater._hydrating = false; updateQueue.push(updater); return dom; } @@ -2483,11 +2451,6 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { dom = lastVnode._hostNode, ref = lastVnode.ref, checkProps = lastVnode.checkProps; - - if (dom === null) { - console.log("此节点已经被移除", vparent); - return null; - } var nextProps = nextVnode.props, nextRef = nextVnode.ref; @@ -2529,8 +2492,7 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { } if (nextLength === lastLength && lastLength === 1) { lastChildren[0]._hostNode = parentNode.firstChild; - dom = alignVnode(lastChildren[0], nextChildren[0], lastVnode, context, updateQueue); - return; + return alignVnode(lastChildren[0], nextChildren[0], lastVnode, context, updateQueue); } var maxLength = Math.max(nextLength, lastLength), insertPoint = parentNode.firstChild, diff --git a/dist/ReactShim.js b/dist/ReactShim.js index 0cc96e317..2faabc6a9 100644 --- a/dist/ReactShim.js +++ b/dist/ReactShim.js @@ -226,12 +226,10 @@ var Refs = { if (ref) { if (ref.string && nextRef.string ? ref.string !== nextRef.string : ref !== getDOMNode) { ref(null); - // pendingRefs.push(ref.bind(0,null)); } } if (dom && nextRef !== getDOMNode) { nextRef(dom); - // pendingRefs.push(nextRef.bind(0,dom)); } }, createInstanceRef: function createInstanceRef(updater, ref) { @@ -1554,9 +1552,9 @@ Updater.prototype = { //调整全局的 CurrentOwner.cur if (!rendered) { + var lastOwn = Refs.currentOwner; + Refs.currentOwner = instance; try { - var lastOwn = Refs.currentOwner; - Refs.currentOwner = instance; if (this.willReceive === false) { rendered = this.rendered; delete this.willReceive; @@ -1667,7 +1665,6 @@ function disposeElement(vnode) { } function disposeComponent(vnode) { - var instance = vnode._instance; if (instance) { options.beforeUnmount(instance); @@ -1687,7 +1684,7 @@ function disposeComponent(vnode) { } //在执行componentWillUnmount后才将关联的元素节点解绑,防止用户在钩子里调用 findDOMNode方法 disposeVnode(updater.rendered); - vnode.ref = vnode._instance = instance.updater = null; + updater._renderInNextCycle = vnode._instance = instance.updater = null; } } @@ -1875,54 +1872,28 @@ function getOptionSelected(option, selected) { dom.selected = selected; } -/* -function callUpdate(updater, instance) { - Refs.clearRefs(); - if (updater._lifeStage === 2) { - updater._didUpdate = true; - instance._didUpdate(); - updater._lifeStage = 1; - updater._hydrating = false; - if (!updater._renderInNextCycle) { - updater._didUpdate = false; - } - } - updater._ref(); -}*/ - function drainQueue(queue) { options.beforePatch(); //先执行所有refs方法(从上到下) - Refs.clearRefs(); - //再执行所有mount/update钩子(从下到上) + Refs.clearRefs(); //假如一个组件实例也没有,也要把所有元素虚拟DOM的ref执行 + var i = 0; while (i < queue.length) { //queue可能中途加入新元素, 因此不能直接使用queue.forEach(fn) - var updater = queue[i]; //, instance = updater._instance; + var updater = queue[i]; i++; Refs.clearRefs(); updater._didUpdate = updater._lifeStage === 2; - updater._didHook(); + updater._didHook(); //执行所有mount/update钩子(从下到上) updater._lifeStage = 1; updater._hydrating = false; if (!updater._renderInNextCycle) { updater._didUpdate = false; } - updater._ref(); - /* - if (!updater._lifeStage) { - Refs.clearRefs(); - updater._didHook(); - updater._lifeStage = 1; - updater._hydrating = false; - updater._ref(); - } else { - callUpdate(updater, instance); - }*/ + updater._ref(); //执行组件虚拟DOM的ref //如果组件在componentDidMount中调用setState if (updater._renderInNextCycle) { options.refreshComponent(updater, queue); - // callUpdate(updater, instance); } } //再执行所有setState/forceUpdate回调,根据从下到上的顺序执行 @@ -2108,9 +2079,7 @@ function mountElement(lastNode, vnode, vparent, context, updateQueue) { ref = vnode.ref; var dom = genMountElement(lastNode, vnode, vparent, type); - vnode._hostNode = dom; - var children = flattenChildren(vnode); var method = lastNode ? alignChildren : mountChildren; method(dom, children, vnode, context, updateQueue); @@ -2237,13 +2206,13 @@ function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQue return alignVnode(updater.rendered, nextRendered, vparent, childContext, updateQueue, updater); }); } - if (updateQueue.isMainProcess) { + /* if (updateQueue.isMainProcess) { queue = updateQueue; queue = []; } else { queue = updateQueue; - } - refreshComponent(updater, queue); + }*/ + refreshComponent(updater, updateQueue); //子组件先执行 updateQueue.push(updater); return updater._hostNode; @@ -2298,7 +2267,6 @@ function refreshComponent(updater, updateQueue) { options.afterUpdate(instance); }; - // updater._hydrating = false; updateQueue.push(updater); return dom; } @@ -2325,11 +2293,6 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { dom = lastVnode._hostNode, ref = lastVnode.ref, checkProps = lastVnode.checkProps; - - if (dom === null) { - console.log("此节点已经被移除", vparent); - return null; - } var nextProps = nextVnode.props, nextRef = nextVnode.ref; @@ -2371,8 +2334,7 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { } if (nextLength === lastLength && lastLength === 1) { lastChildren[0]._hostNode = parentNode.firstChild; - dom = alignVnode(lastChildren[0], nextChildren[0], lastVnode, context, updateQueue); - return; + return alignVnode(lastChildren[0], nextChildren[0], lastVnode, context, updateQueue); } var maxLength = Math.max(nextLength, lastLength), insertPoint = parentNode.firstChild, diff --git a/src/Refs.js b/src/Refs.js index 15da916cf..8b7786c14 100644 --- a/src/Refs.js +++ b/src/Refs.js @@ -24,12 +24,10 @@ export var Refs = { if(ref){ if (ref.string && nextRef.string ? ref.string !== nextRef.string: ref !== getDOMNode ) { ref(null); - // pendingRefs.push(ref.bind(0,null)); } } if(dom && nextRef !== getDOMNode){ nextRef(dom); - // pendingRefs.push(nextRef.bind(0,dom)); } }, createInstanceRef: function(updater, ref){ diff --git a/src/Updater.js b/src/Updater.js index b9f461d4d..0d105861a 100644 --- a/src/Updater.js +++ b/src/Updater.js @@ -51,9 +51,9 @@ Updater.prototype = { let { vnode, parentContext, _instance: instance } = this; //调整全局的 CurrentOwner.cur if (!rendered) { + let lastOwn = Refs.currentOwner; + Refs.currentOwner = instance; try { - var lastOwn = Refs.currentOwner; - Refs.currentOwner = instance; if (this.willReceive === false) { rendered = this.rendered; delete this.willReceive; diff --git a/src/diff.js b/src/diff.js index 6022df89a..12d898a8b 100644 --- a/src/diff.js +++ b/src/diff.js @@ -158,10 +158,8 @@ const formElements = { function mountElement(lastNode, vnode, vparent, context, updateQueue) { let { type, props, ref } = vnode; let dom = genMountElement(lastNode, vnode, vparent, type); - vnode._hostNode = dom; - - var children = flattenChildren(vnode); + let children = flattenChildren(vnode); let method = lastNode ? alignChildren : mountChildren; method(dom, children, vnode, context, updateQueue); if (vnode.checkProps) { @@ -287,13 +285,13 @@ function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQue return alignVnode(updater.rendered, nextRendered, vparent, childContext, updateQueue, updater); }); } - if (updateQueue.isMainProcess) { + /* if (updateQueue.isMainProcess) { queue = updateQueue; queue = []; } else { queue = updateQueue; - } - refreshComponent(updater, queue); + }*/ + refreshComponent(updater, updateQueue); //子组件先执行 updateQueue.push(updater); return updater._hostNode; @@ -339,7 +337,6 @@ function refreshComponent(updater, updateQueue) { options.afterUpdate(instance); }; - // updater._hydrating = false; updateQueue.push(updater); return dom; } @@ -363,10 +360,6 @@ export function alignVnode(lastVnode, nextVnode, vparent, context, updateQueue, function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { let { props: lastProps, _hostNode: dom, ref, checkProps } = lastVnode; - if (dom === null) { - console.log("此节点已经被移除", vparent); - return null; - } let { props: nextProps, ref: nextRef } = nextVnode; nextVnode._hostNode = dom; if (nextProps[innerHTML]) { @@ -406,8 +399,7 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { } if (nextLength === lastLength && lastLength === 1) { lastChildren[0]._hostNode = parentNode.firstChild; - dom = alignVnode(lastChildren[0], nextChildren[0], lastVnode, context, updateQueue); - return; + return alignVnode(lastChildren[0], nextChildren[0], lastVnode, context, updateQueue); } let maxLength = Math.max(nextLength, lastLength), insertPoint = parentNode.firstChild, diff --git a/src/dispose.js b/src/dispose.js index 2a8e531c7..3232a3bb1 100644 --- a/src/dispose.js +++ b/src/dispose.js @@ -39,7 +39,6 @@ function disposeElement(vnode) { } function disposeComponent(vnode) { - let instance = vnode._instance; if (instance) { options.beforeUnmount(instance); @@ -59,6 +58,6 @@ function disposeComponent(vnode) { } //在执行componentWillUnmount后才将关联的元素节点解绑,防止用户在钩子里调用 findDOMNode方法 disposeVnode(updater.rendered); - vnode.ref = vnode._instance = instance.updater = null; + updater._renderInNextCycle = vnode._instance = instance.updater = null; } } diff --git a/src/scheduler.js b/src/scheduler.js index 5c553abcd..18a29c97d 100644 --- a/src/scheduler.js +++ b/src/scheduler.js @@ -6,53 +6,27 @@ import { Refs } from "./Refs"; -/* -function callUpdate(updater, instance) { - Refs.clearRefs(); - if (updater._lifeStage === 2) { - updater._didUpdate = true; - instance._didUpdate(); - updater._lifeStage = 1; - updater._hydrating = false; - if (!updater._renderInNextCycle) { - updater._didUpdate = false; - } - } - updater._ref(); -}*/ - export function drainQueue(queue) { options.beforePatch(); //先执行所有refs方法(从上到下) - Refs.clearRefs(); - //再执行所有mount/update钩子(从下到上) + Refs.clearRefs();//假如一个组件实例也没有,也要把所有元素虚拟DOM的ref执行 + let i = 0; while(i < queue.length){//queue可能中途加入新元素, 因此不能直接使用queue.forEach(fn) - var updater = queue[i];//, instance = updater._instance; + let updater = queue[i]; i++; Refs.clearRefs(); updater._didUpdate = updater._lifeStage === 2; - updater._didHook(); + updater._didHook(); //执行所有mount/update钩子(从下到上) updater._lifeStage = 1; updater._hydrating = false; if (!updater._renderInNextCycle) { updater._didUpdate = false; } - updater._ref(); - /* - if (!updater._lifeStage) { - Refs.clearRefs(); - updater._didHook(); - updater._lifeStage = 1; - updater._hydrating = false; - updater._ref(); - } else { - callUpdate(updater, instance); - }*/ + updater._ref();//执行组件虚拟DOM的ref //如果组件在componentDidMount中调用setState if (updater._renderInNextCycle) { options.refreshComponent(updater, queue); - // callUpdate(updater, instance); } } From e321dc761926b703fcb5bc23ce722376c38d30af Mon Sep 17 00:00:00 2001 From: RubyLouvre <1669866773@qq.com> Date: Sun, 8 Oct 2017 21:14:43 +0800 Subject: [PATCH 43/57] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E6=97=A5=E5=BF=97=EF=BC=8C=E5=87=86=E5=A4=87=E5=8D=87=E7=BA=A7?= =?UTF-8?q?=E5=88=B01.1.3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build/shim.script.js | 2 +- version.md | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/build/shim.script.js b/build/shim.script.js index eec93bce3..60b3f0d2c 100644 --- a/build/shim.script.js +++ b/build/shim.script.js @@ -43,7 +43,7 @@ var text4 = str4 .replace(/\/\/freeze_start([\s\S]+?)freeze_end/, ""); fs.writeFileSync(dir4, text4, { encoding: "utf8" }); -//fs.writeFileSync( path.join(__dirname, "../../antd-test/node_modules/anujs/dist/ReactSelection.js"), text2, { encoding: "utf8" }); +// fs.writeFileSync( path.join(__dirname, "../../antd-test/node_modules/anujs/dist/ReactSelection.js"), text2, { encoding: "utf8" }); console.log("对ReactSelection瘦身完毕"); // eslint-disable-line diff --git a/version.md b/version.md index ad6a4eec7..a445b8af2 100644 --- a/version.md +++ b/version.md @@ -5,6 +5,7 @@ 4. 修正checkbox点一下会触发两次onChange的BUG 5. 添加ReceiveComponent检测机制,如果context,props一样,那么就不会执行receive, render, update等钩子 6. 修改检测空对象的逻辑 +7. 简化任务调度系统的逻辑 ## 1.1.2 1. 修正 onChange 事件 From f5abff009a5e25e2a08338c7542d3fc39fb1006f Mon Sep 17 00:00:00 2001 From: RubyLouvre <1669866773@qq.com> Date: Sun, 8 Oct 2017 21:14:50 +0800 Subject: [PATCH 44/57] 1.1.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a640aa757..ac8313a36 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "anujs", - "version": "1.1.2", + "version": "1.1.3", "description": "a mini React-like framework", "main": "dist/React.js", "scripts": { From 9e41442aef0cce077b8c11effdbd147bcdd88f6f Mon Sep 17 00:00:00 2001 From: RubyLouvre <1669866773@qq.com> Date: Sun, 8 Oct 2017 21:17:49 +0800 Subject: [PATCH 45/57] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=88=B01.1.3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dist/React.js | 2 +- dist/ReactIE.js | 2 +- dist/ReactSelection.js | 2 +- dist/ReactShim.js | 2 +- package.json | 1 - 5 files changed, 4 insertions(+), 5 deletions(-) diff --git a/dist/React.js b/dist/React.js index 41ad0fa52..d886e4016 100644 --- a/dist/React.js +++ b/dist/React.js @@ -2598,7 +2598,7 @@ function isSameNode(a, b) { } var React = { - version: "1.1.2", + version: "1.1.3", render: render, options: options, PropTypes: PropTypes, diff --git a/dist/ReactIE.js b/dist/ReactIE.js index 43052c37f..eb2d5e33c 100644 --- a/dist/ReactIE.js +++ b/dist/ReactIE.js @@ -2715,7 +2715,7 @@ if (msie < 9) { } var React = { - version: "1.1.2", + version: "1.1.3", render: render, options: options, PropTypes: PropTypes, diff --git a/dist/ReactSelection.js b/dist/ReactSelection.js index 43052c37f..eb2d5e33c 100644 --- a/dist/ReactSelection.js +++ b/dist/ReactSelection.js @@ -2715,7 +2715,7 @@ if (msie < 9) { } var React = { - version: "1.1.2", + version: "1.1.3", render: render, options: options, PropTypes: PropTypes, diff --git a/dist/ReactShim.js b/dist/ReactShim.js index 2faabc6a9..9fe51f604 100644 --- a/dist/ReactShim.js +++ b/dist/ReactShim.js @@ -2439,7 +2439,7 @@ function isSameNode(a, b) { } var React = { - version: "1.1.2", + version: "1.1.3", render: render, options: options, Children: Children, //支持react-redux diff --git a/package.json b/package.json index ac8313a36..4a533df50 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,6 @@ "babel-preset-react": "^6.24.1", "babel-runtime": "^5.8.38", "chai": "^3.5.0", - "chai-spies": "^0.7.1", "es3ify-webpack-plugin": "0.0.1", "eslint": "^4.2.0", "eslint-plugin-react": "^7.1.0", From 7d21393a81b2ef9e74ae3c7e4b4f6ba2812293c0 Mon Sep 17 00:00:00 2001 From: "waliang.wang" Date: Mon, 9 Oct 2017 14:05:31 +0800 Subject: [PATCH 46/57] fix: mountElement updateElement --- dist/React.js | 13 +++++++++---- dist/ReactIE.js | 13 +++++++++---- dist/ReactSelection.js | 13 +++++++++---- dist/ReactShim.js | 13 +++++++++---- src/diff.js | 11 ++++++++--- 5 files changed, 44 insertions(+), 19 deletions(-) diff --git a/dist/React.js b/dist/React.js index d886e4016..0c40dd601 100644 --- a/dist/React.js +++ b/dist/React.js @@ -1,5 +1,5 @@ /** - * by 司徒正美 Copyright 2017-10-08 + * by 司徒正美 Copyright 2017-10-09 * IE9+ */ @@ -2242,7 +2242,7 @@ function mountElement(lastNode, vnode, vparent, context, updateQueue) { var children = flattenChildren(vnode); var method = lastNode ? alignChildren : mountChildren; method(dom, children, vnode, context, updateQueue); - if (vnode.checkProps) { + if (vnode.checkProps && dom) { diffProps(props, {}, vnode, {}, dom); } if (ref) { @@ -2466,10 +2466,15 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { if (lastProps[innerHTML]) { dom.vchildren = []; } - diffChildren(lastVnode, nextVnode, dom, context, updateQueue); + if (!dom) { + console.log('dom', dom); + } + if (dom) { + diffChildren(lastVnode, nextVnode, dom, context, updateQueue); + } } - if (checkProps || nextVnode.checkProps) { + if ((checkProps || nextVnode.checkProps) && dom) { diffProps(nextProps, lastProps, nextVnode, lastVnode, dom); } if (nextVnode.type === "select") { diff --git a/dist/ReactIE.js b/dist/ReactIE.js index eb2d5e33c..4dacf5fd1 100644 --- a/dist/ReactIE.js +++ b/dist/ReactIE.js @@ -1,5 +1,5 @@ /** - * IE6+,有问题请加QQ 370262116 by 司徒正美 Copyright 2017-10-08 + * IE6+,有问题请加QQ 370262116 by 司徒正美 Copyright 2017-10-09 */ (function (global, factory) { @@ -2241,7 +2241,7 @@ function mountElement(lastNode, vnode, vparent, context, updateQueue) { var children = flattenChildren(vnode); var method = lastNode ? alignChildren : mountChildren; method(dom, children, vnode, context, updateQueue); - if (vnode.checkProps) { + if (vnode.checkProps && dom) { diffProps(props, {}, vnode, {}, dom); } if (ref) { @@ -2465,10 +2465,15 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { if (lastProps[innerHTML]) { dom.vchildren = []; } - diffChildren(lastVnode, nextVnode, dom, context, updateQueue); + if (!dom) { + console.log('dom', dom); + } + if (dom) { + diffChildren(lastVnode, nextVnode, dom, context, updateQueue); + } } - if (checkProps || nextVnode.checkProps) { + if ((checkProps || nextVnode.checkProps) && dom) { diffProps(nextProps, lastProps, nextVnode, lastVnode, dom); } if (nextVnode.type === "select") { diff --git a/dist/ReactSelection.js b/dist/ReactSelection.js index eb2d5e33c..4dacf5fd1 100644 --- a/dist/ReactSelection.js +++ b/dist/ReactSelection.js @@ -1,5 +1,5 @@ /** - * IE6+,有问题请加QQ 370262116 by 司徒正美 Copyright 2017-10-08 + * IE6+,有问题请加QQ 370262116 by 司徒正美 Copyright 2017-10-09 */ (function (global, factory) { @@ -2241,7 +2241,7 @@ function mountElement(lastNode, vnode, vparent, context, updateQueue) { var children = flattenChildren(vnode); var method = lastNode ? alignChildren : mountChildren; method(dom, children, vnode, context, updateQueue); - if (vnode.checkProps) { + if (vnode.checkProps && dom) { diffProps(props, {}, vnode, {}, dom); } if (ref) { @@ -2465,10 +2465,15 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { if (lastProps[innerHTML]) { dom.vchildren = []; } - diffChildren(lastVnode, nextVnode, dom, context, updateQueue); + if (!dom) { + console.log('dom', dom); + } + if (dom) { + diffChildren(lastVnode, nextVnode, dom, context, updateQueue); + } } - if (checkProps || nextVnode.checkProps) { + if ((checkProps || nextVnode.checkProps) && dom) { diffProps(nextProps, lastProps, nextVnode, lastVnode, dom); } if (nextVnode.type === "select") { diff --git a/dist/ReactShim.js b/dist/ReactShim.js index 9fe51f604..affd2a664 100644 --- a/dist/ReactShim.js +++ b/dist/ReactShim.js @@ -1,7 +1,7 @@ /** * 此版本要求浏览器没有createClass, createFactory, PropTypes, isValidElement, * unmountComponentAtNode,unstable_renderSubtreeIntoContainer - * QQ 370262116 by 司徒正美 Copyright 2017-10-08 + * QQ 370262116 by 司徒正美 Copyright 2017-10-09 */ (function (global, factory) { @@ -2083,7 +2083,7 @@ function mountElement(lastNode, vnode, vparent, context, updateQueue) { var children = flattenChildren(vnode); var method = lastNode ? alignChildren : mountChildren; method(dom, children, vnode, context, updateQueue); - if (vnode.checkProps) { + if (vnode.checkProps && dom) { diffProps(props, {}, vnode, {}, dom); } if (ref) { @@ -2307,10 +2307,15 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { if (lastProps[innerHTML]) { dom.vchildren = []; } - diffChildren(lastVnode, nextVnode, dom, context, updateQueue); + if (!dom) { + console.log('dom', dom); + } + if (dom) { + diffChildren(lastVnode, nextVnode, dom, context, updateQueue); + } } - if (checkProps || nextVnode.checkProps) { + if ((checkProps || nextVnode.checkProps) && dom) { diffProps(nextProps, lastProps, nextVnode, lastVnode, dom); } if (nextVnode.type === "select") { diff --git a/src/diff.js b/src/diff.js index 12d898a8b..211c5e842 100644 --- a/src/diff.js +++ b/src/diff.js @@ -162,7 +162,7 @@ function mountElement(lastNode, vnode, vparent, context, updateQueue) { let children = flattenChildren(vnode); let method = lastNode ? alignChildren : mountChildren; method(dom, children, vnode, context, updateQueue); - if (vnode.checkProps) { + if (vnode.checkProps && dom) { diffProps(props, {}, vnode, {}, dom); } if (ref) { @@ -372,10 +372,15 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { if (lastProps[innerHTML]) { dom.vchildren = []; } - diffChildren(lastVnode, nextVnode, dom, context, updateQueue); + if (!dom) { + console.log('dom', dom) + } + if (dom) { + diffChildren(lastVnode, nextVnode, dom, context, updateQueue); + } } - if (checkProps || nextVnode.checkProps) { + if ((checkProps || nextVnode.checkProps) && dom) { diffProps(nextProps, lastProps, nextVnode, lastVnode, dom); } if (nextVnode.type === "select") { From 838d869263979bbceefd99c0d639f8d0ab4f1f63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A5=9D=E9=91=AB=E5=A5=94?= Date: Mon, 9 Oct 2017 19:46:30 +0800 Subject: [PATCH 47/57] fix issue of _hostNode is null --- dist/React.js | 6 ++++-- dist/ReactIE.js | 6 ++++-- dist/ReactSelection.js | 6 ++++-- dist/ReactShim.js | 6 ++++-- src/diff.js | 4 +++- 5 files changed, 19 insertions(+), 9 deletions(-) diff --git a/dist/React.js b/dist/React.js index d886e4016..d2f8b8245 100644 --- a/dist/React.js +++ b/dist/React.js @@ -1,5 +1,5 @@ /** - * by 司徒正美 Copyright 2017-10-08 + * by 司徒正美 Copyright 2017-10-09 * IE9+ */ @@ -2492,7 +2492,9 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { return mountChildren(parentNode, nextChildren, lastVnode, context, updateQueue); } if (nextLength === lastLength && lastLength === 1) { - lastChildren[0]._hostNode = parentNode.firstChild; + if (parentNode.firstChild) { + lastChildren[0]._hostNode = parentNode.firstChild; + } return alignVnode(lastChildren[0], nextChildren[0], lastVnode, context, updateQueue); } var maxLength = Math.max(nextLength, lastLength), diff --git a/dist/ReactIE.js b/dist/ReactIE.js index eb2d5e33c..e97eee015 100644 --- a/dist/ReactIE.js +++ b/dist/ReactIE.js @@ -1,5 +1,5 @@ /** - * IE6+,有问题请加QQ 370262116 by 司徒正美 Copyright 2017-10-08 + * IE6+,有问题请加QQ 370262116 by 司徒正美 Copyright 2017-10-09 */ (function (global, factory) { @@ -2491,7 +2491,9 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { return mountChildren(parentNode, nextChildren, lastVnode, context, updateQueue); } if (nextLength === lastLength && lastLength === 1) { - lastChildren[0]._hostNode = parentNode.firstChild; + if (parentNode.firstChild) { + lastChildren[0]._hostNode = parentNode.firstChild; + } return alignVnode(lastChildren[0], nextChildren[0], lastVnode, context, updateQueue); } var maxLength = Math.max(nextLength, lastLength), diff --git a/dist/ReactSelection.js b/dist/ReactSelection.js index eb2d5e33c..e97eee015 100644 --- a/dist/ReactSelection.js +++ b/dist/ReactSelection.js @@ -1,5 +1,5 @@ /** - * IE6+,有问题请加QQ 370262116 by 司徒正美 Copyright 2017-10-08 + * IE6+,有问题请加QQ 370262116 by 司徒正美 Copyright 2017-10-09 */ (function (global, factory) { @@ -2491,7 +2491,9 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { return mountChildren(parentNode, nextChildren, lastVnode, context, updateQueue); } if (nextLength === lastLength && lastLength === 1) { - lastChildren[0]._hostNode = parentNode.firstChild; + if (parentNode.firstChild) { + lastChildren[0]._hostNode = parentNode.firstChild; + } return alignVnode(lastChildren[0], nextChildren[0], lastVnode, context, updateQueue); } var maxLength = Math.max(nextLength, lastLength), diff --git a/dist/ReactShim.js b/dist/ReactShim.js index 9fe51f604..28b7db7b2 100644 --- a/dist/ReactShim.js +++ b/dist/ReactShim.js @@ -1,7 +1,7 @@ /** * 此版本要求浏览器没有createClass, createFactory, PropTypes, isValidElement, * unmountComponentAtNode,unstable_renderSubtreeIntoContainer - * QQ 370262116 by 司徒正美 Copyright 2017-10-08 + * QQ 370262116 by 司徒正美 Copyright 2017-10-09 */ (function (global, factory) { @@ -2333,7 +2333,9 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { return mountChildren(parentNode, nextChildren, lastVnode, context, updateQueue); } if (nextLength === lastLength && lastLength === 1) { - lastChildren[0]._hostNode = parentNode.firstChild; + if (parentNode.firstChild) { + lastChildren[0]._hostNode = parentNode.firstChild; + } return alignVnode(lastChildren[0], nextChildren[0], lastVnode, context, updateQueue); } var maxLength = Math.max(nextLength, lastLength), diff --git a/src/diff.js b/src/diff.js index 12d898a8b..2b8a1b964 100644 --- a/src/diff.js +++ b/src/diff.js @@ -398,7 +398,9 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { return mountChildren(parentNode, nextChildren, lastVnode, context, updateQueue); } if (nextLength === lastLength && lastLength === 1) { - lastChildren[0]._hostNode = parentNode.firstChild; + if (parentNode.firstChild) { + lastChildren[0]._hostNode = parentNode.firstChild; + } return alignVnode(lastChildren[0], nextChildren[0], lastVnode, context, updateQueue); } let maxLength = Math.max(nextLength, lastLength), From 130c4ff07226cc29c97b4b1e1cf5581cf96b3615 Mon Sep 17 00:00:00 2001 From: RubyLouvre <1669866773@qq.com> Date: Tue, 10 Oct 2017 11:47:27 +0800 Subject: [PATCH 48/57] =?UTF-8?q?=E6=B7=BB=E5=8A=A0React16=E7=9A=84createP?= =?UTF-8?q?ortal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/React.js | 5 ++--- src/ReactIE.js | 3 ++- src/ReactShim.js | 3 ++- src/diff.js | 4 ++++ version.md | 9 ++++----- 5 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/React.js b/src/React.js index 24ee7320e..cfeae419b 100644 --- a/src/React.js +++ b/src/React.js @@ -8,9 +8,8 @@ import { createClass } from "./createClass"; import { cloneElement } from "./cloneElement"; import { PureComponent } from "./PureComponent"; import { createElement } from "./createElement"; -import { pendingRefs } from "./Refs"; -import { render,findDOMNode, isValidElement, unmountComponentAtNode, unstable_renderSubtreeIntoContainer } from "./diff"; +import { render,createPortal, findDOMNode, isValidElement, unmountComponentAtNode, unstable_renderSubtreeIntoContainer } from "./diff"; var React = { version: "VERSION", @@ -18,7 +17,7 @@ var React = { options, PropTypes, Children, //为了react-redux - pendingRefs, + createPortal, Component, eventSystem, findDOMNode, diff --git a/src/ReactIE.js b/src/ReactIE.js index 6f8a74082..85c60584c 100644 --- a/src/ReactIE.js +++ b/src/ReactIE.js @@ -9,7 +9,7 @@ import { cloneElement } from "./cloneElement"; import { PureComponent } from "./PureComponent"; import { createElement } from "./createElement"; -import { render, findDOMNode, isValidElement, unmountComponentAtNode, unstable_renderSubtreeIntoContainer } from "./diff"; +import { render, createPortal, findDOMNode, isValidElement, unmountComponentAtNode, unstable_renderSubtreeIntoContainer } from "./diff"; import "./compat"; @@ -23,6 +23,7 @@ var React = { eventSystem, findDOMNode, createClass, + createPortal, createElement, cloneElement, PureComponent, diff --git a/src/ReactShim.js b/src/ReactShim.js index 039af6108..b10fe79de 100644 --- a/src/ReactShim.js +++ b/src/ReactShim.js @@ -6,7 +6,7 @@ import { createElement } from "./createElement"; import { cloneElement } from "./cloneElement"; import { PureComponent } from "./PureComponent"; -import { render, findDOMNode, unmountComponentAtNode } from "./diff"; +import { render,createPortal, findDOMNode, unmountComponentAtNode } from "./diff"; var React = { version: "VERSION", @@ -15,6 +15,7 @@ var React = { Children, //支持react-redux Component, findDOMNode, + createPortal, createElement, cloneElement, PureComponent, diff --git a/src/diff.js b/src/diff.js index 12d898a8b..f1d44a5c4 100644 --- a/src/diff.js +++ b/src/diff.js @@ -42,6 +42,10 @@ export function findDOMNode(ref) { return ref.updater ? ref.updater._hostNode : ref._hostNode || null; } +//[Top API] ReactDOM.createPortal +export function createPortal(children, container) { + return renderByAnu(children, container); +} // 用于辅助XML元素的生成(svg, math), // 它们需要根据父节点的tagName与namespaceURI,知道自己是存在什么文档中 function getVParent(container) { diff --git a/version.md b/version.md index a445b8af2..d134c658f 100644 --- a/version.md +++ b/version.md @@ -1,11 +1,10 @@ ## 1.1.3 1. 抽象出一个Update类,用于封装组件实例上的所有私有数据 2. 抽象出一个instantiateComponente用于同时实例化有状态与无状态组件,从此再没有mountStateless, updateStateless方法 -3. 使用detachRef安全清空无用的ref数据 -4. 修正checkbox点一下会触发两次onChange的BUG -5. 添加ReceiveComponent检测机制,如果context,props一样,那么就不会执行receive, render, update等钩子 -6. 修改检测空对象的逻辑 -7. 简化任务调度系统的逻辑 +3. 修正checkbox点一下会触发两次onChange的BUG +4. 添加ReceiveComponent检测机制,如果context,props一样,那么就不会执行receive, render, update等钩子 +5. 修改检测空对象的逻辑 +6. 简化任务调度系统的逻辑 ## 1.1.2 1. 修正 onChange 事件 From 57bfc14c4611c0d580d346dd5d18642005cc88a8 Mon Sep 17 00:00:00 2001 From: RubyLouvre <1669866773@qq.com> Date: Tue, 10 Oct 2017 13:02:58 +0800 Subject: [PATCH 49/57] =?UTF-8?q?=E6=B7=BB=E5=8A=A0React16=E7=9A=84createP?= =?UTF-8?q?ortal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dist/React.js | 8 ++++++-- dist/ReactIE.js | 7 ++++++- dist/ReactSelection.js | 7 ++++++- dist/ReactShim.js | 7 ++++++- 4 files changed, 24 insertions(+), 5 deletions(-) diff --git a/dist/React.js b/dist/React.js index d2f8b8245..e02dd974d 100644 --- a/dist/React.js +++ b/dist/React.js @@ -1,5 +1,5 @@ /** - * by 司徒正美 Copyright 2017-10-09 + * by 司徒正美 Copyright 2017-10-10 * IE9+ */ @@ -2117,6 +2117,10 @@ function findDOMNode(ref) { return ref.updater ? ref.updater._hostNode : ref._hostNode || null; } +//[Top API] ReactDOM.createPortal +function createPortal(children, container) { + return renderByAnu(children, container); +} // 用于辅助XML元素的生成(svg, math), // 它们需要根据父节点的tagName与namespaceURI,知道自己是存在什么文档中 function getVParent(container) { @@ -2605,7 +2609,7 @@ var React = { options: options, PropTypes: PropTypes, Children: Children, //为了react-redux - pendingRefs: pendingRefs, + createPortal: createPortal, Component: Component, eventSystem: eventSystem, findDOMNode: findDOMNode, diff --git a/dist/ReactIE.js b/dist/ReactIE.js index e97eee015..ac960b2d8 100644 --- a/dist/ReactIE.js +++ b/dist/ReactIE.js @@ -1,5 +1,5 @@ /** - * IE6+,有问题请加QQ 370262116 by 司徒正美 Copyright 2017-10-09 + * IE6+,有问题请加QQ 370262116 by 司徒正美 Copyright 2017-10-10 */ (function (global, factory) { @@ -2116,6 +2116,10 @@ function findDOMNode(ref) { return ref.updater ? ref.updater._hostNode : ref._hostNode || null; } +//[Top API] ReactDOM.createPortal +function createPortal(children, container) { + return renderByAnu(children, container); +} // 用于辅助XML元素的生成(svg, math), // 它们需要根据父节点的tagName与namespaceURI,知道自己是存在什么文档中 function getVParent(container) { @@ -2726,6 +2730,7 @@ var React = { eventSystem: eventSystem, findDOMNode: findDOMNode, createClass: createClass, + createPortal: createPortal, createElement: createElement, cloneElement: cloneElement, PureComponent: PureComponent, diff --git a/dist/ReactSelection.js b/dist/ReactSelection.js index e97eee015..ac960b2d8 100644 --- a/dist/ReactSelection.js +++ b/dist/ReactSelection.js @@ -1,5 +1,5 @@ /** - * IE6+,有问题请加QQ 370262116 by 司徒正美 Copyright 2017-10-09 + * IE6+,有问题请加QQ 370262116 by 司徒正美 Copyright 2017-10-10 */ (function (global, factory) { @@ -2116,6 +2116,10 @@ function findDOMNode(ref) { return ref.updater ? ref.updater._hostNode : ref._hostNode || null; } +//[Top API] ReactDOM.createPortal +function createPortal(children, container) { + return renderByAnu(children, container); +} // 用于辅助XML元素的生成(svg, math), // 它们需要根据父节点的tagName与namespaceURI,知道自己是存在什么文档中 function getVParent(container) { @@ -2726,6 +2730,7 @@ var React = { eventSystem: eventSystem, findDOMNode: findDOMNode, createClass: createClass, + createPortal: createPortal, createElement: createElement, cloneElement: cloneElement, PureComponent: PureComponent, diff --git a/dist/ReactShim.js b/dist/ReactShim.js index 28b7db7b2..0bf8be8fb 100644 --- a/dist/ReactShim.js +++ b/dist/ReactShim.js @@ -1,7 +1,7 @@ /** * 此版本要求浏览器没有createClass, createFactory, PropTypes, isValidElement, * unmountComponentAtNode,unstable_renderSubtreeIntoContainer - * QQ 370262116 by 司徒正美 Copyright 2017-10-09 + * QQ 370262116 by 司徒正美 Copyright 2017-10-10 */ (function (global, factory) { @@ -1958,6 +1958,10 @@ function findDOMNode(ref) { return ref.updater ? ref.updater._hostNode : ref._hostNode || null; } +//[Top API] ReactDOM.createPortal +function createPortal(children, container) { + return renderByAnu(children, container); +} // 用于辅助XML元素的生成(svg, math), // 它们需要根据父节点的tagName与namespaceURI,知道自己是存在什么文档中 function getVParent(container) { @@ -2447,6 +2451,7 @@ var React = { Children: Children, //支持react-redux Component: Component, findDOMNode: findDOMNode, + createPortal: createPortal, createElement: createElement, cloneElement: cloneElement, PureComponent: PureComponent, From 57a1deeb1bd90bec8e0cca964b16260540644d8b Mon Sep 17 00:00:00 2001 From: "waliang.wang" Date: Tue, 10 Oct 2017 14:26:58 +0800 Subject: [PATCH 50/57] =?UTF-8?q?fix:=20diffChildren=20=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dist/React.js | 57 +++++++++++++++++++++++++++++++++++++----- dist/ReactIE.js | 57 +++++++++++++++++++++++++++++++++++++----- dist/ReactSelection.js | 57 +++++++++++++++++++++++++++++++++++++----- dist/ReactShim.js | 57 +++++++++++++++++++++++++++++++++++++----- src/diff.js | 36 ++++++++++++++++++++++---- 5 files changed, 235 insertions(+), 29 deletions(-) diff --git a/dist/React.js b/dist/React.js index 0c40dd601..fb58fb802 100644 --- a/dist/React.js +++ b/dist/React.js @@ -1,5 +1,5 @@ /** - * by 司徒正美 Copyright 2017-10-09 + * by 司徒正美 Copyright 2017-10-10 * IE9+ */ @@ -2466,9 +2466,6 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { if (lastProps[innerHTML]) { dom.vchildren = []; } - if (!dom) { - console.log('dom', dom); - } if (dom) { diffChildren(lastVnode, nextVnode, dom, context, updateQueue); } @@ -2484,12 +2481,31 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { return dom; } +function diffDomText(pastDom, dom, insertPoint) { + var isTrue = false; + var dText = dom.innerText.trim(); + var iText = insertPoint.innerText.trim(); + pastDom.forEach(function (v) { + if (v.innerText === dText || dText === iText) { + isTrue = !isTrue; + return false; + } + }); + return isTrue; +} + function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { var lastChildren = parentNode.vchildren, nextChildren = flattenChildren(nextVnode), nextLength = nextChildren.length, lastLength = lastChildren.length, - dom = void 0; + dom = void 0, + isTrue = false, + pastDom = []; + + var insertDom = function insertDom(dom) { + return parentNode.insertBefore(dom, insertPoint); + }; //如果旧数组长度为零, 直接添加 if (nextLength && !lastLength) { @@ -2559,8 +2575,35 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { lastChild = action[0]; nextChild = action[1]; dom = lastChild._hostNode; + if (action[2]) { - parentNode.insertBefore(dom, insertPoint); + // let abc = false; + // 如果有旧DOM记录 + if (pastDom.length && insertPoint.innerText && dom.innerText) { + isTrue = diffDomText(pastDom, dom, insertPoint); + // console.log('pastDom', pastDom) + // if (isTrue) { + // insertDom(dom); + // } + // pastDom.forEach(v => { + // if (v.innerText.trim() === insertPoint.innerText.trim()) { + // abc = true; + // } + // if (dom.innerText.trim() === insertPoint.innerText.trim()) { + // abc = true; + // } + // }); + if (!isTrue) { + insertDom(dom); + // parentNode.insertBefore(dom, insertPoint); + isTrue = false; + } + // 没有旧DOM记录 (这里代码不能合并) + } else { + insertDom(dom); + // parentNode.insertBefore(dom, insertPoint); + } + // pastDom.push(dom) } insertPoint = updateVnode(lastChild, nextChild, lastVnode, context, updateQueue); if (!nextChild._hostNode) { @@ -2574,8 +2617,10 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { if (removed && !removed._disposed && !removeHits[j]) { disposeVnode(removed); } + //如果找不到对应的旧节点,创建一个新节点放在这里 dom = mountVnode(null, nextChild, lastVnode, context, updateQueue); + pastDom.push(dom); parentNode.insertBefore(dom, insertPoint); insertPoint = dom; } diff --git a/dist/ReactIE.js b/dist/ReactIE.js index 4dacf5fd1..97beb631f 100644 --- a/dist/ReactIE.js +++ b/dist/ReactIE.js @@ -1,5 +1,5 @@ /** - * IE6+,有问题请加QQ 370262116 by 司徒正美 Copyright 2017-10-09 + * IE6+,有问题请加QQ 370262116 by 司徒正美 Copyright 2017-10-10 */ (function (global, factory) { @@ -2465,9 +2465,6 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { if (lastProps[innerHTML]) { dom.vchildren = []; } - if (!dom) { - console.log('dom', dom); - } if (dom) { diffChildren(lastVnode, nextVnode, dom, context, updateQueue); } @@ -2483,12 +2480,31 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { return dom; } +function diffDomText(pastDom, dom, insertPoint) { + var isTrue = false; + var dText = dom.innerText.trim(); + var iText = insertPoint.innerText.trim(); + pastDom.forEach(function (v) { + if (v.innerText === dText || dText === iText) { + isTrue = !isTrue; + return false; + } + }); + return isTrue; +} + function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { var lastChildren = parentNode.vchildren, nextChildren = flattenChildren(nextVnode), nextLength = nextChildren.length, lastLength = lastChildren.length, - dom = void 0; + dom = void 0, + isTrue = false, + pastDom = []; + + var insertDom = function insertDom(dom) { + return parentNode.insertBefore(dom, insertPoint); + }; //如果旧数组长度为零, 直接添加 if (nextLength && !lastLength) { @@ -2558,8 +2574,35 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { lastChild = action[0]; nextChild = action[1]; dom = lastChild._hostNode; + if (action[2]) { - parentNode.insertBefore(dom, insertPoint); + // let abc = false; + // 如果有旧DOM记录 + if (pastDom.length && insertPoint.innerText && dom.innerText) { + isTrue = diffDomText(pastDom, dom, insertPoint); + // console.log('pastDom', pastDom) + // if (isTrue) { + // insertDom(dom); + // } + // pastDom.forEach(v => { + // if (v.innerText.trim() === insertPoint.innerText.trim()) { + // abc = true; + // } + // if (dom.innerText.trim() === insertPoint.innerText.trim()) { + // abc = true; + // } + // }); + if (!isTrue) { + insertDom(dom); + // parentNode.insertBefore(dom, insertPoint); + isTrue = false; + } + // 没有旧DOM记录 (这里代码不能合并) + } else { + insertDom(dom); + // parentNode.insertBefore(dom, insertPoint); + } + // pastDom.push(dom) } insertPoint = updateVnode(lastChild, nextChild, lastVnode, context, updateQueue); if (!nextChild._hostNode) { @@ -2573,8 +2616,10 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { if (removed && !removed._disposed && !removeHits[j]) { disposeVnode(removed); } + //如果找不到对应的旧节点,创建一个新节点放在这里 dom = mountVnode(null, nextChild, lastVnode, context, updateQueue); + pastDom.push(dom); parentNode.insertBefore(dom, insertPoint); insertPoint = dom; } diff --git a/dist/ReactSelection.js b/dist/ReactSelection.js index 4dacf5fd1..97beb631f 100644 --- a/dist/ReactSelection.js +++ b/dist/ReactSelection.js @@ -1,5 +1,5 @@ /** - * IE6+,有问题请加QQ 370262116 by 司徒正美 Copyright 2017-10-09 + * IE6+,有问题请加QQ 370262116 by 司徒正美 Copyright 2017-10-10 */ (function (global, factory) { @@ -2465,9 +2465,6 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { if (lastProps[innerHTML]) { dom.vchildren = []; } - if (!dom) { - console.log('dom', dom); - } if (dom) { diffChildren(lastVnode, nextVnode, dom, context, updateQueue); } @@ -2483,12 +2480,31 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { return dom; } +function diffDomText(pastDom, dom, insertPoint) { + var isTrue = false; + var dText = dom.innerText.trim(); + var iText = insertPoint.innerText.trim(); + pastDom.forEach(function (v) { + if (v.innerText === dText || dText === iText) { + isTrue = !isTrue; + return false; + } + }); + return isTrue; +} + function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { var lastChildren = parentNode.vchildren, nextChildren = flattenChildren(nextVnode), nextLength = nextChildren.length, lastLength = lastChildren.length, - dom = void 0; + dom = void 0, + isTrue = false, + pastDom = []; + + var insertDom = function insertDom(dom) { + return parentNode.insertBefore(dom, insertPoint); + }; //如果旧数组长度为零, 直接添加 if (nextLength && !lastLength) { @@ -2558,8 +2574,35 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { lastChild = action[0]; nextChild = action[1]; dom = lastChild._hostNode; + if (action[2]) { - parentNode.insertBefore(dom, insertPoint); + // let abc = false; + // 如果有旧DOM记录 + if (pastDom.length && insertPoint.innerText && dom.innerText) { + isTrue = diffDomText(pastDom, dom, insertPoint); + // console.log('pastDom', pastDom) + // if (isTrue) { + // insertDom(dom); + // } + // pastDom.forEach(v => { + // if (v.innerText.trim() === insertPoint.innerText.trim()) { + // abc = true; + // } + // if (dom.innerText.trim() === insertPoint.innerText.trim()) { + // abc = true; + // } + // }); + if (!isTrue) { + insertDom(dom); + // parentNode.insertBefore(dom, insertPoint); + isTrue = false; + } + // 没有旧DOM记录 (这里代码不能合并) + } else { + insertDom(dom); + // parentNode.insertBefore(dom, insertPoint); + } + // pastDom.push(dom) } insertPoint = updateVnode(lastChild, nextChild, lastVnode, context, updateQueue); if (!nextChild._hostNode) { @@ -2573,8 +2616,10 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { if (removed && !removed._disposed && !removeHits[j]) { disposeVnode(removed); } + //如果找不到对应的旧节点,创建一个新节点放在这里 dom = mountVnode(null, nextChild, lastVnode, context, updateQueue); + pastDom.push(dom); parentNode.insertBefore(dom, insertPoint); insertPoint = dom; } diff --git a/dist/ReactShim.js b/dist/ReactShim.js index affd2a664..8fc843916 100644 --- a/dist/ReactShim.js +++ b/dist/ReactShim.js @@ -1,7 +1,7 @@ /** * 此版本要求浏览器没有createClass, createFactory, PropTypes, isValidElement, * unmountComponentAtNode,unstable_renderSubtreeIntoContainer - * QQ 370262116 by 司徒正美 Copyright 2017-10-09 + * QQ 370262116 by 司徒正美 Copyright 2017-10-10 */ (function (global, factory) { @@ -2307,9 +2307,6 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { if (lastProps[innerHTML]) { dom.vchildren = []; } - if (!dom) { - console.log('dom', dom); - } if (dom) { diffChildren(lastVnode, nextVnode, dom, context, updateQueue); } @@ -2325,12 +2322,31 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { return dom; } +function diffDomText(pastDom, dom, insertPoint) { + var isTrue = false; + var dText = dom.innerText.trim(); + var iText = insertPoint.innerText.trim(); + pastDom.forEach(function (v) { + if (v.innerText === dText || dText === iText) { + isTrue = !isTrue; + return false; + } + }); + return isTrue; +} + function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { var lastChildren = parentNode.vchildren, nextChildren = flattenChildren(nextVnode), nextLength = nextChildren.length, lastLength = lastChildren.length, - dom = void 0; + dom = void 0, + isTrue = false, + pastDom = []; + + var insertDom = function insertDom(dom) { + return parentNode.insertBefore(dom, insertPoint); + }; //如果旧数组长度为零, 直接添加 if (nextLength && !lastLength) { @@ -2400,8 +2416,35 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { lastChild = action[0]; nextChild = action[1]; dom = lastChild._hostNode; + if (action[2]) { - parentNode.insertBefore(dom, insertPoint); + // let abc = false; + // 如果有旧DOM记录 + if (pastDom.length && insertPoint.innerText && dom.innerText) { + isTrue = diffDomText(pastDom, dom, insertPoint); + // console.log('pastDom', pastDom) + // if (isTrue) { + // insertDom(dom); + // } + // pastDom.forEach(v => { + // if (v.innerText.trim() === insertPoint.innerText.trim()) { + // abc = true; + // } + // if (dom.innerText.trim() === insertPoint.innerText.trim()) { + // abc = true; + // } + // }); + if (!isTrue) { + insertDom(dom); + // parentNode.insertBefore(dom, insertPoint); + isTrue = false; + } + // 没有旧DOM记录 (这里代码不能合并) + } else { + insertDom(dom); + // parentNode.insertBefore(dom, insertPoint); + } + // pastDom.push(dom) } insertPoint = updateVnode(lastChild, nextChild, lastVnode, context, updateQueue); if (!nextChild._hostNode) { @@ -2415,8 +2458,10 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { if (removed && !removed._disposed && !removeHits[j]) { disposeVnode(removed); } + //如果找不到对应的旧节点,创建一个新节点放在这里 dom = mountVnode(null, nextChild, lastVnode, context, updateQueue); + pastDom.push(dom); parentNode.insertBefore(dom, insertPoint); insertPoint = dom; } diff --git a/src/diff.js b/src/diff.js index 211c5e842..5916c1783 100644 --- a/src/diff.js +++ b/src/diff.js @@ -372,9 +372,6 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { if (lastProps[innerHTML]) { dom.vchildren = []; } - if (!dom) { - console.log('dom', dom) - } if (dom) { diffChildren(lastVnode, nextVnode, dom, context, updateQueue); } @@ -390,11 +387,28 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { return dom; } +function diffDomText(pastDom, dom, insertPoint) { + const dText = dom.innerText.trim(); + const iText = insertPoint.innerText.trim(); + let isTrue = false; + + pastDom.forEach(v => { + if ((v.innerText === dText) || (dText === iText)) { + isTrue = !isTrue; + return false; + } + }); + return isTrue; +} + function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { + const insertDom = dom => parentNode.insertBefore(dom, insertPoint); let lastChildren = parentNode.vchildren, nextChildren = flattenChildren(nextVnode), nextLength = nextChildren.length, lastLength = lastChildren.length, + isTrue = false, + pastDom = [], dom; //如果旧数组长度为零, 直接添加 @@ -416,7 +430,6 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { nextChild, lastChild; //第一次循环,构建移动指令(actions)与移除名单(removeHits)与命中名单(fuzzyHits) - if (nextLength) { actions.length = nextLength; while (i < maxLength) { @@ -465,8 +478,19 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { lastChild = action[0]; nextChild = action[1]; dom = lastChild._hostNode; + if (action[2]) { - parentNode.insertBefore(dom, insertPoint); + // 如果有旧DOM记录 + if (pastDom.length && insertPoint.innerText && dom.innerText) { + isTrue = diffDomText(pastDom, dom, insertPoint); + if (!isTrue) { + insertDom(dom) + isTrue = false; + } + // 没有旧DOM记录 (这里代码不能合并) + } else { + insertDom(dom); + } } insertPoint = updateVnode(lastChild, nextChild, lastVnode, context, updateQueue); if (!nextChild._hostNode) { @@ -480,8 +504,10 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { if (removed && !removed._disposed && !removeHits[j]) { disposeVnode(removed); } + //如果找不到对应的旧节点,创建一个新节点放在这里 dom = mountVnode(null, nextChild, lastVnode, context, updateQueue); + pastDom.push(dom) parentNode.insertBefore(dom, insertPoint); insertPoint = dom; } From c1f56e8e1d815bf009176b8abd7cb50ce0a281a3 Mon Sep 17 00:00:00 2001 From: "waliang.wang" Date: Tue, 10 Oct 2017 14:57:29 +0800 Subject: [PATCH 51/57] =?UTF-8?q?=E7=BC=96=E8=AF=91dist=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dist/React.js | 31 +++++++------------------------ dist/ReactIE.js | 31 +++++++------------------------ dist/ReactSelection.js | 31 +++++++------------------------ dist/ReactShim.js | 31 +++++++------------------------ 4 files changed, 28 insertions(+), 96 deletions(-) diff --git a/dist/React.js b/dist/React.js index 4504f0049..ef6c4d117 100644 --- a/dist/React.js +++ b/dist/React.js @@ -2486,9 +2486,10 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { } function diffDomText(pastDom, dom, insertPoint) { - var isTrue = false; var dText = dom.innerText.trim(); var iText = insertPoint.innerText.trim(); + var isTrue = false; + pastDom.forEach(function (v) { if (v.innerText === dText || dText === iText) { isTrue = !isTrue; @@ -2499,17 +2500,16 @@ function diffDomText(pastDom, dom, insertPoint) { } function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { + var insertDom = function insertDom(dom) { + return parentNode.insertBefore(dom, insertPoint); + }; var lastChildren = parentNode.vchildren, nextChildren = flattenChildren(nextVnode), nextLength = nextChildren.length, lastLength = lastChildren.length, - dom = void 0, isTrue = false, - pastDom = []; - - var insertDom = function insertDom(dom) { - return parentNode.insertBefore(dom, insertPoint); - }; + pastDom = [], + dom = void 0; //如果旧数组长度为零, 直接添加 if (nextLength && !lastLength) { @@ -2532,7 +2532,6 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { nextChild = void 0, lastChild = void 0; //第一次循环,构建移动指令(actions)与移除名单(removeHits)与命中名单(fuzzyHits) - if (nextLength) { actions.length = nextLength; while (i < maxLength) { @@ -2583,33 +2582,17 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { dom = lastChild._hostNode; if (action[2]) { - // let abc = false; // 如果有旧DOM记录 if (pastDom.length && insertPoint.innerText && dom.innerText) { isTrue = diffDomText(pastDom, dom, insertPoint); - // console.log('pastDom', pastDom) - // if (isTrue) { - // insertDom(dom); - // } - // pastDom.forEach(v => { - // if (v.innerText.trim() === insertPoint.innerText.trim()) { - // abc = true; - // } - // if (dom.innerText.trim() === insertPoint.innerText.trim()) { - // abc = true; - // } - // }); if (!isTrue) { insertDom(dom); - // parentNode.insertBefore(dom, insertPoint); isTrue = false; } // 没有旧DOM记录 (这里代码不能合并) } else { insertDom(dom); - // parentNode.insertBefore(dom, insertPoint); } - // pastDom.push(dom) } insertPoint = updateVnode(lastChild, nextChild, lastVnode, context, updateQueue); if (!nextChild._hostNode) { diff --git a/dist/ReactIE.js b/dist/ReactIE.js index 3d89e1159..a7bc2e852 100644 --- a/dist/ReactIE.js +++ b/dist/ReactIE.js @@ -2485,9 +2485,10 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { } function diffDomText(pastDom, dom, insertPoint) { - var isTrue = false; var dText = dom.innerText.trim(); var iText = insertPoint.innerText.trim(); + var isTrue = false; + pastDom.forEach(function (v) { if (v.innerText === dText || dText === iText) { isTrue = !isTrue; @@ -2498,17 +2499,16 @@ function diffDomText(pastDom, dom, insertPoint) { } function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { + var insertDom = function insertDom(dom) { + return parentNode.insertBefore(dom, insertPoint); + }; var lastChildren = parentNode.vchildren, nextChildren = flattenChildren(nextVnode), nextLength = nextChildren.length, lastLength = lastChildren.length, - dom = void 0, isTrue = false, - pastDom = []; - - var insertDom = function insertDom(dom) { - return parentNode.insertBefore(dom, insertPoint); - }; + pastDom = [], + dom = void 0; //如果旧数组长度为零, 直接添加 if (nextLength && !lastLength) { @@ -2531,7 +2531,6 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { nextChild = void 0, lastChild = void 0; //第一次循环,构建移动指令(actions)与移除名单(removeHits)与命中名单(fuzzyHits) - if (nextLength) { actions.length = nextLength; while (i < maxLength) { @@ -2582,33 +2581,17 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { dom = lastChild._hostNode; if (action[2]) { - // let abc = false; // 如果有旧DOM记录 if (pastDom.length && insertPoint.innerText && dom.innerText) { isTrue = diffDomText(pastDom, dom, insertPoint); - // console.log('pastDom', pastDom) - // if (isTrue) { - // insertDom(dom); - // } - // pastDom.forEach(v => { - // if (v.innerText.trim() === insertPoint.innerText.trim()) { - // abc = true; - // } - // if (dom.innerText.trim() === insertPoint.innerText.trim()) { - // abc = true; - // } - // }); if (!isTrue) { insertDom(dom); - // parentNode.insertBefore(dom, insertPoint); isTrue = false; } // 没有旧DOM记录 (这里代码不能合并) } else { insertDom(dom); - // parentNode.insertBefore(dom, insertPoint); } - // pastDom.push(dom) } insertPoint = updateVnode(lastChild, nextChild, lastVnode, context, updateQueue); if (!nextChild._hostNode) { diff --git a/dist/ReactSelection.js b/dist/ReactSelection.js index 3d89e1159..a7bc2e852 100644 --- a/dist/ReactSelection.js +++ b/dist/ReactSelection.js @@ -2485,9 +2485,10 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { } function diffDomText(pastDom, dom, insertPoint) { - var isTrue = false; var dText = dom.innerText.trim(); var iText = insertPoint.innerText.trim(); + var isTrue = false; + pastDom.forEach(function (v) { if (v.innerText === dText || dText === iText) { isTrue = !isTrue; @@ -2498,17 +2499,16 @@ function diffDomText(pastDom, dom, insertPoint) { } function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { + var insertDom = function insertDom(dom) { + return parentNode.insertBefore(dom, insertPoint); + }; var lastChildren = parentNode.vchildren, nextChildren = flattenChildren(nextVnode), nextLength = nextChildren.length, lastLength = lastChildren.length, - dom = void 0, isTrue = false, - pastDom = []; - - var insertDom = function insertDom(dom) { - return parentNode.insertBefore(dom, insertPoint); - }; + pastDom = [], + dom = void 0; //如果旧数组长度为零, 直接添加 if (nextLength && !lastLength) { @@ -2531,7 +2531,6 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { nextChild = void 0, lastChild = void 0; //第一次循环,构建移动指令(actions)与移除名单(removeHits)与命中名单(fuzzyHits) - if (nextLength) { actions.length = nextLength; while (i < maxLength) { @@ -2582,33 +2581,17 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { dom = lastChild._hostNode; if (action[2]) { - // let abc = false; // 如果有旧DOM记录 if (pastDom.length && insertPoint.innerText && dom.innerText) { isTrue = diffDomText(pastDom, dom, insertPoint); - // console.log('pastDom', pastDom) - // if (isTrue) { - // insertDom(dom); - // } - // pastDom.forEach(v => { - // if (v.innerText.trim() === insertPoint.innerText.trim()) { - // abc = true; - // } - // if (dom.innerText.trim() === insertPoint.innerText.trim()) { - // abc = true; - // } - // }); if (!isTrue) { insertDom(dom); - // parentNode.insertBefore(dom, insertPoint); isTrue = false; } // 没有旧DOM记录 (这里代码不能合并) } else { insertDom(dom); - // parentNode.insertBefore(dom, insertPoint); } - // pastDom.push(dom) } insertPoint = updateVnode(lastChild, nextChild, lastVnode, context, updateQueue); if (!nextChild._hostNode) { diff --git a/dist/ReactShim.js b/dist/ReactShim.js index a52292752..9c9a45977 100644 --- a/dist/ReactShim.js +++ b/dist/ReactShim.js @@ -2327,9 +2327,10 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { } function diffDomText(pastDom, dom, insertPoint) { - var isTrue = false; var dText = dom.innerText.trim(); var iText = insertPoint.innerText.trim(); + var isTrue = false; + pastDom.forEach(function (v) { if (v.innerText === dText || dText === iText) { isTrue = !isTrue; @@ -2340,17 +2341,16 @@ function diffDomText(pastDom, dom, insertPoint) { } function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { + var insertDom = function insertDom(dom) { + return parentNode.insertBefore(dom, insertPoint); + }; var lastChildren = parentNode.vchildren, nextChildren = flattenChildren(nextVnode), nextLength = nextChildren.length, lastLength = lastChildren.length, - dom = void 0, isTrue = false, - pastDom = []; - - var insertDom = function insertDom(dom) { - return parentNode.insertBefore(dom, insertPoint); - }; + pastDom = [], + dom = void 0; //如果旧数组长度为零, 直接添加 if (nextLength && !lastLength) { @@ -2373,7 +2373,6 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { nextChild = void 0, lastChild = void 0; //第一次循环,构建移动指令(actions)与移除名单(removeHits)与命中名单(fuzzyHits) - if (nextLength) { actions.length = nextLength; while (i < maxLength) { @@ -2424,33 +2423,17 @@ function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { dom = lastChild._hostNode; if (action[2]) { - // let abc = false; // 如果有旧DOM记录 if (pastDom.length && insertPoint.innerText && dom.innerText) { isTrue = diffDomText(pastDom, dom, insertPoint); - // console.log('pastDom', pastDom) - // if (isTrue) { - // insertDom(dom); - // } - // pastDom.forEach(v => { - // if (v.innerText.trim() === insertPoint.innerText.trim()) { - // abc = true; - // } - // if (dom.innerText.trim() === insertPoint.innerText.trim()) { - // abc = true; - // } - // }); if (!isTrue) { insertDom(dom); - // parentNode.insertBefore(dom, insertPoint); isTrue = false; } // 没有旧DOM记录 (这里代码不能合并) } else { insertDom(dom); - // parentNode.insertBefore(dom, insertPoint); } - // pastDom.push(dom) } insertPoint = updateVnode(lastChild, nextChild, lastVnode, context, updateQueue); if (!nextChild._hostNode) { From 4659ec214902542311e90ba10b7dc0dd698d6fa6 Mon Sep 17 00:00:00 2001 From: RubyLouvre <1669866773@qq.com> Date: Tue, 10 Oct 2017 15:22:39 +0800 Subject: [PATCH 52/57] =?UTF-8?q?=E6=94=AF=E6=8C=81React16=E7=9A=84ReactDO?= =?UTF-8?q?M.createPortal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dist/React.js | 11 ++- dist/ReactIE.js | 11 ++- dist/ReactSelection.js | 11 ++- dist/ReactShim.js | 11 ++- index3.html | 179 +++++++++++++++++++++++++++-------------- src/diff.js | 11 ++- 6 files changed, 152 insertions(+), 82 deletions(-) diff --git a/dist/React.js b/dist/React.js index e02dd974d..62c2d129e 100644 --- a/dist/React.js +++ b/dist/React.js @@ -2119,7 +2119,11 @@ function findDOMNode(ref) { } //[Top API] ReactDOM.createPortal function createPortal(children, container) { - return renderByAnu(children, container); + if (!container.vchildren) { + container.vchildren = []; + } + diffChildren(getVParent(container), children, container, {}, []); + return null; } // 用于辅助XML元素的生成(svg, math), // 它们需要根据父节点的tagName与namespaceURI,知道自己是存在什么文档中 @@ -2470,7 +2474,7 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { if (lastProps[innerHTML]) { dom.vchildren = []; } - diffChildren(lastVnode, nextVnode, dom, context, updateQueue); + diffChildren(lastVnode, flattenChildren(nextVnode), dom, context, updateQueue); } if (checkProps || nextVnode.checkProps) { @@ -2483,9 +2487,8 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { return dom; } -function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { +function diffChildren(lastVnode, nextChildren, parentNode, context, updateQueue) { var lastChildren = parentNode.vchildren, - nextChildren = flattenChildren(nextVnode), nextLength = nextChildren.length, lastLength = lastChildren.length, dom = void 0; diff --git a/dist/ReactIE.js b/dist/ReactIE.js index ac960b2d8..06ca09027 100644 --- a/dist/ReactIE.js +++ b/dist/ReactIE.js @@ -2118,7 +2118,11 @@ function findDOMNode(ref) { } //[Top API] ReactDOM.createPortal function createPortal(children, container) { - return renderByAnu(children, container); + if (!container.vchildren) { + container.vchildren = []; + } + diffChildren(getVParent(container), children, container, {}, []); + return null; } // 用于辅助XML元素的生成(svg, math), // 它们需要根据父节点的tagName与namespaceURI,知道自己是存在什么文档中 @@ -2469,7 +2473,7 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { if (lastProps[innerHTML]) { dom.vchildren = []; } - diffChildren(lastVnode, nextVnode, dom, context, updateQueue); + diffChildren(lastVnode, flattenChildren(nextVnode), dom, context, updateQueue); } if (checkProps || nextVnode.checkProps) { @@ -2482,9 +2486,8 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { return dom; } -function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { +function diffChildren(lastVnode, nextChildren, parentNode, context, updateQueue) { var lastChildren = parentNode.vchildren, - nextChildren = flattenChildren(nextVnode), nextLength = nextChildren.length, lastLength = lastChildren.length, dom = void 0; diff --git a/dist/ReactSelection.js b/dist/ReactSelection.js index ac960b2d8..06ca09027 100644 --- a/dist/ReactSelection.js +++ b/dist/ReactSelection.js @@ -2118,7 +2118,11 @@ function findDOMNode(ref) { } //[Top API] ReactDOM.createPortal function createPortal(children, container) { - return renderByAnu(children, container); + if (!container.vchildren) { + container.vchildren = []; + } + diffChildren(getVParent(container), children, container, {}, []); + return null; } // 用于辅助XML元素的生成(svg, math), // 它们需要根据父节点的tagName与namespaceURI,知道自己是存在什么文档中 @@ -2469,7 +2473,7 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { if (lastProps[innerHTML]) { dom.vchildren = []; } - diffChildren(lastVnode, nextVnode, dom, context, updateQueue); + diffChildren(lastVnode, flattenChildren(nextVnode), dom, context, updateQueue); } if (checkProps || nextVnode.checkProps) { @@ -2482,9 +2486,8 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { return dom; } -function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { +function diffChildren(lastVnode, nextChildren, parentNode, context, updateQueue) { var lastChildren = parentNode.vchildren, - nextChildren = flattenChildren(nextVnode), nextLength = nextChildren.length, lastLength = lastChildren.length, dom = void 0; diff --git a/dist/ReactShim.js b/dist/ReactShim.js index 0bf8be8fb..1f182eb53 100644 --- a/dist/ReactShim.js +++ b/dist/ReactShim.js @@ -1960,7 +1960,11 @@ function findDOMNode(ref) { } //[Top API] ReactDOM.createPortal function createPortal(children, container) { - return renderByAnu(children, container); + if (!container.vchildren) { + container.vchildren = []; + } + diffChildren(getVParent(container), children, container, {}, []); + return null; } // 用于辅助XML元素的生成(svg, math), // 它们需要根据父节点的tagName与namespaceURI,知道自己是存在什么文档中 @@ -2311,7 +2315,7 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { if (lastProps[innerHTML]) { dom.vchildren = []; } - diffChildren(lastVnode, nextVnode, dom, context, updateQueue); + diffChildren(lastVnode, flattenChildren(nextVnode), dom, context, updateQueue); } if (checkProps || nextVnode.checkProps) { @@ -2324,9 +2328,8 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { return dom; } -function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { +function diffChildren(lastVnode, nextChildren, parentNode, context, updateQueue) { var lastChildren = parentNode.vchildren, - nextChildren = flattenChildren(nextVnode), nextLength = nextChildren.length, lastLength = lastChildren.length, dom = void 0; diff --git a/index3.html b/index3.html index 1a50da148..8f2b6e6e4 100644 --- a/index3.html +++ b/index3.html @@ -6,76 +6,131 @@ - + - + 支持React16的ReactDOM.createPortal + - -
-            'will mount',
-            'render',
-            'did mount',
-            'ref',
-      
-            'render',
-            'did update',
-            'ref',
-    
-
+
+ diff --git a/src/diff.js b/src/diff.js index 09a167aa0..605e63315 100644 --- a/src/diff.js +++ b/src/diff.js @@ -44,7 +44,11 @@ export function findDOMNode(ref) { } //[Top API] ReactDOM.createPortal export function createPortal(children, container) { - return renderByAnu(children, container); + if(!container.vchildren){ + container.vchildren = []; + } + diffChildren(getVParent(container), children, container, {}, [] ); + return null; } // 用于辅助XML元素的生成(svg, math), // 它们需要根据父节点的tagName与namespaceURI,知道自己是存在什么文档中 @@ -376,7 +380,7 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { if (lastProps[innerHTML]) { dom.vchildren = []; } - diffChildren(lastVnode, nextVnode, dom, context, updateQueue); + diffChildren(lastVnode, flattenChildren(nextVnode), dom, context, updateQueue); } if (checkProps || nextVnode.checkProps) { @@ -389,9 +393,8 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { return dom; } -function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) { +function diffChildren(lastVnode, nextChildren, parentNode, context, updateQueue) { let lastChildren = parentNode.vchildren, - nextChildren = flattenChildren(nextVnode), nextLength = nextChildren.length, lastLength = lastChildren.length, dom; From d658ce59d974cbdd9ef4a7d280fae347766c9df2 Mon Sep 17 00:00:00 2001 From: RubyLouvre <1669866773@qq.com> Date: Tue, 10 Oct 2017 15:52:55 +0800 Subject: [PATCH 53/57] =?UTF-8?q?=E5=8E=BB=E6=8E=89updater.lastVnode?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dist/React.js | 12 +----------- dist/ReactIE.js | 12 +----------- dist/ReactSelection.js | 12 +----------- dist/ReactShim.js | 12 +----------- src/diff.js | 11 +---------- 5 files changed, 5 insertions(+), 54 deletions(-) diff --git a/dist/React.js b/dist/React.js index 62c2d129e..201b02c07 100644 --- a/dist/React.js +++ b/dist/React.js @@ -2337,7 +2337,6 @@ function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQue instance = lastVnode._instance; var nextContext = void 0, - queue = void 0, nextProps = nextVnode.props, updater = instance.updater; if (type.contextTypes) { @@ -2361,7 +2360,6 @@ function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQue } //updater上总是保持新的数据 - updater.lastVnode = lastVnode; updater.vnode = nextVnode; updater.context = nextContext; updater.props = nextProps; @@ -2373,12 +2371,6 @@ function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQue return alignVnode(updater.rendered, nextRendered, vparent, childContext, updateQueue, updater); }); } - /* if (updateQueue.isMainProcess) { - queue = updateQueue; - queue = []; - } else { - queue = updateQueue; - }*/ refreshComponent(updater, updateQueue); //子组件先执行 updateQueue.push(updater); @@ -2390,8 +2382,7 @@ function refreshComponent(updater, updateQueue) { dom = updater._hostNode, nextContext = updater.context, nextProps = updater.props, - vnode = updater.vnode, - lastVnode = updater.lastVnode; + vnode = updater.vnode; vnode._instance = instance; //放这里 @@ -2424,7 +2415,6 @@ function refreshComponent(updater, updateQueue) { return alignVnode(lastRendered, nextRendered, vparent, childContext, updateQueue, updater); }); - updater.lastVnode = vnode; updater._lifeStage = 2; var userHook = instance.componentDidUpdate; diff --git a/dist/ReactIE.js b/dist/ReactIE.js index 06ca09027..282252ed3 100644 --- a/dist/ReactIE.js +++ b/dist/ReactIE.js @@ -2336,7 +2336,6 @@ function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQue instance = lastVnode._instance; var nextContext = void 0, - queue = void 0, nextProps = nextVnode.props, updater = instance.updater; if (type.contextTypes) { @@ -2360,7 +2359,6 @@ function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQue } //updater上总是保持新的数据 - updater.lastVnode = lastVnode; updater.vnode = nextVnode; updater.context = nextContext; updater.props = nextProps; @@ -2372,12 +2370,6 @@ function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQue return alignVnode(updater.rendered, nextRendered, vparent, childContext, updateQueue, updater); }); } - /* if (updateQueue.isMainProcess) { - queue = updateQueue; - queue = []; - } else { - queue = updateQueue; - }*/ refreshComponent(updater, updateQueue); //子组件先执行 updateQueue.push(updater); @@ -2389,8 +2381,7 @@ function refreshComponent(updater, updateQueue) { dom = updater._hostNode, nextContext = updater.context, nextProps = updater.props, - vnode = updater.vnode, - lastVnode = updater.lastVnode; + vnode = updater.vnode; vnode._instance = instance; //放这里 @@ -2423,7 +2414,6 @@ function refreshComponent(updater, updateQueue) { return alignVnode(lastRendered, nextRendered, vparent, childContext, updateQueue, updater); }); - updater.lastVnode = vnode; updater._lifeStage = 2; var userHook = instance.componentDidUpdate; diff --git a/dist/ReactSelection.js b/dist/ReactSelection.js index 06ca09027..282252ed3 100644 --- a/dist/ReactSelection.js +++ b/dist/ReactSelection.js @@ -2336,7 +2336,6 @@ function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQue instance = lastVnode._instance; var nextContext = void 0, - queue = void 0, nextProps = nextVnode.props, updater = instance.updater; if (type.contextTypes) { @@ -2360,7 +2359,6 @@ function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQue } //updater上总是保持新的数据 - updater.lastVnode = lastVnode; updater.vnode = nextVnode; updater.context = nextContext; updater.props = nextProps; @@ -2372,12 +2370,6 @@ function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQue return alignVnode(updater.rendered, nextRendered, vparent, childContext, updateQueue, updater); }); } - /* if (updateQueue.isMainProcess) { - queue = updateQueue; - queue = []; - } else { - queue = updateQueue; - }*/ refreshComponent(updater, updateQueue); //子组件先执行 updateQueue.push(updater); @@ -2389,8 +2381,7 @@ function refreshComponent(updater, updateQueue) { dom = updater._hostNode, nextContext = updater.context, nextProps = updater.props, - vnode = updater.vnode, - lastVnode = updater.lastVnode; + vnode = updater.vnode; vnode._instance = instance; //放这里 @@ -2423,7 +2414,6 @@ function refreshComponent(updater, updateQueue) { return alignVnode(lastRendered, nextRendered, vparent, childContext, updateQueue, updater); }); - updater.lastVnode = vnode; updater._lifeStage = 2; var userHook = instance.componentDidUpdate; diff --git a/dist/ReactShim.js b/dist/ReactShim.js index 1f182eb53..857d60703 100644 --- a/dist/ReactShim.js +++ b/dist/ReactShim.js @@ -2178,7 +2178,6 @@ function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQue instance = lastVnode._instance; var nextContext = void 0, - queue = void 0, nextProps = nextVnode.props, updater = instance.updater; if (type.contextTypes) { @@ -2202,7 +2201,6 @@ function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQue } //updater上总是保持新的数据 - updater.lastVnode = lastVnode; updater.vnode = nextVnode; updater.context = nextContext; updater.props = nextProps; @@ -2214,12 +2212,6 @@ function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQue return alignVnode(updater.rendered, nextRendered, vparent, childContext, updateQueue, updater); }); } - /* if (updateQueue.isMainProcess) { - queue = updateQueue; - queue = []; - } else { - queue = updateQueue; - }*/ refreshComponent(updater, updateQueue); //子组件先执行 updateQueue.push(updater); @@ -2231,8 +2223,7 @@ function refreshComponent(updater, updateQueue) { dom = updater._hostNode, nextContext = updater.context, nextProps = updater.props, - vnode = updater.vnode, - lastVnode = updater.lastVnode; + vnode = updater.vnode; vnode._instance = instance; //放这里 @@ -2265,7 +2256,6 @@ function refreshComponent(updater, updateQueue) { return alignVnode(lastRendered, nextRendered, vparent, childContext, updateQueue, updater); }); - updater.lastVnode = vnode; updater._lifeStage = 2; var userHook = instance.componentDidUpdate; diff --git a/src/diff.js b/src/diff.js index 605e63315..bd39617d3 100644 --- a/src/diff.js +++ b/src/diff.js @@ -257,7 +257,6 @@ function mountComponent(lastNode, vnode, vparent, parentContext, updateQueue, pa function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQueue) { let { type, ref, _instance: instance } = lastVnode; let nextContext, - queue, nextProps = nextVnode.props, updater = instance.updater; if (type.contextTypes) { @@ -281,7 +280,6 @@ function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQue } //updater上总是保持新的数据 - updater.lastVnode = lastVnode; updater.vnode = nextVnode; updater.context = nextContext; updater.props = nextProps; @@ -293,12 +291,6 @@ function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQue return alignVnode(updater.rendered, nextRendered, vparent, childContext, updateQueue, updater); }); } - /* if (updateQueue.isMainProcess) { - queue = updateQueue; - queue = []; - } else { - queue = updateQueue; - }*/ refreshComponent(updater, updateQueue); //子组件先执行 updateQueue.push(updater); @@ -306,7 +298,7 @@ function updateComponent(lastVnode, nextVnode, vparent, parentContext, updateQue } function refreshComponent(updater, updateQueue) { - let { _instance: instance, _hostNode: dom, context: nextContext, props: nextProps, vnode, lastVnode } = updater; + let { _instance: instance, _hostNode: dom, context: nextContext, props: nextProps, vnode } = updater; vnode._instance = instance; //放这里 updater._renderInNextCycle = null; @@ -335,7 +327,6 @@ function refreshComponent(updater, updateQueue) { return alignVnode(lastRendered, nextRendered, vparent, childContext, updateQueue, updater); }); - updater.lastVnode = vnode; updater._lifeStage = 2; let userHook = instance.componentDidUpdate; From 0e197075956aaf4c36b43b2cbb6a6bfaaa871345 Mon Sep 17 00:00:00 2001 From: "waliang.wang" Date: Tue, 10 Oct 2017 17:58:11 +0800 Subject: [PATCH 54/57] =?UTF-8?q?diff.js=20=E6=A0=BC=E5=BC=8F=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/diff.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/diff.js b/src/diff.js index c5a555b92..4f08604b7 100644 --- a/src/diff.js +++ b/src/diff.js @@ -494,7 +494,7 @@ function diffChildren(lastVnode, nextChildren, parentNode, context, updateQueue) if (pastDom.length && insertPoint.innerText && dom.innerText) { isTrue = diffDomText(pastDom, dom, insertPoint); if (!isTrue) { - insertDom(dom) + insertDom(dom); isTrue = false; } // 没有旧DOM记录 (这里代码不能合并) @@ -517,7 +517,7 @@ function diffChildren(lastVnode, nextChildren, parentNode, context, updateQueue) //如果找不到对应的旧节点,创建一个新节点放在这里 dom = mountVnode(null, nextChild, lastVnode, context, updateQueue); - pastDom.push(dom) + pastDom.push(dom); parentNode.insertBefore(dom, insertPoint); insertPoint = dom; } From d813b4cad05bb734257b14eb6376e2c3538a2cc1 Mon Sep 17 00:00:00 2001 From: "waliang.wang" Date: Wed, 11 Oct 2017 10:57:07 +0800 Subject: [PATCH 55/57] =?UTF-8?q?opti:=20=E4=BC=98=E5=8C=96diff.js?= =?UTF-8?q?=E7=9A=84updateElemen=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/diff.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/diff.js b/src/diff.js index 46604935b..ab7c37d92 100644 --- a/src/diff.js +++ b/src/diff.js @@ -360,6 +360,9 @@ export function alignVnode(lastVnode, nextVnode, vparent, context, updateQueue, function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { let { props: lastProps, _hostNode: dom, ref, checkProps } = lastVnode; let { props: nextProps, ref: nextRef } = nextVnode; + if (!dom) { + return false; + } nextVnode._hostNode = dom; if (nextProps[innerHTML]) { var list = lastVnode.vchildren || []; @@ -371,13 +374,10 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { if (lastProps[innerHTML]) { dom.vchildren = []; } - if (dom) { - diffChildren(lastVnode, flattenChildren(nextVnode), dom, context, updateQueue); - } - + diffChildren(lastVnode, flattenChildren(nextVnode), dom, context, updateQueue); } - if ((checkProps || nextVnode.checkProps) && dom) { + if (checkProps || nextVnode.checkProps) { diffProps(nextProps, lastProps, nextVnode, lastVnode, dom); } if (nextVnode.type === "select") { From c42655630d94b517865a5930822d6c5937572bce Mon Sep 17 00:00:00 2001 From: "waliang.wang" Date: Wed, 11 Oct 2017 14:12:00 +0800 Subject: [PATCH 56/57] =?UTF-8?q?fix:=20=E4=BF=AE=E6=94=B9diff.js=E7=9A=84?= =?UTF-8?q?diffChildren=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/diff.js | 51 ++++++++++++++++++++++----------------------------- 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/src/diff.js b/src/diff.js index ab7c37d92..072272408 100644 --- a/src/diff.js +++ b/src/diff.js @@ -387,27 +387,27 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { return dom; } -function diffDomText(pastDom, dom, insertPoint) { - const dText = dom.innerText.trim(); - const iText = insertPoint.innerText.trim(); - let isTrue = false; - - pastDom.forEach(v => { - if ((v.innerText === dText) || (dText === iText)) { - isTrue = !isTrue; - return false; - } - }); +function diffDomText(nextChild, insertPoint) { + const body = document.body; + let isTrue = false, + nextTest = insertTest = ''; + if ('innerText' in body) { + nextTest = nextChild._hostNode.innerText; + insertTest = insertPoint.innerText; + } else if ('textContent' in documebnt) { + nextTest = nextChild._hostNode.textContent; + insertTest = insertPoint.textContent; + } + if (nextTest === insertTest) { + isTrue = !isTrue; + } return isTrue; } function diffChildren(lastVnode, nextChildren, parentNode, context, updateQueue) { - const insertDom = dom => parentNode.insertBefore(dom, insertPoint); let lastChildren = parentNode.vchildren, nextLength = nextChildren.length, lastLength = lastChildren.length, - isTrue = false, - pastDom = [], dom; //如果旧数组长度为零, 直接添加 @@ -479,19 +479,8 @@ function diffChildren(lastVnode, nextChildren, parentNode, context, updateQueue) lastChild = action[0]; nextChild = action[1]; dom = lastChild._hostNode; - if (action[2]) { - // 如果有旧DOM记录 - if (pastDom.length && insertPoint.innerText && dom.innerText) { - isTrue = diffDomText(pastDom, dom, insertPoint); - if (!isTrue) { - insertDom(dom); - isTrue = false; - } - // 没有旧DOM记录 (这里代码不能合并) - } else { - insertDom(dom); - } + parentNode.insertBefore(dom, insertPoint); } insertPoint = updateVnode(lastChild, nextChild, lastVnode, context, updateQueue); if (!nextChild._hostNode) { @@ -505,11 +494,15 @@ function diffChildren(lastVnode, nextChildren, parentNode, context, updateQueue) if (removed && !removed._disposed && !removeHits[j]) { disposeVnode(removed); } - //如果找不到对应的旧节点,创建一个新节点放在这里 dom = mountVnode(null, nextChild, lastVnode, context, updateQueue); - pastDom.push(dom); - parentNode.insertBefore(dom, insertPoint); + if (insertPoint && nextChild._hostNode) { + if (!diffDomText(nextChild, insertPoint)) { + parentNode.insertBefore(dom, insertPoint); + } + } else { + parentNode.insertBefore(dom, insertPoint); + } insertPoint = dom; } insertPoint = insertPoint.nextSibling; From a72928a2b9a8e8dd641b1e7c474e1d3077a744c8 Mon Sep 17 00:00:00 2001 From: "waliang.wang" Date: Wed, 11 Oct 2017 14:20:10 +0800 Subject: [PATCH 57/57] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/diff.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/diff.js b/src/diff.js index 072272408..617f12be4 100644 --- a/src/diff.js +++ b/src/diff.js @@ -390,11 +390,12 @@ function updateElement(lastVnode, nextVnode, vparent, context, updateQueue) { function diffDomText(nextChild, insertPoint) { const body = document.body; let isTrue = false, - nextTest = insertTest = ''; - if ('innerText' in body) { + nextTest = "", + insertTest = ""; + if ("innerText" in body) { nextTest = nextChild._hostNode.innerText; insertTest = insertPoint.innerText; - } else if ('textContent' in documebnt) { + } else if ("textContent" in body) { nextTest = nextChild._hostNode.textContent; insertTest = insertPoint.textContent; }