跳到主要内容

React 序列化错误

一、作用

二、导出的常量

1. jsx 属性父级

// Used for DEV messages to keep track of which parent rendered some props,
// in case they error.
// 用于开发消息,以跟踪哪个父组件渲染了某些 props,以防它们出错。
export const jsxPropsParents: WeakMap<any, any> = new WeakMap();

2. jsx 子元素父元素

export const jsxChildrenParents: WeakMap<any, any> = new WeakMap();

三、判定是否是 Getter

export function isGetter(object: any, name: string): boolean {
const ObjectPrototype = Object.prototype;
if (object === ObjectPrototype || object === null) {
return false;
}
const descriptor = Object.getOwnPropertyDescriptor(object, name);
if (descriptor === undefined) {
return isGetter(getPrototypeOf(object), name);
}
return typeof descriptor.get === 'function';
}

四、判定是否为简单对象

export function isSimpleObject(object: any): boolean {
if (!isObjectPrototype(getPrototypeOf(object))) {
return false;
}
const names = Object.getOwnPropertyNames(object);
for (let i = 0; i < names.length; i++) {
const descriptor = Object.getOwnPropertyDescriptor(object, names[i]);
if (!descriptor) {
return false;
}
if (!descriptor.enumerable) {
if (
(names[i] === 'key' || names[i] === 'ref') &&
typeof descriptor.get === 'function'
) {
// React adds key and ref getters to props objects to issue warnings.
// Those getters will not be transferred to the client, but that's ok,
// so we'll special case them.
//
// React 会向 props 对象添加 key 和 ref 的 getter 来发出警告。这些 getter 不会被
// 传递到客户端,但没关系,所以我们会对它们做特殊处理。
continue;
}
return false;
}
}
return true;
}

五、对象名称

export function objectName(object: mixed): string {
const name = Object.prototype.toString.call(object);
return name.slice(8, name.length - 1);
}

六、描述错误消息的值

export function describeValueForErrorMessage(value: mixed): string {
switch (typeof value) {
case 'string': {
return JSON.stringify(
value.length <= 10 ? value : value.slice(0, 10) + '...',
);
}
case 'object': {
if (isArray(value)) {
return '[...]';
}
if (value !== null && value.$$typeof === CLIENT_REFERENCE_TAG) {
return describeClientReference(value);
}
const name = objectName(value);
if (name === 'Object') {
return '{...}';
}
return name;
}
case 'function': {
if ((value: any).$$typeof === CLIENT_REFERENCE_TAG) {
return describeClientReference(value);
}
const name = (value as any).displayName || value.name;
return name ? 'function ' + name : 'function';
}
default:
return String(value);
}
}

七、为错误消息描述对象

