请选择 进入手机版 | 继续访问电脑版

技术控

    今日:0| 主题:61213
收藏本版 (1)
最新软件应用技术尽在掌握

[其他] React Implementation Notes

[复制链接]
软妹子 发表于 2016-10-11 04:14:59
191 1
Implementation Notes

   This section is a collection of implementation notes for thestack reconciler.
   It is very technical and assumes a strong understanding of React public API as well as how it's divided into core, renderers, and the reconciler. If you're not very familiar with the React codebase, read the codebase overview first.
   The stack reconciler is powering all the React production code today. It is located in  src/renderers/shared/stack/reconciler  and is used by both React DOM and React Native.
  Video: Building React from Scratch

   Paul O'Shannessy gave a talk about building React from scratch that largely inspired this document.
  Both this document and his talk are simplifications of the real codebase so you might get a better understanding by getting familiar with both of them.
  Overview

   The reconciler itself doesn't have a public API.Renderers like React DOM and React Native use it to efficiently update the user interface according to the React components written by the user.
  Mounting as a Recursive Process

  Let's consider the first time you mount a component:
  1. ReactDOM.render(<App />, rootEl);
复制代码
  React DOM will pass <App /> along to the reconciler. Remember that <App /> is a React element, that is, a description of what to render. You can think about it as a plain object:
  1. console.log(<App />);
  2. // { type: App, props: {} }
复制代码
  The reconciler will check if App is a class or a function.
   If App is a function, the reconciler will call App(props) to get the rendered element.
   If App is a class, the reconciler will instantiate an App with new App(props) , call the componentWillMount() lifecycle method, and then will call the render() method to get the rendered element.
   Either way, the reconciler will learn the element App "rendered to".
   This process is recursive. App may render to a <Greeting /> , Greeting may render to a <Button /> , and so on. The reconciler will "drill down" through user-defined components recursively as it learns what each component renders to.
  You can imagine this process as a pseudocode:
  1. function isClass(type) {
  2.   // React.Component subclasses have this flag
  3.   return (
  4.     Boolean(type.prototype) &&
  5.     Boolean(type.prototype.isReactComponent)
  6.   );
  7. }
  8. // This function takes a React element (e.g. <App />)
  9. // and returns a DOM or Native node representing the mounted tree.
  10. function mount(element) {
  11.   var type = element.type;
  12.   var props = element.props;
  13.   // We will determine the rendered element
  14.   // by either running the type as function
  15.   // or creating an instance and calling render().
  16.   var renderedElement;
  17.   if (isClass(type)) {
  18.     // Component class
  19.     var publicInstance = new type(props);
  20.     // Set the props
  21.     publicInstance.props = props;
  22.     // Call the lifecycle if necessary
  23.     if (publicInstance.componentWillMount) {
  24.       publicInstance.componentWillMount();
  25.     }
  26.     // Get the rendered element by calling render()
  27.     renderedElement = publicInstance.render();
  28.   } else {
  29.     // Component function
  30.     renderedElement = type(props);
  31.   }
  32.   // This process is recursive because a component may
  33.   // return an element with a type of another component.
  34.   return mount(renderedElement);
  35.   // Note: this implementation is incomplete and recurses infinitely!
  36.   // It only handles elements like <App /> or <Button />.
  37.   // It doesn't handle elements like <div /> or <p /> yet.
  38. }
  39. var rootEl = document.getElementById('root');
  40. var node = mount(<App />);
  41. rootEl.appendChild(node);
复制代码
    Note:

    This really is a pseudo-code. It isn't similar to the real implementation. It will also cause a stack overflow because we haven't discussed when to stop the recursion.
    Let's recap a few key ideas in the example above:
  
       
  • React elements are plain objects representing the component type (e.g. App ) and the props.   
  • User-defined components (e.g. App ) can be classes or functions but they all "render to" elements.   
  • "Mounting" is a recursive process that creates a DOM or Native tree given the top-level React element (e.g. <App /> ).  
  Mounting Host Elements

  This process would be useless if we didn't render something to the screen as a result.
   In addition to user-defined ("composite") components, React elements may also represent platform-specific ("host") components. For example, Button might return a <div /> from its render method.
   If element's type property is a string, we are dealing with a host element:
  1. console.log(<div />);
  2. // { type: 'div', props: {} }
