聊聊React Native中怎么利用echarts画图表

 3626

React Native中怎么画图表?下面本篇文章就来给大家介绍一下怎么使用React Native+Echarts开发一个真实的电商数据统计页面,希望对大家有所帮助!


聊聊React Native中怎么利用echarts画图表


平时写图表相关需求,用得最多的图表库就是echarts。echarts 在 web 端的表现已经相当成熟,官方对小程序端也提供了解决方案,而在 RN 方面却没有相应支持。市面上搜到的,大多本质还是基于 webview 实现,而我更倾向于基于 RN 的方案,毕竟原生的体验会比 Web 的更好一些。

所以我们发布了@wuba/react-native-echarts 来满足需求。

接下来我将使用 @wuba/react-native-echarts来做一个实际项目中的应用,截图如下:


聊聊React Native中怎么利用echarts画图表


小提示

如果你已经有 APP 包,可以忽略前面的打包流程,直接跳到第 4 步。

试用的完整代码放在 github 上了,地址:github.com/iambool/Tes…


详细使用过程如下

1、开发环境搭建

本地搭好 RN 开发环境,搭建过程网上一抓一大把,就不赘述了。

2、准备 RN 工程

因为是试用,所以我用 expo 新初始化了一个 rn 工程,叫 TestApp。

  1. npx create-expo-app TestApp


聊聊React Native中怎么利用echarts画图表


3、build App 包

用命令行生成包 ios android app 包。这里 ios 建议用模拟器(不需要配证书),安卓我是连的真机

  1. yarn android
  2. yarn ios

生成包后,手机看到已经安装了这个应用,就代表成功啦。


聊聊React Native中怎么利用echarts画图表


4、 安装相关依赖

  1. yarn add @wuba/react-native-echarts echarts
  2. yarn add @shopify/react-native-skia
  3. yarn add react-native-svg

注意,如果你是在已有工程中安装,安装完成后要重新打个新包,不然缺少原生依赖会报错;


5、试用 Skia 模式

@wuba/react-native-echarts 支持两种渲染模式(Skia 和 Svg),先用 Skia 试一个简单的图表。大致分为这几个小步骤:

引入 echarts、图表组件等依赖

注册图表组件

创建图表实例,并设置图表的配置(option)

页面销毁时要记得同步销毁图表实例

具体代码如下:

  1. import { useRef, useEffect } from 'react';
  2. import { View } from 'react-native';
  3. /**
  4.  * 一、引入echarts依赖,这里先试下折线图
  5.  */
  6. import * as echarts from 'echarts/core';
  7. import { LineChart } from 'echarts/charts';
  8. import { GridComponent } from 'echarts/components';
  9. import { SVGRenderer, SkiaChart } from '@wuba/react-native-echarts';
  10.  
  11. /**
  12.  * 二、注册需要用到的组件
  13.  * SVGRenderer: 是必须注册的
  14.  * LineChart: 因为用的折线图,所以要引入LineChart(如果不知道该引入哪些组件,就直接看报错,报错说缺什么就加什么)
  15.  * GridComponent: 这个就是报错的时候提示,然后我加的hhh
  16.  */
  17. echarts.use([SVGRenderer, LineChart, GridComponent]);
  18.  
  19. export default () => {
  20.   const skiaRef = useRef(null); // Ref用于保存图表实例
  21.   useEffect(() => {
  22.     /**
  23.      * 四、图表配置
  24.      */
  25.     const option = {
  26.       xAxis: {
  27.         type: 'category',
  28.         data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
  29.       },
  30.       yAxis: {
  31.         type: 'value',
  32.       },
  33.       series: [
  34.         {
  35.           data: [150, 230, 224, 218, 135, 147, 260],
  36.           type: 'line',
  37.         },
  38.       ],
  39.     };
  40.     let chart;
  41.     if (skiaRef.current) {
  42.       /**
  43.        * 五、初始化图表,指定下宽高
  44.        */
  45.       chart = echarts.init(skiaRef.current, 'light', {
  46.         renderer: 'svg',
  47.         width: 400,
  48.         height: 400,
  49.       });
  50.       chart.setOption(option);
  51.     }
  52.     /**
  53.      * 六、页面关闭后要销毁图表实例
  54.      */
  55.     return () => chart?.dispose();
  56.   }, []);
  57.   return (
  58.     <View className='index'>
  59.       <SkiaChart ref={skiaRef} />
  60.     </View>
  61.   );
  62. };

