paint-brush
La mejor manera de hacer gráficos nativos de React en 2023por@zhiqingchen
4,000 lecturas
4,000 lecturas

La mejor manera de hacer gráficos nativos de React en 2023

por zhiqingchen13m2023/03/30
Read on Terminal Reader

Demasiado Largo; Para Leer

La biblioteca de gráficos más utilizada para escribir requisitos relacionados con gráficos es **charts**. El rendimiento de echarts en el sitio web es bastante maduro y la solución oficial se proporciona para el lado del applet, pero no hay soporte correspondiente en RN. En el mercado, la mayor parte de la búsqueda todavía se basa en la esencia de la implementación de la vista web, y prefiero el programa basado en RN.
featured image - La mejor manera de hacer gráficos nativos de React en 2023
zhiqingchen HackerNoon profile picture
0-item
1-item

La biblioteca de gráficos más utilizada para escribir requisitos relacionados con gráficos es echarts .


El rendimiento de echarts en el sitio web es bastante maduro y la solución oficial se proporciona para el lado del applet, pero no hay soporte correspondiente en RN. En el mercado, la mayor parte de la búsqueda todavía se basa en la esencia de la implementación de la vista web, y prefiero el programa basado en RN. Después de todo, la experiencia nativa será mejor que la Web.


Más tarde, encontré @wuba/react-native-echarts para satisfacer mis necesidades, así que pruébalo; los resultados no son malos. Para aquellos interesados en el principio de implementación, haga clic aquí

Consejos

  • Si ya tiene un paquete de APP, puede ignorar el proceso de empaquetado anterior y comenzar directamente desde el paso 4.
  • El código completo para la prueba está en GitHub en: https://github.com/iambool/TestApp

Los pasos para usarlo son los siguientes

Paso 1. Configuración del entorno de desarrollo

Este es el proceso de creación de un entorno de desarrollo de RN local que ya está disponible en Internet, por lo que no lo volveré a mencionar. Puedes buscarlo en Google :)

Paso 2. Creación de un proyecto RN

Como era una prueba, usé la exposición para inicializar nuevamente un proyecto rn llamado TestApp.

 npx create-expo-app TestApp 

crear aplicación de prueba

Paso 3. Creación de una aplicación en dispositivos móviles

Genere paquetes de aplicaciones para iOS y Android con una línea de comandos.


Se recomienda que iOS use el emulador (no es necesario que coincida con el certificado). Mientras estaba en Android, estaba conectado a la máquina real.


 yarn android yarn ios


Después de generar el paquete, la aplicación como la que se muestra a continuación ya está instalada en el teléfono, lo que significa que se ha realizado correctamente.

imagen

Paso 4. Instalar dependencias relacionadas

 yarn add @wuba/react-native-echarts echarts yarn add @shopify/react-native-skia yarn add react-native-svg

Nota: si está instalando en un proyecto existente, debe crear un nuevo paquete después de que se complete la instalación; de lo contrario, la falta de dependencias nativas informará un error.

Paso 5. Prueba el modelo Skia

@wuba/react-native-echarts admite dos modos de representación (Skia y Svg) , pruebe primero un gráfico simple con Skia.


Se divide en estos pequeños pasos:


  • Introducir echarts, componentes de gráficos y otras dependencias.
  • Registro de componentes del gráfico.
  • Cree una instancia de gráfico y establezca una opción.
  • Destrucción sincronizada de instancias de gráficos cuando se destruye la página.


