前端宝典九:React Native从入门到精通实战

news2024/11/22 21:49:13

本文主要介绍

  1. React Native新旧框架对比
  2. React与React Native区别
  3. React Native性能优化

其中第3点React Native性能优化的拆包分包,是项目实战中使用过的,在这里整理分享,如果没有用过的小伙伴会觉得晦涩难懂,建议按照在实际项目中需要去实践,纸上得来终觉浅,绝知此事要躬行。

一、jsBridge

在React Native 中,web和native通过jsBridge进行通信,以js引擎或webview容器作为媒介,通过协定协议进行通信,实现native端和web端双向通信的一种机制。

web端调用native端主要的方法:
1、拦截webview请求的URL_Schema
如果符合我们自定义的URL_Schema,进行拦截,拿到相关操作,从而操作native;
如果不符合自定义的URL_Schema,直接转发,请求真正的服务。

2、向webview中注入JS API
通过webview提供的接口,App将Native的相关接口注入到JS的Content对象中,一般来说这个对象内的方法名与native内方法名相同,Web端就可以在全局下使用暴露的js对象,进而调用原生端方法。

二、React Native旧架构的问题

在这里插入图片描述

1、消息通知是异步的,对于连续的手势操作可能产生卡顿
2、JSON本身在性能上也存在问题,JSON parse、JSON stringfy类似于深拷贝,耗费内存
3、消息的通信,是在一个channel里,异步通信可能会阻塞。

在我们刚做过的一个大型RN项目中,遇到的直接问题有:

1、性能和问题反馈最大的组件

  1. ScrollView:一次渲染不做任何回收,启动性能慢,占用内存大

  2. FlatList:做了组件回收,快速滑动中容易出现白屏和卡顿

  3. Shadow层最终呈现到原生的UI是异步的,滑动太快会有大量的UI没加载

2、ui渲染阻塞

整体UI渲染太过于依赖JsBridge,容易出现阻塞影响整体UI体验

3、时间阻塞

事件阻塞在JsBridge,导致了长时间白屏的出现

三、React Native新架构的变化

在这里插入图片描述

新架构放弃了"桥"的概念,转而采用另一种通信机制:JavaScript 接口(JSI)。JSI 是一个接口,允许 JavaScript 对象持有对 C++ 的引用,反之亦然。

一旦一个对象拥有另一个对象的引用,它就可以直接调用该对象的方法。例如一个 C++ 对象现在可以直接调用一个 JavaScript 对象在 JavaScript 环境中执行一个方法,反之亦然。

这个想法可以带来几个好处:

  1. 同步执行:

现在可以同步执行那些本来就不应该是异步的函数。

  1. 并发:

可以在 JavaScript 中调用在不同线程上执行的函数。

  1. 更低的开销:

新架构不需要再对数据进行序列化/反序列化,因此可以避免序列化的开销。

  1. 代码共享:

通过引入 C++,现在有可能抽象出所有与平台无关的代码,并在平台之间轻松共享它。

  1. 类型安全:

为了确保 JS 可以正确调用 C++ 对象的方法,反之亦然,因此增加了一层自动生成的代码。这些代码必须通过 Flow 或 TypeScript 类型化的 JS 规范来生成。
这些优势是TurboModule系统的基础,也是进一步增强功能的跳板。例如,我们有可能开发出一种新的渲染器,它的速度更快,性能更强:Fabric及其Fabric 组件。

新架构使用了JSI、Hermes 引擎、 Fabric和TurboModules,旨在提高性能和开发效率,其中一个重要的改进就是允许 JS 和 Native 之间更直接的调用,减少了不必要的消息通信。

1. JSI

新的架构使用JSI屏蔽js引擎的差异,允许用不同的js引擎,比如Hermes
有了JSI之后,js能持有C++对象的引用,从而允许js和native之间直接调用,而不用通过跨线程消息通信。

  • 一个用 C++ 写成的轻量级框架

  • 实现JS引擎的互换

  • 通过JS直接调用Native

  • 减少不必要的线程通信,省去序列化和反序列化的成本,

  • 减轻了通信压力,提高了通信性能