写完摇一摇手机,reload bundle 包时出现了报错:

ERROR Invariant Violation: requireNativeComponent: "SkiaDomView" was not found in the UIManager.

google 了一下,说是需要降级解决。其实是要跟 expo 版本对应,在安装依赖的时候也会有类似这样的提示,安装提示的版本就可以了


聊聊React Native中怎么利用echarts画图表


于是按照提示做了版本降级:

  1. @shopify/react-native-skia@0.1.157
  2. react-native-svg@13.4.0

重新构建 app 后加载出来了,针不戳;(安卓遮住了点,看来应该自适应屏幕宽度)


聊聊React Native中怎么利用echarts画图表

6、试用 Svg 模式

写个复杂点的动态排序柱状图,试试 Svg 模式,给 Svg 和 Skia 做个对比,完整代码看这里

  1. // ...此处省略一些不重要的代码
  2.  
  3. // 注册需要用到的组件,BarChart-柱状图 LegendComponent-图例
  4. echarts.use([SVGRenderer, BarChart, LegendComponent, GridComponent]);
  5.  
  6. export default () => {
  7.   const skiaRef = useRef(null);
  8.   const svgRef = useRef(null);
  9.  
  10.   useEffect(() => {
  11.     // Skia模式
  12.     const skiaChartData = getData(); // 生成图表柱状图数据
  13.     let skiaChart;
  14.     let skiaInter;
  15.     if (skiaRef.current) {
  16.       skiaChart = echarts.init(skiaRef.current, 'light', {
  17.         renderer: 'svg',
  18.         width: 300,
  19.         height: 300,
  20.       });
  21.       skiaChart.setOption(getDefaultOption(skiaChartData));
  22.       setTimeout(function () {
  23.         run(skiaChart, skiaChartData);
  24.       }, 0);
  25.       skiaInter = setInterval(function () {
  26.         run(skiaChart, skiaChartData);
  27.       }, 3000);
  28.     }
  29.  
  30.     // Svg模式
  31.     const svgChartData = getData();
  32.     let svgChart;
  33.     let svgInter;
  34.     if (svgRef.current) {
  35.       svgChart = echarts.init(svgRef.current, 'light', {
  36.         renderer: 'svg',
  37.         width: 300,
  38.         height: 300,
  39.       });
  40.       svgChart.setOption(getDefaultOption(svgChartData));
  41.       setTimeout(function () {
  42.         run(svgChart, svgChartData);
  43.       }, 0);
  44.       svgInter = setInterval(function () {
  45.         run(svgChart, svgChartData);
  46.       }, 3000);
  47.     }
  48.  
  49.     return () => {
  50.       skiaChart?.dispose();
  51.       svgChart?.dispose();
  52.       // 定时器得清理掉,不然退出页面后还会运行
  53.       clearInterval(skiaInter);
  54.       clearInterval(svgInter);
  55.     };
  56.   }, []);
  57.   return (
  58.     <View>
  59.       <Text>skia如下</Text>
  60.       <SkiaChart ref={skiaRef} />
  61.       <Text>svg如下</Text>
  62.       <SvgChart ref={svgRef} />
  63.     </View>
  64.   );
  65. };

Skia 和 Svg 模式,肉眼看不出明显差别


iOSAndroid
聊聊React Native中怎么利用echarts画图表聊聊React Native中怎么利用echarts画图表


7、封装 Chart 组件

