React 学习笔记

  • 特性

    1. 声明式设计 React采用声明范式,可以轻松描述应用

    2. 高效 React通过对DOM的模拟(虚拟dom),最大限度地减少与DOM的交互

      • DOM操作频繁是影响前端性能的最大因素

      • Diff算法可以解决

    3. 灵活 React可以与已知的库或框架很好的配合

    4. JSX JSX是Javascript语法的扩展

    5. 组件 通过React构建组件,是的代码更加容易得到复用,能够更好地应用在大项目的开发中

    6. 单项响应的数据流 React实现了单项响应的数据流,从而减少了重复代码,这又是他为什么比还童数据绑定更简单

  • 虚拟DOM

    • 传统DOM更新

      真实页面对应一个DOM树,在传统页面的开发模式中,每次需要更新页面时,都要手动操作DOM来进行更新

基本使用(非脚手架)使用<script>标签

React的安装

  • 安装命令

npm i react react-dom
  • react包是核心,提供创建元素,组件等功能

    • 负责创建元素

  • react-dom 包提供DOM相关功能

    • 只有web应用使用react-dom,若为手机应用或其他,将导入其他包

    • 负责将创建好的元素渲染至dom中

React的使用

  • 引入react和react-dom的两个js文件(类似引入'vue')

<script src="./node_modules/react/umd/react.development.js"></script>
<script src="./node_modules/react-dom/umd/react-dom.development.js"></script>
创建React元素
1.React.createEelement

名称,属性(对象),元素子节点

const title = React.createElement('p',{title:'我是标题',id:'p1'},'hello',React.create.Element('span',null,'我是span节点'))

并不方便,仅限于了解

2.ReactDOM.render()[已弃用并且修改]
  • 渲染React元素到页面中

ReactDOM.render(title,root)
  • React18后该方法已弃用,改为

const root = ReactDOM.createRoot(document.getElementById('root'))//DOM对象,用于指定渲染到页面中的位置
root.render(
    el  //要渲染的React元素
)

React组件

  • 使用React就是在使用组件

两种创建方式

1.使用函数创建组件
  • 函数名称必须以大写开头,React据此区分组件和普通的React元素

  • 函数组件必须有返回值

  • 如果返回值为null表示不渲染任何内容

function Hello(){
    return(
    <div>这是我的第一个函数组件</<div>
    )
}
​
//简写
const Hello = ()=> <div>这是我的第一个函数组件</div>
  • 渲染:用函数名作为组件标签名

  • 可以是单标签也可以是双标签

root.Render(<Hello/>)
2.使用类创建组件
  • 使用ES6的class创建的组件

  • 约定:

    • 类名称也必须以大写字母开头

    • 类组件应该继承React.Component父类,从而可以使用父类中提供的方法或属性

    • 类组件必须有render()方法

    • render()方法必须有返回值,表示该组件的结构

    class Hello extends React.Component{
        render(){
            return <div>hello</div>
        }
    }
    const root = ....getE()
    root.render(<Hello/>)

抽离为独立JS文件

  • 组件作为一个独立的个体,一般都会放到一个单独的JS文件中

  • 步骤

    1. 创建Hello.js

    2. 在Hello.js中导入React

      import React from 'react'

    3. 创建组件(函数或类)

    4. 在Hello.js中导出该组件

      export default Hello

    5. 在index.js中导入Hello组件

      import Hello from 'hello.js'

    6. 渲染组件

有状态组件和无状态组件

  • 函数组件又叫做无状态组件,类组件又叫做有状态组件

  • 状态{state} 即数据

  • 函数组件没有自己的状态,只负责数据显示(静)

  • 类组件有自己的状态,负责更新UI,让页面动起来

组件中的state和setState()

  • state基本使用

  • setState()修改状态

1.state的基本使用
  • 状态{state}即数据,是组件内部的私有数据,只能在组件内部使用

  • state的值是对象,表示一个组件中可以有多个数据

class Hello extends React.Component {
    constructor(){
        super()
        //初始化state
        this.state = {
            count: 0
        }
    }
    render(){
        return{
            <div>有状态组件{this.state.count}</div>
        }
    }
}

