React-基础

React:地址------个人感觉 ,是最好的React教程之一,对新手友好。

1.React_hello

  • index.html
  <!-- 准备好一个容器 -->
  <div id="container"></div>

  <!-- 引入react核心库 -->
  <script src="./js/react.development.js" crossorigin></script>

  <!-- 引入react-dom,用于支持react操作DOM -->
  <script src="./js/react-dom.development.js" crossorigin></script>

  <!-- 引入babel,用于将jsx转为js -->
  <script src="./js/babel.min.js"></script>

  <!-- 引入prop-types,用于对组件标签属性进行限制 -->
  <script src="https://cdn.bootcdn.net/ajax/libs/prop-types/15.7.2/prop-types.js"></script>

  <!-- 此处一定要写type="text/babel" 在这里编写js的逻辑 -->
  <script type="text/babel" src="./src/index.jsx"></script>
  • index.jsx
    在这里插入图片描述

2.虚拟DOM的两种创建方式及优缺点

2.1虚拟DOM的两种创建方式

  • 虚拟DOM的创建方式一
    在这里插入图片描述
  • 虚拟DOM的创建方式二
    在这里插入图片描述

2.2两种方式的优缺点

  • 方式一的创建DOM特点:结构简单/清晰明了可以使用{}读取变量,但是只能写在.jsx文件中
  • 方式二的创建DOM特点:结构复杂无法使用{}读取变量,可以写在.js/.jsx文件中
  • 总结:直接使用方法一,方法二过于复杂

3.虚拟DOM和真实DOM的对比

  • 代码
    在这里插入图片描述
  • 显示
    在这里插入图片描述
  • 总结
/**
 * 关于虚拟DOM
 *    本质就是Object类型的对象(一般对象)
 *    虚拟DOM比较‘轻’,真实DOM比较‘重’,因为虚拟DOM是React内部再用,无需真实DOM上那么多的属性
 *    虚拟DOM最终会被React转换成真实DOM,呈现在页面上
 */

4.jsx的语法规则

/**
 * jsx语法规则
 *  1.定义虚拟DOM时,不要写引号
 *  2.样式中混入js表达式的时候要用{}
 *  3.样式中类名指定不要用class,要用className
 *  4.内联样式,要用style={
    
    {key:value,key1:value1,...}}
 *  5.虚拟DOM必须只有一个跟标签
 *  6.标签必须闭合
 *  7.标签首字母
 *    7.1小写字母开头,则该标签转为HTML同名元素,若HTML中无该标签对应的同名元素则报错
 *    7.2大写字母开头,react就去渲染对应的组件,若组件没有定义,则报错
 *  8.{}中能自动遍历由简单的数据类型构成的数组
 */

/**
 * 一定要注意区分:【js语句(代码)】与【js表达式】
 *  1.一个表达式会产生一个值,可以放在任何一个需要值的地方==>a/a+b/a===b/demo(1)/arr.map()/function test(){}
 *  2.语句(代码):下面这些都是语句/代码 if(){}/for(){}/switch(){case:xxx}
 */

5.组件/组件化

5.1组件

  • 理解:用来实现局部功能效果的代码和资源的集合(html/css/js/image/video等)
  • 作用:复用编码,简化项目编码,提高运行效率

5.2组件化

  • 当应用是以多组件的方式实现,这个应用就是一个组件化的应用

6.函数式组件/类式组件

6.1函数式组件

function FuncMyComponent() {
    
    
  console.log(this);//此处的this是undefined,因为babel编译开启了严格模式
  return (
    <div className="wrap">
      <span>函数式组件</span>
    </div>
  )
}

/**
 * 注意点:函数式组件 首字母必须大写/必须有返回值
 * 执行了ReactDOM.render(<FuncMyComponent />...之后发生了什么?
 *  1.react解析了组件标签,找到了FuncMyComponent组件
 *  2.发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转为真实DOM,随后呈现在页面中
 */
ReactDOM.render(<FuncMyComponent />, document.querySelector('#container'));

6.2类式组件

/**
* 用类定义组件
*  1.类继承React.Component
*  2.类名的首字母大写
*  3.ReactDOM.render(<ClassMyComponent />...
*    3.1React解析组件标签,找到了ClassMyComponent组件
*    3.2发现组件是使用类定义的,随后new出来该类的实例并通过该实例调用原型上的render方法,将render返回的虚拟DOM转成真实DOM,随后呈现在页面中
*/

class MyComponent extends React.Component {
    
    
  render() {
    
    
    // render是放在哪里的?------类的原型对象上,供类的实例对象使用
    // render中的this是谁?------类的实例对象/组件实例对象
    return (
      <div className="wrap">
        <span>欢迎使用react</span>
      </div>
    )
  }
}

ReactDOM.render(<MyComponent />, document.querySelector('#container'))

7.组件实例的三大核心属性-state

7.1初始化state

class MyComponent extends React.Component {
    
    
  constructor(props) {
    
    
    // 构造器中的this就是MyComponent的实例对象
    super(props);
    this.state = {
    
    //初始化state state必须是一个对象
      msg: '欢迎使用react',
      bool: true
    };
  }
  render() {
    
    
    let {
    
     bool } = this.state;
    let wather = null;
    if (bool) {
    
    
      wather = '凉爽';
    } else {
    
    
      wather = '炎热';
    }
    return (
      <div className="wrap">
        <span>欢迎使用react</span>
        <hr />
        <span>今天天气很{
    
    wather}</span>
      </div>
    )
  }
}
ReactDOM.render(<MyComponent />, document.querySelector('#container'))

7.2React中的事件