2. Hermes 引擎

Hermes 是一个专为 React Native 优化的 JavaScript 引擎。它通过优化字节码执行和减少内存占用等方式来提高 JavaScript 代码的执行速度。

Hermes 对代码的预编译和优化减少了在运行时的解释开销,使得 JavaScript 代码的执行更加高效,这在一定程度上为更直接的交互提供了基础。

3.重构Bridge

新的Bridge被划分为Fabric和TurboModules

  • Fabric:负责管理UI
  • TurboModules:负责与Native交互

4. Fabric 渲染器

Fabric 是 React Native 的新渲染器。它重新设计了 React Native 的渲染体系结构,使渲染过程更加高效和可预测。

在 Fabric 中,JS 和 Native 之间的交互通过共享内存和直接函数调用等机制来实现,而不是完全依赖于消息传递。例如,对于一些常见的 UI 操作和交互,JS 端可以直接调用 Native 层的某些函数来触发相应的操作,而不需要经过复杂的消息序列化和传递过程。

Fabric 还采用了异步渲染和可中断渲染等技术,进一步提高了应用的响应性和性能。

例如,在处理 UI 动画时,以前可能需要通过消息将动画的参数从 JS 传递到 Native 层,然后 Native 层再进行动画的渲染。在新架构下,JS 可以直接调用 Native 层的动画函数,并传递参数,Native 层可以直接响应并开始动画渲染,减少了中间的消息传递环节,提高了动画的流畅性和响应速度。

5. TurboModules

实现Native模块按需加载,并在初始化后直接持有其引用,不再依靠消息通信来调用模块功能。
总的来说,React Native 新架构重新设计了 JS 和 Native 之间的交互方式,使得它们之间可以更直接地进行调用,从而提高了应用的性能和开发体验。

四、React 和 React Native 的概念区别

  • React:是一个用于构建用户界面的 JavaScript 库,主要用于在网页开发中构建高效、动态的用户界面,运行在浏览器环境中。
  • React Native:是基于 React 开发的用于构建原生移动应用的框架。它允许开发者使用 JavaScript 和 React 编写应用,然后将其编译为原生代码,运行在 iOS 和 Android 等移动平台上。

1、移动应用

 import {AppRegistry} from 'react-native';
 import App from './App';

 AppRegistry.registerComponent('YourApp', () => App);

在 React Native 中,入口文件通常会使用 AppRegistry.registerComponent 方法来注册应用的根组件。

2、移动组件

 import React from 'react';
 import { Text } from 'react-native';

 function MobileComponent() {
     return (
         <Text>This is a mobile component.</Text>
     );
 }

 export default MobileComponent;

可以看到,React Native 组件中使用的是 react-native 提供的组件(如 Text),而 React 网页组件通常使用 HTML 元素或自定义的 React 组件。

3、移动样式

在网页开发中,可以使用 CSS 文件来定义样式,并通过 className来应用样式。

 import React from 'react';
 import { StyleSheet, Text } from 'react-native';

 const styles = StyleSheet.create({
     mobileStyle: {
         color: 'blue',
         fontSize: 18
     }
 });

 function MobileComponentWithStyles() {
     return (
         <Text style={styles.mobileStyle}>Mobile styled component</Text>
     );
 }

 export default MobileComponentWithStyles;

在 React Native 中,使用 StyleSheet.create 方法来创建样式对象,并将其应用到组件的 style 属性上。

4、尺寸没有单位

所有的尺寸都是没有单位的,我们可以通过实现rem公共方法或者hooks在项目中使用
以下是一种在 React Native 中实现类似 REM 方案的示例方法:

1、创建一个工具函数来计算尺寸

import { Dimensions } from 'react-native';

// 定义一个基础尺寸,比如以设计稿宽度为 750px,基础尺寸为 100px
const baseWidth = 750;
const baseFontSize = 100;

