react笔记(持续更新中.....)

191 阅读9分钟

react起步

需要掌握的知识点: this指向,class类,ES6语法规范、npm包管理器、原型、原型链、数组常用的方法、模块化。

react高效的原因

使用了虚拟dom,不是直接更新真实dom Dom diffing 算法,最小化页面重绘。

react安装

cnpm i create-react-app -g

启动react项目

create-react-app 项目名

react特点

声明化 组件化

启动过程中会安装三个包。

特点

  • 组件化
  • 声明式(数据驱动)
  • 跨平台
  • 虚拟dom驱动 (fiber算法)

脚手架 create-react-app 简写 cra

cra项目运行架构

基于webpack 以入口函数 为中心 (src/index.js) 只要安装三个包 react // react核心语法包 react-dom // react组件构建虚拟dom 编译(比较) 挂载到 index.html上

reactDom render方法 虚拟dom为真实dom挂载

// 入口 index.js

`import ReactDom from 'react-dom'

const root = ReactDom.createRoot(document.querySelector('#root'));
root.render(<h1>Hello, world!</h1>);`

jsx

xml in js (可以在js中写标签)

xml可以理解为 自定义标签,一开始主要用于前后端传输数据(现在主要后端java 安卓 项目配置使用)

<student>
  <name>小明</name>
  <age>18</age>
</student>

洗脑三连 洗脑三连 洗脑三连

jsx目的 是为了 方便开发者写虚拟dom

允许开发者在 js中 直接标签(html标签 还是组件标签)

react自动编译成 虚拟dom对象 来运行

洗脑洗脑洗脑: 在js 你写的所有标签 其实是js对象对象对象

jsx 语法

  • jsx中使用js (一般是表达式) 在jsx中想要使用js 表达式 就加 {}

    注意 注释也要加 {/* 这是注释 */}

任意一个jsx必须 包裹在一个闭合标签中

import ReactDom from 'react-dom'

const root = ReactDom.createRoot(document.querySelector('#root'));

const num = 10;

const tpl = (
  <div>
    <h2>你好世界</h2>
   <h3>{num}</h3>
    {/* 这是jsx中的注释 */}
  </div>
)
root.render(tpl);

特殊属性:避免与关键字重名

  <div className="box"></div>
    <label htmlFor=""></label>

函数式组件

先天缺陷: 没有内部状态、没有生命周期钩子函数 定义

import React from 'react'
function App(props) {
  return (
    <div>
      <h1>{props.title}</h1>
      <h2>{props.subTitle}</h2>
    </div>
  )
}
// props就是使用时 标签传入的自定义属性

使用

<App title="主标题" subTitle="副标题"/>

class语法复习

属性是自己独有的,但是方法是可以进行公用的,这也就解释:属性在实例上,方法都是在原型上。

定义属性两种方式

1.直接定义 直接挂载到实例上

    name='笑你干嘛';
        age=132;

2.使用constructor定义 通过this关键字进行定义,实例的属性 new class时自动触发,且new传递参数 ,会传递到constructor

   constructor(name,age){
            // new class 时自动触发,且new传递参数会传递到constructor的
            this.name=name;
            this.age=age;
        }

定义方法

1.普通函数 (方法在原型链上)

  act(){
            console.log('555'+this.name)
        }

2.箭头函数(方法在实例上)(推荐第二种)

  act1=()=>{
            console.log('2223'+this.name)
        }

属性与方法都需要在创建一个实例进行使用

 const p1=new Person('小红',213231)
    p1.act1()

静态对象

第一种

 static b='长长的路'

第二种, 在实例化对象后面

 Person.z='不不知道'

静态方法

第一种

  static act3=()=>{
            console.log('242432',this.name)
        }

第二种,在实例化对象后面

 const p1=new Person('小红',213231)
  Person.act3=(){
        console.log('11111')
    }

完整写法

  class Person{
        
        // 在constructor通过this 关键字来定义 实例的属性
        constructor(name,age){
            // new class 时自动触发,且new传递参数会传递到constructor的
            this.name=name;
            this.age=age;
        }
        act(){
            console.log('555'+Person.b)
        }
        act1=()=>{
            console.log('2223'+this.name)
        }
        static b='长长的路'
        static act3=()=>{
            console.log('242432',this.name)
        }
    }
    const p1=new Person('小红',213231)
    console.log(p1)
    p1.act1()
    p1.act()
    console.log(Person.b)
    Person.z='不不知道'
    console.log(Person.z)
    Person.act3()
    Person.act3=(){
        console.log('11111')
    }

