React

185 阅读19分钟

React

create-react-app官方脚手架

  • 安装脚手架npm i create-react-app -g
  • create-react-app xxx 创建一个项目
或者npx create-react-app xxx

每个文件夹的作用

  • node_modules 存放安装的模块
  • public 存放HTML页面模块,最后会把基于webpack合并压缩打包的css/js插入到页面中,也会把各个组件合并到页面的指定容器中
  • index.html页面模板,除了最后webpack处理好的内容会插入到模板中,我们自己可能也会在页面中搞点事:如不支持CommonJS/ES6Module规范的模块,我们可以直接在页面中基于script导入,后期在程序中基于window.xxx去访问即可;对于一些需要优先加载和处理的,不想打包到一起,例如loading,我们可以单独卸载HTML中
  • src 所有后期需要基于webpack打包、压缩处理的内容都需要放在这
  • index.js是入口
  • api 处理数据接口
  • assets 静态资源文件
  • pages 页面/业务组件
  • component 公共组件
  • store 公共状态管理
  • package.json 配置清单
  • 默认只安装了react/react-dom/react-scripts
  • 想要跨域的话在package.json里面设置"proxy":"地址"就可以了,也可以基于http-proxy-middleware插件实现
  • name 名字
  • version 版本号
  • private 是否私有
  • dependencies 生产依赖
  • scripts 指令
    • react-scripts是react把webpack要干的那些事情都封装到这个模块中
    • start 开发环境下预览项目
    • build 开发环境下打包项目
    • test 启动单元测试
    • eject 默认情况下,react脚手架把webpack配置项都放到了node_modules/react-scripts中了,真实项目中,我们需要把这些配置信息暴露出来,供自己去修改完善;执行eject命令就是暴露配置项的,但是此命令是不可逆的
      • 如果有仓库需要先提交到历史区,否则会报错
      • 执行以后会多两个文件夹config/script,webpack的配置项就在config文件夹中,且package.json也发生了改变
  • eslintConfig 词法检测
  • browserslist 浏览器兼容
配置less
  • yarn add less less-loader@7.3.0
  • less-loader 新版本不支持create-react-app的配置,所以我们要安装7.3.0

JSX元素,在React里面为模板

  • 全称JavaScript Xml
  • .js默认不支持jsx语法,格式化会报错
  • 方法一:点击vscode右下角jscript,调整格式为JavaScript React
  • 方法二:直接创建.jsx后缀的文件,不用.js
  • 使React中构建视图的语法(Vue中构建视图用的是Template)
  • 模板字符串内一定要放JS表达式,意思就是必须返回个东西,单纯的变量也是个值,且会进行toString()处理 +在jsx资源中导入一些资源,绝对路径的资源没问题,例如http://...这些没问题,导入相对路径的不能直接按照现在的结构目录去做,因为webpack编译后的路径就不是这样的了,此时我们需要基于require/import 导入资源,src赋值导入的内容,在css中则没事

ReactDOM.render(jsx虚拟dom对象,容器,(回调函数,dom渲染完成后执行,可以不传))

  • 这面有两个值,第一个值是XML,第二个值是插入地点
  • 第一个值跟template一样只能有一个根节点
    • 可以写成<></>空节点不会增加多余的结构,叫做fragment
  • 在第一个值里面可以直接写{}来达到模板字符串效果
    • es6模板字符串的区别之处在于数组不用去,了,jsx默认把数组的连接符,去掉
    • 不允许直接嵌入对象,普通对象不是一个合法的jsx元素,但是有些情况是可以使用对象的,例如style,和标准的jsx对象
    • 循环处理的jsx元素需要设置一个key值,唯一,只相对于当前循环唯一即可
  • 样式处理
    • 要把原生的class改成className
    • 行内样式style的值要写成{{包起来}},意思就是{里面包个对象,这个对象用来写样式}
    • 可以使用createElement创建一个dom元素
