【React源码解读】- 组件的实现

栏目: IOS · Android · 发布时间: 5年前

内容简介:react使用也有一段时间了,大家对这个框架褒奖有加,但是它究竟好在哪里呢? 让我们结合它的源码,探究一二!(当前源码为react16,读者要对react有一定的了解)根据react官网上的例子,快速构建react项目打开项目并跑起来以后,暂不关心项目结构及语法糖,看到

react使用也有一段时间了,大家对这个框架褒奖有加,但是它究竟好在哪里呢? 让我们结合它的源码,探究一二!(当前源码为react16,读者要对react有一定的了解)

【React源码解读】- 组件的实现

回到最初

根据react官网上的例子,快速构建react项目

npx create-react-app my-app

cd my-app

npm start
复制代码

打开项目并跑起来以后,暂不关心项目结构及语法糖,看到 App.js 里,这是一个基本的react组件 我们console一下,看看有什么结果。

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

class App extends Component {

  render() {
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <p>
            Edit <code>src/App.js</code> and save to reload.
          </p>
        </header>
      </div>
    );
  }
}

export default App;

console.log(<App/>)


复制代码
【React源码解读】- 组件的实现

可以看到, <App/> 组件其实是一个JS对象,并不是一个真实的dom。

ES6 引入了一种新的原始数据类型Symbol,表示独一无二的值。有兴趣的同学可以去阮一峰老师的ES6入门详细了解一下

上面有我们很熟悉的 props , ref , key ,我们稍微修改一下console,看看有什么变化。

console.log(<App key={1} abc={2}><div>你好,这里是App组件</div></App>)
复制代码
【React源码解读】- 组件的实现

可以看到, props , key 都发生了变化,值就是我们赋予的值, props 中嵌套了children属性。可是为什么我们嵌入的是div,实际上却是一个对象呢?

打开源码

/node_modules/react
【React源码解读】- 组件的实现

首先打开 index.js

'use strict';

if (process.env.NODE_ENV === 'production') {
  module.exports = require('./cjs/react.production.min.js');
} else {
  module.exports = require('./cjs/react.development.js');
}
复制代码

可以知道目前用上的是 ./cjs/react.development.js ,直接打开文件。 根据最初的代码,我们组件 <App/> 用到了 React.Component 。找到React暴露的接口:

【React源码解读】- 组件的实现

接着找到 Component: Component 方法,

function Component(props, context, updater) {
  this.props = props;
  this.context = context;
  // If a component has string refs, we will assign a different object later.
  this.refs = emptyObject;
  // We initialize the default updater but the real one gets injected by the
  // renderer.
  this.updater = updater || ReactNoopUpdateQueue;
}

Component.prototype.isReactComponent = {};

Component.prototype.setState = function (partialState, callback) {
  !(typeof partialState === 'object' || typeof partialState === 'function' || partialState == null) ? invariant(false, 'setState(...): takes an object of state variables to update or a function which returns an object of state variables.') : void 0;
  this.updater.enqueueSetState(this, partialState, callback, 'setState');
};

Component.prototype.forceUpdate = function (callback) {
  this.updater.enqueueForceUpdate(this, callback, 'forceUpdate');
};

复制代码

上面就是一些简单的构造函数,也可以看到,我们常用的setState是定义在原型上的2个方法。

至此,一个 <App/> 组件已经有一个大概的雏形:

【React源码解读】- 组件的实现

到此为止了吗?这看了等于没看啊,究竟组件是怎么变成div的?render吗? 可是全局搜索,也没有一个function是render啊。

原来,我们的jsx语法会被 babel 编译的。

【React源码解读】- 组件的实现

这下清楚了,还用到了 React.createElement

createElement: createElementWithValidation,
复制代码

通过 createElementWithValidation ,

function createElementWithValidation(type, props, children) {
······

  var element = createElement.apply(this, arguments);


  return element;
}
复制代码

可以看到,return了一个element,这个element又是继承自 createElement ,接着往下找:

function createElement(type, config, children) {
  var propName = void 0;

  // Reserved names are extracted
  var props = {};

  var key = null;
  var ref = null;
  var self = null;
  var source = null;
······
  return ReactElement(type, key, ref, self, source, ReactCurrentOwner.current, props);
}
复制代码