继承

父类

class Animal {
      constructor (kind) {
        this.kind = kind;
      };
      act(){
        console.log('这是父类上的方法');
      }
    }

子类

 class Cat extends Animal{
      constructor(name, gender, kind){
        /* 
          子类 继承父类,使用 constructor 时,必须在使用this之前调用
          super
          分析super
          在 子类的 constructor super() 相当于父类 constructor
          父类constructor this指向实例的

          super在子类方法中 super 代表父类在子类方法中调用父类方法
        */
        super(kind);
        this.name = name;
        this.gender= gender;
      };
      act2(){
        console.log('这是子类上的方法');
        super.act();
      }
    }
    const kitty = new Cat('tom', '公', '动物');
    console.log(kitty);
    kitty.act2();

组件中的样式

内联

react将内联样式映射成 js对象

<div style={{width: 200,height: 100+300, background:'red'}}></div>

引入外部的css

注意:注意会影响全局所有组件 cra默认配置的是 sass环境

可以引入外部的css文件 或者 scss文件 注意: jsx class 属性替换成className即可

import './style/a.css'
import './style/a.scss' 
<div style={
  {width: 200,height: 100+300, background:'red'}
}
></div>

问题: 解析 sass 包 node-sass问题 老vue-cli node-sass 一开始常见问题 node-sass资源 特别难安装(启动项目后,只要写sass/scss)就报错 启动项目 node-sass装不下来 (解决方案 手动安装 cnpm i node-sass)

后期(现在) node-sass 和新node版本 14.x以后版本冲突 使用直接报错 1 package.json中删除 node-sass包记录 2 删除整个node_modules 3 重新安装依赖 npm i 4 安装 sass包 替换node-sass npm i sass -D

styled-components

react插件 用于 做 样式组件

  • 安装
npm i styled-components -S
  • 基础使用

    编译成一个组件 渲染div标签 且具备 以下样式 在style 中创建 ***.js 文件

首先需要引入styled,keyframes主要是用来使用动画的

import styled, { keyframes } from 'styled-components'

接着

const Box=styled.div`
   width:300px;
   height:300px;
   background-color:green;
`

+选择器嵌套 可以通过选择器嵌套修改 样式盒子 内部的标签样式 嵌套规则 同 scss 和less

const Box2= styled.div`
  width: 200px;
  height: 200px;
  background: #dcaa12;
  p{
    width: 100px;
    height: 150px;
    background: #aadd44;
    &:hover{
      color:blue;
    }
  }
`
  • 传递props
const Box3 = styled.div`
  width: 200px;
  height: 200px;
  background: ${props => props.bgc?props.bgc:'red'};
`
 //在导入该Box3 的文件中书写
  <Box3 bgc='yellow'/>
  • 样式的继承
const Box4 = styled(Box3)`
  border: 10px solid green;
`
  • 定义动画关键帧
const Move = keyframes`
  0% {
    transform: rotate(-45deg);
  }
  100% {
    transform: rotate(45deg);
  }
`
// 动画
const Box5 = styled.div`
  width: 30px;
  height: 200px;
  background: pink;
  margin: 30px auto;
  //设置中心点
  transform-origin: center bottom;
  //导入需要使用模板来进行导入
  animation: ${Move} 500ms infinite alternate linear;
`

组件数据挂载 (专门针对 class组件)

外部数据 - props属性

  • props类型验证 prop-types包 进行 react props 类型验证
 npm i prop-types -S