const rem = (size) => {
    // 根据当前设备宽度与设计稿宽度的比例来计算
    const deviceWidth = Dimensions.get('window').width;
    const ratio = deviceWidth / baseWidth;
    return size * ratio;
};

在 React Native 项目中,当你导入 react-native 后,就可以访问 Dimensions 对象。它提供了有关设备屏幕尺寸和布局的信息。
2、 在组件中使用

import React from 'react';
import { View, Text, Dimensions } from 'react-native';
import { rem } from './yourUtilsFile';

const YourComponent = () => {
    return (
        <View style={{ padding: rem(16) }}>
            <Text style={{ fontSize: rem(20) }}>Your Text</Text>
        </View>
    );
};

export default YourComponent;

这里使用了 react-nativeDimensions API 来获取设备屏幕的宽度,然后根据设计稿的宽度和设定的基础字体大小来计算出相应的比例,再根据这个比例来动态调整尺寸。

请注意,这只是一个简单的示例,实际应用中你可能需要根据具体的设计需求和项目结构进行适当的调整和优化。

5、默认flex布局

  • 默认flex布局,因此不需要再设置{display: flex}
  • flex的默认方向是column

React Native 中使用 flexbox 规则来指定某个组件的子元素的布局。Flexbox 可以在不同屏幕尺寸上提供一致的布局结构。

一般来说,使用flexDirectionalignItemsjustifyContent三个样式属性就已经能满足大多数布局需求。
1、flexDirection
可以决定布局的主轴。子元素是应该沿着**水平轴(row)方向排列,还是沿着竖直轴(column)方向排列呢;默认值是竖直轴(column)**方向
2、alignItems
决定其子元素沿着次轴(与主轴垂直的轴,比如若主轴方向为row,则次轴方向为column)的排列方式。子元素是应该靠近次轴的起始端还是末尾段分布呢,亦或应该均匀分布
3、justifyContent
子元素沿着主轴的排列方式。子元素是应该靠近主轴的起始端还是末尾段分布,亦或应该均匀分布

详细的flex内容可在react native官网查询使用:
react-native-flex

6、独有的组件

在这里插入图片描述
React Native 控件很多,主要分为三类
(1)基础控件
在这里插入图片描述
(2)交互控件
在这里插入图片描述
(3)列表控件
在这里插入图片描述
在React Native 官网中对每一个控件都有详细的介绍:
React Native控件介绍

五、React Native性能优化

1、拆包、分包

React Native 页面的 JavaScript 代码包是热更新平台根据版本号进行下发的,每次有业务改动,我们都需要通过网络请求更新代码包。

因此,我们在对JavaScript 代码进行打包的时候,需要将包拆分成两个部分:

1.1 Common 部分,也就是 React Native 源码部分;

1.2 业务代码部分,也就是我们需要动态下载的部分

具体操作:

  • JavaScript 代码包中 React Native 源码相关的部分是不会发生变化的,所以我们不需要在每次业务包更新的时候都进行下发,在工程中内置一份就好了。
  • Common 包内置到工程中
  • 业务代码包进行动态下载
  • 利用 JSContext 环境,在进入载体页后在环境中先加载 Common 包,再加载业务代码包就可以完整的渲染出 React Native 页面

我这里使用的是基于Metro的分包方法,也是市面上主要使用的方法。这个拆包方法主要关注的是Metro工具在“序列化”阶段时调用的 createModuleIdFactory(path)方法和processModuleFilter(module)。createModuleIdFactory(path)是传入的模块绝对路径path,并为该模块返回一个唯一的Id。processModuleFilter(module)则可以实现对模块进行过滤,使业务模块的内容不会被写到common模块里。接下来分具体步骤和代码进行讲解。

(1)metro.common.js配置common包

request('react');
request('react-native');

(2)使用moduleId作为标识避免冲突