效果不错,不过每次使用都要把一堆东西引进去好烦,先简单封装下吧

  1. import { useRef, useEffect } from 'react';
  2. import * as echarts from 'echarts/core';
  3. import { BarChart, LineChart, PieChart } from 'echarts/charts';
  4. import {
  5.   DataZoomComponent,
  6.   GridComponent,
  7.   LegendComponent,
  8.   TitleComponent,
  9.   ToolboxComponent,
  10.   TooltipComponent,
  11. } from 'echarts/components';
  12. import {
  13.   SVGRenderer,
  14.   SvgChart as _SvgChart,
  15.   SkiaChart as _SkiaChart,
  16. } from '@wuba/react-native-echarts';
  17. import { Dimensions } from 'react-native';
  18.  
  19. // 注册需要用到的组件
  20. echarts.use([
  21.   DataZoomComponent,
  22.   SVGRenderer,
  23.   BarChart,
  24.   GridComponent,
  25.   LegendComponent,
  26.   ToolboxComponent,
  27.   TooltipComponent,
  28.   TitleComponent,
  29.   PieChart,
  30.   LineChart,
  31. ]);
  32.  
  33. // 图表默认宽高
  34. const CHART_WIDTH = Dimensions.get('screen').width; // 默认用手机屏幕宽度
  35. const CHART_HEIGHT = 300;
  36.  
  37. const Chart = ({
  38.   option,
  39.   onInit,
  40.   width = CHART_WIDTH,
  41.   height = CHART_HEIGHT,
  42.   ChartComponent,
  43. }) => {
  44.   const chartRef = useRef(null);
  45.  
  46.   useEffect(() => {
  47.     let chart;
  48.     if (chartRef.current) {
  49.       chart = echarts.init(chartRef.current, 'light', {
  50.         renderer: 'svg',
  51.         width,
  52.         height,
  53.       });
  54.       option && chart.setOption(option);
  55.       onInit?.(chart);
  56.     }
  57.     return () => chart?.dispose();
  58.   }, [option]);
  59.   return <ChartComponent ref={chartRef} />;
  60. };
  61.  
  62. const SkiaChart = (props) => <Chart {...props} ChartComponent={_SkiaChart} />;
  63. const SvgChart = (props) => <Chart {...props} ChartComponent={_SvgChart} />;
  64. // 对外只暴露这哥俩就行
  65. export { SkiaChart, SvgChart };


8、多个图表使用

封装好了,咱就写个多图表同时使用的页面看看效果。这里写了个“电商数据分析”页面,分别有折线图、柱状图、饼图。下方是主要代码,用的 svg 模式,详细代码见这里

  1. // 页面代码
  2. import { SkiaChart } from '../../components/Chart';
  3. import { ScrollView, Text, View } from 'react-native';
  4. import { StatusBar } from 'expo-status-bar';
  5. import { useCallback, useEffect, useState } from 'react';
  6. import {
  7.   defaultActual,
  8.   lineOption,
  9.   salesStatus,
  10.   salesVolume,
  11.   userAnaly,
  12.   getLineData,
  13. } from './contants';
  14. import styles from './styles';
  15. // 开启图表loading
  16. const showChartLoading = (chart) =>
  17.   chart.showLoading('default', {
  18.     maskColor: '#305d9e',
  19.   });
  20. // 关闭图表loading
  21. const hideChartLoading = (chart) => chart.hideLoading();
  22.  
  23. export default () => {
  24.   const [actual, setActual] = useState(defaultActual); // 记录实时数据
  25.  
  26.   useEffect(() => {
  27.     // 假设循环请求数据
  28.     const interv = setInterval(() => {
  29.       const newActual = [];
  30.       for (let it of actual) {
  31.         newActual.push({
  32.           ...it,
  33.           num: it.num + Math.floor((Math.random() * it.num) / 100),
  34.         });
  35.       }
  36.       setActual(newActual);
  37.     }, 200);
  38.     return () => clearInterval(interv);
  39.   }, [actual]);
  40.  
  41.   const onInitLineChart = useCallback((myChart) => {
  42.     showChartLoading(myChart);
  43.     // 模拟数据请求
  44.     setTimeout(() => {
  45.       myChart.setOption({
  46.         series: getLineData,
  47.       });
  48.       hideChartLoading(myChart);
  49.     }, 1000);
  50.   }, []);
  51.  
  52.   const onInitUserChart = useCallback((myChart) => {
  53.     // 模拟数据请求,跟onInitLineChart类似
  54.   }, []);
  55.   const onInitSaleChart = useCallback((myChart) => {
  56.     // 模拟数据请求,跟onInitLineChart类似
  57.   }, []);
  58.   const onInitStatusChart = useCallback((myChart) => {
  59.     // 模拟数据请求,跟onInitLineChart类似
  60.   }, []);
  61.  
  62.   const chartList = [
  63.     ['订单走势', lineOption, onInitLineChart],
  64.     ['用户统计', userAnaly, onInitUserChart],
  65.     ['各品类销售统计', salesVolume, onInitSaleChart],
  66.     ['订单状态统计', salesStatus, onInitStatusChart],
  67.   ];
  68.  
  69.   return (
  70.     <ScrollView style={styles.index}>
  71.       <StatusBar style='light' />
  72.       <View>
  73.         <View style={styles.index_panel_header}>
  74.           <Text style={styles.index_panel_title}>实时数据</Text>
  75.         </View>
  76.         <View style={styles.index_panel_content}>
  77.           {actual.map(({ title, num, unit }) => (
  78.             <View key={title} style={styles.sale_item}>
  79.               <View style={styles.sale_item_cell}>
  80.                 <Text style={styles.sale_item_text}>{title}</Text>
  81.               </View>
  82.               <View style={[styles.sale_item_cell, styles.num]}>
  83.                 <Text style={styles.sale_item_num}>{num}</Text>
  84.               </View>
  85.               <View style={[styles.sale_item_cell, styles.unit]}>
  86.                 <Text style={styles.sale_item_text}>{unit}</Text>
  87.               </View>
  88.             </View>
  89.           ))}
  90.         </View>
  91.       </View>
  92.       {chartList.map(([title, data, callback]) => (
  93.         <View key={title}>
  94.           <View style={styles.index_panel_header}>
  95.             <Text style={styles.index_panel_title}>{title}</Text>
  96.           </View>
  97.           <View style={styles.index_panel_content}>
  98.             <SkiaChart option={data} onInit={callback} />
  99.           </View>
  100.         </View>
  101.       ))}
  102.     </ScrollView>
  103.   );
  104. };