复制代码
There is no user-defined code associated with host elements.
  When the reconciler encounters a host element, it lets the renderer take care of mounting it. For example, React DOM would create a DOM node.
   If the host element has children, the reconciler recursively mounts them following the same algorithm as above. It doesn't matter whether children are host (like <div><hr /></div> ), composite (like <div><Button /></div> ), or both.
  The DOM nodes produced by the child components will be appended to the parent DOM node, and recursively, the complete DOM structure will be assembled.
     Note:

   The reconciler itself is not tied to the DOM. The exact result of mounting (sometimes called "mount image" in the source code) depends on the renderer, and can be a DOM node (React DOM), a string (React DOM Server), or a number representing a native view (React Native).
    If we were to extend the code to handle host elements, it would look like this:
  1. function isClass(type) {
  2.   // React.Component subclasses have this flag
  3.   return (
  4.     Boolean(type.prototype) &&
  5.     Boolean(type.prototype.isReactComponent)
  6.   );
  7. }
  8. // This function only handles elements with a composite type.
  9. // For example, it handles <App /> and <Button />, but not a <div />.
  10. function mountComposite(element) {
  11.   var type = element.type;
  12.   var props = element.props;
  13.   var renderedElement;
  14.   if (isClass(type)) {
  15.     // Component class
  16.     var publicInstance = new type(props);
  17.     // Set the props
  18.     publicInstance.props = props;
  19.     // Call the lifecycle if necessary
  20.     if (publicInstance.componentWillMount) {
  21.       publicInstance.componentWillMount();
  22.     }
  23.     renderedElement = publicInstance.render();
  24.   } else if (typeof type === 'function') {
  25.     // Component function
  26.     renderedElement = type(props);
  27.   }
  28.   // This is recursive but we'll eventually reach the bottom of recursion when
  29.   // the element is host (e.g. <div />) rather than composite (e.g. <App />):
  30.   return mount(renderedElement);
  31. }
  32. // This function only handles elements with a host type.
  33. // For example, it handles <div /> and <p /> but not an <App />.
  34. function mountHost(element) {
  35.   var type = element.type;
  36.   var props = element.props;
  37.   var children = props.children || [];
  38.   if (!Array.isArray(children)) {
  39.     children = [children];
  40.   }
  41.   children = children.filter(Boolean);
  42.   // This block of code shouldn't be in the reconciler.
  43.   // Different renderers might initialize nodes differently.
  44.   // For example, React Native would create iOS or Android views.
  45.   var node = document.createElement(type);
  46.   Object.keys(props).forEach(propName => {
  47.     if (propName !== 'children') {
  48.       node.setAttribute(propName, props[propName]);
  49.     }
  50.   });
  51.   // Mount the children
  52.   children.forEach(childElement => {
  53.     // Children may be host (e.g. <div />) or composite (e.g. <Button />).
  54.     // We will also mount them recursively:
  55.     var childNode = mount(childElement);
  56.     // This line of code is also renderer-specific.
  57.     // It would be different depending on the renderer:
  58.     node.appendChild(childNode);
  59.   });
  60.   // Return the DOM node as mount result.
  61.   // This is where the recursion ends.
  62.   return node;
  63. }
  64. function mount(element) {
  65.   var type = element.type;
  66.   if (typeof type === 'function') {
  67.     // User-defined components
  68.     return mountComposite(element);
  69.   } else if (typeof type === 'string') {
  70.     // Platform-specific components
  71.     return mountHost(element);
  72.   }
  73. }
  74. var rootEl = document.getElementById('root');
  75. var node = mount(<App />);
  76. rootEl.appendChild(node);
