1.组件props的数据类型校验
# https://react.docschina.org/docs/typechecking-with-proptypes.html
例子:
import PropTypes from 'prop-types';
class Greeting extends React.Component {
render() {
return (
<h1>Hello, {this.props.name}</h1>
);
}
}
// 指定 props 的类型:
Greeting.propTypes = {
name: PropTypes.string
};
// 指定 props 的默认值
Greeting.defaultProps = {
name: 'Stranger'
};
# PropTypes常见类型
import PropTypes from 'prop-types';
MyComponent.propTypes = {
// 你可以将属性声明为 JS 原生类型,默认情况下
// 这些属性都是可选的。
optionalArray: PropTypes.array,
optionalBool: PropTypes.bool,
optionalFunc: PropTypes.func,
optionalNumber: PropTypes.number,
optionalObject: PropTypes.object,
optionalString: PropTypes.string,
optionalSymbol: PropTypes.symbol,
// 任何可被渲染的元素(包括数字、字符串、元素或数组)
// (或 Fragment) 也包含这些类型。
optionalNode: PropTypes.node,
// 一个 React 元素。
optionalElement: PropTypes.element,
// 一个 React 元素类型(即,MyComponent)。
optionalElementType: PropTypes.elementType,
// 你也可以声明 prop 为类的实例,这里使用
// JS 的 instanceof 操作符。
optionalMessage: PropTypes.instanceOf(Message),
// 你可以让你的 prop 只能是特定的值,指定它为
// 枚举类型。
optionalEnum: PropTypes.oneOf(['News', 'Photos']),
// 一个对象可以是几种类型中的任意一个类型
optionalUnion: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.instanceOf(Message)
]),
// 可以指定一个数组由某一类型的元素组成
optionalArrayOf: PropTypes.arrayOf(PropTypes.number),
optionalArrayOf: PropTypes.oneOfType(PropTypes.number, PropTypes.string),
// 可以指定一个对象由某一类型的值组成
optionalObjectOf: PropTypes.objectOf(PropTypes.number),
// 可以指定一个对象由特定的类型值组成
optionalObjectWithShape: PropTypes.shape({
color: PropTypes.string,
fontSize: PropTypes.number
}),
// An object with warnings on extra properties
optionalObjectWithStrictShape: PropTypes.exact({
name: PropTypes.string,
quantity: PropTypes.number
}),
// 你可以在任何 PropTypes 属性后面加上 `isRequired` ,确保
// 这个 prop 没有被提供时,会打印警告信息。
requiredFunc: PropTypes.func.isRequired,
// 任意类型的必需数据
requiredAny: PropTypes.any.isRequired,
// 你可以指定一个自定义验证器。它在验证失败时应返回一个 Error 对象。
// 请不要使用 `console.warn` 或抛出异常,因为这在 `oneOfType` 中不会起作用。
customProp: function(props, propName, componentName) {
if (!/matchme/.test(props[propName])) {
return new Error(
'Invalid prop `' + propName + '` supplied to' +
' `' + componentName + '`. Validation failed.'
);
}
},
// 你也可以提供一个自定义的 `arrayOf` 或 `objectOf` 验证器。
// 它应该在验证失败时返回一个 Error 对象。
// 验证器将验证数组或对象中的每个值。验证器的前两个参数
// 第一个是数组或对象本身
// 第二个是他们当前的键。
customArrayProp: PropTypes.arrayOf(function(propValue, key, componentName, location, propFullName) {
if (!/matchme/.test(propValue[key])) {
return new Error(
'Invalid prop `' + propFullName + '` supplied to' +
' `' + componentName + '`. Validation failed.'
);
}
})
};
2.Props、State与render函数
# 当组件的Props或者State发生改变时,render函数就会重新执行。即数据驱动页面更新渲染
# 当父组件的render函数被运行时,它的子组件的render都会被重新执行一次。
例子:当<input/>的值发生改变,则触发函数changeInputHandle去更新state的inputVal的值,再触发render函数将inputVal的值更新到<input/>的value中
import React from 'react';
// 引入子组件
import TodoItem from './TodoItem';
class App extends React.Component {
// 组件构造器
constructor(props) {
super(props);
this.state = {
todoItems: ['vue', 'react', 'node'],
inputVal: ''
};
// 给方法绑定this的指向作用域,方法内的this指向本组件
this.changeInputHandle = this.changeInputHandle.bind(this);
}
changeInputHandle(e) {
const val = e.target.value;
this.setState({
inputVal: val
});
}
render() {
return (
<div className='App'>
<div>
<input type='text' value={this.state.inputVal} onChange={this.changeInputHandle}/>
<button onClick={this.addHandle}>add</button>
</div>
<ul>
{
this.state.todoItems.map((item, index) => {
return (
<TodoItem
key={index}
index={index}
content={item}/>
)
})
}
</ul>
</div>
)
}
}
export default App;
# 子组件TodoItem
import React from 'react';
class TodoItem extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
// this.props发生改变,则会重新执行render函数
render() {
const {content} = this.props
return (
<li>{content}</li>
)
}
}
export default TodoItem;
3.React的虚拟DOM
react虚拟DOM就是一个js对象,用来描述真实DOM。
例子:
真实DOM:<div>hello</div>
虚拟DOM: ['div',{...属性},'hello']
return( <div>hello</div> ) 等同于 return React.createElement('div',{},'hello')
数据渲染过程:
1. state 初始化数据
2. jsx 定义渲染模板,render(){return(<div>hello</div>)}
3. 生成虚拟DOM,虚拟DOM就是一个js对象,用来描述真实DOM
虚拟DOM: ['div',{...属性},'hello']
4. 数据+模板,结合生成真实DOM, 显示页面(即组件被第一次创建时调用构造函数)
真实DOM:<div>hello</div>
5. state 发生改变,即this.setState({name: 'react'}),setState是异步函数,提高渲染性能
6. 数据+模板,结合生成新的虚拟DOM(比生成真实DOM提高了性能)
新虚拟DOM: ['div',['react']]
7. 新虚拟DOM与旧虚拟DOM做比较,找出不同的地方(Diff算法)
8. 直接操作旧的DOM, 将新改变的内容数据更新渲染出来
render(){
// jsx->虚拟DOM(jsx对象)->真实DOM
return( <div>hello</div> )
}
# setState是异步函数,setState({},callback) 或者 setState(fun,callback)
this.setState(
()=>{
return{}
},
()=>{}
)
4.React的虚拟DOM中的Diff算法(同层级比对)
循环渲染列表,key={},不要使用index值作为key,因为Diff算法同层级比对是根据key值来判断。
5.React组件的生命周期
# 父组件
import React, {Component} from 'react';
import TodoItem from './TodoItem';
class App extends Component {
// 构造函数,组件构建时被执行,只在组件被首次创建时执行一次
constructor(props) {
console.log('1.父组件-构造函数')
super(props);
// 初始化组件数据
this.state = {
todoItems: ['vue', 'react', 'node'],
inputVal: ''
};
this.changeInputHandle = this.changeInputHandle.bind(this);
}
// 组件将要渲染挂载到页面,在render函数执行之前
componentWillMount() {
// 只在组件被首次创建时执行一次
console.log('2.父组件-componentWillMount')
}
// 组件已挂载到页面,一般在此执行ajax数据请求
componentDidMount() {
// 只在组件被首次创建时执行一次
console.log('4.父组件-componentDidMount')
}
// 当state数据发生改变时(即this.setState),执行询问是否允许更新组件
shouldComponentUpdate(nextProps, nextState, nextContext) {
// true为允许更新组件, false为不允许
console.log('5.父组件-shouldComponentUpdate')
if (nextProps.inputVal !== this.state.inputVal) {
return true
}
return false
}
// 组件将要更新
componentWillUpdate(nextProps, nextState, nextContext) {
console.log('6.父组件-componentWillUpdate')
}
// 组件更新完成
componentDidUpdate(prevProps, prevState, snapshot) {
console.log('7.父组件-componentWillUpdate')
}
// props改变时(父传子),执行询问是否更新组件,true允许,false不允许
// 用于子组件中,当父传子的数据发生改变时,是否允许更新子组件
// 被执行的条件:父传子的数据发生改变,同时子组件已经在父组件中(不是第一次构造渲染)
componentWillReceiveProps(nextProps, nextContext) {
console.log('8.父组件-componentWillReceiveProps')
return true
}
// 组件将要销毁
componentWillUnmount() {
console.log('9.父组件-componentWillUnmount')
}
// input输入值发生改变时更新state
changeInputHandle(e) {
const val = e.target.value;
this.setState(() => {
return {
inputVal: val
}
});
}
// state或者props改变都会执行render函数
render() {
console.log('3.父组件-render')
return (
<div className='App'>
<div>
<input type='text' value={this.state.inputVal} onChange={this.changeInputHandle}/>
</div>
<ul>
{
this.state.todoItems.map((item, index) => {
return (
<TodoItem key={index} content={item}/>
)
})
}
</ul>
</div>
)
}
}
export default App;
# 子组件
import React from 'react';
class TodoItem extends React.Component {
// 构造函数,组件构建时被执行,只在组件被首次创建时执行一次
constructor(props) {
console.log('1.子组件-构造函数')
super(props);
this.state = {};
}
// 组件将要渲染挂载到页面,在render函数执行之前
componentWillMount() {
// 只在组件被首次创建时执行一次
console.log('2.子组件-componentWillMount')
}
// 组件已挂载到页面,一般在此执行ajax数据请求
componentDidMount() {
// 只在组件被首次创建时执行一次
console.log('4.子组件-componentDidMount')
}
// 当state数据发生改变时(即this.setState),执行询问是否允许更新组件
shouldComponentUpdate(nextProps, nextState, nextContext) {
// true为允许更新组件, false为不允许
console.log('5.子组件-shouldComponentUpdate')
if (nextProps.inputVal !== this.state.inputVal) {
return true
}
return false
}
// 组件将要更新
componentWillUpdate(nextProps, nextState, nextContext) {
console.log('6.子组件-componentWillUpdate')
}
// 组件更新完成
componentDidUpdate(prevProps, prevState, snapshot) {
console.log('7.子组件-componentWillUpdate')
}
// props改变时(父传子),执行询问是否更新组件,true允许,false不允许
// 用于子组件中,当父传子的数据发生改变时,是否允许更新子组件
// 被执行的条件:父传子的数据发生改变,同时子组件已经在父组件中(不是第一次构造渲染)
componentWillReceiveProps(nextProps, nextContext) {
console.log('8.子组件-componentWillReceiveProps')
return true
}
// 组件将要销毁
componentWillUnmount() {
console.log('9.子组件-componentWillUnmount')
}
render() {
console.log('3.子组件-render')
const {content} = this.props;
return (
<li>{content}</li>
)
}
}
export default TodoItem;
6.Mock接口数据模拟
# 使用 Charles 进行接口数据模拟
下载:https://www.charlesproxy.com/download/
使用流程:
1.在本地创建json文件
2.打开 Charles 工具,tool > map local > 指定接口返回的json数据