React笔记

HTML
CSS
JavaScript基础
JavaScript(高级)DOM
React

简介

React将数据渲染为HTML视图的开源js库

虚拟DOM本质上是普通的Object类型对象

并且虚拟DOM属性很少,比较“轻”

文件

在这里插入图片描述

babel.min.js : ES6转ES5 、 jsx 转 js

react.development.js : react核心库

react-dom.development.js : react扩展库

写react代码的代码块

<script type="text/babel"></script>

创建项目

首先创建react项目可以先下载脚手架create-react-app(类似于vue的脚手架vue-cli)。

①打开cmd,执行:npm install -g create-react-app ; 全局安装。

如果执行失败,可能是node版本问题, react文档中要求Node >= 8.10 和 npm >= 5.6,查看版本:node -v;npm -v;如果node版本低,可以去node官网下载Download | Node.js;(下载完成后,记得编辑环境变量和配置全局路径和缓存路径)

具体安装步骤参考博文:node安装详解_樊小樊的博客-CSDN博客_node安装

安装完node,如果cache报错,可以试一下清除缓存

以管理员身份打开cmd, 执行:npm cache verify;或者npm cache clean

②第一步成功后,cd进到你想要创建项目的文件夹,执行:create-react-app my-app 新建并对react项目进行命名(注:项目名称不能有大写,my-app是自定义的项目名)

③第二步成功后,cd进入my-app项目,执行:npm start

运行成功后浏览器会自动打开,默认端口为3000;如果没有自动打开,手动:http://localhost:3000;

④想在编辑器上开发,以vscode为例,打开vscode,点击文件–>打开文件夹位置–>打开项目(刚刚用cmd创建的my-app项目),点击查看–>终端–>执行:npm start。

虚拟Dom

1、创建虚拟Dom(Virtual-Dom)

const VDOM = <h1>Hello_React!</h1>

2、渲染Dom到页面

ReactDom.render('虚拟Dom','容器')
//这里容器,react并没有提供选择器,需要用原生的返回一个元素

//例如
ReactDOM.render(虚拟Dom,document.getElementById('test'))

使用原生ReactAPI创建虚拟DOM

这种方式对嵌套元素不方便

需要用到React内的一个API

React.createElement(标签名,标签属性,标签内容)

 // 创建虚拟DOM
    const VDOM = React.createElement('h1',{id:'title',class:'ddd'},'Hello React!')

 // 将虚拟DOM添加到页面
    ReactDOM.render(VDOM,document.getElementById('test'))

JSX

简介

JSX全称JavaScript XML

语法

1.定义虚拟DOM时,不要写引号。

2.标签中混入JS表达式时要用 “ { } ” 。

let a = 'React'
let b = 'Vue'
const VDOM = <h1>hello {a} and {b}</h1>
ReactDOM.render(VDOM,document.getElementById('test'))

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p5XLafji-1665128870140)(C:\Users\shu\AppData\Roaming\Typora\typora-user-images\image-20220818185548272.png)]

3.样式的类名指定不要用class,要用className。

const VDOM = <h1 className="h1">我是H1标签</h1>

4.React内联样式的类名需要用驼峰写法,并且内容要用大括号括起来。

const VDOM = <h1 classNam="h1" style={{color:'white',fontSize:'29px'}}>我是H1标签</h1>

5.JSX语法可以用 “ ( ) " 括起来

const VDOM = (
      <h1>我是H1标签
        <span>我是span标签</span>  
      </h1>
      )

4.不允许多个根标签

在这里插入图片描述
这样不允许!

5.标签必须闭合

6.标签首字母

​ 若小写字母开头,则将该标签转为html中同元素,若html中无同名元素,则报错

​ 若大写字母开头,react就去渲染对应的组件,若组件没有定义,则报错

​ 这里的组件定义,等后面会讲到

6.花括号内只能写表达式不能写代码

在这里插入图片描述

注释

{/*<h1></h1>*/}

//大括号内表示要些js的语句,然后再注释

React面 向组件编程

函数式组件

在这里插入图片描述

执行过程

React解析组件标签,找到了MyComponent组件。

发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转为真实DOM,随后呈现在页面当中。

类式组件

创建的类必须继承React内置的一个类

class MyComponent extends React.Component {

}

在这里插入图片描述

复杂组件与简单组件

没有状态的叫做简单组件,有状态的是复杂组件(state)

构造器内的super

在这里插入图片描述

不写的话会报错

在这里插入图片描述

因为子类继承时,需要拿到父类的构造器(实例化的时候会报错,当然你不实例化就不会出错)

1、constructor()