let title = '标题1';
let ary = [1, 2,];
let n=5;
let data = [{
  num: '01',
  text: 'REACT全家桶'
}, {
  num: '02',
  text: 'VUE全家桶'
}];
ReactDOM.render(
  <>
  <div className="box" style={{ color: 'green' }}>
    <h2 className="title">{title}</h2>
    {ary}
    <ul className="con">
      {data.map(item => {
        return <li className="item" key={item.num}>
                <em>{item.num}</em>
                  {item.text}
                </li>
      })}
    </ul>
  </div>
  <div>123</div>
  {React.createElement('h'+n,null,'哈哈哈哈')}
  </>
  ,
  document.getElementById('root')
);

jsx与template的区别

  • jsx比template更灵活
SRC中index.js或index.jsx解析
  • import React from 'react';react核心模板,语法mvc
  • import ReactDOM from 'react-dom';提供一个render方法,负责把jsx元素渲染到页面中
安装了一个babel语法包:babel-preset-react-app,用来解析js/jsx内容的
  1. 基于babel-preset-react-app语法包,把jsx代码编译为React.createElement()这种格式,只要文件中出现jsx代码,则必须在文件中提前导入React,(新版本浏览器可能做处理了,不用必须导入)
  2. 默认会把createElement执行,并且传递一到多个实参,
    • 第一个实参type,后期要创建的元素标签名或者组件,必传
    • 第二个实参props 属性对象,包含当前元素标签上设置的各个属性及属性值,要设置成对象,如果不设置任何值就是null,必传
    • 第三个及以后的所有实参 children 都是当前元素的所有子节点,如果一个都没有,就不用传,如果有几个就传几个,如是文本节点,直接传递内容,如果是元素内容就要React.createElement(),把执行的返回值作为参数传递进来
React.createElement()包含什么
  • 返回的是一个对象,里面存储了当前创建节点的信息,我们把这个对象称为jsx元素对象或者专业一点叫做虚拟DOM对象
  • $$typeof:Symbol(react.element)
  • key:null
  • ref:null
  • type:"h1",存储的是createElement传递的第一个参数
  • props:{}是一个对象,里面有行间属性和值,还有一个children存储的是子节点,和内容,如果有多个就是个数组,如果只有一个就是jsx返回的对象或字符串

框架的核心概念,把虚拟DOM(包含DOM信息的对象)变为真实DOM(页面呈现的DOM)

  • 我们操作MOdule层后,框架内部会根据最新的数据,生成一个最新的虚拟DOM,拿到最新的虚拟DOM对象,和上一次生成的虚拟DOM对象做对比,称为DOM-DIFF,把不一样的地方在真实DOM中重新渲染,第一次没有对比,直接将虚拟DOM渲染
  • Vue里面的虚拟DOM对象是vue实例上的_vnode

setState方法

  • 每一次setState更改数据渲染视图会经过:shouldComponentUpdate->componentWillUpdate->[更改状态]->render->componentDidUpdate
  • setState在周期函数以及合成事件中都是异步操作,触发setState执行,没有立即去进行更新的逻辑,此时的状态还没有被修改shouldComponent->componentWillUpdate->[更改状态]->render->componentDidUpdate,先把下面的任务执行完成,再去执行更新的逻辑,这样可以保证周期函数执行顺序的正确
  • 在异步操作的函数中,执行setState,他执行的就是同步的逻辑,例如在定时器里
  • 支持第二个参数,callback,在当前更新任务执行完后触发,比componentDidUpdate还要晚一些,此时肯定重新渲染完成了

封装React.createElement与ReactDOM.render

