html5首页投稿(暂停使用,暂停投稿)程序员

Reactjs 入门综合教程 (上)

2016-09-24  本文已影响1578人  艾伦先生

按照惯例,先给ReactJS背书

React是一个Facebook开发的UI库,于2013年5月开源,并迅速的从最早的UI引擎演变成一整套前后端通吃的 Web App 解决方案。衍生的 React Native 项目,目标更是宏伟,希望用写 Web App 的方式去写 Native App。

使用这个库可以很方便的开发交互式的、具有表达力的和可重用的UI组件。它本身并不是一个MVC框架,就是一个视图层,并且是一个以组件为基础的高效视图层。

React 的核心思想是:封装组件,各个组件维护自己的状态和 UI,当状态变更,自动重新渲染整个组件。基于这种方式的一个直观感受就是我们不再需要不厌其烦地来回查找某个 DOM 元素,然后操作 DOM 去更改 UI。

推荐一个脚手架程序

git clone https://github.com/hellobeifeng/reactDemo.git

gulp serve
localhost:5000

说说我理解的一些概念

JSX

真要使用JSX?

JSX 并不是一门全新的语言,仅仅是一个语法糖,允许开发者在JavasSript中编写XML语言。

作为React的核心部分,JSX使用XML标记的方式直接声明页面。在JavaScript代码里直接写XML的语法,每一个XML标签都会被JSX转换工具转换成纯JavaScript代码。(学习React的第一个坑)

注意:使用JSX写的代码,需要编译输出成JS代码才能使用。将 JSX 语法转为 JavaScript 语法,这一步很消耗时间。现在前端项目,都会使用前端工程化,不会直接在html页面中直接写js代码,写好的js代码都会使用工具进行编译压缩等。这样的话,我们的JSX也会通过编译直接转化成js语法,让浏览器直接使用。

好消息是你不用使用这个JSX也可以直接创建组件,但是坏消息是,不用JSX你必须使用原声JavaScript通过大段的API来创建。

例如,不使用JSX创建一个标题的函数大概是这样

React.createElement('h1',{className:'question'},'Questions')

如果使用JSX,上述调用就变成了下面这种更熟悉且简练的标签

<h1 className="question"/>Questions</>

而且,一旦嵌套元素就可能出现如下的样子

var child = React.createElement('li', null, 'Text Content');
var root = React.createElement('ul', { className: 'my-list' }, child);
React.render(root, document.body);

所以,当你放下心里负担之后会发现,使用JSX的好处就是,抽象了React Element的创建过程,允许使用熟悉的语法来定义HTML元素树,提供更加语义化且易懂的标签,程序结构更容易被直观化,所以下定决心使用JSX吧~

如此使用JSX

JSX定义属性&&样式使用

在HTML中,可以通过标签上的属性来改变当前元素的样式,当然,这在JSX中也是可以的,只不过JSX在使用行内样式的时候是有缺陷的。使用{{}}而不是引号的方式。

React.render(
    <div className="text-c1" style={{color:'red',marginTop:'20px'}}>
        xxxxx
    </div>,
    document.body
);

直接在标签上使用style属性时,要写成style={{}}是两个大括号,外层大括号是告知JSX这里是js语法,和真实DOM不同的是,属性值不能是字符串而必须为对象,需要注意的是属性名同样需要驼峰命名法。即margin-top要写成marginTop,属性之间以逗号间隔。

使用变量:

JSX将两个花括号中的内容{...}渲染成动态值,花括号指明了一个JavaScript上下文环境————你在花括号中放置的任何内容都会被求值。

JSX中的延展属性

在JSX中我们可以使用ES6中的最新语法...来遍历对象

  var HelloMessage = React.createClass({
    render: function() {
      return <h1>Hello {this.props.name}  get {this.props.votes} votes</h1>;
    }
  });

  var Lucy = {
    name: "feng",
    votes: "23"
  }

  ReactDOM.render(
  <HelloMessage {...Lucy} />,
    document.getElementById('example')
  );

JSX中的事件绑定

JSX支持事件的绑定,语法类似于HTML中事件的绑定,不同的是这里事件的名称必须按照驼峰式,例如change变成onChange,click变成onClick等。

实例1:点击计数

var Counter = React.createClass({
    incrementCount: function(){
        this.setState({
            count: this.state.count + 1
        });
    },
    getInitialState: function(){
        return {
            count: 0
        }
    },
    render: function(){
        return (
            <div class="my-component">
                <h1>Count: {this.state.count}</h1>
                <button type="button" onClick={this.incrementCount}>Increment</button>
            </div>
        );
    }
});
React.render(<Counter />,document.getElementById('example'))  

实例2:事件传递参数