复制代码
This is working but still far from how the reconciler is really implemented. The key missing ingredient is support for updates.
  Introducing Internal Instances

  The key feature of React is that you can re-render everything, and it won't recreate the DOM or reset the state:
  1. ReactDOM.render(<App />, rootEl);
  2. // Should reuse the existing DOM:
  3. ReactDOM.render(<App />, rootEl);
复制代码
  However, our implementation above only knows how to mount the initial tree. It can't perform updates on it because it doesn't store all the necessary information, such as all the publicInstance s, or which DOM node s correspond to which components.
   The stack reconciler codebase solves this by making the mount() function a method and putting it on a class. There are drawbacks to this approach, and we are going in the opposite direction in the ongoing rewrite of the reconciler . Nevertheless this is how it works now.
   Instead of separate mountHost and mountComposite functions, we will create two classes: DOMComponent and CompositeComponent .
   Both classes have a constructor accepting the element , as well as a mount() method returning the mounted node. We will replace a top-level mount() function with a factory that instantiates the correct class:
  1. function instantiateComponent(element) {
  2.   var type = element.type;
  3.   if (typeof type === 'function') {
  4.     // User-defined components
  5.     return new CompositeComponent(element);
  6.   } else if (typeof type === 'string') {
  7.     // Platform-specific components
  8.     return new DOMComponent(element);
  9.   }  
  10. }
复制代码
  First, let's consider the implementation of CompositeComponent :
  1. class CompositeComponent {
  2.   constructor(element) {
  3.     this.currentElement = element;
  4.     this.renderedComponent = null;
  5.     this.publicInstance = null;
  6.   }
  7.   getPublicInstance() {
  8.     // For composite components, expose the class instance.
  9.     return this.publicInstance;
  10.   }
  11.   mount() {
  12.     var element = this.currentElement;
  13.     var type = element.type;
  14.     var props = element.props;
  15.     var publicInstance;
  16.     var renderedElement;
  17.     if (isClass(type)) {
  18.       // Component class
  19.       publicInstance = new type(props);
  20.       // Set the props
  21.       publicInstance.props = props;
  22.       // Call the lifecycle if necessary
  23.       if (publicInstance.componentWillMount) {
  24.         publicInstance.componentWillMount();
  25.       }
  26.       renderedElement = publicInstance.render();
  27.     } else if (typeof type === 'function') {
  28.       // Component function
  29.       publicInstance = null;
  30.       renderedElement = type(props);
  31.     }
  32.     // Save the public instance
  33.     this.publicInstance = publicInstance;
  34.     // Instantiate the child internal instance according to the element.
  35.     // It would be a DOMComponent for <div /> or <p />,
  36.     // and a CompositeComponent for <App /> or <Button />:
  37.     var renderedComponent = instantiateComponent(renderedElement);
  38.     this.renderedComponent = renderedComponent;
  39.     // Mount the rendered output
  40.     return renderedComponent.mount();
  41.   }
  42. }
复制代码
  This is not much different from our previous mountComposite() implementation, but now we can save some information, such as this.currentElement , this.renderedComponent , and this.publicInstance , for use during updates.
   Note that an instance of CompositeComponent is not the same thing as an instance of the user-supplied element.type . CompositeComponent is an implementation detail of our reconciler, and is never exposed to the user. The user-defined class is the one we read from element.type , and CompositeComponent creates an instance of it.
   To avoid the confusion, we will call instances of CompositeComponent and DOMComponent "internal instances". They exist so we can associate some long-lived data with them. Only the renderer and the reconciler are aware that they exist.
   In contrast, we call an instance of the user-defined class a "public instance". The public instance is what you see as this in the render() and other methods of your custom components.
   The mountHost() function, refactored to be a mount() method on DOMComponent class, also looks familiar:
  1. class DOMComponent {
  2.   constructor(element) {
  3.     this.currentElement = element;
  4.     this.renderedChildren = [];
  5.     this.node = null;
  6.   }
  7.   getPublicInstance() {
  8.     // For DOM components, only expose the DOM node.
  9.     return this.node;
  10.   }
  11.   mount() {
  12.     var element = this.currentElement;
  13.     var type = element.type;
  14.     var props = element.props;
  15.     var children = props.children || [];
  16.     if (!Array.isArray(children)) {
  17.       children = [children];
  18.     }
  19.     // Create and save the node
  20.     var node = document.createElement(type);
  21.     this.node = node;
  22.     // Set the attributes
  23.     Object.keys(props).forEach(propName => {
  24.       if (propName !== 'children') {
  25.         node.setAttribute(propName, props[propName]);
  26.       }
  27.     });
  28.     // Create and save the contained children.
  29.     // Each of them can be a DOMComponent or a CompositeComponent,
  30.     // depending on whether the element type is a string or a function.
  31.     var renderedChildren = children.map(instantiateComponent);
  32.     this.renderedChildren = renderedChildren;
  33.     // Collect DOM nodes they return on mount
  34.     var childNodes = renderedChildren.map(child => child.mount());
  35.     childNodes.forEach(childNode => node.appendChild(childNode));
  36.     // Return the DOM node as mount result
  37.     return node;
  38.   }
  39. }
