React入门与进阶(首篇)

欢迎访问我的博客

早期,我们开发web应用,只能是通过请求服务器,服务端响应请求,返回一个页面,,每次浏览器都得对页面加载,渲染,非常影响用户体验;直到出现了ajax,人们感觉web开发的春天终于来了,ajax使得我们可以在不刷新整个页面的情况下,更新页面局部,开发者开始使用各种类库在浏览器端渲染应用,但是随着应用越来越大,这种方式也越来越难整合。

React的设计思路是将请求发生时渲染整个页面这种工作流放到客户端中。

React初识

状态

首先,我们需要了解一下状态与状态机的概念:

  • 状态

状态就是一种事物在之前一段时间经过人为或自主发展后的表现结果。在一个web应用中,状态是指经过用户操作,交互后应用的表现结果。

  • 状态机

对于一组状态,随着时间变化,根据不同的输入,各状态不断进行转换,状态机即负责管理不同状态间的转换。

React特性

在以前,每次交互后,应用状态变化,我们需要重新渲染整个页面,这是很慢的;尽管后来有了ajax,我们得以使用ajax异步请求数据,然后使用JavaScript查询获取DOM,使用新数据更新DOM,比原来体验好了许多,但这也必然引起浏览器对页面的重绘或重排,也有性能局限,我们的程序员又踏上了更高的追求。

React主要只负责两件事,都只和View视图相关:

1. 更新DOM
1. 响应事件
  • 更新DOM

React使用了虚拟DOM(Virtual DOM)构造一个强悍的渲染系统:通过内部渲染函数计算出尽量少的DOM更新,触发最少的重绘改变应用的状态,为用户提供更友好的体验。

渲染函数,可以读取特定时间或操作下应用页面的状态,并将其转换为页面上的虚拟表现(这些虚拟表现由Virtual DOM构成),通过比较不同的虚拟表现,可以计算出状态改变后最少的DOM变化,提供给React的渲染系统。

  • 响应事件

React只使用单个事件处理器,将所有事件委托给此事件处理器,使得应用更高效的处理所有事件。

JSX

除了使用原生JavaScript语法编写React应用,React还提供一种在组件内构建标签的类XML语法--JSX(JavaScript XML)。


    // 旧版本React原生JavaScript语法
    var title1 = React.DOM.h1({className: 'react'}, 'React');

    // 新版本React原生JavaScript语法
    var title2 = React.createElement('h1', {className: 'react'}, 'React');

    // JSX语法
    var title3 = React.createClass({            
        render: function() {                
            return (
                <div onClick={this.handleClick}>
                    <h1 className="react">
                    React, {this.label_txt}
                    </h1>
                    <div className="component-cont">
                        {this.props.children}
                    </div>
                    <p>{this.dateToString(new Date())}</p>
                </div>
            );
        },
        handleClick: function() {
        }
    });

和原生JavaScript语法不同,如果使用了JSX语法,我们需要将其转换成原生js执行:

JSXTransformer.js

React提供JSXTransformer.js转换JSX语法,需要引入JSXTransformer.js并且将script标签type设置为text/jsx:


    <script type="text/jsx" src=""></script>
    <script type="text/jsx">...</script>

如图:

JSXTransformer.js

babel

也可以使用babel转换JS语法:引入babel的browser.js,并且将script标签type设置为text/babel

babel
    
    <script type="text/babel" src=""></script>
    <script type="text/babel">...</script>

注:使用JSX语法后,如果是引入外部js文件的形式,浏览器会请求文件失败,且这种语法转换较慢,尽量在服务端预先转换语法。

React Component

React推荐创建组件处理特定需求,在应用中组合使用组件,实现特定功能。

单向数据流

React组件是一个状态机,每个组件内部都有自己的状态(state),其状态只在内部作用域下操作修改。多个组件之间可以是复合的关系,即从属组件关系,React组件提供设置组件属性(props),属性可以从主组件获取传递到各属组件。

主组件与属组件通信,最简单的方式是通过props,主组件通过props传递回调函数给属组件,在回调函数里可以更新state,触发组件重绘;在属组件中调用回调函数,也可以传入数据参数。

props

props即属性,我们可以给React组件设置属性,属性值可以是任意JavaScript数据类型,且在组件内应该是只读的。

  • this.props

我们可以通过this.props访问到所有组件属性,但是该属性值是只读的。

  • 挂载组件属性

可以在挂载组件时传入组件props,指定其属性值:


    var HelloReact = React.createClass({
        render: function() {
            return (
                <h1>{this.props.greetWord}</h1>
            );
        }   
    });
    
    var greet = "Hello, React";
    React.render(
        <HelloReact greetWord={greet} />,
        document.querySelector('body')
      );
  • getDefaultProps

