# React 基础
# 官网
官网:https://facebook.github.io/react/
# 什么是React?
A JAVASCRIPT LIBRARY FOR BUILDING USER INTERFACES 一个用于建立用户界面的javascript库,由facebook官方发行
# React特点?
# 声明式(Declarative)
React makes it painless to create interactive UIs. Design simple views for each state in your application, and React will efficiently update and render just the right components when your data changes. Declarative views make your code more predictable and easier to debug.
React解决了需要手动更新DOM,费力记录每一个状态的问题,你只需声明式的定义各个时间点的用户界面,而无须关心数据变化时需要更新哪一部分DOM,在任何时间点,React都能以最小的DOM修改更新整个应用程序。
声明式的方式使你的代码更加的可预测并且容易调试
# 基于组件的(Component-Based)
Build encapsulated components that manage their own state, then compose them to make complex UIs. Since component logic is written in JavaScript instead of templates, you can easily pass rich data through your app and keep state out of the DOM.
构建管理自己的状态的封装组件,然后组合它们以创建复杂的UI。 由于组件逻辑是用JavaScript而不是模板编写的,因此您可以通过应用程序轻松传递丰富的数据,并将状态保留在DOM之外。
# Learn Once, Write Anywhere
We don't make assumptions about the rest of your technology stack, so you can develop new features in React without rewriting existing code. React can also render on the server using Node and power mobile apps using React Native.
我们不对您的其余技术水平做出假设,您可以在React中开发新功能,而无需重写现有代码。 React也可以在服务器上使用Node和在移动应用使用React Native渲染。
# 核心
React 的核心思想
是:封装组件,各个组件维护自己的状态和 UI,当状态变更,自动重新渲染整个组件。
基于这种方式的一个直观感受就是我们不再需要不厌其烦地来回查找某个 DOM 元素,然后操作 DOM 去更改 UI
组件
,组件目的是提高代码复用率,降低测试难度和代码复杂度
- 提高代码复用率:组件将数据和逻辑封装
- 降低测试难度:组件高内聚低耦合很容易对每个组件进行测试
- 直观的语法可以极大的提高可读性
# React简介
React 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScript MVC 框架,都不满意,就决定自己写一套,用来架设 Instagram 的网站。做出来以后,发现这套东西很好用,就在2013年5月开源了。由于 React 的设计思想极其独特,属于革命性创新,性能出众,代码逻辑却非常简单。所以,越来越多的人开始关注和使用,认为它可能是将来 Web 开发的主流工具。
React发源自Facebook的PHP框架XHP的一个分支,XHP作为一个PHP框架,旨在为每次有请求进来时重新渲染整个页面,React的产生就是为了把这个重新渲染整个页面的PHP式的工作流带到客户端应用中来。
# React特性
- 不是一个 MVC 框架
- 不使用模板
- 响应式更新非常简单
- HTML5 仅仅是个开始
# 仅仅是UI
许多人使用 React 作为 MVC 架构的 V 层。 尽管 React 并没有假设过你的其余技术栈, 但它仍可以作为一个小特征轻易地在已有项目中使用
# 虚拟DOM
React 为了更高超的性能而使用虚拟 DOM 作为其不同的实现。 它同时也可以由服务端 Node.js 渲染 - 而不需要过重的浏览器 DOM 支持
# 数据流
React 实现了单向响应的数据流,从父节点到子节点,从而减少了重复代码,这也是它为什么比传统数据绑定更简单。
# React误区
- React不是一个完整的MVC框架,最多可以认为是MVC中的V(View),甚至React并不非常认可MVC开发模式;
- React的服务器端Render能力只能算是一个锦上添花的功能,并不是其核心出发点,事实上React官方站点几乎没有提及其在服务器端的应用;
- 有人拿React和Web Component相提并论,但两者并不是完全的竞争关系,你完全可以用React去开发一个真正的Web Component;
- React不是一个新的模板语言,JSX只是一个表象,没有JSX的React也能工作。
# React本质
React本质上是一个状态机,可以帮助开发者管理随着时间而变化
的状态。
React只关心两件事:
- 更新DOM
- 响应事件
# React优势
React赢就赢在最小化重绘
,并避免了不必要的DOM操作
,这两点都是公认的性能瓶颈。
React的虚拟表示差异算法
,能够将上述问题的影响降到最低。 当用户输入或者有其他更新导致状态改变时,我们只需要简单的通知React状态改变,他可以自动的处理剩下的事情。我们无需深入到详细的过程中。
# React 背景和原理
在Web开发中,我们总需要将变化的数据实时反应到UI上,这时就需要对DOM进行操作。而复杂或频繁的DOM操作通常是性能瓶颈产生的原因(如何进行高性能的复杂DOM操作通常是衡量一个前端开发人员技能的重要指标)。React为此引入了虚拟DOM(Virtual DOM)的机制:在浏览器端用Javascript实现了一套DOM API。基于React进行开发时所有的DOM构造都是通过虚拟DOM进行,每当数据变化时,React都会重新构建整个DOM树,然后React将当前整个DOM树和上一次的DOM树进行对比,得到DOM结构的区别,然后仅仅将需要变化的部分进行实际的浏览器DOM更新。而且React能够批处理虚拟DOM的刷新,在一个事件循环(Event Loop)内的两次数据变化会被合并,例如你连续的先将节点内容从A变成B,然后又从B变成A,React会认为UI不发生任何变化,而如果通过手动控制,这种逻辑通常是极其复杂的。尽管每一次都需要构造完整的虚拟DOM树,但是因为虚拟DOM是内存数据,性能是极高的,而对实际DOM进行操作的仅仅是Diff部分,因而能达到提高性能的目的。这样,在保证性能的同时,开发者将不再需要关注某个数据的变化如何更新到一个或多个具体的DOM元素,而只需要关心在任意一个数据状态下,整个界面是如何Render的。
如果你像在90年代那样写过服务器端Render的纯Web页面那么应该知道,服务器端所要做的就是根据数据Render出HTML送到浏览器端。如果这时因为用户的一个点击需要改变某个状态文字,那么也是通过刷新整个页面来完成的。服务器端并不需要知道是哪一小段HTML发生了变化,而只需要根据数据刷新整个页面。换句话说,任何UI的变化都是通过整体刷新来完成的。而React将这种开发模式以高性能的方式带到了前端,每做一点界面的更新,你都可以认为刷新了整个页面。至于如何进行局部更新以保证性能,则是React框架要完成的事情。
借用Facebook介绍React的视频中聊天应用的例子,当一条新的消息过来时,传统开发的思路如上图,你的开发过程需要知道哪条数据过来了,如何将新的DOM结点添加到当前DOM树上;而基于React的开发思路如下图,你永远只需要关心数据整体,两次数据之间的UI如何变化,则完全交给框架去做。可以看到,使用React大大降低了逻辑复杂性,意味着开发难度降低,可能产生Bug的机会也更少。
# React组件
在React中,组件是分离关注点的,而不是被当做模板或处理逻辑的。
在组件的声明周期中,随着该组件的props和state发生改变,它的DOM表现也将会有相应的变化。
# 组件化开发
虚拟DOM(virtual-dom)不仅带来了简单的UI开发逻辑,同时也带来了组件化开发的思想,所谓组件,即封装起来的具有独立功能的UI部件。React推荐以组件的方式去重新思考UI构成,将UI上每一个功能相对独立的模块定义成组件,然后将小的组件通过组合或者嵌套的方式构成大的组件,最终完成整体UI的构建。例如,Facebook的instagram.com整站都采用了React来开发,整个页面就是一个大的组件,其中包含了嵌套的大量其它组件,大家有兴趣可以看下它背后的代码。
如果说MVC的思想让你做到视图-数据-控制器的分离,那么组件化的思考方式则是带来了UI功能模块之间的分离。我们通过一个典型的Blog评论界面来看MVC和组件化开发思路的区别。
对于MVC开发模式来说,开发者将三者定义成不同的类,实现了表现,数据,控制的分离。开发者更多的是从技术的角度来对UI进行拆分,实现松耦合。
对于React而言,则完全是一个新的思路,开发者从功能的角度出发,将UI分成不同的组件,每个组件都独立封装。
在React中,你按照界面模块自然划分的方式来组织和编写你的代码,对于评论界面而言,整个UI是一个通过小组件构成的大组件,每个组件只关心自己部分的逻辑,彼此独立。
React认为一个组件应该具有如下特征:
(1)可组合(Composeable):一个组件易于和其它组件一起使用,或者嵌套在另一个组件内部。如果一个组件内部创建了另一个组件,那么说父组件拥有(own)它创建的子组件,通过这个特性,一个复杂的UI可以拆分成多个简单的UI组件; (2)可重用(Reusable):每个组件都是具有独立功能的,它可以被使用在多个UI场景; (3)可维护(Maintainable):每个小的组件仅仅包含自身的逻辑,更容易被理解和维护;
# 组件化开发实例
React是基于组件化的开发,那么组件化开发最大的优点是什么?毫无疑问,当然是复用
'use strict';
var Search = React.createClass({
render: function() {
return ( < div > { this.props.searchType }
< input type = "text" / >
< button > Search < /button> < /div>
);
}
});
var Page = React.createClass({
render: function() {
return ( < div >
< h1 > Welcome! < /h1>
< Search searchType = "Title" / >
< Search searchType = "Content" / >
< /div>
);
}
});
ReactDOM.render( < Page / > ,
document.getElementById('example')
);
这里我们创建了一个Search组件,然后又创建了一个Page组件,然后我们在Page组件中调用Search组件,并且调用了两次,这里我们通过属性searchType传入值
# 无状态函数式组件
有一些组件不需要state,不需要内部的组件生命周期,那么这类组件可以写成一个纯函数的形式,称为stateless functional component(无状态函数式组件)。这是React推荐的写法
//一个不需要state和生命周期的组件 之前的写法
import React from 'react'
class Helloworld extends React.Component {
constructor(props) {
super(props)
}
sayHi(event) {
alert(`Hi ${this.props.name}`)
}
render() {
return (
<div>
<a href="#"
onClick={this.sayHi.bind(this)}>
Say high</a>
</div>
)
}
}
Helloworld.propTypes = {
name: React.PropTypes.string.isRequired
};
export default Helloworld;
//无状态函数的写法
import React from 'react'
const Helloworld = ({name}) => {
const sayHi = (event) => {
alert(`Hi ${name}`)
}
return (
<div>
<a href="#"
onClick={sayHi}>
Say high</a>
</div>
)
}
Helloworld.propTypes = {
name: React.PropTypes.string.isRequired
};
export default Helloworld;
# Higher order components
高阶组件解决的是多个组件需要共用方法的问题 如何使用: 思路:封装一个HOC的组件, 本质上实在外层加了一层父组件 eg:
const HOC = (InnerComponent) => class extends Component {
componentWillMount() {
console.log('hoc mount')
}
render() {
return (
<InnerComponent
{...this.props}
/>
)
}
}
class App extends Component {
constructor() {
super(rops);
}
render() {
return (
<div className="App">
<div>
<Button>button</Button>
<hr/>
<LabelHOC>label</LabelHOC>
</div>
</div>
);
}
}
const Button = HOC((props) => <button >{props.children}</button>)
class Label extends Component {
render() {
return (
<label htmlFor="">{this.props.children}</label>
)
}
}
const LabelHOC = HOC(Label)
# 数据流
在React中,数据的流向是单向的—从父节点到子节点,组件易于把握,只需从父节点获取props渲染既可。
# props
props就是properties (属性)的缩写,可以把任意类型的数据传递给组件。
一个组件绝对不可以自己修改自己的props
。
# state
每一组件都用有自己的state,state与props的区别是state仅仅存在于组件的内部
千万不能直接修改this.state 永远记得要通过this.setState方法修改
。
# ref
我们可以使用ref去获取真实DOM的中的值,简单的使用如下:
update() {
this.setState({
a: this.refs.a.value
})
}
<input
ref="a"
type="text"
onChange={this.update.bind(this)}
/>
ref的参数不仅仅可以是一个字符串,也可以是一个回调函数 eg:
## update() {
this.setState({
a: ReactDOM.findDOMNode(this.a).value
})
}
<div className="App">
<input
ref={component =>
this.a = component
}
type="text"
onChange={this.update.bind(this)}
/>{this.state.a}
</div>
个人觉得ref的作用就是
# React发展历史?
- 2013.06 Fackbook官方发布React
- 2013.09 热度开始上涨
- 2015.03 React Native发布 ReactNative用于编写跨平台的移动应用
# 为什么facebook推出React?
因为facebook遇到一个问题:他需要解决如何去构建一个数据不断变化的大型应用程序 facebook拥有庞大的用户群,用户数据增长和变化很快,但是数据的变化会造成主要的两个问题:
- 频繁的DOM操作
- 逻辑复杂(数据逻辑相关)
上述问题的解决方法:
- 自动DOM操作
- 状态对应内容
# 文档结构
到官网下载压缩包,解压后会发现有三个文件夹
- build
- react-dom-server.js
- react-dom-server.min.js
- react-dom.js
- react-dom.min.js
- react-with-addons.js 带插件的react(未压缩)
- react-with-addons.min.js 带插件的react(压缩)
- react.js
- react.min.js
- examples 一些示例
- README.md
# 如何使用
# 两种格式使用比较
在使用时需注意的格式,如果使用JSX格式
<html>
<head>
<meta charset="UTF-8" />
<title>Hello React!</title>
<script src="react.js"></script>
<script src="react-dom.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>
</head>
<body>
<div id="example"></div>
<script type="text/babel">
//code here
</script>
</body>
</html>
如果使用JS格式
<html>
<head>
<meta charset="UTF-8" />
<title>Hello React!</title>
<script src="react.js"></script>
<script src="react-dom.js"></script>
</head>
<body>
<div id="example"></div>
<script>
//code here
</script>
</body>
</html>
两者的差别是:JSX需要翻译为JS ,所以需要多加引入一行script的js文件,并且script中的type也必须为
text/babel
# 一个简单的组件
React 组件通过一个render()方法,接受输入的参数并返回展示的对象。 输入的参数通过render()传入组件后,将存储在this.props
React 有两种书写的方式 JSX 和原生的js,但是两者在使用上有所差异,但是可以通过将JSX转化为js,实际上浏览器中执行的是JS
JSX 是可选的,并不强制要求使用。
eg:
JSX版本
var HelloMessage = React.createClass({
render: function() {
return <div>Hello {this.props.name}</div>;
}
});
ReactDOM.render(<HelloMessage name="world" />, mountNode)
//结果: Hello world
js版本
"use strict";
var HelloMessage = React.createClass({
displayName: "HelloMessage",
render: function render() {
return React.createElement(
"div",
null,
"Hello ",
this.props.name
);
}
});
ReactDOM.render(React.createElement(HelloMessage, { name: "John" }), mountNode);
可以看出,js 的版本相对而言比较复杂
# 一个有状态的组件
除了接受输入数据(通过 this.props ),组件还可以保持内部状态数据(通过 this.state )。当一个组件的状态数据的变化,展现的标记将被重新调用 render() 更新。
JSX版本
var Timer = React.createClass({
getInitialState: function() {
return {secondsElapsed: 0};
},
tick: function() {
this.setState({secondsElapsed: this.state.secondsElapsed + 1});
},
componentDidMount: function() {
this.interval = setInterval(this.tick, 1000);
},
componentWillUnmount: function() {
clearInterval(this.interval);
},
render: function() {
return (
<div>Seconds Elapsed: {this.state.secondsElapsed}</div>
);
}
});
ReactDOM.render(<Timer />, mountNode);
javascript 版本
"use strict";
var Timer = React.createClass({
displayName: "Timer",
getInitialState: function getInitialState() {
return { secondsElapsed: 0 };
},
tick: function tick() {
this.setState({ secondsElapsed: this.state.secondsElapsed + 1 });
},
componentDidMount: function componentDidMount() {
this.interval = setInterval(this.tick, 1000);
},
componentWillUnmount: function componentWillUnmount() {
clearInterval(this.interval);
},
render: function render() {
return React.createElement(
"div",
null,
"Seconds Elapsed: ",
this.state.secondsElapsed
);
}
});
ReactDOM.render(React.createElement(Timer, null), mountNode);
# 一个应用程序
通过使用 props 和 state, 我们可以组合构建一个小型的Todo程序。 下面例子使用 state 去监测当前列表的项以及用户已经输入的文本。 尽管事件绑定似乎是以内联的方式,但他们将被收集起来并以事件代理的方式实现。
JSX版本
var TodoList = React.createClass({
render: function() {
var createItem = function(item) {
return <li key={item.id}>{item.text}</li>;
};
return <ul>{this.props.items.map(createItem)}</ul>;
}
});
var TodoApp = React.createClass({
getInitialState: function() {
return {items: [], text: ''};
},
onChange: function(e) {
this.setState({text: e.target.value});
},
handleSubmit: function(e) {
e.preventDefault();
var nextItems = this.state.items.concat([{text: this.state.text, id: Date.now()}]);
var nextText = '';
this.setState({items: nextItems, text: nextText});
},
render: function() {
return (
<div>
<h3>TODO</h3>
<TodoList items={this.state.items} />
<form onSubmit={this.handleSubmit}>
<input onChange={this.onChange} value={this.state.text} />
<button>{'Add #' + (this.state.items.length + 1)}</button>
</form>
</div>
);
}
});
ReactDOM.render(<TodoApp />, mountNode);
javacript版本
'use strict';
var TodoList = React.createClass({
displayName: 'TodoList',
render: function render() {
var createItem = function createItem(item) {
return React.createElement(
'li',
{ key: item.id },
item.text
);
};
return React.createElement(
'ul',
null,
this.props.items.map(createItem)
);
}
});
var TodoApp = React.createClass({
displayName: 'TodoApp',
getInitialState: function getInitialState() {
return { items: [], text: '' };
},
onChange: function onChange(e) {
this.setState({ text: e.target.value });
},
handleSubmit: function handleSubmit(e) {
e.preventDefault();
var nextItems = this.state.items.concat([{ text: this.state.text, id: Date.now() }]);
var nextText = '';
this.setState({ items: nextItems, text: nextText });
},
render: function render() {
return React.createElement(
'div',
null,
React.createElement(
'h3',
null,
'TODO'
),
React.createElement(TodoList, { items: this.state.items }),
React.createElement(
'form',
{ onSubmit: this.handleSubmit },
React.createElement('input', { onChange: this.onChange, value: this.state.text }),
React.createElement(
'button',
null,
'Add #' + (this.state.items.length + 1)
)
)
);
}
});
ReactDOM.render(React.createElement(TodoApp, null), mountNode);
# 一个使用外部插件的组件
React是灵活的,并且提供方法允许你跟其他库和框架对接。 下面例子展现了一个案例,使用外部库Markdown实时转化textarea的值。
JSX版本
var MarkdownEditor = React.createClass({
getInitialState: function() {
return {value: 'Type some *markdown* here!'};
},
handleChange: function() {
this.setState({value: this.refs.textarea.value});
},
rawMarkup: function() {
return { __html: marked(this.state.value, {sanitize: true}) };
},
render: function() {
return (
<div className="MarkdownEditor">
<h3>Input</h3>
<textarea
onChange={this.handleChange}
ref="textarea"
defaultValue={this.state.value} />
<h3>Output</h3>
<div
className="content"
dangerouslySetInnerHTML={this.rawMarkup()}
/>
</div>
);
}
});
ReactDOM.render(<MarkdownEditor />, mountNode);
javascript版本
"use strict";
var MarkdownEditor = React.createClass({
displayName: "MarkdownEditor",
getInitialState: function getInitialState() {
return { value: 'Type some *markdown* here!' };
},
handleChange: function handleChange() {
this.setState({ value: this.refs.textarea.value });
},
rawMarkup: function rawMarkup() {
return { __html: marked(this.state.value, { sanitize: true }) };
},
render: function render() {
return React.createElement(
"div",
{ className: "MarkdownEditor" },
React.createElement(
"h3",
null,
"Input"
),
React.createElement("textarea", {
onChange: this.handleChange,
ref: "textarea",
defaultValue: this.state.value }),
React.createElement(
"h3",
null,
"Output"
),
React.createElement("div", {
className: "content",
dangerouslySetInnerHTML: this.rawMarkup()
})
);
}
});
ReactDOM.render(React.createElement(MarkdownEditor, null), mountNode);
# 兼容性
兼容IE8 +
# 组件的生命周期
React为每个组件提供了生命周期钩子函数去响应不同时刻
- 创建时
- 活动时
- 销毁时
# 实例化(创建期)
首次使用一个组件类时,下面的方法依次被调用:
- getDefaultProps 设置默认的props,只会在装载前调用一次,在组件中赋值会被设置到this.props中(弃用)
- getInitialState 初始化每个实例的state(在ES6中,只需要写到constructor的this.state中即可)
- componentWillMount 会在首次完成渲染之前被调用
- render 创建虚拟DOM,组件输出返回一个ReactElement对象
- componentDidMount 可以获取到DOM结点,在此处如果有需要则发送http请求
对于该组件的所有后续的应用,下面的方法依次被调用:
- getInitialState
- componentWillMount
- render
- componentDidMount
# 存在期
随着应用状态的改变,以及组件逐渐受 到影响,你将会看到下面的方法依次被调用
- componentWillReceiveProps 在组件接受到新的props的时候被触发,参数nextProps就是传入的新的props,可以和this.state比较,决定是否刷新UI
- shouldComponentUpdate 在重新render之前被调用,返回一个布尔值决定是否更新,默认为True
- componentWillUpdate,在render之前被调用,可以在渲染之前做一些准备工作
- render
- componentDidUpdate
任意时刻, 组件的props可以通过父辈组件来更改,这时componentWillReceiveProps会被调用,可以在此时更改props对象和更新state
# 销毁&&清理期
某个组件被使用完成后,componentWillUnmount方法会被调用
组件的生命周期分成三个状态:
- Mounting:已插入真实 DOM
- Updating:正在被重新渲染
- Unmounting:已移出真实 DOM
React 为每个状态都提供了两种处理函数,will 函数在进入状态之前调用,did 函数在进入状态之后调用,三种状态共计五种处理函数。
- componentWillMount()
- componentDidMount()
- componentWillUpdate(object nextProps, object nextState)
- componentDidUpdate(object prevProps, object prevState)
- componentWillUnmount()
此外,React 还提供两种特殊状态的处理函数。
- componentWillReceiveProps(object nextProps):已加载组件收到新的参数时调用
- shouldComponentUpdate(object nextProps, object nextState):组件判断是否重新渲染时调用
# React 注意事项
ReactJs是基于组件化的开发,所以最终你的页面应该是由若干个小组件组成的大组件。
可以通过属性,将值传递到组件内部,同理也可以通过属性将内部的结果传递到父级组件(留给大家研究);要对某些值的变化做DOM操作的,要把这些值放到state中。
为组件添加外部css样式时,类名应该写成className而不是class; 添加内部样式时,
应该是
style={{opacity: this.state.opacity}}
而不是
style="opacity:{this.state.opacity};
组件名称首字母必须大写。
变量名用{}包裹,且不能加双引号。
在render中,只能通过this.props或者this.state访问数据
render中只能出现一个顶级组件
eg:
//正确
render:function(){
return : (
<div>
······
</div>
)
}
//错误
render:function(){
return : (
<div>
······
</div>
<div>
······
</div>
)
}
8.render返回的并不是真实的DOM,而是一个虚拟的表现
9.由于组件并不是真实的 DOM 节点,而是存在于内存之中的一种数据结构,叫做虚拟 DOM (virtual DOM)。只有当它插入文档以后,才会变成真实的 DOM 。有时需要从组件获取真实 DOM 的节点,这时就要用到 ref 属性:
# API
# render
ReactDOM.render 是 React 的最基本方法,用于将模板转为 HTML 语言,并插入指定的 DOM 节点。
ReactDOM.render(component,mountNode)
这里需要注意的是,react并不依赖jQuery,当然我们可以使用jQuery,但是render里面第二个参数必须使用JavaScript原生的getElementByID方法,不能使用jQuery来选取DOM节点。
# createClass
ReactJS是基于组件化的开发,下面我们开始来学习ReactJS里面的组件,React 允许将代码封装成组件(component),然后像插入普通 HTML 标签一样,在网页中插入这个组件。React.createClass 方法就用于生成一个组件类。
'use strict';
var Greet = React.createClass({
render: function() {
return <h1 > hello {
this.props.name
} < /h1>;
}
});
ReactDOM.render( < Greet name = "wei" / > , document.getElementById("example"));
focus:
- 获取属性的值用的是this.props.属性名
- 创建的组件名称首字母必须大写
- 为元素添加css的class时,要用className
- 组件的style属性的设置方式也值得注意,要写成如下形式
style={{width: this.state.width}}
# getInitialState && setState
组件免不了要与用户互动,React 的一大创新,就是将组件看成是一个状态机,一开始有一个初始状态,然后用户互动,导致状态变化,从而触发重新渲染 UI
var InputState = React.createClass({
getInitialState: function() {
return {
enable: false
};
},
handleClick: function(event) {
this.setState({
enable: !this.state.enable
});
},
render: function() {
return ( < p >Hello, {this.props.name}
< input type = "text"
disabled = {
this.state.enable
}
/> < button onClick = {
this.handleClick
} > Change State < /button> < /p>
);
}
});
ReactDOM.render( < InputState name="world"/ > ,
document.getElementById('example')
);
这里值得注意的几点如下:
1)getInitialState函数必须有返回值,可以是NULL或者一个对象。
2)访问state的方法是this.state.属性名。
3)变量用{}包裹,不需要再加双引号。
# componentDidMount
在插入真实DOM之后的操作
'use strict';
var Hello = React.createClass({
getInitialState: function() {
return {
opacity: 1.0
};
},
componentDidMount: function() {
this.timer = setInterval(function() {
var opacity = this.state.opacity;
opacity -= .05;
if (opacity < 0.1) {
opacity = 1.0;
}
this.setState({
opacity: opacity
});
}.bind(this), 100);
},
render: function() {
return ( < div style = {
{
opacity: this.state.opacity
}
} >
Hello {
this.props.name
} < /div>
);
}
});
ReactDOM.render( < Hello name = "world" / > ,
document.body
);
上面代码在hello组件加载以后,通过 componentDidMount 方法设置一个定时器,每隔100毫秒,就重新设置组件的透明度,从而引发重新渲染
# 类型检查 Typechecking
可以通过对props数据类型做一些限制
class Greeting extends React.Component {
render() {
return (
<h1>Hello, {this.props.name}</h1>
);
}
}
Greeting.propTypes = {
name: React.PropTypes.string
//常见的一些类型
optionalArray: React.PropTypes.array,
optionalBool: React.PropTypes.bool,
optionalFunc: React.PropTypes.func,
optionalNumber: React.PropTypes.number,
optionalObject: React.PropTypes.object,
optionalString: React.PropTypes.string,
optionalSymbol: React.PropTypes.symbol
};
可以设定一些默认值
Greeting.defaultProps = {
name: 'Stranger'
};
可以设定一些值是否为必填项
Greeting.propTypes = {
name: React.PropTypes.element.isRequired
};
# JSX
参见JSX的笔记
# 测试
react 官方推荐了两种测试思路:
- 虚拟DOM
- 真实DOM
也提供了自己的测试工具react-addons-test-utils
,在Facebook使用的是Jest的,airbnb自己也开发了一套测试工具叫做enzyme
# react-addons-test-utils
Shallow Rendering 思想是不关心行为和子组件,不需要DOM,仅仅测试返回值
方法:
- createRenderer() 创建一个shallow renderer,相当一个渲染器
- shallowRenderer.render() 和ReactDOM.render()相似,仅仅渲染
single level deep
- shallowRenderer.getRenderOutput() 获取渲染的输出
eg:
function MyComponent() {
return (
<div>
<span className="heading">Title</span>
<Subcomponent foo="bar" />
</div>
);
}
const renderer = ReactTestUtils.createRenderer();
renderer.render(<MyComponent />);
const result = renderer.getRenderOutput();
expect(result.type).toBe('div');
expect(result.props.children).toEqual([
<span className="heading">Title</span>,
<Subcomponent foo="bar" />
]);
react自己的shallow render 有很多的问题,推荐使用Enzyme的 shallow rendering api (opens new window)
一些api:
simlate: 模拟点击
renderIntoDocument: 将react的组件渲染到DOM节点上,这个需要DOM环境
mockComponent: mock一个组件
isElement: 是不是一个React元素
isElementOfType( element, componentClass):元素是不是一个react的组件类
isDOMComponent: 是不是一个DOM
isCompositeComponent:如果实例是一个用户定义的组件
isCompositeComponentWithType:
findAllInRenderedTree: 查找所有能够通过测试的组件
scryRenderedDOMComponentsWithClass: 查找所有的DOM元素一个渲染树上,且名字匹配className的
findRenderedDOMComponentWithClass: 和scryRenderedDOMComponentsWithClass方法一样,但是只会返回一个结果,如果超出一个会报错
scryRenderedDOMComponentsWithTag: 查找所有的DOM元素一个渲染树上,且名字匹配TagName的
findRenderedDOMComponentWithTag: 和scryRenderedDOMComponentsWithTag方法一样,但是只会返回一个结果,如果超出一个会报错
scryRenderedComponentsWithType: 找到名称匹配componentClass的所有的组件实例
findRenderedDOMComponentWithTag:和scryRenderedComponentsWithType方法一样,但是只会返回一个结果,如果超出一个会报错
# 参考文章
未看: 阮一峰react实例入门:http://www.ruanyifeng.com/blog/2015/03/react.html 极客学院wiki react入门教程:http://wiki.jikexueyuan.com/project/react-tutorial/ 极客学院wiki react中文版:http://wiki.jikexueyuan.com/project/react/
已看: 一看就懂的ReactJs入门教程(精华版):http://www.cocoachina.com/webapp/20150721/12692.html
# 思考
React的本质,模板本身就是一份"数据生成视图"
的逻辑,这份逻辑得到复用的时候,只需要维护数据,每次数据发生变化时,渲染一下就能更新视图。
# React 如何处理事件
React并未把事件绑定在特定的DOM节点上,实际上它是用事件代理的方式在最外层绑定了一个事件回调,当组件unmounted的时候,这个事件回调会被自动删除
# Heading
# 常见问题
# 已解决
# Warning: Unknown DOM property for. Did you mean htmlFor?
这个问题是由于在使用label标签时的for属性导致的,修改for,使用htmlFor
if you're using JSX you suppose to use htmlFor instead of for
# 未解决
当React运行在服务器端时,componentDidmount方法不会被调用? React的测试怎么办,如何搭建测试框架?
# 参考资料
简书博客:http://www.jianshu.com/p/fa27ae471531 展示的slides:http://slides.com/sialvsic/react