2.setState()修改状态
  • 状态是可变的

  • 语法:this.setState({要修改的数据})

  • 注意:不要直接修改state中的值,这是错误的

  • 思想:数据驱动视图

export default class extends React.Component {
    // 第二种初始化方式
    state = {
        count:1
    }
    render(){
        return (
            <div>
                <div>计数器 :{this.state.count}</div>
                <button onClick={() => {
                     this.setState({
            	 		count: this.state.count+1
           			  })   
                }}>+1</button>
            </div>
        )
    }
}
注意异步
  • setState是异步更新的

  • 所以使用该语法时,后面的setState()不要依赖于前面的setState()

  • 可以多次调用setState(),只会触发一次重新渲染

推荐语法
  • setState((state,props)=>{})语法

    • state:表示最新的state

    • props:表示最新的props

    setState((state,props)=>{
        return{
            
        }
    })
    //也是异步更新
    //但state是最新状态参数
第二个参数
  • 场景:在状态更新(页面完成重新渲染)后立即执行某个操作

  • 语法:setState(updater[,callback])

组件通讯(props)

组件的props
  • 组件是封闭的,要接受外部数据应该通过props实现

  • props作用:接受传递给组件的数据

  • 传递数据:给组件标签添加属性

  • 接收数据:

    • 函数组件通过参数props接收数据

      <Hello name="jack" age={19}>
          
      function Hello(props){
          console.log(props)
           return{ 
              <div>接收到的数据:{props.name}</div>
            }
      }
    • 类组件通过this.props接收数据

      class Hello extends React.Component{
          render(){
              return{
                  <div>接收到的数据:{this.props.age}</div>
              }
          }
      }
      
      <Hello name="jack" age={19} /></Hello>
特点
  1. 可以给组件传递任意类型的数据

    • 传非字符串{19}

    • 传字符串"19"

    • 也可以传函数 fn={()=>{}}

    • 传JSX: tag={<p>这是一个p标签</p>}

  2. props是只读的对象,只能读取属性值,不可修改

  3. 使用类组件时,如果写了构造函数,应该将props传递给super(),否则无法在构造函数中获取props

    class Hello extends React.Component{
        constructor(props) {
            super(props);
            this.state={
                count:0
            }
        }
        render(){
            return{
                <div>接收到的数据:{this.props.age}</div>
            }
        }
    }
    
    <Hello name="jack" age={19} /></Hello>
props深入
1.children属性
  • 表示组件标签的子节点,当组件标签有子节点时,props就会有该属性

  • 与普通的props一样,值可以是任意值(文本,React元素组件,甚至是函数)

function Hello(props){
    return{
        <div>
        组件的子节点:{props.children}
        </div>
    }
}
<Hello>我是子节点</Hello>
props.children即为"我是子节点"
2.props校验
  • 对于组件来说props是外来的,无法保证使用者传入什么格式的数据

  • props校验:允许在创建组件的时候就制定props的类型,格式等

使用步骤
  1. 安装包prop-types

    yarn add prop-types
    npm i props-types
  2. 导入prop-types包

    import PropTypes from 'prop-types'
  3. 使用组件名.propTypes={}来给组件的props添加校验规则

    Hello.propTypes = {
        //约定colors属性为Array类型
        colors:PropsTypes.array
        //若类型不对,则报出明确错误,便于分析错误原因
        
    }
  4. 校验规则通过PropTypes 对象来指定