这是ES6类的默认方法,通过 new 命令生成对象实例时自动调用该方法。并且,该方法是类中必须有的,如果没有显示定义,则会默认添加空的constructor( )方法。

2、super()

在 class 方法中,子类时会报错。报错的原因是:子类是没有自己的 this 对象的,它只能继承自父类的 this 对象,然后对其进行加工,而 super( ) 就是将父类中的 this 对象继承给子类。没有 super,子类就得不到 this 对象。

3、出现上面情况的原因是,ES5 的继承机制与 ES6 完全不同。简单解释,就是在 ES5 的继承中,先创建子类的实例对象 this,然后再将父类的方法添加到 this 上( Parent.apply(this) )。而ES6采用的是先创建父类的实例 this(故要先调用 super( ) 方法),完后再用子类的构造函数修改 this。

4、当一个构造函数前加上 new 的时候,背地里来做了四件事:

(1)生成一个空的对象并将其作为 this;

(2)将空对象的 proto 指向构造函数的 prototype;

(3)让构造函数里面的 this 指向这个空对象,并且调用该构造函数,给这个空对象添加属性和方法;

(4)返回这个新对象,所以构造函数里面不需要return(如果构造函数没有 return 或者 return 一个返回 this 值是基本类型,则返回 this;如果 return 一个引用类型,则返回这个引用类型)。

5、super(props)

(1)如果你用到了 constructor 就必须写 super(),是用来初始化 this,可以绑定事件到 this 上;

(2)如果你在 constructor 中要使用 this.props,就必须给 super 加参数:super(props);(无论有没有 constructor,在 render 中 this.props 都是可以使用的,这是React自动附带的)

6、为什么不能在 constructor 中直接使用 this.props

其实很简单,因为 this.props 必须要是一个对象,才能在它下面定义属性,而 constructor(props) 方法中 传入的参数 props 为对象,所以 super(props) 的作用就是在父类的构造函数中给 props 赋值一个对象 this.props = props 这样就能在它的下面定义你要用到的属性了,然而其他的由于没有传参就直接赋值为 undefind。

组件三大核心

state

基础

<script type="text/babel">
    class Weather extends React.Component {
      constructor(props) {
        super(props)
        this.state = { isHot: false }
      }
      render() {
        const isHot = this.state.isHot
        return <h1>今天天气很{isHot ? '热' : '凉'}</h1>
      }
    }

    ReactDOM.render(<Weather />, document.getElementById('test'))
  </script>

事件绑定

<script type="text/babel">
    class Weather extends React.Component {
      constructor(props) {
        super(props)
        this.state = { isHot: true }
      }
      render() {
        const { isHot } = this.state
        return <h1 onClick={demo}>今天天气很{isHot ? '热' : '冷'}</h1>
          //这里onClick要大写,并且右侧大括号,代表将函数传递给onClick进行事件处理(不要直接写小括号,那样会被直接调用)
      }
    }

    ReactDOM.render(<Weather />, document.getElementById('test'))
    function demo() {
      alert('啦啦');
    }
  </script>

类中方法的指向

在这里插入图片描述

类中定义的方法自动添加了严格模式

所以x()执行后的结果为undefined

解决他

   class Weather extends React.Component {
      constructor(props) {
        super(props)
        this.state = {
          isHot: true
        }
        this.Weather = this.Weather.bind(this)   //将原型对象的Weather的this指向设置为Weather对象,再将这个函数赋给Weather实例对象上的Weather函数
      }
      render() {
        const { isHot } = this.state
        return <h1 onClick={this.Weather}>今天天气很{isHot ? '热' : '冷'}</h1>
      }
      Weather() {
        console.log(this)
        if (this.state.isHot == true) {
          this.state.isHot = false
        }
        else {
          this.state.isHot = true
        }
        console.log(this.state.isHot)
      }
    }
    ReactDOM.render(<Weather />, document.getElementById('test'))

但是状态不可直接更改,需要借助一个内部的api来更改

setState

    class Weather extends React.Component {
      constructor(props) {
        super(props)
        this.state = {
          isHot: true
        }
        this.Weather = this.Weather.bind(this)
      }
      render() {
        const { isHot } = this.state
        return <h1 onClick={this.Weather}>今天天气很{isHot ? '热' : '冷'}</h1>
      }
      Weather() {
        const isHot = this.state.isHot
        this.setState({ isHot: !isHot })
      }
    }
    ReactDOM.render(<Weather />, document.getElementById('test'))

总结

构造器:初始化数据,更改函数指向

render:状态里面读取数据,根据状态值做展示

简写

在这里插入图片描述