El código específico es el siguiente:

 import { useRef, useEffect } from 'react'; import { View } from 'react-native'; /** * 1. Import the echarts dependency, this example first tries the line chart */ import * as echarts from 'echarts/core'; import { LineChart } from 'echarts/charts'; import { GridComponent } from 'echarts/components'; import { SVGRenderer, SkiaChart } from '@wuba/react-native-echarts'; /** * 2. Register the required components * SVGRenderer: it is required to register * LineChart: because we want to show the line chart, we have to import LineChart * - If you don't know which components to import, just look at the error report and add whatever the error says is missing * GridComponent: This is the prompt when the error is reported, and then I added the, ha ha */ echarts.use([SVGRenderer, LineChart, GridComponent]); export default () => { const skiaRef = useRef(null); // Ref for saving chart instances useEffect(() => { /** * 3. chart option */ const option = { xAxis: { type: 'category', data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'], }, yAxis: { type: 'value', }, series: [ { data: [150, 230, 224, 218, 135, 147, 260], type: 'line', }, ], }; let chart; if (skiaRef.current) { /** * 4. Initialize the chart, specifying the lower width and height */ chart = echarts.init(skiaRef.current, 'light', { renderer: 'svg', width: 400, height: 400, }); chart.setOption(option); } /** * 5. To destroy the chart instance after the page is closed */ return () => chart?.dispose(); }, []); return ( <View className='index'> <SkiaChart ref={skiaRef} /> </View> ); };


Después de escribir el código, agitar el teléfono y recargar el paquete, se informó un error:

ERROR Infracción invariable: requireNativeComponent: "SkiaDomView" no se encontró en UIManager.


Lo busqué en Google y dice que requiere una versión anterior . Debería corresponder a la versión expo, habrá un aviso similar al instalar la dependencia, instale la versión solicitada y estará bien.

advertencia

Así que seguí las instrucciones e hice una versión anterior:

 @shopify/[email protected] [email protected]

Se cargó después de reconstruir la aplicación, lo cual estuvo bien. (pero Android cubre el punto, parece que el ancho de la pantalla debería ser adaptable).

iOS

Androide

iOS

Androide

Paso 6. Prueba el modelo Svg

Escriba un gráfico de barras de clasificación dinámica más complejo con el modo Svg y compare Svg y Skia. El código completo está aquí

 // ...Some unimportant code is omitted here // Register the required components, such as BarChart and LegendComponent echarts.use([SVGRenderer, BarChart, LegendComponent, GridComponent]); export default () => { const skiaRef = useRef(null); const svgRef = useRef(null); useEffect(() => { // Skia mode const skiaChartData = getData(); // Generate chart bar data let skiaChart; let skiaInter; if (skiaRef.current) { skiaChart = echarts.init(skiaRef.current, 'light', { renderer: 'svg', width: 300, height: 300, }); skiaChart.setOption(getDefaultOption(skiaChartData)); setTimeout(function () { run(skiaChart, skiaChartData); }, 0); skiaInter = setInterval(function () { run(skiaChart, skiaChartData); }, 3000); } // Svg mode const svgChartData = getData(); let svgChart; let svgInter; if (svgRef.current) { svgChart = echarts.init(svgRef.current, 'light', { renderer: 'svg', width: 300, height: 300, }); svgChart.setOption(getDefaultOption(svgChartData)); setTimeout(function () { run(svgChart, svgChartData); }, 0); svgInter = setInterval(function () { run(svgChart, svgChartData); }, 3000); } return () => { skiaChart?.dispose(); svgChart?.dispose(); // The timer has to be cleaned up, otherwise it will still run after exiting the page clearInterval(skiaInter); clearInterval(svgInter); }; }, []); return ( <View> <Text>skia</Text> <SkiaChart ref={skiaRef} /> <Text>svg</Text> <SvgChart ref={svgRef} /> </View> ); };


No puedo ver la diferencia entre estos dos modos con mis propios ojos.

iOS

Androide

imagen

imagen

Paso 7. Componentes del gráfico de ajuste

Hasta ahora el efecto era bastante bueno, pero cada vez que usaba un montón de cosas para importar, me molestaba.


Resumámoslo simplemente:

 import { useRef, useEffect } from 'react'; import * as echarts from 'echarts/core'; import { BarChart, LineChart, PieChart } from 'echarts/charts'; import { DataZoomComponent, GridComponent, LegendComponent, TitleComponent, ToolboxComponent, TooltipComponent, } from 'echarts/components'; import { SVGRenderer, SvgChart as _SvgChart, SkiaChart as _SkiaChart, } from '@wuba/react-native-echarts'; import { Dimensions } from 'react-native'; // Register the required components echarts.use([ DataZoomComponent, SVGRenderer, BarChart, GridComponent, LegendComponent, ToolboxComponent, TooltipComponent, TitleComponent, PieChart, LineChart, ]); // Default width and height of the chart const CHART_WIDTH = Dimensions.get('screen').width; // Default with the phone screen width const CHART_HEIGHT = 300; const Chart = ({ option, onInit, width = CHART_WIDTH, height = CHART_HEIGHT, ChartComponent, }) => { const chartRef = useRef(null); useEffect(() => { let chart; if (chartRef.current) { chart = echarts.init(chartRef.current, 'light', { renderer: 'svg', width, height, }); option && chart.setOption(option); onInit?.(chart); } return () => chart?.dispose(); }, [option]); return <ChartComponent ref={chartRef} />; }; const SkiaChart = (props) => <Chart {...props} ChartComponent={_SkiaChart} />; const SvgChart = (props) => <Chart {...props} ChartComponent={_SvgChart} />; // Just export these two guys export { SkiaChart, SvgChart };

Paso 8. Usar múltiples gráficos

Una vez que esté envuelto, escribamos una página con múltiples gráficos y veamos cómo funciona. Aquí hay una página para el "análisis de datos de comercio electrónico", que incluye un gráfico de líneas, un gráfico de barras y un gráfico circular. A continuación se muestra el código principal escrito con el modo svg, haga clic aquí para ver el código detallado.


 import { SkiaChart } from '../../components/Chart'; import { ScrollView, Text, View } from 'react-native'; import { StatusBar } from 'expo-status-bar'; import { useCallback, useEffect, useState } from 'react'; import { defaultActual, lineOption, salesStatus, salesVolume, userAnaly, getLineData, } from './contants'; import styles from './styles'; // Turn on chart loading const showChartLoading = (chart) => chart.showLoading('default', { maskColor: '#305d9e', }); // Close chart loading const hideChartLoading = (chart) => chart.hideLoading(); export default () => { const [actual, setActual] = useState(defaultActual); // Recording real-time data useEffect(() => { // Assuming a recurring request for data const interv = setInterval(() => { const newActual = []; for (let it of actual) { newActual.push({ ...it, num: it.num + Math.floor((Math.random() * it.num) / 100), }); } setActual(newActual); }, 200); return () => clearInterval(interv); }, [actual]); const onInitLineChart = useCallback((myChart) => { showChartLoading(myChart); // Simulation of data requests setTimeout(() => { myChart.setOption({ series: getLineData, }); hideChartLoading(myChart); }, 1000); }, []); const onInitUserChart = useCallback((myChart) => { // Simulate data request, similar to onInitLineChart }, []); const onInitSaleChart = useCallback((myChart) => { // Simulate data request, similar to onInitLineChart }, []); const onInitStatusChart = useCallback((myChart) => { // Simulate data request, similar to onInitLineChart }, []); const chartList = [ ['订单走势', lineOption, onInitLineChart], ['用户统计', userAnaly, onInitUserChart], ['各品类销售统计', salesVolume, onInitSaleChart], ['订单状态统计', salesStatus, onInitStatusChart], ]; return ( <ScrollView style={styles.index}> <StatusBar style='light' /> <View> <View style={styles.index_panel_header}> <Text style={styles.index_panel_title}>实时数据</Text> </View> <View style={styles.index_panel_content}> {actual.map(({ title, num, unit }) => ( <View key={title} style={styles.sale_item}> <View style={styles.sale_item_cell}> <Text style={styles.sale_item_text}>{title}</Text> </View> <View style={[styles.sale_item_cell, styles.num]}> <Text style={styles.sale_item_num}>{num}</Text> </View> <View style={[styles.sale_item_cell, styles.unit]}> <Text style={styles.sale_item_text}>{unit}</Text> </View> </View> ))} </View> </View> {chartList.map(([title, data, callback]) => ( <View key={title}> <View style={styles.index_panel_header}> <Text style={styles.index_panel_title}>{title}</Text> </View> <View style={styles.index_panel_content}> <SkiaChart option={data} onInit={callback} /> </View> </View> ))} </ScrollView> ); };


Vuelva a cargar el paquete y vea el resultado.

iOS

Androide

imagen

imagen

Después de renderizar, la interacción en iOS es muy fluida, mientras que la interacción en Android se siente ocasionalmente lenta (no porque mi teléfono sea demasiado malo, ¿verdad?...)


Probando el modo Skia de nuevo...

imagen

Bueno, aunque puede, parece que el chino no se puede mostrar correctamente, el chino de Android no se muestra y el código de iOS es un desastre.


Después de leer la documentación, skia actualmente no admite chino en el lado de Android,


Podemos mostrar chino en iOS configurando la fuente en 'PingFang SC', por ejemplo:

 const option = { title: { text: '我是中文', textStyle: { fontFamily: 'PingFang SC', // setting the font type }, }, };

Pero cada lugar que muestra chino tiene que configurar la fuente... eso o usar Svg primero, soy perezoso.

Resumen

Después de usarlo por un tiempo, resumí lo siguiente:

  • En términos de soporte, @wuba/react-native-echarts admite todos los tipos de gráficos, excepto las series GL y los gráficos de mapas que aún no son compatibles, lo cual es suficiente para el trabajo diario. El código para implementar los distintos gráficos en echarts se puede encontrar en taro-playground .

  • Interacción, iOS es muy suave como la seda, Android a veces hay casos de caídas de cuadros.

  • Rendimiento: el rendimiento se informa oficialmente como mejor que otras soluciones.

    • Lo probé, una cantidad no muy grande de datos no tendrá ningún problema, pero cuando la cantidad de datos es demasiado grande (como dibujar una gran cantidad de mapa de calor de datos), la velocidad de renderizado disminuyó significativamente, lo cual es un punto esperando que el oficial optimice.
    • Además, si hay muchos gráficos en la página, la velocidad de carga será lenta al depurar en la máquina real, por lo que se recomienda utilizar primero el simulador.
  • Compatible con chino, el modo Svg es compatible con chino, pero el modo Skia aún no está disponible.


Lo anterior es solo una vista personal, ¡cualquier pregunta y comentario son bienvenidos a continuación!