前言
作为一个前端页面仔和需求粉碎机,在日常的工作中重复雷同的业务需求,能够获得的提高是很有限的。要想跳出此山中,开阔新视野,笔者墙裂建议大家阅读市面上顶尖开源库的源码。这是学习和掌握js语言特性的绝佳机会(前端发展到现在,大型应用高度依赖框架,正常情况下普通开发者是没有机会接触底层的语言特性),同时也是深刻理解框架底层思维的契机。这里笔者选择react
第一个开刀,市面上不少关于react源码分析的文章要么过于老旧,要么只截取部分代码或者是伪代码,笔者这里将选取react的16.8.6版本作为示例,从第0行开始,不漏过任何一个源码细节,和大家分享笔者在源码阅读过程中的体会。希望和大家共同进步,本系列博文中涉及的源码本人会放在git仓库中,链接在文末。
正文
React.lazy和forwardRef
// lazy的构造函数
function lazy(ctor) {
var lazyType = {
$$typeof: REACT_LAZY_TYPE,
_ctor: ctor,
// react使用这些值去储存结果
// React uses these fields to store the result.
_status: -1,
_result: null
};
{
// 在生产环境下,这些都会设置在对象上
// In production, this would just set it on the object.
var defaultProps = void 0;
var propTypes = void 0;
Object.defineProperties(lazyType, {
defaultProps: {
configurable: true,
get: function () {
return defaultProps;
},
set: function (newDefaultProps) {
// react不支持给lazy加载的组件修改默认属性,要么在定义的时候修改,要么在外面包上一层
warning$1(false, 'React.lazy(...): It is not supported to assign `defaultProps` to ' + 'a lazy component import. Either specify them where the component ' + 'is defined, or create a wrapping component around it.');
defaultProps = newDefaultProps;
// 和生产环境的行为更接近
// 设置可枚举类型
// Match production behavior more closely:
Object.defineProperty(lazyType, 'defaultProps', {
enumerable: true
});
}
},
propTypes: {
configurable: true,
get: function () {
return propTypes;
},
set: function (newPropTypes) {
// react不支持给懒加载组件设置属性类型
warning$1(false, 'React.lazy(...): It is not supported to assign `propTypes` to ' + 'a lazy component import. Either specify them where the component ' + 'is defined, or create a wrapping component around it.');
propTypes = newPropTypes;
// Match production behavior more closely:
Object.defineProperty(lazyType, 'propTypes', {
enumerable: true
});
}
}
});
}
return lazyType;
}
// 前向ref
function forwardRef(render) {
{
// 感觉就是一堆校验
if (render != null && render.$$typeof === REACT_MEMO_TYPE) {
// 如果render不为null且其类型为REACT_MEMO_TYPE,抛错
warningWithoutStack$1(false, 'forwardRef requires a render function but received a `memo` ' + 'component. Instead of forwardRef(memo(...)), use ' + 'memo(forwardRef(...)).');
} else if (typeof render !== 'function') {
// 如果不是函数,抛错
warningWithoutStack$1(false, 'forwardRef requires a render function but was given %s.', render === null ? 'null' : typeof render);
} else {
!(
// 0参数的时候不报错,因为这可能是因为参数是对象
// Do not warn for 0 arguments because it could be due to usage of the 'arguments' object
// 如果参数数组长度不为0或2,抛错:forwardRef只接受2个参数,属性值和ref,'你忘记传ref'或'多出来的参数都会被省略'
render.length === 0 || render.length === 2) ? warningWithoutStack$1(false, 'forwardRef render functions accept exactly two parameters: props and ref. %s', render.length === 1 ? 'Did you forget to use the ref parameter?' : 'Any additional parameter will be undefined.') : void 0;
}
// 如果不满足之前的条件且render不为空
if (render != null) {
// 如果render上存在defaultProps或者propTypes,抛错:forwardRef的渲染函数不支持propTypes或者defaultProps,你实际上传入了一个react组件是吗
!(render.defaultProps == null && render.propTypes == null) ? warningWithoutStack$1(false, 'forwardRef render functions do not support propTypes or defaultProps. ' + 'Did you accidentally pass a React component?') : void 0;
}
}
return {
$$typeof: REACT_FORWARD_REF_TYPE,
render: render
};
}
接下来的内容主要是部分react方法的实现,到这里大家应该会逐步看到一些熟悉的方法,首先介绍的lazy和forwardRef
组件有效判断与hooksAPI定义
// 判断是否是元素的可用类型
function isValidElementType(type) {
// 字符串,函数,react片段或者组件
return typeof type === 'string' || typeof type === 'function' ||
// 这里的类型可能是symbol或者是数字,如果使用了降级的写法
// Note: its typeof might be other than 'symbol' or 'number' if it's a polyfill.
// 后面的这些type要调一次$$typeof
type === REACT_FRAGMENT_TYPE || type === REACT_CONCURRENT_MODE_TYPE || type === REACT_PROFILER_TYPE || type === REACT_STRICT_MODE_TYPE || type === REACT_SUSPENSE_TYPE || typeof type === 'object' && type !== null && (type.$$typeof === REACT_LAZY_TYPE || type.$$typeof === REACT_MEMO_TYPE || type.$$typeof === REACT_PROVIDER_TYPE || type.$$typeof === REACT_CONTEXT_TYPE || type.$$typeof === REACT_FORWARD_REF_TYPE);
}
// 纯函数版本的pureComponent,第一个参数是个函数
function memo(type, compare) {
{
// 如果不是react类型
if (!isValidElementType(type)) {
// memo的第一个参数必须是组件
warningWithoutStack$1(false, 'memo: The first argument must be a component. Instead ' + 'received: %s', type === null ? 'null' : typeof type);
}
}
return {
$$typeof: REACT_MEMO_TYPE,
type: type,
compare: compare === undefined ? null : compare
};
}
// 返回当前的dispatcher
function resolveDispatcher()