复制代码
  The main difference after refactoring from mountHost() is that we now keep this.node and this.renderedChildren associated with the internal DOM component instance. We will also use them for applying non-destructive updates in the future.
   As a result, each internal instance, composite or host, now points to its child internal instances. To help visualize this, if a functional <App> component renders a <Button> class component, and Button class renders a <div> , the internal instance tree would look like this:
  1. [object CompositeComponent] {
  2.   currentElement: <App />,
  3.   publicInstance: null,
  4.   renderedComponent: [object CompositeComponent] {
  5.     currentElement: <Button />,
  6.     publicInstance: [object Button],
  7.     renderedComponent: [object DOMComponent] {
  8.       currentElement: <div />,
  9.       node: [object HTMLDivElement],
  10.       renderedChildren: []
  11.     }
  12.   }
  13. }
复制代码
  In the DOM you would only see the <div> . However the internal instance tree contains both composite and host internal instances.
  The composite internal instances need to store:
  
       
  • The current element.   
  • The public instance if element type is a class.   
  • The single rendered internal instance. It can be either a DOMComponent or a CompositeComponent .  
  The host internal instances need to store:
  
       
  • The current element.   
  • The DOM node.   
  • All the child internal instances. Each of them can be either a DOMComponent or a CompositeComponent .  
   If you're struggling to imagine what how an internal instance tree is structured in more complex applications, React DevTools can give you a close approximation, as it highlights host instances with grey, and composite instances with purple:
   

React Implementation Notes

React Implementation Notes-1-技术控-collection,technical,overview,building,document

   To complete this refactoring, we will introduce a function that mounts a complete tree into a container node, just like ReactDOM.render() . It returns a public instance, also like ReactDOM.render() :
  1. console.log(<App />);
  2. // { type: App, props: {} }0
复制代码
Unmounting

  Now that we have internal instances that hold onto their children and the DOM nodes, we can implement unmounting. For a composite component, unmounting calls a lifecycle hook and recurses.
  1. console.log(<App />);
  2. // { type: App, props: {} }1
复制代码
  For DOMComponent , unmounting tells each child to unmount:
  1. console.log(<App />);
  2. // { type: App, props: {} }2
复制代码
In practice, unmounting DOM components also removes the event listeners and clears some caches, but we will skip those details.
   We can now add a new top-level function called unmountTree(containerNode) that is similar to ReactDOM.unmountComponentAtNode() :
  1. console.log(<App />);
  2. // { type: App, props: {} }3
复制代码
  In order for this to work, we need to read an internal root instance from a DOM node. We will modify mountTree() to add the _internalInstance property to the root DOM node. We will also teach mountTree() to destroy any existing tree so it can be called multiple times:
  1. console.log(<App />);
  2. // { type: App, props: {} }4
复制代码
  Now, running unmountTree() , or running mountTree() repeatedly, removes the old tree and runs the componentWillUnmount() lifecycle hook on components.
  Updating

  In the previous section, we implemented unmounting. However React wouldn't be very useful if each prop change unmounted and mounted the whole tree. The goal of the reconciler is to reuse existing instances where possible to preserve the DOM and the state:
  1. console.log(<App />);
  2. // { type: App, props: {} }5
