├── .gitattributes ├── .gitignore ├── README.md ├── LittleReact.html └── LittleReact.js /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear in the root of a volume 35 | .DocumentRevisions-V100 36 | .fseventsd 37 | .Spotlight-V100 38 | .TemporaryItems 39 | .Trashes 40 | .VolumeIcon.icns 41 | 42 | # Directories potentially created on remote AFP share 43 | .AppleDB 44 | .AppleDesktop 45 | Network Trash Folder 46 | Temporary Items 47 | .apdisk 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LittleReact 2 | LittleReact说明: 3 | 4 | 2017-12-6更新: 5 | 6 | 其实当时写这个博客的本意是为了找工作方便,显摆显摆自己;不过这么长时间竟然有好多人看过,还有几个小伙伴给我的github几个start,心中甚是惶恐,担心误人子弟,便再更新自己的一些看法。 7 | 8 | 9 | 10 | 写此文时是年初 当时为了换个好工作 未免有些急功近利 只求快 未曾细细品味~如今看来当时的我无非就是一个看了几本书就觉得自己挺牛逼的二货而已; 11 | 12 | 而今马上就是18年了,真是一入江湖岁月催啊~ 13 | 14 | 15 | 16 | 言归正传:其实当时我的水准应当还算可以 毕竟看了很多很多书 不过对React的理解还是有些勉强了,当时写这个LittleReact算是照猫画虎吧,跟着断点一点一点写下来了的,所以只是描摹了一些,很多地方都没有思考过; 17 | 18 | 虽然努力不会白费,但这样写一遍下来对React的理解我估计撑死也就三成; 19 | 20 | 21 | 如果:你工作比较稳定,没那么着急,那就这么来:真正的自己实现一个React,先学会karma rollup这两个工具karma是用来做单元测试的,rollup是用来打包代码的;把preact(迷你版react),react,anu(司徒正美先生写的一个类react框架很棒的),下载到自己本地,react的单元测试是用的facebook自己的jest先不用管,先看preact这个库比较简单,然后自己一点点写,写一部分然后就跑跑单元测试(单元测试这个三个框架都有,自己一点一点写相似的单元测试);过程中一定要自己写,把React对应的api当成黑盒子自己 22 | 23 | 实现自己的逻辑,实在不知道怎么写再看看别人怎么写的,这个过程会比较漫长,不过你肯定会收获满满,我现在就在这么做,在这给你安利下我的 MayReact https://github.com/sven36/MayReact 24 | 25 | 最后的效果就是你会写一个能在生产环境使用的React,因为你最终会把所有的单元测试都跑一遍, 再之后就是同样的思路写写react-redux,react-router,那时再使用React 如烹小鲜尔(我目前还在写React之中,这些都完成之后我打算写写webpack和babel了~) 26 | 27 | 路漫漫其修远兮,愿你我都能孜孜以求~ 28 | 29 | 30 | 31 | 32 | 33 | ---------------------------------2333的分割线-----以下为原文就不修改了算是对自己的中二一种纪念吧~------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ 34 | 35 | 36 | 37 | 前言:最近一直在看React源码,看了几天总有雾里观花之感;便想着自己从头写一遍,这几天基本上写了个大概了; 38 | 39 | React的VirtualDOM,DOM diff,生命周期管理,单向数据流动等等都具备了; 40 | 41 | 当然也省略了非常多的细节和检查,事件系统和CallBackQuenue,PoolClass等都没有~(暂时DOM diff不太全,我再想想怎么写更好); 42 | 43 | 不过这样足够理解React了,而且React剩下的部分看看就基本知道作用了; 44 | 45 | 46 | 47 | 48 | 那么言归正传:说一说我个人感觉其中比较难得地方和一些思路;(我使用的是React15.3版本) 49 | 50 | 首先:React现在已经非常庞大了,我当初本想多写几篇一点一点介绍的,不过我发现即使写了光看的话肯定看不明白的,最好的方法只能是自己写一遍; 51 | 52 | 第二点:必须要首先弄懂Transition事务模块; 53 | 54 | Transition模块:现在React绝大部分模块都需要Transition触发的,这个是一个包裹函数,构造函数需原型继承Transaction,或原型方法逐个赋值。且getTransactionWrappers方法用于添加前置钩子与后置钩子;reinitializeTransaction方法在初始化时调用,用于清空前、后置钩子;perform方法实际执行method函数、及前后钩子。(看不懂可以去跑一跑我的Git上的示例) 55 | 56 | 第三点:ReactElement以及ReactComponent;传入的参数先转化为ReactElement,然后根据不同的node类型转换为不同的ReactComponent; 57 | 58 | 第四点:ReactComponent的递归渲染和ReactClass的原型混入传入的参数,在递归渲染时原型调用(这个我有些说不明白),我是调试React的运行过程,看了十来遍看明白的~~ 59 | 60 | 第五点:纸上得来终觉浅,绝知此事要躬行~~ 61 | 62 | 63 | 绝大部分代码都加上注释了,想自己写一写的可以参照一下; 64 | 65 | 最后附上我的参照资料,也深深感谢这两位作者: 66 | 67 | http://purplebamboo.github.io/2015/09/15/reactjs_source_analyze_part_two/ 68 | 69 | http://schifred.iteye.com/category/368891 70 | -------------------------------------------------------------------------------- /LittleReact.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 |-----------------
12 | 13 | 14 | 15 | 16 | 17 | 105 | 106 | 107 | 108 | 109 | 110 | 270 | -------------------------------------------------------------------------------- /LittleReact.js: -------------------------------------------------------------------------------- 1 | //空函数用于ReactClass原型继承及混入 2 | var ReactClassComponent = function ReactClassComponent() { }; 3 | var emptyObject = {}; 4 | var ReactClass = { 5 | //创建自定义组件;creactClass是创建自定义组件的入口方法,负责管理生命周期中的getDefaultProps; 6 | //该方法在整个生命周期中只执行一次,这样所有实例初始化的props将会共享; 7 | createClass: function createClass(spec) { 8 | var Constructor = function (props, context, updater) { 9 | // 自动绑定 10 | if (this.__reactAutoBindPairs.length) { 11 | bindAutoBindMethods(this); 12 | } 13 | this.props = props; 14 | this.context = context; 15 | this.refs = emptyObject; 16 | this.updater = updater; 17 | // ReactClasses没有构造函数,通过getInitialState和componentWillMount来代替 18 | var initialState = this.getInitialState ? this.getInitialState() : null; 19 | this.state = initialState; 20 | }; 21 | //原型继承父类 22 | Constructor.prototype = new ReactClassComponent(); 23 | Constructor.prototype.constructor = Constructor; 24 | Constructor.prototype.__reactAutoBindPairs = []; 25 | //Constructor原型即ReactClassComponent渲染传入的方法以及React的API; 26 | var proto = Constructor.prototype; 27 | for (var name in spec) { 28 | var property = spec[name]; 29 | proto[name] = property; 30 | } 31 | //渲染完原型API之后初始化getDefaultProps;(在整个生命周期中getDefaultProps只执行一次); 32 | if (Constructor.getDefaultProps) { 33 | Constructor.defaultProps = Constructor.getDefaultProps(); 34 | } 35 | for (var methodName in ReactClassInterface) { 36 | if (!Constructor.prototype[methodName]) { 37 | Constructor.prototype[methodName] = null; 38 | } 39 | } 40 | return Constructor; 41 | } 42 | } 43 | ReactClassComponent.prototype.setState = function (partialState) { 44 | var nextState = partialState; 45 | var inst = this; 46 | inst.state = extend(inst.state, nextState); 47 | var nextRenderedElement = inst.render(); 48 | if (inst.componentWillUpdate) { 49 | inst.componentWillUpdate(); 50 | } 51 | var internalInstance = inst._reactInternalInstance; 52 | var prevElement = internalInstance._currentElement; 53 | //比较之前的props与setstate调用render方法生成的props,如果type与key都相同 54 | //则替换原先的props为setstate调用之后的props; 55 | if (shouldUpdateReactComponent(prevElement, nextRenderedElement)) { 56 | internalInstance._currentElement = nextRenderedElement; 57 | internalInstance.updateComponent(prevElement, nextRenderedElement); 58 | 59 | } else { 60 | 61 | } 62 | 63 | if (inst.componentDidUpdate) { 64 | inst.componentDidUpdate(); 65 | } 66 | } 67 | var ReactInstanceMap = { 68 | //缓存渲染后的node 69 | remove: function remove(key) { 70 | key._reactInternalInstance = undefined; 71 | }, 72 | 73 | get: function get(key) { 74 | return key._reactInternalInstance; 75 | }, 76 | 77 | has: function has(key) { 78 | return key._reactInternalInstance !== undefined; 79 | }, 80 | 81 | set: function set(key, value) { 82 | key._reactInternalInstance = value; 83 | } 84 | 85 | }; 86 | //继承 87 | var extend = function (target, source) { 88 | var from; 89 | var to = Object(target); 90 | var symbols; 91 | for (var s = 1; s < arguments.length; s++) { 92 | from = Object(arguments[s]); 93 | for (var key in from) { 94 | if (hasOwnProperty.call(from, key)) { 95 | to[key] = from[key]; 96 | } 97 | } 98 | if (Object.getOwnPropertySymbols) { 99 | symbols = Object.getOwnPropertySymbols(from); 100 | for (var i = 0; i < symbols.length; i++) { 101 | if (propIsEnumerable.call(from, symbols[i])) { 102 | to[symbols[i]] = from[symbols[i]]; 103 | } 104 | } 105 | } 106 | } 107 | return to; 108 | }; 109 | //React的批量更新策略即递归更新 110 | var ReactUpdates = { 111 | /** 112 | * React references `ReactReconcileTransaction` using this property in order 113 | * to allow dependency injection. 114 | * 115 | * @internal 116 | */ 117 | ReactReconcileTransaction: null, 118 | //callback batchedMountComponentIntoNode 119 | batchedUpdates: function (callback, a, b, c, d, e) { 120 | return ReactDefaultBatchingStrategy.batchedUpdates(callback, a, b, c, d, e); 121 | }, 122 | enqueueUpdate: function () { }, 123 | flushBatchedUpdates: function () { }, 124 | injection: function () { }, 125 | asap: function () { } 126 | }; 127 | //Transaction模块用于实现,某构造函数的实例调用perform(method,args)方法时,在method函数执行前后调用特定钩子函数的功能。 128 | //成对的前置钩子initialize函数和后置钩子close函数以数组形式添加, 129 | //且前置钩子initialize函数用于向后置钩子close函数提供参数,在method函数前调用; 130 | //后置钩子close在method函数后调用。 131 | 132 | //构造函数需原型继承Transaction,或原型方法逐个赋值。且getTransactionWrappers方法用于添加前置钩子与后置钩子; 133 | //reinitializeTransaction方法在初始化时调用,用于清空前、后置钩子;perform方法实际执行method函数、及前后钩子。 134 | 135 | //React事件~就是将需要执行的方法用wrapper封装起来,再通过包装提供的perform方法执行; 136 | //而在perform之前,先执行所有wrapper中的initiallize方法,执行完perform之后再执行所有的close的方法。一组initial及close方法称为一个wrapper; 137 | 138 | //声明一个空对象~代表错误的状态~因为Transaction初始化数据返回不会是空对象; 139 | var OBSERVED_ERROR = {}; 140 | 141 | var Transaction = { 142 | reinitializeTransaction: function reinitializeTransaction() { 143 | //初始化事件,根据调用对象不同,生成不同的transactionWrappers;并清空已有的钩子函数; 144 | this.transactionWrappers = this.getTransactionWrappers(); 145 | if (this.wrapperInitData) { 146 | this.wrapperInitData.length = 0; 147 | } else { 148 | this.wrapperInitData = []; 149 | } 150 | this._isInTransaction = false; 151 | }, 152 | 153 | _isInTransaction: false, 154 | 155 | getTransactionWrappers: null, 156 | 157 | isInTransaction: function isInTransaction() { 158 | return !!this._isInTransaction; 159 | }, 160 | //true代表正在执行事件中 161 | perform: function perform(method, scope, a, b, c, d, e, f) { 162 | var errorThrown; 163 | var ret; 164 | try { 165 | this._isInTransaction = true; 166 | errorThrown = true; 167 | this.initializeAll(0); 168 | //调用传入的方法,其实我感觉ReactTransaction就是抽象出一层事件模型~根据调用对象初始化不同的数据~ 169 | //优点一:优化JS固有的try catch如果事件出错的话transaction粒度更细; 170 | //优点二:解耦React的事件触发,根据不同对象传入不同transactionWrappers,然后调用不同的initial和close方法,不过通用一个事件处理过程; 171 | ret = method.call(scope, a, b, c, d, e, f); 172 | errorThrown = false; 173 | } finally { 174 | try { 175 | if (errorThrown) { 176 | try { 177 | this.closeAll(0); 178 | } catch (err) { } 179 | } else { 180 | this.closeAll(0); 181 | } 182 | } finally { 183 | this._isInTransaction = false; 184 | } 185 | } 186 | return ret; 187 | }, 188 | 189 | initializeAll: function initializeAll(startIndex) { 190 | var transactionWrappers = this.transactionWrappers; 191 | for (var i = startIndex; i < transactionWrappers.length; i++) { 192 | var wrapper = transactionWrappers[i]; 193 | try { 194 | //虽然Wrappers初始化都是close方法和initialize方法;不过不同的 Wrappers传入的close方法和initialize方法是不同的 195 | // this.wrapperInitData[i]先复制为初始状态空对象代表错误,再调用wrapper.initialize.call(this),如果Wrappers的 196 | //initialize方法需要初始化数据则进行,如果不需要则返回undefined,undefined是!=={}(空对象的即OBSERVED_ERROR) 197 | this.wrapperInitData[i] = OBSERVED_ERROR; 198 | this.wrapperInitData[i] = wrapper.initialize ? wrapper.initialize.call(this) : null; 199 | } finally { 200 | if (this.wrapperInitData[i] === OBSERVED_ERROR) { 201 | // 到这一步代表wrapper初始化数据出错了~那么继续下一个,如果有err则停止初始化 202 | try { 203 | this.initializeAll(i + 1); 204 | } catch (err) { } 205 | } 206 | } 207 | } 208 | }, 209 | 210 | closeAll: function closeAll(startIndex) { 211 | var transactionWrappers = this.transactionWrappers; 212 | for (var i = startIndex; i < transactionWrappers.length; i++) { 213 | var wrapper = transactionWrappers[i]; 214 | var initData = this.wrapperInitData[i]; 215 | var errorThrown; 216 | try { 217 | errorThrown = true; 218 | if (initData !== OBSERVED_ERROR && wrapper.close) { 219 | wrapper.close.call(this, initData); 220 | } 221 | errorThrown = false; 222 | } finally { 223 | if (errorThrown) { 224 | try { 225 | this.closeAll(i + 1); 226 | } catch (e) { } 227 | } 228 | } 229 | } 230 | //把初始化的数据清空 231 | this.wrapperInitData.length = 0; 232 | } 233 | }; 234 | //用来初始化的空函数 235 | var emptyFunction = function emptyFunction() { }; 236 | var RESET_BATCHED_UPDATES = { 237 | initialize: emptyFunction, 238 | close: function close() { 239 | ReactDefaultBatchingStrategy.isBatchingUpdates = false; 240 | } 241 | }; 242 | var FLUSH_BATCHED_UPDATES = { 243 | initialize: emptyFunction, 244 | // ReactUpdates.flushBatchedUpdates方法以特定钩子重绘dirtyComponents中的各组件 245 | // 钩子包括ReactUpdatesFlushTransaction前后钩子,含组件重绘完成后的回调_pendingCallbacks 246 | // 包括ReactReconcileTransaction前后钩子,含componentDidMount、componentDidUpdate回调 247 | close: ReactUpdates.flushBatchedUpdates.bind(ReactUpdates) 248 | }; 249 | function ReactDefaultBatchingStrategyTransition() { 250 | this.reinitializeTransaction(); 251 | } 252 | var ReactDefaultBatchingStrategy = { 253 | isBatchingUpdates: false, 254 | batchedUpdates: function batchedUpdates(callback, a, b, c, d, e) { 255 | var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates; 256 | ReactDefaultBatchingStrategy.isBatchingUpdates = true; 257 | // 如果已经初始化过一次了则直接调用~否则transaction.perform方法还会在过程中初始化数据 258 | if (alreadyBatchingUpdates) { 259 | return callback(a, b, c, d, e); 260 | } else { 261 | return transaction.perform(callback, null, a, b, c, d, e); 262 | } 263 | }, 264 | }; 265 | // 首先我们要先混入事件系统的方法,包括初始化,close,perform等等; 266 | extend(ReactDefaultBatchingStrategyTransition.prototype, Transaction, { 267 | //用于初始化transactionWrappers的方法 268 | getTransactionWrappers: function () { 269 | return [FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES]; 270 | } 271 | }); 272 | //初始化transaction会调用混入的reinitializeTransaction方法,然后返回[FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES]数组对应的initial和close方法;还会初始化数据; 273 | var transaction = new ReactDefaultBatchingStrategyTransition(); 274 | //用来判定两个element需不需要更新 275 | //这里的key是我们createElement的时候可以选择性的传入的。用来标识这个element,当发现key不同时,我们就可以直接重新渲染,不需要去更新了。 276 | function shouldUpdateReactComponent(prevElement, nextElement) { 277 | var prevEmpty = prevElement === null || prevElement === false; 278 | var nextEmpty = nextElement === null || nextElement === false; 279 | var prevType = typeof prevElement; 280 | var nextType = typeof nextElement; 281 | if (prevEmpty || nextEmpty) { 282 | return prevEmpty === nextEmpty; 283 | } 284 | if (prevType === 'string' || prevType === 'number') { 285 | return nextType === 'string' || nextType === 'number'; 286 | } else { 287 | return nextType === 'object' && prevElement.type === nextElement.type && prevElement.key === nextElement.key; 288 | } 289 | } 290 | 291 | var LittleReact = { 292 | nextReactRootIndex: 0, 293 | createClass: ReactClass.createClass, 294 | render: function (nextElement, container, callback) { 295 | //React之中是需要先包裹一下,我把这个去了; 这个包裹是对reactElement进行一个元素包裹;在React之前的版本中需要根据传入的node是DOM还是字符串,区分为ReactDOMComponent或ReactDOMTextComponent 296 | //或ReactCompositeComponent;用这个临时的TopLevelWrapper函数包裹所有的属性,这样就不必在这判断node的类型了; 297 | //var nextWrappedElement = LittleReactElement(TopLevelWrapper, null, null, null, null, null, nextElement); 298 | var nextContext = {}; 299 | //if (parentComponent) { 300 | // var parentInst = ReactInstanceMap.get(parentComponent); 301 | // nextContext = parentInst._processChildContext(parentInst._context); 302 | //} else { 303 | // nextContext;//emptyObject; 304 | //} 305 | var containerHasReactMarkup;//是否有渲染过的reactElement 306 | var containerHasNonRootReactChild;//是否有子节点; 307 | //获取容器的子元素; 308 | var reactRootElement; 309 | //nodeType为9代表是Document 310 | if (container.nodeType === 9) { 311 | reactRootElement = container.documentElement; 312 | } else { 313 | reactRootElement = container ? container.firstChild : null; 314 | } 315 | //如果容器此前拥有过react子节点则需要找到它,然后进行DOMdiff比较; 316 | var prevComponent = getClosestInstanceFromNode(reactRootElement); 317 | if (prevComponent) { 318 | //获取容器子节点上次渲染的的ReactElement元素 319 | var prevElement = prevComponent._currentElement; 320 | //检查是不是同一个类型的node; 321 | var shouldUpdate = shouldUpdateReactComponent(prevElement, nextElement); 322 | if (shouldUpdate) { 323 | //脏检查; 324 | var dirtyComponents = []; 325 | var updateNumber = 0; 326 | var domDiff; 327 | var publicInst = prevComponent._hostNode; 328 | //给先前节点赋上当前需要渲染的元素,开始脏检查; 329 | prevComponent._pendingElement = nextElement; 330 | if (prevComponent._updateNumber == null) { 331 | prevComponent._updateNumber = updateNumber + 1; 332 | } 333 | var prevChildren = prevComponent._renderedChildren; 334 | var nextChildren = nextElement.props.children; 335 | var name; 336 | var prevChild; 337 | //ReactDOMComponent的_updateChildren方法; 338 | for (name in nextChildren) { 339 | prevChild = prevChildren && prevChildren[name];//保存之前渲染的节点包含相同名字(即顺序相同的子节点); 340 | var difPrevElement = prevChild && prevChild._currentElement; 341 | var difNextElement = nextChildren[name]; 342 | if (prevChild != null && shouldUpdateReactComponent(difPrevElement, difNextElement)) { 343 | if (difPrevElement.key === difPrevElement.key) { 344 | nextChildren[name] = prevChild;//二者相同则直接采用之前渲染的node; 345 | } 346 | } else {//不同则需要渲染这个节点; 347 | var nextChildInstance = new instantiateReactComponent(difNextElement); 348 | nextChildren[name] = nextChildInstance; 349 | } 350 | } 351 | if (Array.isArray(nextChildren)) { 352 | for (var i = 0; i < nextChildren.length; i++) { 353 | nextChildren[i].mountComponent(prevComponent._hostNode, null, null, prevComponent._hostNode.ownerDocument); 354 | } 355 | return; 356 | } 357 | } 358 | } 359 | else { 360 | containerHasNonRootReactChild = false; 361 | } 362 | 363 | if (reactRootElement) { 364 | containerHasReactMarkup = !!reactRootElement.getAttribute && !!reactRootElement.getAttribute('"data-reactid"'); 365 | } 366 | //是否需要重新渲染? 367 | var shouldReuseMarkup = containerHasReactMarkup && !prevComponent && !containerHasNonRootReactChild; 368 | //var component = ReactMount._renderNewRootComponent(nextWrappedElement, container, shouldReuseMarkup, nextContext)._renderedComponent.getPublicInstance(); 369 | 370 | //当使用React创建组件时,首先会调用instantiateReactComponent,这是初始化组件的入口函数 371 | //它根据判断node类型来区分不同组件的入口 372 | var componentInstance = instantiateReactComponent(nextElement); 373 | ReactInstanceMap.set(componentInstance, this); 374 | //获取容器的标签和doc用于创建DOM标签; 375 | var tag = container ? container.nodeName.toLowerCase() : null; 376 | var _ownerDocument = container ? container.nodeType === 9 ? container : container.ownerDocument : null 377 | ReactUpdates.batchedUpdates(performInitialMount, componentInstance, container, tag, _ownerDocument); 378 | } 379 | } 380 | 381 | 382 | //type是dom标签如p,div等或者是自定义组件;props是标签的属性如id,onclick方法等;Children就是是否有子节点,比如标签下的文本就算子节点; 383 | LittleReact.createElement = function (type, config, children) { 384 | //type是string的话就代表是普通DOM标签,如果是function则代表是自定义的组件; 385 | var validType = typeof type === 'string' || typeof type === 'function'; 386 | var propName; 387 | var props = {}; 388 | 389 | var key = null; 390 | var ref = null; 391 | var self = null; 392 | var source = null; 393 | //ref用于父组件引用子组件的真实DOM,key用于调和算法,判断该组件是否update或remove 这个比较高级可稍后再议 394 | if (config) { 395 | ref = config.ref; 396 | key = config.key; 397 | //RESERVED_PROPS为_source, self,key,ref的一个枚举,此方法的作用是让props复制除_source, self,key,ref之外的其它属性; 398 | for (propName in config) { 399 | if (hasOwnProperty.call(config, propName) && !RESERVED_PROPS.hasOwnProperty(propName)) { 400 | props[propName] = config[propName]; 401 | } 402 | } 403 | } 404 | //处理children,全部挂载到props的Children属性上,如果只有一个子元素则直接赋值; 405 | //这个arguments.length - 2;如果不太明白可以看ReactDOM.render(UL, document.getElementById('content'))这个示例; 406 | var childrenLength = arguments.length - 2; 407 | if (childrenLength === 1) { 408 | props.children = children; 409 | } else if (childrenLength > 1) { 410 | var childArray = Array(childrenLength); 411 | for (var i = 0; i < childrenLength; i++) { 412 | childArray[i] = arguments[i + 2]; 413 | } 414 | props.children = childArray; 415 | } 416 | //如果某个prop为空且存在默认的prop,则将默认prop赋给当前prop; 417 | if (type && type.defaultProps) { 418 | var defaultProps = type.defaultProps; 419 | for (propName in defaultProps) { 420 | if (props[propName] === undefined) { 421 | props[propName] = defaultProps[propName]; 422 | } 423 | } 424 | } 425 | return LittleReactElement(type, key, ref, self, source, null, props); 426 | //这里react还需要验证子元素是否正确以及props属性是否正确,我们先略过; 427 | } 428 | var LittleReactElement = function (type, key, ref, self, source, owner, props) { 429 | var element = { 430 | $$typeof: 'Symbol(react.element)',//一个标识; 判断是React生成的元素还是dom自带的元素 431 | type: type, 432 | key: key, 433 | ref: ref, 434 | props: props, 435 | _owner: owner 436 | } 437 | //react这部分有一个生成一个元素备份的方法,并定义了几个属性,然后把element密封,我们先去掉这些细节; 438 | return element; 439 | } 440 | 441 | 442 | //render方法里面的方法 443 | //node元素标识 444 | var internalInstanceKey = '__littleReactInstance$' + Math.random().toString(36).slice(2); 445 | //标识顺序,用于递归渲染; 446 | var topLevelRootCounter = 1; 447 | var TopLevelWrapper = function () { 448 | this.rootID = topLevelRootCounter++; 449 | } 450 | //递归渲染节点时调用render方法; 451 | TopLevelWrapper.prototype.render = function () { 452 | return this.props; 453 | }; 454 | 455 | //容器如果之前曾渲染过Reat组件,则根据缓存的标识internalInstanceKey返回该组件 456 | function getClosestInstanceFromNode(node) { 457 | if (!node) { 458 | return null; 459 | } 460 | //传入一个DOM node 返回ReactDOMComponent 或 ReactDOMTextComponent实例,如果不是ReactElement则返回null 461 | if (node[internalInstanceKey]) { 462 | return node[internalInstanceKey]; 463 | } 464 | var parents = []; 465 | while (!node[internalInstanceKey]) { 466 | parents.push(node); 467 | if (node.parentNode) 468 | node = node.parentNode; 469 | else 470 | return null; 471 | } 472 | } 473 | 474 | //创建ReactComponent的工厂模式~根据传入node返回不同组件; 475 | function instantiateReactComponent(node) { 476 | var instance; 477 | //当node类型为对象时,即是DOM标签或者自定义组件,如果element类型为字符串时,则初始化DOM标签组件,否则初始化自定义组件; 478 | if (typeof node === 'object') { 479 | if (typeof node.type === 'string') { 480 | instance = new ReactDOMComponent(node); 481 | } 482 | else { 483 | var isInternalComponentType = typeof type === 'function' && typeof type.prototype !== 'undefined' && typeof type.prototype.mountComponent === 'function' && typeof type.prototype.receiveComponent === 'function'; 484 | if (!isInternalComponentType) { 485 | //自定义组件 486 | instance = new ReactCompositeComponent(node); 487 | } else { 488 | //不是字符串表示的自定义组件暂时无法使用,此后将不做组件初始化操作; 489 | 490 | } 491 | 492 | } 493 | } 494 | else if (typeof node === 'string' || typeof node === 'number') { 495 | //字符串或数字(ReactTextComponent)在执行mountComponent方法时,ReactDOMTextComponent通过transition.useCreateElement判断该文本是否是通过createElement 496 | //方法创建的节点;如果是则为该节点创建相应的标签和标识domID;这样每个文本节点也拥有了自己的唯一标识也拥有了Virtual DOM diff的权利; 497 | instance = new ReactDOMTextComponent(node); 498 | } 499 | else if (node === null || node === false) { 500 | instance = ReactEmptyComponent.create(instantiateReactComponent); 501 | } 502 | //初始化参数 503 | instance._mountIndex = 0; 504 | instance._mountImage = null; 505 | return instance; 506 | } 507 | 508 | 509 | 510 | var ReactCompositeComponent = function (element) { 511 | // ReactComponentElement,配置了组件的构造函数、props属性等 512 | this._currentElement = element; 513 | this._rootNodeID = 0; 514 | //区分纯函数无状态组件、继承自PureComponent的纯组件、以及继承自Component的组件 515 | this._compositeType = null; 516 | this._instance = null;// ReactComponent实例 517 | this._hostParent = null;// 文档元素,作为组件元素的父节点 518 | this._hostContainerInfo = null; 519 | this._updateBatchNumber = null; 520 | this._pendingElement = null;// ReactDom.render方法渲染时包裹元素由react组件渲染,_pendingElement存储待渲染元素 521 | this._pendingStateQueue = null;// 组件调用setState、replaceState方法,通过ReactUpdateQueue将更迭后的state推入_pendingStateQueue 522 | this._pendingReplaceState = false;// 判断组件是否通过调用replaceState方法向_pendingStateQueue推入state数据 523 | this._pendingForceUpdate = false;// 组件调用forceUpdate赋值为真 524 | this._renderedNodeType = null;// 节点类型,区分ReactComponentElement、ReactDomElement元素 525 | this._renderedComponent = null;// 子组件的ReactComponent实例 526 | this._context = null;// 赋值给组件的context属性 527 | this._mountOrder = 0;// 挂载的第几个组件 528 | this._topLevelWrapper = null; 529 | this._pendingCallbacks = null; 530 | this._calledComponentWillUnmount = false; 531 | } 532 | ReactCompositeComponent.prototype.displayName = 'ReactCompositeComponent'; 533 | ReactCompositeComponent.prototype.mountComponent = function (hostParent, hostContainerInfo, tag, ownerDocument) { 534 | return performInitialMount(this, hostParent, tag, ownerDocument); 535 | } 536 | 537 | 538 | 539 | //mountComponent附则管理生命周期中的getInitialState,componentWillMount,render和componentDidMount 540 | //初始化组件,渲染标记,注册监听事件 541 | var nextMountID = 1; 542 | function FactoryMountComponent(internalInstance, hostContainerInfo, tag, ownerDocument) { 543 | var hostParent; 544 | //var internalInstance; 545 | //if (child.$$typeof) { 546 | // internalInstance 547 | //} 548 | //调用组件对应原型的渲染方法; 549 | if (internalInstance.displayName === 'ReactCompositeComponent') { 550 | var markup = internalInstance.mountComponent(internalInstance, hostContainerInfo, tag, ownerDocument); 551 | } 552 | var markup = internalInstance.mountComponent(hostParent, hostContainerInfo, tag, ownerDocument); 553 | return markup; 554 | } 555 | 556 | //进行递归渲染,每次都获得当前节点的子节点,根据子节点的不同类型调用相应的渲染方法; 557 | function performInitialMount(componentInstance, hostContainerInfo, tag, ownerDocument) { 558 | var props = componentInstance._currentElement.props; 559 | var displayName = componentInstance.displayName; 560 | var Component = componentInstance._currentElement.type; 561 | var child; 562 | var inst; 563 | 564 | //如果拥有子组件则找到它,目的是先渲染子组件; 565 | if (displayName == 'ReactCompositeComponent') { 566 | inst = new Component(props, null, null); 567 | 568 | child = inst.render(); 569 | child = instantiateReactComponent(child); 570 | inst._reactInternalInstance = child; 571 | //存储constructor,用于回调绑定的函数 572 | child._constructor = inst; 573 | tag = child._tag; 574 | } 575 | else { 576 | inst = function () { }; 577 | //如果是ReactDOMComponent则直接调用其原型渲染方法; 578 | child = componentInstance; 579 | } 580 | //得到currentElement子节点对应的Component类实例; 581 | //保存子节点的实例,用于循环递归到最底层然后从最底层开始渲染;比如ReactDOMComponentTree的precacheNode方法; 582 | this._renderedComponent = child; 583 | //如果存在componentWillMount则调用; 584 | if (inst.componentWillMount) { 585 | inst.componentWillMount(); 586 | // componentWillMount调用setState时,不会触发re-render而是自动提前合并; 587 | if (this._pendingStateQueue) { 588 | inst.state = this._processPendingState(inst.props, inst.context); 589 | } 590 | } 591 | //递归渲染 592 | var markup = FactoryMountComponent(child, hostContainerInfo, tag, ownerDocument); 593 | //如果存在componentDidMount则调用; 594 | if (inst.componentDidMount) { 595 | inst.componentDidMount(); 596 | } 597 | var identify = false; 598 | if (child._containerInfo) { 599 | identify = true; 600 | } 601 | mountNodesToContainer(markup, hostContainerInfo, identify); 602 | } 603 | 604 | //最后一步~把渲染后的Node添加进容器; 605 | function mountNodesToContainer(markup, container, identify) { 606 | if (identify) { 607 | container.appendChild(markup.node); 608 | } else { 609 | while (container.lastChild) { 610 | container.removeChild(container.lastChild); 611 | } 612 | container.insertBefore(markup.node, null); 613 | } 614 | 615 | } 616 | 617 | var ReactDOMComponent = function (element) { 618 | var tag = element.type; 619 | this._currentElement = element; 620 | this._tag = tag.toLowerCase(); 621 | this._namespaceURI = null; 622 | this._renderedChildren = [];//存放渲染后的子节点,如果节点更改,进行DOMdiff比较; 623 | this._previousStyle = null; 624 | this._previousStyleCopy = null; 625 | this._hostNode = null; 626 | this._hostParent = null; 627 | this._rootNodeID = 0; 628 | this._domID = 0; 629 | this._hostContainerInfo = null; 630 | this._wrapperState = null; 631 | this._topLevelWrapper = null; 632 | this._flags = 0; 633 | } 634 | var _idCounter = 1; 635 | ReactDOMComponent.prototype.displayName = 'ReactDOMComponent'; 636 | ReactDOMComponent.prototype.mountComponent = function (hostParent, container, tag, ownerDocument) { 637 | this._domID = _idCounter++; 638 | this._containerInfo = container; 639 | this._hostParent = hostParent;//是否有React父组件; 640 | var props = this._currentElement.props; 641 | 642 | switch (tag) { 643 | case 'audio': 644 | case 'form': 645 | case 'iframe': 646 | case 'img': 647 | case 'link': 648 | case 'object': 649 | case 'source': 650 | case 'video': 651 | this._wrapperState = { 652 | listeners: null 653 | }; 654 | transaction.getReactMountReady().enqueue(trapBubbledEventsLocal, this); 655 | break; 656 | case 'button': 657 | props = ReactDOMButton.getHostProps(this, props, hostParent); 658 | break; 659 | case 'input': 660 | ReactDOMInput.mountWrapper(this, props, hostParent); 661 | props = ReactDOMInput.getHostProps(this, props); 662 | transaction.getReactMountReady().enqueue(trapBubbledEventsLocal, this); 663 | break; 664 | case 'option': 665 | ReactDOMOption.mountWrapper(this, props, hostParent); 666 | props = ReactDOMOption.getHostProps(this, props); 667 | break; 668 | case 'select': 669 | ReactDOMSelect.mountWrapper(this, props, hostParent); 670 | props = ReactDOMSelect.getHostProps(this, props); 671 | transaction.getReactMountReady().enqueue(trapBubbledEventsLocal, this); 672 | break; 673 | case 'textarea': 674 | ReactDOMTextarea.mountWrapper(this, props, hostParent); 675 | props = ReactDOMTextarea.getHostProps(this, props); 676 | transaction.getReactMountReady().enqueue(trapBubbledEventsLocal, this); 677 | break; 678 | } 679 | 680 | // We create tags in the namespace of their parent container, except HTML 681 | // tags get no namespace. 682 | var namespaceURI; 683 | var parentTag; 684 | if (hostParent != null) { 685 | namespaceURI = hostParent._namespaceURI; 686 | parentTag = hostParent._tag; 687 | } else if (tag) { 688 | namespaceURI = container.namespaceURI; 689 | parentTag = tag; 690 | } 691 | this._namespaceURI = namespaceURI; 692 | var mountImage; 693 | if (container) {//?~暂未确定 694 | var el; 695 | if (namespaceURI) { 696 | if (this._tag === 'script') { 697 | // Create the script via .innerHTML so its "parser-inserted" flag is 698 | // set to true and it does not execute 699 | var div = ownerDocument.createElement('div'); 700 | var type = this._currentElement.type; 701 | div.innerHTML = '<' + type + '>' + type + '>'; 702 | el = div.removeChild(div.firstChild); 703 | } else if (props.is) { 704 | el = ownerDocument.createElement(this._currentElement.type, props.is); 705 | } else { 706 | // Separate else branch instead of using `props.is || undefined` above becuase of a Firefox bug. 707 | // See discussion in https://github.com/facebook/react/pull/6896 708 | // and discussion in https://bugzilla.mozilla.org/show_bug.cgi?id=1276240 709 | el = ownerDocument.createElement(this._currentElement.type); 710 | } 711 | } else { 712 | el = ownerDocument.createElement(this._currentElement.type); 713 | //el = ownerDocument.createElementNS(namespaceURI, this._currentElement.type); 714 | } 715 | 716 | ReactDOMComponentTree.precacheNode(this, el); 717 | //this._flags |= Flags.hasCachedChildNodes; 718 | if (!this._containerInfo) { 719 | el.setAttribute('data-reactroot', ''); 720 | } 721 | var child;//如果有子节点 722 | var nextName;//如果有子节点,子节点名称 723 | 724 | //把属性赋上去; 725 | this.updateDOMProperties(null, props, transaction); 726 | var lazyTree = DOMLazyTree(el); 727 | if (typeof props.children === 'string' || typeof props.children === 'number') { 728 | var firstChild = lazyTree.node.firstChild; 729 | if (firstChild && firstChild === node.lastChild && firstChild.nodeType === 3) { 730 | firstChild.nodeValue = props.children; 731 | return; 732 | } 733 | lazyTree.node.textContent = props.children; 734 | this._renderedComponent = lazyTree.node; 735 | } 736 | else if (Array.isArray(props.children)) {//有未渲染的子元素 737 | var children = props.children; 738 | for (var i = 0; i < children.length; i++) { 739 | child = new instantiateReactComponent(children[i]); 740 | child._constructor = this._constructor; 741 | this._renderedChildren.push(child);//保存渲染后的子节点;用于DOM diff比较; 742 | nextName = "." + i.toString(36); 743 | child.mountComponent(lazyTree.node, container, null, ownerDocument); 744 | } 745 | } 746 | if (hostParent) { 747 | hostParent.appendChild(lazyTree.node); 748 | } 749 | mountImage = lazyTree; 750 | } else { 751 | var tagOpen = this._createOpenTagMarkupAndPutListeners(transaction, props); 752 | var tagContent = this._createContentMarkup(transaction, props, context); 753 | if (!tagContent && omittedCloseTags[this._tag]) { 754 | mountImage = tagOpen + '/>'; 755 | } else { 756 | mountImage = tagOpen + '>' + tagContent + '' + this._currentElement.type + '>'; 757 | } 758 | } 759 | 760 | switch (tag) { 761 | case 'input': 762 | transaction.getReactMountReady().enqueue(inputPostMount, this); 763 | if (props.autoFocus) { 764 | transaction.getReactMountReady().enqueue(AutoFocusUtils.focusDOMComponent, this); 765 | } 766 | break; 767 | case 'textarea': 768 | transaction.getReactMountReady().enqueue(textareaPostMount, this); 769 | if (props.autoFocus) { 770 | transaction.getReactMountReady().enqueue(AutoFocusUtils.focusDOMComponent, this); 771 | } 772 | break; 773 | case 'select': 774 | if (props.autoFocus) { 775 | transaction.getReactMountReady().enqueue(AutoFocusUtils.focusDOMComponent, this); 776 | } 777 | break; 778 | case 'button': 779 | if (props.autoFocus) { 780 | transaction.getReactMountReady().enqueue(AutoFocusUtils.focusDOMComponent, this); 781 | } 782 | break; 783 | case 'option': 784 | transaction.getReactMountReady().enqueue(optionPostMount, this); 785 | break; 786 | } 787 | 788 | return mountImage; 789 | } 790 | ReactDOMComponent.prototype.updateDOMProperties = function (lastProps, nextProps, transaction) { 791 | var propKey; 792 | var styleName; 793 | var styleUpdates; 794 | for (propKey in lastProps) { 795 | if (nextProps.hasOwnProperty(propKey) || !lastProps.hasOwnProperty(propKey) || lastProps[propKey] == null) { 796 | continue; 797 | } 798 | if (propKey === "style") { 799 | var lastStyle = this._previousStyleCopy; 800 | for (styleName in lastStyle) { 801 | if (lastStyle.hasOwnProperty(styleName)) { 802 | styleUpdates = styleUpdates || {}; 803 | styleUpdates[styleName] = ''; 804 | } 805 | } 806 | this._previousStyleCopy = null; 807 | } else if (registrationNameModules.hasOwnProperty(propKey)) { 808 | if (lastProps[propKey]) { 809 | // Only call deleteListener if there was a listener previously or 810 | // else willDeleteListener gets called when there wasn't actually a 811 | // listener (e.g., onClick={null}) 812 | deleteListener(this, propKey); 813 | } 814 | } else if (isCustomComponent(this._tag, lastProps)) { 815 | if (!RESERVED_PROPS.hasOwnProperty(propKey)) { 816 | DOMPropertyOperations.deleteValueForAttribute(getNode(this), propKey); 817 | } 818 | } else if (DOMProperty.properties[propKey] || DOMProperty.isCustomAttribute(propKey)) { 819 | DOMPropertyOperations.deleteValueForProperty(getNode(this), propKey); 820 | } 821 | } 822 | for (propKey in nextProps) { 823 | var nextProp = nextProps[propKey]; 824 | var lastProp = propKey === "style" ? this._previousStyleCopy : lastProps != null ? lastProps[propKey] : undefined; 825 | if (!nextProps.hasOwnProperty(propKey) || nextProp === lastProp || nextProp == null && lastProp == null) { 826 | continue; 827 | } 828 | if (propKey === "style") { 829 | if (nextProp) { 830 | if ("development" !== 'production') { 831 | checkAndWarnForMutatedStyle(this._previousStyleCopy, this._previousStyle, this); 832 | this._previousStyle = nextProp; 833 | } 834 | nextProp = this._previousStyleCopy = _assign({}, nextProp); 835 | } else { 836 | this._previousStyleCopy = null; 837 | } 838 | if (lastProp) { 839 | // Unset styles on `lastProp` but not on `nextProp`. 840 | for (styleName in lastProp) { 841 | if (lastProp.hasOwnProperty(styleName) && (!nextProp || !nextProp.hasOwnProperty(styleName))) { 842 | styleUpdates = styleUpdates || {}; 843 | styleUpdates[styleName] = ''; 844 | } 845 | } 846 | // Update styles that changed since `lastProp`. 847 | for (styleName in nextProp) { 848 | if (nextProp.hasOwnProperty(styleName) && lastProp[styleName] !== nextProp[styleName]) { 849 | styleUpdates = styleUpdates || {}; 850 | styleUpdates[styleName] = nextProp[styleName]; 851 | } 852 | } 853 | } else { 854 | // Relies on `updateStylesByID` not mutating `styleUpdates`. 855 | styleUpdates = nextProp; 856 | } 857 | } 858 | else if (typeof nextProp==='function') {//如果是事件则添加event 859 | if (nextProp&&this._constructor) { 860 | var eventType = propKey.replace('on', '').toLowerCase(); 861 | //绑定作用域 862 | nextProp=nextProp.bind(this._constructor); 863 | EventListener.listen(this._hostNode, eventType, nextProp);//EventListener.dispatchEvent 864 | } else if (lastProp) { 865 | deleteListener(this, propKey); 866 | } 867 | } 868 | //else if (isCustomComponent(this._tag, nextProps)) { 869 | // if (!RESERVED_PROPS.hasOwnProperty(propKey)) { 870 | // DOMPropertyOperations.setValueForAttribute(getNode(this), propKey, nextProp); 871 | // }} 872 | else if (typeof nextProps[propKey] === 'string') { 873 | var node; 874 | if (this._hostNode) {//取出我们在precacheNode方法中保存的父节点; 875 | node = this._hostNode; 876 | } 877 | // 把传入的props赋给节点; 878 | if (propKey != null&&propKey!=='children') { 879 | node.setAttribute(propKey, '' + nextProps[propKey]); 880 | } else { 881 | //DOMPropertyOperations.deleteValueForProperty(node, propKey); 882 | } 883 | } 884 | } 885 | if (styleUpdates) { 886 | CSSPropertyOperations.setValueForStyles(getNode(this), styleUpdates, this); 887 | } 888 | } 889 | ReactDOMComponent.prototype.updateComponent = function (prevElement, nextElement) { 890 | var lastProps = prevElement.props; 891 | var nextProps = this._currentElement.props; 892 | this.updateDOMProperties(lastProps, nextProps, transaction); 893 | //替换children; 894 | var prevChildren = this._renderedChildren; 895 | var nextChildren; 896 | var prevChild; 897 | if (Array.isArray(nextProps.children)) { 898 | nextChildren = nextProps.children; 899 | for (var i = 0; i < nextChildren.length; i++) { 900 | prevChild = prevChildren && prevChildren[i]; 901 | var prevChildElement = prevChild && prevChild._currentElement; 902 | var nextChildElement = nextChildren[i]; 903 | if (prevChild != null && shouldUpdateReactComponent(prevChildElement, nextChildElement)) { 904 | prevChild._currentElement = nextChildElement; 905 | prevChild.updateComponent(prevChildElement, nextChildElement); 906 | nextChildren[i] = prevChild; 907 | } else { 908 | if (prevChild) { 909 | //清除以改变的子节点,否则会造成该组件一直存在内存中; 910 | } 911 | var nextChildInstance = instantiateReactComponent(nextChildElement); 912 | nextChildren[i] = nextChildInstance; 913 | var markup = nextChildInstance.mountComponent(this._hostNode, this._containerInfo, nextChildElement.tag, this._containerInfo.ownerDocument); 914 | } 915 | } 916 | } 917 | if (typeof nextProps.children === 'string' || typeof nextProps.children === 'number') { 918 | var firstChild = this._hostNode.firstChild; 919 | if (firstChild && firstChild === this._hostNode.lastChild && firstChild.nodeType === 3) { 920 | firstChild.nodeValue = nextProps.children; 921 | //return; 922 | } 923 | this._hostNode.textContent = nextProps.children; 924 | } 925 | //this._updateDOMChildren(lastProps, nextProps, transaction, context); 926 | } 927 | var ReactDOMComponentTree = { 928 | getClosestInstanceFromNode: '', 929 | getInstanceFromNode: '', 930 | getNodeFromInstance: '', 931 | precacheChildNodes: function (inst, node) { 932 | }, 933 | precacheNode: function (inst, node) { 934 | var rendered; 935 | while (rendered = inst._renderedComponent) { 936 | inst = rendered; 937 | } 938 | inst._hostNode = node; 939 | node[internalInstanceKey] = inst; 940 | }, 941 | uncacheNode: '' 942 | }; 943 | function DOMLazyTree(node) { 944 | return { 945 | node: node, 946 | children: [], 947 | html: null, 948 | text: null, 949 | toString: toString 950 | }; 951 | } 952 | var ReactDOMTextComponent = function (text) { 953 | this._currentElement = text; 954 | this._stringText = '' + text; 955 | // ReactDOMComponentTree uses these: 956 | this._hostNode = null; 957 | this._hostParent = null; 958 | // Properties 959 | this._domID = 0; 960 | this._mountIndex = 0; 961 | this._closingComment = null; 962 | this._commentNodes = null; 963 | } 964 | ReactDOMTextComponent.prototype.displayName = 'ReactDOMTextComponent'; 965 | ReactDOMTextComponent.prototype.mountComponent = function (hostParent, container, tag, ownerDocument) { 966 | var domID = _idCounter++; 967 | var openingValue = ' react-text: ' + domID + ' '; 968 | var closingValue = ' /react-text '; 969 | this._domID = domID; 970 | this._hostParent = hostParent; 971 | var openingComment = ownerDocument.createComment(openingValue); 972 | var closingComment = ownerDocument.createComment(closingValue); 973 | var lazyTree = DOMLazyTree(ownerDocument.createDocumentFragment()); 974 | lazyTree.node.appendChild(openingComment); 975 | if (this._stringText) { 976 | lazyTree.node.appendChild(ownerDocument.createTextNode(this._stringText)); 977 | } 978 | lazyTree.node.appendChild(closingComment); 979 | ReactDOMComponentTree.precacheNode(this, openingComment); 980 | this._closingComment = closingComment; 981 | return lazyTree; 982 | } 983 | 984 | //冒泡阶段的事件监听; 985 | var EventListener = { 986 | listen: function (target,eventType,callback) { 987 | if (target.addEventListener) { 988 | target.addEventListener(eventType, callback, false); 989 | return { 990 | remove: function () { 991 | target.removeEventListener(eventType, callback, false); 992 | } 993 | } 994 | } 995 | else if (target.attchEvent) { 996 | target.attchEvent('on'+eventType, callback); 997 | return { 998 | remove: function () { 999 | target.detachEvent('on' + eventType, callback); 1000 | } 1001 | } 1002 | } 1003 | }//, 1004 | //dispatchEvent: function (nativeEvent) { 1005 | // //ReactUpdates.batchedUpdates(nativeEvent); 1006 | // var targetInst = getClosestInstanceFromNode(nativeEvent.target); 1007 | // if (targetInst._hostNode) { 1008 | // targetInst = targetInst._hostNode; 1009 | // } 1010 | //} 1011 | }; 1012 | 1013 | 1014 | //RESERVED_PROPS为_source, self,key,ref的一个枚举,此方法的作用是让props复制除_source, self,key,ref之外的其它属性; 1015 | var RESERVED_PROPS = { 1016 | _source: true, 1017 | self: true, 1018 | key: true, 1019 | ref: true 1020 | } 1021 | //React API,ReactClass初始化的时候需要赋上方法名; 1022 | var ReactClassInterface = { 1023 | mixins: 'DEFINE_MANY', 1024 | statics: 'DEFINE_MANY', 1025 | propTypes: 'DEFINE_MANY', 1026 | contextTypes: 'DEFINE_MANY', 1027 | childContextTypes: 'DEFINE_MANY', 1028 | getDefaultProps: 'DEFINE_MANY_MERGED', 1029 | getInitialState: 'DEFINE_MANY_MERGED', 1030 | getChildContext: 'DEFINE_MANY_MERGED', 1031 | render: 'DEFINE_ONCE', 1032 | componentWillMount: 'DEFINE_MANY', 1033 | componentDidMount: 'DEFINE_MANY', 1034 | componentWillReceiveProps: 'DEFINE_MANY', 1035 | shouldComponentUpdate: 'DEFINE_ONCE', 1036 | componentWillUpdate: 'DEFINE_MANY', 1037 | componentDidUpdate: 'DEFINE_MANY', 1038 | componentWillUnmount: 'DEFINE_MANY', 1039 | updateComponent: 'OVERRIDE_BASE' 1040 | 1041 | }; 1042 | --------------------------------------------------------------------------------