2016-09-02 14:05:10 sinat_17775997 阅读数 0

React作为目前最流行的前端框架之一,其受欢迎程度不容小觑,从这门框架上我们可以学到许多其他前端框架所缺失的东西,也是其创新性所在的地方,比如虚拟DOM、JSX等。那么接下来我们就来学习一下这门框架是如何构建起一个单页应用的。

前言

首先在学习这门框架前,你需要对以下知识有所了解:

  1. 原生JS基础

  2. CSS基础

  3. npm包管理基础

  4. webpack构建项目基础

  5. ES6规范

以上五个知识点也是目前学习其他前端框架所必须了解的前置任务。
JS和CSS就不多说了,npm是目前最提倡也是占据主导地位的包管理工具,还在用bower或者其他工具的童鞋可以考虑下了。而webpack作为新一代打包工具,已经在前端打包工具中独占鳌头,和Browserify相比也有很大优势。至于ES6规范虽然现在主流浏览器还不兼容,但可以使用babel等转换器进行转换。

结合其他的一些主流前端框架,我个人认为构建单页应用有这样三个基本的东西:组件、路由、状态管理。那么接下来我就基于这三者来介绍React,当然其中会穿插一些额外的知识点。

 

组件

React的组件撰写和调用主要依赖于ES6的模块化和JSX的语法,以下是一个例子:

复制代码
// main.js
import React from 'react'
import { render } from 'react-dom'
import MyComponent from './component.js'
import './main.css'


// 主组件
class MyDemo extends React.Component {
    render() {
        return (
            <div className="box">
                <MyComponent />
            </div>
        )
    }
}

render((
    <MyDemo />
), document.getElementById('app'))


// component.js

// 子组件
import React from 'react'

export default class MyComponent extends React.Component {
    render() {
        return (
            <div>
                <p>这是一个组件!</p>
            </div>
        )
    }
}


// main.css
.box {
    width: 100%
}
复制代码

相比Vue.js框架,我个人认为React的组件编写方式还是没有Vue来的舒服,组件的css样式还是脱离在组件外部的,维护起来也不是很方便。想了解Vue组件编写方式的可以看一下我之前写的一篇文章《浅谈Vue.js》

从这个例子中我们就可以看到React的虚拟DOM和JSX的特性了。相比其他框架,React的虚拟DOM不仅可以提升页面的性能,同时还可以防止XSS攻击等。关于虚拟DOM的具体原理这里不作介绍,有兴趣的童鞋可以参考
http://www.alloyteam.com/2015/10/react-v...

至于JSX语法则是JS的一种语法糖,我们可以通过这种语法糖来便捷实现一些功能,这里JSX 把类 XML 的语法转成纯粹 JavaScript,XML 元素、属性和子节点被转换成 React.createElement 的参数。类似的JS语法糖还有TypeScript等。

 

路由

前端路由机制是目前构建单页应用(SPA)最重要的一环之一。通过前端路由我们可以优化用户体验,不需要每次都从服务器获取全部数据,从而快速将页面展现给用户。

React路由依赖于React Router。React Router 保持 UI 与 URL 同步。它拥有简单的 API 与强大的功能例如代码缓冲加载、动态路由匹配、以及建立正确的位置过渡处理。

下面是一个React路由的例子:

复制代码
import React, { Component } from 'react'
import { render } from 'react-dom'
import { Router, Route, IndexRoute, Link, browserHistory } from 'react-router'

const ACTIVE = { color: 'red' }

class App extends Component {
    render() {
        return (
            <div>
                <h1>我的路由</h1>
                <ul>
                    <li><Link to="/" activeStyle={ACTIVE}>首页</Link></li>
                    <li><Link to="/users" activeStyle={ACTIVE}>用户页</Link></li>
                </ul>
                {this.props.children}
            </div>
        )
    }
}

class Index extends React.Component {
    render() {
        return (
            <div>
                <h2>Index!</h2>
            </div>
        )
    }
}

class Users extends React.Component {
    render() {
        return (
            <div>
                <h2>Users</h2>
            </div>
        )
    }
}

render((
    <Router history={browserHistory}>
        <Route path="/" component={App}>
            <IndexRoute component={Index}/>
            <Route path="users" component={Users}></Route>
        </Route>
    </Router>
), document.getElementById('app'))
复制代码

