12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147 |
- 'use strict';
- /**
- *
- * @param {*} type dom类型或func
- * @param {*} props dom属性
- * @param {*} children 子节点
- */
- function createElement(type, config, children) {
- var props = {};
- var key = null;
- var ref = null;
- var mtype = null;
- var getContext = null;
- var len = arguments.length - 2;
- //0代表没有ref 1代表ref为func 2为string
- var refType = 0;
- //既然render的时候都需要判断下type 是fun或string
- //那把这一步提前 比render循环里判断更好些;
- var _type = typeof type;
- switch (_type) {
- case 'string': //HtmlElement 1 SVG 3
- mtype = type !== 'svg' ? 1 : 3;
- break;
- case 'function': //component 或statelessComponent
- mtype = 2;
- //如果有contextTypes代表该组件可以取context
- type.contextTypes && (getContext = true);
- break;
- }
- if (config) {
- key = config.key !== void 0 ? ('' + config.key) : null;
- ref = config.ref || null;
- if (typeof ref === 'number') {
- ref = '' + ref;
- }
- if (ref) {
- var _refType = typeof ref;
- switch (_refType) {
- case 'function':
- refType = 1;
- break;
- case 'string':
- refType = 2;
- break;
- }
- }
- for (var i in config) {
- if (i !== 'key' && i != 'ref') {
- props[i] = config[i];
- }
- }
- }
- var defaultProps = type.defaultProps;
- if (defaultProps) {
- for (var propName in defaultProps) {
- if (props[propName] === void 666) {
- props[propName] = defaultProps[propName];
- }
- }
- }
- if (len > 1) {
- var array = new Array();
- for (var i = 0; i < len; i++) {
- var c = arguments[i + 2];
- if (!Array.isArray(c)) {
- array.push(c);
- } else {
- c.forEach(function (item) {
- array.push(item);
- });
- }
- }
- props.children = array;
- } else if (len === 1) {
- props.children = children;
- }
- return new Vnode(type, key, ref, props, mtype, getContext, refType);
- }
- /**
- *
- * @param {*} type
- * @param {*} key
- * @param {*} ref
- * @param {*} self
- * @param {*} source
- * @param {*} owner
- * @param {*} props
- */
- var Vnode = function (type, key, ref, props, mtype, getContext, refType) {
- this.type = type;
- this.key = key;
- this.ref = ref;
- this.props = props;
- this.$$typeof = 1;
- this.mtype = mtype;
- //之前是直接赋在vnode上 多了之后容易混
- //单独新建个对象存放各种信息
- this.mayInfo = {};
- this.getContext = getContext;
- this.refType = refType;
- };
- // function isArray(o) {
- // return Object.prototype.toString.call(o) == '[object Array]';
- // }
- //文本节点重复利用
- var recyclables={
- '#text':[]
- };
- function mergeState(instance) {
- var newState;
- var prevState = instance.state;
- if (instance.mayInst.mergeStateQueue && instance.mayInst.mergeStateQueue.length > 0) {
- var queue = instance.mayInst.mergeStateQueue;
- var newState = extend({}, prevState);
- for (var i = 0; i < queue.length; i++) {
- var s = queue[i];
- if (s && s.call) {
- s = s.call(instance, newState, instance.nextProps || instance.props);
- }
- newState = extend(newState, s);
- }
- instance.mayInst.mergeStateQueue.length = 0;
- } else {
- newState = prevState;
- }
- return newState;
- }
- // export function eventProxy(e) {
- // return this._listener[e.type](e);
- // }
- function extend(target, src) {
- for (var key in src) {
- if (src.hasOwnProperty(key)) {
- target[key] = src[key];
- }
- }
- return target;
- }
- /**
- * 寄生组合式继承
- * @param {*} target
- * @param {*} superClass
- */
- function inherits(target, superClass) {
- function b() { }
- b.prototype = superClass.prototype;
- var fn = target.prototype = new b();
- fn.constructor = target;
- return fn;
- }
- var Refs = {
- //当子component含有ref的时候,需要把对应的instance或dom添加到 父component的refs属性中
- //如果在mountComponent中做这样的操作需要每一层都要添加owner 放在外面更好些;
- currentOwner: null,
- //开始render时isRoot为true,方便ref定位最顶端节点
- isRoot: false,
- attachRef: function (vnode, hostNode) {
- if (vnode.refType === 1) { //func
- hostNode.mayInst && hostNode.mayInst.stateless && (hostNode = null);
- lifeCycleQueue.push(vnode.ref.bind(vnode, hostNode));
- } else if (vnode.refType === 2) { //string
- this.currentOwner.refs[vnode.ref] = hostNode;
- }
- }
- };
- //mayQueue 保存render过程中的各种事件队列
- var mayQueue = {
- dirtyComponentsQueue: [], //setState 需要diff的component队列
- callbackQueue: [], //回调队列 setState 中的事件回调
- lifeCycleQueue: [], //生命周期过程中的回调队列 DidUpdate DidMount ref回调
- isInEvent: false, //是否在触发事件 回调事件中的setstate合并触发
- clearQueue: clearQueue,
- flushUpdates: flushUpdates,
- };
- //存放生命周期中的 DidMount DidUpdate以及ref回调
- var lifeCycleQueue = mayQueue.lifeCycleQueue;
- /**
- * 清空回调队列
- * @param {*} mayQueue
- */
- function clearQueue() {
- //ComponentDidMount
- clearLifeCycleQueue();
- //如有有dirty Component diff
- flushUpdates();
- //setState传入的回调函数
- clearCallbackQueue();
- }
- function flushUpdates() {
- var instance;
- var i = 0;
- //如果在当前生命周期的DidMount调用setState 放到下一生命周期处理
- mayQueue.dirtyComponentsQueue = mayQueue.dirtyComponentsQueue.sort(sortComponent);
- while (instance = mayQueue.dirtyComponentsQueue.shift()) {
- if (i++ === 0) {
- Refs.isRoot = true;
- }
- if (instance.mayInst.dirty) {
- //如果C是脏组件diff 如果其在diff过程中子组件也需要diff diff之后
- //子组件_dirty会为false 没必要再diff一次;
- reRender(instance);
- }
- if (instance) {
- //diff之后组件的状态返回0
- instance.mayInst.lifeState = 0;
- }
- }
- //ComponentDidUpdate
- clearLifeCycleQueue();
- //防止setState currentOwner混乱
- Refs.currentOwner = null;
- }
- function clearLifeCycleQueue() {
- //先清空 生命周期 ref 的回调函数
- if (mayQueue.lifeCycleQueue && mayQueue.lifeCycleQueue.length > 0) {
- var callback;
- //其实ref很像生命周期函数,它比较特殊的地方在于vnode.type='div'之类的vnode
- //其string ref要指向其真实dom func ref也是回调真实的dom 其它的都是回调instance
- //vnode.type='div'之类的ref特殊处理;剩余的就和生命周期很像了;
- while (callback = mayQueue.lifeCycleQueue.shift()) {
- callback();
- }
- }
- }
- function clearCallbackQueue() {
- //再清空 setState传入的回调函数
- if (mayQueue.callbackQueue && mayQueue.callbackQueue.length > 0) {
- var callback;
- mayQueue.callbackQueue = mayQueue.callbackQueue.sort(sortCallback);
- while (callback = mayQueue.callbackQueue.shift()) {
- callback();
- }
- }
- }
- function sortCallback(a, b) {
- return a._mountOrder - b._mountOrder;
- }
- function sortComponent(a, b) {
- return a.mayInst.mountOrder - b.mayInst.mountOrder;
- }
- var globalEvent = {
- };
- var eventHooks = {}; //用于在元素上绑定特定的事件
- var document$1 = window.document;
- var isTouch = "ontouchstart" in document$1;
- function dispatchEvent(e, type, end) {
- e = new SyntheticEvent(e);
- if (type) {
- e.type = type;
- }
- var _type = e.type;
- //全局的一个标识 在事件中setState应当合并
- mayQueue.isInEvent = true;
- //onClickCapture 在捕获阶段触发
- var captured = _type + 'capture';
- var eventCollect = bubbleEvent(e.target, end || document$1);
- //先触发捕获
- triggerEventFlow(e, eventCollect, captured);
- if (!e._stopPropagation) {
- //触发冒泡
- triggerEventFlow(e, eventCollect.reverse(), _type);
- }
- mayQueue.isInEvent = false;
- //在事件中合并state之后 触发reRender
- mayQueue.clearQueue();
- }
- /**
- * 自己冒泡,收集冒泡过程中的所有事件
- * @param {*event target} from
- * @param {*end || document} end
- */
- function bubbleEvent(from, end) {
- var collect = [];
- do {
- if (from === end) {
- break;
- }
- var event = from._listener;
- if (event) {
- collect.push({
- dom: from,
- events: event
- });
- }
- } while ((from = from.parentNode) && from.nodeType === 1);
- // target --> parentNode --> body --> html
- return collect;
- }
- function triggerEventFlow(e, collect, prop) {
- for (var i = collect.length; i--;) {
- var eObj = collect[i];
- var fn = eObj.events[prop];
- if (isFn(fn)) {
- e.currentTarget = eObj.dom;
- fn.call(eObj.dom, e);
- if (e._stopPropagation) {
- break;
- }
- }
- }
- }
- var eventLowerCache = {
- onClick: "click",
- onChange: "change",
- onWheel: "wheel"
- };
- var rcapture = /Capture$/;
- function getBrowserName(onStr) {
- var lower = eventLowerCache[onStr];
- if (lower) {
- return lower;
- }
- var camel = onStr.slice(2).replace(rcapture, "");
- lower = camel.toLowerCase();
- eventLowerCache[onStr] = lower;
- return lower;
- }
- function addEvent(name) {
- if (!globalEvent[name]) {
- globalEvent[name] = true;
- addDocumentEvent(document$1, name, dispatchEvent);
- }
- }
- function addDocumentEvent(el, name, fn, bool) {
- if (el.addEventListener) {
- el.addEventListener(name, fn, bool || false);
- } else if (el.attachEvent) {
- el.attachEvent('on' + name, fn);
- }
- }
- function SyntheticEvent(event) {
- if (event.nativeEvent) {
- return event;
- }
- if (!this.target) {
- this.target = event.srcElement;
- }
- for (var i in event) {
- if (!eventProto[i]) {
- this[i] = event[i];
- }
- }
- this.timeStamp = new Date() - 0;
- this.nativeEvent = event;
- }
- function createHandle(name, fn) {
- return function (e) {
- if (fn && fn(e) === false) {
- return;
- }
- dispatchEvent(e, name);
- };
- }
- if (isTouch) {
- eventHooks.click = noop;
- eventHooks.clickcapture = noop;
- }
- var changeHandle = createHandle("change");
- var doubleClickHandle = createHandle("doubleclick");
- //react将text,textarea,password元素中的onChange事件当成onInput事件
- eventHooks.changecapture = eventHooks.change = function (dom) {
- if (/text|password/.test(dom.type)) {
- addDocumentEvent(document$1, "input", changeHandle);
- }
- };
- eventHooks.doubleclick = eventHooks.doubleclickcapture = function () {
- addDocumentEvent(document$1, "dblclick", doubleClickHandle);
- };
- var eventProto = (SyntheticEvent.prototype = {
- preventDefault: function () {
- var e = this.nativeEvent || {};
- e.returnValue = this.returnValue = false;
- if (e.preventDefault) {
- e.preventDefault();
- }
- },
- fixHooks: function () { },
- stopPropagation: function () {
- var e = this.nativeEvent || {};
- e.cancleBubble = this._stopPropagation = true;
- if (e.stopPropagation) {
- e.stopPropagation();
- }
- },
- persist: noop,
- stopImmediatePropagation: function () {
- this.stopPropagation();
- this.stopImmediate = true;
- },
- toString: function () {
- return "[object Event]";
- }
- });
- Object.freeze ||
- (Object.freeze = function (a) {
- return a;
- });
- function isFn(obj) {
- return Object.prototype.toString.call(obj) === "[object Function]";
- }
- function noop() { }
- /* IE6-11 chrome mousewheel wheelDetla 下 -120 上 120
- firefox DOMMouseScroll detail 下3 上-3
- firefox wheel detlaY 下3 上-3
- IE9-11 wheel deltaY 下40 上-40
- chrome wheel deltaY 下100 上-100 */
- /* istanbul ignore next */
- const fixWheelType = "onmousewheel" in document$1 ? "mousewheel" : document$1.onwheel !== void 666 ? "wheel" : "DOMMouseScroll";
- const fixWheelDelta = fixWheelType === "mousewheel" ? "wheelDetla" : fixWheelType === "wheel" ? "deltaY" : "detail";
- eventHooks.wheel = function (dom) {
- addDocumentEvent(dom, fixWheelType, function (e) {
- var delta = e[fixWheelDelta] > 0 ? -120 : 120;
- var deltaY = ~~dom.__wheel + delta;
- dom.__wheel = deltaY;
- e = new SyntheticEvent(e);
- e.type = "wheel";
- e.deltaY = deltaY;
- dispatchEvent(e);
- });
- };
- var fixFocus = {};
- "blur,focus".replace(/\w+/g, function (type) {
- eventHooks[type] = function () {
- if (!fixFocus[type]) {
- fixFocus[type] = true;
- addDocumentEvent(document$1, type, dispatchEvent, true);
- }
- };
- });
- /**
- *
- DOM通过event对象的relatedTarget属性提供了相关元素的信息。这个属性只对于mouseover和mouseout事件才包含值;
- 对于其他事件,这个属性的值是null。IE不支持realtedTarget属性,但提供了保存着同样信息的不同属性。
- 在mouseover事件触发时,IE的fromElement属性中保存了相关元素;
- 在mouseout事件出发时,IE的toElement属性中保存着相关元素。
- 但fromElement与toElement可能同时都有值
- */
- function getRelatedTarget(e) {
- if (!e.timeStamp) {
- e.relatedTarget = e.type === "mouseover" ? e.fromElement : e.toElement;
- }
- return e.relatedTarget;
- }
- function contains(a, b) {
- if (b) {
- while ((b = b.parentNode)) {
- if (b === a) {
- return true;
- }
- }
- }
- return false;
- }
- String("mouseenter,mouseleave").replace(/\w+/g, function (type) {
- eventHooks[type] = function (dom, name) {
- var mark = "__" + name;
- if (!dom[mark]) {
- dom[mark] = true;
- var mask = name === "mouseenter" ? "mouseover" : "mouseout";
- addDocumentEvent(dom, mask, function (e) {
- let t = getRelatedTarget(e);
- if (!t || (t !== dom && !contains(dom, t))) {
- var common = getLowestCommonAncestor(dom, t);
- //由于不冒泡,因此paths长度为1
- dispatchEvent(e, name, common);
- }
- });
- }
- };
- });
- function getLowestCommonAncestor(instA, instB) {
- var depthA = 0;
- for (var tempA = instA; tempA; tempA = tempA.parentNode) {
- depthA++;
- }
- var depthB = 0;
- for (var tempB = instB; tempB; tempB = tempB.parentNode) {
- depthB++;
- }
- // If A is deeper, crawl up.
- while (depthA - depthB > 0) {
- instA = instA.parentNode;
- depthA--;
- }
- // If B is deeper, crawl up.
- while (depthB - depthA > 0) {
- instB = instB.parentNode;
- depthB--;
- }
- // Walk in lockstep until we find a match.
- var depth = depthA;
- while (depth--) {
- if (instA === instB) {
- return instA;
- }
- instA = instA.parentNode;
- instB = instB.parentNode;
- }
- return null;
- }
- //之前是 mount的时候setDomAttr一个方法 diff的时候diffProps一个方法
- //后来发现 写着写着要修改点setDomAttr的内容 diff的时候还要在判断一遍
- //那干脆把这两个合成一个方法好了
- function diffProps(prev, now) {
- var props = now.props;
- var hostNode = now.mayInfo.hostNode;
- var prevStyle = prev && prev.props.style;
- var nowStyle = props.style;
- if (!prev) { //setDomAttr
- for (var key in props) {
- setDomAttr(hostNode, key, props[key]);
- }
- if (nowStyle) {
- patchStyle(hostNode, prevStyle, nowStyle);
- }
- } else {
- var prevProps = prev.props;
- for (var name in props) {
- if (name !== 'children' && !(props[name] === prevProps[name])) {
- setDomAttr(hostNode, name, props[name]);
- }
- }
- for (var prop in prevProps) {
- if (prop !== 'children' && (props[prop] === void 666)) {
- removeDomAttr(hostNode, prevProps, prop);
- }
- }
- if (prevStyle !== nowStyle) {
- patchStyle(hostNode, prevStyle, nowStyle);
- }
- }
- }
- var FormElement = {
- input: 1,
- select: 1,
- // option: 1,
- textarea: 1
- };
- /**
- * 设置DOM属性
- * @param {*} dom
- * @param {*} key
- * @param {*} val
- */
- function setDomAttr(dom, key, val) {
- var nodeType = dom.nodeType;
- // Don't get/set attributes on text, comment and attribute nodes
- if (nodeType === 3 || nodeType === 8 || nodeType === 2) {
- return;
- }
- if (!isEvent(key)) {
- switch (key) {
- case 'children':
- case 'style':
- case 'key':
- break;
- case 'dangerouslySetInnerHTML':
- var html = val && val.__html;
- dom.innerHTML = html;
- break;
- case 'defaultValue': //input等受控组件
- key = 'value';
- case 'className':
- key = 'class';
- default:
- if (key in dom) { //property
- try {
- if (val !== null && val !== false) {
- dom[key] = val;
- }
- } catch (e) {
- dom.setAttribute(key, val + '');
- }
- } else { //attribute
- if (val !== null && val !== false) {
- //attribute 永远是字符串
- dom.setAttribute(key, val + '');
- } else {
- //如果是null 或 false 不必添加
- dom.removeAttribute(key);
- }
- }
- break;
- }
- } else {
- var e = key.substring(2).toLowerCase();
- var eventName = getBrowserName(key);
- var listener = dom._listener || (dom._listener = {});
- if (!listener[e]) {
- //添加过一次之后不必再添加;
- addEvent(eventName);
- var hook = eventHooks[eventName];
- if (hook) {
- //input的change等特殊事件需要特殊处理
- hook(dom, eventName);
- }
- }
- listener[e] = val;
- }
- }
- function removeDomAttr(dom, props, key) {
- var nodeType = dom.nodeType;
- // Don't get/set attributes on text, comment and attribute nodes
- if (nodeType === 3 || nodeType === 8 || nodeType === 2) {
- return;
- }
- if (!isEvent(key)) {
- switch (key) {
- case 'dangerouslySetInnerHTML':
- dom.innerHTML = '';
- case 'className':
- dom.removeAttribute('class');
- break;
- default:
- if (key in dom) {
- dom[key] = '';
- } else {
- dom.removeAttribute(key);
- }
- }
- } else {
- var e = key.substring(2).toLowerCase();
- if (dom._listener && dom._listener[e]) {
- delete dom._listener[e];
- }
- }
- }
- function isEvent(name) {
- return /^on[A-Z]/.test(name);
- }
- function patchStyle(dom, prevStyle, newStyle) {
- var _style = '';
- for (var name in newStyle) {
- var _type = typeof newStyle[name];
- //backgroundColor 替换为 background-color Webkit替换为-webkit- ms单独替换一次
- var cssName = name.replace(/([A-Z])/g, '-$1').toLowerCase().replace(/^ms-/i, '-ms-');
- switch (_type) {
- case 'string':
- _style = newStyle[name].trim();
- break;
- case 'number':
- _style = newStyle[name];
- if (cssSuffix[name]) {
- _style += newStyle[name] !== 0 ? +cssSuffix[name] : '';
- }
- break;
- case 'boolean':
- _style = '';
- break;
- default:
- _style = newStyle[name];
- break;
- }
- dom.style[cssName] = _style;
- }
- for (var key in prevStyle) {
- if (!newStyle || !(key in newStyle)) {
- key = key.replace(/([A-Z])/g, '-$1').toLowerCase().replace(/^ms-/i, '-ms-');
- dom.style[key] = '';
- }
- }
- }
- /**
- input, select, textarea这几个元素如果指定了value/checked的**状态属性**,就会包装成受控组件或非受控组件
- 受控组件是指,用户除了为它指定**状态属性**,还为它指定了onChange/onInput/disabled等用于控制此状态属性
- 变动的属性
- 反之,它就是非受控组件,非受控组件会在框架内部添加一些事件,阻止**状态属性**被用户的行为改变,只能被setState改变
- */
- var eGroup = [{
- 'onChange': 1,
- 'onInput': 1,
- 'readOnly': 1,
- 'disabled': 1
- }, {
- 'onChange': 1,
- 'onClick': 1,
- 'readOnly': 1,
- 'disabled': 1
- }, {
- 'onChange': 1,
- 'disabled': 1
- }];
- function getIsControlled(hostNode, vnode) {
- //记录第一次render的值 非受控组件值不可变
- var type = hostNode.type;
- var hasValue, isControlled, eObj, event, ename;
- var vprops = vnode.props;
- switch (type) {
- case 'text':
- case 'textarea':
- //非受控组件有value属性 但是没有绑定修改value的onChange等事件
- hasValue = 'value' in vprops;
- ename = 'oninput';
- eObj = eGroup[0];
- //如果是非受控组件那么我们需要阻止用户改变数据
- event = preventInput;
- break;
- case 'checkbox':
- case 'radio':
- hasValue = 'checked' in vprops;
- ename = 'onclick';
- eObj = eGroup[1];
- event = preventClick;
- break;
- case 'select-one':
- case 'select-multiple':
- hasValue = 'value' in vprops;
- ename = 'onchange';
- eObj = eGroup[2];
- event = preventChange;
- var _val = vnode.props['value'] || vnode.props['defaultValue'] || '';
- var _optionsChilds = [].slice.call(hostNode.childNodes);
- if (_optionsChilds) {
- for (var k = 0; k < _optionsChilds.length; k++) {
- var oChild = _optionsChilds[k];
- if (oChild.value === _val) {
- oChild.selected = true;
- hostNode._selectIndex = k;
- }
- }
- }
- break;
- }
- isControlled = hasValue && hasEventProps(vprops, eObj);
- if (!isControlled) {
- console.warn(vnode.type + (vprops['type'] ? ('[type=' + vprops['type'] + ']') : '') + '元素为非受控组件,用户无法通过输入改变元素的值;更多信息参见React官方文档https://reactjs.org/docs/uncontrolled-components.html');
- setDomAttr(hostNode, ename, event);
- }
- return isControlled;
- }
- function preventInput(e) {
- var target = e.target;
- var name = e.type === "textarea" ? "innerHTML" : "value";
- target[name] = target._lastValue;
- }
- function preventClick(e) {
- e.preventDefault();
- }
- function preventChange(e) {
- var target = e.target;
- if (target._selectIndex) {
- target.options[target._selectIndex].selected = true;
- }
- }
- function hasEventProps(props, events) {
- for (var key in props) {
- if (events[key]) {
- return true;
- }
- }
- }
- const cssSuffix = {
- //需要加后缀如 px s(秒)等css属性摘出来
- //其实用正则更简洁一些,不过可能 可读性可维护性不如key value
- //动画属性(Animation)
- animationDelay: 's',
- //CSS 边框属性(Border 和 Outline)
- borderBottomWidth: 'px',
- borderLeftWidth: 'px',
- borderRightWidth: 'px',
- borderTopWidth: 'px',
- borderWidth: 'px',
- outlineWidth: 'px',
- borderBottomLeftRadius: 'px',
- borderBottomRightRadius: 'px',
- borderRadius: 'px',
- borderTopLeftRadius: 'px',
- borderTopRightRadius: 'px',
- //Box 属性
- rotation: 'deg',
- //CSS 尺寸属性(Dimension)
- height: 'px',
- maxHeight: 'px',
- maxWidth: 'px',
- minHeight: 'px',
- minWidth: 'px',
- width: 'px',
- //CSS 字体属性(Font) font-variant:small-caps; 段落设置为小型大写字母字体
- fontSize: 'px',
- //CSS 外边距属性(Margin)
- margin: 'px',
- marginLeft: 'px',
- marginRight: 'px',
- marginTop: 'px',
- marginBottom: 'px',
- //多列属性(Multi-column)
- columnGap: 'px',
- WebkitColumnGap: 'px',
- MozColumnGap: 'px',
- columnRuleWidth: 'px',
- WebkitColumnRuleWidth: 'px',
- MozColumnRuleWidth: 'px',
- columnWidth: 'px',
- WebkitColumnWidth: 'px',
- MozColumnWidth: 'px',
- //CSS 内边距属性(Padding)
- padding: 'px',
- paddingLeft: 'px',
- paddingRight: 'px',
- paddingTop: 'px',
- paddingBottom: 'px',
- //CSS 定位属性(Positioning)
- left: 'px',
- right: 'px',
- top: 'px',
- bottom: 'px',
- //CSS 文本属性(Text)
- letterSpacing: 'px',
- lineHeight: 'px'
- };
- var NAMESPACE = {
- html: 'http://www.w3.org/1999/xhtml',
- mathml: 'http://www.w3.org/1998/Math/MathML',
- svg: 'http://www.w3.org/2000/svg'
- };
- /**
- * 如果instance具备getChildContext方法 则调用
- * @param {component实例} instance
- * @param {当前上下文} context
- */
- function getChildContext(instance, context) {
- var prevProps = instance.props;
- if (instance.nextProps) {
- instance.props = instance.nextProps;
- }
- var getContext = instance.getChildContext();
- if (instance.nextProps) {
- instance.props = prevProps;
- }
- if (getContext && typeof getContext === 'object') {
- if (!context) {
- context = {};
- }
- context = Object.assign(context, getContext);
- }
- return context;
- }
- function getContextByTypes(context, typeCheck) {
- var ret = {};
- if (!context || !typeCheck) {
- return ret;
- }
- for (const key in typeCheck) {
- if (context.hasOwnProperty(key)) {
- ret[key] = context[key];
- }
- }
- return ret;
- }
- var REAL_SYMBOL = typeof Symbol === "function" && Symbol.iterator;
- var FAKE_SYMBOL = "@@iterator";
- function getIteractor(a) {
- var iteratorFn = REAL_SYMBOL && a[REAL_SYMBOL] || a[FAKE_SYMBOL];
- if (iteratorFn && iteratorFn.call) {
- return iteratorFn;
- }
- }
- function callIteractor$1(iteratorFn, children) {
- var iterator = iteratorFn.call(children),
- step,
- ret = [];
- if (iteratorFn !== children.entries) {
- while (!(step = iterator.next()).done) {
- ret.push(step.value);
- }
- } else {
- //Map, Set
- while (!(step = iterator.next()).done) {
- var entry = step.value;
- if (entry) {
- ret.push(entry[1]);
- }
- }
- }
- return ret;
- }
- //https://segmentfault.com/a/1190000010336457 司徒正美先生写的分析
- //hydrate是最早出现于inferno(另一个著名的react-like框架),并相邻的简单数据类型合并成一个字符串。
- //因为在react的虚拟DOM体系中,字符串相当于一个文本节点。减少children中的个数,
- //就相当减少实际生成的文本节点的数量,也减少了以后diff的数量,能有效提高性能。
- //render过程中有Key的 是最有可能变动的,无Key的很可能不会变(绝大部分情况)
- //把children带Key的放一起 不带Key的放一起(因为他们很可能不变化,顺序也不变减少diff寻找)
- function transformChildren(renderedVnode, parent) {
- var children = renderedVnode.props.children || null;
- if (children && !Array.isArray(children)) {
- children = [children];
- }
- var len = children ? children.length : 0;
- var childList = [].slice.call(parent.childNodes);
- var result = children ? {} : null;
- //如有undefined null 简单数据类型合并 noCount++;
- var noCount = 0;
- for (var i = 0; i < len; i++) {
- var c = children[i];
- var __type = typeof c;
- switch (__type) {
- case 'object':
- if (c.type) {
- if (c.mayInfo.reused) {
- //如果该组件 diff 两次 第一次vnode重用之后_reused为true
- //生成vchildren时需要其为false 否则第二次diff
- c.mayInfo.reused = false;
- }
- var _key = genKey(c);
- if (!result[_key]) {
- result[_key] = [c];
- } else {
- result[_key].push(c);
- }
- if (c.ref) { //如果子dom有ref 标识一下
- var owner = renderedVnode.mayInfo.refOwner;
- if (owner) {
- if (owner.refs) {
- owner.refs[c.ref] = c.mayInfo.instance || c.mayInfo.hostNode || null;
- } else {
- owner.refs = {};
- owner.refs[c.ref] = c.mayInfo.instance || c.mayInfo.hostNode || null;
- }
- }
- }
- } else {
- var iteratorFn = getIteractor(c);
- if (iteratorFn) {
- var ret = callIteractor$1(iteratorFn, c);
- for (var _i = 0; _i < ret.length; _i++) {
- var _key = genKey(ret[_i]);
- if (!result[_key]) {
- result[_key] = [ret[_i]];
- } else {
- result[_key].push(ret[_i]);
- }
- }
- }
- }
- break;
- case 'number':
- case 'string':
- //相邻的简单数据类型合并成一个字符串
- var tran = {
- type: '#text',
- mtype: 4,
- value: c,
- mayInfo: {}
- };
- if (childList[i - noCount]) {
- tran.mayInfo.hostNode = childList[i - noCount];
- }
- if ((i + 1 < len)) {
- var _ntype = typeof children[i + 1 - noCount];
- if (_ntype === 'string' || _ntype === 'number') {
- tran.value += children[i + 1 - noCount];
- noCount++;
- i++;
- }
- }
- var _k = '#text';
- if (!result[_k]) {
- result[_k] = [tran];
- } else {
- result[_k].push(tran);
- }
- break;
- default:
- noCount++;
- break;
- }
- }
- return result;
- }
- function genKey(child) {
- return !child.key ? (child.type.name || child.type) : ('_$' + child.key);
- }
- var mountOrder = 0;
- function buildComponentFromVnode(vnode) {
- var props = vnode.props;
- var key = vnode.key;
- var ref = vnode.ref;
- var context = vnode.context;
- var inst, rendered;
- var Ctor = vnode.type;
- //Component PureComponent
- if (Ctor.prototype && Ctor.prototype.render) {
- //props, context需要放在前俩
- inst = new Ctor(props, context, key, ref);
- //constructor里面props不可变
- inst.props = props;
- inst.refType = vnode.refType;
- inst.mayInst.mountOrder = mountOrder;
- mountOrder++;
- //_lifeState来控制生命周期中调用setState的作用
- //为0代表刚创建完component实例 (diff之后也会重置为0)
- inst.mayInst.lifeState = 0;
- if (inst.componentWillMount) {
- //此时如果在componentWillMount调用setState合并state即可
- //为1代表componentWillMount
- inst.mayInst.lifeState = 1;
- inst.componentWillMount();
- }
- if (inst.mayInst.mergeStateQueue) {
- inst.state = mergeState(inst);
- }
- //为2代表开始render
- //children 初次render的生命周期render DidMount
- //调用父组件的setState 都放在父组件的下一周期;
- inst.mayInst.lifeState = 2;
- rendered = inst.render(props, context);
- if (inst.getChildContext) {
- context = getChildContext(inst, context);
- }
- if (vnode.getContext) {
- inst.context = getContextByTypes(context, Ctor.contextTypes);
- }
- rendered && (rendered.mayInfo.refOwner = inst);
- } else {
- //StatelessComponent 我们赋给它一个inst 省去之后判断inst是否为空等;
- inst = {
- mayInst: {
- stateless: true
- },
- render: function (type) {
- return type(this.props, this.context);
- }
- };
- rendered = inst.render.call(vnode, Ctor);
- //should support module pattern components
- if (rendered && rendered.render) {
- console.warn('不推荐使用这种module-pattern component建议换成正常的Component形式,目前只支持render暂不支持其它生命周期方法');
- rendered = rendered.render.call(vnode, props, context);
- }
- }
- if (rendered) {
- //需要向下传递context
- rendered.context = context;
- }
- vnode.mayInfo.instance = inst;
- inst.mayInst.rendered = rendered;
- return rendered;
- }
- //React是根据type类型分成不同的Component各自具备各自的mount与update方法
- //我们这里简化成根据type类型调用不同的方法
- //mountDOM vnode.type为string直接createElement 然后render children即可
- //mountComposite vnode.type为function 需实例化component 再render children
- var mountStrategy = {
- 1: mountDOM, //dom
- 2: mountComposite, //component
- 3: mountDOM, //svg dom
- 4: mountText //text
- };
- function mountDOM(vnode, isSVG) {
- var vtype = vnode.type;
- vnode.mayInfo.isSVG = isSVG;
- var hostNode = !isSVG ? document.createElement(vtype) : document.createElementNS(NAMESPACE.svg, vnode.type);
- if (Refs.isRoot) {
- Refs.currentOwner = hostNode;
- hostNode.refs = {};
- Refs.isRoot = false;
- }
- vnode.mayInfo.hostNode = hostNode;
- diffProps(null, vnode);
- var children = vnode.props.children;
- if (!Array.isArray(children)) {
- children = [children];
- }
- var cdom, c;
- var len = children.length;
- for (let i = 0; i < len; i++) {
- var c = children[i];
- var type = typeof c;
- switch (type) {
- case 'number':
- case 'string':
- cdom = document.createTextNode(c);
- if ((i + 1) < len && (typeof children[i + 1] === 'string')) {
- cdom.nodeValue += children[i + 1];
- i++;
- }
- hostNode.appendChild(cdom);
- break;
- case 'object': //vnode
- if (c.type) {
- c.context = getContextByTypes(vnode.context, c.type.contextTypes);
- cdom = mountStrategy[c.mtype](c, isSVG);
- c.mtype === 2 && (c.mayInfo.instance.mayInst.hostNode = cdom);
- hostNode.appendChild(cdom);
- } else { //有可能是子数组iterator
- var iteratorFn = getIteractor(c);
- if (iteratorFn) {
- var ret = callIteractor$1(iteratorFn, c);
- for (var _i = 0; _i < ret.length; _i++) {
- cdom = mountStrategy[ret[_i].mtype](ret[_i], isSVG);
- ret[_i].mayInfo.hostNode = cdom;
- hostNode.appendChild(cdom);
- }
- }
- }
- }
- }
- vnode.mayInfo.vChildren = transformChildren(vnode, hostNode);
- if (FormElement[vtype]) {
- //如果是受控组件input select之类需要特殊处理下
- if (vnode.props) {
- var _val = vnode.props['value'] || vnode.props['defaultValue'] || '';
- getIsControlled(hostNode, vnode);
- hostNode._lastValue = _val;
- }
- }
- //本来想放在调度模块的 但是这种vnode type为dom类型的 func是要在DidMount之前调用的
- //因为DidMount中可能用到;
- if (vnode.ref) {
- Refs.attachRef(vnode, hostNode);
- }
- return hostNode;
- }
- function mountComposite(vnode, isSVG) {
- var hostNode = null;
- var rendered = buildComponentFromVnode(vnode);
- var inst = vnode.mayInfo.instance;
- if (!inst.mayInst.stateless && Refs.isRoot) {
- Refs.currentOwner = inst;
- inst.refs = {};
- Refs.isRoot = false;
- }
- if (rendered) {
- if (!isSVG) {
- //svg的子节点namespace也是svg
- isSVG = rendered.mtype === 3;
- }
- //递归遍历 深度优先
- hostNode = mountStrategy[rendered.mtype](rendered, isSVG);
- //dom diff需要分类一下children以方便diff
- rendered.mayInfo.vChildren = transformChildren(rendered, hostNode);
- // rendered.mayInfo.hostNode = hostNode;
- } else { //render 返回null
- hostNode = document.createComment('empty');
- vnode.mayInfo.hostNode = hostNode;
- //用于isMounted 判断 即使是null
- inst.mayInst.isEmpty = true;
- }
- if (inst.componentDidMount) {
- lifeCycleQueue.push(inst.componentDidMount.bind(inst));
- } else {
- //如果没有回调则其render生命周期结束lifeState为0
- inst.mayInst.lifeState = 0;
- }
- if (vnode.ref) {
- Refs.attachRef(vnode, inst);
- }
- return hostNode;
- }
- function mountText(vnode) {
- if (vnode) {
- var node = recyclables['#text'].pop();
- if (node) {
- node.nodeValue = node.value;
- return node;
- }
- return document.createTextNode(vnode.value);
- } else {
- return document.createComment('empty');
- }
- }
- function disposeVnode(vnode) {
- if (!vnode) {
- return;
- }
- if (vnode.refType === 1) {
- vnode.ref(null);
- vnode.ref = null;
- }
- if (vnode.mayInfo.instance) {
- disposeComponent(vnode, vnode.mayInfo.instance);
- } else if (vnode.mtype === 1) {
- disposeDomVnode(vnode);
- }
- vnode.mayInfo = null;
- }
- function disposeDomVnode(vnode) {
- var children = vnode.mayInfo.vChildren;
- if (children) {
- for (var c in children) {
- children[c].forEach(function (child) {
- disposeVnode(child);
- });
- }
- vnode.mayInfo.vChildren = null;
- }
- if (vnode.mayInfo.refOwner) {
- vnode.mayInfo.refOwner = null;
- }
- vnode.mayInfo = null;
- }
- function disposeComponent(vnode, instance) {
- if (instance.setState) {
- instance.setState = noop$1;
- instance.forceUpdate = noop$1;
- }
- if (instance.componentWillUnmount) {
- instance.componentWillUnmount();
- instance.componentWillUnmount = noop$1;
- }
- if (instance.refs) {
- instance.refs = null;
- }
- if (instance.mayInst.rendered) {
- // vnode.mayInfo.rendered = null;
- disposeVnode(instance.mayInst.rendered);
- }
- instance.mayInst.forceUpdate = instance.mayInst.dirty = vnode.mayInfo.instance = instance.mayInst = null;
- }
- var isStandard = 'textContent' in document;
- var fragment = document.createDocumentFragment();
- function disposeDom(dom) {
- if (dom._listener) {
- dom._listener = null;
- }
- if (dom.nodeType === 1) {
- if (isStandard) {
- dom.textContent = '';
- } else {
- emptyElement(dom);
- }
- } else if (dom.nodeType === 3) {
- if (recyclables['#text'].length < 100) {
- recyclables['#text'].push(dom);
- }
- }
- fragment.appendChild(dom);
- fragment.removeChild(dom);
- }
- function emptyElement(dom) {
- var c;
- while (c = dom.firstChild) {
- emptyElement(c);
- dom.removeChild(c);
- }
- }
- function noop$1() { }
- //diff根据vnode的不同类型调用不同的diff方法~
- //其实写着写着就发现还是类似React 根据不同的类型生成不同的Component
- //拥有对应的diff方法;
- var updateStrategy = {
- 1: updateDOM, //dom
- 2: updateComposite, //component
- 3: updateDOM, //svg dom
- 4: updateText //text
- };
- function updateDOM(prevVnode, newVnode) {
- if (prevVnode.refType === 1) {
- prevVnode.ref(null);
- }
- var hostNode = (prevVnode && prevVnode.mayInfo.hostNode) || null;
- var vtype = newVnode.type;
- if (!newVnode.mayInfo.hostNode) {
- newVnode.mayInfo.hostNode = hostNode;
- }
- if (Refs.isRoot) {
- Refs.currentOwner = hostNode;
- hostNode.refs = {};
- Refs.isRoot = false;
- }
- var isSVG = hostNode && hostNode.namespaceURI === NAMESPACE.svg;
- if (isSVG) {
- newVnode.mayInfo.isSVG = true;
- }
- diffProps(prevVnode, newVnode);
- diffChildren(prevVnode, newVnode, hostNode);
- newVnode.mayInfo.vChildren = transformChildren(newVnode, hostNode);
- if (FormElement[vtype]) {
- //如果是受控组件input select之类需要特殊处理下
- if (newVnode.props) {
- var isControlled = getIsControlled(hostNode, newVnode);
- var _val, hasSelected;
- if (isControlled) {
- _val = newVnode.props['value'] || hostNode._lastValue || '';
- switch (vtype) {
- case 'select':
- var _optionsChilds = [].slice.call(hostNode.childNodes);
- if (_optionsChilds) {
- for (var k = 0; k < _optionsChilds.length; k++) {
- var oChild = _optionsChilds[k];
- if (oChild.value === _val) {
- oChild.selected = true;
- hasSelected = true;
- } else {
- oChild.selected = false;
- }
- }
- if (!hasSelected) { //如果给定value没有选中的 默认第一个
- hostNode.value = _optionsChilds[0].value;
- }
- }
- break;
- }
- } else {
- //如果reRender时 dom去掉了value属性 则其变为非受控组件 value取上一次的值
- hostNode.value = hostNode._lastValue || '';
- }
- }
- }
- if (newVnode.ref) {
- Refs.attachRef(newVnode, hostNode);
- }
- return hostNode;
- }
- function updateComposite(prevVnode, newVnode) {
- if (prevVnode.refType === 1) {
- prevVnode.ref(null);
- }
- //如果newVnode没有定义contextTypes 在componentWillReceiveProps等生命周期方法中
- //是不应该传入context的 那么用空的temporaryContext代替
- var temporaryContext = {};
- var context = newVnode.context || {};
- if (newVnode.getContext) {
- context = getContextByTypes(context, newVnode.type.contextTypes);
- temporaryContext = context;
- }
- var instance = prevVnode.mayInfo.instance;
- var prevRendered = instance.mayInst.rendered;
- var hostNode = (prevRendered && prevRendered.mtype === 1) ? prevRendered.mayInfo.hostNode : instance.mayInst.hostNode;
- //empty代表prevVnode为null
- var isEmpty = instance.mayInst.isEmpty || (hostNode && hostNode.nodeType === 8);
- var newDom, newRendered, prevState, prevProps;
- var skip = false;
- if (!instance.mayInst.stateless) {
- //需要兼容componentWillReceiveProps直接this.state={///}的情况
- //先保存下之前的state
- prevState = instance.state;
- prevProps = prevVnode.props;
- //lifeState为3组件开始diff
- //WillReceive WillUpdate render DidUpdate等周期setState放到下一周期;
- instance.mayInst.lifeState = 3;
- //用于mergeState如果setState传入一个function s.call(instance, newState, instance.nextProps || instance.props);
- //其参数应当是newState nextProps
- instance.nextProps = newVnode.props;
- if (instance.getChildContext) {
- //getChildContext 有可能用到新的props
- context = getChildContext(instance, context);
- }
- var newState = mergeState(instance);
- //如果context与props都没有改变,那么就不会触发组件的receive,render,update等一系列钩子
- //但还会继续向下比较
- var needReceive = prevVnode !== newVnode || prevVnode.context !== context;
- if (needReceive) {
- if (instance.componentWillReceiveProps) {
- //componentWillReceiveProps中调用了setState 合并state
- instance.mayInst.lifeState = 1;
- instance.componentWillReceiveProps(newVnode.props, temporaryContext);
- if (instance.mayInst.mergeStateQueue && instance.mayInst.mergeStateQueue.length > 0) {
- newState = mergeState(instance);
- } else { //this.state={///}的情况
- if (instance.state !== prevState) {
- newState = instance.state;
- instance.state = prevState;
- }
- }
- }
- instance.mayInst.lifeState = 3;
- } else {
- var rendered = instance.mayInst.rendered;
- //context穿透更新问题
- rendered.context = newVnode.temporaryContext || context;
- hostNode = updateStrategy[rendered.mtype](rendered, rendered);
- return hostNode;
- }
- //shouldComponentUpdate 返回false 则不进行子组件渲染
- if (!instance.mayInst.forceUpdate && instance.shouldComponentUpdate && instance.shouldComponentUpdate(newVnode.props, newState, temporaryContext) === false) {
- skip = true;
- } else if (instance.componentWillUpdate) {
- instance.componentWillUpdate(newVnode.props, newState, temporaryContext);
- }
- newVnode.mayInfo.instance = instance;
- instance.props = newVnode.props;
- if (skip) {
- instance.state = newState;
- instance.context = context;
- instance.mayInst.dirty = false;
- return hostNode;
- }
- instance.state = newState;
- instance.context = context;
- if (Refs.isRoot) {
- Refs.currentOwner = instance;
- Refs.currentOwner.refs = {};
- Refs.isRoot = false;
- }
- newRendered = instance.render();
- newRendered && (newRendered.context = context);
- instance.mayInst.rendered = newRendered;
- if (!isEmpty && newRendered) {
- if (isSameType(prevRendered, newRendered)) {
- hostNode = updateStrategy[newRendered.mtype](prevRendered, newRendered);
- newRendered.mayInfo.hostNode = hostNode;
- } else {
- disposeVnode(prevRendered);
- var isSVG = newRendered.mtype === 3;
- newDom = mountStrategy[newRendered.mtype](newRendered, isSVG);
- newRendered.mayInfo.hostNode = newDom;
- hostNode.parentNode.replaceChild(newDom, hostNode);
- }
- } else {
- if (isEmpty && newRendered) {
- var isSVG = newRendered.mtype === 3;
- newDom = mountStrategy[newRendered.mtype](newRendered, isSVG);
- newRendered.mayInfo.hostNode = newDom;
- if (hostNode.parentNode) {
- hostNode.parentNode.replaceChild(newDom, hostNode);
- }
- } else {
- hostNode = document.createComment('empty');
- instance.mayInst.hostNode = hostNode;
- instance.mayInst.isEmpty = true;
- }
- //如果之前node为空 或 新render的为空 直接释放之前节点
- disposeVnode(prevRendered);
- }
- if (!instance.mayInst.needNextRender) {
- instance.mayInst.dirty = false;
- instance.mayInst.needNextRender = false;
- }
- if (newDom) {
- hostNode = newDom;
- }
- if (instance.componentDidUpdate) {
- lifeCycleQueue.push(instance.componentDidUpdate.bind(instance, prevProps, prevState, instance.context));
- } else {
- //如果没有回调则其render生命周期结束lifeState为0
- instance.mayInst.lifeState = 0;
- }
- if (newVnode.refType === 1) {
- Refs.attachRef(newVnode, instance);
- }
- } else { //stateless component
- var newRendered = newVnode.type.call(newVnode, newVnode.props, newVnode.context);
- newRendered.context = newVnode.context;
- if (prevRendered && isSameType(prevRendered, newRendered)) {
- hostNode = updateStrategy[newRendered.mtype](prevRendered, newRendered);
- newRendered.mayInfo.hostNode = hostNode;
- } else if (newVnode) {
- disposeVnode(prevRendered);
- var isSVG = newVnode.mtype === 3;
- newDom = mountStrategy[newVnode.mtype](newVnode, isSVG);
- newVnode.mayInfo.hostNode = newDom;
- hostNode.parentNode.replaceChild(newDom, hostNode);
- hostNode = newDom;
- }
- }
- return hostNode;
- }
- function updateText(prev, now) {
- var hostNode = now.mayInfo.hostNode || null;
- if (prev) { //child._prevVnode
- if (hostNode.nodeValue !== now.value) {
- hostNode.nodeValue = now.value;
- }
- } else {
- hostNode = document.createTextNode(now.value);
- }
- return hostNode;
- }
- function diffChildren(prevVnode, updatedVnode, parent) {
- var prevChildren = prevVnode.mayInfo.vChildren || null;
- var newRenderedChild = updatedVnode.props.children;
- if (newRenderedChild && !Array.isArray(newRenderedChild)) {
- newRenderedChild = [newRenderedChild];
- }
- //diff之前 遍历prevchildren 与newChildren 如有相同key的只对其props diff
- var _mountChildren = [];
- var _unMountChildren = [];
- var k, prevK, _prevK, _tran;
- if (newRenderedChild) {
- var len = newRenderedChild.length;
- for (var i = 0; i < len; i++) {
- var c = newRenderedChild[i];
- var t = typeof c;
- switch (t) {
- case 'object':
- k = genKey(c);
- //
- if (c.type && c.type.contextTypes) {
- c.context = getContextByTypes(updatedVnode.context, c.type.contextTypes);
- } else {
- c.context = {};
- //该组件没有定义contextTypes 无法使用context
- //我们还是需要保存一份context
- c.temporaryContext = updatedVnode.context;
- }
- break;
- case 'boolean':
- k = "#text";
- _tran = {
- type: '#text',
- mtype: 4, //text
- value: '',
- mayInfo: {}
- };
- c = _tran;
- break;
- case 'number':
- case 'string':
- k = "#text";
- _tran = {
- type: '#text',
- mtype: 4, //text
- value: c,
- mayInfo: {}
- };
- c = _tran;
- //相邻简单数据类型合并
- if ((i + 1 < newRenderedChild.length)) {
- var _ntype = typeof newRenderedChild[i + 1];
- if (_ntype === 'string' || _ntype === 'number') {
- c.value += newRenderedChild[i + 1];
- i++;
- }
- }
- break;
- case 'undefined':
- break;
- }
- prevK = prevChildren && prevChildren[k];
- if (prevK && prevK.length > 0) { //试试=0 else
- for (var _i = 0; _i < prevK.length; _i++) {
- var vnode = prevK[_i];
- if (c.type === vnode.type && !vnode.mayInfo.used) {
- vnode.mayInfo.hostNode && (c.mayInfo.hostNode = vnode.mayInfo.hostNode);
- c.mayInfo.instance = vnode.mayInfo.instance;
- c.mayInfo.prevVnode = vnode;
- vnode.mayInfo.used = true;
- c.mayInfo.reused = true;
- break;
- }
- }
- }
- if (c) {
- _mountChildren.push(c);
- }
- }
- }
- for (var name in prevChildren) {
- var _c = prevChildren[name];
- for (let j = 0; j < _c.length; j++) {
- if (!_c[j].mayInfo.used) _unMountChildren.push(_c[j]);
- }
- }
- flushMounts(_mountChildren, parent);
- flushUnMounts(_unMountChildren);
- _mountChildren.length = 0;
- }
- function flushMounts(newChildren, parent) {
- for (var _i = 0; _i < newChildren.length; _i++) {
- var child = newChildren[_i];
- var _node = parent.childNodes[_i];
- var newDom;
- if (child.mayInfo.prevVnode) { //如果可以复用之前节点
- var prevChild = child.mayInfo.prevVnode;
- delete child.mayInfo.prevVnode;
- newDom = updateStrategy[child.mtype](prevChild, child);
- if (_node && _node !== newDom) { //移动dom
- newDom = parent.removeChild(newDom);
- parent.insertBefore(newDom, _node);
- } else if (!_node) {
- parent.appendChild(newDom);
- }
- } else { //新增节点
- var isSVG = child.mtype === 3;
- // var isSVG = vnode.namespaceURI === "http://www.w3.org/2000/svg";
- newDom = mountStrategy[child.mtype](child, isSVG);
- child.mtype === 2 && (child.mayInfo.instance.mayInst.hostNode = newDom);
- // child.mayInfo.hostNode = newDom;
- if (_node) {
- parent.insertBefore(newDom, _node);
- } else {
- parent.appendChild(newDom);
- }
- }
- }
- }
- function flushUnMounts(oldChildren) {
- var c, dom;
- while (c = oldChildren.shift()) {
- if (c.mayInfo.hostNode) {
- dom = c.mayInfo.hostNode;
- c.mayInfo.hostNode = null;
- } else if (c.mtype === 2) {
- dom = c.mayInfo.instance.mayInst.hostNode;
- }
- disposeDom(dom);
- disposeVnode(c);
- c = null;
- }
- }
- function isSameType(prev, now) {
- return prev.type === now.type && prev.key === now.key;
- }
- function render(vnode, container, callback) {
- return renderByMay(vnode, container, callback);
- }
- /**
- * render传入的Component都是一个function 该方法的原型对象上绑定了render方法
- * @param {*} vnode
- * @param {*} container
- * @param {*} callback
- */
- var renderByMay = function (vnode, container, callback) {
- var renderedVnode, rootDom, result;
- var lastVnode = container._lastVnode || null;
- if (lastVnode) { //update
- Refs.isRoot = true;
- rootDom = mayUpdate(lastVnode, vnode, container);
- } else {
- if (vnode && vnode.type) {
- //为什么React使用var markup=renderChilden();这样的形式呢; 2018-1-13
- //因为如果按renderComponentChildren(renderedVnode, rootDom, _isSvg);传入container这种
- //碰上component嵌套不好处理 参见 ReactChildReconciler-test的 warns for duplicated array keys with component stack info
- var isSVG = vnode.mtype === 3;
- Refs.isRoot = true;
- rootDom = mountStrategy[vnode.mtype](vnode, isSVG);
- if (rootDom && container && container.appendChild) {
- container.appendChild(rootDom);
- } else {
- throw new Error('container参数错误');
- }
- } else {
- throw new Error('render参数错误');
- }
- }
- var instance = vnode.mayInfo.instance;
- result = instance && !instance.mayInst.stateless && instance || rootDom;
- //执行render过程中的回调函数lifeCycle ref等
- mayQueue.clearQueue();
- if (callback) { //render的 callback
- callback();
- }
- if (instance) {
- //render之后lifeState也初始化为0;
- instance.mayInst.lifeState = 0;
- instance.mayInst.hostNode = rootDom;
- //当我开始着手解决 层层嵌套的component 最底层的那个setState触发的时候lifeState并不为0
- //因为我不能确定其是否有componentDidMount的回调,它在这个回调setState是需要放到下一周期处理的
- //一种办法是该instance如果具备componentDidMount我把其lifeState标个值如10 setState的时候判断
- //另一种办法就是我新建一个instance队列 instance.pendingCallback=componentDidMount
- //然后我每次render完再遍历这个队列有回调就调用 这貌似就是ANU的逻辑吧~
- //快写完了我才发现~惭愧~看来还是自己写写好,不如琢如磨,怎么对这流程了如指掌,不胸有成竹又如何找寻这
- //最优方法
- }
- Refs.isRoot = false;
- Refs.currentOwner = null;
- container._lastVnode = vnode;
- return result;
- };
- function reRender(instance) {
- var prevProps = instance.props;
- var context = instance.context;
- var prevState = instance.state;
- var prevRendered = instance.mayInst.rendered;
- var isEmpty = instance.mayInst.isEmpty;
- var hostNode = instance.mayInst.hostNode;
- var skip = false;
- //lifeState为3组件开始diff
- //WillReceive WillUpdate render DidUpdate等周期setState放到下一周期;
- instance.mayInst.lifeState = 3;
- var newState = mergeState(instance);
- //forceUpdate时 忽略shouldComponentUpdate
- if (!instance.mayInst.forceUpdate && instance.shouldComponentUpdate && instance.shouldComponentUpdate(prevProps, newState, context) === false) {
- skip = true;
- } else if (instance.componentWillUpdate) {
- instance.componentWillUpdate(prevProps, newState, context);
- }
- instance.state = newState;
- //getChildContext有可能setState 所以需要newState再调用
- if (instance.getChildContext) {
- context = getChildContext(instance, context);
- }
- if (skip) {
- instance.mayInst.dirty = false;
- return hostNode;
- }
- if (Refs.isRoot) {
- Refs.currentOwner = instance;
- Refs.isRoot = false;
- }
- var updated = instance.render(prevProps, context);
- instance.mayInst.rendered = updated;
- updated && (updated.context = context);
- if (!updated) {
- disposeVnode(prevRendered);
- return;
- }
- if (!isEmpty && isSameType(prevRendered, updated)) {
- // updated.mayInfo.instance = instance;
- // updated.mayInfo.hostNode = hostNode;
- hostNode = updateStrategy[updated.mtype](prevRendered, updated);
- //mtype === 1在这在设置instance会循环引用
- // updated.mtype === 2 && ();
- updated.mayInfo.vChildren = transformChildren(updated, hostNode);
- instance.mayInst.forceUpdate = null;
- //原先这种diffProps 再diffChildren的方式并未考虑到 render返回之后还是
- //组件怎么办,连续几个组件嵌套children都需要render怎么办 这些情况都是diffProps
- //无法处理的,当时想的是一个组件diff diff之后再diff下一个组件;但如果组件之间发生交互的
- //话是需要在一个处理流程的;那diff我们也需要这种递归的思想了,所以setState的时候我们设置
- //instance的_dirty为true如果父组件 子组件都dirty,父组件向下diff的过程中也会diff子组件
- //此时在子组件diff之后我们要把_dirty设为false 否则因为子组件也在diffQueue之中,会再进行
- //一次diff是多余的;不晓得我说明白没有~参考测试ReactCompositeComponentNestedState-state的
- //should provide up to date values for props
- // diffProps(prevRenderedVnode, updatedVnode);
- } else {
- var isSVG = updated.mtype === 3;
- var dom = mountStrategy[updated.mtype](updated, isSVG);
- //component上的hostNode保持最新
- // var lastVnode = hostNode.parentNode._lastVnode;
- // lastVnode && (lastVnode.mayInfo.hostNode = dom);
- hostNode.parentNode.replaceChild(dom, hostNode);
- // updated.mayInfo.hostNode = dom;
- hostNode = dom;
- instance.mayInst.hostNode = dom;
- updated.mayInfo.vChildren = transformChildren(updated, hostNode);
- instance.mayInst.forceUpdate = null;
- disposeVnode(prevRendered);
- }
- if (instance.componentDidUpdate) {
- instance.componentDidUpdate(prevProps, prevState, instance.context);
- // lifeCycleQueue.push(instance.componentDidUpdate.bind(instance, prevProps, prevState, instance.context));
- } else {
- instance.mayInst.lifeState = 0;
- }
- //needNextRender情况是 子组件在diff生命周期(如WillReceiveProps)调用父组件的setState
- //这种情况下父组件需要再进行一次diff,不过本地diff完成时c.mayInst.dirty 会为false 所以需要
- //mayInst.dirty为true;ReactCompositeComponentState-test 的should update state when called from child cWRP
- if (!instance.mayInst.needNextRender) {
- instance.mayInst.dirty = false;
- instance.mayInst.needNextRender = false;
- }
- }
- function mayUpdate(prevVnode, newVnode, parent) {
- var dom;
- if (isSameType(prevVnode, newVnode)) {
- dom = updateStrategy[newVnode.mtype](prevVnode, newVnode);
- } else {
- var isSVG = newVnode.mtype === 3;
- dom = mountStrategy[newVnode.mtype](newVnode, isSVG);
- var hostNode = (prevVnode && prevVnode.mtype === 1) ? prevVnode.mayInfo.hostNode : prevVnode.mayInfo.instance.mayInst.hostNode;
- if (!parent) {
- parent = hostNode && hostNode.parentNode;
- }
- parent.replaceChild(dom, hostNode);
- disposeVnode(prevVnode);
- hostNode = null;
- }
- // newVnode.mtype === 2 && (newVnode.mayInfo.instance.mayInst.hostNode = dom);
- // newVnode.mayInfo.hostNode = dom;
- return dom;
- }
- function unmountComponentAtNode(dom) {
- var lastVnode = dom._lastVnode;
- if (dom.refs) {
- dom.refs = null;
- }
- if (lastVnode) {
- disposeVnode(lastVnode);
- emptyElement(dom);
- //unmount之后又render
- //参见ReactComponentLifeCycle-test
- //should not reuse an instance when it has been unmounted
- dom._lastVnode.mayInfo = {};
- dom._lastVnode = null;
- }
- }
- function findDOMNode(ref) {
- var ret = null;
- if (ref) {
- if (ref.nodeType === 1) {
- return ref;
- } else {
- var c = ref.mayInst.rendered;
- while (c) {
- if (c.mtype === 1) {
- return c.mayInfo.hostNode;
- } else if (c.mayInfo.hostNode) {
- return c.mayInfo.hostNode;
- }
- c = c.mayInfo.instance.mayInst.rendered;
- }
- }
- }
- return ret;
- }
- function Component(props, context, key, ref) {
- this.props = props;
- this.key = key;
- this.ref = ref;
- this.context = context;
- //新建个对象存放各种信息
- this.mayInst = {};
- }
- Component.prototype.setState = function (state, callback) {
- var lifeState = this.mayInst.lifeState;
- if (callback) {
- //回调队列调用之前也许sort
- callback = callback.bind(this);
- callback._mountOrder = this.mayInst.mountOrder;
- mayQueue.callbackQueue.push(callback);
- }
- if (this.mayInst.mergeStateQueue) {
- this.mayInst.mergeStateQueue.push(state);
- } else {
- this.mayInst.mergeStateQueue = new Array(state);
- }
- if (mayQueue.isInEvent) {
- //如果在绑定事件中 触发setState合并state
- if (mayQueue.dirtyComponentsQueue.indexOf(this) === -1) {
- this.mayInst.dirty = true;
- mayQueue.dirtyComponentsQueue.push(this);
- }
- return;
- }
- switch (lifeState) {
- //componentWillReceiveProps触发setState会合并state
- case 1: //componentWillMount 触发setState会合并state
- return;
- //ComponentWillReceiveProps 中setState 3
- //子组件在ComponentWillMount中调用父组件的setState 3
- case 3:
- case 2: //componentDidMount 触发setState会放到下一周期 2
- if (mayQueue.dirtyComponentsQueue.indexOf(this) === -1) {
- this.mayInst.dirty = true;
- this.mayInst.needNextRender = true; //子组件componentWillReceiveProps 调用父组件的setState 触发setState会放到下一周期
- mayQueue.dirtyComponentsQueue.push(this);
- }
- return;
- default:
- if (mayQueue.dirtyComponentsQueue.indexOf(this) === -1) {
- this.mayInst.dirty = true;
- mayQueue.dirtyComponentsQueue.push(this);
- }
- break;
- }
- mayQueue.clearQueue();
- };
- Component.prototype.forceUpdate = function (callback) {
- if (callback) {
- mayQueue.callbackQueue.push(callback.bind(this));
- }
- if (mayQueue.dirtyComponentsQueue.indexOf(this) === -1) {
- this.mayInst.forceUpdate = true;
- this.mayInst.dirty = true;
- mayQueue.dirtyComponentsQueue.push(this);
- }
- var lifeState = this.mayInst.lifeState;
- switch (lifeState) {
- case 1: //ComponentWillMount
- case 2: //componentDidMount
- case 3: //ComponentWillReceiveProps
- case 4: //ComponentWillReceiveProps
- return;
- default:
- mayQueue.clearQueue();
- break;
- }
- };
- Component.prototype.isMounted = function () {
- return this.mayInst ? (!!(this.mayInst.rendered && this.mayInst.rendered.mayInfo.hostNode) || this.mayInst.isEmpty) : false;
- };
- function PureComponent(props, key, ref, context) {
- return Component.apply(this, arguments);
- }
- var fn = inherits(PureComponent, Component);
- //返回false 则不进行之后的渲染
- fn.shouldComponentUpdate = function (nextProps, nextState, context) {
- var ret = true;
- var a = shallowEqual(this.props, nextProps);
- var b = shallowEqual(this.state, nextState);
- if (a === true && b === true) {
- ret = false;
- }
- return ret;
- };
- function shallowEqual(now, next) {
- if (Object.is(now, next)) {
- return true;
- }
- //必须是对象
- if ((now && typeof now !== 'object') || (next && typeof next !== 'object')) {
- return false;
- }
- var keysA = Object.keys(now);
- var keysB = Object.keys(next);
- if (keysA.length !== keysB.length) {
- return false;
- }
- // Test for A's keys different from B.
- for (var i = 0; i < keysA.length; i++) {
- if (!hasOwnProperty.call(next, keysA[i]) || !Object.is(now[keysA[i]], next[keysA[i]])) {
- return false;
- }
- }
- return true;
- }
- function cloneElement(element, additionalProps) {
- var type = element.type;
- var props = element.props;
- var mergeProps = {};
- Object.assign(mergeProps, props, additionalProps);
- var config = {};
- if (element.key) {
- config.key = element.key;
- }
- if (element.ref) {
- config.ref = element.ref;
- }
- for (const key in mergeProps) {
- if (key !== 'children') {
- config[key] = mergeProps[key];
- }
- }
- var children = mergeProps.children;
- var ret = createElement(type, config, children);
- return ret;
- }
- const Children = {
- only: function (child) {
- if (child && !Array.isArray(child)) {
- return child;
- }
- if (child && child.length === 1 && child[0].mtype) {
- return child[0];
- }
- throw new Error("expect only one child");
- },
- forEach: function (children, callback, context) {
- var ret;
- if (!children) {
- return null;
- }
- ret = toArray(children);
- ret.forEach(callback, context);
- return ret;
- },
- map: function (children, callback, context) {
- var ret = [];
- if (children == null) {
- //null 或undefinded直接返回
- return children;
- }
- toArray(children).forEach(function (item, index) {
- var res = callback.call(context, item, index);
- if (res == null) {
- return;
- } else {
- ret.push(res);
- }
- });
- return ret;
- },
- toArray: function (children) {
- if (children == null) {
- return [];
- }
- return toArray(children);
- }
- };
- function toArray(children) {
- var ret = [];
- if (Array.isArray(children)) {
- for (var i = 0; i < children.length; i++) {
- var c = children[i];
- if (c.type) {
- ret.push(c);
- } else { //有可能是子数组iterator
- var iteratorFn = getIteractor(c);
- if (iteratorFn) {
- var iterators = callIteractor(iteratorFn, c);
- for (var _i = 0; _i < iterators.length; _i++) {
- ret.push(iterators[_i]);
- }
- } else {
- ret.push(c);
- }
- }
- }
- } else {
- ret.push(children);
- }
- return ret;
- }
- //为了兼容yo
- var check = function () {
- return check;
- };
- check.isRequired = check;
- var PropTypes = {
- array: check,
- bool: check,
- func: check,
- number: check,
- object: check,
- string: check,
- any: check,
- arrayOf: check,
- element: check,
- instanceOf: check,
- node: check,
- objectOf: check,
- oneOf: check,
- oneOfType: check,
- shape: check
- };
- var May = {
- createElement: createElement,
- Component: Component,
- PureComponent: PureComponent,
- cloneElement: cloneElement,
- Children: Children,
- render: render,
- PropTypes: PropTypes,
- findDOMNode: findDOMNode,
- unmountComponentAtNode: unmountComponentAtNode,
- isValidElement: function (vnode) {
- return vnode && vnode.mtype;
- },
- createFactory: function createFactory(type) {
- console.error("createFactory is deprecated");
- var factory = createElement.bind(null, type);
- factory.type = type;
- return factory;
- }
- };
- window.React = window.ReactDOM = May;
- module.exports = May;
|