mirror of
https://github.com/NohamR/knowledge-kit.git
synced 2026-05-25 04:17:17 +00:00
update: image source
This commit is contained in:
@@ -747,6 +747,13 @@ didFinishLaunching:通过 UIApplicationDidFinishLaunchingNotification 拿到
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### 5. RN 启动时间监控
|
||||
跨端类的统计口径一般不会像 Native 那么严格,不会从 main 函数冷启动开始计算,启动还区分那么多 t1(进程创建到 main函数执行)、t2(main 函数执行到 didFinishLaunching 完成)、t3(didFinishLaunching 完成到首屏渲染完成)
|
||||
|
||||
开始时间点:一般需要 Native 配合,容器页面创建好就是开始时间点。比如 iOS 的 VC `viewDidLoad`、Android 的 `onActivityCreated`
|
||||
结束时间点:结束时间一般在跨端侧,比如 RN 中的组件挂载完成 componentDidMount 回调的时刻。
|
||||
|
||||
## 三、 CPU 使用率监控
|
||||
|
||||
### 1. CPU 架构
|
||||
@@ -4497,6 +4504,29 @@ typedef CFHTTPMessageRef (*APMURLResponseFetchHTTPResponse)(CFURLRef response);
|
||||
}
|
||||
```
|
||||
|
||||
### 3. RN 网络监控
|
||||
RN 中的 fetch 或者 axios 请求都是基于 XMLHttpRequest 包装的,所以要统计请求耗时,就可以统一收口在 XMLHttpRequest 侧,监听 open 事件、onreadystatechange 事件。open 事件记录请求开始的时间点,在 onreadystatechange 事件且 xht.readyState == 4 的时候记录结束点
|
||||
```
|
||||
let startTime = 0
|
||||
const originalOpen = XMLHttpRequest.prototype.open
|
||||
XMLHttpRequest.prototype.open(function(...args) {
|
||||
startTime = Date.now();
|
||||
|
||||
const xhr = this;
|
||||
const originalOnReady = xhr.prototype.onreadystatechange;
|
||||
xhr.prototype.onreadystatechange = function(...readyStateArgs) {
|
||||
if (xhr.readyState === 4) {
|
||||
const totalTimeSpan = Date.now() - startTime;
|
||||
EventUtil.save(totalTimeSpan, EventType.Request);
|
||||
}
|
||||
originalOnReady(...readyStateArgs);
|
||||
}
|
||||
originalOpen.apply(xhr, args);
|
||||
})
|
||||
|
||||
```
|
||||
|
||||
|
||||
## 七、 电量消耗
|
||||
|
||||
移动设备上电量一直是比较敏感的问题,如果用户在某款 App 的时候发现耗电量严重、手机发热严重,那么用户很大可能会马上卸载这款 App。所以需要在开发阶段关心耗电量问题。
|
||||
@@ -6324,7 +6354,43 @@ if (global?.HermesInternal?.hasPromise?.()) {
|
||||
}
|
||||
```
|
||||
|
||||
定义配置项 `defualtRejectionTrackingOptions`,其中的 onHnhandled 回调函数,该回调函数主要来处理未被 catch 的 Promise 错误。然后通过 `global?.HermesInternal?.hasPromise` 来判断 RN 是否采用 Hermes 引擎。如果采用 Hermes 引擎,则使用 `enablePromiseRejectionTracker` 方法来捕获未被 catch 的 Promise 错误。如果不是 Hermes 引擎则使用第三方 Promise 库中的 `rejection-tracking` 文件中的 enable 方法来捕获未被 catch 的 Promise 错误。
|
||||
首先,定义配置项 `defualtRejectionTrackingOptions`,其中的 onUnhandled 回调函数,该回调函数主要来处理未被 catch 的 Promise 错误。
|
||||
|
||||
然后通过 `global?.HermesInternal?.hasPromise` 来判断 RN 是否采用 Hermes 引擎:
|
||||
|
||||
- 如果采用 Hermes 引擎,则使用 `enablePromiseRejectionTracker` 方法来捕获未被 catch 的 Promise 错误
|
||||
- 如果不是 Hermes 引擎则使用第三方 Promise 库中的 `rejection-tracking` 文件中的 enable 方法来捕获未被 catch 的 Promise 错误
|
||||
|
||||
那如何捕获 RN 侧产生的 Promise 错误?
|
||||
做法和上面呼应。先判断是 Hermes 引擎还是非 Hermes 引擎,然后执行对应的异常处理函数,我们只需要注册好对应的异常发生后,数据收集方法即可
|
||||
```
|
||||
const customPromiseRejectionTrackingOptions = {
|
||||
allRejections: true,
|
||||
onUnhandled:(id:string, error: Error) => {
|
||||
let errorInfo = {
|
||||
errorMessage: error.message,
|
||||
errorStack: error.stack,
|
||||
type: ErrorType.Promise
|
||||
}
|
||||
ErrorUtils.save(errorInfo)
|
||||
},
|
||||
onHandled: (id:string) => {}
|
||||
}
|
||||
|
||||
if (global?.HermesInternal?.hasPromise?.()) {
|
||||
if (__DEV__) {
|
||||
global.HermesInternal?.enablePromiseRejectionTracker?.(
|
||||
defualtRejectionTrackingOptions,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (__DEV__) {
|
||||
require('promise/setimmediate/rejection-tracking').enable(
|
||||
defualtRejectionTrackingOptions,
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
###### 2.7.3.3 组件 render 错误
|
||||
|
||||
@@ -6351,6 +6417,44 @@ render 错误在本地就是红屏,线上可能没有反应或者白屏。如
|
||||
|
||||
所以可以通过异常边界组件捕获组件生命周期内的所有异常然后渲染兜底组件 ,防止 App crash,提高用户体验。也可引导用户反馈问题,方便问题的排查和修复
|
||||
|
||||
不过,React/React Native 只提供了类组件捕获 render 错误的方法,如果是函数组件,必须将其嵌套在类组件中才可以捕获其 render 错误。业界常见做法是将其封装成一个通用方法来给其他组件使用。比如 Sentry 就提供了 ErrorBoundary 组件和 withErrorBoundary 方法来帮助其他类组件和函数组件捕获 render 错误。
|
||||
|
||||
为了 cover 大多数组件渲染错误导致的 bug,代码如下:
|
||||
|
||||
```
|
||||
class ErrorBoundary extends React.component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = { hasError: false };
|
||||
}
|
||||
|
||||
static getDrivedStateFromError(error) {
|
||||
// 更新 state 使下一次渲染能够显示出降级后的 UI
|
||||
return { hasError: true };
|
||||
}
|
||||
|
||||
componentDidCatch(error, errorInfo) {
|
||||
// 将异常信息收集起来,然后按照数据上报策略做异常上报
|
||||
ErrorUtils.save(error, errorInfo);
|
||||
}
|
||||
|
||||
render () {
|
||||
if (this.state.hasError) {
|
||||
// 可以是安抚用户情绪的文案或者是统一的 feedback 页
|
||||
return <View>错误引导或者降级页</View>
|
||||
}
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
|
||||
// 然后在 App 入口处做统一收口
|
||||
<ErrorBoundary>
|
||||
<App />
|
||||
</ErrorBoundary>
|
||||
```
|
||||
如果 App 组件 render 没有报错,则会走 else 分支,也就是 this.props.children 正常渲染;如果 App 组件 render 出错了,则会触发 getDrivedStateFromError 回调,内部将 hasError 设置为 true,再次 render 的时候则展示降级后的页面。同时会触发 componentDidCatch 回调,并记录异常信息。
|
||||
|
||||
|
||||
至此 RN 的 crash 分为 2 种,分别是 js 逻辑错误、组件 js 错误,都已经被监控处理了。接下来就看看如何从工程化层面解决这些问题
|
||||
|
||||
##### 2.7.4 RN Crash 还原
|
||||
|
||||
Reference in New Issue
Block a user