关于react-native:Luna你想要的-React-Native-调试工具

41次阅读

共计 5745 个字符,预计需要花费 15 分钟才能阅读完成。

本文作者 Shopee Digital Purchase 前端团队。

1. 背景

React Native(以下简称 RN)目前在 Shopee 前端团队失去大量利用。RN 尽管有很多劣势,然而其开发和调试流程与 Mobile Web 相比却不那么敌对,特地是在运行时的调试。

在开发模式下,尽管 RN 提供了官网的调试工具,然而相比纯前端的浏览器 Devtool,它的性能比拟弱;而非开发模式下,例如 Test 和 UAT 环境,RN 代码被打包成了一个 Bundle,这个时候官网调试工具也派不上用场了,这不仅对测试同学的问题复现产生妨碍,也使开发同学的问题定位变得更加艰难。

目前业界对于 RN 的调试尽管有工具,但或多或少都存在缺点(如下图所示),而且这些工具都是针对开发模式下的调试,对于打包后的生产环境的调试往往还是须要靠人肉去做,效率比拟低下。

因而一款可能帮忙在非开发环境定位问题的工具显得尤为重要,Luna 就此应运而生,本文将介绍这款 RN 工具关键技术的设计以及实现。

2. 性能介绍

先通过上面几张图理解一下 Luna:

从图片能够看进去,Luna 是一款 RN 的利用内调试工具,更偏差于解决生产环境调试的痛点

Luna 由一个橙色的触发按钮以及占据半屏的本体组成。本体蕴含了 Log、Network、Redux 和 Shopee 这四个版块,别离承载了日志记录、网络申请查看、Redux 树查看以及 Shopee 相干信息查看的性能。

其中,Log 和 Network 作为外围模块存在,而 Shopee 和 Redux 则是作为 Luna 提供的公共插件引入进来的。这种 Core-Plugin 模式就是 Luna 当初的运行模式:默认提供 Log、Network 等性能,也反对使用者编写自定义模块导入到 Luna。

四大版块的性能如下:

1)Log 版块

Log 版块接管了 console.log,将所有 Log 和未捕捉的谬误收集到 Luna,而后倒序展现进去。它反对按 Log 的类型进行过滤,也反对对 Log 进行含糊查找。如下图所示:

2)Network 版块

Network 版块收集了页面收回的申请信息,蕴含了申请状态、申请消耗时长、申请头、申请体以及响应头和响应体等等,用户能够不便地查看 API 申请。

3)Shopee 版块

Shopee 版块提供了一些 Shopee App 相干的性能,比方便捷的翻译文案切换、Cookies 查看、DataStore 存储查看与删除,还有用户 ID / 名字与设施零碎信息,以及版本号相干的信息查看。这些性能能够帮忙开发者更不便地调试利用,也便于 QA 更快地复现与定位 bug。

4)Redux 版块

Redux 版块展现了 Store(共享数据存储仓库)树,不便用户查看整个 Store 的状态。

3. 方案设计

3.1 整体设计

Luna 作为一个 monorepo 多包单仓库架构的我的项目,蕴含了 Core、Shopee Plugin 和 Redux Plugin 三个包模块。

其中,Core 外围模块蕴含了三大部分:Log 日志版块、Network 网络版块、Plugins 插件接入版块。下文将一一介绍每个模块的设计。

3.2 Core

Core 模块是 Luna 的外围模块,作为一个独自的 npm 包存在,提供了最根本的性能与插件接入的能力。Core 模块作为一个 Provider 嵌套在组件树的根部,承受业务代码,并将 Luna 插入进去。Core 应用 mobx 作为存储,保护了 Log 日志和 Network 记录的收集与展现、以及自定义插件的管制等等性能。

3.2.1 接入计划

Luna 的灵感源自于 Web 端开源的 vConsole 和 Eruda 这两款调试工具,但在 Luna 的接入计划抉择中,咱们碰到了在 Mobile Web 中从未碰到过的难题:在现代化 Web 开发中,不论是 Vue 还是 React,只有是单页利用,都会有一个用于挂载的根节点,以这个根节点为终点构建整个组件树。所以调试工具也只须要挂在某一根节点下,即可感知整个利用的状态:

而在 React Native 中,每个页面(View)都有本人的根节点(如下图所示),不同的页面之间并没有一个公共的先人节点,如果要保障每个页面都能拜访到 Luna,就得在每个页面都独自进行一次注入,不仅接入老本陡增,而且数据的保留也成了一大难题。

