Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >【React源码解读】- 组件的实现

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

作者头像
用户2356368
发布于 2019-04-03 08:06:36
发布于 2019-04-03 08:06:36
70400
代码可运行
举报
文章被收录于专栏:薄荷前端薄荷前端
运行总次数:0
代码可运行

前言

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

回到最初

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
npx create-react-app my-app

cd my-app

npm start
复制代码

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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/>)


复制代码

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

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

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
console.log(<App key={1} abc={2}><div>你好,这里是App组件</div></App>)
复制代码

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

打开源码

/node_modules/react

首先打开index.js

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
'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暴露的接口:

接着找到Component: Component方法,

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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/>组件已经有一个大概的雏形:

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

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

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
createElement: createElementWithValidation,
复制代码

通过createElementWithValidation,

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function createElementWithValidation(type, props, children) {
······

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


  return element;
}
复制代码

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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方法,再顺着往下找:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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/>的结构,是不是很像

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

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

createElementWithValidation:

  • 首先校验type是否是合法的
  • 校验了props是否符合设置的proptypes
  • 校验了子节点的key,确保每个数组中的元素都有唯一的key

createElement

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

ReactElement

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

组件的挂载

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
ReactDOM.render(<App />, document.getElementById('root'));
复制代码

我们接着用babel编译一下:

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var ReactDOM = {
······
  render: function (element, container, callback) {
    return legacyRenderSubtreeIntoContainer(null, element, container, false, callback);
  },
······
};

复制代码

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

这是打印出来的结果,

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

  • 清除dom容器元素的子元素
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
while (rootSibling = container.lastChild) {
      {
        if (!warned && rootSibling.nodeType === ELEMENT_NODE && rootSibling.hasAttribute(ROOT_ATTRIBUTE_NAME)) {
          warned = true;
        }
      }
      container.removeChild(rootSibling);
    }
复制代码
  • 创建ReactRoot对象

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

广而告之

本文发布于薄荷前端周刊,欢迎Watch & Star ★,转载请注明出处。

