初识时下热门框架 - React

React 做为时下最流行的框架之一,不能否认其在前端框架当中的地位,不少的项目也都在使用 React;做为一名 Front-end engineer,学习 React 不管是对于项目开发仍是自身的水平都会有必定程度的帮助。

最近开始简单的学习了 React,因此在此简单的总结一下。css

原文地址:初识 Reacthtml

关于脚手架的简单使用

在网上看到了不少 React 的脚手架工具,这里我使用的是 create-react-app ,建立项目也比较简单。

  • 安装 create-react-app
npm install -g create-react-app

  • 建立一个 React 项目
create-react-app my-app

  • 启动项目
npm start

项目结构

项目建立后,如图,目录结构也比较清晰:前端

clipboard.png

  • node_modules:存放项目依赖
  • public:存放项目入口文件
  • src:存放项目代码以及项目资源
  • package.json:项目依赖管理

关于目录结构,能够依据实际项目需求进行划分,以下图结构:node

clipboard.png

  • assets -- 存放项目静态资源
  • components -- 存放组件
  • css -- 样式表
  • page -- 存放各个页面组件(也可称做父组件)

随着项目的复杂程度,项目结构划分的也更可加精细。react

React 简单上手

写了一些 demo,整体感受 React 仍是颇有意思的,由于以前接触过 Vue 的关系,因此以为上手仍是相对简单的,固然 React 和 Vue 也有不少不一样点,如 React 组件有独立状态机,大大提升了组件的独立性、可操做性;React 支持 Jsx 语法,对于开发者来讲能够必定程度提升开发效率;再配合 ES6 语法, 代码写起来会很舒服。ios

固然,随着对 React 更加深刻的学习与了解,学习的梯度也会愈来愈高。做为开发者咱们都知道 React 强大完整的生态体系,它已不仅仅是一个框架,而是已经发展为一个行业的解决方案。git

简单总结上手碰到的坑

  1. 关于 getInitialState
  2. getDefaultProps 的疑惑
  3. 关于请求数据
  4. 关于事件传参
  5. React 中的 this
  6. 关于组件的生命周期
  7. 父子组件之间的传值
  8. react-router 的使用

关于 getInitialState

一开始看了阮大神的文章,却不知在 ES6 中须要在 constructor 中定义 state ,致使了程序报错:github

constructor(props) {
        super(props);
        this.state = {flag: false};
        this.handle = () =>{
            //setState 是异步执行的,render时才会调用,能够写入回调函数
            this.setState({flag: !this.state.flag}, () => {
                //code
            });

        }
    }

getDefaultProps 的疑惑

在建立了一个组件后,使用 getDefaultProps 设置默认属性时,控制台报了这样一条警告:web

clipboard.png

经查阅后了解到: getDefaultProps 方法是用在使用 React.createClass 方式建立组件的,应该使用 ES6 语法:ajax

static defaultProps = {
    content: ''
}

关于请求数据

React 请求数据有不少方法如 $.ajax(), fetch api, axios, ajax() 等等,这里我简单使用了一下 fetch() 方法,fetch() 方法返回了一个 Promise 对象,reslove 时会返回 response 对象,一个最基本的用法:

fetch(api)
  .then(function(response) {
    return response.json()
  }).then(function(json) {
    //resolve callback
    console.log('parsed json', json)
  }).catch(function(ex) {
    //reject callback
    console.log('parsing failed', ex)
  })

固然,由于 babel 对部分 ES7 的支持,也可使用 await/async 进行请求,把异步代码当作同步代码来写,开发体验要比写回调好不少

//必须声明 async
async getData() {
    let response = await fetch(api)
    let data = await response.json();
    console.log(data);
}

注:在请求本地文件时需将资源放到 public 目录下,请求路径为 '/xx.json',也可使用 import 引入文件。


关于事件传参

在为元素绑定事件的过程当中,若是须要传参,可使用 bind 方法进行传参:

<span className="g-data" onClick={this.handle.bind(this, this.props.source)}> 
    //code
 </span>

其中,bind 的第一个参数 this 是指改变函数运行时的指针,如:在 render() 中使用该方法,则 this 指向组件的实例,这样才能准确访问组件的属性和方法。

一样的,也可使用箭头函数进行传参:

<span className="g-data" onClick={ () => this.handle(this.props.source) }> 
    //code
 </span>

React 中的 this

一开始在我为组件绑定事件的时候,发如今绑定事件中没法拿到 props 属性:

<span className="g-data" onClick={this.handle}> 
    //code
 </span>

最初个人理解是:绑定事件的函数中, this 应该是指向 class 建立的实例的。事实证实,个人理解是错误的:事件的回调函数是在全局环境下执行的,也就是 window 。因而我在 handle 中试着输出 this :

handle() {
    console.log(this); //undefined
}

输出了 undefined,又是一个大写的问号,因而我找了一下 ES6 的相关规范:在 class 中,定义类的时候默认使用严格模式,而在严格模式下,this 不能指向全局变量,所以变成了 undefined。因此,为了解决这个问题,大体有以下几种办法:

  • 在组件渲染时绑定 this
