1.工作中遇到的问题
我们在使用react-native肯定遇到过各种奇葩的问题,比如引入Echarts时候莫名报错,但是Echarts官网明显告诉我们可以懒加载的,这是因为基本上js大部分原生的组件库都不支持React-Native,直接引用都会报"undefined is not an object (evaluating ‘ua.match‘)" when importing an incompatible (browser) library.
2.经过调研得知react-native的WebView可以解决这个问题
有时候可以使用 WebView 弥补一些 ReactNative 内置的组件实现不了的东西,我们可以借助 HTML 来完成,毕竟 HTML 有丰富的工具可以用。例如要想在 ReactNative 里展示图表,原生自带的组件则没办法实现,其他的图表组件都是基于 react-native-svg 实现的,展示效果目前还不足人意,如果仅仅是展示,不在乎图表的各项数据和动态操作,这里也介绍几个小巧的图表插件,react-native-pathjs-charts,但是如果需要echarts或者highChart这些丰富的功能,这个时候 HTML 则有一大堆图表工具可以使用。
那我们接下就教大家如何一步一步封装自己的echarts组件。
3.封装echarts
假设我有一个本地的react-native目录如下
Demo/ ???android/ ???ios/ ???App.js ???index.js ???packege.json ??????src/ ???????components/ ???????????chart/ ???????view ???...
我们在src/components/chart目录下新建两个文件,一个叫chart.html,一个叫chartComponent.js
编写chart.html如下:
<!DOCTYPE html><html lang="en"><head> ???<meta charset="UTF-8"> ???<meta name="viewport" content="width=device-width, initial-scale=1.0"> ???<meta http-equiv="X-UA-Compatible" content="ie=edge"> ???<title>Document</title></head><body> ???<h1>测试文件</h1></body></html>
然后编写chartComponent.js
import React ,{Component} from ‘react‘;import { ????View, ????Text, ???ScrollView , ???WebView, ????Dimensions, ???StyleSheet, ???Platform} from ‘react-native‘;export default class SelfEChart extends Component { ?????render() { ???????return ( ???????????<WebView ?????????????????source={require(‘./chart.html‘)} //加载的html资源 ????????????/> ?????????) ???} ?} ?
接下来引入你的SelfEchart组件展示到你的页面中,如果出现刚才的测试文件,那么你的webview就是起效果了。
我们开始改造我们的chart.html使它正式成为平时我们写echarts的样子
<!DOCTYPE html><html lang="en"><head> ???<meta charset="UTF-8"> ???<meta name="viewport" content="width=device-width, initial-scale=1.0"> ???<meta http-equiv="X-UA-Compatible" content="ie=edge"> ???<title>Document</title> ???<style type="text/css"> ???????html,body { ?????????height: 100%; ?????????width: 100%; ?????????margin: 0; ?????????padding: 0; ???????} ???????#main { ?????????height: 100%; ???????} ?????</style></head><body> ???<div id="main"></div> ??<script> ??????/* ???echarts.min.js代码拷贝到这里 ??*/ ??</script></body></html>
在使用html加载好我们的html文件以及文件内部的echarts.min.js后需要初始化echarts插件,这个时候需要用到webview的 injectedJavaScript 属性,但是该属性必须是一段js的字符串,我们先将需要执行的js字符串编写好如下:
/*在WebView加载外部html后执行的js,主要是初始化echart图表*/function renderChart(props) {const height = `${props.height || 400}px`;const width = props.width ? `${props.width}px` : ‘auto‘;return ` ???document.getElementById(‘main‘).style.height = "${height}"; ???document.getElementById(‘main‘).style.width = "${width}"; ???var myChart = echarts.init(document.getElementById(‘main‘)); ???myChart.setOption(${toString(props.option)});
//这个自定义的message主要是监听webview组件传递来的数据变化的,假设图表数据变化,我们需要更新echart的option,使的
???????????//图表的变化不间断,可以实现实时监控的效果,以至于不闪屏
???window.document.addEventListener(‘message‘, function(e) { ???var option = JSON.parse(e.data); ???myChart.setOption(option); ???});`}
我们注意到上述代码有个toString方法,主要是将option对象转成字符串,因为JSON.stringify()方法本身会忽略函数属性,所以toString队JSON.stringify做了判断,代码如下
function toString(obj) {let result = JSON.stringify(obj, function(key, val) { ???????if (typeof val === ‘function‘) { ???????????return `~--demo--~${val}~--demo--~`; ???????} ???????return val; ???}); ???do { ???????result = result.replace(‘\"~--demo--~‘, ‘‘).replace(‘~--demo--~\"‘, ‘‘).replace(/\\n/g, ‘‘).replace(/\\\"/g,"\""); ???} while (result.indexOf(‘~--demo--~‘) >= 0); ???return result;}
所要的东西都准备好了,家下来开始在webview组件内引入在chart.html加载后需要执行的js代码了
export default class SelfEChart extends Component { ?render() {return ( ?<WebView ?source={require(‘./chart.html‘)} //加载的html资源 injectedJavaScript = {renderChart(this.props)} //在html内执行js代码,必须是字符串/> ?); ?} ?} ?
option当然是父组件传递过来的,我们也可以指定该图标显示的高度和宽度,以及一些其他的属性,还有就是webview还有一些其他的辅助属性,可以帮组我们优化组件的功能,接下来我们看看完整的charComponent.js的代码
import React ,{Component} from ‘react‘;import { ????View, ????Text, ???ScrollView , ???WebView, ????Dimensions, ???StyleSheet, ???Platform} from ‘react-native‘;/*获取设备的屏幕宽度和高度*/const {width, height} = Dimensions.get(‘window‘); ?function toString(obj) {let result = JSON.stringify(obj, function(key, val) { ???????if (typeof val === ‘function‘) { ???????????return `~--demo--~${val}~--demo--~`; ???????} ???????return val; ???}); ???do { ???????result = result.replace(‘\"~--demo--~‘, ‘‘).replace(‘~--demo--~\"‘, ‘‘).replace(/\\n/g, ‘‘).replace(/\\\"/g,"\""); ???} while (result.indexOf(‘~--demo--~‘) >= 0); ???return result;}/*在WebView加载外部html后执行的js,主要是初始化echart图表*/function renderChart(props) {const height = `${props.height || 400}px`;const width = props.width ? `${props.width}px` : ‘auto‘;return ` ???document.getElementById(‘main‘).style.height = "${height}"; ???document.getElementById(‘main‘).style.width = "${width}"; ???var myChart = echarts.init(document.getElementById(‘main‘)); ???myChart.setOption(${toString(props.option)}); ???window.document.addEventListener(‘message‘, function(e) { ???var option = JSON.parse(e.data); ???myChart.setOption(option); ???});`}/** * 通过WebView封装react-native不支持的插件,本次封装echarts * ?* 该组件需要的props * option ?必填,为ECharts配置属性option,详细配置参考官网EChartshttp://echarts.baidu.com/option.html#title * width ??不必填,为图表的宽度 * height ?不必填,为图表的高度 * ?* ?*/export default class SelfEChart extends Component { ?constructor(props) {super(props);this.setNewOption = this.setNewOption.bind(this);}componentWillReceiveProps(nextProps) {if(nextProps.option !== this.props.option) { ?this.refs.chart.reload();}}setNewOption(option) {
//postMessage会触发刚才js中的message监听方法,使得图表刷新option配置this.refs.chart.postMessage(JSON.stringify(option));}render() {/**在安卓下加载的资源跟ios不同,需要做兼容处理, * 就是将当下的chart.html拷贝到android/app/src/main/assets */ ???????????????const source = (Platform.OS == ‘ios‘) ? require(‘./chart.html‘) : { uri: ‘file:///android_asset/chart.html‘ } ?return ( ?<View style={{width:this.props.width || width,flex: 1, height: this.props.height || 400,}}> ?<WebView ?ref="chart"scrollEnabled = {false}style={{height: this.props.height || 400,backgroundColor: this.props.backgroundColor || ‘transparent‘}} ?source={source} //加载的html资源 scalesPageToFit={Platform.OS !== ‘ios‘}injectedJavaScript = {renderChart(this.props)} //在html内执行js代码,必须是字符串/> ?</View> ?); ?} ?} ?
到此为止,我们的echar组件已经封装好了,接下来我们看看怎么使用
import React ,{Component} from ‘react‘;import { View, Text,ScrollView } from ‘react-native‘;import SelfEChart from ‘../../components/chart/chart‘export default class ListScreen extends React.Component {componentDidMount(){ ???????/** ????????* 连续不间断刷新图标demo ????????*/ ???????setInterval(()=>{ ???????????let data = [5, 20, 36, 10, 10, 20].map((v)=>{ ???????????????return Math.random()*v ???????????}) ???????????var option = { ???????????????title: { ???????????????????text: ‘ECharts 入门示例‘ ???????????????}, ???????????????tooltip: {}, ???????????????legend: { ???????????????????data:[‘销量‘] ???????????????}, ???????????????xAxis: { ???????????????????data: ["衬衫","羊毛衫","雪纺衫","裤子","高跟鞋","袜子"] ???????????????}, ???????????????yAxis: {}, ???????????????series: [{ ???????????????????name: ‘销量‘, ???????????????????type: ‘bar‘, ???????????????????data: data ???????????????}] ???????????}; ???????????/**普通图表刷新通过改变state内部的option实现,缺点就是组件不断更新,导致图表组件重头开始渲染,没有连贯效果 ????????????* 在chartComponent里面封装的setNewOption方法, ????????????* 目的是为了调用myChart.setOption(option) ?????????????* 达到不抖屏不更新state刷新图表 ????????????* */ ???????????this.refs.charts.setNewOption(option) ???????},2000) ?????}render() {var option = { ???????????title: { ???????????????text: ‘ECharts 入门示例‘ ???????????}, ???????????tooltip: {}, ???????????legend: { ???????????????data:[‘销量‘] ???????????}, ???????????xAxis: { ???????????????data: ["衬衫","羊毛衫","雪纺衫","裤子","高跟鞋","袜子"] ???????????}, ???????????yAxis: {}, ???????????series: [{ ???????????????name: ‘销量‘, ???????????????type: ‘bar‘, ???????????????data: [5, 20, 36, 10, 10, 20] ???????????}] ???????};return (<ScrollView><View style={ { flex: 1, justifyContent: ‘center‘, alignItems: ‘center‘} }><Text>ListScreen!</Text><SelfEChart ref="charts"option={option}/></View></ScrollView>);}}
react-native中使用Echarts,自己使用WebView封装Echarts经验
原文地址:https://www.cnblogs.com/zhenfei-jiang/p/9133301.html