复制代码
  We will extend our internal instance contract with one more method. In addition to mount() and unmount() , both DOMComponent and CompositeComponent will implement a new method called receive(nextElement) :
  1. console.log(<App />);
  2. // { type: App, props: {} }6
复制代码
  Its job is to do whatever is necessary to bring the component (and any of its children) up to date with the description provided by the nextElement .
  This is the part that is often described as "virtual DOM diffing" although what really happens is that we walk the internal tree recursively and let each internal instance receive an update.
  Updating Composite Components

   When a composite component receives a new element, we run the componentWillUpdate() lifecycle hook.
  Then we re-render the component with the new props, and get the next rendered element:
  1. console.log(<App />);
  2. // { type: App, props: {} }7
复制代码
  Next, we can look at the rendered element's type . If the type has not changed since the last render, the component below can also be updated in place.
   For example, if it returned <Button color="red" /> the first time, and <Button color="blue" /> the second time, we can just tell the corresponding internal instance to receive() the next element:
  1. console.log(<App />);
  2. // { type: App, props: {} }8
复制代码
  However, if the next rendered element has a different type than the previously rendered element, we can't update the internal instance. A <button> can't "become" an <input> .
   Instead, we have to unmount the existing internal instance and mount the new one corresponding to the rendered element type. For example, this is what happens when a component that previously rendered a <button /> renders an <input /> :
  1. console.log(<App />);
  2. // { type: App, props: {} }9
复制代码
To sum this up, when a composite component receives a new element, it may either delegate the update to its rendered internal instance, or unmount it and mount a new one in its place.
   There is another condition under which a component will re-mount rather than receive an element, and that is when the element's key has changed. We don't discuss key handling in this document because it adds more complexity to an already complex tutorial.
   Note that we needed to add a method called getHostNode() to the internal instance contract so that it's possible to locate the platform-specific node and replace it during the update. Its implementation is straightforward for both classes:
  1. function isClass(type) {
  2.   // React.Component subclasses have this flag
  3.   return (
  4.     Boolean(type.prototype) &&
  5.     Boolean(type.prototype.isReactComponent)
  6.   );
  7. }
  8. // This function takes a React element (e.g. <App />)
  9. // and returns a DOM or Native node representing the mounted tree.
  10. function mount(element) {
  11.   var type = element.type;
  12.   var props = element.props;
  13.   // We will determine the rendered element
  14.   // by either running the type as function
  15.   // or creating an instance and calling render().
  16.   var renderedElement;
  17.   if (isClass(type)) {
  18.     // Component class
  19.     var publicInstance = new type(props);
  20.     // Set the props
  21.     publicInstance.props = props;
  22.     // Call the lifecycle if necessary
  23.     if (publicInstance.componentWillMount) {
  24.       publicInstance.componentWillMount();
  25.     }
  26.     // Get the rendered element by calling render()
  27.     renderedElement = publicInstance.render();
  28.   } else {
  29.     // Component function
  30.     renderedElement = type(props);
  31.   }
  32.   // This process is recursive because a component may
  33.   // return an element with a type of another component.
  34.   return mount(renderedElement);
  35.   // Note: this implementation is incomplete and recurses infinitely!
  36.   // It only handles elements like <App /> or <Button />.
  37.   // It doesn't handle elements like <div /> or <p /> yet.
  38. }
  39. var rootEl = document.getElementById('root');
  40. var node = mount(<App />);
  41. rootEl.appendChild(node);0