import React from 'react';//react核心模板,语法mvc
import ReactDOM from 'react-dom';//提供一个render方法,负责把jsx元素渲染到页面中
//封装的for in
const each = function each(obj, callback) {
  if (obj == null || typeof obj !== "object") throw new TypeError('obj must be an object!');
  let keys = Object.keys(obj),
    key,
    i = 0;
  //react里不允许三元运算符中成功或失败不执行东西,null不行,会报错
  if (typeof Symbol !== "undefined") keys = keys.concat(Object.getOwnPropertySymbols(obj))
  for (; i < keys.length; i++) {
    key = keys[i];
    if (typeof callback === "function") callback(obj[key], key)
  }
};
//生成一个jsx虚拟DOM对象
React.createElement = function (type, props, ...children) {
  let len = children.length, obj = { type, props: {}, key: null, ref: null };
  //处理传递进来的属性
  if (props !== null && typeof props === "object") {
    each(props, (value, key) => {
      if (/^(key|ref)$/.test(key)) {
        obj[key] = value;
        return
      }
      obj.props[key] = props[value];
    })
  }
  //处理子节点
  if (len > 0) {
    obj.props.children = len === 1 ? children[0] : children;
  }
  return obj;
}
//把虚拟DOM对象变为真实DOM
ReactDOM.render = function rander (obj, container, callback) {
  let { type, props } = obj, element;
  //核心思想,动态创建制定类型的元素,插入到指定容器中
  if (typeof type == "string") {
    element = document.createElement(type);
    each(props, (value, key) => {
      if (key === 'className') {
        element.setAttribute('class', value)
        return
      }
      if (key === 'style') {
        //style是对象,要分别设置
        each(value, (item, attr) => {
          element.style[attr] = item
        })
        return;
      }
      if (key === 'children') {
        //肯定有子节点,value:children属性值 可能是一项/数组
        if (!Array.isArray(value)) value = [value];//如果是一项,将他变为数组
        value.forEach((item, index) => {
          //item是每个子节点
          //如果是文本节点,直接创建一个文本节点,插入到element里
          //如果是元素节点,新的jsx对象,我们需要把它创建成一个元素标签插入到element里
          if (typeof item === 'string') {
            let textNode = document.createTextNode(item);
            element.appendChild(textNode);
            return
          }
          //递归
          rander(item,element)
        })
        return;
      }
      element.setAttribute(key, value)
    })
    container.appendChild(element);
    if (typeof callback === "function") callback();
    return;
  }
  //如果type是组件有不同的方案
}

ReactDOM.render(
  React.createElement("h1", {
    id: "title"
  }, "\u54C8\u54C8\u54C8")
  ,
  document.getElementById('root')
);

组件

类型

  • 函数式组件
    • 函数式组件,创建一个组件,返回一个jsx即可
  • 类组件
  • HOOKS组件,函数式组件中使用HOOKS函数

组件的使用

  • 每创建一个.jsx就相当于创建了一个组件,这里只能写js{包含视图的jsx},需要导出,样式是单独写在一个文件中,在组件中导入进来即可
import React from 'react'

import './index.less';//导入样式
  • 组件的使用,需要先导入,命名上没有什么约束可以使用单闭合,也可以使用双闭合
  • 双闭合标签可以在中间写一些节点,在组件内部可以判断children是否存在存在就将他展现出来,实现Vue中slot插槽效果
  • 传递进来的属性是只读的,改了就报错
  • 为了在组件中区分传递过来的子节点,就在行间传递个name,来判断
  • React.Children.forEach(children,item=>{})专门用来遍历children的,传第一个值是children,第二个值是回调,好处是忽略值的类型
  • this.setState({})里的值如果是一个数组,只要他空间地址不改变,那么走了render但是视图里面没有刷新,解决办法:在视图里[...参数],或传递的时候换一个空间地址就会刷新视图,
  • 如果用PureComponent那么数据数组更改的时候,如果没有换空间地址就不执行周期函数,原理:它内部设置了一个shouldComponentUpdate会进行浅比较,如果不同才会重新渲染,引用数据类型浅比较比的是空间地址,如果手动设置了shouldComponentUpdate,16版本则会以用户自己的为主,17版本用了会报错,而且不以自己为主
函数式组件的调用的底层原理
  1. 也要基于babel-preset-react-app把JSX编译为React.createElement()格式,最后得到的虚拟DOM对象(JSX对象)
  2. 当我们去基于ReactDOM.render渲染的时候,如果type是函数,则会把这个函数Clock执行,并且把props中所有的值传递给这个函数,函数内部可以定义一个props接收,这就是传说中的属性,然后函数执行返回一个新的jsx对象,如果不写return值,默认返回undefined,(undefined/null)不会报错,代表空元素
  3. render方法把返回的虚拟DOM对象最后进行渲染即可
  4. 函数式组件是静态组件,第一次调用,渲染出来的页面是什么样,以后就是啥样,里面呈现的内容是无法去修改和渲染了,除非重新调用和渲染这个组件
