前端已不止于前端-ReactJs初体验

@kuitos 2015-08-23 09:44:06发表于 kuitos/kuitos.github.io React前端架构

前端已不止于前端-ReactJs初体验

原文写于 2015-04-15

要说2015年前端届最备受瞩目的技术是啥,当然非ReactJs莫属。作为一个只关注最前沿前端技术的系列,自然少不了关于它的介绍。

ReactJs简介

  • React最初来自Facebook内部的广告系统项目,项目实施过程中前端开发遇到了巨大挑战,代码变得越来越臃肿且混乱不堪,难以维护。于是痛定思痛,他们决定抛开很多所谓的“最佳实践”,重新思考前端界面的构建方式,于是就有了React。
  • React的设计依托于Facebook另一个叫做FLUX的项目,FLUX是一个为了解决Facebook在MVC应用中碰到的工程性问题而生的设计模式,主要思路是单向数据流。解析 Facebook 的 Flux 应用架构
  • React是MVC中薄薄的一层V,它只关注表现层,对组件化开发有很大的裨益。

ReactJs核心特征

  • virtual dom react中的组件跟页面真实dom之间会有一层virtual dom(虚拟dom),virtual dom是内存中的javascript对象,它具有与真实dom一致的树状结构。开发者每次试图更新view,react都会重新构建virtual dom树,然后将其与上一次virtual dom树作对比,依靠react强劲的核心算法dom diff找出真正发生变更的节点,最后映射到真实dom上,这也是react号称性能高效的秘密所在。依赖于virtual dom,对React而言,每一次界面的更新都是整体更新,而不是层叠式更新(即一个复杂的,各个UI之间可能存在互相依赖的时候,每一次独立的更新可能会引发其他UI的变化,假如我们的目的是更新C的数据,逻辑流很可能是这样的 A -->B-->C-->A-->B–>C,这种情况下中间状态的DOM操作就是极大的浪费)。
  • 单向数据流 flux架构下的数据流呈现出一种单向、闭环的流动路线,使得一切行为变的可预测,也能更好的定位错误发生点。react官方的卖点之一就是 友好的错误提示(这是在针对angular么哈哈)
  • 每个组件都是状态机 react认为组件的数据模型是不可变的,组件的属性不应该被修改。组件关注的只应该是状态,不同的状态呈现不同的表现形式。每个状态下的组件都是一个virtual dom对象,这样react就能直接通过等号对比对象地址判断组件是否被修改从而是否需要更新dom,这也是其能提高性能的原因之一(空间换时间)。
  • 组件 一切都是组件 react倡导开发者将view切分成一个个组件从而达到松耦合及重用的目的。开发者构建页面只需要排列组合就行了。
  • immutable object React提倡使用只读数据来建立数据模型,每次更新都是new object,这也是dom diff 性能强劲的密码所在(===即可判断两个对象是否相等,而不需要深度遍历)。参考资料 immutable.js
  • JSX 不是在js里面写html,jsx是xml的javascript表示法。