约束规则
  • 创建的类型: array、bool、func、number、object、string

  • React元素类型:element

  • 必填项:isRequired

  • 特定结构的对象: shape({})

  • 更多的[约束规则

//常见类型
optionalFunc:PropTypes.func,
//必选
requiredFunc:PropTypes.func.isRequired,
//特定结构的对象
optionalObjectWithShape:PropTypes。shape({
    color:PropTypes.string
    fontSize:PropTypes.number
})

屏幕截图 2024-06-28 145124

3.props默认值
  • 场景:分页组件=>每页显示条数

    Hello.defaultProps = {
        pageSize:10
    }
    //若传入数据,则以传入的数据为主

父传子
  1. 父组件提供要传递的数据

    class Fu extends React.Component{
        state={
            lastName:'王'
        }
        render(){
            return(
            <Hello name={this.state.lastName}></Hello>
            )
        }
    }
    class Hello extends React.Component{
        render(){
            return(
            <div>子组件接收到的数据:{props.name}</div>
            )
        }
    }
子传父
  • 利用回调函数,父组件提供回调,子组件调用,将要传递的数据作为回调函数的数据

    • 父组件提供一个回调函数(用于接收数据)

      class Fu extends React.Component{
          state={
              count:10
          }
          getChildMsg=data=>{
              this.setState(
                  {count:data}
              )
          }
          render(){
              return(
              <Child getMsg={this.getChildMsg}></Child>
              )
          }
      }
      class Child extends React.Component{
          state={
              myCount:20
          }
          handleClick=()=>{
              this.props.getMsg(this.state.myCount)
          }
          render(){
              return(
              <button onClick={this.handleClick}>点击更改父组件数据</button>
              )
          }
      }

兄弟组件
  • 将共享状态提升到最近的公共组件中,由公共父组件管理这个状态

  • 思想:状态提升

  • 公共父组件职责:

    1. 提供共享状态

    2. 提供操作共享状态的方法

  • 要通讯的子组件只需通过props接收状态或操作状态的方法

class Fu2 extends React.Component{
    state={
        count:0
    }
    OnIncrement=()=>{
        this.setState({count:this.state.count+1})
        console.log('点击了')
    }
    render(){
        return(
            <div>
            <Child1 data={this.state.count}/>
            <Child2 OnIn={this.OnIncrement}/>
            </div>

    )
    }
}
class Child1 extends React.Component {
    render() {
        return(
            <div>我是子组件1,数据为{this.props.data}</div>
        )
    }

}


class Child2 extends React.Component{
        render(){
            return(
                <button onClick={()=>this.props.OnIn()}>我是子组件2,点击我更改子组件1数据</button>
                )
            }
}
Context
  • 祖级传递数据

  • eg:App组件要传递数据给Child组件,该如何处理?

    • 使用Context

    • 作用:跨组件传递数据(比如主题,语言)

  • 使用步骤

    1. 调用React.createContext()创建Provider(提供数据)和Consumer(消费数据)两个组件

      const {Provider,Consumer} = React.createContext() 
    2. 使用Privider组件作为父节点,设置value属性表示要传递的数据

      <Provider value="pink">//包裹父组件
          <div className="App">
              </div>
      </Provider>
    3. 调用Consumer组件接收数据

      <Consumer>
          {data=><span>data参数表示接收到的数据</span>}
      </Consumer>

render-props和高阶组件

React组件复用

  • 两个组件功能相同/相似->复用相似的功能(联想函数封装)

  • 复用什么?

    • state

    • 操作state的方法(组件状态逻辑)

  • 两种方式:

    1. render props模式

    2. 高阶组件(HOC)

    • 注意:这两种方式不是新的API,而是利用React自身特点的编码技巧,演化而成的固定模式

1.render props模式

  • 思路:将要复用的state和操作state的方法封装到一个组件中

  • 在使用组件时,添加一个值为函数的prop,通过函数参数来获取(需要组件内部实现)

2.高阶组件

  • 思路:高阶组件(HOC)是一个函数,接收要包装的组件,返回增强后的组件

组件生命周期

  • 组件生命周期:组件从被创建到挂载到页面中运行,再到组件不用时卸载的过程

  • 只有类组件才有生命周期(因为只有类组件能让页面动起来,函数组件不可以)

  • 三个阶段

image-20240628150728814

1.创建时(挂载阶段)

  • 执行时期:组件创建时(页面加载时)

  • 执行顺序

    • constructor()=>

      • 创建组件时最先执行

      • 初始化state

      • 为事件处理程序绑定this

    • render()=>

      • 每次组件渲染都会触发

      • 渲染UI(注意不能调用setState())

    • componentDidMount

      • DOM已经完成渲染(组件挂载后)

      • 可以发送网络请求(一进页面就发请求)

2.更新时(更新阶段)

  • 三种导致组件更新的情况

    1. New props

    2. setState()

    3. forceUpdate()

      • 强制更新

      this.forceUpdate()
  • 执行顺序

    • render()=>

      • 先执行渲染

      • 每次组件渲染都会触发

    • componentDidUpdate()

      • 在这里写函数,render也会重新执行

      • 组件更新(完成DOM渲染后)

        • 发送网络请求

        • DOM操作

        • 注意:如果要setState()必须放在一个if条件中

          • 否则导致递归更新::setState导致重新渲染,则会重新执行render,则会重新执行componentDidUpdate(),导致重新调用setState

        • 正确做法:比较更新前后props是否相同

        componentDidUpdate(prevProps){
          // 上一次的props:prevProps,
          //  当前props:this.props
            if(prevProps.count!==this.props.count){
                this.setState({})
                //在此发送网络请求
            }
        }

3.卸载时(卸载阶段)

  • 执行时机:组件从页面中消失

  • 钩子函数:ComponentWillUnmount

    • 触发时机:组件卸载(从页面中消失)

    • 执行清理工作:(比如:清理定时器等)

    componentDidMount(){
        //开启定时器
        this.timerId = setInterval(()=>{
            console.log('定时器正在执行')
        },500)
    }
    
    
    componentWillUnmount(){
        //清理定时器
        clearInterval(this.timerId)
        
    }

不常用的钩子函数

旧版的生命周期钩子函数

新版完整生命会走棋钩子函数

getDerivedStateFromProps()
  • getDerivedStateFromProps 会在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。它应返回一个对象来更新 state,如果返回 null 则不更新任何内容

  • 不管原因是什么,都会在每次渲染前触发此方法

shouldComponentUpdate()
  • 根据 shouldComponentUpdate() 的返回值,判断 React 组件的输出是否受当前 state 或 props 更改的影响。默认行为是 state 每次发生变化组件都会重新渲染

  • 当 props 或 state 发生变化时,shouldComponentUpdate() 会在渲染执行之前被调用。返回值默认为 true

getSnapshotBeforeUpdate()
  • getSnapshotBeforeUpdate() 在最近一次渲染输出(提交到 DOM 节点)之前调用。它使得组件能在发生更改之前从 DOM 中捕获一些信息(例如,滚动位置)。此生命周期的任何返回值将作为参数传递给 componentDidUpdate()

  • 此用法并不常见,但它可能出现在 UI 处理中,如需要以特殊方式处理滚动位置的聊天线程等

组件性能优化


React事件处理

事件绑定

  • 语法:on+事件名称={事件处理程序},比如 onClick={()=>{}}

  • 注意:React事件采用驼峰命名法,比如onMouseEnter,onFocus

1.函数组件
function App(){
    function handleClick(){
        console.log('单击事件触发了')
    }
    return{
        <button onClick={handleClick}>点我</button>
    }
}

2.类组件
class Hello extends React.Component{
    handleClick(){
    console.log('点了点了')
}
    render(){
        return(
        <button οnclick={this.handleClick}>点我点我</button>	
        )
    }
}
事件绑定中this指向
  • 箭头函数没有this,所以箭头函数中的this指向render中实例

1.class实例方法(推荐)
  • 将组件内部函数直接改为箭头函数

2. 箭头函数
3. bind
constructor(){
    super()
    this.onIncrement = this.
}

3. class实例方法(推荐)
  • 将组件内部函数直接改为箭头函数

事件对象

  • 可以通过事件处理函数的参数获取到事件对象

  • React中的事件对象叫做:合成事件

  • 合成事件:兼容所有浏览器,无需担心跨浏览器兼容问题

export default class extends React.Component {
    clickHandle(e){
        // 获取原生事件对象
        console.log(e.nativeEvent)
    }
    render(){
        return (
            <div><button onClick = {this.clickHandle}>点我点我点我</button></div>
        )
    }
}

表单处理

1.受控组件

(v-model---->React)

  • React将state与表单元素值value绑定到一起,由state的值来控制表单元素的值

  • 受控组件:其值收到React控制的表单元素

<input type="text" value={this.state.count}/>
class App extends React.Component {
    constructor(){
        super()
        this.inputChange = this.inputChange.bind(this)
    }
    state = {
        txt : ''
    }
    inputChange(e){
       this.setState({
           txt: e.target.value
       })
    }
    render(){
        console.log(this.state);
        
        return (
            <div>
                {/* 把state的值设置给输入框的value,绑定change事件,这样用户在输入内容的时候调用相应函数,在函数里面把当前设置的值赋值给state,从而达到数据的统一 */}
                <input type="text" value={this.state.txt} onChange={this.inputChange}/>
            </div>
        )
    }
}
ReactDOM.render(<App />,document.getElementById('root'))
多表单元素优化
  • 问题:每个表单元素都有一个单独的事件处理函数,这样太繁琐

  • 优化:使用一个事件处理程序同时处理多个表单元素

步骤
  • 给表单元素添加name属性(用来区分是哪一个表单),名称与state相同(用来更新数据的)

  • 根据表单内容来获取对应值

  • 在change事件处理程序中通过 [name] 来修改对应的state

示例demo
inputChange(e){
   let target = e.target;
   let value = target.type == 'checkbox' ? target.checked : target.value;
   this.setState({
       [e.target.name]: value
   })
}
<input type="text" value={this.state.txt} name="txt" onChange={this.inputChange}/>
<input type="checkbox" value={this.state.isChecked} name="isChecked" onChange={this.inputChange}/>

2.非受控组件 (了解)

  • 说明:借助于ref,使用元素DOM方式获取表单元素值

  • ref的作用:获取DOM或者组件

使用步骤
  • 调用 React.createRef() 方法创建ref对象

  • 将创建好的 ref 对象添加到文本框中

  • 通过ref对象获取到文本框的值

class App extends React.Component {
    constructor(){
        super()
        
        //创建 ref
        this.txtRef = React.createRef()
    }
    // 获取文本框的值
    getTxt =() => {
        console.log(this.txtRef.current.value)
    }
    render(){
        return (
          <div>
            <input type ="text" ref={this.txtRef} />
            <button onClick ={this.getTxt}>获取值</button>
          </div>
        )
    }
}

路由

基本使用步骤

  1. 安装

    yarn add react-router-dom
    1. 导入路由的三个核心组件Router/Router/Link

    import {BrowserRouter as Router,Route,Link} from 'react-router-dom'
  2. 使用Router组件包裹整个应用

    <Router>
    	<div>
        	我是React路由器
        </div>
    </Router>
  3. 使用Link组件作为导航菜单(路由入口)

    <Router>
    	<div>
        	我是React路由器
    <Link to="/first"></Link>
        </div>
    </Router>
  4. 使用Route组件配置路由规则和要展示的组件(路由出口)

    <Router>
    	<div>
        	我是React路由器
    <Link to="/first"></Link>
    <Route path="/first" component={First}></Route>
        </div>
    </Router>

常用组件说明

  • Router组件:包裹整个应用,一个React应用只需要使用一次

    • 两种常用的Router: HashRouter和BrowserRouter

    • HashRouter: 使用URL的哈希值实现 (localhost:3000/#/first)

    • 推荐 BrowserRouter:使用H5的history API实现(localhost3000/first)

  • Link组件:用于指定导航链接(a标签)

    • 最终Link会编译成a标签,而to属性会被编译成 a标签的href属性

  • Route组件:指定路由展示组件相关信息

    • path属性:路由规则,这里需要跟Link组件里面to属性的值一致

    • component属性:展示的组件

    • Route写在哪,渲染出来的组件就在哪

路由的执行过程

  • 当我们点击Link组件的时候,修改了浏览器地址栏中的url

  • React路由监听地址栏url的变化

  • React路由内部遍历所有的Route组件,拿着Route里面path规则与pathname进行匹配

  • 当路由规则(path)能够匹配地址栏中的pathname时,就展示该Route组件的内容

编程式导航

  • 场景:点击登陆按钮,登陆成功后,通过代码跳转到后台首页,如何实现?

  • 编程式导航:通过JS代码来实现页面跳转

  • history是React路由提供的,用于获取浏览器历史记录的相关信息

  • push(path):跳转到某个页面,参数path表示要跳转的路径

  • go(n):前进或后退功能,参数n表示前进或后退页面数量

默认路由

  • 现在的路由都是通过点击导航菜单后展示的,如果进入页面的时候就主动触发路由呢

  • 默认路由:表示进入页面时就会匹配的路由

  • 默认路由:只需要把path设置为 '/'

匹配模式

模糊匹配模式
  • 当Link组件的to属性值为 '/login' 时候,为什么默认路由也被匹配成功?

  • 默认情况下,React路由是模糊匹配模式

  • 模糊匹配规则:只要pathname以path开头就会匹配成功

精准匹配
  • 默认路由认可情况下都会展示,如果避免这种问题?

  • 给Route组件添加exact属性,让其变为精准匹配模式

  • 精确匹配:只有当path和pathname完全匹配时才会展示改路由

小结

  • React路由可以有效的管理多个视图实现 SPA

  • 路由先需要通过安装

  • Router组件包裹整个应用,只需要使用一次

  • Link组件是入口,Route组件是出口

  • 通过props.history实现编程式导航

  • 默认是模糊匹配,添加exact编程精确匹配

  • React路由的一切都是组件,可以像思考组件一样思考路由

目录

README.md

使用方法的文档

node_modules

所有的依赖安装的目录

package-lock.json

npm install
  • npm install 会严格按照该锁定版本进行创建,安装和团队一致的项目所有文件版本

  • node_modules是根据该锁定的配置文件创建的,不用复制给他人

package.json

public

静态公共目录

index.html

项目唯一入口页面

src

开发用的源代码目录

index.js
import React from 'react'
import REACTDOM from 'react-dom'

React原理-虚拟DOM和Diff算法

  • React更新视图的思想:只要state变化就重新渲染视图

虚拟DOM

  • 本质上就是一个JS对象,用来描述你希望在屏幕上看到的内容(ui)

  • 其实就是react元素

Diff算法

  • 初次渲染时React会根据初始state(Model),创建一个虚拟DOM对象(树)

  • 根据虚拟DOM生成真正的DOM,渲染到页面中

  • 当数据变化后(SetState()),重新根据新的数据,创建新的虚拟DOM对象(树)

  • 与上一次得到的虚拟DOM对象,使用Diff算法对比(找不同),得到需要更新的内容

第一个项目

从头创建

1.脚手架 create-react-app

npm install -g create-react-app
create-react-app mydemo
npm和npx区别
  • npm 一直为创建时的版本

  • npx 不断更新,一直是最新工具

npx create-react-app mydemo
启动
npm start
初始化项目
1. npx create-react-app mydemo
2. npm init react-app mydemo
yarn create react-app mydemo

2.导入react和react-dom两个包

  • 删除src下所有文件

react开发需要引入多个依赖文件:react.js,react-dom.js,分别又有开发版本和生产版本,create-react-app里已经帮我们把这些东西都安装好了.把通过CRA创建的工程目录下的src目录清空,然后在里面重新创建一个index.js

  • 新建index.js(必须叫这个名字)

3.调用React.createElement()方法创建react元素

4.调用ReactDOM.render()方法渲染react元素到页面中

//从react包当中引入了React,只要写React.js组件就必须引入React,因为React里有一种语法叫做JSX,要写JSX就必须引入React
import React from 'react'
//ReactDOM可以帮助我们把React组件渲染到页面上去,没有其他作用了.他是从react-dom中引入的,而不是从react引入
import ReactDOM from 'react-dom'
//ReactDOM中有一个render方法,功能就是把组件渲染并且构造DOM树,然后插入到页面上某个特定元素上,React18后必须const  root后才能render
const root = ReactDOM.createRoot(document.getElementById('root'))//渲染到哪里
root.render(
//JSX语法--在Javascript写标签---JavaScriptXML
    <h1>hello</h1>, 
)

路由

  • 路由模式

    • 开发,本地阶段:hash 不会从服务器请求资源

    • 不带hash √ createBrowserRouter

  • 命令

    npm i reat-router-dom
  • 通过配置文件

    1. 引入createBrowserRouter

Vite+TS+Hook

Hooks语法

1.useState:状态钩子

  • 基本用法

    useState,能让函数组件拥有自己的状态,因此,他是一个管理状态的hooksAPI,通过useState可以实现状态的初始化,读取,更新

  • 语法

    const [状态名,set函数] = useState(初始值)
  • eg

    export const Count:React.FC = ()=>{
        const [count,setCount] = useState(0)
        const add = ()=>{
            setCount(count+1)
        }
        return (
            <>
                <h1>我是COUNT{count}</h1>
    ​
                <button onClick={add}>点击</button>
            </>
        )
    }
以函数形式为状态赋初值
import React, {useState} from "react";
​
export const DateCom:React.FC = ()=>{
    const [date, setDate] = useState(()=>{
        const dt = new Date()
        return {
            year:dt.getFullYear(),
            month:dt.getMonth()+1,
            day:dt.getDate()
        }
​
    })
​
    return(
        <>
            <h1>当前年月日信息:</h1>
​
​
            <p>年:{date.year}</p>
            <p>月:{date.month}</p>
            <p>日:{date.day}</p>
        </>
    )
}
异步解决方案
setCount(count+1)
setCount(count+1)
//调两次count值不会变,因为setState函数是异步的
//可以写为prev
setCoune((prev)=>{prev+1})
//count值改变
更新对象类型的值
import React, {useState} from "react";
import {render} from "react-dom";
​
export const DateCom:React.FC = ()=>{
    const [date, setDate] = useState(()=>{
        const dt = new Date()
        return {
            year:dt.getFullYear(),
            month:dt.getMonth()+1,
            day:dt.getDate()
        }
​
    })
​
​
​
​
​
    return(
        <>
            <h1>当前年月日信息:</h1>
​
​
            <p>年:{date.year}</p>
            <p>月:{date.month}</p>
            <p>日:{date.day}</p>
        </>
    )
}
​
export const UserInfo:React.FC = ()=>{
    const [user,setUser] =  useState({
        name:'aaaa',
        age:21,
        gender:'男'
    })
    const onChangeUser = ()=>{
      user.name = 'wwwww'
        user.age = 20
​
        setUser({...user})
        //或者
        setUser(Object.assign({},user))
    }
    return(
        <>
            <button onClick={onChangeUser}>点击更改用户信息</button>
            <h1></h1>
        </>
    )
}

2.useContext():共享状态钩子

  • 使用React.createContext()+useContext()轻松实现多层组件的数据传递

使用步骤
  1. 在全局创建Context对象

  2. 在父组件中使用Context.Provider 提供数据

  3. 在子组件中使用useContext 使用数据

3.useReducer():action钩子

  • 当状态更新逻辑复杂时考虑使用useReducer

  • 可以同步更新多个状态,而且能把对状态的修改从组件中独立出来

语法格式
const [state,dispatch] = useReducer(reducer,initState,initAction?)
  1. . reducer 是一个函数,类似于 (prevState, action) => newState。形参 prevState 表示旧状态,形参 action 表示本次的行为,返回值 newState 表示处理完毕后的新状态。

  2. initState 表示初始状态,也就是默认值。

  3. initAction 是进行状态初始化时候的处理函数,它是可选的,如果提供了 initAction 函数,则会把 initState 传递给 initAction 函数进行处理,initAction 的返回值会被当做初始状态。

  4. 返回值 state 是状态值。dispatch 是更新 state 的方法,让他接收 action 作为参数,useReducer 只需要调用 dispatch(action) 方法传入的 action 即可更新 state。 作者:刘龙彬 8. react hooks - useReducer - 哔哩哔哩 出处:bilibili

4.useEffect():副作用钩子

语法格式
useEffect(fn,deps?)

.useRef

  • ref

项目

1.创建vite项目

npm create vite@latest
----选择-----
npm i 
npm run dev

2.将路径模式改为@

npm i -D @types/node
  • 配置 vite.config.ts 文件:

// 1. 以 ES6 模块化的方式,从 Node 的 path 模块中,导入 join 函数
import { join } from 'path'
​
// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
  // 2. 在 resolve.alias 对象下,配置 @ 的指向路径
  resolve: {
    alias: {
      '@': join(__dirname, './src/')
    }
  }
}) 

