技术控

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

[其他] Building Your Own React Clone in Five Easy Steps

[复制链接]
Sud丶笨笨 发表于 2016-10-6 10:53:00
143 3

立即注册CoLaBug.com会员,免费获得投稿人的专业资料,享用更多功能,玩转个人品牌!

您需要 登录 才可以下载或查看,没有帐号?立即注册

x

Building Your Own React Clone in Five Easy Steps-1 (understand,principles,particular,happening,question)
React's ability to efficiently render declarative user interfaces can seem almost magical. Most developers probably have a rough idea of how it identifies changes and applies them using a "virtual DOM". But how many truly understand the details of what is happening behind the scenes?
   Fascinated by this question, I decided to build my own rather naive React in order to get more familiar with the details of the implementation. Naturally my implementation doesn't offer the full power of React, but it is more than adequate to illustrate some of its key principles. I should mention in particular that I decided to support only stateless components (those don't have their own instance).
  Getting to Know the Virtual DOM

  Once we decide not to worry about managing internal state, the basic idea of our React is very straightforward. During each render cycle we first create a Virtual DOM (vDOM), which is nothing more than a "bushy" tree that mirrors the structure of the real DOM. Each node has three basic attributes:
   1) Type - a string identifying the DOM element such as div or span
  2) Props - in our case an object with properties corresponding to HTML attributes and event handlers
  3) Children - an array containing further nodes
  [code]{
  type: 'ul',
  props: { className: 'ul-wrapper' },
  children: [
    {
      type: 'li',
      props: { className: 'active' },
      children: ['First Item']
    },
    {
      type: 'li',
      props: { onClick: () => console.log('Hello, I have just clicked the second item') },
      children: ['Second Item']
    }
  ]
}[/code]  It is no coincidence that the vDOM representation looks very similar to the actual DOM. React identifies the differences between the two DOMs, uses these to generate a list of patches and then applies them to the real DOM. The vDOM has to use a similar representation so that the differences can be found efficiently.
  Step One: The Virtual DOM Representation

   Since version 0.14.0, React supports stateless components in the form of pure functions . These components are used like this:
  [code]const WelcomeComponent = ({ name }) =>  
  
Welcome {name}!
;

const RootComponent = ({ user }) => {  
  if (user) {
    return ;
  } else {
    return
Please, Log in
;
  }
}[/code]  Since in this case a component is a function, we get some cool functional characteristics, such as dead easy composition, for free:
  [code]const welcome = name => `Welcome ${name}!`;

const root = user => {  
  if (user) {
    // In this context composition just means calling
    // a function inside another function
    return welcome(user.name);
  } else {
    return 'Please, Log in';
  }
}[/code]  Now imagine that our stateless component is going to return a vDOM, i.e. a tree structure containing information about how the actual DOM should look.
  [code]const WelcomeComponent = ({ name }) => ({  
  type: 'div',
  props: {},
  children: [`Welcome ${name}`]
});[/code]   This syntax is fairly verbose and error-prone, so let's make a function that returns a vDOM node. We'll call this function h since it is similar to Hyperscript .
  [code]const h = (type, props = {}, children = []) => ({  
  type,
  props,
  children
});

const WelcomeComponent = ({ name }) => h('div', {}, [`Welcome ${name}`]);