Metro 以这个 common.js 为入口文件,打一个 common bundle 包,同时要记录所有的公有模块的 moduleId,但是存在一个问题:每次启动 Metro 打包的时候,moduleId 都是从 0 开始自增,这样会导致不同的 JSBundle ID 重复,为了避免 id 重复,目前业内主流的做法是把模块的路径当作 moduleId(因为模块的路径基本上是固定且不冲突的),这样就解决了 id 冲突的问题。Metro 暴露了 createModuleIdFactory 这个函数,我们可以在这个函数里覆盖原来的自增逻辑,把公有模块的 moduleId 写入 txt文件

(3)在package.json里配置命令

"build:common:ios": "rimraf moduleIds_ios.txt && react-native bundle --entry-file common.js --platform ios --config metro.common.config.ios.js --dev false --assets-dest ./bundles/ios --bundle-output ./bundles/ios/common.ios.jsbundle",

(4)配置metro.common.config.js

const fs = require('fs');
const pathSep = require('path').sep;
 
 
function createModuleId(path) {
  const projectRootPath = __dirname;
  let moduleId = path.substr(projectRootPath.length + 1);
 
  let regExp = pathSep == '\\' ? new RegExp('\\\\', "gm") : new RegExp(pathSep, "gm");
  moduleId = moduleId.replace(regExp, '__');
  return moduleId;
}
 
module.exports = {
  transformer: {
    getTransformOptions: async () => ({
      transform: {
        experimentalImportSupport: false,
        inlineRequires: true,
      },
    }),
  },
  serializer: {
    createModuleIdFactory: function () {
      return function (path) {
        const moduleId = createModuleId(path)
 
        fs.appendFileSync('./moduleIds_ios.txt', `${moduleId}\n`);
        return moduleId;
      };
    },
  },
};

生成的moduleIds_ios.txt文件

common.js
node_modules__react__index.js
node_modules__react__cjs__react.production.min.js
node_modules__object-assign__index.js
node_modules__react-native__index.js
node_modules__react-native__Libraries__Components__AccessibilityInfo__AccessibilityInfo.ios.js
......

(5)配置metro.business.config.js

这个步骤的关键在于过滤公有模块的 moduleId(公有模块的Id已经记录在了上一步的moduleIds_ios.txt中),Metro 提供了 processModuleFilter 这个方法,借助它可以实现模块的过滤。这部分的处理主要写在了metro.business.config.ios.js文件中,写在哪个文件中主要取决于最上面package.json命令里指定的文件。

const fs = require('fs');
const pathSep = require('path').sep;
 
const moduleIds = fs.readFileSync('./moduleIds_ios.txt', 'utf8').toString().split('\n');
 
function createModuleId(path) {
  const projectRootPath = __dirname;
  let moduleId = path.substr(projectRootPath.length + 1);
 
  let regExp = pathSep == '\\' ? new RegExp('\\\\', "gm") : new RegExp(pathSep, "gm");
  moduleId = moduleId.replace(regExp, '__');
  return moduleId;
}
 
module.exports = {
  transformer: {
    getTransformOptions: async () => ({
      transform: {
        experimentalImportSupport: false,
        inlineRequires: true,
      },
    }),
  },
  serializer: {
    createModuleIdFactory: function () {
      return createModuleId;
    },
    processModuleFilter: function (modules) {
      const mouduleId = createModuleId(modules.path);
 
      if (modules.path == '__prelude__') {
        return false
      }
      if (mouduleId == 'node_modules__metro-runtime__src__polyfills__require.js') {
        return false
      }
 
      if (moduleIds.indexOf(mouduleId) < 0) {
        return true;
      }
      return false;
    },
    getPolyfills: function() {
      return []
    }
  },
  resolver: {
    sourceExts: ['jsx', 'js', 'ts', 'tsx', 'cjs', 'mjs'],
  },
};

(6)最后一步

原生app端需要配置,先读取common包再读取bussiness包

2、拆分业务包

在这里插入图片描述

通过拆包的方案,已经减少了动态下载的业务代码包的大小。但是还会存在部分业务非常庞大,拆包后业务代码包的大小依然很大的情况,依然会导致下载速度较慢,并且还会受网络情况的影响。

因此,我们可以再次针对业务代码包进行拆分

