内容简介: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 源码解析(一)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
The Master Switch
Tim Wu / Knopf / 2010-11-2 / USD 27.95
In this age of an open Internet, it is easy to forget that every American information industry, beginning with the telephone, has eventually been taken captive by some ruthless monopoly or cartel. Wit......一起来看看 《The Master Switch》 这本书的介绍吧!