var HelloComponent = React.createClass({
    testClick: function (str) {
      alert('hello ' +  str)
    },
    render: function() {
      return (
          <p
              onClick={this.testClick.bind(this, 'feng')} 
            style={{color:'#ff6600',width:'200px', height:'100p'}}
          >
          Click me
          </p>
          )
        
    }
  })

  
  ReactDOM.render(
  <HelloComponent  />,
    document.getElementById('example')
  );

条件判断

JSX中,不能使用 if/else语句,替代方案:三目运算符

        var MessageBox = React.createClass({
            getInitialState:function(){
              return {
                isComplete:true
              }
            },
            getIsComplete:function(){
              return this.state.isComplete ?'is-complete':''
            },
            render: function(){
            
              return (
                <div className={this.getIsComplete()}>
                  hi all
                </div>
              )
            }
          });

注意,这里说的是在HTML中不能使用if/else语句,在函数中依然可以使用。

使用子节点

React将开始标签和结束标签中的所有子节点保存在一个叫this.props.children 的特殊组件属性内。

  var SubMessageComp = React.createClass({
    render: function(){
      return (
        <div>
          {this.props.children}
        </div>
      )
    }
  });
  var MessageBox = React.createClass({
    render: function(){
      return (
        <SubMessageComp>
            <li>a</li>
            <li>b</li>
        </SubMessageComp>
      )
    }
  });

注释:

JSX本质上就是JavaScript,因此可以再标签内添加原声的JavaScript注释

        render: function(){
          //普通javascript注释
          return (
            //普通javascript注释
            <div /*内联注释*/ className={this.getIsComplete()}>
              {/*这里节点内注释*/}
              hi all
            </div>
          )
        }

//注释不能放置在return 结构内

/*内联注释*/ 内联注释不要放置到节点内(会自动被当成一个span标签)

{/*节点内注释*/} 节点内注释不要和内联注释替换位置

虚拟DOM

Virtual DOM 用于优化视图的渲染和刷新。以前我们更新视图时,需要先清空DOM容器中的内容,然后将最新的DOM和数据追加到容器中(重绘与重排),现在React将这一操作放到了内存中。

React 会在内存中维护一个虚拟 DOM 树,当我们对这个树进行读或写的时候,实际上是对虚拟 DOM 进行的。当数据变化时,然后 React 会自动更新虚拟 DOM,然后拿新的虚拟 DOM 和旧的虚拟 DOM 执行Diff算法,找到有变更的部分,得出一个Patch,然后将这个 Patch 放到一个队列里,最终批量更新这些 Patch 到 DOM 中。

虚拟DOM

这样的机制可以保证即便是根节点数据的变化,最终表现在 DOM 上的修改也只是受这个数据影响的部分,可以保证非常高效的渲染。

单向事件流

在jquery时代,我们都是基于事件驱动,对于简单的交互需求而言,这确实足够了,而且开发起来非常迅速。但业务一旦复杂,这种基于事件驱动的东西就会变得很乱:某个事件,更新的DOM逐渐变多,不好管理,就容易出错。

React中有单向数据流的概:更新 DOM 的数据总是从顶层流下来,用户事件不直接操作 DOM,而是操作顶层数据。这些数据从顶层流下来同时更新了DOM。你的代码就很少会直接处理DOM,而是只处理数据的变更。这样会很大程度上简化代码和逻辑。

举个例子:我点击一个button,然后页面上一个span里数字+1,原有的思考逻辑是“点击发生,然后数据变化,然后UI跟着变化+1”。而现在的思考逻辑是我的数据变化了,那么我的UI会自动更新,那么我只用考虑“点击发生,数据变化”。甚至可以把UI和数据变化分层然后处理。

具体地说:

在一个多组件结构里,一个父组件需要负责管理状态,并把数据通过props向下发放。

组件的状态通过setState方法更新。数据通过设置子组件的属性来传递给子组件。

子组件通过this.props来获取这些数据,通过数据和自身状态绑定事件UI,触发UI的改变。

例子: 实现一个动态查找框

var FilteredList = React.createClass({
filteredList: function (event) {
    var updateList = this.state.initialItems;
    updateList = updateList.filter(function (item) {
        return item.toLowerCase().search(event.target.value.toLowerCase()) !== -1;
    });
    this.setState({items: updateList});
},
getInitialState: function () {
    return {
        initialItems: ["javascript", "java", "c", "c++", "python", "php", "nodejs","react"],
        items: []
    }
},
componentWillMount: function () {
    this.setState({items: this.state.initialItems});
},
render: function () {
    return (
        <div className="filter-class">
            <input type="text" placeholder="Search" onChange={this.filteredList} />  
            <List items={this.state.items} />
        </div>
    )
}
})
 
var List = React.createClass({
    render: function () {
        //List组件中通过this.props来获取到FilteredList传递进来的值
        return (
            <ul>
                {
                    this.props.items.map(function (item) {
                        return <li key={item}>{item}</li>
                    })
                }
            </ul>
        )
    }
});
 