在创建组件时,可以定义getDefaultProps方法为组件设置默认属性值,该方法在调用创建组件类方法时即被调用:


    var HelloReact = React.createClass({
        getDefaultProps: function() {
            return {
                name: 'coding'
            };
        }
        // ...
    });
  • setProps

我们也可以使用setProps()方法设置组件属性,但是只能在组建外或者子组件中调用该方法:


    var HelloReact = React.createClass({
        render: function() {
            return (
                <h1>{this.props.greetWord}</h1>
            );
        }
    });

    var helloReact = React.render(
        <HelloReact greetWord={greet} />,
        document.querySelector('body')
      );
    helloReact.setProps({greetWord: 'Hello, world!'});
  • PropTypes

React提供了一种验证props的方式:定义一个propTypes对象,指定组件属性应该满足的数据类型,若不满足则会输出一条console.warn语句:

    
    var HelloReact = React.createClass({
        propTypes: {
            greetWord: React.PropTypes.func
        },
        render: function() {
            return (
                <h1>{this.props.greetWord}</h1>
            );
        }
    });

    var helloReact = React.render(
        <HelloReact greetWord={greet} />,
        document.querySelector('body')
    );

state

我们说每一个组件都是一个状态机,管理着各自的状态,就是所谓的state,state只存在于组件的内部。

  • this.state

可以通过this.state访问组件状态值。

  • getInitialState

React组件state默认是null,在定义组件类时我们可以定义getInitialState方法指定组件初始state值。


    var HelloReact = React.createClass({
        getInitialState: function() {
            return {
                isShowContent: false
            };
        },
        render: function() {
            if (this.state.isShowContent) {
                return (
                    <div>state modified</div>
                );
                    }
            return (
                <h1>{this.props.greetWord}</h1>
            );
        }
    });

    var helloReact = React.render(
        <HelloReact greetWord={greet} />,
        document.querySelector('body')
    );
  • setState

在组件渲染到页面后,我们可以通过setState方法修改组件状态值,state变化后会自动调用render方法,重新渲染DOM。


    var HelloReact = React.createClass({
        getInitialState: function() {
            return {
                isShowContent: false
            };
        },
        render: function() {
            if (this.state.isShowContent) {
                return (
                    <div>state modified</div>
                );
                    }
            return (
                <h1 onClick={this.handleClick}>{this.props.greetWord}</h1>
            );
        },
        handleClick: function() {
            this.setState({
                isShowContent: !this.state.isShowContent
            });
        }
    });

    var helloReact = React.render(
        <HelloReact greetWord={greet} />,
        document.querySelector('body')
    );
  • replaceState

更新组件状态有两种方法:setState和replaceState,和前者在原有state对象上拓展不同,replaceState是使用传入的参数直接替换原有的state。

永远不要通过setState和replaceState方法以外的方式更新state,如:this.state.isShowContent = true;,这样更新state,React无法监控,组件无法重新渲染。

props & state

state只存在与组件内部,它应该只是对应组件视图的某一种状态,是一种简单的值,任何不必要的或通过计算得出的值都不应该在这里出现。

如果需要在组件树中传递或给特定组件传入数据,可以通过props给上层组件设置属性值,可以是任意类型的数据;一旦属性传入组件,其应该是只读的。

组件的生命周期

每个组件拥有其生命周期:从实例化,到生存期,到被销毁。

实例化

当一个组件在首次实例化时,调用的生命周期方法依次为:

1. getDefaultProps
2. getInitialState
3. componentWillMount
4. render
5. componentDidMount

在组件的后续应用,则会依次调用如下方法:

1. getInitialState
2. componentWillMount
3. render
4. componentDidMount
  • getDefaultProps

该方法返回一个对象设置实例的默认props值,在调用定义组件类方法时调用且只调用一次。

  • getInitialState

此方法可以初始化每个实例的state,每次实例化都只会调用一次。

  • componentWillMount

该方法在组件完成首次渲染之前调用,此时可以修改组件state。

  • render

此方法创建虚拟DOM,返回一个虚拟表现;此方法是定义组件类是必需定义地方法。

  • componentDidMount

此方法在render方法成功返回值,且真实DOM在页面渲染完成之后,调用,经常通过this.getDOMNode()方法获取真实DOM节点。

生存期

组件渲染好,在页面中生成DOM并且可以与用户交互,即是组件的生存期。

在生存期,随着用户的交互,可能触发的生存期方法有:

1. componentWillReceiveProps
2. shouldComponentUpdate
3. componentWillUpdate
4. componentDidUpdate
  • componentWillReceiveProps