类组件
  1. 创建一个类,让其继承React.Component/PureComponent,则当前类变为一个类组件
  2. 每一次调用这个组件都是创造这个类的一个实例
  3. 必须包含一个render函数,并且函数返回的是一个jsx,当我们new Clock,内部会执行Clock.prototype.render
  4. 每一次调用类组件,都会把props属性传递进来,可以在constructor获取到,当constructor执行完成后React内部会把props直接挂载到实例的私有属性上,所以在除了constructor函数之外的所有函数都可以获取到this.props,且只能读取
  5. 如果在constructor中也想访问this.props,就手动加一个this.props=props;可以简写为super(props)
    • 公有方法:自己写的一些方法,
    • 强制要求内置方法:render、周期函数、
    • 继承过来的方法:setState(partialState,callback)/forceUpdate(callback)
  6. 这里的状态是state,在new的时候会初始化状态,我们要把后期使用的状态写好
    • 状态可以被更改,但是直接更改不会渲染视图,我们一般使用setState({要修改的状态和值})来修改状态,这样这个方法会在修改数据以后,渲染视图
class Clock extends React.Component {
    constructor(props) {
        super();//super(props)===React.Component.call(this)===this.props=props
        //初始化状态,后期需要用到的状态,都在这里初始化一下
        this.state = {
            time: new Date().toLocaleString()
        }
    }
    render() {
        let props = this.props;
        // let time = new Date().toLocaleString();
        return <div className="clock-box">
            <h1>今天的日期</h1>
            <p>{this.state.time}</p>
            {props.children}
        </div>
    }
    //第一次渲染完的周期函数
    componentDidMount() {
        //设置一个定时器,每隔1000ms重新更改状态,只要重新更改,那就重新渲染视图,这就是MVC
        setInterval(() => {
            //this.state.time = new Date().toLocaleString()//这样修改无效
            this.setState({time : new Date().toLocaleString()})//需要进行setState处理数据,然后通知视图渲染
        }, 1000)
    }
}
Hooks组件
  • 在函数式组件中使用React提供的Hooks函数,实现类似于类组件的状态管理以及一些生命周期的处理=>Hooks组件
  • useState 在函数式组件中使用状态
  • useRef 在函数式组件中使用ref
  • useEffect 在函数式组件中使用周期函数
  • useContext 在函数式组件中使用上下文
  • useCallback 在函数式组件中使用回调函数
useState
  • 作用:在函数中使用状态
  • 使用方法 let [num, changeNum] = useState(initialValue)
  • initialValue是初始值
  • num是状态名
  • changeNum是修改状态的方法
  • 每一次执行changeNum都会改变状态,然后把Demo方法重新执行实现重新渲染
  • 方法重新执行的时候,重新执行useState的时候,内部会做一个处理,如果当前状态有值,不是第一次执行的,则用最新的值如果没有就是我们赋的初始值
  • 如果初始值写成对象不支持部分更改,如果改变单个的话会把没有改变的丢掉
  • 如果初始值是一个函数,会默认先把函数执行函数执行的结果当做初始值给这个状态,而后Dome再执行重新渲染的时候,这个函数就不会再做任何处理
//可以写成
export default function Demo() {
    let [num, changeNum] = useState(0);
    let [x, changeX] = useState(0);
    return <div>
        {num}
        <button onClick={ev => { changeNum(num + 1) }}>点我啊</button>
        {x}
        <button onClick={ev => { changeX(x + 10) }}>点我啊</button>
    </div>
}

export default function Demo() {
    let [state, setState] = useState({
        num: 0,
        x: 0
    })
    return <div>
        {state.num}
        <button onClick={ev => { setState({...state,num:state.num+1}) }}>点我啊</button>
    </div>
}
useRef
  • 添加一个变量,可以写在行间,可以用useEffect获取到