所以如何保障 Luna 在各个页面都能拜访到,并且还能保留不同页面数据、以及在产生谬误时不影响到 Luna,同时还要缩小页面接入的老本,成为了一个难题。那么 Luna 是怎么做的呢?

首先,Luna 将初始化与页面注册解耦,将 Luna.init 前置到了利用初始化时。这使得数据的收集与页面的注册拆散,保障了页面的切换不会导致数据的失落。

import Luna from "@shopee/luna";
Luna.init();

接着,Luna 利用 Shopee Plugin 重写了用于注册 Shopee RN Page 的办法,用新的组件包裹了传入的页面组件,同时将 Luna 也蕴含在外面,以 HOC 的模式将组件返回到外层。每一个应用这个注册页面的办法所注册的页面,都会把 Luna 主动蕴含在页面里,无需在每个页面手动引入 Luna,同时每个页面也都能够拜访到 Luna。

最初,Luna 还对传入的 Component 包裹了一层 ErrorBoundary,用于捕捉页面产生的运行时谬误,使得在页面产生谬误时 Luna 还能够拜访失去,并且能够在 Luna 里看到报错的信息。

3.2.2 Log

日志收集

Log 模块顾名思义,用于显示零碎和用户打印进去的日志。

Luna 劫持了全局变量 global.console,对各种类型的 Log 进行收集;同时,Luna 也劫持了 console.tron.log,收集开发时应用 Reactotron 打印进去的相干 Log;Luna 还劫持了 ErrorUtils,将未捕捉的谬误也一并收集到日志 Store 里。这三种类型的日志就是 Log 版块的数据起源。

Luna 以相似于中间件的做法劫持了全局的 console,劫持的过程中将其退出 Log store,而后执行其本来的执行函数,其次要代码如下所示:

export const overrideConsole = (consoleStore) => {
  const mixinType = [
    LOG_TYPE.LOG,
    LOG_TYPE.ERROR,
    LOG_TYPE.WARN,
    LOG_TYPE.DEBUG,
    LOG_TYPE.INFO,
  ];
  mixinType.forEach((type) => {
    // @ts-ignore
    const originConsoleFun = global.console[type];
    // @ts-ignore
    global.console[type] = (...params) => {consoleStore.addLog(params, type);
      originConsoleFun(...params);
    };
  });
};

日志展现

Log 日志蕴含了类型筛选、搜寻框和日志列表,因为 Luna 日志的类型泛滥、内容简单且始终处于一个动静更新的状态,所以很容易产生性能问题。所以在日志列表的展现局部,咱们做了大量的性能优化,次要蕴含两个局部,如下图所示:

1)嵌套类型展现优化

因为开源计划的树状展现库存在兼容性问题,咱们抉择本人编写树状展现组件,用于解决数据类型简单、数据量大带来的展现问题。它具备以下特点:

  • 反对多行文本的开展与膨胀,膨胀时只显示局部内容;
  • 对大数组与对象采取了懒加载计划,开展后只展现小于 100 行的内容,用户每点击一次残余局部(N),则展现后 N*100 条数据。这种做法防止了大数据显示所带来的性能问题;
  • 对一行的超长文本进行换行管制,放弃每个 Log 不超过三行,保障每屏的 Log 数量是受控的。

2)列表滑动性能优化

Luna 的 Log 并不是一次性加载结束,而是实时生成的。这使得在列表滑动过程中很可能同时有新的数据产生,而用户往往须要往下滑动,来寻找他们打印进去的 Log。所以 Luna 针对滑动的性能也做了一些特定优化:

  • Luna 采纳了 FlatList 来渲染 Log 列表,同时还在 Log 收集时隐式生成 ID,作用于 FlatList 的 keyExtractor,以此进步渲染效率;
  • 因为 Log 是动静生成的,这对 FlatList 的性能有着不小的影响。针对于此,Luna 将 Log 列表进行倒序显示,将最初产生的数据,也就是用户点击 Luna 时最关怀的数据放在 FlatList 的最后面,同时打印出工夫。这样就缩小了用户滑动的频率;
  • 咱们还打算对 Luna 进行更严格的日志分页加载,将显示和存储的 Log 列表离开,在滑动进行到底时,获取存储的 Log 列表的「下一页」,彻底保障动态数据产生过程中的列表滑动性能。

3.2.3 Network

Network 模块的数据收集源于 XMLHttpRequest。Luna 劫持了 React Native 的 XMLHttpRequest,重写了 open、send 和 setRequestHeader 办法,将每个申请,以及申请相干的字段都存储到 Network 列表里。因为 RN 的 Fetch 底层其实也是应用了 XHR,所以对 XHR 作劫持,能够达到全笼罩的成果。Network 劫持的次要代码如下所示:

export const overrideNetwork = (consoleStore) => {
  originOpen = XMLHttpRequest.prototype.open;
  const originSetHeader = XMLHttpRequest.prototype.setRequestHeader;
  XMLHttpRequest.prototype.open = function (...args) {this._xmlItem = { openData: args};
    this.addEventListener("load", () => {
      const xmlItem = this._xmlItem;
      const requestHeaders = this._requestHeaders;
      const endTime = new Date().getTime();
      const time = endTime - xmlItem.startTime;
      consoleStore.addNetworkLog({
        url: this.responseURL,
        method: xmlItem.openData[0],
        status: this.status,
        rspHeader: this.getAllResponseHeaders(),
        response: this.response,
        body: xmlItem.sendData,
      });
    });
    originOpen.apply(this, args);
  };
};

而在 Network 列表的展现计划中,咱们则退出了很多细节上的考量,比方:

  • 优先展现申请的 URL 的开端 Path;
  • 依据 response 的状态码的不同设置不同的底色;
  • 依据申请工夫的长短展现不同的工夫单位。

这些细节是在与日俱增的应用中进行的点滴改良,它们的确让 Luna 理论的用户体验更上了一层楼。

3.3 Plugins

3.3.1 插件机制

为什么须要插件机制?

在介绍什么是插件机制之前,你可能心田会有一个疑难,为什么会有插件机制呢?究其原因,Luna 在实现性能的时候,有一些性能是依靠于 Shopee 的 SDK 实现的;另一部分性能如 Redux 是非必选的,用户应用的状态治理框架可能是 mbox;为了放弃 Luna 外围模块的污浊,以及保留 Luna 对于非 Shopee 框架下的可拓展性,咱们解开了这些不必要的耦合,将 Shopee 模块与 Redux 模块革新成插件机制,供使用者按需援用。

什么是插件机制?

Luna 在外围模块之外,Core 还反对自定义插件。Luna 提供了两个第一方插件:Redux Plugin 和 Shopee Plugin,如果你有本人 App 的定制化需要,也能够十分不便地编写本人的插件,导入到 Luna 里,如下图所示。

3.3.2 官网插件

Luna 也采纳插件机制提供了两个官网插件:Redux Plugin 和 Shopee Plugin,这两个包作为独自的 npm 包供有须要的使用者引入。其中:

  • Redux Plugin 作为一个 Redux 中间件存在,通过 Store.getState 获取到 Redux 的状态,并将其显示在界面上。使用者能够很不便地查找到以后 Redux 的存储值。
  • Shopee Plugin 是依靠于 Shopee React Native SDK 的一个插件,专门针对于 Shopee App 内的我的项目开发。它通过 Shopee 的 SDK 提供了许多性能,这个插件次要面向 Shopee 外部的开发与测试同学,不便他们进行 Shopee App 内的调试。

3.3.2 开发自定义插件

除了官网插件之外,使用者还能够本人扩大插件,如何开发一个 Luna 的插件呢?Luna 的插件机制非常相似 Vue 的 install-use 机制,然而它省略了 Vue 插件的 install 步骤,只有须要提供组件内容注入到 Luna 提供的 use 办法就能够。所以其实步骤非常简单,只须要两步:

  • 编写你的组件,申明名称;
  • 将组件和名称导入到 Luna Core 的实例。

Luna 便能够辨认到你的组件,显示在主界面上了,接下来你就能够在插件里增加本人所需的性能。

4. 将来瞻望

Luna 现阶段曾经在 Shopee 的一些业务里稳固运行,也受到了应用它的开发和测试同学的统一好评。在将来咱们会朝两个大的指标致力:

1)自动化 Luna 接入

现阶段 Luna 的接入还是具备侵入性的人工代码接入,将来咱们打算通过部署平台,在部署的时候主动将 Luna 接入进去,并且只在开发、测试环境下失效,不仅能够实现 0 代码的接入老本,也不影响生产环境,还缩小了打包后的代码体积。

2)组件树状态查看器

在 Web 端简直每个开发者都会应用 React Devtool,而其中深受大家青睐的就是 Components 模块,它展现了开发时的整棵组件树,以及每个组件相干的 Props、State 和 Hooks。而在 React Native 端现时还没有一个相似 React Devtool 一样好用的开发调试工具,而对 RN 的状态查看又是开发者的一大痛点,因而 Luna 打算在将来减少对于组件树以及组件状态的查看器,届时在 RN 上同时查看 Log、Network 以及组件状态,将变得不再艰难。

正文完
 0