const RootComponent = ({ user }) => {  
  if (user) {
    // The vDOM node's type can also be another
    // component. React calls it automatically when creating
    // the vDOM tree
    return h(WelcomeComponent, { name: user.name });
  } else {
    return h('div', {}, [`Please, Log in`]);
  }
}[/code]   Seeing as an application that only uses one component isn't of much use, we need a way to combine multiple components. We've already mentioned simple composition, and you can see this in action in the above example. React allows the node type to be either a string or another component (i.e. function). Developers can easily create declarative UIs of arbitrary complexity using the magic h function. React itself gives us some syntactic sugar, in the form of JSX , to further ease this task, but we can do without this for our simple example.
  We've already mentioned two important points:
  
       
  • Every rendering cycles starts by creating a vDOM representation.   
  • The vDOM representation mirrors the actual DOM.  
   If we just called the RootComponent function and left it at that, we would violate the second point, since we would get an object whose type is a component and not a string:
  [code]{
  type: WelcomeComponent, // This is a function, not a string
  props: { user: { name: 'Tomas Weiss' }},
  children: []
}[/code]  However, the vDOM should correspond to the actual DOM:
  [code]{
  type: 'div',
  props: {},
  children: ['Welcome Tomas Weiss!']
}[/code]   The component tree must therefore be processed recursively, with nodes whose type is a component being called with the appropriate props until the whole structure has been transformed into a tree containing only DOM elements.
  [code]// To simplify the correlation of nodes in the vDOM and DOM,
// we generate a unique ID for each DOM node. These are just
// dot-delimited paths containing the index of each child in
// the list of children maintained by its parent.
// This allows us to use simple string manipulation to implement
// advanced features like a synthetic event system
export const createVDOM = (element, id = '.') => {  
  // This function must be called recursively for all descendants
  // in order to transform the entire tree
  const newElement = {
    ...element,
    id,
    children: element.children.map((child, index) => createVDOM(child, `${id}${index}.`))
  };

  // Is this a component?
  if (typeof element.type === 'function') {
    // Call the component and pass in the props.
    // Returns the generated subtree
    const subtree = newElement.type(element.props);

    // Call ourself recursively in order to assign the right ID
    // to the nodes and process any subcomponents in the subtree
    return createVDOM(subtree, id);
  } else {
    // If we come across an element that is not a function,
    // all we have to do is return it
    return newElement;
  }
};[/code]  Step Two: Optimizing the Virtual DOM

   Pure stateless components in React are referentially transparent by nature. They can therefore by replaced by the value they return. We can use this fact to our advantage by memoizing the component itself, as in this example:
  [code]const memoize = component => {

  // We need to record the previous props and return value of the component.
  // On the first call these are empty
  let lastProps = null;
  let lastResult = null;

  // We return a new component that uses the cached values for
  // optimization purposes
  return props => {

    // If the props have changed since the last function call,
    // we invoke the component with the new props and store the result
    if (!shallowEqual(props, lastProps)) {
      lastResult = component(props);
      lastProps = props;

      // We also record whether the result came from the cache. We will
      // use this information later when we create the vDOM
      lastResult.memoized = true;
    } else {
      lastResult.memoized = false;
    }

    // Finally we return the cached value. This is referential transparency
    // at work (i.e. we can replace the component invocation with its value)
    return lastResult;
  }
}[/code]   Now we can safely assume that all of our components are memoized and that the tree contains information about whether each value was cached or not. We can use this fact to great effect when creating the vDOM. We simply adjust the createVDOM function to stop recursing when it reaches a cached subtree, since we know in this case that the subtree is identical to the one returned by the previous call.
  [code]export const createVDOM = (element, id = '.') => {  
  const newElement = {
    ...element,
    id,
    children: element.children.map((child, index) => createVDOM(child, `${id}${index}.`))
  };

  if (typeof element.type === 'function') {
    const subtree = newElement.type(element.props);

    // If we hit a cached subtree, we don't need to keep recursing
    if (subtree.memoized) {
      return subtree;
    } else {
      return createVDOM(subtree, id);
    }
  } else {
    return newElement;
  }
};[/code]  Step Three: Identifying DOM Changes

  We already know how to use components to create a vDOM. Now let's create two different vDOM trees to demonstrate the tree diffing process:
  [code]const WelcomeComponent = ({ name }) =>  
  
Welcome {name}!
;