let btnX = useRef();
<button ref={btnX}>点我啊</button>
useEffect
  • 类似于componentDidMount & componentDidUpdate
  • 获取到ref
  • 视图在第一次渲染触发,且每次更新都会触发
  • current基于这个利用成员访问获取到dom元素
  • 第一个值是一个回调函数第二个值可以不传,如果传了就不具备每一次更新都会触发的特点了,只有在第一次渲染触发
  • 他的第二个值[]其实里面是放依赖项,意思是当依赖项改变时才会触发回调,当我们为空数组时候,他没有可以依赖的,所以达到不触发
  • 第一个参数A函数还可以return出一个B函数,第二个参数不传,第一次执行A不执行B,而后每次重新渲染都会先执行B,再执行A,因为B是在组件销毁时候执行,他里面拿的还是上次的状态信息,A里面拿的是最新的状态信息
let [num, changeNum] = useState(0);

//第一次渲染,每次更新视图都触发
useEffect(() => {
        console.log(btnX.current)
    })
//第一次渲染触发,以后视图更新不触发
useEffect(() => {
        console.log(btnX)
    }[])
//第一次渲染触发,以后依赖项num改变才执行回调
useEffect(() => {
        console.log(btnX)
    }[num])
// 第一次不执行内部,函数组件重新渲染会销毁老的组件,销毁的时候会执行return的小函数,然后渲染完成重新执行外边的大函数
useEffect(() => {
        console.log('外OK')
        return () => {
            console.log('内OK')
        }
    })
组件通信
基于属性传递,适用于父子组件
  • 父组件调用子组件的时候,基于属性的方式,把信息传递给子组件
  • 默认是单向数据流
  • 但是我们可以基于回调函数的方式实现子组件更改父组件的内容
  • 实施方案:父组件基于属性把自己的一个方法传递给子组件,子组件中获取这个方法,并且把其执行,从而实现修改父组件的信息
设置共同祖先组件
  • 执行上下文适用于具备共同祖先组件,页面最外层包一层壳子,这个页面可以作为所有组件的祖先元素
  • 祖先元素内部,把后期元素需要的信息,全部挂载到上下文中
  • 在后代元素内部,如果需要用到哪些信息,就直接注册一下,这样就可以使用了,信息可以为属性、状态、函数
  • 传统实现上下文挂载的方案要求祖先元素只能是类组件(15版本),因为用到了周期函数getChildContext,看1.1
  • 利用一个新的js来存放状态,看1.2
1.1老版本必须使用类组件
  • getChildContext这个方法return出一个对象,这个对象就是挂载的信息,内容自定义,这个周期函数会在第一次页面加载以及后期每一次重新渲染都会执行
  • 组先级别设置的时候要先childContextTypes={属性名:PropTypes.类型}限制类型,然后getChildContext(){return出一个对象}
  • 使用的时候子孙级必须要注册且contextTypes中在配合PropTypes限制类型
  • 子孙级组件是函数组件就有,函数就有第二个参数,context,这里面可以拿到上下文的信息,然后在函数外面,函数名.contextTypes={变量名:PropTypes.类型}限制即可使用
  • 子孙是calss类的话,直接static contextTypes={变量名:PropTypes.类型}限制即可使用
//父组件
static childContextTypes = {
        title: PropTypes.string,
        supNum: PropTypes.number,
        oppNum: PropTypes.number,
        handle: PropTypes.func

    }
//2.向上下文中挂载信息
//getChildContext这个方法return出一个对象,这个对象就是挂载的信息,内容自定义,这个周期函数会在第一次页面加载以及后期每一次重新渲染都会执行
getChildContext() {
        let { title } = this.props,
            { supNum, oppNum } = this.state,
            handle = this.handle;
        console.log(title)
        return {
            title, supNum, oppNum, handle
        }
    }
 
 //函数式组件接受第二个参数context,里面存储的是上下文信息,但必须要注册且限制类型
 const VoteHeader = function VoteHeader(props, context) {
    //--在函数组件内部基于第二个参数context即可获取上下文信息
    let { title, supNum, oppNum } = context
    return <header className="header-box">
        <h2 className="title">{title}</h2>
        <span className="total">{supNum + oppNum}</span>
    </header>
}