3.配置ts

{
  "compilerOptions": {
    /* 新增以下两个配置项,分别是 baseUrl 和 paths */
    "baseUrl": ".",
    "paths": {
      "@/*": [
        "src/*"
      ]
    },
    "target": "ES2020",
    "useDefineForClassFields": true,
    "lib": [
      "ES2020",
      "DOM",
      "DOM.Iterable"
    ],
    "module": "ESNext",
    "skipLibCheck": true,
    /* Bundler mode */
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx",
    /* Linting */
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true
  },
  "include": [
    "src"
  ],
  "references": [
    {
      "path": "./tsconfig.node.json"
    }
  ]
} 

4.目录

main.tsx
  • 删除React.StrictMode

    • 只是为了确保组件由同样的输入输出

    <React.StrictMode>
        <App />
      </React.StrictMode>
App.jsx
  • 创建函数式组件

import React from "react";
​
//React.FC<T> 表示这是一个函数式的react组件
const App:React.FC = ()=>{
    console.log('渲染了App')
    return (
        <>
        <h1>这是App组件</h1>
        </>
    )
}
export default App

JSX

  • JSX表示在JS代码中写html结构,是react生命式的实现

  • JSX是将HTML语法直接加入到JavaScript代码中,在通过翻译器转化到纯JavaScript后由浏览器执行

  • 编译过程由Babel的JSX编译器实现