这里只列出了React的一种路由写法。相比其他框架,React路由的语法更加通俗易懂。关于React Router的详细介绍请参照官方文档:http://react-guide.github.io/react-route...

 

状态管理

状态管理不是单页应用必须的,使用它能够帮助我们统一管理各个状态的变更,使整个项目流程清晰可维护。React实现状态管理可以使用官方推荐的Redux。
Redux使用的是严格的单向数据流。整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中。因为Redux状态管理的知识点繁多,所有我额外写了一篇文章,详情请戳这里:《Redux 状态管理方法与实例》

 

项目实例

这里我用React写了一个单页网站,页面如下:

 

这是一个基于React和Antd的实例,全部源码我已经上传至我的github,地址为:https://github.com/luozhihao/react-antd-...,这里只为不懂如何用React构建单页应用的童鞋作参考。

Antd是蚂蚁金服的一款基于React的开源UI组件库,其官网为:http://ant.design/

 

Fetch

因为上面的实例中我用到了Fetch来进行Ajax交互,所以这里简单介绍下Fetch。
我们可以把Fetch作为下一代Ajax技术,它采用了目前流行的 Promise 方式处理。利用Fetch我们可以这样写Ajax进行数据交互:

复制代码
// 获取数据方法
    fetchFn = () => {
        fetch('../../data.json')
            .then((res) => { console.log(res.status);return res.json() })
            .then((data) => { this.setState({lists:data.listData}) })
            .catch((e) => { console.log(e.message) })
    }
复制代码

这里有一篇介绍Fetch的文章写的不错,推荐给大家《传统 Ajax 已死,Fetch 永生》

 

结语

还是那句话,学习一门框架最重要的并不是学习它的技术,而是学习其带来的解决问题的思路。通过React这一门框架的学习,你可以从它独特的新特性中发掘一种新的思维模式。只有思维层面得到了扩展,你才能在前端的海洋里自由翱翔。

 