const RootComponent = ({ user }) => {  
  if (user) {
    return ;
  } else {
    return
Please, Log in
;
  }
}0[/code]   Finding all the patches needed to transform the left tree into the right tree is a non-trivial problem with complexity O(n 3 ). Luckily React uses some basic assumptions to greatly simplify this process. The fundamental assumption is that different components will always generate different DOM subtrees. As a result, we can short-circuit the tree comparison if we encounter a subtree whose root component has a different type from the corresponding node in the other tree. In this case we recreate the entire subtree from scratch, rather than performing the computationally intensive task of finding the exact differences.
  In our sample implementation we will only deal with adding, removing and replacing nodes. In the real React, of course, we would also have to take into account prop changes.
  [code]const WelcomeComponent = ({ name }) =>  
  
Welcome {name}!
;

const RootComponent = ({ user }) => {  
  if (user) {
    return ;
  } else {
    return
Please, Log in
;
  }
}1[/code]  Step Four: Applying Changes to the DOM

   We already know how to create a vDOM and diff two vDOM trees. There is just one more piece to the puzzle: applying the changes to the actual DOM. We have prepared the ground since we have identified each vDOM node with a unique ID. We need to implement a function that applies patches in the DOM (let's call it applyPatch ). (This code would be part of the react-dom package rather than react itself, since it is DOM-specific.) This is the only code we would need to change if we wanted to do server-side rendering or paint, for example, on an OpenGL context.
  [code]const WelcomeComponent = ({ name }) =>  
  
Welcome {name}!
;

const RootComponent = ({ user }) => {  
  if (user) {
    return ;
  } else {
    return
Please, Log in
;
  }
}2[/code]  Step Five: Render Function

  Now we have everything we need to create the function that displays the DOM, applying any incremental changes that have occurred in the vDOM in the meantime. The idea is simple: the function remembers the previous DOM state and finds any changes with the newly create vDOM. These changes are then applied to the actual DOM.
  [code]const WelcomeComponent = ({ name }) =>  
  
Welcome {name}!
;

const RootComponent = ({ user }) => {  
  if (user) {
    return ;
  } else {
    return
Please, Log in
;
  }
}3[/code]  As you can see, React is based on simple assumptions and functional programming principles. A naive implementation is not that complicated, although React naturally provides a lot more than just rendering.
   If the idea of a simplified React is of interest to you, we strongly recommend that you follow the virtual-dom project, where these concepts are implemented in production quality. In a future article we will illustrate how React implements intelligent event delegation and look at the real-world performance implications.
   This article would not have been possible without the excellent article by Christopher Chedeau @vjeux which inspired me to delve into the details of React and write my own simplified version.
友荐云推荐




上一篇:C++ IntelliSense Improvements – Predictive IntelliSense & Filtering
下一篇:Elegant Form Validation Using React
酷辣虫提示酷辣虫禁止发表任何与中华人民共和国法律有抵触的内容!所有内容由用户发布,并不代表酷辣虫的观点,酷辣虫无法对用户发布内容真实性提供任何的保证,请自行验证并承担风险与后果。如您有版权、违规等问题,请通过"联系我们"或"违规举报"告知我们处理。

ghg8254461 发表于 2016-10-25 22:36:01
谁能明白谁的深爱,谁又能理解谁的离开...
回复 支持 反对

使用道具 举报

韩林野 发表于 2016-11-7 12:24:49
我顶你,你懂的!
回复 支持 反对

使用道具 举报

yinghaizhilin 发表于 2016-11-14 21:24:35
纯粹路过,没任何兴趣,仅仅是看在老用户份上回复一下
回复 支持 反对

使用道具 举报

*滑动验证:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

我要投稿

推荐阅读

扫码访问 @iTTTTT瑞翔 的微博
回页顶回复上一篇下一篇回列表手机版
手机版/CoLaBug.com ( 粤ICP备05003221号 | 文网文[2010]257号 )|网站地图 酷辣虫

© 2001-2016 Comsenz Inc. Design: Dean. DiscuzFans.

返回顶部 返回列表