自定义方法要用赋值语句和箭头函数的方法,因为箭头函数没有this

props

基础

在这里插入图片描述


这里是之前版本的写法,现在最新的写法如下图(2022年10月更新)
在这里插入图片描述
用之前的方法不影响食用,但会出错(希望学会这种方法,以后遇到用来替之)

    class Person extends React.Component {
      render() {
        return (
          <ul>
            <li>姓名:{this.props.name}</li>
            <li>年龄:{this.props.age}</li>
            <li>性别:this.props.sex</li>
          </ul>
        )
      }
    }
    const PersonText = {
      name: '张三',
      age: 19,
      sex: '男'
    }
    ReactDOM.render(<Person name={PersonText.name} age={Person.age} sex={Person.sex} />, document.getElementById('test'))

当然也可以简写为

在这里插入图片描述

三点运算符(展开运算符)

ReactDOM.render(<Person {...PersonText} />, document.getElementById('test'))
// 这种方式是批量传递props
//props  标签  很大量的标签

图片内为原生写法
在这里插入图片描述

原生里面的花括号和react里面的花括号含义不同

所以对…运算符实现也不同,所以react可以将对象展开(只能用在标签属性的传递),原生里面是不能…对象的

对props限制

在这里插入图片描述

引入这个依赖包

在这里插入图片描述

简写

在这里插入图片描述

static会变量放到对象身上,如果不加则放到原型对象上

当然,经测试可知,这里不用static而是直接使用

propTypes = {
      name: PropTypes.string
}

也是可以的

构造器是否接收props,是否传递props

就看你是否需要在构造器中通过this访问props

但是一般直接写就完事了,这种场景用不到

类中构造器能省略就省略

函数式组件使用props

函数不能使用react三大属性的state和refs,但是可以使用props

,因为函数有个特点——能够接收参数

    function Fun(props) {
      const { name, age, sex } = props
      return (
        <h1>
          <ul>
            <li>姓名:{name}</li>
            <li>性别:{sex}</li>
            <li>年龄:{age}</li>
          </ul>
        </h1>
      )
    }
    ReactDOM.render(<Fun name="张三" age="19" sex="男"/>, document.getElementById('test'))

总结

理解

每个组件对象都会有props(properties的简写 “属性”)

组件标签的所有属性都保存在props中

作用

通过标签属性从组件外向组件内传递变化的数据

注意

组件内部不要更改props的值,否则报错

refs

与事件的搭配使用(快被淘汰了)

  class Demo extends React.Component {
    Left = () => {
      const { input1, btn } = this.refs
      alert(input1.value)
    }
    Right = () => {
      const { input2 } = this.refs
      alert(input2.value)
    }

    render() {
      return (
        <h1>
          <input ref="input1" />
          <button ref="btn" onClick={this.Left}>按下获取信息</button>
          <input ref="input2" onBlur={this.Right} />
        </h1>
      )
    }
  }
  ReactDOM.render(<Demo />, document.getElementById('test'))

这种字符串形式的ref已经不推荐使用了

回调ref(内联和独立写法的区别)

  class Demo extends React.Component {
    Left = () => {
      const { input1 } = this
      alert(input1.value)
    }
    Right = () => {
      const { input1 } = this
      alert(input2.value)
    }
    render() {
      return (
        <h1>
          <input type="text" ref={(c) => { this.input1 = c }} />
              //这里简写为 <input type="text" ref={ c => this.input1 = c } />
          <button onClick={this.Left}>按下获取信息</button>
          <input onBlur={this.Right} ref={(c) => { this.input2 = c }} type="text" />
        </h1>
      )
    }
  }
  ReactDOM.render(<Demo />, document.getElementById('test'))

这里建议用 对象上的函数来做回调函数,优点在于可以避免react刷新state导致的多次调用回调函数的问题

//案例展示

class Demo extends React.Component {
    state = {
      isHot: true
    }
    Left = () => {
      const { input1 } = this
      alert(input1.value)
    }
    change = () => {
      const { isHot } = this.state
      this.setState({
        isHot: !isHot
      })
    }
    render() {
      const { isHot } = this.state
      return (
        <h1>
          <p>今天天气很{isHot ? '热' : '冷'}</p>
          <input ref={(c) => { this.input1 = c;console.log(c) }} type="text" />
          <button onClick={this.Left}>按下获取信息</button>
          <button onClick={this.change}>按下我更改天气</button>
        </h1>
      )
    }
  }
  ReactDOM.render(<Demo />, document.getElementById('test'))
