明天就是春节了 
预祝大家新春快乐 [ ]~( ̄▽ ̄)~* 
天天饭局搞得我是身心疲惫= = 
所以更新比较慢 
今天想跟大家分享的就是这个大名鼎鼎的React框架
React是这两年非常流行的框架 
并不难,还是挺容易上手的 
起源于Facebook内部项目(一个广告系统)
传统页面从服务器获取数据,显示到浏览器上,用户输入数据传入服务器 
但随着数据量增大,越来越难以维护了 
Facebook觉得MVC不能满足他们的扩展需求了(巨大的代码库和庞大的组织) 
每当需要添加一项新的功能或特性时,系统复杂度就几何增长 
致使代码脆弱不堪、不可预测,结果导致他们的MVC正走向崩溃 
当系统中有很多的模型和相应视图时,其复杂度就会迅速扩大,非常难以理解和调试
总之就是Facebook对市场上所有JS-MVC框架都不满意,认为都不适合大规模应用 
就自己写了一套,用来架设Instagram网站 
写完后用着用着,发现哎呦这货还真是不错,然后就开源了 
随着这几年的沉淀,React已经变得越来越强大了
科普一下MVC 
MVC就分为M、V、C三部分
简单的理解一下 
我们就是user,在页面中点击了一个按钮触发了事件 
控制器Controller调整数据,模型Model中数据改变 
数据改变又会导致视图View更新 
UI的改变反馈呈现给我们user
这里还要说明一下,虽然介绍了MVC 
但是React不是MVC框架 
而是用于构建组件化UI的库,是一个前端界面开发工具 
顶多算作MVC中的View视图 
而且MVC更多的是数据双向绑定 
但我们React是单向数据流
React它具有以下特点
我选择使用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-loader和babel-core 
还要安装babel-preset-es2015 和 babel-preset-react  
用于解析ES6语法(为了老版本浏览器)和React的JSX语法 
还有react和react-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"
}简单说一下React的JSX语法是个神马东西 
可能一会儿大家会看大不明觉厉的代码 
比如
return (
    <div>hehe<div>
)这就是JSX代码,它是React提供的语法糖 
是React的重要组成部分,使用类似XML标记的方式来声明界面及关系 
语法糖的意思我写ES6的时候也说了 
就是计算机语言中添加的语法,对语言的功能没影响 
方便我们开发人员使用的,可以增强可读性
如果使用JS代码也可以,不过官方推荐使用JSX 
这样来写,结构层次关系都很清晰 
webpack会帮我们把他们转换成浏览器认识的js代码(loader的作用) 
(如果好奇转换成了什么,可以去webpack输出文件查看,或者找jsx转js的工具)
JSX语法结构说的通俗一点就是HTML、JS混写 
可能大家会有疑惑,说好的结构、样式、行为相分离的前端思想呢?! 
React其中一个主要的设计理念是编写简单容易理解的代码 
但我们的组件确实不好松耦合 
大家也不要过分较真
这样的语法结构是怎样解析的呢?其实并不神奇 
JSX的语法规则:
不理解不要慌,看了下面就懂了 
提前渗透一下
终于写到语法正题了 
在此之前我们必须要引用的两个对象 
一个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组件有以下特点
React.createClass()就是用于将代码封装成组件Component的方法 
它会生成一个React组件
var App = React.createClass({
    render: function(){
        return (
            <p>This is a component...</p>
        )
    }
});这个方法参数是一个对象 
对象中有一个render返回一个虚拟DOM 
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>
        )
    }
});组件两边加括号的目的,是防止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‘)
);还要注意两个特例
因为他们是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就必须使用大括号 
下面就不再赘述了
组件的属性同样可以像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对象中的属性是一一对应的 
但有一个例外,它是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很奇怪,它有三种类型值
(可以在控制台上输出验证) 
所以我们处理它要特别小心 
好在我们可以使用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以及作用给大家整理在下面 
(关于它们在整个生命周期的触发次数大家应该都能想明白就不写了) 
(不常用的我在后面的标注了*号)
return {name: ‘payen‘} 相当于初始化了组件属性this.props.name = ‘payen‘return {show: false} 相当于初始化了组件状态this.state.show = falsethis.setState({show: false})
附上一张我盗的图,帮助大家理解(手动滑稽)
关于这些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‘,
            left: ‘40px‘,
            top: ‘40px‘,
            width: ‘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‘,
            width: ‘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‘)
);用一句话来总结一下,就是数据都交给父级来管理
客户端高性能组件化框架React简介、特点、环境搭建及常用语法
原文:http://blog.csdn.net/q1056843325/article/details/54729657