将一个业务代码包拆分为一个主包和多个子包的方式

在进入页面后优先请求主包的 JavaScript 代码资源,能够快速地渲染首屏页面,

紧接着用户点击某一个模块时,再继续下载对应模块的代码包并进行渲染,就能再进一步减少加载时间。

3、常规优化

React Native常规的优化方案和react的优化方案是重叠的,我专门写了一篇:
前端宝典之七:React性能优化实战精华篇

文中涵盖了React性能优化的三大方面公11项优化方案,基本涵盖了当今主流优化方案,大家可以前往查看。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2056221.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【等保测评】IIS模拟测评

一、身份鉴别 a)应对登录的用户进行身份标识和鉴别&#xff0c;身份标识具有唯一性&#xff0c;身份鉴别信息具有复杂度要求并定期更换&#xff1b; 结果记录&#xff1a;此项不适用&#xff0c;IIS中间件无管理控制台&#xff0c;身份鉴别功能依赖于所部署的服务器 b)应具有…

计算机毕业设计 心理健康服务系统 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

创新驱动发展,SiLM5768LCG-DG 支持输入输出同相逻辑 带互锁功能的六通道数字隔离器 科技稳健赋能,工业汽车应用安全升级!

SiLM5768Lx系列带互锁功能的六通道数字隔离器选型表: SiLM5768LCG-DG:支持输入输出同相逻辑 SiLM5768LNCG-DG:支持输入输出反相逻辑 数字隔离器广泛应用于工业、汽车和通信等领域&#xff0c;为系统中的强电和弱电电路提供了安全、可靠的电气隔离解决方案&#xff0c;确保强…

【TCP】核心机制:延时应答、捎带应答和面向字节流

文章目录 延时应答捎带应答面向字节流粘包问题方案一&#xff1a;指定分隔符方案二&#xff1a;指定数据的长度 TCP 报头首部长度保留&#xff08;6 位&#xff09;选项序号确认序号 延时应答 尽可能降低可靠传输带来的性能影响 提升性能>让滑动窗口变大 如果我们立即返回 …

Chat App 项目之解析(二)

Chat App 项目介绍与解析&#xff08;一&#xff09;-CSDN博客文章浏览阅读76次。Chat App 是一个实时聊天应用程序&#xff0c;旨在为用户提供一个简单、直观的聊天平台。该应用程序不仅支持普通用户的注册和登录&#xff0c;还提供了管理员登录功能&#xff0c;以便管理员可以…

Docker最佳实践进阶(二):Docker Compose部署SpringCloud微服务项目

大家好&#xff0c;在上篇文章中博主演示了Dockerfile常用的命令&#xff0c;以及如何利用Dockerfile构建镜像&#xff0c;生成容器服务&#xff0c;但是在实际应用环境中&#xff0c;特别是在微服务架构中&#xff0c;一个应用系统可能包含多个微服务&#xff0c;每个微服务可…

软数据与硬数据的深度解析:住宅代理如何优化数据抓取

引言 什么是软数据&#xff1f;有哪些类型&#xff1f; 什么是硬数据&#xff1f;有哪些类型&#xff1f; 软数据和硬数据的区别是什么&#xff1f; 如何收集软数据和硬数据&#xff1f; 如何优化抓取软数据和硬数据&#xff1f; 总结 引言 在大数据时代&#xff0c;企业…

Sanic 和 Go Echo 对比

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐&#xff1a;「storm…

【Python系列】 并发编程在数据处理中的应用

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

iOS 17.6.1版本重发,修复高级数据保护错误

今日&#xff0c;苹果没有带来iOS 17.6.2的更新&#xff0c;而是重新发布了iOS 17.6.1版本&#xff0c;本次升级版本号为21G101&#xff0c;高于第一版的21G93。距离初版发布相隔一周半时间。 在 iOS / iPadOS 17.6.1 的更新日志&#xff0c;苹果公司写道&#xff1a;“此更新包…

只用一个 HTML 元素可以写出多少形状?——伪元素篇(上)