原创文章,转载请注明来自一个萝卜一个坑 -博客园[http://www.cnblogs.com/luozhihao] 

本文地址:http://www.cnblogs.com/luozhihao/p/5579786.html

本文同步发表于:https://segmentfault.com/a/1190000005703694

2020-02-28 10:32:22 weixin_38404899 阅读数 142

 

需求:

1.左上角显示总业务数,上中间显示总业务数里面的告警数;

2.下面tab展示所有告警数对应的图表

组件使用的是antd,图表使用的是antv

 父组件 Demo1.js

import React, { Component } from 'react'
import './relevance.css'
import Relevancenum from './Relevancenum'
import Tabschange from './Tabschange'

export default class Demo1 extends Component {
    constructor(props) {
        super(props);
        this.state={
            data_num:{
                num1:45,
                num2:40
            }
        }
    }
    render() {
        return (
            <div className='App'>
                <div className='app_relevance'>
                    <h4>应用关联业务</h4>
                    // 总数据
                    <Relevancenum data_num={this.state.data_num}/>
                    //tab切换和图表 
                    <Tabschange/>
                </div>
            </div>
        )
    }
}

relevance.css

.App{
    width: 100%;  
    background-color: #fafafa;
    padding-top: 30px;
}
.app_relevance{
    width: 90%;
    height: 430px;
    background-color: #fff;
    box-shadow: 0px 0px 20px #ccc;
    margin:0 auto;
    border-radius: 10px;
}
.app_relevance h4{
    font-weight: 500;
    font-size: 18px;
    height: 27px;
    margin-left: 20px;
    padding-top: 20px;
    padding-bottom: 20px;
}

数字组件 Relevancenum.js

import React, { Component } from 'react'
import './Relevancenum.css'

export default class Relevancenum extends Component {
    render() {
        // console.log(this.props)
        return (
            <div className='Relevancenum'>
                <div className='num_content'>
                    <p>应用关联业务数</p>
                    <h2>{this.props.data_num.num1}</h2>
                </div>
                <div className='num_content'>
                    <p>当前警告业务数</p>
                    <h2>{this.props.data_num.num2}</h2>
                </div>
                <div className='num_content num_content3' >
                    <p>最新警告业务</p>
                    <a href="#">xx页面数据趋势</a>
                </div>
            </div>
        )
    }
}

Relevancenum.css

.Relevancenum{
    display: flex;
    height: 58px;
}
.num_content {
    width: 25%;
    margin-left: 20px;
    border-right: 1px solid #ccc;
}
.num_content h2{
    line-height: 10px;
    font-weight: 500;
}
.num_content a{
    text-decoration:none;
    color:#2799FF;
}
.num_content p{
    margin-top: 3px;
    color: #666;
}
.num_content3{
    border-right: none;
}

tabs组件 Tabschange.js


import React, { Component } from 'react'
import { Tabs } from 'antd';
import './Tabschange.css'

//图表组件
import  ChildrenTab from './ChildrenTab'

const { TabPane } = Tabs;

// const tabBar = {
// backgroundColor: "skyblue", //驼峰法
// };

export default class Tabschange extends Component {
    constructor(props) {
        super(props)
        //activeKey={this.state.activeKey}
        this.state = {
            keys: "1",
            tabpane: [
                { tab: "花呗页面相关数据折线", keys: "1", },
                { tab: "贷后入账打款", keys: "2", },
                { tab: "延期还款", keys: "3" },
                { tab: "交易后分期", keys: "4" },
                { tab: "账单分期", keys: "5" },
                { tab: "贷后入账打款", keys: "6", },
                { tab: "延期还款", keys: "7" },
                { tab: "交易后分期", keys: "8" },
                { tab: "账单分期", keys: "9" },
            ],
            data: [
                { Data: "2010-01", sales: 564 },
                { Data: "2010-02", sales: 657 },
                { Data: "2010-03", sales: 565 },
                { Data: "2010-04", sales: 787 },
                { Data: "2010-05", sales: 566 },
            ]
        }
    }
    render() {
        return (
            <div className="Tabschange">
                <Tabs tabPosition="left" onTabClick={this.handleChange.bind(this)} >
                    {this.state.tabpane.map(item => (
                        <TabPane key={item.keys} tab={item.tab} >
                            <ChildrenTab 
                                keys={this.state.keys} data={this.state.data}>            
                            </ChildrenTab >
                        </TabPane>
                    ))}
                </Tabs>
            </div>
        )
    }
//tab点击事件 默认传值当前key key是一个string
//用不同的key区分传入不同的data渲染不同的图表
    handleChange(key) {
        console.log(key)
        this.setState({
            keys: key
        });
        if (key == 1) {
            this.setState({
                data: [
                    { Data: "2010-01", sales: 564 },
                    { Data: "2010-02", sales: 657 },
                    { Data: "2010-03", sales: 565 },
                    { Data: "2010-04", sales: 787 },
                    { Data: "2010-05", sales: 566 },
                ]
            })
        } else if (key == 2) {
            this.setState({
                data: [
                    { Data: "2010-01", sales: 342 },
                    { Data: "2010-02", sales: 1132 },
                    { Data: "2010-03", sales: 454 },
                    { Data: "2010-04", sales: 634 },
                    { Data: "2010-05", sales: 232 },
                ]
            })
        }
    }
}

Tabschange.css

.Tabschange{
    margin-top: 30px;
    /* height:calc(100% - 86px); */
    border-top: 1px solid #f0ededc7;
}
.Tabschange .ant-tabs-nav {
    font-size: 14px !important;
    width: 300px !important;
}
.Tabschange .ant-tabs .ant-tabs-left-bar .ant-tabs-tab {
    text-align: left !important;
    padding:15px 10px !important;
}
.Tabschange .ant-tabs-nav .ant-tabs-tab-active {
    color: #2799FF !important;
    font-size: 14px !important;
    background-color: rgb(105,183,255,0.3);
}
.Tabschange .ant-tabs .ant-tabs-left-bar .ant-tabs-ink-bar, .ant-tabs .ant-tabs-right-bar .ant-tabs-ink-bar{
    width: 0 !important;
    height: 0 !important;
}
.ant-tabs .ant-tabs-left-bar .ant-tabs-tab, .ant-tabs .ant-tabs-right-bar .ant-tabs-tab{
    margin: 0 0 0 0 !important;
}

ChildrenTab.js组件

import React, { Component } from 'react'
import G2 from '@antv/g2';

export default class ChildrenTab  extends Component {

    componentDidMount(){
        // fetch('https:/g2.antv.vision/zh/examples/data/fireworks-sales.json')
        // .then(res => res.json())
        // .then(data => {
        //   console.log("data",data)
        //接收父组件props的data
        let data = this.props.data
          const chart = new G2.Chart({
            container: 'container',
            forceFit: true,
            height: 270,
            padding: [ 20, 40, 50, 50 ]
          });
          chart.source(data);
          chart.scale('Data', {
            range: [ 0, 1 ],
            tickCount: 10,
            type: 'timeCat'
          });
          chart.axis('Data', {
            label: {
              textStyle: {
                fill: '#aaaaaa'
              }
            }
          });
          chart.axis('sales', {
            label: {
              textStyle: {
                fill: '#aaaaaa'
              },
              formatter: text => {
                return text.replace(/(\d)(?=(?:\d{3})+$)/g, '$1,');
              }
            }
          });
          chart.tooltip({
            crosshairs: 'y',
            share: true
          });
          chart.legend({
            attachLast: true
          });
        //   chart.guide().dataMarker({
        //     top: true,
        //     content: '因政策调整导致销量下滑',
        //     position: [ '2014-01', 1750 ],
        //     style: {
        //       text: {
        //         fontSize: 13
        //       }
        //     },
        //     lineLength: 30
        //   });
  
          chart.line().position('Data*sales');
          chart.area().position('Data*sales');
          chart.render();
        // });
    }
    render() {
        return (
            <>
                <div id='container'></div>
            </>
        )
    }
    
}

 

 

2019-02-21 13:48:00 weixin_33774308 阅读数 299

官方示例效果:http://antv.alipay.com/zh-cn/g6/2.x/demo/net/2017-link-data.html

改编效果:

实现步骤:

  环境:nodejs、yarn/npm、umi(最新即可)

  创建项目:

    1.打开cmd

    2.创建项目文件夹:输入mkdir ReactUmiG6App & cd ReactUmiG6App 回车

    3.创建项目:yarn create umi

     4.添加需要的依赖包:react 、antd、@antv/g6

        yarn add antd、yarn add react 、yarn add @antv/g6

     5.以上就是所有准备,接下来就可以具体实现了

 具体编码参考官方API及实例就可,其他需要注意的就是依赖引用了,以下供参考

 最后附上源码:

https://gitee.com/wiaoong/studynotes.git

转载于:https://www.cnblogs.com/JQKA/p/10291586.html

2019-06-04 10:28:14 qq_36560180 阅读数 1573

在react项目中使用antv G6的时候出现了invalid container的错误,各种找解决方案以及在GitHub上找G6的项目,花费了一些时间但是没有找到解决方法,不得不吐槽一下antv官网给出的例子,没有一个完整的demo,文档很不友好。

最后发现一篇博客,博客中说在componentDidMount的时候做图形的初始化,这个时候恍然大悟,我之前直接把初始化的代码写在render中,而render执行的时候return中的div还没有渲染,所以才会出现invalid container的错误。
使用function + hooks实现官网的一个例子,代码:

import React, { useState, useEffect } from 'react';
import G6 from '@antv/g6';

const DeviceGraph = () =>{
	const data = {
	  nodes: [{
	    x: 100,
	    y: 100,
	    shape: 'circle',
	    label: 'circle',
	 },{
	    x: 200,
	    y: 100,
	    shape: 'rect',
	    label: 'rect',
	 },{
	    x: 300,
	    y: 100,
	    size: [60, 30],
	    shape: 'ellipse',
	    label: 'ellipse',
	 },{
	    x: 400,
	    y: 100,
	    shape: 'image',
	    img: 'https://gw.alipayobjects.com/zos/rmsportal/XuVpGqBFxXplzvLjJBZB.svg',
	    label: 'image',
	 }]
	};
	  
	useEffect(() => {
		const devicegraph = new G6.Graph({
		    container: 'mountNode',
		    fitViewPadding: 20,
		    height: 500,
		    width: Width,
		    nodeStyle: {
			    default: {
			    	fill: '#40a9ff',
			    	stroke: '#096dd9'
			    }
			  },
		    	edgeStyle: {
		        	default: { stroke: '#A3B1BF' }
		        },
		        modes: {
		            default: ['zoom-canvas', 'drag-canvas']
		        }
		   	});
		   	devicegraph.read(data);
		 	}
	 });
 
	 return (
	 	<div id='mountNode'> </div>
	 )
};