//案例解决

  class Demo extends React.Component {
    state = {
      isHot: true
    }
    Left = () => {
      alert(this.input1.value)
    }
    change = () => {
      const { isHot } = this.state
      this.setState({
        isHot: !isHot
      })
    }
    inputfun = (c) => {
      this.input1 = c
      console.log(c)
    }
    render() {
      const { isHot } = this.state
      return (
        <h1>
          <p>今天天气很{isHot ? '热' : '冷'}</p>
              //这里直接将回调函数独立出来,而不是用内联
          <input ref={this.inputfun} type="text" />
          <button onClick={this.Left}>按下获取信息</button>
          <button onClick={this.change}>按下我更改天气</button>
        </h1>
      )
    }
  }
  ReactDOM.render(<Demo />, document.getElementById('test'))

在这里插入图片描述

淦!!!

createRef API的使用(最新的)

React.createRef调用后可以返回一个容器,该容器而已存储被ref所标识的节点,该容器是“专人专用的”,只能存一个

所以需要创建多个ref容器

  class Demo extends React.Component {
    creatRef1 = React.createRef()
    creatRef2 = React.createRef()
    fun1 = () => {
      alert(this.creatRef1.current.value)
    }
    fun2 = () => {
      alert(this.creatRef2.current.value)
    }
    render() {
      return (
        <h1>
          <input type="text" ref={this.creatRef1} />
          <button onClick={this.fun1}>点击提示信息</button>
          <input type="text" ref={this.creatRef2} onBlur={this.fun2} placeholder="失去焦点显示内容" />
        </h1>
      )
    }
  }
  ReactDOM.render(<Demo />, document.getElementById('test'))

总结

尽量避免字符串形式的ref

回调形式的ref,稍微麻烦

事件注意

通过onXxxx属性指定事件处理函数注意大小写,React使用的是自定义(合成)事件,而不是原生DOM事件(为了兼容性),React中的事件是通过事件委托方式处理的(委托给组件最外层的元素)(事件委托更高效)

通过event.target得到发生事件的DOM元素对象(所以这里不要过渡使用Ref,而是直接使用event事件对象)

  class Demo extends React.Component {
    fun = (event) => { //直接传入event,使用target获取发生事件的事件源
      alert(event.target.value)
    }
    render() {
      return (
        <h1>
          <input type="text" onBlur={this.fun} />  {/*这里不需要ref*/}
        </h1>
      )
    }
  }
  ReactDOM.render(<Demo />, document.getElementById('test'))

受控组件与非受控组件

非受控组件

阻止默认事件的触发

event.preventDefault()

btn = (e) => {
      // 阻止默认行为
      e.preventDefault();
      alert(`您的用户名:${this.username.value} 密码:${this.password.value}`)
    }

页面中所有现用现取(输入类的)组件,就叫非受控组件

  class Demo extends React.Component {
    username = (c) => {
      this.username = c
    }
    password = (c) => {
      this.password = c
    }
    btn = (e) => {
      e.preventDefault();
      alert(`您的用户名:${this.username.value} 密码:${this.password.value}`)
    }
    render() {
      return (
        <form>
          <input type="text" ref={this.username} />
          <br />
          <input type="password" ref={this.password} />
          <br />
          <button onClick={this.btn}>登录</button>
        </form>
      )
    }
  }
  ReactDOM.render(<Demo />, document.getElementById('test'))

受控组件

页面中输入类组件的信息随时都保存到状态state中,需要用时直接调用state

这种方式可以减少使用ref

  class Demo extends React.Component {
    state = {
      password: '',
      username: ''
    }
    usernameFun = (event) => {
      this.setState({ username: event.target.value })
    }
    passwordFun = (event) => {
      this.setState({ password: event.target.value })
    }
    btn = (event) => {
      // 阻止默认行为
      alert('您的账号:' + this.state.username + '您的密码:' + this.state.password)
      console.log(this.state)
      event.preventDefault();
    }
    render() {
      return (
        <form>
          <input type="text" onChange={this.usernameFun} />
          <input type="password" onChange={this.passwordFun} />
          <button onClick={this.btn}>登录</button>
        </form>
      )
    }
  }
  ReactDOM.render(<Demo />, document.getElementById('test'))

高阶函数/函数柯里化

必须要哪一个函数作为事件的回调,所以这里的函数其返回值是一个函数即可

在这里插入图片描述

这里的saveFormData即为高阶函数
在这里插入图片描述

高阶函数/函数柯里化

必须要哪一个函数作为事件的回调,所以这里的函数其返回值是一个函数即可

在这里插入图片描述

这里的saveFormData即为高阶函数
在这里插入图片描述

高阶函数定义

