精华内容
下载资源
问答
  • React源码分析

    2018-04-02 03:43:36
    组件是什么样的? 下面是一个简单的组件 ...这是因为我们在创建组件的时候,使用了extends.React.Component,即继承了Component,看下Component的源码 function Component(props, context, updater) { t...

    组件是什么样的?

    下面是一个简单的组件 打印一下这个组件,发现其实是一个js对象,而不是真实的dom。 那么react是如何将react组件生成js对象的呢? 这是因为我们在创建组件的时候,使用了extends.React.Component,即继承了Component,看下Component的源码

    function Component(props, context, updater) {
      this.props = props;
      this.context = context;
      this.refs = emptyObject;
      // renderer.
      this.updater = updater || ReactNoopUpdateQueue;
    }
    Component.prototype.setState = function (partialState, callback) {}
    Component.prototype.forceUpdate = function (callback) {}
    复制代码

    我们声明了一个组件,继承了Component,它的原型上有setState等方法。 小结

    组件初始化

    我们在react类和我们平时写的js类都一样,唯一的区别在与react类多了一个render()方法,输出类似“这是A组件”的结构并挂载到真实DOM上,才能触发组件的生命周期并成为DOM树的一部分。 首先我们看下render()方法

    发现render()实际是是调用一个React.creatEment('div',null),实际是实际是ReactElement方法 这个方法的作用是:每一个组件的对象都是通过React.creatEment()创建出来的ReactElement类型的对象,ReactElement对象记录了组件的特征,比如key,type,ref,props等,这些特征与渲染成页面息息相关。 下图是ReactElement方法源码:

    小结

    组件的挂载

    我们知道react是通过ReactDOM.render(component,monutNode)的形式进行挂载的(可以挂载自定义组件,原生dom,字符串) 挂载过程是怎么样的呢?

    ReactDOM.render实际调用了内部的ReactMount.render,进而执行ReactMount._renderSubtreeIntoContainer。从字面意思上就可以看出是将"子DOM"插入容器的逻辑,我们看下源码实现:

    render: function (element, container, callback) {
        return renderSubtreeIntoContainer(null, element, container, false, callback);
      },
      function renderSubtreeIntoContainer(parentComponent, children, container, forceHydrate, callback){}
    复制代码

    我们看下renderSubtreeIntoContainer的入参:

    parentComponent:当前组件的父组件,第一次渲染时为null children:要插入DOM中的组件,如helloWorld container:要插入的容器,如document.getElementById('root')

    小结

    组件的生命周期

    我们知道,根据ReactDOM.render()的入参类型不同,在内部通过工厂方法生成四种不同类型的封装组件:

    ReactEmptyComponent(空对象)

    ReactTextComponent(字符串)

    ReactDOMComponent(原生dom)

    ReactCompositeComponent(自定义组件,这个类型才会有生命周期)

    在执行挂载流程时,通过执行每种封装组件内部的mountComponent方法触发生命周期,但显然生命周期只在React自定义组件中存在,也就是ReactCompositeComponent。

    小结

    转载于:https://juejin.im/post/5abe05ea5188255c61631d6c

    展开全文
  • React源码浅析

    2020-08-08 13:26:53
    class HelloMessage extends React.Component { render() { return ( <div> Hello {this.props.name} </div> ); } } ReactDOM.render( <HelloMessage name="Taylor" />, document....

    0、虚拟dom与diff算法

    什么是虚拟dom

    用JavaScript对象表示DOM信息和结构,当状态变更的时候,重新渲染这个JavaScript的对象结构,这个JavaScript对象称为virtual dom;

    为什么使用虚拟dom

    DOM操作很慢,轻微的操作都可能导致页面重新排版,非常耗性能.相对于DOM对象,js对象处理起来更快,而且更贱哒.通过diff算法对比新旧vdom之间的差异,可以批量的、最小化的执行dom操作,从而提高性能.

    虚拟dom如何工作

    react中用jsx语法描述试图,通过babel-loader转译后他们变成React.c re a te E le ment()形式,该函数将生成vdom来描述真是dom.将来如果有状态变化,vdom将作出相应的变化,再通过diff算法对比新旧vdom区别,从而作出最终dom操作

    diff算法策略
    1、同级比较,web ui中dom节点跨层级的移动操作特别少,可以忽略不计
    0
    2、拥有相同类的两个组件将会生成相似的树形结构,拥有不同类的两个组件将会生成不同的属性结构 例如div->p, CompA->CompB
    3、对于同一层级的一组子节点,通过唯一的key进行区分

    当节点处于同一层级时,Diff提供三种DOM操作:删除、移动、插入。
    01
    如图2所示,首先将OldVnode 和 NewVnode的首尾位置分别标记为oldS、oldE、newS、newE。

    (1) oldS和newS相同,不发生变化,oldS++,newS++。

    oldS = a,oldE = d
    newS = a, newE = c

    (2) newS与OldVnode不匹配,oldS前面插入f,newS++。

    oldS = b,oldE = d
    newS = f, newE = c

    (3) newS与oldE相同,oldE移动到oldS前面,newS++,oldE–。

    oldS = b,oldE = d
    newS = d, newE = c

    (4) newE与oldE相同,不发生变化,newE–,oldE–。

    oldS = b,oldE = c
    newS = e, newE = c

    (5) 都不相同,oldS前插入newE,删除oldS,oldS++,newS++,newE–,oldE–。

    oldS = b,oldE = b
    newS = e, newE = e

    (6) oldS > oldE,Diff结束,最后结果为:a、f、d、e、c。
    参考

    1、JSX与JS对比

    代码来源
    JSX写法

    class HelloMessage extends React.Component {
      render() {
        return (
          <div>
            Hello {this.props.name}
          </div>
        );
      }
    }
    
    ReactDOM.render(
      <HelloMessage name="Taylor" />,
      document.getElementById('hello-example')
    );
    

    JS写法

    class HelloMessage extends React.Component {
      render() {
        return React.createElement(
          "div",
          null,
          "Hello ",
          this.props.name
        );
      }
    }
    
    ReactDOM.render(React.createElement(HelloMessage, { name: "Taylor" }), document.getElementById('hello-example'));
    

    其中对比我们发现Reac转换t中比较重要的两个方法是React.createElement、ReactDOM.render,下面我就就分别对两个方法进行分析.

    2、React.createElement

    创建kreact.js代码如下,代码中打印数据如图1

    // kreact.js
    function createElement(type, props, ...children) {
        props.children = children;
        delete props._source;
        delete props._self;
        // type: 标签类型如div
        // vtype: 组件的类型
        let vtype;
        if (typeof type === "string") {
          vtype = 1;
        } else if (typeof type === "functio") {
          if (type.isClassComponent) {
            // 类组件
            vtype = 2;
          } else {
            // 函数组件
            vtype = 3;
          }
        } 
        return createVNode(vtype, type, props)
    }
    export default {createElement}
    export class Component {
      // 区分组件是function还是class
      static isClassComponent = true;
      constructor(props) {
        this.props = props;
        this.state = {};
      }
      setState() {
    
      }
    }
    

    图1

    3、ReactDOM.render

    我们把其中虚拟dom转dom的代码单独提取出来,那这里面的源码稍微简单

    // kreact-domjs
    function render(vnode, container) {
      const node = initVNode(vnode);
      container.appendChild(node);
    }
    export default {render};
    
    

    虚拟dom单独提取成initVNode

    
    // kvdom.js
    // vtype元素类型: 1-html元素  2-function组件  3-class组件
    export function createVNode(vtype, type, props) {
      const vnode = {vtype, type, props}
      return vnode
    }
    
    // 转换虚拟dom为dom
    export function initVNode(vnode) {
      const {vtype} = vnode;
      if(!vtype) {
        // 文本节点
        return document.createTextNode(vnode);
      }
      if (vtype === 1) {
        // 原生元素
        return createElement(vnode)
      } else if (vtype === 2) {
        // class元素
        return createClassComp(vnode)
      } else if (vtype === 3) {
        // function元素
        return createFuncComp(vnode)
      }
    }
    
    
    function createElement() {
      // 根据type创建元素
      const { type, props } = vnode;
      const node = document.createElement(type);
    
      // 处理属性
      const { key, chidren, ...rest } = props;
      Object.keys(rest).forEach((k) => {
        // 处理特别属性名: className
        if (k === "className") {
          node.setAttribute("class", rest[k]);
        } else if (k === "style" && typeof rest[k] === "object") {
          const style = Object.keys(rest[k])
            .map((s) => s + ":" + rest[k][s])
            .join(";");
          node.setAttribute("style", style);
        } else if (k.startsWith("on")) {
          // conClick
          const event = k.toLowerCase();
          node[event] = rest[k];
        } else {
          node.setAttribute(k, rest[k]);
        }
      });
      // 递归自元素
      chidren.forEach((c) => {
        if (Array.isArray(c)) {
          c.forEach((n) => node.appendChild(initVNode(n)));
        } else {
          node.appendChild(initVNode(c));
        }
        node.appendChild(initVNode(v));
      });
      return node;
    }
    // class组件转换
    function createClassComp() {
      // type是class组件声明
      const {type, props} = vnode;
      const component = new type(props);
      const vdom = component.render();
      return initVNode(vdom)
    }
    function createFuncComp() {
      // type是函数
      const {type, props} = vnode;
      const vdom = type(props);
      return initVNode(vdom);
    }
    

    那么到这里一个简易版的react解析就完成了,后面有机会再补充下…

    展开全文
  • React源码理解

    2018-03-21 14:19:00
    ,在相应的js中会有一个React.createElement的方法,接受component、props... children的参数,还有ref、key等属性,使用js构建出一个虚拟dom。 虚拟dom:在js中构造出dom,用js对dom进行增删改...

    渲染阶段   

    React中的jsx文件,babel会将jsx转化成js。

    假设在React中render一个<div>,在相应的js中会有一个React.createElement的方法,接受component、props... children的参数,还有ref、key等属性,使用js构建出一个虚拟dom。

    虚拟dom:在js中构造出dom,用js对dom进行增删改,最后将修改的部分应用到真实dom中。

    构造出虚拟的dom后,调用instantiateReactComponent的方法,这个方法会根据传入的vdom的类型,3种类型,1.string\value类型 2.原生的类型例如div 3.自定义类型

    构建出相应的dom后,调用mountComponent方法,最终将产生的添加到相应的节点中。

    createInstanceForText();
    ReactCompositeComponentWrapper是一个工厂类

    觉得写的比较清晰的一篇文章: https://www.jianshu.com/p/2a6fe61d918c

     

    setState之后阶段

    1.通过ReactInstanceMap方法获得相应渲染对象。

    2.对渲染对象的_pendingStateQueue操作,将需要更新的状态push进去。

    3.检查是否处于一个批量更新的状态,若处于批量更新状态,则将该渲染对象放入dirtyComponent中。若不处于,则调用ReactDefaultBatchingStrategyTransaction开启一个新的事物,进行vdom的比较。

    4.internalInstance.updateComponent(partialState);

    可参考链接:https://github.com/sixwinds/blog/issues/10

     

    转载于:https://www.cnblogs.com/bounceFront/p/8616755.html

    展开全文
  • ReactElement算是React源码中比较简单的部分了,直接看源码: var ReactElement = function(type, key, ref, self, source, owner, props) { var element = { // This tag allow us to uniquely identify ...

    ReactElement算是React源码中比较简单的部分了,直接看源码:

    var ReactElement = function(type, key, ref, self, source, owner, props) {
      var element = {
        // This tag allow us to uniquely identify this as a React Element
        $$typeof: REACT_ELEMENT_TYPE,
    
        // Built-in properties that belong on the element
        type: type,
        key: key,
        ref: ref,
        props: props,
    
        // Record the component responsible for creating this element.
        _owner: owner,
      };
    
      if (__DEV__) {
        // The validation flag is currently mutative. We put it on
        // an external backing store so that we can freeze the whole object.
        // This can be replaced with a WeakMap once they are implemented in
        // commonly used development environments.
        element._store = {};
    
        // To make comparing ReactElements easier for testing purposes, we make
        // the validation flag non-enumerable (where possible, which should
        // include every environment we run tests in), so the test framework
        // ignores it.
        if (canDefineProperty) {
          Object.defineProperty(element._store, 'validated', {
            configurable: false,
            enumerable: false,
            writable: true,
            value: false,
          });
          // self and source are DEV only properties.
          Object.defineProperty(element, '_self', {
            configurable: false,
            enumerable: false,
            writable: false,
            value: self,
          });
          // Two elements created in two different places should be considered
          // equal for testing purposes and therefore we hide it from enumeration.
          Object.defineProperty(element, '_source', {
            configurable: false,
            enumerable: false,
            writable: false,
            value: source,
          });
        } else {
          element._store.validated = false;
          element._self = self;
          element._source = source;
        }
        if (Object.freeze) {
          Object.freeze(element.props);
          Object.freeze(element);
        }
      }
    
      return element;
    };

    可以看出ReactElement就是一个函数,传入一系列参数作为一个element对象的属性,然后再把这个对象return出来,但是注意到有一个属性是$$typeof: REACT_ELEMENT_TYPE,然后我查找了一下这个REACT_ELEMENT_TYPE的定义:

    // The Symbol used to tag the ReactElement type. If there is no native Symbol
    // nor polyfill, then a plain number is used for performance.
    var REACT_ELEMENT_TYPE =
      (typeof Symbol === 'function' && Symbol.for && Symbol.for('react.element')) ||
      0xeac7;

    根据注释可以看出这个属性是用来标记当前对象是一个ReactElement,如果不是原生Symbol类型或者填充,则使用普通数字来代替,这也区分了element并非普通对象,而是特殊的ReactElement。

    接下来我们再来看一下ReactElement.createElement这个方法,还是直接来看源码:

    ReactElement.createElement = function(type, config, children) {
      var propName;
    
      // Reserved names are extracted
      var props = {};
    
      var key = null;
      var ref = null;
      var self = null;
      var source = null;
    
      if (config != null) {
        if (__DEV__) {
          ref = !config.hasOwnProperty('ref') ||
            Object.getOwnPropertyDescriptor(config, 'ref').get ? null : config.ref;
          key = !config.hasOwnProperty('key') ||
            Object.getOwnPropertyDescriptor(config, 'key').get ? null : '' + config.key;
        } else {
          ref = config.ref === undefined ? null : config.ref;
          key = config.key === undefined ? null : '' + config.key;
        }
        self = config.__self === undefined ? null : config.__self;
        source = config.__source === undefined ? null : config.__source;
        // Remaining properties are added to a new props object
        for (propName in config) {
          if (config.hasOwnProperty(propName) &&
              !RESERVED_PROPS.hasOwnProperty(propName)) {
            props[propName] = config[propName];
          }
        }
      }
    
      // Children can be more than one argument, and those are transferred onto
      // the newly allocated props object.
      var childrenLength = arguments.length - 2;
      if (childrenLength === 1) {
        props.children = children;
      } else if (childrenLength > 1) {
        var childArray = Array(childrenLength);
        for (var i = 0; i < childrenLength; i++) {
          childArray[i] = arguments[i + 2];
        }
        props.children = childArray;
      }
    
      // Resolve default props
      if (type && type.defaultProps) {
        var defaultProps = type.defaultProps;
        for (propName in defaultProps) {
          if (props[propName] === undefined) {
            props[propName] = defaultProps[propName];
          }
        }
      }
      if (__DEV__) {
        // Create dummy `key` and `ref` property to `props` to warn users
        // against its use
        if (typeof props.$$typeof === 'undefined' ||
            props.$$typeof !== REACT_ELEMENT_TYPE) {
          if (!props.hasOwnProperty('key')) {
            Object.defineProperty(props, 'key', {
              get: function() {
                if (!specialPropKeyWarningShown) {
                  specialPropKeyWarningShown = true;
                  warning(
                    false,
                    '%s: `key` is not a prop. Trying to access it will result ' +
                      'in `undefined` being returned. If you need to access the same ' +
                      'value within the child component, you should pass it as a different ' +
                      'prop. (https://fb.me/react-special-props)',
                    typeof type === 'function' && 'displayName' in type ? type.displayName : 'Element'
                  );
                }
                return undefined;
              },
              configurable: true,
            });
          }
          if (!props.hasOwnProperty('ref')) {
            Object.defineProperty(props, 'ref', {
              get: function() {
                if (!specialPropRefWarningShown) {
                  specialPropRefWarningShown = true;
                  warning(
                    false,
                    '%s: `ref` is not a prop. Trying to access it will result ' +
                      'in `undefined` being returned. If you need to access the same ' +
                      'value within the child component, you should pass it as a different ' +
                      'prop. (https://fb.me/react-special-props)',
                    typeof type === 'function' && 'displayName' in type ? type.displayName : 'Element'
                  );
                }
                return undefined;
              },
              configurable: true,
            });
          }
        }
      }
      return ReactElement(
        type,
        key,
        ref,
        self,
        source,
        ReactCurrentOwner.current,
        props
      );
    };

    有点长,但其实也很简单,总结一下就是四点:

    1,如果有config参数,那么就把config内的同名ReactElement属性给替换掉;

    2,如果arguments.length为3,表示有第三个参数children,并且children只有一个,那么直接进行赋值同名属性children,如果大于3,表示children不止一个,就直接for循环赋值;

    3,如果存在type参数并且有defaultprops静态变量,那么就循环defaultProps的属性,只要属性名不为undefined,就赋值给props同名属性;

    4,不允许向props添加key和ref属性;

    最后返回创建的ReactElement。

    还有一个常用的是React.cloneElement:

    ReactElement.cloneElement = function(element, config, children) {
      var propName;
    
      // Original props are copied
      var props = Object.assign({}, element.props);
    
      // Reserved names are extracted
      var key = element.key;
      var ref = element.ref;
      // Self is preserved since the owner is preserved.
      var self = element._self;
      // Source is preserved since cloneElement is unlikely to be targeted by a
      // transpiler, and the original source is probably a better indicator of the
      // true owner.
      var source = element._source;
    
      // Owner will be preserved, unless ref is overridden
      var owner = element._owner;
    
      if (config != null) {
        if (config.ref !== undefined) {
          // Silently steal the ref from the parent.
          ref = config.ref;
          owner = ReactCurrentOwner.current;
        }
        if (config.key !== undefined) {
          key = '' + config.key;
        }
        // Remaining properties override existing props
        var defaultProps;
        if (element.type && element.type.defaultProps) {
          defaultProps = element.type.defaultProps;
        }
        for (propName in config) {
          if (config.hasOwnProperty(propName) &&
              !RESERVED_PROPS.hasOwnProperty(propName)) {
            if (config[propName] === undefined && defaultProps !== undefined) {
              // Resolve default props
              props[propName] = defaultProps[propName];
            } else {
              props[propName] = config[propName];
            }
          }
        }
      }
    
      // Children can be more than one argument, and those are transferred onto
      // the newly allocated props object.
      var childrenLength = arguments.length - 2;
      if (childrenLength === 1) {
        props.children = children;
      } else if (childrenLength > 1) {
        var childArray = Array(childrenLength);
        for (var i = 0; i < childrenLength; i++) {
          childArray[i] = arguments[i + 2];
        }
        props.children = childArray;
      }
    
      return ReactElement(
        element.type,
        key,
        ref,
        self,
        source,
        owner,
        props
      );
    };

     如果config里面有ref,key,props,则全部替换掉config里面的,同名的props直接覆盖,同时参数的children,也作为新的children,最后返回赋值后的ReactElement;

    最后还有一个检测ReactElement是否为有效的方法ReactElement.isValidElement:

    /**
     * @param {?object} object
     * @return {boolean} True if `object` is a valid component.
     * @final
     */
    ReactElement.isValidElement = function(object) {
      return (
        typeof object === 'object' &&
        object !== null &&
        object.$$typeof === REACT_ELEMENT_TYPE
      );
    };

    而这个REACT_ELEMENT_TYPE前面已经介绍过了,这里不再多说。

     

     

     

     

     

     

     

     

    1

    转载于:https://www.cnblogs.com/yanchenyu/p/8304981.html

    展开全文
  • React源码解析(一)

    2020-03-08 23:35:08
    React源码解析(一) github地址 要点掌握:三个API作用,Component、render、createElement jsx模板渲染的过程: createElement function createElement(type, props, ...children) { if (props) { delete ...
  • 首先我们回顾一下 defaultProps 的用法 import React from 'react';...{this.props.name}</div> } } App.defaultProps = { name: "Hello React" }; ReactDOM.render(<App />, document.getE
  • React源码之ReactElement

    2019-11-18 10:33:38
    import React from "react"; class Aa extends React.Component{ render() { return ( ... 1111 {this.props.children} </div> ); } } function App() { return ( <div class...
  • tcomb-react - Library allowing you to check all the props of your React components react-responsive - Media queries in react for responsive design react-cursor - Functional state management ...
  • function ChildrenDemo(props) { console.log(props.children, 'children30');... console.log(React.Children.map(props.children, item => [item, [item, [item]]]), 'children31'); // console.log(React....
  • 我们平常写的class组件都需要继承React.Component这个基类,从而在类都实例中能使用相应的方法。下面让我们看看React.Component这个类究竟长什么样。 Component const emptyObject = {}; function Component...
  • react相关库源码浅析, react ts3 项目 总览: 你将会明白: 开发环境下,key和ref会存在于react元素的props上,但是获取到的值为undefined,并报错。在生产环境下,key和ref不会存在于react元素的props上的。 react...
  • 实例化过程 一、ReactBaseClasses基类解析 ... this.props = props; this.context = context; // If a component has string refs, we will assign a different object later. this.refs = emptyObjec
  • 平时我们可能会有这样的需求,就是需要获取某个dom节点或者子组件的实例来更新,而并不是只是通过props等来更新dom节点或者组件。如果没有一个好的方法我们可能自己去获取节点(例:querySelector等)或者去绑定事件来...
  • Children 这个对象提供了帮你处理组件内部props.children的相关方法。...// React.Children.map(this.props.children, (child) => null) function mapChildren(children, func, context) { ...
  • <div><p>这是我的 React 源码解读课的第一篇文章,首先来说说为啥要写这个系列文章: <ul><li>现在工作中基本都用 React 了,由此想了解下内部原理</li><li>市面上 Vue 的源码解读数不胜数,...
  • React源码解析第三节

    2020-07-21 14:12:23
    源码前言 function Component (props, context,updater) Component.prototype.setState = function(partialState,callback) { … this.updater.enqueueSetState(this,partialState,callback,) } updater....
  • React源码解析:setState

    2018-01-17 10:24:00
    先来几个例子热热身: ......... ...constructor(props){ super(props); this.state = { index: 0 } } componentDidMount() { this.setState({ index: this.state.index + 1 ...
  • 这里我们来看一下,传递给render方法的第一个参数是...import React from './react' import ReactDOM from './react-dom' class Home extends Component { constructor(props) { super(props); this.state = { ...
  • React源码】Day02——实现事件绑定和同步状态更新 github 实现事件绑定 // react-dom.js function updateDOMAttr(dom, props) { for (let key in props) { if (key === "children") { continue; } if (key ==...
  • <p><a href="https://github.com/jsonz1993/react/blob/jsonz-v16.6.3/packages/react/src/ReactContext.js#L17">源码地址</a></p> 该方法传入一个初始值/默认值,创建一个 <code>ReactContext。 <pre><code>...
  • 作用:因为react中Function Component是没有实例的,没有办法进行传递ref。所以,就产生了React.forwardRef这个API来解决这个问题。...Props, ElementType: React$ElementType>( render: (props: Props, ref: R...
  • 学习计划 学习网址:https://react.jokcy.me/ (笔记中内容均为转载) 每天自学一点计划一个月的学习主要通过看视频和看文章 ...Children: { //提供处理props.children的方法 map, forEach, count, toArray, o...
  • React 中 keys 的作用是什么? key是React中用于追踪哪些列表中元素被修改、删除...函数组件中,你无法使用State,也无法使用组件的生命周期方法,这就决定了函数组件都是展示性组件,接收Props,渲染DOM,而不关注..

空空如也

空空如也

1 2 3 4 5 ... 15
收藏数 282
精华内容 112
关键字:

propsreact源码