复制代码
Updating Host Components

   Host component implementations, such as DOMComponent , update differently. When they receive an element, they need to update the underlying platform-specific view. In case of React DOM, this means updating the DOM attributes:
  1. function isClass(type) {
  2.   // React.Component subclasses have this flag
  3.   return (
  4.     Boolean(type.prototype) &&
  5.     Boolean(type.prototype.isReactComponent)
  6.   );
  7. }
  8. // This function takes a React element (e.g. <App />)
  9. // and returns a DOM or Native node representing the mounted tree.
  10. function mount(element) {
  11.   var type = element.type;
  12.   var props = element.props;
  13.   // We will determine the rendered element
  14.   // by either running the type as function
  15.   // or creating an instance and calling render().
  16.   var renderedElement;
  17.   if (isClass(type)) {
  18.     // Component class
  19.     var publicInstance = new type(props);
  20.     // Set the props
  21.     publicInstance.props = props;
  22.     // Call the lifecycle if necessary
  23.     if (publicInstance.componentWillMount) {
  24.       publicInstance.componentWillMount();
  25.     }
  26.     // Get the rendered element by calling render()
  27.     renderedElement = publicInstance.render();
  28.   } else {
  29.     // Component function
  30.     renderedElement = type(props);
  31.   }
  32.   // This process is recursive because a component may
  33.   // return an element with a type of another component.
  34.   return mount(renderedElement);
  35.   // Note: this implementation is incomplete and recurses infinitely!
  36.   // It only handles elements like <App /> or <Button />.
  37.   // It doesn't handle elements like <div /> or <p /> yet.
  38. }
  39. var rootEl = document.getElementById('root');
  40. var node = mount(<App />);
  41. rootEl.appendChild(node);1
复制代码
Then, host components need to update their children. Unlike composite components, they might contain more than a single child.
   In this simplified example, we use an array of internal instances and iterate over it, either updating or replacing the internal instances depending on whether the received type matches their previous type . The real reconciler also takes element's key in the account and track moves in addition to insertions and deletions, but we will omit this logic.
  We collect DOM operations on children in a list so we can execute them in batch:
  1. function isClass(type) {
  2.   // React.Component subclasses have this flag
  3.   return (
  4.     Boolean(type.prototype) &&
  5.     Boolean(type.prototype.isReactComponent)
  6.   );
  7. }
  8. // This function takes a React element (e.g. <App />)
  9. // and returns a DOM or Native node representing the mounted tree.
  10. function mount(element) {
  11.   var type = element.type;
  12.   var props = element.props;
  13.   // We will determine the rendered element
  14.   // by either running the type as function
  15.   // or creating an instance and calling render().
  16.   var renderedElement;
  17.   if (isClass(type)) {
  18.     // Component class
  19.     var publicInstance = new type(props);
  20.     // Set the props
  21.     publicInstance.props = props;
  22.     // Call the lifecycle if necessary
  23.     if (publicInstance.componentWillMount) {
  24.       publicInstance.componentWillMount();
  25.     }
  26.     // Get the rendered element by calling render()
  27.     renderedElement = publicInstance.render();
  28.   } else {
  29.     // Component function
  30.     renderedElement = type(props);
  31.   }
  32.   // This process is recursive because a component may
  33.   // return an element with a type of another component.
  34.   return mount(renderedElement);
  35.   // Note: this implementation is incomplete and recurses infinitely!
  36.   // It only handles elements like <App /> or <Button />.
  37.   // It doesn't handle elements like <div /> or <p /> yet.
  38. }
  39. var rootEl = document.getElementById('root');
  40. var node = mount(<App />);
  41. rootEl.appendChild(node);2