const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
    <h1>hello</h1>, 
)
//等价于(经过Babel的JSX编译后)
root.render(
    React.createElement("div",{
        id:"aaa",
        className:"bbb" //不能写成class
    },"111111")
)

JSX语法转化过程

  • JSX仅仅是createElement() 方法的语法糖(简化语法)

  • JSX语法被 @babel/preset-react 插件编译为createElement() 方法

  • React 元素: 是一个对象,用来描述你希望在屏幕上看到的内容

注意点

  • React元素的属性名使用驼峰命名法

  • 特殊属性名: class->className,for->htmlFor,tabindex->tabIndex

  • 没有子节点的React元素可以用/>结束(不能再html里这样!!)

  • 推荐使用小括号包裹JSX,从而避免js中自动插入分号陷阱

嵌入JS表达式

语法:{JavaScritp表达式}

  • 不要与vue插值表达式{{}}搞混

例子:

let content = '插入的内容'
let h1 = <h1>我是通过JSX创建的元素+ {content}</h1>
注意点
  • 只要是合法的js表达式都可以进行嵌入

  • JSX自身也是js表达式

  • 注意:js中的对象是一个例外,一般只会出现在style属性中

  • 注意:在{}中不能出现语句

JSX条件渲染

根据不同的条件来渲染不同的JSX结构