说了这么多理论性的东西,还是直接来上代码吧

  1. ReactJs开发准备工作

    1. 首先你需要reactjs的开发环境。

      bower install react
    2. 脚本中引入react,由于我们需要使用jsx语法提高开发效率,所以还需要引入能讲jsx转化为javascript的库
      不过这样JSXTransformer每次都会在app打开的时候做转换工作,并不适合生产环境,转换工作应该放在服务端进行,借助jsx工具

      npm install -g react-tools
      jsx --watch src/ build/

      然后页面依赖改成这样
      node插件会在你开发的时候自动将源码转成javascript文件到指定目录

  2. 第一个react程序

    // Hello World
    React.render(
        <h1>Hello, world!</h1>,
        document.getElementById('example')
    );
  3. 接下来我们介绍一下react的一些基础语法

    1. React.render() 将模版转换成html并插入到指定节点 参见上文的hello world示例

    2. React解析引擎的规则就是遇到<符号就以jsx语法解析,遇到{就以javascript语法解析。比如这样

      var array = [
          <h1>Example 2</h1>,
          <h2>Hello World</h2>
      ];
      
      React.render(
          <div>{array}</div>,
          document.getElementById("example2")
      );

      通过查看转换后的代码,我们可以看到他摘下面具后长这样

      var array = [
          React.createElement("h1", null, "Example 2"),
          React.createElement("h2", null, "Hello World")
      ];
      
      React.render(
          React.createElement("div", null, array),
          document.getElementById("example2")
      );
    3. 如何创建组件

      var HelloWorldComponent = React.createClass({
          render: function () {
              return <div>React Component {this.props.author}</div>;
          }
      });
      
      React.render(
          <HelloWorldComponent author="Kuitos"/>,
          document.getElementById("hello")
      );

      通过React.createClass可以创建一个关联了虚拟dom的组件对象,每次组件数据更新便会调用组件的 render 方法重新渲染dom。

    4. 组件对象的props属性
      上面一个例子我们看到在组件render方法中我们可以通过this.props.xx的方式拿到组件上关联的属性。另外需要额外提到的是,this.props有一个特殊属性children,它指向组件的子节点集合,like this

      var List = React.createClass({
          render: function () {
              return (
                  <ol>
                      {
                          this.props.children.map(function (child) {
                              return <li>{child}</li>
                          })
                      }
                  </ol>
              );
          }
      });
      
      React.render(
          <List>
              <a href="#">百度</a>
              <a href="#">谷歌</a>
          </List>,
          document.getElementById("example3")
      );

      页面渲染的结果就是一个 ol 列表中还有两个li,每个li中包含一个超链接。通过这里我们也可以看出,在jsx中{}是会getValue的

    5. 获取真实dom React.findDOMNode()

      var counter = 0;
      var Button = React.createClass({
      
          handleClick: function () {
              React.findDOMNode(this.refs.input).focus();
          },
      
          render: function () {
      
              return (
                  <div>
                      <input type="text" ref="input"/>
                      <input type="button" value="counter" onClick={this.handleClick}/>
                  </div>
              );
      
          }
      });
      
      React.render(
          <Button />,
          document.getElementById("button")
      );
    6. 组件状态 this.state

      var Toggle = React.createClass({
      
          getInitialState: function () {
              return {liked: false};
          },
      
          handleClick: function (event) {
              this.setState({liked: !this.state.liked});
          },
      
          render: function () {
      
              var text = this.state.liked ? "like" : "unlike";
      
              return (
                  <p onClick={this.handleClick}>
                      U {text} this.
                  </p>
              );
          }
      });
      
      React.render(
          <Toggle />,
          document.getElementById("button1")
      );
    7. 用React的方式实现angular中双向绑定的效果

      var Input = React.createClass({
      
          getInitialState: function () {
              return {value: "Kuitos"};
          },
      
          handleChange: function (event) {
              this.setState({value: event.target.value});
          },
      
          render: function () {
      
              var value = this.state.value;
      
              return (
                  <div>
                      <p>{value}</p>
                      <input type="text" value={value} onChange={this.handleChange}/>
                  </div>
              );
          }
      });
      
      React.render(
          <Input/>,
          document.getElementById("inputDataBind")
      );
    8. virtual dom状态变更回调

      组件生命周期分为三个状态:

      • Mouting: 已插入真实 DOM
      • Updating: 正在被重新渲染
      • Unmounting: 已移出真实 DOM
      • React为每个状态都提供相应的pre跟post处理函数。只不过React的命名是will(pre 进入状态之前)跟did(post 进入状态之后)。
      • componentWillMount()
      • componentDidMount()
      • componentWillUpdate(Object nextProps, Object nextState)
      • componentDidUpdate(Object prevProps, Object prevState)
      • componentWillUnmount()
        我们这样写
      var Input = React.createClass({
      
          getInitialState: function () {
              return {firstName: "Kuitos", lastName: "Lau"};
          },
      
          handleChange: function (event) {
              this.setState({firstName: event.target.value});
          },
      
          componentWillMount: function () {
              console.log("dom will be insert", this.state.firstName);
          },
      
          componentDidMount: function () {
              console.log("dom had be insert", this.state.firstName);
          },
      
          componentWillUpdate: function (nextProps, nextState) {
              console.log("dom will be update", nextProps, nextState);
          },
      
          componentDidUpdate: function (prevProps, prevState) {
              console.log("dom had be update", prevProps, prevState);
          },
      
          render: function () {
      
              var state = this.state;
      
              return (
                  <div>
                      <p>{state.firstName} {state.lastName}</p>
                      <input type="text" value={state.firstName} author={state.firstName} onChange={this.handleChange}/>
                  </div>
              );
          }
      });
      
      React.render(
          <Input/>,
          document.getElementById("inputDataBind")
      );

      打印的顺序依次是,dom will be update , dom had be update
      当input输入时 dom will be update , dom had be update

react的基本知识就介绍到这里,后续我们会继续介绍react在实战项目中的应用及react native在移动端的表现力。