client高性能组件化框架React简单介绍、特点、环境搭建及经常使用语法

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

明天就是除夕了

预祝大家新春快乐 [ ]~( ̄▽ ̄)~*

天天饭局搞得我是身心疲惫= =

所以更新比較慢

今天想跟大家分享的就是这个大名鼎鼎的React框架

简单介绍

React是近两年非常流行的框架

流行到什么程度呢?

我看了一下Github上的数据

client高性能组件化框架React简单介绍、特点、环境搭建及经常使用语法

React达到了5w8+的star

在JavaScript中star排名第4

受欢迎程度可见一斑

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


React并不难。还是挺easy上手的

起源于Facebook内部项目(一个广告系统)

client高性能组件化框架React简单介绍、特点、环境搭建及经常使用语法

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcTEwNTY4NDMzMjU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" title="">

传统页面从server获取数据。显示到浏览器上,用户输入数据传入server

但随着数据量增大,越来越难以维护了

Facebook觉得MVC不能满足他们的扩展需求了(巨大的代码库和庞大的组织)

每当须要加入一项新功能或特性时,系统复杂度就呈几何增长

致使代码脆弱不堪、不可预測,结果导致他们的MVC正走向崩溃

当系统中有非常多的模型和相应视图时,其复杂度就会迅速扩大。非常难以理解和调试

总之就是Facebook对市场上全部JS-MVC框架都不惬意。觉得都不适合大规模应用

就自己写了一套,用来架设Instagram站点

写完后用着用着,发现哎呦这货还真是不错。然后就开源了

随着这几年的沉淀。React已经变得越来越强大了

值得我们去了解~

MVC

科普一下MVC

MVC是一种软件架构模式(后端影响到了前端)

MVC就分为M、V、C三部分

  • Model(模型):

    应用程序中用于处理应用程序数据逻辑的部分,通常负责在数据库中存取数据
  • View(视图):

    应用程序中处理数据显示的部分,通常根据模型数据创建
  • Controller(控制器):

    应用程序中处理用户交互的部分。通常负责从视图读取数据,控制用户输入,并向模型发送数据

client高性能组件化框架React简单介绍、特点、环境搭建及经常使用语法

简单的理解一下

我们就是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”

client高性能组件化框架React简单介绍、特点、环境搭建及经常使用语法

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

client高性能组件化框架React简单介绍、特点、环境搭建及经常使用语法

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcTEwNTY4NDMzMjU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" title="">

组件化

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')
);

client高性能组件化框架React简单介绍、特点、环境搭建及经常使用语法


了解了这个,我们能够做一个小练习

如今有一组数据,利用它组成一个有序列表组件

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值

client高性能组件化框架React简单介绍、特点、环境搭建及经常使用语法

这里我就使用了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')
);

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

client高性能组件化框架React简单介绍、特点、环境搭建及经常使用语法

client高性能组件化框架React简单介绍、特点、环境搭建及经常使用语法

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcTEwNTY4NDMzMjU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" title="">

这个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')
);

client高性能组件化框架React简单介绍、特点、环境搭建及经常使用语法

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcTEwNTY4NDMzMjU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" title="">

client高性能组件化框架React简单介绍、特点、环境搭建及经常使用语法

组件验证

组件的属性能够接受不论什么值。数字、字符串、函数、对象什么都能够

但有时候,我们拿到一个组件,想要验证參数是否符合我们的要求(这事实上非常重要。不要藐视)

这时就须要使用组件的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

client高性能组件化框架React简单介绍、特点、环境搭建及经常使用语法

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcTEwNTY4NDMzMjU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" title="">

浏览器就会发出警告

具体见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结构

client高性能组件化框架React简单介绍、特点、环境搭建及经常使用语法

生命周期

生命周期不难理解

组件的一生无非就是产生、更新、销毁

在组件的每个生命周期内,都会按顺序触发一些组件方法

比方我们刚刚的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.定时器、事件监听器等等)

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

client高性能组件化框架React简单介绍、特点、环境搭建及经常使用语法

关于这些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')
);

client高性能组件化框架React简单介绍、特点、环境搭建及经常使用语法


首先我们先来实现第一个功能:点击按钮出现弹框

问题是改如何实现

我们的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')
);

client高性能组件化框架React简单介绍、特点、环境搭建及经常使用语法

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

获取真实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就会渲染到页面

client高性能组件化框架React简单介绍、特点、环境搭建及经常使用语法

获取真实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]

否则会报错的

==主页传送门==

上一篇:生成任意长度的随机数 JS


下一篇:让nodeJS支持ES6的词法----babel的安装和使用