export default DeviceGraph

结果:
在这里插入图片描述

需要注意的一点是如果用于绘图的数据源发生改变的时候,需要清空画布,否则画出的图会闪烁。具体方法见https://www.yuque.com/antv/g6/graph
我的代码:

import React, { useState, useEffect } from 'react';
import G6 from '@antv/g6';

const DeviceGraph = () =>{
	const [ graph, setGraph ] = useState(undefined);
	
	const data = {
	  nodes: [{
	    x: 100,
	    y: 100,
	    shape: 'circle',
	    label: 'circle',
	 },{
	    x: 200,
	    y: 100,
	    shape: 'rect',
	    label: 'rect',
	 },{
	    x: 300,
	    y: 100,
	    size: [60, 30],
	    shape: 'ellipse',
	    label: 'ellipse',
	 },{
	    x: 400,
	    y: 100,
	    shape: 'image',
	    img: 'https://gw.alipayobjects.com/zos/rmsportal/XuVpGqBFxXplzvLjJBZB.svg',
	    label: 'image',
	 }]
	};
	  
    useEffect(() => {
      if(graph !== undefined){
        graph.changeData(deviceData)
      }
      if(Data.length > 0 ){			// 这里的Data是绘图的数据源
        const devicegraph = new G6.Graph({
          container: 'mountNode',
          fitViewPadding: 20,
          height: 500,
          width: Width,
          nodeStyle: {
            default: {
              fill: '#40a9ff',
              stroke: '#096dd9'
            }
          },
          edgeStyle: {
            default: { stroke: '#A3B1BF' }
          },
          modes: {
            default: ['zoom-canvas', 'drag-canvas']
          }
        });
        devicegraph.read(data);
        setGraph(devicegraph);
      }
    }, [Data]);

 
	 return (
	 	<div id='mountNode'> </div>
	 )
};