复制代码
As the last step, we execute the DOM operations. Again, the real reconciler code is more complex because it also handles moves:
  1. function isClass(type) {
  2.   // React.Component subclasses have this flag
  3.   return (
  4.     Boolean(type.prototype) &&
  5.     Boolean(type.prototype.isReactComponent)
  6.   );
  7. }
  8. // This function takes a React element (e.g. <App />)
  9. // and returns a DOM or Native node representing the mounted tree.
  10. function mount(element) {
  11.   var type = element.type;
  12.   var props = element.props;
  13.   // We will determine the rendered element
  14.   // by either running the type as function
  15.   // or creating an instance and calling render().
  16.   var renderedElement;
  17.   if (isClass(type)) {
  18.     // Component class
  19.     var publicInstance = new type(props);
  20.     // Set the props
  21.     publicInstance.props = props;
  22.     // Call the lifecycle if necessary
  23.     if (publicInstance.componentWillMount) {
  24.       publicInstance.componentWillMount();
  25.     }
  26.     // Get the rendered element by calling render()
  27.     renderedElement = publicInstance.render();
  28.   } else {
  29.     // Component function
  30.     renderedElement = type(props);
  31.   }
  32.   // This process is recursive because a component may
  33.   // return an element with a type of another component.
  34.   return mount(renderedElement);
  35.   // Note: this implementation is incomplete and recurses infinitely!
  36.   // It only handles elements like <App /> or <Button />.
  37.   // It doesn't handle elements like <div /> or <p /> yet.
  38. }
  39. var rootEl = document.getElementById('root');
  40. var node = mount(<App />);
  41. rootEl.appendChild(node);3
复制代码
And that is it for updating host components.
  Top-Level Updates

   Now that both CompositeComponent and DOMComponent implement the receive(nextElement) method, we can change the top-level mountTree() function to use it when the element type is the same as it was the last time:
  1. function isClass(type) {
  2.   // React.Component subclasses have this flag
  3.   return (
  4.     Boolean(type.prototype) &&
  5.     Boolean(type.prototype.isReactComponent)
  6.   );
  7. }
  8. // This function takes a React element (e.g. <App />)
  9. // and returns a DOM or Native node representing the mounted tree.
  10. function mount(element) {
  11.   var type = element.type;
  12.   var props = element.props;
  13.   // We will determine the rendered element
  14.   // by either running the type as function
  15.   // or creating an instance and calling render().
  16.   var renderedElement;
  17.   if (isClass(type)) {
  18.     // Component class
  19.     var publicInstance = new type(props);
  20.     // Set the props
  21.     publicInstance.props = props;
  22.     // Call the lifecycle if necessary
  23.     if (publicInstance.componentWillMount) {
  24.       publicInstance.componentWillMount();
  25.     }
  26.     // Get the rendered element by calling render()
  27.     renderedElement = publicInstance.render();
  28.   } else {
  29.     // Component function
  30.     renderedElement = type(props);
  31.   }
  32.   // This process is recursive because a component may
  33.   // return an element with a type of another component.
  34.   return mount(renderedElement);
  35.   // Note: this implementation is incomplete and recurses infinitely!
  36.   // It only handles elements like <App /> or <Button />.
  37.   // It doesn't handle elements like <div /> or <p /> yet.
  38. }
  39. var rootEl = document.getElementById('root');
  40. var node = mount(<App />);
  41. rootEl.appendChild(node);4
复制代码
  Now calling mountTree() two times with the same type isn't destructive:
  1. function isClass(type) {
  2.   // React.Component subclasses have this flag
  3.   return (
  4.     Boolean(type.prototype) &&
  5.     Boolean(type.prototype.isReactComponent)
  6.   );
  7. }
  8. // This function takes a React element (e.g. <App />)
  9. // and returns a DOM or Native node representing the mounted tree.
  10. function mount(element) {
  11.   var type = element.type;
  12.   var props = element.props;
  13.   // We will determine the rendered element
  14.   // by either running the type as function
  15.   // or creating an instance and calling render().
  16.   var renderedElement;
  17.   if (isClass(type)) {
  18.     // Component class
  19.     var publicInstance = new type(props);
  20.     // Set the props
  21.     publicInstance.props = props;
  22.     // Call the lifecycle if necessary
  23.     if (publicInstance.componentWillMount) {
  24.       publicInstance.componentWillMount();
  25.     }
  26.     // Get the rendered element by calling render()
  27.     renderedElement = publicInstance.render();
  28.   } else {
  29.     // Component function
  30.     renderedElement = type(props);
  31.   }
  32.   // This process is recursive because a component may
  33.   // return an element with a type of another component.
  34.   return mount(renderedElement);
  35.   // Note: this implementation is incomplete and recurses infinitely!
  36.   // It only handles elements like <App /> or <Button />.
  37.   // It doesn't handle elements like <div /> or <p /> yet.
  38. }
  39. var rootEl = document.getElementById('root');
  40. var node = mount(<App />);
  41. rootEl.appendChild(node);5