class MyComponent extends React.Component {
    
    
  //构造器调用几次?------一次
  constructor(props) {
    
    
    // 构造器中的this就是MyComponent的实例对象
    super(props);
    this.state = {
    
    
      msg: '欢迎使用react',
      bool: true
    };
    //this.changeBool.bind(this)==>找到MyComponent原型上的changeBool方法,通过bind的方式将this指向MyComponent的实例对象,并挂载到MyComponent类的自身上
    this.changeBool = this.changeBool.bind(this);
  }
  
  //changeBool调用几次? 触发几次调用几次
  changeBool() {
    
    
    // changeBool放在哪里? ------ MyComponent的原型对象上,供实例使用
    // 由于changeBool是作为onClick的回调,所以不是通过实例调用的,是直接调用,又因为类中的方法默认开启了局部的严格模式,所以 changeBool中的this为undefined

    let {
    
     bool } = this.state;
    // 警告:状态(state)不可以直接更改,需要借助一个内置的API去更改==>setState
    // bool = !bool;//这是错误的写法
    this.setState({
    
    //正确写法 这里面的更新是替换是一种合并,而非替换
      bool: !bool
    })
  }
  
  //render调用几次?1+n次 n就是状态更新的次数
  render() {
    
    //在render中使用if条件语句
    let {
    
     bool } = this.state;
    let wather = null;
    if (bool) {
    
    
      wather = '凉爽';
    } else {
    
    
      wather = '炎热';
    }
    return (
      <div className="wrap">
        <span>欢迎使用react</span>
        <hr />
        <span onClick={
    
    this.changeBool}>今天天气很{
    
    wather}</span>
      </div>
    )
  }
  
}
ReactDOM.render(<MyComponent />, document.querySelector('#container'))

7.3state的简写方式

class MyComponent extends React.Component {
    
    
  state = {
    
    //给类的自身添加state属性
    msg: '欢迎使用react',
    bool: true
  }
  changeBool = () => {
    
    //给类的自身添加方法 ==>赋值语句+箭头函数
    let {
    
     bool } = this.state;
    this.setState({
    
    
      bool: !bool
    })
  }
  render() {
    
    
    console.log(this);
    let {
    
     bool } = this.state;
    let wather = null;
    if (bool) {
    
    
      wather = '凉爽';
    } else {
    
    
      wather = '炎热';
    }
    return (
      <div className="wrap">
        <span>欢迎使用react</span>
        <hr />
        <span onClick={
    
    this.changeBool}>今天天气很{
    
    wather}</span>
      </div>
    )
  }
}

ReactDOM.render(<MyComponent />, document.querySelector('#container'))

8.组件实例的三大核心属性-props

8.1props的基本使用

class FatherCom extends React.Component {
    
    
  state = {
    
    
    msg: '这里是父亲组件'
  }
  changePropsVal = () => {
    
    
    let {
    
     key0, key1, key2, str, bool, num, obj, arr1, arr, arrObj, obj1 } = this.props;
    console.log(str);
    // 注意点 props的值是只读属性,不能修改
    this.props.str = '修改str';//这里就会报错,
  }
  render() {
    
    
    console.log(this.props);
    let {
    
     key0, key1, key2, str, bool, num, obj, arr1, arr, arrObj, obj1 } = this.props;
    console.log(key0, key1, key2, str, bool, num, obj, arr1, arr, arrObj, obj1);
    let {
    
     msg } = this.state;
    return (
      <div className="father">
        {
    
    msg}
        <hr />
        <button onClick={
    
    this.changePropsVal}>修改props的值</button>
      </div>
    )
  }
}
let obj = {
    
    
  'key0': '对象',
  'key1': '对象1',
  'key2': '对象2'
};
let arr = [1, 2, 3];

let arrObj = [
  {
    
     'key': '对象1' },
  {
    
     'key': '对象2' },
  {
    
     'key': '对象3' }
];

ReactDOM.render(<FatherCom {
    
    ...obj} str='字符串' bool={
    
    true} num={
    
    996} obj={
    
    obj} arr1={
    
    [1, 1, 1]} arr={
    
    arr} arrObj={
    
    arrObj} obj1={
    
    {
    
     age: 26 }} />, document.querySelector('#container'));

8.2对props进行限制(类型/必要性/默认值)

//文档地址:https://react.docschina.org/docs/typechecking-with-proptypes.html
class FatherCom extends React.Component {
    
    
  state = {
    
    
    msg: '这里是父亲组件'
  }
  static propTypes = {
    
    //限制标签属性类型以及是否必填
    name: PropTypes.string.isRequired,//姓名的类型为字符串并且必填
    age: PropTypes.number//年龄的类型为数字非必填
  }
  static defaultProps = {
    
    //给标签属性设置默认的值
    sex: '男'//性别的默认值为男
  }
  render() {
    
    
    console.log(this.props);
    let {
    
     name, age, sex, key0, key1, key2, str, bool, num, obj, arr1, arr, arrObj, obj1 } = this.props;
    console.log(name, age, sex, key0, key1, key2, str, bool, num, obj, arr1, arr, arrObj, obj1);
    let {
    
     msg } = this.state;
    return (
      <div className="father">
        {
    
    msg}
        <hr />
      </div>
    )
  }
}
let obj = {
    
    
  'key0': '对象',
  'key1': '对象1',
  'key2': '对象2'
};
let arr = [1, 2, 3];

let arrObj = [
  {
    
     'key': '对象1' },
  {
    
     'key': '对象2' },
  {
    
     'key': '对象3' }
];