<span className="g-data" onClick={this.handle.bind(this)}> 
    //code
 </span>
  • 在 constructor 内绑定
constructor() {
    this.handle = this.handle.bind(this);
}
  • 使用箭头函数绑定
handle = () => {

}

或者在 DOM 结构中写箭头函数

<span className="g-data" onClick= { () => this.handle() } > 
    //code
 </span>

关于组件的生命周期

每个组件都有几个你能够重写以让代码在处理环节的特定时期运行的“生命周期方法”。方法中带有前缀 will 的在特定环节以前被调用,而带有前缀 did 的方法则会在特定环节以后被调用。如:

  • componentWillMount( )

表示组件即将渲染以前会调用,即在 render() 以前调用。

  • componentDidMount( )

表示组件渲染完成当即调用,这个生命周期设置状态会触发从新渲染。

  • componentWillUnmount( )

表示组件将要被移除以前调用,在这里能够进行一系列的清理工做。

更多组件生命周期请移步 React 官方文档

父子组件之间的传值

  • 父组件给子组件传值

在父组件中为子组件添加属性和属性值,在子组件中经过 props 属性得到父组件对应的属性值。

//父组件
<div>
    <child title="This is title" />
</div>

//子组件
<div>{this.props.title}</div>
  • 子组件给父组件传值

能够在父组件的子组件中添加一个回调函数;子组件中设置状态值,当子组件中的值改变时,修改状态值会触发重渲,经过 props 拿到父组件的回调函数,并将新的状态值做为参数传入,这样父组件就能够拿到子组件的值并进行后续操做。

//父组件
    constructor(props) {
        super(props)
        this.state = {
            childState: ''
        }
    }

    handleChange(childState) {
        this.setState({
            childState: childState
        })
    }

    render() {
        return (
            <div className="parent">
                <Child callBack={this.handleChange.bind(this)} />
            </div>
        )
    }
    
    //子组件
    constructor(props) {
        super(props)
        this.state = {
            childState: 'childState'
        }
    }

    handleChange(e) {
        //setState 是异步的,因此要先拿到 childState 的值,
        //或者写入 setState 的回调函数中
        var childState = e.target.value
        this.setState({
            childState : e.target.value
        })
        this.props.callBack(childState)
    }

    render() {
        return (
            <div className="child">
                <input type="text" onChange={this.handleChange.bind(this)} />
            </div>
        )
    }

react-router 的使用

一开始的时候,参考了一些文章试着使用了一下 react-router ,结果一直报错,后来发现版本更新致使部分语法改变,这里我使用的是 4.x 版本,一个比较基本的路由实现:
首先,引入 react-router:

import {
    BrowserRouter as Router,
    Route,
    Link
} from 'react-router-dom

接下来进行路由配置,告诉路由如何进行匹配以及匹配后如何执行代码:

<Router>
    <div>
        <Link to="/">Home</Link>
        <Link to="/About">About</Link>
        
        //exact(bool):为true时,则要求路径与location.pathname必须彻底匹配;
        <Route exact path="/" component={Home}/>
        <Route path="/About" component={About}/>
    </div>
</Router>

在路由跳转到另外一个页面时,若是当前页面存在路由跳转,则涉及到路由嵌套:

render() {
    let match = this.props.match
    return (
        <Router>
            <div>
                <Link to={match.url}>Tab One</Link>
                <Link to={`${match.url}/Two`}>Tab Two</Link>
                <Route exact path={match.url} render={()=>(<h3>1</h3>)} />
                //:param 能够理解为一种匹配模式,能够经过 params 拿到对应属性值
                <Route path={`${match.url}/:param`} component={Topic} />
            </div>
        </Router>
    )
}

match 对象中存储着路由路径的相关信息,经过 match 对象能够拿到一级路径,而后再根据二级路径进行匹配便可。

有些状况,在进行路由跳转时须要传递一些参数,当参数比较多的时候,经过路由路径传递参数就不是一个好的选择了,能够经过 query/state 传参:

//经过 location 对象取值
<Link to={{pathname:'/Message',query:{name:'react'},state:{name:'react'}}} >Message</Link>

//使用 js 传参
this.props.router.push({pathname:'/Message',query:{name:'react'}})

小结

经过简单上手 React ,我以为用来开发项目很方便, 由于 React 使用了 virtual dom 因此在性能方面也比较给力,加上组件化开发、Jsx 和 ES6 语法的加持以及完善的生态体系,我想这大概就是 React 成为时下热门框架的缘由之一吧。

参考文献

  1. React 入门实例教程
  2. React - 用于构建用户界面的 JavaScript 库
  3. 从 React 绑定 this,看 JS 语言发展和框架设计
  4. 深刻浅出Fetch API
  5. 类 - JavaScript | MDN
  6. React 组件之间如何交流
  7. React Router 中文文档
相关文章
相关标签/搜索