客户端高性能组件化框架React简介、特点、环境搭建及常用语法

前端之家收集整理的这篇文章主要介绍了客户端高性能组件化框架React简介、特点、环境搭建及常用语法前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

明天就是除夕了
预祝大家新春快乐 [ ]~( ̄▽ ̄)~*
天天饭局搞得我是身心疲惫= =
所以更新比较慢
今天想跟大家分享的就是这个大名鼎鼎的React框架

简介

React是这两年非常流行的框架
流行到什么程度呢?
我看了一下Github上的数据

React达到了5w8+的star(1w+就很了不起了)
在JavaScript中star排名第4
受欢迎程度可见一斑

感兴趣的同学,给大家设置一个传送门:Github-JavaScript-most stars


React并不难,还是挺容易上手的
起源于Facebook内部项目(一个广告系统)

传统页面从服务器获取数据,显示到浏览器上,用户输入数据传入服务器
但随着数据量增大,越来越难以维护了
Facebook觉得MVC不能满足他们的扩展需求了(巨大的代码库和庞大的组织)
每当需要添加一项新功能或特性时,系统复杂度就几何增长
致使代码脆弱不堪、不可预测,结果导致他们的MVC正走向崩溃
当系统中有很多的模型和相应视图时,其复杂度就会迅速扩大,非常难以理解和调试

总之就是Facebook对市场上所有JS-MVC框架都不满意,认为都不适合大规模应用
就自己写了一套,用来架设Instagram网站
写完后用着用着,发现哎呦这货还真是不错,然后就开源了
随着这几年的沉淀,React已经变得越来越强大了

MVC

