原文地点:https://gmiam.com/post/react-…
React 是一个 JS 库,主假如经由历程支配数据的体式格局去支配 DOM,为何要重造轮子呢,因为 FB 觉的如今市面上的 MV* 框架关于建立大型运用顺序不够直观,不能满足需求,所以诞生了 React。
React 如今官方的引见是 Declarative、Component-Based、Learn Once, Write Anywhere,实在最先推出时重要的特征是 Virtual DOM,因为 DOM 支配老是很慢的,而 JS 的机能日益向上,所以 React 内部用 JS 保护一颗 DOM 树,每次数据变了从新天生一颗树与之前的做对照,把现实变化的处所运用到实在的 DOM 上。实在说它机能高,只不过是用 JS 的体式格局盘算出最小的 DOM 支配,所以机能就上来了。
说到这里我们现实支配下吧,这里假定你熟习 node、babel、webpack 体式格局,固然你也能够挑选你喜欢的体式格局 传送门
起首建立目次构造
react-demo
.babelrc
index.html
src
app.js
index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>React App</title>
</head>
<body>
<div id="app"></div>
<script src="bundle.js"></script>
</body>
</html>
app.js
var React = require( 'react' )
var ReactDOM = require( 'react-dom' )
var HelloMessage = React.createClass( {
render: function () {
return <div>Hello {this.props.name}</div>
}
})
ReactDOM.render( <HelloMessage name="John" />, document.getElementById( 'app' ) )
.babelrc
{ "presets": ["es2015","react"] }
装置依靠 npm install --save react react-dom babel-preset-react babel-loader babel-core
编译监听 webpack src/app.js bundle.js -w --module-bind 'js=babel'
翻开 index.html 检察结果
先说下 jsx 语法,React 让你不须要再写 html 拼接字符等支配,而是直接写 html,js 处置惩罚放到 { } 里誊写,官方供应 jsx 语法非必要,也能够离开写纯 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" }), document.getElementById( 'app' ));
然则能够看出这么贫苦没人去手写的
再来说下组件,React 的观点就是给运用分层,建立一个个组件,末了拼出一个页面,组件轻易后期的保护、扩大、以及再重用,随着组件的越多背面写的代码越少,来个例子
var Avatar = React.createClass({
render: function() {
return (
<div>
<PagePic pagename={this.props.pagename} />
<PageLink pagename={this.props.pagename} />
</div>
);
}
});
var PagePic = React.createClass({
render: function() {
return (
<img src={'https://graph.facebook.com/' + this.props.pagename + '/picture'} />
);
}
});
var PageLink = React.createClass({
render: function() {
return (
<a href={'https://www.facebook.com/' + this.props.pagename}>
{this.props.pagename}
</a>
);
}
});
ReactDOM.render(
<Avatar pagename="Engineering" />,
document.getElementById('app')
);
能够看到组件要供应本身的 render 要领,组件能够互相嵌套,数据经由历程 this.props 单向通报
同时须要注重
属性 class 要写成 className,for 写成 htmlFor,因为它们是 js 的保留字
关于render 返回的内容只能有一个顶级标签,假如标签凌驾多行要用 ( ) 包括
关于 props 不要去转变它,会致使一些不可预知的题目,别的官方引荐用 es6 的 … 支配符去挂载属性
var props = { foo: 'default', bar:'bar' };
var component = <Component {...props} foo={'override'} />;
console.log(component.props.bar); // 'bar'
console.log(component.props.foo); // 'override'
这里有个特别属性 this.props.children,来个例子
var NotesList = React.createClass({
propTypes: {
children: React.PropTypes.array.isRequired,
},
render: function() {
return (
<ol>
{
React.Children.map(this.props.children, function (child) {
return <li>{child}</li>;
})
}
</ol>
);
}
});
ReactDOM.render(
<NotesList>
<span>hello</span>
<span>world</span>
</NotesList>, document.getElementById('app')
);
同时能够看到这里供应了 propTypes 能够给属性做搜检,考证申明 children 必需供应且是一个数组(多个),更多的范例考证能够 看这里
前面建立组件都是经由历程 React.createClass ,能够经由历程 es6 class 语法
class HelloMessage extends React.Component {
render() {
return <div>Hello {this.props.name}</div>;
}
}
ReactDOM.render(<HelloMessage name="Sebastian" />, document.getElementById('app'));
另有 Stateless Functions 体式格局
function HelloMessage(props) {
return <div>Hello {props.name}</div>;
}
ReactDOM.render(<HelloMessage name="Sebastian" />, document.getElementById('app'));
官方引荐只管写 stateless functions ,因为将来会优化这些来防止无用的搜检和内存分派
下面看下怎样写事宜
var Input = React.createClass({
getInitialState: function() {
return {value: 'Hello!'};
},
handleChange: function(event) {
this.setState({value: event.target.value});
},
render: function () {
var value = this.state.value;
return (
<div>
<input type="text" value={value} onChange={this.handleChange} />
<p>{value}</p>
</div>
);
}
});
ReactDOM.render(<Input/>, document.getElementById('app'));
骆驼式的 on 语法即可监听事宜,事宜是规范的跨浏览器的事宜,虽然内联写法,然则是托付完成的~
说到了事宜交互能够就要设及猎取实在的 dom 节点,React 经由历程 ref 设置,来个例子
var React = require( 'react' )
var ReactDOM = require( 'react-dom' )
var MyComponent = React.createClass({
handleClick: function() {
this.refs['myinput'].focus()
},
render: function() {
return (
<div>
<input type="text" ref="myinput" />
<input
type="button"
value="Focus the text input"
onClick={this.handleClick}
/>
</div>
);
}
});
ReactDOM.render(
<MyComponent />,
document.getElementById('app')
);
ref 字符属性的体式格局将来会被烧毁,官方引荐运用 ref callback 体式格局
var MyComponent = React.createClass({
handleClick: function() {
if (this.myTextInput !== null) {
this.myTextInput.focus();
}
},
render: function() {
return (
<div>
<input type="text" ref={(ref) => this.myTextInput = ref} />
<input
type="button"
value="Focus the text input"
onClick={this.handleClick}
/>
</div>
);
}
});
ReactDOM.render(
<MyComponent />,
document.getElementById('app')
);
说到这里看下组件的生命周期与怎样更新,照样来个例子
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 />, document.getElementById('app'));
生命周期有三个重要部份
Mounting 插进去 dom
getInitialState()
componentWillMount()
componentDidMount ()
Updating 从新衬着
componentWillReceiveProps(object nextProps)
shouldComponentUpdate(object nextProps, object nextState)
componentWillUpdate(object nextProps, object nextState)
componentDidUpdate(object prevProps, object prevState)
Unmounting 移除 dom
componentWillUnmount()
周期供应了 will 要领在事变发作之前挪用, did 要领在事变法神以后挪用,详细检察这里
关于更新,上面的例子在组件 componentDidMount (插进去 dom 后) hook 中定时更新组件的 state,state变更会致使 render 从新衬着页面
关于这里说下机能题目,虽然假造dom盘算历程很快,然则许多时刻我们能够防止它的盘算以更好的优化处置惩罚
比方 一个组件的更新能够会致使它的子组件一同随着更新,子组件极能够没有变化,但同样会举行一次diff运算,白白浪费了时候,所以 React 供应了 shouldComponentUpdate 钩子函数,默许是直接返回 true,也及是每次都运算比较,所以我们能够在这里优化,来个例子
React.createClass({
propTypes: {
value: React.PropTypes.string.isRequired
},
shouldComponentUpdate: function(nextProps, nextState) {
return this.props.value !== nextProps.value;
},
render: function() {
return <div>{this.props.value}</div>;
}
});
这里只要 value 变化的时刻在从新衬着盘算,不然直接跳过
关于上面的浅对照,React 供应了通用解决方案 PureRenderMixin 扩大,运用 React 的 mixins 功用即可自动完成处置惩罚比对
var PureRenderMixin = require('react-addons-pure-render-mixin');
React.createClass({
mixins: [PureRenderMixin],
render: function() {
return <div>{this.props.value}</div>;
}
});
然则假如有深层构造,上面的处置惩罚能够不会按预期事情,比方
// this.props.value 的值为 { foo: 'bar' }
// nextProps.value 的值为 { foo: 'bar' },
// 然则对象的援用差别,致使不会相称
this.props.value !== nextProps.value; // true
而且假如我们不小心治理援用的话也会激发另一些题目,比方这个组件有一个父组件
React.createClass({
getInitialState: function() {
return { value: { foo: 'bar' } };
},
onClick: function() {
var value = this.state.value;
value.foo += 'bar'; // ANTI-PATTERN!
this.setState({ value: value });
},
render: function() {
return (
<div>
<InnerComponent value={this.state.value} />
<a onClick={this.onClick}>Click me</a>
</div>
);
}
});
起首内部组件获得 { foo: 'bar' }
,点击后动身 value 更新 { foo: 'barbar' }
,触发 re-rendering 顺序,内部组件将会获得 { foo: 'barbar' }
,然则 this.props.value 与 nextProps.value 指向同一个援用,致使任何时刻比对都是 true,而致使页面不更新
而且假如父组件运用 PureRenderMixin 的话,因为修改雷同援用所以也会致使父组件的 re-rendering 不触发
那末了该怎样处置惩罚呢?请看下一篇 Immutable-js 来拯救你~