export default DeviceGraph

之前写React代码几乎都是使用Class,现在用了用function + React Hooks的方式感觉还是很好用的,比如使用useState可以保存graph实例,可以在其他的方法中访问这个变量,而使用class是无法保存到state中,会造成死循环。所以如果不是特别依赖react的生命周期类似componentDidMount这些的,推荐使用function + Hooks的方式构建,真香!!!

2019-12-14 22:32:00 ansheng02 阅读数 1493

        现在是2020年04月28日 23:45分,背景音乐是隔壁老樊的"这一生关于你的风景".

        我开始对本篇文章做补充,今天应评论要求,我把antV/L7整理了一下源码,还是会发布在公众号上,文章末尾关注公众号"DataShowCharts",回复"antV地图"即可获取(我还附赠了echarts的堆叠折线图-自动轮播图例效果呢-.-)

        现在是我自己的絮絮叨叨时间:不想看的可以直接文末见啦~

        今天下午下班后,准备开始整理antV/L7的地图的代码,本来是直接运行项目的,可以运行起来,然后拷贝一份,准备修改.npm  install突然给了我一个大惊喜,出错了,地图渲染不出来,我赶紧查看文档,现在的已经更新到了2.2.2版本,而我之前用的是1.3.20版本,一些属性配置项都变了,一点一点根据文档磨以前的代码是真的有点痛苦,忍不住吐槽,哎,还是得干,不然以后项目更新了怎么办.虽然截止到现在我还没磨出来.....

      已经很晚了,还想洗澡睡觉,我今天还特勤快的洗了床单被罩,哎,还得铺床....吐槽无力,有需要的去公众号获取吧.文章末尾见啦~

