内容简介:react使用也有一段时间了,大家对这个框架褒奖有加,但是它究竟好在哪里呢? 让我们结合它的源码,探究一二!(当前源码为react16,读者要对react有一定的了解)根据react官网上的例子,快速构建react项目打开项目并跑起来以后,暂不关心项目结构及语法糖,看到
react使用也有一段时间了,大家对这个框架褒奖有加,但是它究竟好在哪里呢? 让我们结合它的源码,探究一二!(当前源码为react16,读者要对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/>) 复制代码
可以看到, <App/>
组件其实是一个JS对象,并不是一个真实的dom。
ES6 引入了一种新的原始数据类型Symbol,表示独一无二的值。有兴趣的同学可以去阮一峰老师的ES6入门详细了解一下
上面有我们很熟悉的 props
, ref
, key
,我们稍微修改一下console,看看有什么变化。
console.log(<App key={1} abc={2}><div>你好,这里是App组件</div></App>) 复制代码
可以看到, props
, key
都发生了变化,值就是我们赋予的值, props
中嵌套了children属性。可是为什么我们嵌入的是div,实际上却是一个对象呢?
打开源码
/node_modules/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暴露的接口:
接着找到 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/>
组件已经有一个大概的雏形:
到此为止了吗?这看了等于没看啊,究竟组件是怎么变成div的?render吗? 可是全局搜索,也没有一个function是render啊。
原来,我们的jsx语法会被 babel
编译的。
这下清楚了,还用到了 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/>
的结构,是不是很像
验证一下我们的探索究竟对不对,再每一个方法上我们都打上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的呢
ReactDOM.render(<App />, document.getElementById('root')); 复制代码
我们接着用babel编译一下:
原来 ReactDOM.render
调用的是render方法,一样,找暴露出来的接口。
var ReactDOM = { ······ render: function (element, container, callback) { return legacyRenderSubtreeIntoContainer(null, element, container, false, callback); }, ······ }; 复制代码
它返回的是一个 legacyRenderSubtreeIntoContainer
方法,这次我们直接打上console.log
这是打印出来的结果,
legacyRenderSubtreeIntoContainer 这个方法除主要做了两件事:
- 清除dom容器元素的子元素
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
究竟是什么,我们将在后续文章接着介绍。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- React Hooks 源码解析(一):类组件、函数组件、纯组件
- 权限组件的源码分析
- OpenFalcon 源码分析(Nodata组件)
- 深入剖析Vue源码 - 组件基础
- 深入剖析Vue源码 - 组件进阶
- 生命周期组件 Lifecycle 源码解析(一)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Effective Modern C++ 简体中文版
Scott Meyers / 高博 / 中国电力出版社 / 2018-4-23 / 99
想要彻底理解C++11和C++14,不可止步于熟悉它们引入的语言特性(例如,auto型别推导、移动语义、lambda表达式以及并发支持)。挑战在于高效地运用这些特性——从而使你的软件具备正确性、高效率、可维护性和可移植性。这正是这本实用的图书意欲达成的定位。它描述的正是使用C++11和C++14——现代C++来撰写真正卓越的软件之道。 涵盖以下主题: 大括号初始化、noexcept规格......一起来看看 《Effective Modern C++ 简体中文版》 这本书的介绍吧!