//--注册使用
VoteHeader.contextTypes = {
    title: PropTypes.string.isRequired,
    supNum: PropTypes.number,
    oppNum: PropTypes.number
}
//类组件中直接用static contextTypes={}来获取
static contextTypes = {
        supNum: PropTypes.number,
        oppNum: PropTypes.number,
        title: PropTypes.string
    }
1.2利用一个新的js来存放状态
  • 创建一个js文件,自定义文件名,例如文件名为ThemContext.js,在里面利用React.createContext()创建一个上下文对象,然后在哪里用就先导入
  • 在组级组件中,先导入ThemContext,然后return出的jsx用<ThemContext.Provider value={}></ThemContext.Provider>包起来,在value中传一个对象,这个对象就是我们要用的状态,注意:ThemContext不是固定的
  • 使用的话分3种情况,不过都要先导入ThemContext.js
    • 函数子组件中使用useContext(ThemContext),然后就可以拿到全部状态,按需解构使用即可
    • 在class类子组件中使用static contextType = ThemContext;添加静态属性,然后调用的时候使用this.context就可以拿到所有的状态,按需解构即可
    • 在函数子组件中也可以使用ThemContext.Consumer消费上下文,具体就是在return的时候用<ThemContext.Consumer>{context=>{这里面的context也可以拿到所有的状态,在这里return出jsx}}</ThemContext.Consumer>
//ThemContext.js内容

import React from 'react';
// 创建一个上下文对象
const ThemContext = React.createContext();
export default ThemContext;

//祖先级组件
render() {
        // 在祖先祖先中放置上下文信息ThemContext.Provider
        let { title } = this.props,
            { supNum, oppNum } = this.state;
        return <ThemContext.Provider value={{
            title,
            supNum,
            oppNum,
            handle: this.handle
        }}>
            <div className="vote-box">
                <VoteHeader />
                <VoteMain />
                <VoteFooter />
            </div>
        </ThemContext.Provider>;
    }
//函数中使用useContext方法

import ThemContext from '../ThemContext';
const VoteFooter = function VoteFooter(props) {
    // 我们可以基于Hook函数,让函数式组件支持新版本ContentAPI中的上下文使用
    const context = useContext(ThemContext);
    let { handle } = context;
    return <footer className="footer-box">
        <button onClick={handle.bind(null, 'sup')}>支持</button>
    </footer>;
};

//使用ThemContext.Consumer消费上下文方法
import ThemContext from '../ThemContext';

const VoteHeader = function VoteHeader(props) {
    // 也可以使用ThemContext.Consumer消费上下文
    return <ThemContext.Consumer>
        {context => {
            let { title, supNum, oppNum } = context;
            return <header className="header-box">
                <h2 className="title">{title}</h2>
                <span className="total">{supNum + oppNum}</span>
            </header>;
        }}
    </ThemContext.Consumer>;
};

//类组件使用静态资源导入的方法
import ThemContext from '../ThemContext';

export default class VoteMain extends React.Component {
    // 在后代组件中使用上下文对象「祖先中放置的上下文信息,在后代中都可以使用」
    static contextType = ThemContext;

    render() {
        let { supNum, oppNum } = this.context;
        return <main className="main-box">
            <p>支持人数:{supNum}</p>
            <p>反对人数:{oppNum}</p>
        </main>;
    }
};