这里又返回了一个 ReactElement 方法,再顺着往下找:

var ReactElement = function (type, key, ref, self, source, owner, props) {
  var element = {
    // This tag allows 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
  };

······
  return element;
};
复制代码

诶,这里好像返回的就是 element 对象,再看我们最初的 <App/> 的结构,是不是很像

【React源码解读】- 组件的实现

验证一下我们的探索究竟对不对,再每一个方法上我们都打上console,(注意,将App里的子元素全部删空,利于我们观察)

【React源码解读】- 组件的实现

React.createElement 、 createElementWithValidation 、 createElement 、 ReactElement,通过这些方法,我们用class声明的React组件在变成真实dom之前都是 ReactElement 类型的js对象

createElementWithValidation :

  • 首先校验type是否是合法的
【React源码解读】- 组件的实现
  • 校验了props是否符合设置的proptypes
【React源码解读】- 组件的实现
  • 校验了子节点的key,确保每个数组中的元素都有唯一的key
【React源码解读】- 组件的实现

createElement

  • type是你要创建的元素的类型,可以是html的div或者span,也可以是其他的react组件,注意大小写
  • config中包含了props、key、ref、self、source等
【React源码解读】- 组件的实现
  • 向props加入children,如果是一个就放一个对象,如果是多个就放入一个数组。
【React源码解读】- 组件的实现
  • 那如果type.defaultProps有默认的props时,并且对应的props里面的值是undefined,把默认值赋值到props中
【React源码解读】- 组件的实现
  • 也会对key和ref进行校验
【React源码解读】- 组件的实现

ReactElement

ReactElement就比较简单了,创建一个element对象,参数里的type、key、ref、props、等放进去,然后return了。最后调用Object.freeze使对象不可再改变。

组件的挂载

我们上面只是简单的探究了 <App/> 的结构和原理,那它究竟是怎么变成真实dom的呢

【React源码解读】- 组件的实现
ReactDOM.render(<App />, document.getElementById('root'));
复制代码

我们接着用babel编译一下:

【React源码解读】- 组件的实现

原来 ReactDOM.render 调用的是render方法,一样,找暴露出来的接口。

var ReactDOM = {
······
  render: function (element, container, callback) {
    return legacyRenderSubtreeIntoContainer(null, element, container, false, callback);
  },
······
};

复制代码

它返回的是一个 legacyRenderSubtreeIntoContainer 方法,这次我们直接打上console.log

【React源码解读】- 组件的实现

这是打印出来的结果,

【React源码解读】- 组件的实现

legacyRenderSubtreeIntoContainer 这个方法除主要做了两件事:

  • 清除dom容器元素的子元素
while (rootSibling = container.lastChild) {
      {
        if (!warned && rootSibling.nodeType === ELEMENT_NODE && rootSibling.hasAttribute(ROOT_ATTRIBUTE_NAME)) {
          warned = true;
        }
      }
      container.removeChild(rootSibling);
    }
复制代码
  • 创建ReactRoot对象
【React源码解读】- 组件的实现

源码暂时只读到了这里,关于React16.1~3的新功能,以及新的生命周期的使用和原理、 Fiber 究竟是什么,我们将在后续文章接着介绍。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

艾伦•图灵传

艾伦•图灵传

(英)安德鲁·霍奇斯 / 孙天齐 / 湖南科学技术出版社 / 2012-8-1 / 68.00元

《艾伦·图灵传:如谜的解谜者》是图灵诞辰100周年纪念版,印制工艺更为精美。本书是世界共认的最权威的图灵传记。艾伦?图灵是现代人工智能的鼻祖,在24岁时奠定了计算机的理论基础。二战期间,他为盟军破译密码,为结束战争做出巨大贡献。战后,他开创性地提出人工智能的概念,并做了大量的前期工作。因同性恋问题事发,被迫注射激素,后来吃毒苹果而死。作者是一名数学家,也是一名同性恋者。他对图灵的生平有切身的体会,......一起来看看 《艾伦•图灵传》 这本书的介绍吧!

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具