React
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了React,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含17482字,纯文字阅读大概需要25分钟。
内容图文
Introduction
React并不是一个完整的MVC或者MVVM框架,它与Angular也是负责不同的方面,它最大的功能是提供一个高效的视图层。React提供了一些新颖的概念、库和编程原则让你能够同时在服务端和客户端编写快速、紧凑、漂亮的代码来构建你的web应用。如果你使用React,那么可能会涉及到一些常用的概念或技术,包括:
- ES6 React
- 虚拟DOM(virtual DOM)
- 组件驱动开发(component-driven development)
- 不变性(immutability)
- 自上而下的渲染(top-down rendering)
- 渲染路径和优化
- 打包工具, ES6, 构建请求, debugging, 路由等
-
同构React(isomorphic React)
在具体的React实践中,考虑到纯粹的UI或者UX设计人员,他们可能只会将CSS与HTML进行组合,换言之,大量的赋值还是会放置在HTML而非JSX中,建议还是可以运用jQuery+React或者Angular+React的方式。
Virtual Dom
如我们所知,在浏览器渲染网页的过程中,加载到HTML文档后,会将文档解析并构建DOM树,然后将其与解析CSS生成的CSSOM树一起结合产生爱的结晶——RenderObject树,然后将RenderObject树渲染成页面(当然中间可能会有一些优化,比如RenderLayer树)。这些过程都存在与渲染引擎之中,渲染引擎在浏览器中是于JavaScript引擎(JavaScriptCore也好V8也好)分离开的,但为了方便JS操作DOM结构,渲染引擎会暴露一些接口供JavaScript调用。由于这两块相互分离,通信是需要付出代价的,因此JavaScript调用DOM提供的接口性能不咋地。各种性能优化的最佳实践也都在尽可能的减少DOM操作次数。
而虚拟DOM干了什么?它直接用JavaScript实现了DOM树(大致上)。组件的HTML结构并不会直接生成DOM,而是映射生成虚拟的JavaScript DOM结构,React又通过在这个虚拟DOM上实现了一个 diff 算法找出最小变更,再把这些变更写入实际的DOM中。这个虚拟DOM以JS结构的形式存在,计算性能会比较好,而且由于减少了实际DOM操作次数,性能会有较大提升。
React渲染出来的HTML标记都包含了`data-reactid`属性,这有助于React中追踪DOM节点。
单向数据流
在React中,应用利用State与Props对象实现单向数据流的传递。换言之,在一个多组件的架构中,某个父类组件只会负责响应自身的State,并且通过Props在链中传递给自己的子元素。
/** @jsx React.DOM */
var FilteredList = React.createClass({
filterList: function(event){var updatedList = this.state.initialItems;
updatedList = updatedList.filter(function(item){return item.toLowerCase().search(
event.target.value.toLowerCase()) !== -1;
});
this.setState({items: updatedList});
},
getInitialState: function(){return {
initialItems: [
"Apples",
"Broccoli",
"Chicken",
"Duck",
"Eggs",
"Fish",
"Granola",
"Hash Browns"
],
items: []
}
},
componentWillMount: function(){this.setState({items: this.state.initialItems})
},
render: function(){return (
<divclassName="filter-list"><inputtype="text"placeholder="Search"onChange={this.filterList}/><Listitems={this.state.items}/></div>
);
}
});
var List = React.createClass({
render: function(){
return (
<ul>
{
this.props.items.map(function(item) {
return <likey={item}>{item}</li>
})
}
</ul>
)
}
});
React.render(<FilteredList/>, document.getElementById(‘mount-point‘));
Quick Start
Usage
基本的React的页面形式如下所示:
<!DOCTYPE html>
<html>
<head>
<scriptsrc="../build/react.js">
</script>
<scriptsrc="../build/JSXTransformer.js">
</script>
</head>
<body>
<divid="example">
</div>
<scripttype="text/jsx">
// ** Our code goes here! **
</script>
</body>
</html>
React独创了一种JS、CSS和HTML混写的JSX格式,可以通过在页面中引入JSXTransformer这个文件进行客户端的编译,不过还是推荐在网页端编译,笔者习惯使用Babel这个平台。
HelloWorld
var HelloMessage = React.createClass({
render: function() {return<div>Hello {this.props.name}</div>;
}
});
React.render(
<HelloMessagename="John" />,
document.getElementById(‘container‘)
);
React.render 是 React 的最基本方法,用于将模板转为 HTML 语言,并插入指定的 DOM 节点。要注意的是,React的渲染函数并不是简单地把HTML元素复制到页面上,而是维护了一张Virtual Dom映射表。
class ExampleComponent extends React.Component {
constructor() {
super();
this. _handleClick = this. _handleClick.bind(this);
this.state = Store.getState();
}
// ...
}
Setup:开发环境搭建
完整的React开发环境应该包括了JSX/ES6的解析以及模块化管理,笔者在这里是选用了WebPack与Babel。Webpack是一个强大的包管理以及编译工具,
参考资料
Webpack
Webpack是一个非常强大依赖管理与打包工具,其基本的配置方式可以如下:
var path = require(‘path‘);
var node_modules = path.resolve(__dirname, ‘node_modules‘);
var pathToReact = path.resolve(node_modules, ‘react/dist/react.min.js‘);
config = {
entry: [‘webpack/hot/dev-server‘, path.resolve(__dirname, ‘app/main.js‘)],
resolve: {
alias: {
‘react‘: pathToReact
}
},
output: {
path: path.resolve(__dirname, ‘build‘),
filename: ‘bundle.js‘,
},
module: {
loaders: [{
test: /\.jsx?$/,
loader: ‘babel‘
}],
noParse: [pathToReact]
}
};
module.exports = config;
Project Structure:项目结构
一个典型的项目结构你可以参考这个仓库。
config/
app.js
webpack.js (js config over json -> flexible)
src/
app/ (the React app: runs on server and client too)
components/
__tests__ (Jest test folder)
AppRoot.jsx
Cart.jsx
Item.jsx
index.js (just to export app)
app.js
client/ (only browser: attach app to DOM)
styles/
scripts/
client.js
index.html
server/
index.js
server.js.gitignore.jshintrc
package.json
README.md
Integrated With Angular
Angular与React是笔者喜欢的两个框架,二者可以相辅相成。可以查看笔者的这个库。
JSX
HTML 语言直接写在 JavaScript 语言之中,不加任何引号,这就是 JSX 的语法,它允许 HTML 与 JavaScript 的混写。
var names = [‘Alice‘, ‘Emily‘, ‘Kate‘];
React.render(
<div>
{
names.map(function (name) {
return <div>Hello, {name}!</div>
})
}
</div>,
document.getElementById(‘example‘)
);
上面代码体现了 JSX 的基本语法规则:遇到 HTML 标签(以 < 开头),就用 HTML 规则解析;遇到代码块(以 { 开头),就用 JavaScript 规则解析。JSX 允许直接在模板插入 JavaScript 变量。如果这个变量是一个数组,则会展开这个数组的所有成员:
var arr = [
<h1>Hello world!</h1>,
<h2>React is awesome</h2>,
];
React.render(
<div>{arr}</div>,
document.getElementById(‘example‘)
);
Transfer
JSX编译器的核心是将基于XML的语言编译成JS代码,主要是依赖于React.createElment函数。
var Nav;
// Input (JSX):var app = <Navcolor="blue" />;// Output (JS):var app = React.createElement(Nav, {color:"blue"});
var Nav, Profile;
// Input (JSX):var app = <Navcolor="blue"><Profile>click</Profile></Nav>;// Output (JS):var app = React.createElement(
Nav,
{color:"blue"},
React.createElement(Profile, null, "click")
);
JavaScript Expressions
属性表达式
如果需要在HTML中混入JavaScript变量值,需要利用{}来代替”“。
// Input (JSX):
var person = <Person name={window.isLoggedIn ? window.name : ‘‘} />;
// Output (JS):
var person = React.createElement(
Person,
{name: window.isLoggedIn ? window.name : ‘‘}
);
Boolean Attributes
// These two are equivalent in JSX for disabling a button
<input type="button" disabled />;
<input type="button" disabled={true} />;
// And these two are equivalent in JSX for not disabling a button
<input type="button" />;
<input type="button" disabled={false} />;
Child Expressions
// Input (JSX):
var content = <Container>{window.isLoggedIn ? <Nav /> : <Login />}</Container>;
// Output (JS):
var content = React.createElement(
Container,
null,
window.isLoggedIn ? React.createElement(Nav) : React.createElement(Login)
);
Comments
var content = (
<Nav>
{/* child comment, put {} around */}
<Person
/* multi
line
comment */
name={window.isLoggedIn ? window.name : ‘‘} // end of line comment
/>
</Nav>
);
Components
React提供了和以往不一样的方式来看待视图,它以组件开发为基础。组件是React的核心概念,React 允许将代码封装成组件(component),然后像插入普通 HTML 标签一样,在网页中插入这个组件。React.createClass 方法就用于生成一个组件类。对React应用而言,你需要分割你的页面,使其成为一个个的组件。也就是说,你的应用是由这些组件组合而成的。你可以通过分割组件的方式去开发复杂的页面或某个功能区块,并且组件是可以被复用的。这个过程大概类似于用乐高积木去瓶装不同的物体。我们称这种编程方式称为**组件驱动开发**。
LifeCycle
组件的生命周期分成三个状态:
-
Mounting:已插入真实 DOM,即Initial Render
-
Updating:正在被重新渲染,即Props与State改变
-
Unmounting:已移出真实 DOM,即Component Unmount
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):组件判断是否重新渲染时调用
Ini-tial Render
Props Change
State Change
这里可以看出,Props比State的改变会有多出一个shouldComponentUpdate的回调方法。
Com-po-nent Unmount
总结而言,一个完整的React Component的写法应该如下:
/**
* @jsx React.DOM
*/
var React = require(‘react‘),
MyReactComponent = React.createClass({
// The object returned by this method sets the initial value of this.state
getInitialState: function(){return {};
},
// The object returned by this method sets the initial value of this.props// If a complex object is returned, it is shared among all component instances
getDefaultProps: function(){return {};
},
// Returns the jsx markup for a component// Inspects this.state and this.props create the markup// Should never update this.state or this.props
render: function(){return (<div></div>);
},
// An array of objects each of which can augment the lifecycle methods
mixins: [],
// Functions that can be invoked on the component without creating instances
statics: {
aStaticFunction: function(){}
},
// -- Lifecycle Methods --
// Invoked once before first render
componentWillMount: function(){
// Calling setState here does not cause a re-render
},
// Invoked once after the first render
componentDidMount: function(){
// You now have access to this.getDOMNode()
},
// Invoked whenever there is a prop change
// Called BEFORE render
componentWillReceiveProps: function(nextProps){
// Not called for the initial render
// Previous props can be accessed by this.props
// Calling setState here does not trigger an an additional re-render
},
// Determines if the render method should run in the subsequent step
// Called BEFORE a render
// Not called for the initial render
shouldComponentUpdate: function(nextProps, nextState){
// If you want the render method to execute in the next step
// return true, else return false
return true;
},
// Called IMMEDIATELY BEFORE a render
componentWillUpdate: function(nextProps, nextState){
// You cannot use this.setState() in this method
},
// Called IMMEDIATELY AFTER a render
componentDidUpdate: function(prevProps, prevState){
},
// Called IMMEDIATELY before a component is unmounted
componentWillUnmount: function(){
}
});
module.exports = MyReactComponent;
Props
getDefaultProps
设置默认的Props.
Attributes
children
this.props 对象的属性与组件的属性一一对应,但是有一个例外,就是 this.props.children 属性。它表示组件的所有子节点。
var NotesList = React.createClass({
render: function() {return (
<ol>
{
this.props.children.map(function (child) {
return <li>{child}</li>
})
}
</ol>
);
}
});
React.render(
<NotesList><span>hello</span><span>world</span></NotesList>,
document.body
);
其效果图如下所示:
Validation
React.createClass({
propTypes: {
// You can declare that a prop is a specific JS primitive. By default, these// are all optional.
optionalArray: React.PropTypes.array,
optionalBool: React.PropTypes.bool,
optionalFunc: React.PropTypes.func,
optionalNumber: React.PropTypes.number,
optionalObject: React.PropTypes.object,
optionalString: React.PropTypes.string,
// Anything that can be rendered: numbers, strings, elements or an array// containing these types.
optionalNode: React.PropTypes.node,
// A React element.
optionalElement: React.PropTypes.element,
// You can also declare that a prop is an instance of a class. This uses// JS‘s instanceof operator.
optionalMessage: React.PropTypes.instanceOf(Message),
// You can ensure that your prop is limited to specific values by treating// it as an enum.
optionalEnum: React.PropTypes.oneOf([‘News‘, ‘Photos‘]),
// An object that could be one of many types
optionalUnion: React.PropTypes.oneOfType([
React.PropTypes.string,
React.PropTypes.number,
React.PropTypes.instanceOf(Message)
]),
// An array of a certain type
optionalArrayOf: React.PropTypes.arrayOf(React.PropTypes.number),
// An object with property values of a certain type
optionalObjectOf: React.PropTypes.objectOf(React.PropTypes.number),
// An object taking on a particular shape
optionalObjectWithShape: React.PropTypes.shape({
color: React.PropTypes.string,
fontSize: React.PropTypes.number
}),
// You can chain any of the above with `isRequired` to make sure a warning// is shown if the prop isn‘t provided.
requiredFunc: React.PropTypes.func.isRequired,
// A value of any data type
requiredAny: React.PropTypes.any.isRequired,
// You can also specify a custom validator. It should return an Error// object if the validation fails. Don‘t `console.warn` or throw, as this// won‘t work inside `oneOfType`.
customProp: function(props, propName, componentName) {if (!/matchme/.test(props[propName])) {
returnnew Error(‘Validation failed!‘);
}
}
},
/* ... */
});
State
React不提倡数据的双向绑定,而在用户行为下面产生的数据更新,React建议还是通过事件机制来处理。譬如下述例子中,输入框文本内容的改变,还是通过onChange事件,然后出发状态机的变化。
var LikeButton = React.createClass({
getInitialState: function() {return {liked: false};
},
handleClick: function(event) {this.setState({liked: !this.state.liked});
},
render: function() {var text = this.state.liked ? ‘like‘ : ‘haven\‘t liked‘;
return (
<p onClick={this.handleClick}>
You {text} this. Click to toggle.
</p>
);
}
});
React.render(
<LikeButton />,
document.getElementById(‘example‘)
);
Props与State对比
参考资料
组件的主要职责是将原始数据转化为HTML中的富文本格式,而Props与State协作完成这件事,换言之,Props与State的并集即是全部的原始数据。Props与State之间也是有很多交集的,譬如:
- Props与State都是JS对象。
- Props与State的值的改变都会触发界面的重新渲染。
- Props与State都是确定性的,即在确定的Props或者State的值的情况下都会得出相同的界面。
不过Props顾名思义,更多的是作为Component的配置项存在。Props往往是由父元素指定并且传递给自己的子元素,不过自身往往不会去改变Props的值。另一方面,State在组件被挂载时才会被赋予一个默认值,而常常在与用户的交互中发生更改。往往一个组件独立地维护它的整个状态机,可以认为State是一个私有属性。他们的对比如下:
描述 | Props | State |
---|---|---|
是否可以从父元素获取初始值 | Yes | Yes |
是否可以被父元素改变 | Yes | No |
是否可以设置默认值 | Yes | Yes |
是否可以在组件内改变 | No | Yes |
是否可以设置为子元素的初始值 | Yes | Yes |
是否可以在子元素中改变 | Yes | No |
Interactivity and Dynamic UIs
React.findDOMNode()
组件并不是真实的 DOM 节点,而是存在于内存之中的一种数据结构,叫做虚拟 DOM (virtual DOM)。只有当它插入文档以后,才会变成真实的 DOM 。根据 React 的设计,所有的 DOM 变动,都先在虚拟 DOM上发生,然后再将实际发生变动的部分,反映在真实 DOM上,这种算法叫做 DOM diff ,它可以极大提高网页的性能表现。但是,有时需要从组件获取真实 DOM 的节点,这时就要用到 React.findDOMNode 方法。
var MyComponent = React.createClass({
handleClick: function() {
React.findDOMNode(this.refs.myTextInput).focus();
},
render: function() {return (
<div>
<input type="text" ref="myTextInput" />
<input type="button" value="Focus the text input" onClick={this.handleClick} />
</div>
);
}
});
React.render(
<MyComponent />,
document.getElementById(‘example‘)
);
需要注意的是,由于 React.findDOMNode 方法获取的是真实 DOM ,所以必须等到虚拟 DOM 插入文档以后,才能使用这个方法,否则会返回 null 。上面代码中,通过为组件指定 Click 事件的回调函数,确保了只有等到真实 DOM 发生 Click 事件之后,才会调用 React.findDOMNode 方法。
getInitialState
设置State的初始状态。
var MyComponent = React.createClass({
getInitialState: function(){return {
count: 5
}
},
render: function(){return (
<h1>{this.state.count}</h1>
)
}
});
Style
参考资料
Inline-style
在React中,如果要使用行内元素,不可以直接使用style="”这种方式,可以有:
import React from ‘react‘;
var style = {
backgroundColor: ‘#EEE‘
};
export default React.createClass({
render: function() {return (
<divstyle={style}>
//或者<divstyle={{backgroundColor: ‘#EEE‘}}><h1>Hello world</h1></div>
)
}
});
Class
你可以根据这个策略为每个组件创建 CSS 文件,可以让组件名和 CSS 中的 class 使用一个命名空间,来避免一个组件中的一些 class 干扰到另外一些组件的 class。
app/components/MyComponent.css
.MyComponent-wrapper
{
background-color:#EEE;
}
app/components/MyComponent.jsx
import ‘./MyComponent.css‘;
import React from ‘react‘;
export default React.createClass({
render: function() {return (
<divclassName="MyComponent-wrapper"><h1>Hello world</h1></div>
)
}
});
Event
React对于事件的支持非常完善,可以查看这里。
TouchEvent
If you’d like to use React on a touch device such as a phone or tablet, simply call React.initializeTouchEvents(true);
to enable touch event handling.
接口暴露
譬如在某个子组件中,提供了某个方法:
var ButtonComponent = React.createClass({
getDragonKillingSword: function(){//送宝刀
},
render: function(){return (<buttononClick={this.getDragonKillingSword}>屠龙宝刀,点击就送</button>);
}
});
如果在父组件中想手动调用该方法,则可以利用ref方式:
var ImDaddyComponent = React.createClass({
render: function(){return (
<div>
//其他组件
<ButtonComponent />
//其他组件
</div>
);
}
});
在父组件的功能方程中:
this.refs.getSwordButton.getDragonKillingSword();
反之,如果需要在子组件中调用父组件的方法,则可以直接将父组件的方法作为Props参数传入到子组件中:
<ButtonComponent clickCallback={this.getSwordButtonClickCallback}/>
Ajax
组件的数据来源,通常是通过 Ajax 请求从服务器获取,可以使用 componentDidMount 方法设置 Ajax 请求,等到请求成功,再用 this.setState 方法重新渲染 UI。
var UserGist = React.createClass({
getInitialState: function() {return {
username: ‘‘,
lastGistUrl: ‘‘
};
},
componentDidMount: function() {
$.get(this.props.source, function(result) {var lastGist = result[0];
if (this.isMounted()) {
this.setState({
username: lastGist.owner.login,
lastGistUrl: lastGist.html_url
});
}
}.bind(this));
},
render: function() {return (
<div>
{this.state.username}‘s last gist is
<ahref={this.state.lastGistUrl}>here</a>.
</div>
);
}
});
React.render(
<UserGistsource="https://api.github.com/users/octocat/gists" />,
document.body
);
不过笔者习惯还是将整个获取数据,处理数据的业务逻辑放在Angular中进行。
Test:组件测试
对于React组件的测试,这里推荐使用[Jest](https://facebook.github.io/jest/),Jest也是由Facebook提供的测试框架,并且有很多强大的特性,但这里并会详细的介绍它们。关于Jest,我推荐你阅读和尝试来自Facebook的[Tutorial](https://facebook.github.io/jest/docs/tutorial-react.html#content)。对于ES6代码的测试,你可以参考 [React ES6 Testing](https://github.com/facebook/jest/tree/master/examples/react-es6)。
Reference
Tutorial
Practice
版权声明:本文为博主原创文章,未经博主允许不得转载。
原文:http://blog.csdn.net/wxyyxc1992/article/details/47791709
内容总结
以上是互联网集市为您收集整理的React全部内容,希望文章能够帮你解决React所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。