-------------------------------------------------我是分界线-----------------------------------------------

      关于antV/L7除了官方文档,可翻阅的资料实在有限,关于地图的实现上,我真的是无力吐槽我自己……其实官方文档给的也还不错,emmm……深深的感受到作为一名开发者的自主学习能力有多么重要。额~对了,在本次的大屏开发上,尤其是图表渲染和地图,森森的感受到了自己的浅薄无知,每天都在“我没文化”、“我文盲”、“我看不懂”、“我不会”中怀疑自己,是英文的文档,考我英语阅读理解也就算了,竟然还有地理知识,好,毕竟是地图嘛~我忍了~可你还有高数计算是怎么个回事……忍不住想哭,要深深的虐一遍自己的脑子……好,我学!重新再来一遍高数!!!已经列入了日程……

    其他的也不多说了,关于这次的地图实现主要是用的@antV/L7@1.3.20,没办法,它最新的1.4版本还有bug……

   

 npm install @antV/l7@1.3.20

   接着就可以在需要的页面中引入了,

import L7 from "@antv/l7";
import { isDuration } from "moment";

地图的实现主要是用到了scene属性,Scene是基础的地图类,提供地图/图层的创建/管理等。这个是关于Scene的简介

const scene =new L7.Scene({
    id:'map'
    mapStyle:'dark',
    center:[ 110.770672, 34.159869 ],
    pitch:45
})

接下来我说一下这个地图的功能,可以实现交互,即,鼠标滚动的缩放,点击事件等,由于我需要出现散点,这个时候就用到了antV/l7---》layer-->PointLayer.

当地图缩放到某一个地图层级范围的时候,我需要显示整合一部分点数据,当再缩小范围的时候,我将把这些点合成一个点。其中判断地图层级,用到了

scene.getZoom()

scene.getZoom()>8.5  -->点数据

6.5<scene.getZoom()<8.5  --》区域点数据

scene.getZoom()<8.5  --》整点数据

我使用了一个笨方法,就是每一次判断完了之后,就重新加载地图进行渲染,这样做虽然实现了功能,但是在前端消耗有点大。之后我在gitLab上提交了代码,我们的项目主管-》刘又把这段代码(就是我重新加载渲染的部分)做了调整,代码简洁了很多,而且他没有用我写的这个方法,而是充分运用了文档提供的render。由此深深感受自己的马虎和对文档不够彻底理解的能力,实在有愧。

所以我会用两种方法说一下地图的实现,首先是我的笨方法,然后才是优化后的。

笨方法思路:

通过var scene = new L7.Scene({})加载完地图后,触发loaded事件

scene.on('loaded',()=>{})  //地图加载完成触发

在loaded写事件函数,关于Scene的地图时间或者鼠标事件等,都需要写在loaded里,写在外部是不生效的。在loaded里,new PointLayer,并且把三个判断写在这里,然后这样在每一次地图等级改变的时候,会重新加载地图,造成大量的渲染浪费。

本来想把自己原本的代码拿出来,做一次记录,但想想,也就算了吧,还是直接放优化后的吧。

优化后,把scene作为一个对象,在data里生命,并在methods--》mapNew函数里new且引用。

先说一个概念,scene在new之后是一个对象,啊么就可以对这个对象添加各种属性,包括渲染散点数据以及重新渲染散点数据,鼠标滑过后出现该散点的图例信息。LineLayer是用来勾勒北京地图的边界线,antV/l7中的每个地图的边境线是用的geojson数据格式,这个案例中的北京地图是我们主管找的,然而我现在还不知道在哪里找,下周我会问一下,再来更新。

这里是把这个地图当成组件,在vue中引用,应该在react中同样适用,暂时还没尝试,仅仅是猜测,这里先放部分代码,文章末尾会把代码的下载地址放上。这部分代码好好理解下,也可以不用下载压缩包的。

 var layer = _this.layer(_this.BJMapData);
//只需要传入data数据,就可以渲染散点,这里是把layer封装了一下
layer(dataObj) {
      var layer = this.scene
        .PointLayer({
          zIndex: 2
        })
        .source(dataObj, {
          parser: {
            type: "json",
            x: "longitude",
            y: "latitude"
          }
        })
        .shape("circle")
        .size(15)
        .active(false)
        .color("rgba(0,231,255,0.5)")
        .style({
          stroke: "#27F5FF",
          strokeWidth: 2,
          opacity: 1.0
        })
        .render();
      return layer;
    },

还有其他的,好吧,我有点懒了,如果有需要大家可以自行下载代码。

--------我是文章末尾-----

关注公众号"DataShowCharts",回复关键词"antV地图",即可获取本文章的源码