React组件的props在其指定或被上级组件更改时,将触发此方法,此方法接收一个参数:更改后的props,我们可以在此方法内根据props值更改组件内部state值。

  • shouldComponentUpdate

在组件首次渲染完后,如果我们改变了组件props或state值,将触发此方法,若该组件及其下级组件都不需要渲染新的props和state,则此方法返回false,不会调用render()方法,否则,返回true,调用render()方法重新渲染组件。我们可以自定义覆盖该方法,优化该组件决定是否重新渲染组件的规则,获得更适合需求的用户体验。

  • componentWillUpdate

组件接收新的props或state后,render()方法触发前将调用此方法。

注:不要在此方法内更新state.

  • componentDidUpdate

组件接收新的props或state后,render()方法触发,组件渲染好后将调用此方法。

销毁

在使用完React组件后,我们需要将其从页面文档移除销毁。

  • componentWillUnmount

当一个组件被移除时,将触发componentWillUnmount()方法,我们可以在此方法内清理之前的一些引用或事件监听程序。

事件处理

前文提到,React主要关注两件事:更新DOM,响应事件。关于更新DOM,前文已经阐述,接下来要学习React的单一事件处理器。

组件相当于一个状态机,其提供给用户一个视图(view),组件内部状态(state)对应特定的视图表现,用户与视图交互时,我们通过在组件上绑定事件处理器监听用户输入,事件触发时,在事件处理器中更新组件状态(state),React根据state值的变化决定是否更新(重绘或重新渲染)组件,组件在render方法内渲染新的state数据,更新视图表现。

绑定事件处理器

React绑定事件处理器写法类似HTML内联事件写法,但其本质是通过事件代理实现的:


    render: function() {
            if (this.state.isShowContent) {
                return (
                    <div>state modified</div>
                );
                    }
            return (
                <h1 onClick={this.handleClick}>{this.props.greetWord}</h1>
            );
        }

React支持的事件类型可以参考:http://facebook.github.io/react/docs/events.html

触摸事件

若需要在React中使用触摸事件,则需要手动调用开启:React.initializeTouchEvents(true);

事件与状态

只有组件的props或state变化才会触发组件重绘和渲染,如果组件需要随用户交互输入改变视图表现,则需要在事件处理程序里更新组件state。


    var HelloReact = React.createClass({
        getInitialState: function() {
            return {
                isShowContent: false
            };
        },
        render: function() {
            if (this.state.isShowContent) {
                return (
                    <div>state modified</div>
                );
                    }
            return (
                <h1 onClick={this.handleClick}>{this.props.greetWord}</h1>
            );
        },
        handleClick: function(event) {
            this.setState({
                isShowContent: !this.state.isShowContent
            });
        }
    });

    React.render(
            React.createElement(title3, null, 'this is my react practice.'),
            document.querySelector('.main')
    );

线上地址:点此查看效果

事件对象

和原生DOM事件一样,React事件处理器函数会传入一个事件对象。React并不是直接把原生DOM的事件对象传给事件处理器函数,而是封装在SyntheticEvent对象中,但其使用和原始事件对象保持一致,并且可以通过其nativeEvent属性获取原生事件对象:


    handleClick: function(event) {
        console.log(event);
        console.log(event.nativeEvent);
        this.setState({
            isShowContent: !this.state.isShowContent
        });
    }

输出如下:

React事件对象与原生事件对象

线上地址,可以打开控制台查看打印消息。:点此查看效果

对于有一定基础的前端同学,经过本篇学习,应该可以使用React编写一个基本的程序了,接下来将深入学习React组件复合,通信。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容

  • 原教程内容详见精益 React 学习指南,这只是我在学习过程中的一些阅读笔记,个人觉得该教程讲解深入浅出,比目前大...
    leonaxiong阅读 2,770评论 1 18
  • 本笔记基于React官方文档,当前React版本号为15.4.0。 1. 安装 1.1 尝试 开始之前可以先去co...
    Awey阅读 7,532评论 14 128
  • 我们在野外的森林里度过了一夜,觉得还不错。第二天早上我和同伴早早的起来了,兴奋的期待着这一天会怎样发展。 有几个同...
    紫幽水仙f1d9阅读 181评论 0 0
  • 在农村,像我们孩子这么大的小朋友早就会骑自行车了,可是我们孩子,因为胆子小,因为我们始终没有教他学骑车,所以他就一...
    西瓜甜甜啦阅读 585评论 29 30
  • 看着一道色彩妖艳的美食,闻着一股让人神魂颠倒的香气,你还能保持一颗平静的心吗?作为一个纯粹的美食爱好者,每天烹制各...
    诗桦阅读 3,467评论 38 106