如果一个函数符合下面2个规范中的任何一个,那么该函数就是高阶函数

1、若A函数,接收的参数是一个函数,那么A就可以称之为高阶函数

2、若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数

函数的柯里化

通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式

个人理解的作用是:

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

不用柯里化的方式

  class Demo extends React.Component {
    btn = () => {
      alert('您的账号:' + this.state.username + '您的密码:' + this.state.password)
    }
    saveData = (name, value) => {
      this.setState({
        [name]: value
      })
    }
    render() {
      return (
        <form>
          用户名:<input type="text" onChange={(event) => { this.saveData('username', event.target.value) }} />
          密码:<input type="password" onChange={(event) => { this.saveData('password', event.target.value) }} />
          <button onClick={this.btn}>登录</button>
        </form>
      )
    }
  }
  ReactDOM.render(<Demo />, document.getElementById('test'))

生命周期

卸载组件

ReactDOM.unmountComponentAtNode(document.getElementById(‘test’))

render的兄弟

componentDidMount

组件挂载完毕后调用

componentWillUnmount

组件将要卸载是调用

生命周期回调函数 <=> 生命周期钩子函数 <=> 生命周期函数 <=> 生命周期钩子

理解

组件从创建到死亡会经历一些特定的阶段

React组件中 包含一系列钩子函数(生命周期回调函数),会在特定的时刻调用

我们在定义组件时,会在特定的生命周期回调函数,中做特定的工作

生命周期图(旧)

在这里插入图片描述

挂载的执行流程

1、constructor构造器

2、componentWillMount组件将要挂载

3、render挂载组件

4、componentDidMount组件挂载完成

————————————挂载初始化完毕————————————

5、componentWillUnmount组件将要卸载

在这里插入图片描述

更新的执行流程

render父组件更新

componentWillRecceiveProps组件将要接收props

第一次往子组件传的参数不算(不会执行),以后传的才算(才会执行)

并且这个函数会有参数,值为父组件传递过来的值

在这里插入图片描述

shouldComponentUpdate组件是否应该更新?(开关/阀门)

componentWillUpdate组件将要更新

render更新组件

componentDidUpdate组件更新完成

componentWillUnmount组件将要卸载

setState状态更新

shouldComponentUpdate组件是否应该更新?(开关/阀门)

在这里插入图片描述

如果这里返回false,则不更新

默认不写,那么默认也就返回true,则更新(如果写了这个函数,那必须要写return并且返回true或者false)

componentWillUpdate组件将要更新

render更新组件

componentDidUpdate组件更新完成,他会传递两个参数,一个props(之前的props ,并不是实时的),一个状态state(之前的state,并不是实时的)

在这里插入图片描述

componentWillUnmount组件将要卸载

③强制更新,这种更新一般是不想通过状态更新所使用

forceUpdate强制更新,不受②中的阀门控制

在这里插入图片描述

render更新组件

componentDidUpdate组件更新完成

componentWillUnmount组件将要卸载

组件与组件相互通信

在这里插入图片描述

总结

在这里插入图片描述

常用

在这里插入图片描述

对比新旧生命周期

新版本中 这三个需要加UNSAFE

在这里插入图片描述

componentWillMount

componentWillReceiveProps

componentWillUpdate

在这里插入图片描述

这些需要加UNSAFE的生命周期,会带来不安全的编码(红色框)

新版生命周期

在这里插入图片描述

getDerivedStateFromProps和getSnapshotBeforeUpdate不常用

getDerivedStateFromProps

在这里插入图片描述

getSnapshotBeforeUpdate

快照
在这里插入图片描述

这个getSnapshotBeforeUpdate返回的值传给了componentDidUpdate,如下
在这里插入图片描述

getSnapshotBeforeUpdate案例

作用

在这里插入图片描述
在这里插入图片描述

diffing算法

虚拟DOM中key的作用

key是虚拟DOM对象的标识,在更新显示时key起着极其重要的作用

在这里插入图片描述

React脚手架

安装

第一步 安装库

npm i -g create-react-app

第二步 切换到要创建项目的目录 创建脚手架

create-react-app react_staging

或者这种方式

在这里插入图片描述

第三步 启动 yarn start

工程介绍

在这里插入图片描述

渲染组件时,包裹这个标签,react可以帮我们检查组件代码的合理性

在这里插入图片描述

记录性能
在这里插入图片描述

样式模块化

类名冲突了,这个样式会被覆盖,因为最后要汇聚到一个网页上面,所以其他组件如果类名或者id相同,就会导致冲突

在这里插入图片描述

rcc

rfc

在这里插入图片描述

还在更新,不妨点个收藏

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值