天天看点

React知识点

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组件的生命周期

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数据