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%;
          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框架地址:https://github.com/jiangzhenfei/react-native-demo

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM