zoukankan      html  css  js  c++  java
  • client高性能组件化框架React简单介绍、特点、环境搭建及经常使用语法

    【本文源址:http://blog.csdn.net/q1056843325/article/details/54729657 转载请加入该地址】

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

    简单介绍

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

    React达到了5w8+的star
    在JavaScript中star排名第4
    受欢迎程度可见一斑

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


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

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

    总之就是Facebook对市场上全部JS-MVC框架都不惬意。觉得都不适合大规模应用
    就自己写了一套,用来架设Instagram站点
    写完后用着用着,发现哎呦这货还真是不错,然后就开源了
    随着这几年的沉淀。React已经变得越来越强大了
    值得我们去了解~

    MVC

    科普一下MVC
    MVC是一种软件架构模式(后端影响到了前端)
    MVC就分为M、V、C三部分

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

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


    这里我要特别说明一下,尽管这里介绍了MVC
    可是不能说React就是MVC框架
    能够说React是用于构建组件化UI的库。是一个前端界面开发工具
    它能够作为MVC中的View视图部分

    框架特点

    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渲染就可以
      (往下看就理解了)

    环境搭建

    我选择使用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当中一个基本的设计理念是编写简单且easy理解的代码
    但我们为了实现组件化确实不方便松耦合
    大家也不要过分较真


    这种语法结构是如何解析的呢?事实上并不奇妙
    JSX的语法规则:

    • 遇到 HTML 标签(以 < 开头),就使用 HTML 规则解析
    • 遇到代码块(以 { 开头)。就使用 JavaScript 规则解析
    • 代码块中假设仅仅有一个数组变量,就展开这个数组的全部成员

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

    渲染到页面

    最终写到语法正题了
    在此之前我们必须要引用的两个对象
    一个React核心对象和一个React-Dom对象
    (这里就先不使用ES6的语法了)

    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和组件相关逻辑都封装在组件内部。便于维护

    组件生成

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

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

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

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

    所以,各位,以下的写法都是不正确的

    //错误的写法:变量名首字母没大写
    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>
            )
        }
    });

    组件中html语法两边加括号的目的
    是为了
    防止JavaScript自己主动分号机制产生问题

    组件渲染

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

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

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

    组件特性

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

    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

    由于他们是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.B','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数组

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

    var data = ['Mr.A','Mr.B','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.B','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','Mr.B','Mr.C'];
    var List = React.createClass({
        render: function(){
            return (
                <ol>
                    {
                        this.props.data.map(function(item, 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'
      • getInitialState( )
        • 作用于组件的实例,返回对象作为this.state的初始值
        • e,g.return {show: false} 相当于初始化了组件状态this.state.show = false
      • componentWillMount( )
        • 首次渲染前调用,可做一些业务初始化操作,也能够通过this.setState()设置组件状态
        • e,g.this.setState({show: false})
      • render( )
        • 必选方法,用于创建虚拟DOM,有特殊规则(再啰嗦一遍)
          • 仅仅能通过this.props和this.state訪问数据
          • 能够返回null、false或不论什么React组件
          • 仅仅能出现一个顶级组件(不能返回数组)
          • 不能改变组件的状态
          • 不能改动DOM的输出
      • componentDidMount( )(server端不会调用)
        • 真实DOM被渲染后调用,可通过this.getDOMNode()訪问到真实的DOM元素
          此时可使用其它类库来操作该DOM
    • 组件存在期Updateing【组件更新时触发】(state,props变化触发)
      • componentWillReceiveProps( )*
        • 组件接收新props时调用。并将其作为參数nextProps使用。此时能够更改组件props及state
      • shouldComponentUpdate( )*
        • 组件是否应当渲染新props或state
          返回false表示跳过兴许生命周期方法(通常不须要使用以避免出现bug)
          在出现应用瓶颈时。可通过该方法进行适当的优化。


          在首次渲染期间或者调用了forceUpdate方法后,该方法不会被调用

      • componentWillUpdate( )
        • 接收到新props或state后,进行渲染前调用,此时不同意更新props或state
      • render( )
        • 不再赘述
      • componentDidUpdate( )*
        • 完毕渲染新的props或state后调用。此时能够訪问到新的DOM元素
    • 组件销毁期Unmounting【组件销毁时触发】
      • componentWillUnmount()*
        • 组件移除前调用,可用于做一些清理工作
          在componentDidMount中加入的全部任务都须要在该方法中撤销(e.g.定时器、事件监听器等等)


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

    关于这些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',
                 '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',
                left: '40px',
                top: '40px',
                 '100px',
                height: '100px',
                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
            }
        },
        buttonHandler: function(){
            this.setState({
                open: true
            });
        },
        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',
                left: '40px',
                top: '40px',
                 '100px',
                height: '100px',
                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中的输入字符串
    步骤也非常easy,完整代码例如以下

    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]
    否则会报错的


    ==主页传送门==

  • 相关阅读:
    jQuery禁用或启用
    ASP.NET MVC一次删除多笔记录
    在你的ASP.NET MVC中使用查找功能
    Get radio selected value
    绑定一个值给radio
    ASP.NET MVC实现权限控制
    为Guid数据类型的属性(property)赋值
    Razor语法中绑定一个值给checkbox
    判断IEnumerable<T>集合中是否包含有T对象
    SqlDateTime overflow. Must be between 1/1/1753 12:00:00 AM and 12/31/9999 11:59:59 PM.
  • 原文地址:https://www.cnblogs.com/lytwajue/p/7344543.html
Copyright © 2011-2022 走看看