前言
最近开始接触react开发,根据官网文档和react源码,记录学习过程中的一些知识点。
准备工作
创建一个index.html文件,然后直接引入react的相关js文件,通过debug的方式调试源码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>react-demon</title>
<script src="<https://unpkg.com/react@17/umd/react.development.js>" crossorigin></script>
<script src="<https://unpkg.com/react-dom@17/umd/react-dom.development.js>" crossorigin></script>
</head>
<body>
<script>
var app = document.createElement('div');
document.body.appendChild(app);
console.log('react', React);
ReactDOM.render(
React.createElement(
'div',
null,
'Hello World'
),
app
);
</script>
</body>
</html>
React api 总览
exports.Children = Children;
exports.Component = Component;
exports.PureComponent = PureComponent;
exports.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = ReactSharedInternals$1;
exports.cloneElement = cloneElement$1;
exports.createContext = createContext;
exports.createElement = createElement$1;
exports.createFactory = createFactory;
exports.createRef = createRef;
exports.forwardRef = forwardRef;
exports.isValidElement = isValidElement;
exports.lazy = lazy;
exports.memo = memo;
exports.version = ReactVersion;
Children
Children是用来处理props.children 的一个对象, Children 对象里面包含了map,forEach,count,toArray,only等一些方法,处理children可以直接通过React.Children.方法;
var Children = {
map: mapChildren,
forEach: forEachChildren,
count: countChildren,
toArray: toArray,
only: onlyChild
};
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 = {}
实例上挂载的方法
| 实例方法 | 作用 |
|---|---|
| isReactComponent | 是否是react组件 |
| setState | 设置状态 |
| forceUpdate | 强制更新状态 |
PureComponent
跟Component基本一样,会合并两个实例对象
区别:在props,state改变时,PureComponent会进行一个浅比较,即实现了shouldComponentUpdate方法。
/**
* Convenience component with default shallow equality check for sCU.
*/
// 源码
function PureComponent(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;
this.updater = updater || ReactNoopUpdateQueue;
}
var pureComponentPrototype = PureComponent.prototype = new ComponentDummy();
pureComponentPrototype.constructor = PureComponent; // Avoid an extra prototype jump for these methods.
assign(pureComponentPrototype, Component.prototype);
pureComponentPrototype.isPureReactComponent = true;
tip:如果赋予组件相同的props和state,render()函数会渲染相同的内容,PureComponent性能会更高。另外由于PureComponent只是对对象的浅层比较,会跳过子组件组件树的prop更新,因此需确保子组件也是纯组件。
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
没有文档的API和内部数据结构,因为后续可能更改变动,不建议使用。
cloneElement
以element元素为样板克隆,并返回新的React元素
// 源码
function cloneElementWithValidation(element, props, children) {
var newElement = cloneElement.apply(this, arguments);
for (var i = 2; i < arguments.length; i++) {
validateChildKeys(arguments[i], newElement.type);
}
validatePropTypes(newElement);
return newElement;
}
var cloneElement$1 = cloneElementWithValidation ;
createContext
创建一个 Context 对象。当 React 渲染一个订阅了这个 Context 对象的组件,这个组件会从组件树中离自身最近的那个匹配的 Provider 中读取到当前的 context 值。
createElement
创建并返回指定类型的新的React元素。入参是type,props,children
// 源码
function createElementWithValidation(type, props, children) {
var validType = isValidElementType(type); // We warn in this case but don't throw. We expect the element creation to
// succeed and there will likely be errors in render.
// ……
return element;
}
var cloneElement$1 = cloneElementWithValidation ;
其中type值可以标签名字符串(如 'div' 或 'span'),也可以是 React组件类型 (class 组件或函数组件),或是 Fragment,Profiler等类型。
// 源码
function isValidElementType(type) {
if (typeof type === 'string' || typeof type === 'function') {
return true;
} // Note: typeof might be other than 'symbol' or 'number' (e.g. if it's a polyfill).
if (type === exports.Fragment || type === exports.Profiler || type === REACT_DEBUG_TRACING_MODE_TYPE || type === exports.StrictMode || type === exports.Suspense || type === REACT_SUSPENSE_LIST_TYPE || type === REACT_LEGACY_HIDDEN_TYPE || enableScopeAPI ) {
return true;
}
if (typeof type === 'object' && type !== null) {
if (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 || type.$$typeof === REACT_FUNDAMENTAL_TYPE || type.$$typeof === REACT_BLOCK_TYPE || type[0] === REACT_SERVER_BLOCK_TYPE) {
return true;
}
}
return false;
}
createFactory
返回用于生成指定类型的React元素的函数。该 方法内部是挂载createElementWithValidation函数的属性方法,入参是type,其中type值同createElement方法类型。此辅助函数已废弃,建议使用 JSX 或直接调用 React.createElement() 来替代它
// 源码
function createFactoryWithValidation(type) {
var validatedFactory = createElementWithValidation.bind(null, type);
validatedFactory.type = type;
// ……
return validatedFactory;
}
var createFactory = createFactoryWithValidation ;
createRef
创建一个能够通过 ref 属性附加到 React 元素的 ref。
// 源码
function createRef() {
var refObject = {
current: null
};
{
Object.seal(refObject);
}
return refObject;
}
forwardRef
创建一个React组件,这个组件能够将其接受的 ref 属性转发到其组件树下的另一个组件中,
此方法入参是是一个渲染函数, react 将props, ref作为参数来调用此函数
// 用法
const FancyButton = React.forwardRef((props, ref) => (
<button ref={ref} className="FancyButton">
{props.children}
</button>
));
// You can now get a ref directly to the DOM button:
const ref = React.createRef();
<FancyButton ref={ref}>Click me!</FancyButton>;
// 源码
function forwardRef(render) {
// ...
var elementType = {
$$typeof: REACT_FORWARD_REF_TYPE,
render: render
};
{
var ownName;
Object.defineProperty(elementType, 'displayName', {
enumerable: false,
configurable: true,
get: function () {
return ownName;
},
set: function (name) {
ownName = name;
if (render.displayName == null) {
render.displayName = name;
}
}
});
}
return elementType;
}
isValidElement
返回值是一个Boolean值,验证是否为React元素,入参是一个object对象,判断类型是否是react自定义的属性REACT_ELEMENT_TYPE
var REACT_ELEMENT_TYPE = 0xeac7;
REACT_ELEMENT_TYPE = symbolFor('react.element');
function isValidElement(object) {
return typeof object === 'object' && object !== null && object.$$typeof === REACT_ELEMENT_TYPE;
}
lazy
通过此方法可以定义一个动态组件,有助于减少代码提及大小,且只有在使用的时候才会去加载,改属性需要<React.Suspense>组件配合使用。lazy动态加载的本质是通过Promise的方式异步操作的,具体可以查看lazyInitializer方法
// example
// 这个组件是动态加载的
const SomeComponent = React.lazy(() => import('./SomeComponent'));
// 源码
function lazy(ctor) {
var payload = {
// We use these fields to store the result.
_status: -1,
_result: ctor
};
var lazyType = {
$$typeof: REACT_LAZY_TYPE,
_payload: payload,
_init: lazyInitializer
};
// ....
return lazyType;
}
// 异步加载
function lazyInitializer(payload) {
if (payload._status === Uninitialized) {
var ctor = payload._result;
var thenable = ctor(); // Transition to the next state.
var pending = payload;
pending._status = Pending;
pending._result = thenable;
thenable.then(function (moduleObject) {
if (payload._status === Pending) {
var defaultExport = moduleObject.default;
{
if (defaultExport === undefined) {
error('lazy: Expected the result of a dynamic import() call. ' + 'Instead received: %s\n\nYour code should look like: \n ' + // Break up imports to avoid accidentally parsing them as dependencies.
'const MyComponent = lazy(() => imp' + "ort('./MyComponent'))", moduleObject);
}
} // Transition to the next state.
var resolved = payload;
resolved._status = Resolved;
resolved._result = defaultExport;
}
}, function (error) {
if (payload._status === Pending) {
// Transition to the next state.
var rejected = payload;
rejected._status = Rejected;
rejected._result = error;
}
});
}
if (payload._status === Resolved) {
return payload._result;
} else {
throw payload._result;
}
}
memo
用此方法,如果组件在相同 props 的情况下渲染相同的结果,那么你可以通过将其包装在 React.memo 中调用,以此通过记忆组件渲染结果的方式来提高组件的性能表现。这意味着在这种情况下,React 将跳过渲染组件的操作并直接复用最近一次渲染的结果。由于该方法只检查props变更,当state 或 context发生变化时,仍会重新渲染。
// type为type值可以标签名字符串(如 'div' 或 'span'),
// 也可以是 React组件类型 (class 组件或函数组件),
// 或是 Fragment,Profiler等类型
// compare 可以为一个函数可以控制对比过程
function memo(type, compare) {
{
if (!isValidElementType(type)) {
error('memo: The first argument must be a component. Instead ' + 'received: %s', type === null ? 'null' : typeof type);
}
}
var elementType = {
$$typeof: REACT_MEMO_TYPE,
type: type,
compare: compare === undefined ? null : compare
};
{
var ownName;
Object.defineProperty(elementType, 'displayName', {
enumerable: false,
configurable: true,
get: function () {
return ownName;
},
set: function (name) {
ownName = name;
if (type.displayName == null) {
type.displayName = name;
}
}
});
}
return elementType;
}
结尾
后续会一直总结和记录在学习react中一些知识点,以及项目开发中碰见的问题。