前言
作为一个前端页面仔和需求粉碎机,在日常的工作中重复雷同的业务需求,能够获得的提高是很有限的。要想跳出此山中,开阔新视野,笔者墙裂建议大家阅读市面上顶尖开源库的源码。这是学习和掌握js语言特性的绝佳机会(前端发展到现在,大型应用高度依赖框架,正常情况下普通开发者是没有机会接触底层的语言特性),同时也是深刻理解框架底层思维的契机。这里笔者选择react
第一个开刀,市面上不少关于react源码分析的文章要么过于老旧,要么只截取部分代码或者是伪代码,笔者这里将选取react的16.8.6版本作为示例,从第0行开始,不漏过任何一个源码细节,和大家分享笔者在源码阅读过程中的体会。希望和大家共同进步,本系列博文中涉及的源码本人会放在git仓库中,链接在文末。
正文
- reactElement的构造函数
// 定义一个创建react 元素的构造函数,这跟class模式的组建不一样,请不要使用new来调用,所有instanceof来检查是失效的,不要使用要用Symbol.for('react.element'),而要用$$typeof来检查,
// 来判断是否是react组件
// self是一个暂时的变量,是用来判断当React.createElement被调用的时候this和owner是否一致,以便我们告警。我们打算摆脱owner这个概念并且
// 使用箭头函数,只要这个二者一致,组件就没有变化
// source是一个注释对象(被转译器或者其他文件名,行数,等信息所添加)
/**
* Factory method to create a new React element. This no longer adheres to
* the class pattern, so do not use new to call it. Also, no instanceof check
* will work. Instead test $$typeof field against Symbol.for('react.element') to check
* if something is a React Element.
*
* @param {*} type
* @param {*} key
* @param {string|object} ref
* @param {*} self A *temporary* helper to detect places where `this` is
* different from the `owner` when React.createElement is called, so that we
* can warn. We want to get rid of owner and replace string `ref`s with arrow
* functions, and as long as `this` and owner are the same, there will be no
* change in behavior.
* @param {*} source An annotation object (added by a transpiler or otherwise)
* indicating filename, line number, and/or other information.
* @param {*} owner
* @param {*} props
* @internal
*/
// react元素构造函数
// 返回的其实是element对象
var ReactElement = function (type, key, ref, self, source, owner, props) {
var element = {
// 通过这个标签来识别react的元素
// 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
};
{
// 这个验证标志是可变的,我们把这个放在外部支持存储,以便我们能够冻结整个对象,
// 这个可以被若映射替代,一旦在开发环境下实现了
// 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.
// 给_store设置validated属性false
Object.defineProperty(element._store, 'validated', {
configurable: false,
enumerable: false,
writable: true,
value: false
});
// self和source都是开发环境才存在的
// 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
});
// 如果Object有freeze的实现,我们冻结元素和它的属性
if (Object.freeze) {
Object.freeze(element.props);
Object.freeze(element);
}
}
return element;
};
这里首先引出ReactElement
的构造函数,注意react内部是使用$$typeof
来判断react 元素的类型的。使用_store
来记录内部的状态,后面会有用到。为了方便测试框架,_store
中定义了不可配置不可枚举的validated属性。类似的,框架内部定义了self和source的副本_self
和_source
,他们都是不可配置不可枚举不可写的。
- 创建一个给定类型的ReactElement