export function describeObjectForErrorMessage(
// objectOrArray: {+[key: string | number]: mixed, ...} | $ReadOnlyArray<mixed>,
objectOrArray: { [key: string | number]: mixed } | $ReadOnlyArray<mixed>,
expandedName?: string,
): string {
const objKind = objectName(objectOrArray);
if (objKind !== 'Object' && objKind !== 'Array') {
return objKind;
}
let str = '';
let start = -1;
let length = 0;
if (isArray(objectOrArray)) {
if (__DEV__ && jsxChildrenParents.has(objectOrArray)) {
// Print JSX Children
// 打印 JSX 子元素
const type = jsxChildrenParents.get(objectOrArray);
str = '<' + describeElementType(type) + '>';
const array: $ReadOnlyArray<mixed> = objectOrArray;
for (let i = 0; i < array.length; i++) {
const value = array[i];
let substr;
if (typeof value === 'string') {
substr = value;
} else if (typeof value === 'object' && value !== null) {
substr = '{' + describeObjectForErrorMessage(value) + '}';
} else {
substr = '{' + describeValueForErrorMessage(value) + '}';
}
if ('' + i === expandedName) {
start = str.length;
length = substr.length;
str += substr;
} else if (substr.length < 15 && str.length + substr.length < 40) {
str += substr;
} else {
str += '{...}';
}
}
str += '</' + describeElementType(type) + '>';
} else {
// Print Array
// 打印数组
str = '[';
const array: $ReadOnlyArray<mixed> = objectOrArray;
for (let i = 0; i < array.length; i++) {
if (i > 0) {
str += ', ';
}
const value = array[i];
let substr;
if (typeof value === 'object' && value !== null) {
substr = describeObjectForErrorMessage(value);
} else {
substr = describeValueForErrorMessage(value);
}
if ('' + i === expandedName) {
start = str.length;
length = substr.length;
str += substr;
} else if (substr.length < 10 && str.length + substr.length < 40) {
str += substr;
} else {
str += '...';
}
}
str += ']';
}
} else {
if (objectOrArray.$$typeof === REACT_ELEMENT_TYPE) {
str = '<' + describeElementType(objectOrArray.type) + '/>';
} else if (objectOrArray.$$typeof === CLIENT_REFERENCE_TAG) {
return describeClientReference(objectOrArray);
} else if (__DEV__ && jsxPropsParents.has(objectOrArray)) {
// Print JSX
// 打印 JSX
const type = jsxPropsParents.get(objectOrArray);
str = '<' + (describeElementType(type) || '...');
// const object: {+[key: string | number]: mixed, ...} = objectOrArray;
const object: { [key: string | number]: mixed } = objectOrArray;
const names = Object.keys(object);
for (let i = 0; i < names.length; i++) {
str += ' ';
const name = names[i];
str += describeKeyForErrorMessage(name) + '=';
const value = object[name];
let substr;
if (
name === expandedName &&
typeof value === 'object' &&
value !== null
) {
substr = describeObjectForErrorMessage(value);
} else {
substr = describeValueForErrorMessage(value);
}
if (typeof value !== 'string') {
substr = '{' + substr + '}';
}
if (name === expandedName) {
start = str.length;
length = substr.length;
str += substr;
} else if (substr.length < 10 && str.length + substr.length < 40) {
str += substr;
} else {
str += '...';
}
}
str += '>';
} else {
// Print Object
// 打印对象
str = '{';
// const object: {+[key: string | number]: mixed, ...} = objectOrArray;
const object: { [key: string | number]: mixed } = objectOrArray;
const names = Object.keys(object);
for (let i = 0; i < names.length; i++) {
if (i > 0) {
str += ', ';
}
const name = names[i];
str += describeKeyForErrorMessage(name) + ': ';
const value = object[name];
let substr;
if (typeof value === 'object' && value !== null) {
substr = describeObjectForErrorMessage(value);
} else {
substr = describeValueForErrorMessage(value);
}
if (name === expandedName) {
start = str.length;
length = substr.length;
str += substr;
} else if (substr.length < 10 && str.length + substr.length < 40) {
str += substr;
} else {
str += '...';
}
}
str += '}';
}
}
if (expandedName === undefined) {
return str;
}
if (start > -1 && length > 0) {
const highlight = ' '.repeat(start) + '^'.repeat(length);
return '\n ' + str + '\n ' + highlight;
}
return '\n ' + str;
}

八、常量

1. 客户参考标签

const CLIENT_REFERENCE_TAG = Symbol.for('react.client.reference');

九、工具

1. 判定是否为对象原型

function isObjectPrototype(object: any): boolean {
if (!object) {
return false;
}
const ObjectPrototype = Object.prototype;
if (object === ObjectPrototype) {
return true;
}
// It might be an object from a different Realm which is
// still just a plain simple object.
// 它可能是来自不同领域的对象,但仍然只是一个普通的简单对象。
if (getPrototypeOf(object)) {
return false;
}
const names = Object.getOwnPropertyNames(object);
for (let i = 0; i < names.length; i++) {
if (!(names[i] in ObjectPrototype)) {
return false;
}
}
return true;
}

2. 描述错误消息的关键

function describeKeyForErrorMessage(key: string): string {
const encodedKey = JSON.stringify(key);
return '"' + key + '"' === encodedKey ? key : encodedKey;
}

3. 描述元素类型

function describeElementType(type: any): string {
if (typeof type === 'string') {
return type;
}
switch (type) {
case REACT_SUSPENSE_TYPE:
return 'Suspense';
case REACT_SUSPENSE_LIST_TYPE:
return 'SuspenseList';
case REACT_VIEW_TRANSITION_TYPE:
if (enableViewTransition) {
return 'ViewTransition';
}
}
if (typeof type === 'object') {
switch (type.$$typeof) {
case REACT_FORWARD_REF_TYPE:
return describeElementType(type.render);
case REACT_MEMO_TYPE:
return describeElementType(type.type);
case REACT_LAZY_TYPE: {
const lazyComponent: LazyComponent<any, any> = type as any;
const payload = lazyComponent._payload;
const init = lazyComponent._init;
try {
// Lazy may contain any component type so we recursively resolve it.
// 用于开发消息,以跟踪哪个父组件渲染了某些 props,以防它们出错。
return describeElementType(init(payload));
} catch (x) {}
}
}
}
return '';
}

4. 描述客户参考

备注
  • () 由 [] 实现
  • () 由 [] 提供
  • () 由 渲染平台提供
function describeClientReference(ref: any) {
return 'client';
}