事件

  • 还是在行间传递
  • 执行时候与原生一致,也就是如果加上括号就直接执行,而不是如Vue里一样触发才执行
  • 事件执行时被执行的函数里面this为undefined
  • 但是可以使用bind
  • 有事件对象
  • 还有一种方法就是直接写成箭头函数,那他的this就是上级作用域了,这样就不用写bind了,但是因为是=()=>{},所以是添加在类的实例上的
  • React中直接在jsx中基于onXXX处理的事件是合成事件"SyntheticBaseEvent",ev是合成事件对象,鼠标事件有的东西他也有
  • 合成事件原理是事件委托,委托给document
  • React打造的是跨平台的框架,基于事件委托,我们可以把事件对象/事件兼容问题统一处理了;例如:在移动端click事件行为有300ms延迟,Vue需要引入插件解决,但是React自动识别设备,然后自动去掉300ms的延迟
    state = { n: 0 };
    render() {
        let { n } = this.state;
        return <div className="box">
            <span>{n}</span>
            <button onClick={this.handle.bind(this,123)}>点我</button>
        </div>
    };
    handle(e) {
        let { n } = this.state;
        n++;
        this.setState({ n })
        console.log(this, e);//undefined
    }
    //    handle=(e)=>{
    //    this.setState({ n:this.state.n+1 })
    //}

ref

  • 使用的时候在行间添加ref="名字"
  • 获取,在函数内用this.refs.名字获取,但是不支持这样写
  • 建议使用函数式编程,基于ref获取元素,我们把获取到的DOM元素挂载到实例的私有属性上,以后可以让其他方法基于实例访问到
 render() {
        let { n } = this.state;
        return <div className="box">
            <span ref="spanBox">0</span>
            <button ref={x=>{this.button=x}}
            >点我</button>
        </div>
    };
    componentDidMount(){
        this.button.addEventListener('click',()=>{
            this.refs.spanBox.innerHTML++;
        })
    }

传参校验

  • 调用组件传递进来的属性是只读的,不允许组件内部直接修改属性,但是我们可以把属性值赋值给别的状态或者变量,以后修改别的变量或者状态是可以的,虽然不可以更改,但是我们可以设置一些校验规则,类型、默认值、是否必传等
  • React中属性校验是非必需的,我们想用的时候直接this.props.属性就可以调用了,但是想要进行校验就需要安装导入prop-types
  • 使用方法:static 方法={}
export default class Demo extends React.Component {
    state = { n: 0 };
    static defaultProps = {//设置默认值
        n:0,
        m:0
    }
    static propTypes={//设置数据类型
        n:PropTypes.number,
        m:PropTypes.number,//数据类型
        title:PropTypes.any.isRequired//任意属性类型,且必传
    }
    }

生命周期

UNSAFE_componentWillMount()第一次渲染之前

我们可以在这里发http数据请求[异步],紧接着视图渲染,当视图渲染完,数据也请求回来之后,我们通过修改状态,让视图按照最新请求重新渲染,但是React官网已经不建议我们这样使用了,我们一般都是在componentDidMount中最这个处理

componentDidMount()

可以获取到真实的DOM元素了

shouldComponentUpdate(nextProps, nextState)
  • 重新渲染
  • 如果return true重新渲染,如果return false就到此停止,不允许重新渲染
  • nextProps/nextState即将要修改的属性和状态值,最新的,但是此时还没有更改呢
  • 在这里我们会做一些优化,自己控制哪些状态和属性改变,才会重新渲染
UNFASE_componentWillUpdate()

数据更改之前,这时候最新的状态在这里还没有更改

componentDidUpdate()

数据更改之后

render

组件第一次渲染/重新渲染时候

UNFASE_componentWillReceiveProps()

传递进来的属性发生改变,父组件重新调用这个组件

componentWillUnmount()

卸载组件之前

getChildContext
  • 这个方法return出一个对象,这个对象就是挂载上下文的信息,内容自定义,这个周期函数会在第一次页面加载以及后期每一次重新渲染都会执行,作用是供后代使用的
组件生命周期执行顺序
  • 打比方:A是父组件,B是子组件 A组件调用了子组件
  • A组件第一次渲染,A.WilllMount->A.render->B.WillMount->B.render->B.DidMount->->A.DidMount
  • A组件重新渲染:A.should->A.WillUpdate->A.render->B.WillReceiveProps->B.should->B.WillUpdate->B.render->B.DidUpdate->A.DidUpdate