ichengbo / comments Goto Github PK
View Code? Open in Web Editor NEWutterances + issue 实现 comments 功能
utterances + issue 实现 comments 功能
Redux 是 JavaScript 状态容器,提供可预测化的状态管理。解决在大型应用中,状态过多难以维护的问题。
在 JavaScript 中,我们可以有多种方式定义函数,如:函数声明、函数表达式和箭头函数
// 函数声明
function normalFn() {
return 'normalFn';
}
// 函数表达式
const normalFn = function() {
return 'normalFn';
}
// 箭头函数
const arrowFn = () => {
return 'arrowFn';
}
其中,箭头函数是在 ES2015(ES6) 标准中新增的,其语法与 ES6 之前的函数声明及函数表达式两种定义方式不同。本文中,将函数声明和函数表达式两种定义方式归为普通函数。
那么,普通函数和箭头函数有什么区别呢?🤔️
在 JavaScript 中,this
的指向是个基础且重要的知识点。
在普通函数中,this
的指向(执行上下文)是动态的,其值取决于函数是如何被调用的,通常有以下 4 种调用方式:
function fnc() {
console.log(this);
}
fnc(); // 全局对象(global 或 window)
var obj = {
fnc1: function(){
console.log(this === obj);
}
}
obj.fnc2 = function() {
console.log(this === obj);
}
function fnc3() {
console.log(this === obj);
}
obj.fnc3 = fnc3;
obj.fnc1(); // true
obj.fnc2(); // true
obj.fnc3(); // true
function fnc() {
console.log(this);
}
new fnc(); // fnc 的实例 fnc {}
function fnc() {
console.log(this);
}
const ctx = { value: 'a' };
fnc.call(ctx); // { value: 'a' }
fnc.apply(ctx); // { value: 'a' }
fnc.bind(ctx)(); // { value: 'a' }
在旧版的 JavaScript 中,经常使用 bind 显式的设置 this 的指向,这种模式通常可以在 ES6 出现之前的某些早期版本的框架(如 React)中找到。而箭头函数的出现则提供了一种更便捷的方式解决此问题。
无论如何执行或在何处执行,箭头函数内部的 this 值始终等于外部函数的值,即箭头函数不会改变 this 的指向,
const obj = {
fnc(arr) {
console.log(this); // obj
const cb = () => {
console.log(this); // obj
};
arr.forEach(cb);
}
};
obj.fnc([1, 2, 3]);
注意:由于箭头函数没有自己的 this 指针,通过 call() 、 apply() 和 bind() 方法调用时,只能传递参数,而不能绑定 this,他们的第一个参数会被忽略。如下:(例子来源于 MDN)
var adder = {
base : 1,
add : function(a) {
var f = v => v + this.base;
return f(a);
},
addThruCall: function(a) {
var f = v => v + this.base;
var b = {
base : 2
};
return f.call(b, a);
}
};
console.log(adder.add(1)); // 输出 2
console.log(adder.addThruCall(1)); // 仍然输出 2
在 JavaScript 中,函数和类的继承是通过 prototype
属性实现的,且 prototype
拥有属性 constructor
指向构造函数,如下:
function fnc() {}
console.lof(fnc.prototype) // {constructor: ƒ}
而采用箭头函数定义函数时,其是没有 prototype
属性的,也就无法指向构造函数。
const arrowFnc = () => {}
console.log(arrowFnc.prototype) // undefined
针对普通函数与箭头函数在构造函数上的区别,可以引出一个问题 -- 箭头函数可以通过 new 进行实例化吗?
const arrowFnc = () => {}
const arrowIns = new arrowFnc() // Uncaught TypeError: arrowFnc is not a constructor
答案是【不可以】,那么为什么呢?🤔️
function newOperator(Con, ...args) {
let obj = {};
Object.setPrototypeOf(obj, Con.prototype); // 相当于 obj.__proto__ = Con.prototype
let result = Con.apply(obj, args);
return result instanceof Object ? result : obj;
}
更具体的原因是,JavaScript函数两个内部方法: [[Call]] 和 [[Construct]],当函数被直接调用时执行的是 [[Call]] 方法,即直接执行函数体,而 new 调用时是执行的 [[Construct]]方法。箭头函数没有 [[Construct]]方法,因此不能被用作构造函数进行实例化。
'use strict';
var obj = {
i: 10,
b: () => console.log(this.i, this),
c: function() {
console.log(this.i, this)
}
}
obj.b(); // undefined, Window{...}
obj.c(); // 10, Object {...}
可以看到,箭头函数是没有 this 绑定的,其指向始终与上一级保持一致。
上文中提到,当构造函数或类被 new 调用时,其 this 指向为新创建的实例对象,需要注意的是,这里的 this 是 constructor 中的 this,而不是该函数或类的任意地方。如下所示:
class Person {
constructor(name) {
this.name = name;
}
getName() {
console.log(this.name, this);
}
}
const p = new Person('Tom');
p.getName(); // Tom
setTimeout(p.getName, 100); // undefined, Window{...}
为了避免这种错误,我们通常需要在 constructor 中绑定 this,如下所示:
class Person {
constructor(name) {
this.name = name;
this.getName = this.getName.bind(this);
}
getName() {
console.log(this.name);
}
}
const p = new Person('Tom');
setTimeout(p.getName, 100); // Tom
这种写法,很容易在 React 中发现,其实也是为了填补 JavaScript 的坑 😂。当然,也可以使用箭头函数来避免这种错误,并简化写法,如下:
class Person {
constructor(name) {
this.name = name;
}
getName = () => {
console.log(this.name);
}
}
const p = new Person('Tom');
setTimeout(p.getName, 100); // Tom
在使用箭头函数时,this 是具有词法约束的,也就是说箭头函数会自动将 this 绑定到定义它的上下文。
普通函数与箭头函数在参数上区别主要在于,箭头函数不绑定 arguments 对象。
const fn = () => arguments[0];
fn(1); // Uncaught ReferenceError: arguments is not defined
当我们需要使用参数时,可以考虑使用剩余参数,如下:
const fn = (...args) => args[0];
fn(1, 2); // 1
另外,当函数参数个数为 1 时,箭头函数可以省略括号,进行缩写,如下所示:
const fn = x => x * x;
在处理函数的返回值时,相比于普通函数,箭头函数可以隐式返回。
const sum = (a, b) => {
return a + b
}
const sum = (a, b) => (a + b);
隐式返回通常会创建一个单行操作用于 map、filter 等操作,注意:如果不能将函数主题编写为单行代码的话,则必须使用普通的函数体语法,即不能省略花括号和 return。
[1,2,3].map(i => i * 2); // [2,4,6]
[1,2,3].filter(i => i != 2); // [1,3]
本文主要介绍了普通函数与箭头函数的区别,相对于普通函数来说,ES6 箭头函数的主要区别如下:
arguments
,可以使用 ...args
代替;this
是词法绑定的,与外层函数保持一致;prototype
属性,不能进行 new 实例化,亦不能通过 call、apply 等绑定 this;constructor
中绑定 this
。最近在做 RN 应用线上错误监控的需求,在此记录下常用方案。
React Native 在架构上整体可分为三块:Native、JavaScript 和 Bridge。其中,Native 管理 UI 更新及交互,JavaScript 调用 Native 能力实现业务功能,Bridge 负责在二者之间传递消息。
最上层提供类 React 支持,运行在 JavaScriptCore
提供的 JavaScript 运行时环境中,Bridge 层将 JavaScript 与 Native 世界连接起来。
本文从以下三个角度,分别介绍如何捕获 RN 应用中未被处理的异常:
Native 有较多成熟的方案,如友盟、Bugly、网易云捕和 crashlytics 等,这些平台不仅提供异常捕获能力,还相应的有上报、统计、预警等能力。本文不对以上平台异常捕获实现方式进行分析,而是通过分析 react-native-exception-handler 了解 Native 端异常捕获的实现原理。 react-native-exception-handler 实现了 setNativeExceptionHandle
用于设置 Native 监测到异常时的回调函数,如下所示:
export const setNativeExceptionHandler = (customErrorHandler = noop, forceApplicationToQuit = true, executeDefaultHandler = false) => {
if (typeof customErrorHandler !== "function" || typeof forceApplicationToQuit !== "boolean") {
console.log("setNativeExceptionHandler is called with wrong argument types.. first argument should be callback function and second argument is optional should be a boolean");
console.log("Not setting the native handler .. please fix setNativeExceptionHandler call");
return;
}
if (Platform.OS === "ios") {
ReactNativeExceptionHandler.setHandlerforNativeException(executeDefaultHandler, customErrorHandler);
} else {
ReactNativeExceptionHandler.setHandlerforNativeException(executeDefaultHandler, forceApplicationToQuit, customErrorHandler);
}
};
Android 提供了一个异常捕获接口 Thread.UncaughtExceptionHandler
用于捕获未被处理的异常。react-native-exception-handler 亦是基于此实现对 Android 端异常捕获的,其主要代码及分析如下所示:
@ReactMethod
public void setHandlerforNativeException(
final boolean executeOriginalUncaughtExceptionHandler,
final boolean forceToQuit,
Callback customHandler) {
callbackHolder = customHandler;
// 获取原有的异常处理器
originalHandler = Thread.getDefaultUncaughtExceptionHandler();
// 实例化异常处理器后,利用 setDefaultUncaughtExceptionHandler 重置异常处理器
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
// 重写 uncaughtException 方法,当程序中有未捕获的异常时,会调用该方法
@Override
public void uncaughtException(Thread thread, Throwable throwable) {
String stackTraceString = Log.getStackTraceString(throwable);
// 执行传入 JS 处理函数
callbackHolder.invoke(stackTraceString);
// 用于兼容自定义 Native Exception handler 的情况,即通过 MainApplication.java 中 实例化 NativeExceptionHandlerIfc 并重写其 handleNativeException 方法。
if (nativeExceptionHandler != null) {
nativeExceptionHandler.handleNativeException(thread, throwable, originalHandler);
} else {
// 获取 activity 并展示错误信息(一个弹窗,并提供重启和退出按钮)
activity = getCurrentActivity();
Intent i = new Intent();
i.setClass(activity, errorIntentTargetClass);
i.putExtra("stack_trace_string",stackTraceString);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
activity.startActivity(i);
activity.finish();
// 允许执行且已存在异常处理函数时,执行原异常处理函数
if (executeOriginalUncaughtExceptionHandler && originalHandler != null) {
originalHandler.uncaughtException(thread, throwable);
}
// 设置出现异常情况直接退出
if (forceToQuit) {
System.exit(0);
}
}
}
});
}
可以看到,主要做了四件事:
iOS 通常利用 NSSetUncaughtExceptionHandler
设置全部的异常处理器,当异常情况发生时,会执行其设置的异常处理器。react-native-exception-handler 也是基于此实现对 iOS 端异常的捕获,如下所示:
// ====================================
// REACT NATIVE MODULE EXPOSED METHODS
// ====================================
RCT_EXPORT_MODULE();
// METHOD TO INITIALIZE THE EXCEPTION HANDLER AND SET THE JS CALLBACK BLOCK
RCT_EXPORT_METHOD(setHandlerforNativeException:(BOOL)callPreviouslyDefinedHandler withCallback: (RCTResponseSenderBlock)callback)
{
// 1.设置异常处理函数用于执行 JS 回调;
jsErrorCallbackBlock = ^(NSException *exception, NSString *readeableException){
callback(@[readeableException]);
};
// 2.获取已存在的 native 异常处理器;
previousNativeErrorCallbackBlock = NSGetUncaughtExceptionHandler();
callPreviousNativeErrorCallbackBlock = callPreviouslyDefinedHandler;
// 3. 利用 NSSetUncaughtExceptionHandler 自定义异常处理器 HandleException;
NSSetUncaughtExceptionHandler(&HandleException);
signal(SIGABRT, SignalHandler);
signal(SIGILL, SignalHandler);
signal(SIGSEGV, SignalHandler);
signal(SIGFPE, SignalHandler);
signal(SIGBUS, SignalHandler);
//signal(SIGPIPE, SignalHandler);
//Removing SIGPIPE as per https://github.com/master-atul/react-native-exception-handler/issues/32
NSLog(@"REGISTERED RN EXCEPTION HANDLER");
}
上述代码主要做了三件事:
接下来,看下具体的 handleException
又做了些什么呢?
// ================================================================
// ACTUAL CUSTOM HANDLER called by the EXCEPTION AND SIGNAL HANDLER
// WHICH KEEPS THE APP RUNNING ON EXCEPTION
// ================================================================
- (void)handleException:(NSException *)exception
{
NSString * readeableError = [NSString stringWithFormat:NSLocalizedString(@"%@\n%@", nil),
[exception reason],
[[exception userInfo] objectForKey:RNUncaughtExceptionHandlerAddressesKey]];
dismissApp = false;
// 1.允许执行且已存在异常处理函数时,执行原异常处理函数
if (callPreviousNativeErrorCallbackBlock && previousNativeErrorCallbackBlock) {
previousNativeErrorCallbackBlock(exception);
}
// 2. 用于兼容自定义 Native Exception handler 的情况,可通过调用 replaceNativeExceptionHandlerBlock 实现
if(nativeErrorCallbackBlock != nil){
nativeErrorCallbackBlock(exception,readeableError);
}else{
defaultNativeErrorCallbackBlock(exception,readeableError);
}
// 3. 执行 js 异常处理函数
jsErrorCallbackBlock(exception,readeableError);
CFRunLoopRef runLoop = CFRunLoopGetCurrent();
CFArrayRef allModes = CFRunLoopCopyAllModes(runLoop);
while (!dismissApp)
{
long count = CFArrayGetCount(allModes);
long i = 0;
while(i < count){
NSString *mode = CFArrayGetValueAtIndex(allModes, i);
if(![mode isEqualToString:@"kCFRunLoopCommonModes"]){
CFRunLoopRunInMode((CFStringRef)mode, 0.001, false);
}
i++;
}
}
CFRelease(allModes);
NSSetUncaughtExceptionHandler(NULL);
signal(SIGABRT, SIG_DFL);
signal(SIGILL, SIG_DFL);
signal(SIGSEGV, SIG_DFL);
signal(SIGFPE, SIG_DFL);
signal(SIGBUS, SIG_DFL);
signal(SIGPIPE, SIG_DFL);
kill(getpid(), [[[exception userInfo] objectForKey:RNUncaughtExceptionHandlerSignalKey] intValue]);
}
通过对 react-native-exception-handler 源码的解读,可以知道,Android 和 iOS 分别利用 Thread.UncaughtExceptionHandler
和 NSSetUncaughtExceptionHandler
实现对应用程序的异常捕获。需要注意一点的是,当我们重置异常处理器时,需要考虑到其已存在的异常处理逻辑,避免将其直接覆盖,导致其他监测处理程序失效。
为了解决部分 UI 的 JavaScript 错误导致整个应用白屏或者崩溃的问题,React 16 引入了新的概念 —— Error Boundaries(错误边界)。
错误边界是一种 React 组件,这种组件可以捕获并打印发生在其子组件树任何位置的 JavaScript 错误,并且,它会渲染出备用 UI,而不是渲染那些崩溃了的子组件树。错误边界在渲染期间、生命周期方法和整个组件树的构造函数中捕获错误。
借用 static getDerivedStateFromError()
和 componentDidCatch()
两个生命周期实现错误边界,当抛出错误后,使用 static getDerivedStateFromError()
渲染备用 UI ,使用 componentDidCatch()
打印错误信息。
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
// 更新 state 使下一次渲染能够显示降级后的 UI
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// 你同样可以将错误日志上报给服务器
logErrorToMyService(error, errorInfo);
}
render() {
if (this.state.hasError) {
// 你可以自定义降级后的 UI 并渲染
return <Text>Something went wrong.</Text>;
}
return this.props.children;
}
}
错误边界仅可以捕获其子组件的错误,它无法捕获其自身的错误。错误边界无法捕获以下场景中产生的错误:
- 事件处理
- 异步代码(例如 setTimeout 或 requestAnimationFrame 回调函数)
- 服务端渲染
- 它自身抛出来的错误(并非它的子组件)
上文中提到,Error Boundaries 能捕获子组件生命周期函数中的异常,包括构造函数(constructor)和 render 函数。而无法捕获以下异常:
对于这些错误边界无法捕获的异常,在 web 中可以通过 window.onerror() 加载一个全局的error
事件处理函数用于自动收集错误报告。
那么 React Native 中是如何处理的呢?
React Native 是通过 JS Bridge 处理 JS 与 Native 的所有通信的,而 JS Bridge (BatchedBridge.js)是 MessageQueue.js 的实例。
'use strict';
const MessageQueue = require('MessageQueue');
const BatchedBridge = new MessageQueue();
Object.defineProperty(global, '__fbBatchedBridge', {
configurable: true,
value: BatchedBridge,
});
module.exports = BatchedBridge;
BatchedBridge 创建一个 MessageQueue 实例,并将它定义到全局变量中,以便给 JSCExecutor.cpp 中获取到。
MessageQueue 是 JS Context 和 Native Context 之间的唯一连接,如图,网络请求/响应、布局计算、渲染请求、用户交互、动画序列指令、Native 模块的调用和 I/O 的操作等,都要经过 MessageQueue 进行处理。开发中,可以通过调用 MessageQueue.spy 查看 JS <-> Native 之间的具体通信过程:
import MessageQueue from 'react-native/Libraries/BatchedBridge/MessageQueue';
MessageQueue.spy(true);
// -or-
// MessageQueue.spy((info) => console.log("I'm spying!", info));
MessageQueue.js 有三个作用:
查看 MessageQueue.js 构造函数:
constructor() {
this._lazyCallableModules = {};
this._queue = [[], [], [], 0];
this._successCallbacks = [];
this._failureCallbacks = [];
this._callID = 0;
this._lastFlush = 0;
this._eventLoopStartTime = new Date().getTime();
if (__DEV__) {
this._debugInfo = {};
this._remoteModuleTable = {};
this._remoteMethodTable = {};
}
(this: any).callFunctionReturnFlushedQueue = this.callFunctionReturnFlushedQueue.bind(
this,
);
(this: any).callFunctionReturnResultAndFlushedQueue = this.callFunctionReturnResultAndFlushedQueue.bind(
this,
);
(this: any).flushedQueue = this.flushedQueue.bind(this);
(this: any).invokeCallbackAndReturnFlushedQueue = this.invokeCallbackAndReturnFlushedQueue.bind(
this,
);
}
从 MessageQueue 源码中,可以看到,其定义了多个变量以及四个函数:
而以上四个函数的调用时机则是交给 c++ 端 NativeToJsBridge.cpp,具体的通信机制可参考文章
继续阅读上述四个函数的实现,可以看到都调用了 MessageQueue 的私有方法 __guard:
__guard(fn: () => void) {
if (this.__shouldPauseOnThrow()) {
fn();
} else {
try {
fn();
} catch (error) {
ErrorUtils.reportFatalError(error);
}
}
}
代码很简单,可以看到 __guard 会 根据 ___shouldPauseOnThrow 的返回值决定是否对 fn 进行 try catch 处理,当 __shouldPauseOnThrow 返回 false 时,且 fn 有异常时,则会执行 ErrorUtils.reportFatalError(error) 将错误上报。
// MessageQueue installs a global handler to catch all exceptions where JS users can register their own behavior
// This handler makes all exceptions to be propagated from inside MessageQueue rather than by the VM at their origin
// This makes stacktraces to be placed at MessageQueue rather than at where they were launched
// The parameter DebuggerInternal.shouldPauseOnThrow is used to check before catching all exceptions and
// can be configured by the VM or any Inspector
__shouldPauseOnThrow(): boolean {
return (
// $FlowFixMe
typeof DebuggerInternal !== 'undefined' &&
DebuggerInternal.shouldPauseOnThrow === true // eslint-disable-line no-undef
);
}
注释写的也很清晰,MessageQueue 设置了一个用于处理所有 JS 侧异常行为的处理器,并且可以通过设置 DebuggerInternal.shouldPauseOnThrow 来决定是否对异常进行捕获。
/**
* This is the error handler that is called when we encounter an exception
* when loading a module. This will report any errors encountered before
* ExceptionsManager is configured.
*/
let _globalHandler: ErrorHandler = function onError(
e: mixed,
isFatal: boolean,
) {
throw e;
};
/**
* The particular require runtime that we are using looks for a global
* `ErrorUtils` object and if it exists, then it requires modules with the
* error handler specified via ErrorUtils.setGlobalHandler by calling the
* require function with applyWithGuard. Since the require module is loaded
* before any of the modules, this ErrorUtils must be defined (and the handler
* set) globally before requiring anything.
*/
const ErrorUtils = {
setGlobalHandler(fun: ErrorHandler): void {
_globalHandler = fun;
},
getGlobalHandler(): ErrorHandler {
return _globalHandler;
},
reportError(error: mixed): void {
_globalHandler && _globalHandler(error, false);
},
reportFatalError(error: mixed): void {
// NOTE: This has an untyped call site in Metro.
_globalHandler && _globalHandler(error, true);
},
...
}
当调用 ErrorUtils.reportFatalError(error)
时,若存在 __globalHandler 则执行 _globalHandler,并将错误信息作为参数传入。同时,ErrorUtils 提供了函数 setGlobalHandler 用于重置 _globalHandler。
global.ErrorUtils.setGlobalHandler(function (err) {
consolo.log('global error: ', err);
});
那么 JS 的异常错误会被 MessageQueue 处理吗?我们可以开启 MessageQueue 看下其日志。
import React from 'react';
import {
View,
Text,
} from 'react-native';
import MessageQueue from 'react-native/Libraries/BatchedBridge/MessageQueue';
MessageQueue.spy(true); // -or- MessageQueue.spy((info) => console.log("I'm spying!", info));
global.ErrorUtils.setGlobalHandler(function (err) {
consolo.log('global error: ', err);
});
const App = () => {
const onPressButton = () => {
throw new Error('i am error');
};
return (
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<Text onPress={onPressButton}>按钮</Text>
</View>
);
};
当点击屏幕按钮时,可在控制台上看到如下信息:
可以看到,当 JS 抛出异常时,会被 ErrorUtils 捕获到,并执行通过 global.ErrorUtils.setGlobalHandler 设置的处理函数。
注意:0.64 版本开始,react-native pollfills 相关(包含 ErrorUtils 实现)已由 react-native/Libraries/polyfills
抽离为 @react-native/polyfills
除了上述提到的几种导致 APP crash 或者崩溃的异常处理之外,当我们使用 Promise 时,若抛出异常时未被 catch 捕获或在 catch 阶段再次抛出异常,此时会导致后续逻辑无法正常执行。
在 web 端,浏览器会自动追踪内存使用情况,通过垃圾回收机制处理这个 rejected Promise,并且提供unhandledrejection
事件进行监听。
window.addEventListener('unhandledrejection', event => ···);
那么,那么在 React Native 中是如何处理此类 Promise 异常的呢?
在 RN 中,当遇到未处理的 Promise 异常时,控制台输出黄色警告
而设备则表现为弹出黄屏:
查看源码 react-native/Libraries/Promise.js
可知,RN 默认在开发环境下,通过promise/setimmediate/rejection-tracking
去追踪 rejected 状态的Promise,并提供了onUnhandled
回调函数处理未进行处理的 rejected Promise:
if (__DEV__) {
require('promise/setimmediate/rejection-tracking').enable({
allRejections: true,
onUnhandled: (id, error) => {
const {message, stack} = error;
const warning =
`Possible Unhandled Promise Rejection (id: ${id}):\n` +
(message == null ? '' : `${message}\n`) +
(stack == null ? '' : stack);
console.warn(warning);
},
onHandled: (id) => {
const warning =
`Promise Rejection Handled (id: ${id})\n` +
'This means you can ignore any previous messages of the form ' +
`"Possible Unhandled Promise Rejection (id: ${id}):"`;
console.warn(warning);
},
});
}
其执行时机可以在rejection-tracking.js
中源码中找到:
//...
timeout: setTimeout(
onUnhandled.bind(null, promise._51),
// For reference errors and type errors, this almost always
// means the programmer made a mistake, so log them after just
// 100ms
// otherwise, wait 2 seconds to see if they get handled
matchWhitelist(err, DEFAULT_WHITELIST)
? 100
: 2000
),
//...
那么,我们是否可以仿照 RN 的处理,自定义 Promise 的异常处理逻辑呢?答案当然可以了,直接从源码中 copy 并将其中的 onUnhandled 替换为自己的异常处理逻辑即可,具体代码也可参考🔗。
本文从 React Native 应用异常监控出发,基于 react-native-exception-handler
分析了 Native 侧异常捕获的常用方案,然后介绍了 React 利用错误边界处理组件渲染异常的方式,接着通过分析 React Native 中 MessageQueue.js 的源码引出调用 global.ErrorUtils.setGlobalHandler
捕获并处理 JS 侧的全局未捕获异常,最后提供了捕获 Promise Rejection 的方法。
文章的最后,提下本人实现的 react-native-error-helper,与 react-native-exception-handler 相比,去除了 Native 异常处理捕获
,在 JS 异常捕获
的基础上,添加了用于捕获 React 异常
的 错误边界组件 ErrorBoundary 和高阶组件 withErrorBoundary(hook useErrorBoundary 计划中),期待您的 star⭐️。
实现简易版的 createStore 及 combineReducers。
https://xie7.netlify.app/blog/2020/12/27/redux-store_reducer
本文探讨下基于洋葱模型的中间件机制,及 redux applyMiddleware 的实现原理。
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.