let isLoading = true
let loading = ()=>{
    if(isLoading){
        return <div>Loading...</div>
    }
    return <div>加载完成</div>
}

可以发现,写JSX的条件渲染与我们之前编写代码的逻辑是差不多的,根据不同的判断逻辑,返回不同的 JSX结构,然后渲染到页面中

JSX列表渲染

  • 如果要渲染一组数据,应该使用数组的map()方法

const songs = [
    {id:1,name:'痴心绝对'},
    {id:2,name:'像我这样的人'},
    {id:3,name:'南山南'}
]

const title = (
	<ul>
    	{songs.map(item => <li key={item.id}>{item.name}</<li>)}
    </ul>
)
  • 如果需要渲染一组数据,我们应该使用数组的 map () 方法

  • 注意:渲染列表的时候需要添加key属性,key属性的值要保证唯一

  • 原则:map()遍历谁,就给谁添加key属性

  • 注意:尽量避免使用索引号作为key

样式处理

1.行内样式--style

const list = (
	<h1 style={ {color:'red',backgroundColor:'skyblue'} }>
    
	</h1>
)

2.类名--className

const list = (
	<h1 className = "title">
        
	</h1>
)

Redux

类似vuex

else

v-dom 虚拟dom

v-node 虚拟节点

nrm

npm的仓库(配置淘宝镜像)

npm i -g nrm
nrm ls
nrm use taobao
  • 配置华为镜像比较好用

npm config set registry https://mirrors.huaweicloud.com/repository/npm/
​
  • 5
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值