重新加载 bundle,看看效果图


iOSAndroid
聊聊React Native中怎么利用echarts画图表聊聊React Native中怎么利用echarts画图表


渲染出来后,iOS 上交互很丝滑,安卓上交互时感觉偶尔会有卡顿(不会是因为我手机太差吧…)。

再换 Skia 模式看看


聊聊React Native中怎么利用echarts画图表


emmm 虽然可以,但是好像中文不能正常显示,安卓上中文都没有显示,iOS 则是乱码。看了下文档,目前 skia 在安卓端还不支持中文,在 iOS 端可以通过设置字体为 'PingFang SC'显示中文,比如:

  1. const option = {
  2.   title: {
  3.     text: '我是中文',
  4.     textStyle: {
  5.       fontFamily: 'PingFang SC', // 指定字体类型
  6.     },
  7.   },
  8. };

但是每个显示中文的地方都要设置字体……那还是先用 svg 吧,我懒。


总结

使用了一段时间后,我总结了下:

支持度上,@wuba/react-native-echarts 除了 GL 系列、地图类图表还不支持外,其余类型的图表都支持,对于日常业务来说已经非常 enough 了。echarts 各种类型的图表实现,都可以在taro-playground上找到;

交互上,iOS 很丝滑,安卓有时会出现掉帧的情况;

性能上,还挺好的。

个人试了下,不是超大数据量就不会有什么问题,但是数据量太大的时候(比如画大数据量的热力图),渲染速度明显下降了很多,这是一个等待官方去优化的点。

另外页面内图表多的话,真机调试时加载速度会变慢,建议先用模拟器。

中文支持,Svg 模式支持中文,但 Skia 模式目前还不可以。


本文网址:https://www.zztuku.com/index.php/detail-13986.html
站长图库 - 聊聊React Native中怎么利用echarts画图表
申明:本文转载于《掘金社区》,如有侵犯,请 联系我们 删除。

评论(0)条

您还没有登录,请 登录 后发表评论!

提示:请勿发布广告垃圾评论,否则封号处理!!

    编辑推荐