zoukankan      html  css  js  c++  java
  • react-native中使用Echarts,自己使用WebView封装Echarts经验

    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,victory-native ( 展示效果丰富,极力推荐,名字有点随意,导致很多人不知道这个插件 )。但是如果需要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%;
               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={{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框架地址:https://github.com/jiangzhenfei/react-native-demo

  • 相关阅读:
    2018年12月9日 带小苗苗打针 函数2 前向引用 函数即变量
    2018年12月8日 函数变量与递归
    2018年12月7日 字符串格式化2 format与函数1
    2018年12月6日 字符串拼接 %的用法
    2018年11月29日 16点50分 小苗苗出生了
    2018年11月27日 分类与集合
    2018年11月26日 练习3
    2018年11月25日 练习2
    2018年11月24日 周末学习1 字典2
    2018年11月22日 字典 E18灯翼平整度 D&G is SB
  • 原文地址:https://www.cnblogs.com/zhenfei-jiang/p/9133301.html
Copyright © 2011-2022 走看看