React - 进阶详解
提供一个在线编辑器:react
https://codesandbox.io/s/new
一、JSX深入理解
JSX只是React.createElement(component,props,...children)
的语法糖
//JSX
import React from 'react';//必须引入
.........
<Greeting date={2} name="cindy"/>
__________________________________
编译后JSX会调用
React.createElement(
Greeting,
{data: 2, name: "cindy" },
null
)
1.JSX标签
1)大写的 JSX 标签名代表一个 React 组件,若是小写字母,表示JSX内置的组件
2) JSX编译首先会调用React.creteElement()
,所以JSX必须现调用
es6: import React from 'react';
es5: var React = require('react')
3) 点表示法:可以任意从一个模块中调出任意一个组件
import React from 'react';
const myComponent = {
picker:function(props){
return <h1>children get a props {props.cololr}</h1>
}
}
class blueDatePicker
4)不能用表达式作为React 元素的标签
`
2.JSX属性的表达方式
注意:在react中,没有class属性,替代为className属性
1.javascript表达式
你可以传递任何 {} 包裹的 JavaScript 表达式作为一个属性值,这个表达式会自动计算最后的结果。 <MyComponent foo={1 + 2 + 3 + 4} />
注意 :在JSX中if语句和for语句不能使用,因为不是JSX表达式
2.字符串
<MyComponent message={'hello world'} />
<MyComponent message="hello world" />
3.扩展属性{…props}
如果有了个 props 对象,并且想在 JSX 中传递它,你可以使用 … 作为扩展操作符来传递整个属性对象。下面两个组件是等效的:
function Greeting() {
return <Greeting firstName="Cindy" lastName="Stella" />;
}
function World() {
const props = {firstName: 'Ben', lastName: 'Hector'};
return <Greeting {...props} />;
}
3.子代
props.children - 一个标签内的子元素
4.JSX - 嵌套元素
二、React.Component - lifecycles
React的Compoent组件将react的UI界面分割成一个个组件。
Component最重要的就是生命周期(lifecycles),生命周期分为3个阶段:初始化、更新、卸载
如图,可以把组件生命周期大致分为三个阶段:
第一阶段:是组件第一次绘制阶段,如图中的上面虚线框内,在这里完成了组件的加载和初始化;
第二阶段:是组件在运行和交互阶段,如图中左下角虚线框,这个阶段组件可以处理用户交互,或者接收事件更新界面;
第三阶段:是组件卸载消亡的阶段,如图中右下角的虚线框中,这里做一些组件的清理工作。
1.创建时(初始化组件):
constructor() - getInitialState
defaultProps - getDefaultProps
componentWillMount()
render()
componentDidMount()
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
age: 12
};
}
componentWillMount() {
console.log("componentWillmount");
}
render() {
console.log("render");
return <h1>{this.props.name}</h1>;
}
componentDidMount() {
console.log("componentDidMount");
}
}
App.defaultProps = {
name: "this.is life cycle test"
};
2.更新时:
render()
componentWillReceiveProps()
shouldComponentUpdate()
componentWillUpdate()
componentDidUpdate()
2.1 state更新
updating State
ShouldComponentUpdate()
componentWillUpdate()
render()
ComponentDidUpdate()
2.2 props更新
updateing Props
ComponentWillReciveProps()
shouldComponentUpdate()
ComponentWillUpdate()
render()
ComponentDidUpdate()
3.卸载时:
componentWillUnmount()
4.详细介绍:
1.render()
更新UI界面的方法
该方法是必须的,有一个return 返回。
创建虚拟DOM,进行diff算法,更新DOM树都要在render()方法中进行
2.constructor(props)
构造函数,在创建组件的时候调用一次。
React组件的构造函数将会在render()之前被调用。当为一个React.Component子类定义构造函数时,你应该在任何其他的表达式之前调用super(props)。否则,this.props在构造函数中将是未定义,并可能引发异常。
3.ComponentWillMount()
在组件初始化之前调用一次,组件更新时不调用。如果在这个函数里面调用setState,本次的render函数可以看到更新后的state,并且只渲染一次。
4.componentDidMount()
在组件挂载之后调用一次。这个时候,子主键也都挂载好了,可以在这里使用refs。
若你需要从远端加载数据,这是一个适合实现网络请求的地方。在该方法里设置状态将会触发重新渲染。
5.componentWillReceiveProps(nextProps)
组件初始化时不调用,组件接受新的props时调用。
6.shouldComponentUpdate(nextProps,nextState) {}
该方法总是在render()之前被唤起,但不会在初始化组件执行
react性能优化非常重要的一环。组件接受新的state或者props时调用,我们可以设置在此对比前后两个props和state是否相同,如果相同则返回false阻止更新,因为相同的属性状态一定会生成相同的dom树,这样就不需要创造新的dom树和旧的dom树进行diff算法对比,节省大量性能,尤其是在dom结构复杂的时候.
总是返回 :return true;//return false;
7.componentWillUpdate(nextProps,nextState) {}
当shouldComponentUpdate函数返回true时,componentWillUpdate()为在渲染前被立即调用。在更新发生前,使用该方法是一次准备机会。该方法不会在初始化渲染时调用。
注意你不能在这调用this.setState(),若你需要更新状态响应属性的调整,使用componentWillReceiveProps()代替。
注意:
若shouldComponentUpdate()返回false,componentWillUpdate()将不会被调用。
8.ComponentDidUpdate(prevProps,prevState) {}
componentDidUpdate()会在更新发生后(render方法之后)立即被调用。该方法并不会在初始化渲染时调用。
当组件被更新时,使用该方法是操作DOM的一次机会。这也是一个适合发送请求的地方,要是你对比了当前属性和之前属性(例如,如果属性没有改变那么请求也就没必要了)。
若shouldComponentUpdate()返回false,componentDidUpdate()将不会被调用。
9.componentWillUnmount(){}
componentWillUnmount()在组件被卸载和销毁之前立刻调用。可以在该方法里处理任何必要的清理工作,例如解绑定时器,取消网络请求,清理任何在componentDidMount环节创建的DOM元素。
测试代码:
//可在项目中测试使用
class App extends Component {
constructor(props) {
super(props);
this.state = {
age: 12
}
alert("initial render");
alert("constructor");
}
componentWillMount() {
alert("componentWillMount");
alert(this.state.age);
}
componentDidMount() {
alert("componentdidMout");
this.setState({
age: 33
});
}
componentWillUnmount() {
alert("componentWillUnmounted");
}
ComponentWillUpdate() {
alert("componentWILLuPDATE");
}
componentWillReceiveProps(nextProps) {
}
shouldComponentUpdate(nextProps,nextState) {
alert("shouldComponentUpdate");
console.log(nextState);
return true; //必须写词一部
//return false;若为false,不会进行任何后续更新state 操作
}
componentWillUpdate(nextProps,nextState) {
alert("compoentWillUpdate");
}
componentDidUpdate(prevProps,prevState) {
alert("componentDidUpdate");
}
render() {
alert("render");
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Welcome to React</h1>
</header>
<p className="App-intro">
To get started, edit <code>src/App.js</code> and save to reload.
</p>
<p>{this.state.age}</p>
</div>
);
}
}
export default App;
三、Refs & DOM
React支持给任意组建添加特殊属性,其中就有ref属性。
ref属性接受一个回调参数,他在组件被加载和卸载时被调用
1.DOM元素添加ref 属性
class App extends Component {
constructor(props) {
super(props);
this.changeFocus = this.changeFocus.bind(this);
}
changeFocus() {
this.input.focus();
}
render() {
return (
<div className="App">
<form>
<input
type="text"
ref={(input) => this.input = input }
/>
<input
type="text"
value="hello"
onFocus = {this.changeFocus}
/>
</form>
</div>
);
}
}
注:ref 回调参数中:接收 底层DOM元素 作为参数,
所以上述代码中,ref中的参数是<input/>
2.类组件添加ref
当 ref 属性用于使用 class 声明的自定义组件时,ref 的回调接收的是已经加载的 React 实例。
class AutoFocusTextInput extends React.Component {
componentDidMount() {
this.textInput.focusTextInput();
}
render() {
return (
<CustomTextInput
ref={(input) => { this.textInput = input; }} />
);
}
}
3.用 函数声明的组件 不能绑定ref属性
四、不使用Es6
因各大浏览器已兼容ES6,此作为了解:http://www.css88.com/react/docs/react-without-es6.html
五、不适用JSX
react 的语法糖就是使用JSX,不使用仅作了解:http://www.css88.com/react/docs/react-without-jsx.html
六、使用PropsTypes检查属性类型
import PropTypes from 'prop-types';
class Greeting extends React.Component {
render() {
return (
<h1>Hello, {this.props.name}</h1>
);
}
}
Greeting.propTypes = {
name: PropTypes.string
};
注意:protoytypes直再开发模式下进行检查
七、uncontrolled Components / controlled Components
都用于表单元素,两者区别:表单数据是交给react组件处理(受控组件),还是让表单数据交给DOM元素处理(非受控组建)
6.1非受控组建 - 使用ref属性来接收DOM表单数据
class NameForm extends React.Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(event) {
alert('A name was submitted: ' + this.input.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" ref={(input) => this.input = input} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
1)默认值 - defaultvalue
因为在react生命周期中,表单中value属性会覆盖DOM中的value。
defaultValue - react的初始值。和后续DOM中的值无关
input :radio/checkbox 支持defaultValue
select textarea: 支持defaulteValue
//1.input - text/textarea
class App extends React.Component{
handelData(){
alert(`data :${this.textInput.value}`)
}
render(){
return(
<form onSubmit={this.handelData.bind(this)}>
<label>name</label>
<input type="text"
ref={(input) => this.textInput = input}
defaultValue={"name"}/>
<br/>
<input type="submit" value="submit"/>
</form>
);
}
}
//2.input - radio/checkbox
class App extends React.Component {
handelData() {
alert(`data :${this.textInput.value}`);
}
render() {
return (
<form onSubmit={this.handelData.bind(this)}>
<input type="radio" name="sex" value="male" defaultChecked />mail
<input type="radio" name="sex" value="femail" />femial
</form>
);
}
}
//3.select
class App extends React.Component {
handelData() {
alert(`data :${this.selectValue.value}`);
}
render() {
return (
<form onSubmit={this.handelData.bind(this)}>
<select
defaultValue={"japan"}
ref={select => (this.selectValue = select)}
>
<option value="china">china</option>
<option value="india">india</option>
<option value="japan">japan</option>
</select>
<input type="submit" value="submit"/>
</form>
);
}
}
6.2受控组建 - 将表单元素:<input><select><textarea>
的值以及更新状态保存在组件state状态中,并且只能通过setState方法,进行更新
//1.input:text/textarea
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: "name"
};
}
changeValue(event) {
this.setState({value: event.target.value});
}
handelData() {
alert(`data :${this.state.value}`);
}
render() {
return (
<form onSubmit={this.handelData.bind(this)}>
<input
type="text"
value={this.state.value}
onChange={this.changeValue.bind(this)}
/>
<input type="submit" value="submit" />
</form>
);
}
}
设置表单元素的value属性之后,其显示值将由this.state.value决定,以满足React状态的同一数据理念。每次键盘敲击之后会执行handleChange方法以更新React状态,显示值也将随着用户的输入改变。
由于 value 属性设置在我们的表单元素上,显示的值总是 this.state.value,以满足 state 状态的同一数据理念。由于 handleChange 在每次敲击键盘时运行,以更新 React state(状态),显示的值将更新为用户的输入。
//2.select
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: "japan"
};
}
changeValue(event) {
this.setState({value: event.target.value});
}
handelData() {
alert(`data :${this.state.value}`);
}
render() {
return (
<form onSubmit={this.handelData.bind(this)}>
<select value={this.state.value} onChange={this.changeValue.bind(this)}>
<option value="china">china</option>
<option value="japan">japan</option>
<option value="india">india</option>
</select>
<input type="submit" value="submit" />
</form>
);
}
}
1.在react中,不在单独的给每一个option标签添加selected属性,而是给select标签添加value属性,表示选中的元素
2.处理多个输入元素
当您需要处理多个受控的 input 元素时,您可以为每个元素添加一个 name 属性,并且让处理函数根据 event.target.name 的值来选择要做什么。
七、
七、优化性能
使用react - 最大限度减少更新UI带来的DOM操作数量,那如何更进一步的加快react应用
1.使用react开发者工具