欢迎讨论,点个赞再走吧 。◕‿◕。 ~

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018年10月31日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
【React源码解读】- 组件的实现
react使用也有一段时间了,大家对这个框架褒奖有加,但是它究竟好在哪里呢? 让我们结合它的源码,探究一二!(当前源码为react16,读者要对react有一定的了解)
用户2356368
2024/02/04
1220
【React源码解读】- 组件的实现
React源码分析1-jsx转换及React.createElement
我们从 react 应用的入口开始对源码进行分析,创建一个简单的 hello, world 应用:
goClient1992
2022/10/06
9410
react源码分析:babel如何解析jsx_2023-02-27
同作为MVVM框架,React相比于Vue来讲,上手更需要JavaScript功底深厚一些,本系列将阅读React相关源码,从jsx -> VDom -> RDOM等一些列的过程,将会在本系列中一一讲解
flyzz177
2023/02/27
2770
react源码分析:babel如何解析jsx
同作为MVVM框架,React相比于Vue来讲,上手更需要JavaScript功底深厚一些,本系列将阅读React相关源码,从jsx -> VDom -> RDOM等一些列的过程,将会在本系列中一一讲解
flyzz177
2022/10/19
3570
react源码解析5.jsx&核心api
一句话概括就是,用js对象表示dom信息和结构,更新时重新渲染更新后的对象对应的dom,这个对象就是React.createElement()的返回结果
用户9002110
2021/12/04
4090
react源码解析5.jsx&核心api
一句话概括就是,用js对象表示dom信息和结构,更新时重新渲染更新后的对象对应的dom,这个对象就是React.createElement()的返回结果
zz1998
2021/11/30
4200
react源码解析--jsx&核心api
一句话概括就是,用js对象表示dom信息和结构,更新时重新渲染更新后的对象对应的dom,这个对象就是React.createElement()的返回结果
长腿程序员165858
2022/12/12
3670
react源码解析5.jsx&核心api
一句话概括就是,用js对象表示dom信息和结构,更新时重新渲染更新后的对象对应的dom,这个对象就是React.createElement()的返回结果
全栈潇晨
2021/06/04
4830
react源码解析5.jsx&核心api
渐进式React源码解析--State源码
文章中涉及到的知识都是渐进式的讲解开发,当然如果对之间内容不感兴趣(已经了解),也可以直接切入本文内容,每一个章节都和之前不会有很强的耦合。
19组清风
2021/11/15
7810
渐进式React源码解析--State源码
React源码解读【一】API复习与基础
四年,如人生小溪中的一洼清水,如历史长河中的一点水滴,而却就是这四年,我完成了从懵懂到成熟的蜕变。回首这四年,有过创业,有过生病,有过说不出的苦楚,也有过让我笑不间断的喜悦。
合一大师
2020/07/20
6980
React源码解读【一】API复习与基础
React 源码深度解读(九):单个元素更新
React 是一个十分庞大的库,由于要同时考虑 ReactDom 和 ReactNative ,还有服务器渲染等,导致其代码抽象化程度很高,嵌套层级非常深,阅读其源码是一个非常艰辛的过程。在学习 React 源码的过程中,给我帮助最大的就是这个系列文章,于是决定基于这个系列文章谈一下自己的理解。本文会大量用到原文中的例子,想体会原汁原味的感觉,推荐阅读原文。
Dickensl
2022/06/14
6430
React 源码深度解读(九):单个元素更新
ReactDOM.render在react源码中执行之后发生了什么?
通常是如下图使用,在提供的 container 里渲染一个 React 元素,并返回对该组件的引用(或者针对无状态组件返回 null)。本文主要是将ReactDOM.render的执行流程在后续文章中会对创建更新的细节进行分析,文中的源代码部分为了方便阅读将__DEV__部分的代码移除掉了。
flyzz177
2022/09/30
5640
React源码解读【二】更新创建
•微信公众号 《JavaScript全栈》•掘金 《合一大师》•Bilibili 《合一大师》•源码Github地址:https://github.com/Walker-Leee/react-learn-code-v16.12.0
合一大师
2020/07/20
8882
React源码解读【二】更新创建
「React进阶」 React全部api解读+基础实践大全(夯实基础万字总结)
很多同学用react开发的时候,真正用到的React的api少之又少,基本停留在Component,React.memo等层面,实际react源码中,暴露出来的方法并不少,只是我们平时很少用。但是React暴露出这么多api并非没有用,想要玩转react,就要明白这些API究竟是干什么的,应用场景是什么,今天就让我们从react 到 react-dom,一次性把react生产环境的暴露api复习个遍(涵盖90%+)。
用户6835371
2021/06/01
2.2K0
「React进阶」 React全部api解读+基础实践大全(夯实基础万字总结)
React 源码深度解读(一):首次DOM元素渲染 - Part 1
React 是一个十分庞大的库,由于要同时考虑 ReactDom 和 ReactNative ,还有服务器渲染等,导致其代码抽象化程度很高,嵌套层级非常深。阅读 React 源码是一个非常艰辛的过程,在学习过程中给我帮助最大的就是这个系列文章。作者对代码的调用关系梳理得非常清楚,而且还有配图帮助理解,非常值得一读。站在巨人的肩膀之上,我尝试再加入自己的理解,希望对有志于学习 React 源码的读者带来一点启发。
Dickensl
2022/06/14
6230
React 源码深度解读(一):首次DOM元素渲染 - Part 1
react面试题详解
当请求 /users/:id 被重定向去 '/users/profile/:id':
beifeng1996
2022/11/18
1.3K0
相关推荐
【React源码解读】- 组件的实现
更多 >
领券
社区富文本编辑器全新改版!诚邀体验~
全新交互,全新视觉,新增快捷键、悬浮工具栏、高亮块等功能并同时优化现有功能,全面提升创作效率和体验
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验