只用一个 div 元素&#xff0c;我们已经通过四个篇章写了很多形状。 首先&#xff0c;我们通过对这个 div 的宽度与高度的直接控制&#xff0c;轻松写出矩形和正方形&#xff0c;并结合 transform 的 skew 方法写出了平行四边形与菱形。 其次&#xff0c;我们通过对边框的灵活…

iphone异常问题常用修复方法

作为智能手机的领军者&#xff0c;iPhone凭借其卓越的性能和稳定的系统赢得了全球用户的青睐。然而&#xff0c;就像任何电子设备一样&#xff0c;iPhone在使用过程中也难免会遇到各种异常问题&#xff0c;如卡顿、无法充电、应用闪退等。这些问题虽然令人头疼&#xff0c;但大…

linux之ELK

ELK概述 ELK是一套开源的日志分析系统&#xff0c;由elasticsearchlogstashKibana组成。 官网说明:https://www.elastic.co/cn/products 首先: 先一句话简单了解E,L,K这三个软件 elasticsearch: 分布式搜索引擎 logstash: 日志收集与过滤&#xff0c;输出给elasticsearch Kiban…

校园快递代取系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图详细视频演示技术栈系统测试为什么选择我官方认证玩家&#xff0c;服务很多代码文档&#xff0c;百分百好评&#xff0c;战绩可查&#xff01;&#xff01;入职于互联网大厂&#xff0c;可以交流&#xff0c;共同进步。有保障的售后 代码参考数据库参…

Windows搭建我的世界MC服务器 【Minecraft外网联机教程】

目录 ⛳️推荐 1. 搭建我的世界服务器 1.1 服务器安装java环境 1.2 配置服务端 1.3 创建我的世界服务器 2. 局域网联机测试 3. 安装cpolar内网穿透 4. 公网联机Minecraft 5. 配置固定远程联机端口地址 ⛳️推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通…

【启明智显技术分享】工业级HMI芯片Model系列GUI合成到项目中的指南

在工业自动化、智能终端HMI、车载仪表盘等领域&#xff0c;高性能的HMI&#xff08;人机界面&#xff09;芯片是不可或缺的核心组件。启明智显推出的Model系列&#xff08;如Model3C、Model3、Model4&#xff09;HMI芯片&#xff0c;以其卓越的性能和广泛的应用领域&#xff0c…

大模型学习应用 2:快速上手大模型基于langchain实现RAG检索应用

快速上手大模型基于langchain实现RAG检索应用 - 项目作业 目录 准备工作镜像选择算力选择安装包数据说明提示参考链接 Task1 申请 api 后&#xff0c;使用 langchain 导入大模型&#xff0c;并打印出大模型信息Task2 使用 langchian 加载数据&#xff0c;并把数据打印出来Task…

WebSocket 快速入门

WebSocket是什么 WebSocket 是基于 TCP 的一种新的应用层网络协议。它实现了浏览器与服务器全双工通信&#xff0c;即允许服务器主动发送信息给客户端。因此&#xff0c;在 WebSocket 中&#xff0c;浏览器和服务器只需要完成一次握手&#xff0c;两者之间就直接可以创建持久性…

Linux系统中安装Git(详细教程)

在Linux系统中安装Git&#xff0c;可以通过多种方式来实现&#xff0c;主要包括使用包管理器安装和从源代码编译安装。以下是详细的安装步骤&#xff1a; 一、使用包管理器安装&#xff08;不建议该方式&#xff09; 大多数Linux发行版都提供了包管理器&#xff0c;如Debian/…

90%的人都在用这7个图片转pdf技巧,转换速度很快!

图片怎么转换成pdf格式&#xff1f;图片和pdf格式是两种完全不一样的格式&#xff0c;但是如果想要将图片转换成pdf格式还是蛮容易的&#xff0c;常见的方法就有数十种了。 本文整理了几种常见的图片转pdf的方法&#xff0c;包括图片转pdf在线方法&#xff0c;有需要的朋友可以…