//  在需要验证组件中引入 并定义 class或者(函数式) 静态属性
import PropTypes from 'prop-types'
class MyComponent extends Component{

}
MyComponent.propTypes = {
 // 一下是常用类型验证
 a: PropTypes.array,
 b: PropTypes.bigint,
 c: PropTypes.bool,
 d: PropTypes.func,
 e: PropTypes.number,
 f: PropTypes.object,
 g: PropTypes.string,
 h: PropTypes.symbol,

 // react组件 <组件/>
 i: PropTypes.element,

 // 这也是组件 直接传递 不加 <>
 j: PropTypes.elementType,
 // k必须是对象 且 必须是 Message实例 (new Message)
 k: PropTypes.instanceOf(Message),
 // 枚举  l值必须是给定值中的一个
 l: PropTypes.oneOf(['News', 'Photos']),
 // o值的类型必须是 给定类型中的其中一个
 o: PropTypes.oneOfType([
   PropTypes.string,
   PropTypes.number,
   PropTypes.instanceOf(Message)
 ]),
 // 固定类型数组  必须  number数组 json数组等
 p: PropTypes.arrayOf(PropTypes.number),
 // 对象 属性 必须是固定数据类型的值
 q: PropTypes.objectOf(PropTypes.number),
 // 对象 其中两个属性 必须符合给定的验证
 r: PropTypes.shape({
   optionalProperty: PropTypes.string,
   requiredProperty: PropTypes.number.isRequired
 }),
 // 对象精准结构 只能有这两个属性 符合要求
 s: PropTypes.exact({
   optionalProperty: PropTypes.string,
   requiredProperty: PropTypes.number.isRequired
 }),

 t: PropTypes.func.isRequired,

 // A value of any data type
 u: PropTypes.any.isRequired,
}

prop默认值

定义 静态属性 defaultProps 即可

// 定义静态属性 defaultProps
class Todo extends Component {
Todo.defaultProps = {
  a: 50
  // 属性名 prop 值默认值
}
}

props.children

类似于 vue slot组件 可以在一个组件标签 双标签嵌套内容 在 组件内部 this.props.children渲染组件内部

  • 嵌套内容
  • 父组件
        <Todo>
          <div>
            <h3>这是嵌套的内容</h3>
            <button>按钮</button>
          </div>
        </Todo>
  • 组件内部渲染
  • 子组件
class Todo extends Component{
  render(){
    return (
      <div>
        {this.props.children}
      </div>
    )
  }
}

也就是说: 在todo组件内部能够拿到父组件中在写的内容,也就是下面部分代码,并且可以渲染到页面上

 <div>
       <h3>这是嵌套的内容</h3>
       <button>按钮</button>
 </div>

+同时也可以不使用props.children,将一个组件中的内容嵌入到另一个组件中去

 //主组件
  render() {
    return (
      <div>
        <Todo b={A}/>
       )
  } 
 // Todo组件
  render() {
    return (
      <div>
        <this.props.b/>
        </div>
        )
      }

+也可以直接通过props来获得想在todo中的渲染内容

      <Todo a={
          <div><h2>这是另一个内容</h2></div>
        }>
     {this.props.a}

组件内部状态管理 - state

原理: 只需要 给实例定义 state属性即可

定义state

import React, { Component } from 'react'
class Todo extends Component {
  // 1. 第一种:直接外部定义
  /* state = {
    msg: '这是内部的状态'
  }; */
  // 2. 第二种:内部定义
  constructor(){
  //必须要写super()因为继承父类的
    super();
    this.state = {
      msg: '这是内部状态',
      isBeauty: true
    }
  };
  render() {
    return (
      <div>
        {this.state.msg}
        <br />
        { this.state.isBeauty? '哇塞大美女': '你是个好姑娘' }
      </div>
    )
  } 
}

注意: react虽然是数据驱动的,但是 并不是 mvvm,state 直接修改 视图不会刷新

修改state 并刷新视图 必须使用 实例调用setState方法 --setState方法

  • 参数为对象
 <button onClick = {this.clickBtn}>修改msg</button>
 //注意必须使用箭头函数,否则this并不是指向实例的,这个方法是一个复合型的函数,需要
  修改this的指向,使用bind方法,将this重新指向实例。
clickBtn = () => {
this.setState({
  msg: '值'
})
}
// 参数为对象 想要修改哪个state 就 在对象中 定义这个state的值即可
  • 参数是函数
  // 函数的参数 1 改变state  2  组件的props
    this.setState((state, props) => {
      return {
        msg: '值改变了',
        isBeauty: !state.isBeauty
      }
    })

问题?

setState修改数据时异步的 意味着: setState调用之后,无法立即获取 修改后 最新的数据(新的dom也无法获取)