复制代码
These are the basics of how React works internally.
  What We Left Out

  This document is simplified compared to the real codebase. There are a few important aspects we didn't address:
  
       
  •   Components can render null , and the reconciler can handle "empty slots" in arrays and rendered output.
       
  •   The reconciler also reads key from the elements, and uses it to establish which internal instance corresponds to which element in an array. A bulk of complexity in the actual React implementation is related to that.
       
  •   In addition to composite and host internal instance classes, there are also classes for "text" and "empty" components. They represent text nodes and the "empty slots" you get by rendering null .
       
  •   Renderers useinjection to pass the host internal class to the reconciler. For example, React DOM tells the reconciler to use ReactDOMComponent as the host internal instance implementation.
       
  •   The logic for updating the list of children is extracted into a mixin called ReactMultiChild which is used by the host internal instance class implementations both in React DOM and React Native.
       
  •   The reconciler also implements support for setState() in composite components. Multiple updates inside event handlers get batched into a single update.
       
  • The reconciler also takes care of attaching and detaching refs to composite components and host nodes.
       
  •   Lifecycle hooks that are called after the DOM is ready, such as componentDidMount() and componentDidUpdate() , get collected into "callback queues" and are executed in a single batch.
       
  • React puts information about the current update into an internal object called "transaction". Transactions are useful for keeping track of the queue of pending lifecycle hooks, the current DOM nesting for the warnings, and anything else that is "global" to a specific update. Transactions also ensure React "cleans everything up" after updates. For example, the transaction class provided by React DOM restores the input selection after any update.
      
  Jumping into the Code

  
       
  •   ReactMount  is where the code like mountTree() and unmountTree() from this tutorial lives. It takes care of mounting and unmounting top-level components.  ReactNativeMount  is its React Native analog.   
  •   ReactDOMComponent  is the equivalent of DOMComponent in this tutorial. It implements the host component class for React DOM renderer.  ReactNativeBaseComponent  is its React Native analog.   
  •   ReactCompositeComponent  is the equivalent of CompositeComponent in this tutorial. It handles calling user-defined components and maintaining their state.   
  •    instantiateReactComponent  contains the switch that picks the right internal instance class to construct for an element. It is equivalent to instantiateComponent() in this tutorial.
       
  •    ReactReconciler  is a wrapper with mountComponent , receiveComponent() , and unmountComponent() methods. It calls the underlying implementations on the internal instances, but also includes some code around them that is shared by all internal instance implementations.
       
  •    ReactChildReconciler  implements the logic for mounting, updating, and unmounting children according to the key of their elements.
       
  •    ReactMultiChild  implements processing the operation queue for child insertions, deletions, and moves independently of the renderer.
       
  •   mount() , receive() , and unmount() are really called mountComponent() , receiveComponent() , and unmountComponent() in React codebase for legacy reasons, but they receive elements.
       
  •   Properties on the internal instances start with an underscore, e.g. _currentElement . They are considered to be read-only public fields throughout the codebase.
      
  Future Directions

   Stack reconciler has inherent limitations such as being synchronous and unable to interrupt the work or split it in chunks. There is a work in progress on thenew Fiber reconciler with a completely different architecture . In the future, we intend to replace stack reconciler with it, but at the moment it is far from feature parity.
  Next Steps

   Read thenext section to learn about the guiding principles we use for React development.
シ尊嚴的色調 发表于 2016-10-17 01:25:54
楼主说的,句句都是真理啊!
回复 支持 反对

使用道具 举报

我要投稿

回页顶回复上一篇下一篇回列表
手机版/c.CoLaBug.com ( 粤ICP备05003221号 | 文网文[2010]257号 | 粤公网安备 44010402000842号 )

© 2001-2017 Comsenz Inc.

返回顶部 返回列表