ReactDOM.render(<FatherCom {
    
    ...obj} name='张三' age={
    
    18} sex='女' str=' 字符串' bool={
    
    true} num={
    
    996} obj={
    
    obj} arr1={
    
    [1, 1, 1]} arr={
    
    arr} arrObj={
    
    arrObj} obj1={
    
    {
    
     age: 26 }} />, documen

8.3类的构造器(constructor)props

class FatherCom extends React.Component {
    
    
  constructor(props) {
    
    //接了props传给super this.props==>正常
    // 构造器是否接受props,是否传递给super,取决于是否希望在构造器中通过this访问props
    console.log(props);//{key0: "对象", key1: "对象1", key2: "对象2"}
    super(props);
    console.log('this.props', this.props);//{key0: "对象", key1: "对象1", key2: "对象2"}
  }
  // constructor() {//不接props不传给super this.props==>undefined
  //   super();
  //   console.log('this.props', this.props);//undefined
  // }
  state = {
    
    
    msg: '这里是父亲组件'
  }
  render() {
    
    
    console.log(this.props);
    let {
    
     key0, key1, key2 } = this.props;
    console.log(key0, key1, key2);
    let {
    
     msg } = this.state;
    return (
      <div className="father">
        {
    
    msg}
        <hr />
      </div>
    )
  }
}
let obj = {
    
    
  'key0': '对象',
  'key1': '对象1',
  'key2': '对象2'
};


ReactDOM.render(<FatherCom {
    
    ...obj} />, document.querySelector('#container'));

8.3函数组件使用props

function FatherCom(props) {
    
    
  let {
    
     key0, key1, key2, name, age, sex } = props;
  return (
    <ul>
      <li>{
    
    key0}</li>
      <li>{
    
    key1}</li>
      <li>{
    
    key2}</li>
      <li>{
    
    name}</li>
      <li>{
    
    age}</li>
      <li>{
    
    sex}</li>
    </ul>
  )
}
FatherCom.propTypes = {
    
    //限制标签属性类型以及是否必填
  name: PropTypes.string.isRequired,
  age: PropTypes.number
}
FatherCom.defaultProps = {
    
    //给标签属性设置默认的值
  sex: '女'
}

let obj = {
    
    
  'key0': '对象',
  'key1': '对象1',
  'key2': '对象2'
};
ReactDOM.render(<FatherCom name='张三' age={
    
    18} sex='男' {
    
    ...obj} />, document.querySelector('#container'));

9.组件实例的三大核心属性-refs

9.1refs-字符串形式(不推荐:会有效率上的问题)

class ClassRefsCom extends React.Component {
    
    
  state = {
    
    
    msg: '换用使用react'
  }
  tipInputValue = (event) => {
    
    
    console.log(event, this.refs);
    let {
    
     firstInput } = this.refs;
    console.log(firstInput.value);
  }
  getBlurInput = (event) => {
    
    
    console.log(event.target.value);
  }
  render() {
    
    
    let {
    
     msg } = this.state;
    return (
      <div className="refs">
        {
    
    msg}
        <hr />
        <input ref='firstInput' type="text" placeholder="点击按钮提示数据" />
        <button onClick={
    
    this.tipInputValue}>提示输入数据</button>
        <input ref='secondInput' type="text" onBlur={
    
    this.getBlurInput} placeholder="失去焦点提示数据" />
      </div>
    )
  }
}

ReactDOM.render(<ClassRefsCom />, document.querySelector('#container'))

9.2refs-回调函数形式(推荐)

class ClassRefsCom extends React.Component {
    
    
  state = {
    
    
    msg: '换用使用react',
    bool: true
  }
  changeWeather = () => {
    
    
    let {
    
     msg, bool } = this.state;
    this.setState({
    
    
      bool: !bool
    })
  }
  tipInputValue = (event) => {
    
    //按钮触发事件-内联绑定方式-推荐
    console.log(event, this);
    let {
    
     firstInput, secondInput } = this;
    console.log(firstInput, secondInput, firstInput.value);
  }
  getBlurInput = (event) => {
    
    //输入框失去焦点事件
    console.log(event.target.value);
  }
  saveInput = (currentNode) => {
    
    //ref-绑定函数方式
    console.log(currentNode);
    this.threeInput = currentNode;
    console.log(this);
  }
  render() {
    
    
    let {
    
     msg, bool } = this.state;
    let weather = null;
    if (bool) {
    
    
      weather = '冷'
    } else {
    
    
      weather = '热'
    }
    return (
      <div className="refs">
        {
    
    msg}
        <hr />
        {
    
    /* 绑定的第一种方式-内联绑定方式 */}
        <input ref={
    
    
          (currentNode) => {
    
    //这个函数会自动执行,将[currentNode]挂载到this的实例上 随着render的每一次刷新,内联函数都是一个新的函数,这个都会重新执行,第一次为null(清空之前的) 第二次才是真的的dom节点
            this.firstInput = currentNode;
          }
        } type="text" placeholder="点击按钮提示数据" />
        <button onClick={
    
    this.tipInputValue}>提示输入数据</button>
        <input ref={
    
    
          (currentNode) => {
    
    
            this.secondInput = currentNode;
            console.log(currentNode);
          }
        } type="text" onBlur={
    
    this.getBlurInput} placeholder="失去焦点提示数据" />
        {
    
    /* 绑定的第二种方式绑定函数方式 随着render的刷新,不会调用两次,所以不会出现null的情况 */}
        <input ref={
    
    this.saveInput} type="text" />

        <span onClick={
    
    this.changeWeather}>今天{
    
    weather}</span>
      </div>
    )
  }
}

ReactDOM.render(<ClassRefsCom />, document.querySelector('#container'))

9.3refs-createRef形式(推荐)

class ClassRefsCom extends React.Component {
    
    
  state = {
    
    
    msg: '换用使用react'
  }
  firstRef = React.createRef()
  secondRef = React.createRef()
  getInputVal = () => {
    
    
    let {
    
     firstRef, secondRef } = this;
    console.log(firstRef, secondRef);
    console.log(firstRef.current.value, secondRef.current.value);
  }
  render() {
    
    
    let {
    
     msg } = this.state;
    return (
      <div className="refs">
        {
    
    msg}
        <hr />
        <input ref={
    
    this.firstRef} type="text" />
        <input ref={
    
    this.secondRef} type="text" />
        <button onClick={
    
    this.getInputVal}>获取输入框的数据</button>
      </div>
    )
  }
}

ReactDOM.render(<ClassRefsCom />, document.querySelector('#container'))

10.React中的事件处理

/**
 * 1.通过onXxx属性指定事件处理函数,注意大小写
 *    1.1.React使用的是自定义(合成)事件,而不是使用原生的DOM事件------为了更好的兼容性
 *    1.2.React中的事件是通过事件委托方式处理的(委托给组件最外层的元素)------为了高效
 * 2.通过event.target得到发生事件DOM元素对象------不要过度的使用ref
 */
class ClassRefsCom extends React.Component {
    
    
  state = {
    
    
    msg: '换用使用react'
  }
  eventTarget = (event) => {
    
    
    console.log(event.target);
  }
  render() {
    
    
    let {
    
     msg } = this.state;
    return (
      <div className="refs">
        {
    
    msg}
        <button onClick={
    
    this.eventTarget}>react中的事件处理</button>
        <hr />
      </div>
    )
  }
}

ReactDOM.render(<ClassRefsCom />, document.querySelector('#container'))

10.1React中的非受控组件(不推荐-不要过度的使用ref)

/**
 * 非受控组件:页面中所有输入类的DOM都是现用现取
 */
class ClassRefsCom extends React.Component {
    
    
  state = {
    
    
  }
  login = (event) => {
    
    
    console.log(event.target);
    event.preventDefault();
    let {
    
     userName, userpass } = this;
    console.log(userName.value, userpass.value);
  }
  render() {
    
    
    return (
      <div className="refs">
        <form action="">
          用户名: <input ref={
    
    (currentNode) => {
    
    
            this.userName = currentNode;
          }} type="text" />
          密码: <input ref={
    
    (currentNode) => {
    
    
            this.userpass = currentNode;
          }} type="password" />
          <button onClick={
    
    this.login}>登录</button>
        </form>
        <hr />
      </div>
    )
  }
}

ReactDOM.render(<ClassRefsCom />, document.querySelector('#container'))

10.2React中的受控组件(推荐)

/**
 * 受控组件:页面中所有输入类的组件,可以随着输入的同时维护到状态中,就是受控组件
 */
class ClassRefsCom extends React.Component {
    
    
  state = {
    
    
    username: '',
    userpass: ''
  }
  changeUserName = (event) => {
    
    
    let {
    
     target } = event;
    this.setState({
    
    
      username: target.value
    })
  }
  changeUserPass = () => {
    
    
    let {
    
     target } = event;
    this.setState({
    
    
      userpass: target.value
    })
  }
  login = (event) => {
    
    
    console.log(event.target);
    event.preventDefault();
    let {
    
     username, userpass } = this.state;
    console.log(username, userpass);
  }
  render() {
    
    
    return (
      <div className="refs">
        <form action="">
          用户名: <input onChange={
    
    this.changeUserName} type="text" />
          密码: <input onChange={
    
    this.changeUserPass} type="password" />
          <button onClick={
    
    this.login}>登录</button>
        </form>
        <hr />
      </div>
    )
  }
}

ReactDOM.render(<ClassRefsCom />, document.querySelector('#container'))

10.3React中的事件传参

10.3.1使用函数柯里化解决函数传参问题

/**
 * 高阶函数:如果一个函数符合下面两个规范中的任何一个,那么该函数就是高阶函数
 * 1.若A函数,接受的参数是一个函数,那么A就可以称之为高阶函数
 * 2.若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数
 * 3.常见的高阶函数:Promise/setTimeout/arr/map等
 * 
 * 函数柯里化:通过函数调用继续返回函数的方式,实现多次接受参数最后统一处理的函数编码形式
 */
class ClassRefsCom extends React.Component {
    
    
  state = {
    
    
    username: '',
    userpass: ''
  }
  changeInputValue = (inputType) => {
    
    //函数柯里化解决函数传参的问题
    return (event) => {
    
    
      let {
    
     target } = event;
      this.setState({
    
    
        [inputType]: target.value
      })
    }
  }
  login = (event) => {
    
    
    console.log(event.target);
    event.preventDefault();
    let {
    
     username, userpass } = this.state;
    console.log(username, userpass);
  }
  render() {
    
    
    return (
      <div className="refs">
        {
    
    /* 方法一:使用柯里化函数 */}
        <form action="">
          {
    
    /* this.changeInputValue('username')的意思就是将this.changeInputValue('username')函数的返回值交给onChange去调用,所以我可以使用函数柯里化在this.changeInputValue种返回一个新的函数交给onChange去调用 */}
          用户名: <input onChange={
    
    this.changeInputValue('username')} type="text" />
          密码: <input onChange={
    
    this.changeInputValue('userpass')} type="password" />
          <button onClick={
    
    this.login}>登录</button>
        </form>
      </div>
    )
  }
}

ReactDOM.render(<ClassRefsCom />, document.querySelector('#container'))

10.3.2不使用函数柯里化解决函数传参问题

class ClassRefsCom extends React.Component {
    
    
  state = {
    
    
    username: '',
    userpass: ''
  }
  changeInputValue = (inputType, event) => {
    
    
    let {
    
     target } = event;
    this.setState({
    
    
      [inputType]: target.value
    })
  }
  login = (event) => {
    
    
    console.log(event.target);
    event.preventDefault();
    let {
    
     username, userpass } = this.state;
    console.log(username, userpass);
  }
  render() {
    
    
    return (
      <div className="refs">
        {
    
    /* 方法二:直接返回一个函数 */}
        <form action="">
          用户名: <input onChange={
    
    (event) => {
    
    
            this.changeInputValue('username', event)
          }} type="text" />
          密码: <input onChange={
    
    () => {
    
    
            this.changeInputValue('userpass', event)
          }} type="password" />
          <button onClick={
    
    this.login}>登录</button>
        </form>
      </div>
    )
  }
}

ReactDOM.render(<ClassRefsCom />, document.querySelector('#container'))

11.React的生命周期

  • 生命周期图
    在这里插入图片描述

11.1React的生命周期-实例

class ClassRefsCom extends React.Component {
    
    
  state = {
    
    
    msg: '欢迎实用react',
    opacity: 1
  }
  changeOpacity = () => {
    
    //改变透明度事件 调用次数==>事件触发
    let {
    
     opacity } = this.state;
    this.timer = setInterval(() => {
    
    
      opacity -= 0.1;
      if (opacity <= 0) {
    
    
        opacity = 1;
      }
      this.setState({
    
    
        opacity: opacity
      })
    }, 100);
  }
  unmountCom = () => {
    
    //卸载组件事件 调用次数==>事件触发
    ReactDOM.unmountComponentAtNode(document.querySelector('#container'))
  }
  render() {
    
    //组件初始化/组件更新 调用次数==>1+n
    let {
    
     msg, opacity } = this.state;
    return (
      <div className="refs" style={
    
    {
    
     opacity: opacity }}>
        <span>{
    
    msg}</span>
        <button onClick={
    
    this.unmountCom}>卸载组件</button>
      </div>
    )
  }
  componentDidMount() {
    
    //组件挂载完毕 调用次数==>1
    console.log(this);
    this.changeOpacity();
  }
  componentWillUnmount() {
    
    //组件将要被卸载 调用次数==>1
    clearInterval(this.timer)
  }
}

ReactDOM.render(<ClassRefsCom />, document.querySelector('#container'))

11.2React的生命周期-创建时

class ClassRefsCom extends React.Component {
    
    
  constructor(props) {
    
    
    super(props);
    console.log('constructor');
  }
  state = {
    
    
    msg: '欢迎实用react',
    count: 0
  }
  changeState = () => {
    
    
    let {
    
     count } = this.state;
    this.setState({
    
    
      count: count += 1,
      msg: 'changeMsg'
    })
  }
  /**
   * 此方法运用于一个罕见的用例:state在任何情况下都取决于props
   * 无论是初始化还是修改,都不起作用
   */
  static getDerivedStateFromProps(props, state) {
    
    
    console.log('getDerivedStateFromProps-获取派生状态从props中', props, state);
    // return null;
    return props;
  }
  render() {
    
    
    console.log('组件初始化/更新完毕', 'render');
    let {
    
     msg, count } = this.state;
    return (
      <div className="refs" >
        <span>{
    
    msg}-{
    
    count}</span>
      </div>
    )
  }
  componentDidMount(str) {
    
    //组件挂载完毕
    console.log('组件挂载完毕', 'componentDidMount');
    setTimeout(() => {
    
    
      this.changeState();
    }, 1000);
  }
}

ReactDOM.render(<ClassRefsCom name='xiaobaocheng' count={
    
    996} />, document.querySelector('#container'))

11.3React的生命周期-更新时

11.3.1React的生命周期-更新时-正常更新

class ClassRefsCom extends React.Component {
    
    
  state = {
    
    
    msg: '欢迎实用react',
    count: 0
  }
  changeState = () => {
    
    
    let {
    
     count } = this.state;
    this.setState({
    
    
      count: count += 1
    })
  }
  /**
   * 此方法运用于一个罕见的用例:state在任何情况下都取决于props
   * 无论是初始化还是修改,都不起作用
   */
  static getDerivedStateFromProps(props, state) {
    
    
    console.log('getDerivedStateFromProps-获取派生状态从props中', props, state);
    return null;
    // return props;
  }
  shouldComponentUpdate(props, state) {
    
    //是否应该更新组件 返回布尔值==>默认为true
    console.log('shouldComponentUpdate-是否应该更新组件', props, state);
    return true;//继续走下一个生命周期
    return false;//中止更新
  }
  render() {
    
    
    console.log('组件初始化/更新完毕', 'render');
    let {
    
     msg, count } = this.state;
    return (
      <div className="refs" >
        <span>{
    
    msg}-{
    
    count}</span>
        <button onClick={
    
    this.changeState}>点击更新state</button>
      </div>
    )
  }
  getSnapshotBeforeUpdate(prevProps, prevState) {
    
    
    console.log('getSnapshotBeforeUpdate-获取快照在更新之前', prevProps, prevState);
    return {
    
    
      name: '张三',
      age: 26
    }
  }
  componentDidUpdate(prevProps, prevState, snapshotValue) {
    
    
    console.log('componentDidUpdate-组件更新完毕', prevProps, prevState, snapshotValue);
  }
}

ReactDOM.render(<ClassRefsCom name='xiaobaocheng' count={
    
    996} />, document.querySelector('#container'))

11.3.2React的生命周期-更新时-强制更新

class ClassRefsCom extends React.Component {
    
    
  state = {
    
    
    msg: '欢迎实用react',
    count: 0
  }
  strongUpdate = () => {
    
    
    this.forceUpdate();
  }
  /**
   * 此方法运用于一个罕见的用例:state在任何情况下都取决于props
   * 无论是初始化还是修改,都不起作用
   */
  static getDerivedStateFromProps(props, state) {
    
    
    console.log('getDerivedStateFromProps-获取派生状态从props中', props, state);
    return null;
    // return props;
  }
  render() {
    
    
    console.log('组件初始化/更新完毕', 'render');
    let {
    
     msg, count } = this.state;
    return (
      <div className="refs" >
        <span>{
    
    msg}-{
    
    count}</span>
        <button onClick={
    
    this.strongUpdate}>强制更新</button>
      </div>
    )
  }
  getSnapshotBeforeUpdate(prevProps, prevState) {
    
    
    console.log('getSnapshotBeforeUpdate-获取快照在更新之前', prevProps, prevState);
    return {
    
    
      name: '张三',
      age: 26
    }
  }
  componentDidUpdate(prevProps, prevState, snapshotValue) {
    
    
    console.log('componentDidUpdate-组件更新完毕', prevProps, prevState, snapshotValue);
  }
}

ReactDOM.render(<ClassRefsCom name='xiaobaocheng' count={
    
    996} />, document.querySelector('#container'))

11.4React的生命周期-卸载时

class ClassRefsCom extends React.Component {
    
    
  state = {
    
    
    msg: '欢迎实用react',
    count: 0
  }
  unmountCom = () => {
    
    
    ReactDOM.unmountComponentAtNode(document.querySelector('#container'))
  }
  render() {
    
    
    console.log('组件初始化/更新完毕', 'render');
    let {
    
     msg, count } = this.state;
    return (
      <div className="refs" >
        <span>{
    
    msg}-{
    
    count}</span>
        <button onClick={
    
    this.unmountCom}>卸载组件</button>
      </div>
    )
  }
  componentWillUnmount() {
    
    //组件将要被卸载 调用次数==>1
    console.log('componentWillUnmount------组件将要被卸载');
  }
}

ReactDOM.render(<ClassRefsCom name='xiaobaocheng' count={
    
    996} />, document.querySelector('#container'))

11.5React的生命周期-getSnapshotBeforeUpdate/componentDidUpdate使用实例

class ClassRefsCom extends React.Component {
    
    
  state = {
    
    
    msg: '欢迎实用react',
    arr: []
  }
  stateArrPushItem = () => {
    
    
    let {
    
     arr } = this.state;
    arr.push({
    
    
      name: '新加' + (arr.length + 1),
      id: arr.length
    });
    this.setState({
    
    
      arr: arr
    })
  }
  shouldComponentUpdate() {
    
    
    return true;
  }
  render() {
    
    
    console.log('组件初始化/更新完毕', 'render');
    let {
    
     arr } = this.state;
    return (
      <div className="wrap" ref={
    
    (currentNode) => {
    
    
        this.wrap = currentNode;
      }}>
        <ul>
          {
    
    
            arr.map(item => {
    
    
              return (
                <li key={
    
    item.id}>{
    
    item.name}</li>
              )
            })
          }
        </ul>
      </div>
    )
  }
  getSnapshotBeforeUpdate() {
    
    
    let beforeUpdateWrapHeight = this.wrap.scrollHeight;
    return {
    
    
      beforeUpdateWrapHeight: beforeUpdateWrapHeight
    }
  }
  componentDidUpdate(prevProps, prevState, snapshotValue) {
    
    
    console.log(this.wrap.scrollHeight - snapshotValue.beforeUpdateWrapHeight);
    this.wrap.scrollTop += this.wrap.scrollHeight - snapshotValue.beforeUpdateWrapHeight;
  }
  componentDidMount() {
    
    
    setInterval(() => {
    
    
      this.stateArrPushItem()
    }, 500);
  }
}

ReactDOM.render(<ClassRefsCom name='xiaobaocheng' count={
    
    996} />, document.querySelector('#container'))

12.DOMdiff算法

/**
 * 1.react/vue中key有什么作用?(key的内部原理是什么?)
 * 2.为什么遍历列表时,key最好不要用index
 * 
 * 虚拟DOM中的作用
 *    1.简单来说:key就是虚拟DOM对象的标识,更新显示key起着极其重要的作用
 *    2.详细来说:当状态的数据发生变化时,react会根据【新数据】生成【新的虚拟DOM】,随后react进行【新虚拟DOM】与【旧虚拟DOM】的diff比较,比较规则如下
 *        a.旧的虚拟DOM中找到了与新的虚拟DOM相同的key 
 *            a1.若虚拟DOM中内容没有改变,直接使用之前的真实DOM
 *            a2.若虚拟DOM中内容改变,则生成新的真实DOM,随后替换掉页面中之前的真实DOM
 *        b.旧的虚拟DOM中未能找到与新的虚拟DOM中相同的key
 *            b1.根据数据创建新的真实DOM,随后渲染到界面
 * 
 * 用index作为key可能会引发的问题
 *    1.若对数据进行:逆序添加/删除等破坏顺序操作,会产生没有必要的真实DOM更新==>界面效果没问题,但是效率低
 *    2.如果结构中还包含输入类的DOM,会产生错误DOM更新==>界面有问题
 *    3.注意!如果不存在对数据的逆序添加/删除等破坏顺序的操作,仅用于渲染列表用于展示,使用index作为key是没有问题的
 * 
 * 开发中如何选择key
 *    1.最好使用每条数据的唯一值作为key,比如id/手机号/身份证/学号等
 *    2.如果缺点只是简单的展示数据,用index也是可以的
 */
class ClassRefsCom extends React.Component {
    
    
  state = {
    
    
    msg: '欢迎实用react',
    personsArr: [
      {
    
     id: 1, name: '张三' },
      {
    
     id: 2, name: '李四' }
    ]
  }
  addPeople = () => {
    
    
    let {
    
     personsArr } = this.state;
    personsArr.unshift(
      {
    
     id: 3, name: '王五' },
    )
    this.setState({
    
    
      personsArr: personsArr
    })
  }
  render() {
    
    
    let {
    
     personsArr } = this.state;
    let arr = [];

    personsArr.forEach((item, index) => {
    
    
      arr.push(
        /**
         * 使用index索引值做key
         * 
         * 初始化数据:
         *  [
         *    { id: 1, name: '张三' },
         *    { id: 2, name: '李四' }
         *  ]
         * 初始的虚拟DOM:
         *  <li key=0>张三</li>
         *  <li key=1>李四</li>
         * 
         * 更新后的数据
         *  [
         *    { id: 0, name: '王五' },  
         *    { id: 1, name: '张三' },   
         *    { id: 2, name: '李四' }
         *  ]
         * 更新数据后的虚拟DOM
         *  <li key=0>王五<input style={
    
    { width: '50px' }} type="text" /></li>//更新
         *  <li key=1>张三<input style={
    
    { width: '50px' }} type="text" /></li>//更新
         *  <li key=2>李四<input style={
    
    { width: '50px' }} type="text" /></li>//更新
         * 
         * 这样做导致的问题,
         * 1.由于初始化的虚拟DOM中找不到和更新后的虚拟DOM中相同的key,所以,整个arr就都会更新,这样就直接导致了效率极低,但是从内容上来说,张三/李四并没有修改,只是数组的unshift方法修改了数组的index,导致key全部修改了,从而致使页面全部更新,其实这样的更新是没有必要的
         * 2.input会复用,但是由于破坏了arr的结构,导致input对应出了问题
         * --------------------------------------
         * 使用唯一值做key
         * 
         * 初始化数据:
         *  [
         *    { id: 1, name: '张三' },
         *    { id: 2, name: '李四' }
         *  ]
         * 初始的虚拟DOM:
         *  <li key=0>张三</li>
         *  <li key=1>李四</li>
         * 
         * 更新后的数据
         *  [
         *    { id: 0, name: '王五' },  
         *    { id: 1, name: '张三' },   
         *    { id: 2, name: '李四' }
         *  ]
         * 更新数据后的虚拟DOM
         *  <li key=3>王五<input style={
    
    { width: '50px' }} type="text" /></li>//更新
         *  <li key=1>张三<input style={
    
    { width: '50px' }} type="text" /></li>//复用
         *  <li key=2>李四<input style={
    
    { width: '50px' }} type="text" /></li>//复用
         * 
         * 这样做,更新之后张三/李四的key/value和初始化是一样的,这样只会更新王五对应的DOM,大大提高了效率
         */
        <li key={
    
    item.id}> <input style={
    
    {
    
     width: '50px' }} type="text" /> {
    
    item.name}</li>
      )
    })
    console.log(arr);
    return (
      <div className="wrap">
        <ul>
          {
    
    arr}
        </ul>
        <button onClick={
    
    this.addPeople}>加人</button>
      </div>
    )
  }
}

ReactDOM.render(<ClassRefsCom name='xiaobaocheng' count={
    
    996} />, document.querySelector('#container'))

13.初始化React脚手架

13.1环境搭建

  • 全局安装:npm install create-react-app -g
  • 创建项目:create-react-app ‘自定义项目名称’
  • 进入项目文件夹:cd [‘自定义项目名称’]
  • 启动项目:npm start

13.2脚手架文件解读

  • public ⇒ 静态资源文件夹
    • favicon.ico ⇒ 网站页签图标
    • index.html ⇒ 主页面
      • 内容
        <!DOCTYPE html>
        <html lang="en">
        
        <head>
          <meta charset="utf-8" />
          <!-- %PUBLIC_URL%代表public文件夹的路径 -->
          <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
          <!-- 用于开启理想视口,用于做移动端的适配 -->
          <meta name="viewport" content="width=device-width, initial-scale=1" />
          <!-- 用于配置浏览器页签+地址栏的颜色(仅仅支持安卓手机) -->
          <meta name="theme-color" content="#000000" />
          <!-- 描述 -->
          <meta name="description" content="Web site created using create-react-app" />
          <!-- 用于指定网页添加到主屏幕的图标(仅仅支持苹果手机) -->
          <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
          <!-- 引用加壳时的配置文件 -->
          <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
          <title>React App</title>
        </head>
        
        <body>
          <!-- 如果浏览器不支持js的允许,才会显示 -->
          <noscript>You need to enable JavaScript to run this app.</noscript>
          <div id="root"></div>
        </body>
        
        </html>
        
    • logo192.png ⇒ logo图
    • logo512.png ⇒ logo图
    • manifest.json ⇒ 应用加壳的配置文件
    • robots.txt ⇒ 爬虫协议文件
  • src ⇒ 源码文件夹
    • App.css ⇒ App组件的样式
    • App.js ⇒ App组件
    • App.test.js ⇒ 用于给App做测试
    • index.css ⇒ 通用样式
    • index.js ⇒ 入口文件
    • logo.svg ⇒ logo图
    • reportWebVitals.js ⇒ 页面性能分析文件(需要web-vitals库的支持)
    • setupTests.js ⇒ 组件单元测试文件(需要jest-dom库的支持)

13.3脚手架配置请求代理

  • src文件夹下创建setupProxy.js文件 只能使用commonJS规范
const proxy = require('http-proxy-middleware');

module.exports = function (app) {
    
    
  app.use(
    proxy('/api', {
    
    //遇见/api前缀的请求,就会触发该代理配置
      target: 'http://localhost:5000',//请求转发给谁
      changeOrigin: true,//控制服务器收到的响应头中Host字段的值
      pathRewrite:{
    
    //重写请求路径 如果不设置这个参数服务器接到的地址就是/api/路径;设置了路径就是/路径
        '^/api':''
      }
    }),
    proxy('/api1', {
    
    
      target: 'http://localhost:5001',
      changeOrigin: true,
      pathRewrite:{
    
    
        '^/api1':''
      }
    })
  )
}

14组件通信

14.0-nanoid-唯一字符串 ID 生成器

import {
    
     nanoid } from 'nanoid';
console.log(nanoid());//生成唯一的id 参数可以是数字

14.1组件通信-后代组件向祖先组件发送数据

class GrandsonCom extends React.Component {
    
    
  state = {
    
    
    msg: 'grandson'
  }
  sendDataFromGrandsonCom = () => {
    
    
    let {
    
     getGrandsonValue } = this.props;
    getGrandsonValue({
    
    
      grandson: '来自孙子组件的值'
    })
  }
  render() {
    
    
    let {
    
     msg } = this.state;
    return (
      <div className="grandson">
        <span>{
    
    msg}</span>
        <button onClick={
    
    this.sendDataFromGrandsonCom}>点击按钮向祖父组件发送数据</button>
      </div>
    )
  }
}

class ChildrenCom extends React.Component {
    
    
  state = {
    
    
    msg: 'children'
  }
  sendDataFromChildrenCom = () => {
    
    
    let {
    
     getChildrenValue } = this.props;
    getChildrenValue({
    
    
      children: '来自孩子组件的值'
    })
  }
  render() {
    
    
    let {
    
     getGrandsonValue } = this.props;
    return (
      <div className="children">
        {
    
    this.state.msg}
        <button onClick={
    
    this.sendDataFromChildrenCom} >点击按钮向父组件发送数据</button>
        <GrandsonCom getGrandsonValue={
    
    getGrandsonValue} />
      </div>
    )
  }
}

class FatherCom extends React.Component {
    
    
  state = {
    
    
    msg: 'father'
  }
  getChildrenValue = (childrenValue) => {
    
    
    console.log('获取到的孩子组件的值', childrenValue);
  }
  getGrandsonValue = (childrenValue) => {
    
    
    console.log('获取到的孙子组件的值', childrenValue);
  }
  render() {
    
    
    return (
      <div className="father">
        {
    
    this.state.msg}
        <ChildrenCom getChildrenValue={
    
    this.getChildrenValue} getGrandsonValue={
    
    this.getGrandsonValue} />
      </div>
    )
  }
}
ReactDOM.render(<FatherCom />, document.querySelector('#container'))

14.2组件通信-任意组件通信/发布订阅者模式

  • 工具库:PubSubJS
  • 下载:npm install pubsub-js -S
class ChildrenCom1 extends React.Component {
    
    
  state = {
    
    
    msg: 'children1'
  }
  sendDataFromChildrenCom = () => {
    
    
    PubSub.publish('sendDataFromChildrenCom1', {
    
    //在ChildrenCom1中发布消息
      name: "children1",
      value: "你好啊,ChildrenCom2"
    })
  }
  render() {
    
    
    return (
      <div className="children">
        {
    
    this.state.msg}
        <button onClick={
    
    this.sendDataFromChildrenCom} >点击按钮向ChildrenCom1发送数据</button>
      </div>
    )
  }
  componentDidMount() {
    
    //在componentDidMount中订阅消息
    this.token = PubSub.subscribe('sendDataFromChildrenCom2', (msg, data) => {
    
    
      console.log('children1', msg, data);
    })
  }
  componentWillUnmount() {
    
    //在组件将要卸载的时候取消订阅消息
    PubSub.unsubscribe(this.token);
  }
}

class ChildrenCom2 extends React.Component {
    
    
  state = {
    
    
    msg: 'children2'
  }
  sendDataFromChildrenCom = () => {
    
    
    PubSub.publish('sendDataFromChildrenCom2', {
    
    //在ChildrenCom2中发布消息
      name: "children2",
      value: "你好啊,ChildrenCom1"
    })
  }
  render() {
    
    
    return (
      <div className="children">
        {
    
    this.state.msg}
        <button onClick={
    
    this.sendDataFromChildrenCom} >点击按钮向ChildrenCom2发送数据</button>
      </div>
    )
  }
  componentDidMount() {
    
    //在componentDidMount中订阅消息
    this.token = PubSub.subscribe('sendDataFromChildrenCom1', (msg, data) => {
    
    
      console.log('children2', msg, data);
    })
  }
  componentWillUnmount() {
    
    //在组件将要卸载的时候取消订阅消息
    PubSub.unsubscribe(this.token);
  }
}

class FatherCom extends React.Component {
    
    
  state = {
    
    
    msg: 'father'
  }
  getChildrenValue = (childrenValue) => {
    
    
    console.log('获取到的孩子组件的值', childrenValue);
  }
  getGrandsonValue = (childrenValue) => {
    
    
    console.log('获取到的孙子组件的值', childrenValue);
  }
  render() {
    
    
    return (
      <div className="father">
        {
    
    this.state.msg}
        <ChildrenCom1 />
        <ChildrenCom2 />
      </div>
    )
  }
}
ReactDOM.render(<FatherCom />, document.querySelector('#container'))

antd组件库的使用

  • 下载 cnpm install ant-S
  • 使用
import 'antd/dist/antd.css';
import { Button } from 'antd';
  • antd的按需引入和自定义主题
1.安装依赖:cnpm install react-app-rewired customize-cra babel-plugin-import less less-loader
2.修改package.json
...
	"scripts": {
	    "start": "react-app-rewired start",
	    "build": "react-app-rewired build",
	    "test": "react-app-rewired test"
  },

3.根目录下创建config-overrides.js

//配置antd按需引入css的规则
const { override, fixBabelImports } = require('customize-cra');
module.exports = override(
  fixBabelImports('import', {
    libraryName: 'antd',
    libraryDirectory: 'es',
    style:true,
  }),
  addLessLoader({
  	lessOptions:{
  		javascriptEnabled:true,
  		modifyVars: { '@primary-color': '#1DA57A' },
  	}
})
);

猜你喜欢

转载自blog.csdn.net/big_sun_962464/article/details/112666397