UNPKG

hm-react-cli

Version:

Create a Huimei React project by module

424 lines (376 loc) 14.1 kB
(function(global, factory) { typeof exports === "object" && typeof module !== "undefined" ? (module.exports = factory()) : typeof define === "function" && define.amd ? define(factory) : (global.DevTools = factory()); })(this, function() { var options = React.options || {}; var roots = {}; options.roots = roots; function findVNodeFromDOM(vnode, dom) { if (!vnode) { for (var i in roots) { var root = roots[i]; var result = findVNodeFromDOM(root, dom); if (result) { return result; } } } else { if (vnode.node === dom) { //如果是原子虚拟DOM,直接比较 _hostNode === dom return vnode; } var children = vnode._renderedChildren; if (children) { for (var _i = 0; _i < children.length; _i++) { var child = children[_i]; if (child) { var _result = findVNodeFromDOM(child, dom); if (_result) { return _result; } } } } } } function normalizeChildren(vnode, dom) { var ret = []; for (var a = vnode.child; a; a = a.sibling) { ret.push(updateReactComponent(a, dom)); } return ret; } //===================================  用于根据一个虚拟DOM找到它的组件实例 var instanceMap = new Map(); function getKeyForVNode(vnode) { return vnode.stateNode } function getInstanceFromVNode(vnode) { var key = getKeyForVNode(vnode); return instanceMap.get(key); } /** * Create a bridge for exposing Inferno's component tree to React DevTools. * * It creates implementations of the interfaces that ReactDOM passes to * devtools to enable it to query the component tree and hook into component * updates. * * See https://github.com/facebook/react/blob/59ff7749eda0cd858d5ee568315bcba1be75a1ca/src/renderers/dom/ReactDOM.js * for how ReactDOM exports its internals for use by the devtools and * the `attachRenderer()` function in * https://github.com/facebook/react-devtools/blob/e31ec5825342eda570acfc9bcb43a44258fceb28/backend/attachRenderer.js * for how the devtools consumes the resulting objects. */ function createDevToolsBridge() { console.log("createDevToolsBridge....."); var ComponentTree = { getNodeFromInstance: function getNodeFromInstance(instance) { //createReactDOMComponent生成的实例有vnode对象 return instance.node; }, getClosestInstanceFromNode: function getClosestInstanceFromNode(dom) { var vnode = findVNodeFromDOM(null, dom); //转换为ReactCompositeComponent或ReactDOMComponent return vnode ? updateReactComponent(vnode, null) : null; } }; // Map of root ID (the ID is unimportant) to component instance. //会根据vnode创建实例,并保存在instanceMap与roots中 if(document.readyState === "complete"){ findRoots(document.body, roots); }else{ var id = setInterval(function(){ if(document.readyState === "complete"){ clearInterval(id); findRoots(document.body, roots); } }, 100); } var Mount = { _instancesByReactRootID: roots, // tslint:disable-next-line:no-empty _renderNewRootComponent: function _renderNewRootComponent() {} }; var Reconciler = { // tslint:disable-next-line:no-empty mountComponent: function mountComponent() {}, // tslint:disable-next-line:no-empty performUpdateIfNecessary: function performUpdateIfNecessary() {}, // tslint:disable-next-line:no-empty receiveComponent: function receiveComponent() {}, // tslint:disable-next-line:no-empty unmountComponent: function unmountComponent() {} }; //============ var queuedMountComponents = new Map(); var queuedReceiveComponents = new Map(); var queuedUnmountComponents = new Map(); var queueUpdate = function queueUpdate(updater, map, component) { if (!map.has(component)) { map.set(component, true); requestAnimationFrame(function() { updater(component); map.delete(component); }); } }; var queueMountComponent = function queueMountComponent(component) { return queueUpdate( Reconciler.mountComponent, queuedMountComponents, component ); }; var queueReceiveComponent = function queueReceiveComponent(component) { return queueUpdate( Reconciler.receiveComponent, queuedReceiveComponents, component ); }; var queueUnmountComponent = function queueUnmountComponent(component) { return queueUpdate( Reconciler.unmountComponent, queuedUnmountComponents, component ); }; // 创建 componentAdded, componentUpdated,componentRemoved三个重要钩子 var componentAdded = function componentAdded(vnode) { var instance = updateReactComponent(vnode); //将_currentElement代替为ReactCompositeComponent实例 if (isRootVNode(vnode)) { instance._rootID = nextRootKey(roots); roots[instance._rootID] = instance; Mount._renderNewRootComponent(instance); } //遍历非实组件的孩子 visitNonCompositeChildren(instance, function(childInst) { if (childInst) { childInst._inDevTools = true; queueMountComponent(childInst); } }); queueMountComponent(instance); }; var componentUpdated = function componentUpdated(vnode) { var prevRenderedChildren = []; //通过anujs instance得到 ReactCompositeComponent实例 visitNonCompositeChildren(instanceMap.get(vnode), function( childInst ) { prevRenderedChildren.push(childInst); }); var instance = updateReactComponent(vnode); queueReceiveComponent(instance); visitNonCompositeChildren(instance, function(childInst) { if (!childInst._inDevTools) { // New DOM child component childInst._inDevTools = true; queueMountComponent(childInst); } else { // Updated DOM child component queueReceiveComponent(childInst); } }); prevRenderedChildren.forEach(function(childInst) { if (!document.body.contains(childInst.node)) { instanceMap.delete(childInst.node); queueUnmountComponent(childInst); } }); }; var componentRemoved = function componentRemoved(vnode) { var instance = updateReactComponent(vnode); visitNonCompositeChildren(instance, function(childInst) { instanceMap.delete(childInst.node); queueUnmountComponent(childInst); }); queueUnmountComponent(instance); instanceMap.delete(vnode); if (instance._rootID) { delete roots[instance._rootID]; } }; return { componentAdded: componentAdded, componentUpdated: componentUpdated, componentRemoved: componentRemoved, ComponentTree: ComponentTree, Mount: Mount, Reconciler: Reconciler }; } //是否为根节点的虚拟DOM function isRootVNode(vnode) { if(vnode.isTop){ return vnode; } // TODO 判断是否为根节点的方法和 preact 流程不一样,这里有可能有问题 for (var i in roots) { if (roots[i] === vnode) { return true; } } return false; } /** *roots里面都是经过 * * @param {DOMElement} node * @param {[key: string] => ReactDOMComponent|ReactCompositeComponent} */ function findRoots(node, roots) { Array.from(node.childNodes || []).forEach(function(child) { var vnode = child.__component || child._reactInternalFiber; if (vnode) { roots[nextRootKey(roots)] = updateReactComponent(vnode); } else { findRoots(child, roots); } }); } function updateReactComponent(vnode, parentDom) { if (!vnode) { return null; } var newInstance; if (vnode.tag < 3) { newInstance = createReactCompositeComponent(vnode); } else { newInstance = createReactDOMComponent(vnode, parentDom); } var oldInstance = getInstanceFromVNode(vnode); if (oldInstance) { Object.assign(oldInstance, newInstance); return oldInstance; } var key = getKeyForVNode(vnode); if (key) { instanceMap.set(key, newInstance); } //将它存入instanceMap中 return newInstance; } function createReactDOMComponent(vnode, parentDom) { var type = vnode.type; if ( type === "#comment" || vnode === null || vnode == undefined || vnode === true || vnode === false ) { return null; } var props = vnode.props; var dom = vnode.stateNode; var isText = typeof vnode !== "object" || type === "#text"; return { _currentElement: isText ? vnode.text + "" : { type: type, props: props }, _inDevTools: false, _renderedChildren: !isText && normalizeChildren(vnode, dom), _stringText: isText ? vnode.text + "" : null, node: dom || parentDom }; } /** * Return a ReactCompositeComponent-compatible object for a given Inferno * component instance. * * This implements the subset of the ReactCompositeComponent interface that * the DevTools requires in order to walk the component tree and inspect the * component's properties. * * See https://github.com/facebook/react-devtools/blob/e31ec5825342eda570acfc9bcb43a44258fceb28/backend/getData.js */ function createReactCompositeComponent(vnode) { var type = vnode.type; var typeName = type.displayName || type.name; var instance = vnode.stateNode var dom = React.findDOMNode(instance); return { getName: function getName() { return typeName; }, type: type, _instance: instance, state: instance.state, node: dom, props: instance.props, _currentElement: { type: type, key: normalizeKey(vnode.key), props: vnode.props, ref: null }, // _renderedComponent: updateReactComponent(instance.updater.rendered, dom), _renderedComponent: updateReactComponent(vnode.child, dom), forceUpdate: instance.forceUpdate && instance.forceUpdate.bind(instance), setState: instance.setState && instance.setState.bind(instance) }; } function nextRootKey(roots) { return "." + Object.keys(roots).length; } function normalizeKey(key) { if (key && key[0] === ".") { return null; } } /** * Visit all child instances of a ReactCompositeComponent-like object that are * not composite components (ie. they represent DOM elements or text) */ function visitNonCompositeChildren(instance, callback) { if(!instance) { return; } if (instance._renderedComponent) { callback(instance._renderedComponent); visitNonCompositeChildren(instance._renderedComponent, callback); } else if (instance._renderedChildren) { instance._renderedChildren.forEach(function(child) { if (child) { callback(child); visitNonCompositeChildren(child, callback); } }); } } function initDevTools() { /* tslint:disable */ console.log("初始chrome react 调试工具"); var bridge = createDevToolsBridge(); var Methods = { afterMount: "componentAdded", afterUpdate: "componentUpdated", beforeUnmount: "componentRemoved" }; for (var name in Methods) { (function(key, alias) { var oldMethod = options[key]; //重写anujs原有的方法 options[key] = function(instance) { var updater = instance.updater;//1.2.8 var vnode = updater._reactInternalFiber; bridge[alias](vnode); if (oldMethod) { oldMethod(vnode); } }; })(name, Methods[name]); } window["__REACT_DEVTOOLS_GLOBAL_HOOK__"].inject(bridge); } initDevTools(); });