科普一下MVC
MVC就分为M、V、C三部分

  • Model(模型):
    应用程序中用于处理应用程序数据逻辑的部分, 通常负责在数据库中存取数据
  • View(视图):
    应用程序中处理数据显示的部分, 通常依据模型数据创建
  • Controller(控制器):
    应用程序中处理用户交互的部分, 通常负责从视图读取数据,控制用户输入,并向模型发送数据
  • @H_502_50@

    简单的理解一下
    我们就是user,在页面中点击了一个按钮触发了事件
    控制器Controller调整数据,模型Model中数据改变
    数据改变又会导致视图View更新
    UI的改变反馈呈现给我们user


    这里我要特别说明一下,虽然介绍了MVC
    但是React不是MVC框架
    而是用于构建组件化UI的库,是一个前端界面开发工具
    顶多算作MVC中的View视图
    而且MVC更多的是数据双向绑定
    但我们React是单向数据流

    框架特点

    React它具有以下特点

    • 性能
      传统web页面操作DOM涉及重绘重排相当耗性能
      React 提供了一种不同而又强大的方式来更新Dom(轻量级虚拟Dom——Virtual Dom),代替直接操作DOM
      更新virtual dom时不一定马上影响真实Dom,React会等到事件循环结束
      利用diff算法,通过当前新Dom表述与之前做比较,计算出最小步骤来更新真实Dom
    • 组件化
      Dom树上的节点称为元素,而虚拟Dom 的节点称作组件(可复用性)
      (组件的特定下面还会谈到)
    • 可预测性
      state属性包含定义组件所需要的一些数据,当数据发生变化时,将会调用Render重现渲染
      React 把组件看成是一个状态机(State Machines)
      通过与用户的交互,实现不同状态,然后渲染 UI,让用户界面和数据保持一致
    • 单向数据流
      数据从父节点传递到子节点,只需要从父节点获取props渲染即可
      (往下看就理解了)
    • @H_502_50@

      环境搭建

      我选择使用webpack搭建环境
      关于webpack就不多说了,提一下重点
      这是我的webpack.config.js配置文件

      module.exports = {
          entry: {
              index: './src/js/entry.js'
          },output: {
              path: './static/dist/',publicPath: 'http://localhost:8080/static/dist/',filename: '[name].js'
          },module: {
              loaders: [
                  {
                      test: /\.js$/,loader: 'babel',query: {
                          presets: ['react','es2015']
                      }
                  },{
                      test: /.less$/,loader: 'style!css!less'
                  }
              ]
          }
      }

      这里的关键就是除了要安装babel-loaderbabel-core
      还要安装babel-preset-es2015babel-preset-react
      用于解析ES6语法(为了老版本浏览器)和React的JSX语法
      还有reactreact-dom也是必须要下载的依赖


      全部依赖模块在这里

      "devDependencies": {
        "babel-core": "^6.22.1","babel-loader": "^6.2.10","babel-preset-es2015": "^6.22.0","babel-preset-react": "^6.22.0","css-loader": "^0.26.1","less": "^2.7.2","less-loader": "^2.2.3","react": "^15.4.2","react-dom": "^15.4.2","style-loader": "^0.13.1","webpack": "^1.14.0","webpack-dev-server": "^1.16.2"
      }

      JSX

      简单说一下React的JSX语法是个神马东西
      可能一会儿大家会看大不明觉厉的代码
      比如

      return (
          <div>hehe<div>
      )

      这就是JSX代码,它是React提供的语法糖
      是React的重要组成部分,使用类似XML标记的方式来声明界面及关系
      语法糖的意思我写ES6的时候也说了
      就是计算机语言中添加的语法,对语言的功能没影响
      方便我们开发人员使用的,可以增强可读性

      如果使用JS代码也可以,不过官方推荐使用JSX
      这样来写,结构层次关系都很清晰
      webpack会帮我们把他们转换成浏览器认识的js代码(loader的作用)
      (如果好奇转换成了什么,可以去webpack输出文件查看,或者找jsx转js的工具)


      JSX语法结构说的通俗一点就是HTML、JS混写
      可能大家会有疑惑,说好的结构、样式、行为相分离的前端思想呢?!
      React其中一个主要的设计理念是编写简单容易理解的代码
      但我们的组件化确实不方便松耦合
      大家也不要过分较真


      这样的语法结构是怎样解析的呢?其实并不神奇
      JSX的语法规则:

      • 遇到 HTML 标签(以 < 开头),就用 HTML 规则解析
      • 遇到代码块(以 { 开头),就用 JavaScript 规则解析
      • 代码块中如果只有一个数组变量,就展开这个数组的所有成员
      • @H_502_50@

        不理解不要慌,看了下面就懂了
        提前渗透一下

        渲染到页面

        终于写到语法正题了
        在此之前我们必须要引用的两个对象
        一个React核心对象和一个React-Dom对象

        var React = require('react');
        var ReactDom = require('react-dom');

        ReactDom.render()是react最最基本的方法
        所以我放到最开始来讲
        它通过ReactDom将我们的组件渲染到页面
        我在页面添加一个节点

        <div id="root"></div>

        现在页面中什么也没有
        不过马上就有了

        ReactDom.render(
            <h1>Demo</h1>,document.getElementById('demo')
        );

        第一个参数是要插入的组件(不过我们这里插入了一个DOM节点)
        第二个参数就是要渲染的DOM节点
        (React不建议直接添加到body标签document.body)

        页面中出现了“Demo”

        实际上react将我们的节点插入到了div节点的内部

        组件化

        React的一大特点就是组件化
        React组件Component有以下特点

        • 组合:简单组件可组合为复杂组件
        • 重用:组件独立,可被多个组件使用
        • 测试:组件独立,便于测试
        • 维护:UI和组件相关逻辑都封装在组件内部,便于维护
        • @H_502_50@

          组件生成

          React.createClass()就是用于将代码封装成组件的方法
          它会生成一个React组件

          var App = React.createClass({
              render: function(){
                  return (
                      <p>This is a component...</p>
                  )
              }
          });

          这个方法参数是一个对象
          对象中有一个render返回一个组件
          render是输出组件必须要写的(关于它下面还会再说)
          先记住两点

          • 组件名首字母一定大写,否则会报错
          • 输出的组件只能有一个顶级标签,其他标签会失效
          • @H_502_50@

            所以各位,下面的写法都是不对的

            //错误的写法
            var app = React.createClass({
                render: function(){
                    return <p>This is a component...</p>
                }
            })
            //错误的写法
            var App = React.createClass({
                render: function(){
                    return (
                        <p>This is a component...</p>
                        <p>This is also a component...</p>
                    )
                }
            });

            组件两边加括号的目的,是为了防止JavaScript自动分号机制产生问题

            组件渲染

            生成的组件要想渲染到页面
            就使用我们刚讲完的ReactDom.render( )

            ReactDom.render(
                <App></App>,document.getElementById('root')
            );

            组件要写成标签的形式
            这里我们就要写<App></App>或者单标签形式<App/>也可以

            组件特性

            为了加以区分,我把标签属性叫组件特性

            var App = React.createClass({
                render: function(){
                    return <p name="demo">This is a component...</p>;
                }
            });
            ReactDom.render(
                <App></App>,document.getElementById('root')
            );

            还要注意两个特例

            • class要写成className
            • for要写成htmlFor
            • @H_502_50@

              因为他们是JavaScript的保留字


              如果想要为组件添加内联样式,可以这样写

              var App = React.createClass({
                  render: function(){
                      var styles = {
                          color: '#fff',backgroundColor: '#000'
                      }
                      return <p className="demo" style={styles}>This is a component...</p>; // <--
                  }
              });
              ReactDom.render(
                  <App></App>,document.getElementById('root')
              );

              声明了一个styles对象
              但是将它添加属性时,要使用 { }
              因为JSX语法中,html中使用js就必须使用大括号
              关于这一点下面就不再赘述了

              组件属性

              this.props

              组件的属性同样可以像html一样添加<App name="payen"></App>
              并且这个组件属性内部可以通过this.props对象获取

              var App = React.createClass({
                  render: function(){
                      return <p>name:{this.props.name} age:{this.props.age}</p>;
                  }
              });
              ReactDom.render(
                  <App name="payen" age="20"></App>,document.getElementById('root')
              );

              了解了这个,我们可以做一个小练习
              现在有一组数据,利用它组成一个有序列表组件

              var data = ['Mr.A','Mr.B','Mr.C'];

              可以将这个数组成为组件属性
              然后利用this.props.data获取数据
              最后使用ES5数组的map方法就大功告成了

              var List = React.createClass({
                  render: function(){
                      return (
                          <ol>
                              {
                                  this.props.data.map(function(item,index){
                                      return <li key={1000 + index}>{item}</li>;
                                  })
                              }
                          </ol>
                      )    
                  }
              });
              ReactDom.render(
                  <List data={data}></List>,document.getElementById('root')
              );

              还要注意<li key={1000 + index}>{item}</li>
              key值如果不写的话,虽然可以正常渲染
              但会警告我们数组或迭代器的每一项都应该有一个独一无二的key值

              这里我就使用了1000加上索引的形式添加了key值

              this.props.children

              通常组件的属性与this.props对象中的属性是一一对应的
              但有一个例外,它是this.props.children
              它表示我们组件的所有子节点
              什么意思呢?接着我们上面的例子
              我们在List组件中添加一些子节点
              修改ReactDom.render( )方法的参数

              ReactDom.render(
                  <List data={data}>
                      <span>Mr.D</span>
                      <span>Mr.E</span>
                  </List>,document.getElementById('root')
              );

              我们发现页面中并没有什么变化,但浏览器也没有报错
              这时我们需要使用this.props.children

              var data = ['Mr.A','Mr.C'];
              var List = React.createClass({
                  render: function(){
                      console.log(this.props.children);
                      return (
                          <ol>
                              {
                                  this.props.data.map(function(item,index){
                                      return <li key={1000 + index}>{item}</li>;
                                  })
                              }
                              {
                                  this.props.children
                              }
                          </ol>
                      )    
                  }
              });
              ReactDom.render(
                  <List data={data}>
                      <span>Mr.D</span>
                      <span>Mr.E</span>
                  </List>,document.getElementById('root')
              );

              如此页面中就显示出了子节点

              这个this.props.children很奇怪,它有三种类型值

              • 没有子节点,值为undefined
              • 有一个子节点,值为object对象
              • 有多个子节点,值为array数组
              • @H_502_50@

                (可以在控制台上输出验证)
                所以我们处理它要特别小心
                好在我们可以使用React给我们提供的方法
                利用React.Children.map( )我们就可以放心遍历处理子节点

                var data = ['Mr.A','Mr.C'];
                var List = React.createClass({
                    render: function(){
                        return (
                            <ol>
                                {
                                    this.props.data.map(function(item,index){
                                        return <li key={1000 + index}>{item}</li>;
                                    })
                                }
                                {
                                    React.Children.map(this.props.children,function(child){
                                        return <li>{child}</li>
                                    })
                                }
                            </ol>
                        )    
                    }
                });
                ReactDom.render(
                    <List data={data}>
                        <span>Mr.D</span>
                        <span>Mr.E</span>
                    </List>,document.getElementById('root')
                );


                组件验证

                组件的属性可以接受任何值,数字、字符串、函数、对象什么都可以
                但有时候,我们拿到一个组件,想要验证参数是否符合我们的要求(这其实很重要,不要轻视)
                这时就需要使用组件的propTypes( )方法和React.PropTypes配合验证了

                var data = ['Mr.A','Mr.C'];
                var App = React.createClass({
                    propTypes: {
                        data: React.PropTypes.array
                    },render: function(){
                        return (
                            <div>{this.props.data}</div>
                        )
                    }
                });
                ReactDom.render(
                    <App data={data}></App>,document.getElementById('root')
                );

                这里我期望的data属性值为array数组类型
                没有任何问题,因为我们传入的就是数组
                可是如果改成期望字符串类型data: React.PropTypes.string

                浏览器就会发出警告

                详细见React中文官网:Prop 验证

                组件嵌套

                还记得React单向数据流的特点么
                也就是说我们应该把数据传递给父节点
                父节点通过this.prop将数据传递给子节点
                子节点再通过自己的this.prop处理收到的数据

                var data = ['Mr.A',index){
                                        return <li key={1000 + index}>{item}</li>; 
                                    })
                                }
                            </ol>
                        )    
                    }
                });
                var App = React.createClass({
                    render: function(){
                        return (
                            <div>
                                <List data={this.props.data}></List>
                            </div>
                        )
                    }
                });
                ReactDom.render(
                    <App data={data}></App>,document.getElementById('root')
                );

                所呈现的DOM结构

                生命周期

                生命周期不难理解
                组件的一生无非就是产生、更新、销毁
                在组件的每一个生命周期内,都会按顺序触发一些组件方法
                比如我们刚刚的render方法就会在产生和更新的阶段都会触发
                具体触发的回调函数API以及作用给大家整理在下面
                (关于它们在整个生命周期的触发次数大家应该都能想明白就不写了)
                (不常用的我在后面的标注了*号)

                • 组件实例化Mouting【组件生成时触发】
                  • getDefaultProps( )
                    • 作用于组件类,返回对象用于设置默认的this.props(引用值会在实例中共享)
                    • e,g.return {name: 'payen'} 相当于初始化了组件属性this.props.name = 'payen'
                    • @H_502_50@
                    • getInitialState( )
                      • 作用于组件的实例,返回对象作为this.state的初始值
                      • e,g.return {show: false} 相当于初始化了组件状态this.state.show = false
                      • @H_502_50@
                      • componentWillMount( )
                        • 首次渲染前调用,可做一些业务初始化操作,也可以通过this.setState()设置组件状态
                        • e,g.this.setState({show: false})
                        • @H_502_50@
                        • render( )
                          • 必选方法,用于创建虚拟DOM,有特殊规则(再啰嗦一遍)
                            • 只能通过this.props和this.state访问数据
                            • 可以返回null、false或任何React组件
                            • 只能出现一个顶级组件(不能返回数组)
                            • 不能改变组件的状态
                            • 不能修改DOM的输出
                            • @H_502_50@ @H_502_50@
                            • componentDidMount( )(服务器端不会调用
                              • 真实DOM被渲染后调用,可通过this.getDOMNode()访问到真实的DOM元素
                                此时可使用其他类库来操作该DOM
                              • @H_502_50@ @H_502_50@
                              • 组件存在期Updateing【组件更新时触发】(state,props变化触发)
                                • componentWillReceiveProps( )*
                                  • 组件接收新props时调用,并将其作为参数nextProps使用,此时可以更改组件props及state
                                  • @H_502_50@
                                  • shouldComponentUpdate( )*
                                    • 组件是否应当渲染新props或state
                                      返回false表示跳过后续生命周期方法(通常不需要使用以避免出现bug)
                                      在出现应用瓶颈时,可通过该方法进行适当的优化。
                                      在首次渲染期间或者调用了forceUpdate方法后,该方法不会被调用
                                    • @H_502_50@
                                    • componentWillUpdate( )
                                      • 接收到新props或state后,进行渲染前调用,此时不允许更新props或state
                                      • @H_502_50@
                                      • render( )
                                        • 不再赘述
                                        • @H_502_50@
                                        • componentDidUpdate( )*
                                          • 完成渲染新的props或state后调用,此时可以访问到新的DOM元素
                                          • @H_502_50@ @H_502_50@
                                          • 组件销毁期Unmounting组件销毁时触发】
                                            • componentWillUnmount()*
                                              • 组件移除前调用,可用于做一些清理工作
                                                在componentDidMount中添加的所有任务都需要在该方法中撤销(e.g.定时器、事件监听器等等)
                                              • @H_502_50@ @H_502_50@ @H_502_50@


                                                附上一张我盗的图,帮助大家理解(手动滑稽)

                                                关于这些API更详细的信息
                                                建议大家可以去React中文官网查看:Component Specs and Lifecycle

                                                组件状态

                                                上面提到了this.state,和我们之前介绍的this.props一样重要
                                                不过this.props通常不会变,但this.state会变
                                                就如其字面意思,表示组件的状态
                                                这个属性是只读的
                                                所以设置状态我们需要使用this.setState( )
                                                可使用this.setState( )的方法
                                                componentWillMount、componentDidMount、componentWillReceiveProps

                                                组件交互

                                                在此之前我们需要了解的就是React的事件系统
                                                JavaScript原始行间绑定事件都是普遍小写<button onclick="clickHandle()"></button>
                                                但我们在React中要使用驼峰写法<button onClick="clickHandle()"></button>

                                                React的事件处理器会传入虚拟事件对象的实例(一个对浏览器本地事件的跨浏览器封装)
                                                它有和浏览器本地事件相同的属性方法包括 stopPropagation() 和 preventDefault(),
                                                但是没有浏览器兼容问题

                                                详细支持事件见中文官网:事件系统-支持的事件


                                                现在我们要来实现这样一个简单的功能
                                                点击按钮,出现弹框
                                                单击弹框,弹框消失

                                                先来实现结构与样式

                                                var App = React.createClass({
                                                    render: function(){
                                                        return (
                                                            <div>
                                                                <button>点击</button>
                                                                <PopUp></PopUp>
                                                            </div>     
                                                        )
                                                    }
                                                });
                                                var PopUp = React.createClass({
                                                    render: function(){
                                                        var styles = {
                                                            position: 'absolute',left: '40px',top: '40px',width: '100px',height: '100px',backgroundColor: '#f40'
                                                        }
                                                        return (
                                                            <div className="popup" style={styles}></div>
                                                        )
                                                    }
                                                })
                                                ReactDom.render(
                                                    <App/>,document.getElementById('root')
                                                );

                                                首先我们先来实现第一个功能:点击按钮出现弹框
                                                问题是改如何实现
                                                我们的React是单向数据流
                                                父级向子级传递数据
                                                最好的办法就是在父级设置组件状态this.state
                                                将状态通过组件属性this.props传递给子级
                                                这样点击事件要做的就是改变父级状态
                                                子级状态也会随之改变

                                                var App = React.createClass({
                                                    getInitialState: function(){
                                                        return {
                                                            open: false
                                                        }
                                                    },buttonHandler: function(){
                                                        this.setState({
                                                            open: true
                                                        });
                                                    },render: function(){
                                                        return (
                                                            <div>
                                                                <button onClick={this.buttonHandler}>点击</button>
                                                                <PopUp open={this.state.open}></PopUp>
                                                            </div>     
                                                        )
                                                    }
                                                });
                                                var PopUp = React.createClass({
                                                    render: function(){
                                                        var styles = {
                                                            position: 'absolute',backgroundColor: '#f40'
                                                        }
                                                        if(this.props.open){
                                                            styles.display = 'block';
                                                        }else{
                                                            styles.display = 'none';
                                                        }
                                                        return (
                                                            <div className="popup" style={styles}></div>
                                                        )
                                                    }
                                                })
                                                ReactDom.render(
                                                    <App/>,document.getElementById('root')
                                                );

                                                第一个功能实现了,再来看第二个
                                                点击弹窗让其消失
                                                同样子级的显示与否掌控在父级手里
                                                要向让子级消失,就必须要改变父级的组件状态this.state
                                                所以我们必须要把事件函数绑定在父级
                                                再利用组件属性this.props将其传递给子级
                                                完整代码如下

                                                var App = React.createClass({
                                                    getInitialState: function(){
                                                        return {
                                                            open: false
                                                        }
                                                    },popupHandler: function(){
                                                        this.setState({
                                                            open: false
                                                        });
                                                    },render: function(){
                                                        return (
                                                            <div>
                                                                <button onClick={this.buttonHandler}>点击</button>
                                                                <PopUp open={this.state.open} handler={this.popupHandler}></PopUp>
                                                            </div>     
                                                        )
                                                    }
                                                });
                                                var PopUp = React.createClass({
                                                    render: function(){
                                                        var styles = {
                                                            position: 'absolute',backgroundColor: '#f40'
                                                        }
                                                        if(this.props.open){
                                                            styles.display = 'block';
                                                        }else{
                                                            styles.display = 'none';
                                                        }
                                                        return (
                                                            <div className="popup" style={styles} onClick={this.props.handler}></div>
                                                        )
                                                    }
                                                })
                                                ReactDom.render(
                                                    <App/>,document.getElementById('root')
                                                );

                                                用一句话来总结一下,那就是数据都交给父级来管理

                                                获取真实DOM节点

                                                我们已经知道了
                                                创建的组件都是虚拟DOM节点
                                                只有当它渲染到了页面,才会成为真正的DOM节点
                                                但是有些时候,我们需要获取到真正的DOM节点
                                                这时需要先设置标签ref属性,再利用组件的this.refs对象获取

                                                还是通过一个小例子来解释
                                                现在要实现这样一个功能
                                                在输入栏中输入字符并在外部实时输出
                                                我们要获取的真实DOM节点就是input中的输入字符串
                                                步骤也很简单,完整代码如下

                                                var Input = React.createClass({
                                                    getInitialState: function(){
                                                        return {
                                                            val: ''
                                                        }
                                                    },changeHandler: function(){
                                                        this.setState({
                                                            val: this.refs.node.value
                                                        });
                                                    },render: function(){
                                                        return (
                                                            <div>
                                                                <input type="text" ref="node" onChange={this.changeHandler}/>
                                                                <p>{this.state.val}</p>
                                                            </div>
                                                        )
                                                    }
                                                });
                                                ReactDom.render(
                                                    <Input/>,document.getElementById('root')
                                                );

                                                我为input标签设置了ref值为node
                                                可以把它理解为为这个节点起了个小名“node”
                                                那么this.refs.node就可以引用这个真实的节点<input/>
                                                通过绑定一个change事件
                                                我们的输入每次改变都会改变组件的状态state
                                                state改变,value就会渲染到页面

                                                获取真实DOM节点还有一个不常用的方法
                                                比如在我们的例子中可以把input标签改成这样

                                                <input type="text" ref={function(dom){this._node = dom}.bind(this)} onChange={this.changeHandler}/>

                                                向ref属性添加一个匿名函数
                                                这个函数的参数就是真实DOM节点
                                                我们可以把它保存下来,比如做为组件的_node属性
                                                不要忘了改变this的指向
                                                事件触发函数就可以通过this._node获取真正的DOM节点

                                                changeHandler: function(){
                                                    this.setState({
                                                        val: this._node.value
                                                    });
                                                }

                                                还要注意的一点是
                                                这个真·DOM节点的获取
                                                必须要等到虚拟DOM插入文档以后,才能使用属性this.refs.[ref-name]
                                                否则会报错的


                                                ==主页传送门==

猜你在找的React相关文章