解决方案? setState的第二个参数 是 异步回调函数 触发时机: setState修改数据后,数据 修改完成,且真实dom 更新完成后触发(触发时机和vue中的$nextTick方法 基本一致) react中:修改完数据 想要立即获取修改后最新的数据 和 最新的dom,都要在 setState的回调中获取

this.setState({

}, () => {
  // 在这里获取即可
})

react中的——事件

react 合成 语法: on原生事件(首字母大写) onClick onMouseover ...

绑定 react合成事件

基础语法:

<button onClick={函数}>按钮</button>

要求: 事件函数this指向需要指向组件实例

  • 行内定义箭头函数 作为事件函数使用
        <button onClick = {
          () => {
            this.setState({
              msg: '值改变了'
            })
          }
        }>修改msg</button>

注意: 造成业务代码 混入 视图结构 不利于 项目 维护 和 可读性降低

  • 在原型上定义一个方法 作为事件函数使用 问题: this指向 是undefined 不指向 实例(因为不是 实例调用 合成事件调用) 解决:行内定义bind 改变this指向 (指向实例)
import React, { Component } from 'react'

class Todo extends Component {
  state = {
    msg: '这是内部的状态'
  }; 
  render() {
    return (
      <div>
        <button onClick = {this.clickBtn.bind(this)}>修改msg</button>
        {this.state.msg}
      </div>
    )
  };
  clickBtn () {
    this.setState({
      msg:'值改变了'
    })
  }
}

注意: 也是不推荐, render在初始化 和每一次更新时 都会触发,bind是在render调用的,会导致 多次bind 每一次都返回新的函数

  • 挂载到原型上 在 constructor bind改变 this指向 原理: constructor时,给实例上定义一个同名方法 值 为 原型上方法 bind 改变this指向,绑定事件时,作用域关系 绑定的是 实例上的方法 优点:constructor只触发一次 不会多次bind
import React, { Component } from 'react'

class Todo extends Component {
  /* state = {
    msg: '这是内部的状态'
  }; */
  constructor(){
    super();
    this.state = {
      msg: '这是内部状态'
    }
    this.clickBtn = this.clickBtn.bind(this);
  };
  render() {
    return (
      <div>
        <button onClick = {this.clickBtn}>修改msg</button>
        {this.state.msg}
      </div>
    )
  };
  clickBtn () {
    this.setState({
      msg:'值改变了'
    })
  }
}

  • ❤ 直接 k=箭头函数 直接在实例上定义一个方法
import React, { Component } from 'react'

class Todo extends Component {
  state = {
    msg: '这是内部的状态'
  }; 
  render() {
    return (
      <div>
        <button onClick = {this.clickBtn}>修改msg</button>
        {this.state.msg}
      </div>
    )
  };
  clickBtn = () => {
    this.setState({
      msg:'值改变了'
    })
  }
}

注意: 推荐使用

获取事件对象

事件函数的第一个参数即为事件对象

e.stopPropagation() // 取消冒泡
e.preventDefault() // 阻止默认事件
e.target // 获取事件源

事件函数调用传参

原理: 行内新增箭头函数,让它作为事件函数, 实例上方法 在 箭头函数中调用即可传参

class Todo extends Component {
  render() {
    return (
      <div>
        <button onClick = {
          (e) => {
            this.clickBtn(e, 1)
          }
        }>按钮</button>
      </div>
    )
  };
  clickBtn = (e, id) => {
    console.log(e);
    console.log(id);
  }
}

react 内容渲染

条件渲染

使用短路或者三目表达式即可


        {/* 单分支 */}
        {
          this.state.isShow
          &&
          <div className="box"></div>
        }
        {/* 双分支 */}
        {
          this.state.isShow
          ?
          <div className="box1"/>
          :
          <div className="box2"/>
        }

循环

使用数组map方法循环


class Todo extends Component {
  state = {
    arr: ['a', 'b', 'c', 'd']
  };
  render() {
    return (
      <div>
        <ul>
          {
            this.state.arr.map((item, index) => {
              return (
                <li key={index}>
                  { item }
                </li>
              )
            })
          }          
        </ul>
      </div>
    )
  }; 
}

注意: 循环需要给 循环元素 加独一无二key (fiber比较使用)