React.render(<FilteredList/>, document.getElementById("example"));

组件

创建组件

对于React应用而言,你需要分割你的页面,使其成为一个个的组件。也就是说你的应用是由一个个组件构成的。

使用React.createClass方法创建组件。

    var HelloMessage = React.createClass({
      render: function() {
        return <div>Hello {this.props.name}</div>;
      }
    });
    
    // 加载组件到 DOM 元素 mountNode
    React.render(<HelloMessage name="feng" />, mountNode);

组件首字母必须大写,用来区分是组件标签还是HTML标签

props

props是一个对象,是组件用来接收外面传来数据的容器。组件内部是不允许修改自己的props属性的,只能够通过父组件来修改。具体的使用方法如下

state属性的用法

React把组件当成状态机,状态(state)是子组件内部维护的数据,一旦用户交互导致状态发生变化,触发不同的钩子函数(详见下文中生命周期),组件也会进行更新,重新渲染UI。

生命周期

生命周期各阶段

在整个ReactJS的声明周期中,主要会经历这四个阶段:创建阶段、实例化阶段、更新阶段、销毁阶段

  var OneComponent = React.createClass({
    clickHandle: function(){
        this.setState({
            count: this.state.count + 1
        })
    },
    //1.创建阶段
    getDefaultProps: function() {
      //在创建类的时候被调用
      console.log('getDefaultProps');
      return {
        count: 0
      }
    },

    //2.实例化阶段
    getInitialState: function() {
      //获取this.state的默认值
      console.log('getInitialState');
      return {};
    },
    componentWillMount: function() {
      //在render之前调用此方法
      //业务逻辑的处理都应该放在这里,比如对state的操作等
      console.log('componentWillMount')
    },
    render: function() {
      //渲染并返回一个虚拟DOM
      console.log('render');
      return (
          <div> hello
               <b> {this.props.name} 
               </b>
               <button onClick={this.clickHandle}>Click me</button>
           </div>
        )
    },
    componentDidMount: function() {
      //该方法发生在render方法之后
      //在该方法中,ReactJS会使用render方法返回的虚拟DOM对象来创建真实DOM结构
      console.log('componentDidMount');
      console.log('###end###')
    },

    //3.个更新阶段
    componentWillReceieveProps: function() {
      //该方法发生在this.props被修改或父组件调用setProps()方法之后
      console.log('componentWillRecieveProps');
    },
    shouldComponentUpdate: function() {
      //是否需要更新
      console.log('shouldComponentUpdate');
      return true;
    },
    componentWillUpadate: function() {
      //将要更新
      console.log('componentWillUpadate');
    },
    componentDidUpdate: function() {
      //更新完毕
      console.log('componentDidUpdate');
    },
    //4.销毁阶段
    componentWillUnmout: function() {
      //销毁时被调用
      console.log('componentWillUnmout');
    }
  })

  
  ReactDOM.render(
  <OneComponent  name="World "/>,
    document.getElementById('example')
  );

创建阶段:

该阶段主要发生在创建组件类的时候,即在调用React.createClass的时候。这个阶段只会触发一个getDefaultProps方法,该方法会返回一个对象,并缓存下来。然后与父组件制定的props对象合并,最后赋值给this.props作为该组件的默认属性。

实例化阶段

该阶段主要发生在实例化组件类的时候,也就是组件类被调用的时候。

  ReactDOM.render(
  <OneComponent  name="World "/>,
    document.getElementById('example')
  );

该组件被调用的时候,这个阶段会触发一系列的流程,具体的执行顺序如下所示。

更新阶段

主要发生在用户操作或者组件有更新的时候,此时会根据用户的操作行为进行相应的页面结构的调整。该阶段也会触发一系列的流程,具体的执行顺序如下所示。

销毁阶段

componentWillMount、componentDidMount和componentWillUpdate、componentDidUpdate可以对应起来。区别在于,前者只有在挂载的时候会被调用;而后者在以后的每次更新渲染之后都会被调用。

组件生命周期流程图

组件之间的通信

先创建一个父类组件Parent,它内部调用一个叫做Child的子组件,并将接收到的外部参数name传递给子组件Child。

var Parent = React.createClass({
    handleClick: function() {
      this.refs.myTextInput.focus();
    },
    render: function() {
      return (
          <div onClick={this.handleClick}>
            <input type="text" ref="myTextInput" />
            Parent is:
              <Child name={this.props.name}></Child>
          </div>
        )
    }
});

在创建一个子类组件Child

  var Child = React.createClass({
    render: function() {
      return <span>{this.props.name}</span>
    }
  });

自后通过React.render方法将组件渲染到页面上

  ReactDOM.render(
    <Parent name="React" />,document.getElementById('example')
  );

运行结果

上一篇下一篇

猜你喜欢

热点阅读