React学习笔记

React

一、React基础

1.React概述

1.1 什么是React
  • 官方文档

  • React是一个用于构建用户界面的JavaScript库

    • 用户界面:HTML页面(前端)
  • React主要是用来写HTML页面,或者构建Web应用

  • 如果从MVC的角度来说,React仅仅是视图层,也就是只负责视图的渲染,而并非提供了完整的M,C的功能

1.2React的特点
  • 声明式
  • 基于组件

2.React基础使用

2.1React的安装
  • npm i react react-dom
    • react 包是核心,提供创建元素、组件等功能
    • react-dom 包提供DOM相关功能等
2.2React的使用
  • 引入react和react-dom

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

    const title=React.createElement('h1',null,'HelloWorld!')
    //参数:元素名称,元素属性,元素子节点1,元素子节点2...
    React.createElement(img,{src:'xxx',style:'width:200px;'},'HelloWorld!')
    
  • 渲染React元素到页面中

    ReactDOM.render(title,document.getElementById('root'))
    //参数:要渲染的元素,元素挂载点
    

3.React脚手架的使用

3.1使用React脚手架初始化项目
  • 初始化项目 npx create-react-app 项目名称
    • npx 是 npm v5.2.0 引入的一条命令
    • 可以提升包内提供的命令行工具的使用体验
  • 启动项目 npm start
3.2在脚手架中使用React
  • 导入react 和 react-dom两个包

    import React from 'react'
    import ReactDOM from 'react-dom'
    
  • 调用 React.createElement()方法创建react元素

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

二、JSX

1.JSX的基本使用

1.1 createElement()的问题
  • 繁琐不简洁
  • 不直观,不能从代码中轻易的看出结构
  • 不优雅
1.2 JSX简介
  • JSXJavaScript XML的简写,表示在JavaScript代码中写**XML(HTML)**格式的代码。
  • 优势:
    • 声明式语法更加直观
    • 与HTML结构相同,降低了学习成本
    • 提升开发效率
1.3使用步骤
  • 使用JSX语法创建react元素

    const title = (
    	<h1>HelloWorld!</h1>
    )
    
  • 渲染元素 ReactDOM.render()

1.4为什么React脚手架中可以使用JSX语法
  • JSX不是标准的ECMAScript语法,它是ECMAScript的语法扩展
  • 需要使用babel编译处理后,才能在浏览器环境中使用
  • React脚手架中默认已经帮我们配置好了babel相关的配置,无需手动配置
  • 编译JSX语法的包为:@babel/preset-react
1.5注意事项
  • React元素的属性名使用驼峰命名法
  • class —> className for —> htmlFor tabindex —> tabIndex
  • 没有子节点的React元素可以用 />结束
  • 推荐 :使用小括号包裹JSX,从而避免JS中自动插入分号陷阱

2.JSX中使用JavaScript表达式

const test='haha'
const title = (
  <h1>HelloWorld! {test}</h1>
)
ReactDOM.render(title,document.getElementById('root'))

3.JSX的条件渲染

const show=(n)=>{
  if(n===1) return <p>n是1</p>
  else return <p>n不是1</p>
}
const title = (
  <div>
    <h2>{show(1)}</h2>
  </div>
)
ReactDOM.render(title,document.getElementById('root'))

4.JSX的列表渲染

const p = [
  {name:'张三',age:'18'},
  {name:'罗老师',age:'不知道'}
]
const test = (
  <div>
    <ul>
      {p.map(item=> <li>{item.name}---{item.age}</li>)}
    </ul>
  </div>
)
ReactDOM.render(test,document.getElementById('root'))

5.JSX的样式处理

import './index.css'

const test = (
  <div>
    //行内样式
    <p style={{color:"red", fontSize:"30px"}}>你好</p>  
	//类名
    <p className='test'>你好</p>
  </div>
)
ReactDOM.render(test,document.getElementById('root'))

三、组件基础

1.React组件介绍

  • 非常重要
  • 组件表示页面中的部分功能
  • 组合多个组件实现完整的页面功能
  • 特点
    • 可复用
    • 独立
    • 可组合

2.React组件的两种创建方式

  • 使用函数创建组件

    • 函数组件:使用函数(普通函数,箭头函数均可)创建的组件

    • 约定1:函数名称必须以大写字母开头

    • 约定2:函数组件必须有返回值,表示该组件结构

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

    • //创建一个组件
      const Test = ()=>(
      	<div>
      		<p style={{color:"red", fontSize:"30px"}}>你好</p>
      		<p>你好</p>
      	</div>
      )
      //渲染组件,以函数名为标签名
      ReactDOM.render(<Test/>,document.getElementById('root'))
      
  • 使用类创建组件

    • 类组件:使用ES6中的class创建的组件

    • 约定1:类名称首字母大写

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

    • 约定3:类组件必须提供**render()**方法

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

    • //创建一个类组件
      class Test extends React.Component { 
        render(){
          return (
            <div>
              <p style={{color:"red", fontSize:"30px"}}>你好</p>
              <p>你好</p>
            </div>
          )
        }
      }
      //渲染组件,以函数名为标签名
      ReactDOM.render(<Test/>,document.getElementById('root'))
      
  • 抽离组件到单独js文件中

    • 创建js文件

    • 在文件中导入React

    • 创建组件

    • 暴露组件

    • //在src/components/Test.js文件中
      import React from 'react'
      
      //创建并暴露一个组件
      export default class Test extends React.Component { 
          render(){
              return (
              <div>
                  <p style={{color:"red", fontSize:"30px"}}>你好</p>
                  <p>你好</p>
              </div>
              )
          }
      }
      
      //在index.js文件中
      import Test from './components/Test'
      
      ReactDOM.render(<Test/>,document.getElementById('root'))
      

3.React事件处理

  • 语法:on+事件名称={事件处理函数}

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

  • export default class Test extends React.Component {
        render(){
            return (
                <div>
                    <button onClick={this.testClick}>点我一下</button>
                </div>
            )
        }
        testClick(){
            alert('点我干嘛!')
        }
    }
    
  • 事件对象

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

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

      • 合成事件:兼容所有浏览器,无需担心跨浏览器兼容问题
    • export default class Test extends React.Component {
          render(){
              return (
                  <div>
                      <a onClick={this.testClick} href='https://www.baidu.com'>点我一下</a>
                  </div>
              )
          }
          testClick(e){
              alert('点我干嘛!')
              // 阻止默认行为
              e.preventDefault()
          }
      }
      

4.有状态组件和无状态组件

  • 函数组件 ====> 无状态组件
    • 函数组件没有自己的状态,只负责数据展示(静态)
  • 类组件 ====> 有状态组件
    • 类组件有自己的状态,负责更新UI(动态)

5.组件中的state和setState()

  • state即数据,是组件内部私有的

  • state是一个对象

  • export default class Test extends React.Component {
        // constructor(){
        //     super()
        //     //初始化state
        //     this.state={
        //         n:0
        //     }
        // }
        //简写
        state={
            n:0
        }
        render(){
            return (
                <div>
                    <h1>n:{this.state.n}</h1>
                    <button onClick={this.add}>点我n加一</button>
                </div>
            )
        }
        //用箭头函数,this才指向Test这个类!!!!!!!!
        add=()=>{
            //直接更改(this.state.n++)可以改变n的值,但是不会导致页面更新,需要使用setState()方法
            this.setState({n:this.state.n+1})
        }
    }
    

6.事件绑定this指向

  • 箭头函数

  • Function.prototype.bind()方法

    constructor(){
        super()
        this.test=this.test.bind(this)
    }
    
  • class的实例方法

    • 利用箭头函数形式的class实例方法
    //用箭头函数,this才指向Test这个类!!!!!!!!
    add=()=>{
        //直接更改(this.state.n++)可以改变n的值,但是不会导致页面更新,需要使用setState()方法
        this.setState({n:this.state.n+1})
    }
    

7.表单处理

7.1受控组件
  • 将表单元素的value值交给state管理

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

  • 使用

    export default class Test extends React.Component {
        state={
            mes:''
        }
        render(){
            return (
                <div>
                    <input type="text" value={this.state.mes} onChange={e => this.setState({mes:e.target.value})}/>
                </div>
            )
        }
    }
    
  • 多个表单优化

    export default class Test extends React.Component {
        state={
            mes:'',
            isChecked:false
        }
        render(){
            return (
                <div>
                    {/* 为表单元素添加name属性,值为state中的key值 */}
                    <input type="text" name="mes" value={this.state.mes} onChange={this.change}/>
                    <br/>
                    同意:<input type="checkbox" name="isChecked" checked={this.state.isChecked} onChange={this.change}/>
                </div>
            )
        }
        //事件处理函数
        change=e=>{
            let newvalue
            //特殊处理复选框
            if(e.target.type==='checkbox')newvalue=e.target.checked
            else newvalue=e.target.value
            this.setState({[e.target.name]:newvalue})
        }
    }
    
7.2非受控组件(DOM方式)(不推荐)
  • 借助ref,使用原生方式获取表单元素值

    export default class Test extends React.Component {
       constructor(){
           super()
           //创建ref
           this.mesRef=React.createRef()
       }
        render(){
            return (
                <div>
                    <input type="text" ref={this.mesRef}/><button onClick={this.getMes}>点我</button>
                </div>
            )
        }
        //获取文本框值
        getMes=()=>{
            alert(this.mesRef.current.value)
        }
    }
    

四、组件进阶

1.组件的props

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

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

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

    render(
    	return (
    		<Test data='xxx'/>  //通过属性传递数据
    	)
    )
    
  • 接收数据:

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

      function Test(props){
          console.log(props.data)
          return (...)
      }
      
    • 类组件通过this.props接收数据

      class Test extends React.Component {
          render(){
              console.log(this.props.data)
              return (...)
          }
      }
      
  • 注意!!!!!!!!!

    • 通过props可以传递任意类型数据

    • props的值是只读的

    • 使用类组件时,如果写了构造函数,应该将props传递个super(),否则,无法在构造函数中取到props!!!

      class Test extends React.Component{
          constructor(props){
              super(props)
              ...
          }
      }
      

2.组件通信的三种方式

  • 父组件 ==> 子组件
    • 父组件提供要传递的数据(state)
    • 给子组件标签添加属性,值为state中的数据
    • 子组件通过props接收父组件传递的数据
  • 子组件 ==> 父组件
    • 父组件向子组件传递一个回调函数,回调函数的参数为需要接收的数据
    • 子组件调用父组件传递的回调函数,将数据传递给父组件
  • 兄弟组件间
    • 状态提升:将共享的状态(即数据)提升到最近的公共父组件中,由公共的父组件管理这个状态
    • 公共的父组件提供数据及操作数据的方法
    • 子组件只需要通过接收数据或者调用方法去传递或者接收数据

3.Context

  • 作用:跨组件传递数据

  • 使用

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

      const {Provider,Consumer}=React.createContext()
      
    • 使用Provider组件作为父节点

      <Provider value={data}>  {/*将要传递的数据通过Provider标签的value属性传递*/}
      	<div className="App">
          	<Child1/>
          </div>
      </Provider>
      
    • 使用Consumer组件接收数据

      <Consumer>
      	{data => <p>{data}</p>}  {/*接收数据*/}
      </Consumer>
      

4.props深入

  • children属性:表示组件标签的子节点。当前组件标签有子节点时,props就会有该属性

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

    • 作用:捕获使用组件时因为props导致的错误,给出明确的错误提示,增加组件的健壮性

    • 使用:

      • 安装 prop-types npm i props-types

      • 导入 import PropTypes from 'prop-types'

      • 使用 组件名.propTypes={} 给组件的props添加校验规则

        Test.propTypes={
            test:PropTypes.array
        }
        
    • 约束规则:

      • 常见类型:array\bool\func\number\object\string
      • React 元素类型:element
      • 必填项:isRequired
      • 特定结构的对象:shape({})
      //必选
      test:PropTypes.类型.isRequired
      
      //特定结构对象
      test:PropTypes.shape({
          testc1:PropTypes.类型
          testc2:PropTypes.类型
          ...
      })
      
  • props默认值

    • 组件.defaultProps={}

5.组件的生命周期

5.1概述
  • 意义:有助于理解组件的运行方式、完成更加复杂的组件功能、分析组件错误原因等
  • 组件的生命周期:组件从被创建到挂载到页面中运行,再到组件不再用时卸载的过程
  • 生命周期钩子:生命周期中某一个重要阶段会有一些方法的调用,这些方法就叫做生命周期钩子
  • 生命周期钩子,为开发人员在不同阶段操作组件提供了时机
  • 只有类组件才有生命周期!
5.2生命周期的三个阶段

生命周期(简)
生命周期(全)
React组件生命周期官方图示

  • 在Commit阶段要避免调用setState等更新组件,如果这样会造成无限递归循化。

    • 在componentDidUpdate钩子中使用setState方法要添加限制条件

      //一般是比较当前props与上一次的props是否一样
      componentDidUpdate(prevProps){//可以通过该参数获取到上一次的props
          if(prevProps.data!==this.props.data){
              this.setState({...})
          }
      } 
      

6.render-props和高阶组件

  • render props模式

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

    • 使用该函数的返回值作为要渲染的UI内容(需要组件内部实现)

    • 使用

      • 创建复用组件,在组件中提供复用的状态及操作状态的方法

        class TestA extends React.Component{
            state={
                ...
            }
            //操作数据方法
            ...
        }
        
      • 在需要组件中定义回调函数接收相应的状态并返回相应的结构

        class App extends React.Component{
            fun=data=>{
                return (...)  //结构
            }
            //将该回调传递给复用组件
            render(){
                // 这里通常用children属性传递
                return(
                	<TestA children={this.fun}/>
                )
            }
        }
        
      • 在复用组件的render方法中调用props中接收到的render方法

        class TestA extends React.Component{
            ...
            //推荐为children添加校验
            propTypes={
                children:PropTypes.func.isRequired
            }
            render(){
                return this.props.children(this.state.data)
            }
            ...
        }
        
  • 高阶组件(HOC)

    • 原理:装饰模式

    • 高阶组件(HOC ,Higher-Order Component)是一个函数,接收要包装的组件,返回增强后的组件

    • 高阶组件内部创建一个类组件,在这个组件中提供复用的状态逻辑代码,通过props将复用的状态传递给被包裹的组件

    • 使用

      • 创建一个函数,名称约定以with开头

      • 指定函数参数,参数应该以大写字母开头(作为要渲染的组件)

      • 在函数内部创建一个类组件,提供复用的状态逻辑代码,并返回

      • 在该组件中,渲染参数组件,同时将状态通过props传递给参数组件

        function withTest(Example){
            class Test extends React.Component{
                state={
                    data
                }
            	//操作数据方法
            	...
            	render(){
                    return (
                        //通过children属性传递数据
                    	<Example children={...this.state.data}/>
                    )
                }
            }
            return Test  //返回装饰后的组件
        }
        
        //在需要组件中
        class Father extends React.Component{
            ...
            //推荐为children添加校验
            propTypes={
                children:PropTypes.func.isRequired
            }
            render(){
                return (...) //结构,其中使用props中的数据
            }
        }
        
        //调用高阶组件,装饰需要组件
        const FatherPlus = withTest(App)
        
        //在其他组件中渲染装饰后的需要组件
        <FatherPlus/>
        
      • 推荐为高阶组件设置displayName属性,便于在开发者工具等调试

        //使用高阶组件存在问题:得到的两个组件名称相同
        //原因:默认情况下,React使用组件名称作为displayName
        //为高阶组件设置displayName属性
        Test.displayName=`WithTest${getDisplayName(Example)}`
        function getDisplayName(Component){
            return Component.displayName || Component.name || 'Component'
        }
        
      • 推荐高阶组件往下传递props

        //问题:props丢失
        //原因:高阶组件没有向下传递props
        //解决方式,在高阶组件中渲染需要组件参数时,将state和this.props一起向下传递
        <Example children={...this.state.data} {...this.props}/>
        

五、React原理

1.setState()说明

  • setState更新数据是异步的!!!!!!!!!!!

  • 避免更新数据直接依赖上次更新后的数据

  • 连续多次调用setState(),只会触发一次页面重新渲染

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

    //这里的state永远是最新的state,但是仍然是异步的
    this.setState((state,props)=>{
        return {
            
        }
    })
    
  • setState有第二个参数,参数是一个回调函数,这个函数会在状态更新完成后立即执行。

2.JSX语法的转换过程

  • JSX实际上是createElement()方法的语法糖
  • JSX被@babel/preset-react插件编译为createElement()
  • createElement()会把内容转化为React元素(是一个对象,用来描述界面内容)

3.组件更新机制

  • 过程:父组件更新,会更新其组件子树

4.组件性能优化

  • 减轻state:只存储跟组件相关的数据

  • 避免不必要的重新渲染

    • 使用钩子函数 shouldComponentUpdate(nextProps,nextState) 返回值为bool,true:需要更新,flase:不需要更新
  • 纯组件(pureComponent)

    • 纯组件内部自动实现了shouldComponentUpdate钩子,不需要手动进行比较

    • 纯组件内部的对比是shallow compare(浅层对比)

      • 对于值类型没有影响
      • 对于引用类型,浅层对比只比较对象的引用是否相同(解决方式:创建新对象)

5.虚拟DOM和Diff算法

  • 只要状态改变,就重新更新视图
  • 虚拟DOM:本质上就是JS对象,用来描述你希望在屏幕上看到的内容(UI)
  • Diff算法:
    • 初次渲染时,React会根据state(Model),创建一个虚拟DOM对象(树)
    • 根据虚拟DOM生成真正的真实DOM,渲染到页面中
    • 当状态发送改变的时候,更新新的数据,创建新的虚拟DOM对象(树)
    • 根据diff算法,找到需要更新的地方,更新真实DOM

六、React路由

1.路由的基本使用

  • 安装 react-router-dom : npm i react-router-dom

  • index.js中引入BrowserRouter import {BrowserRouter} from 'react-router-dom'

  • 用BrowserRouter组件将整个应用包裹起来

    <BrowserRouter>
    	<App />
    </BrowserRouter>
    
    • 两种常用的Router:
      • HashRouter:使用URL的哈希值实现 (不美观,不优雅,有/#/)
      • BrowserRouter:使用H5的historyAPI实现
  • 引入Link ,Routes,Route import { Link,Routes,Route } from "react-router-dom"

  • 添加Link <Link to="/test">test</Link> (最终会解析成a标签)

    • 补充 NavLink组件,可以通过isActive实现显示路由的样式

      import { NavLink } from "react-router-dom";
      <NavLink className={({ isActive }) => isActive ? "red" : "blue"} />
      
  • 添加Routes及Route

    <Routes>
        <Route path="/" element={<App />} />  {/*path为'/'表示默认路由*/}
        <Route path="/test" element={<Test />} />
        <Route
          path="*"  {/*path为'*'表示其它未定义路由404 No Found*/}
          element={
            <main style={{ padding: "1rem" }}>
              <p>404 No Found!</p>
            </main>
          }
        />
    </Routes>
    

2.路由嵌套

<Routes>
    <Route path="/" element={<App />}>
        <Route index path="expenses" element={<Expenses />} />&nbsp;&nbsp;  {/*index 属性,让子级路由设为默认路由*/}
        <Route path="invoices" element={<Invoices />} />
    </Route>
</Routes>

//自适应渲染组件Outlet
//在App组件中
export default function App() {
  return (
    <div>
      <nav>
        <Link to="/invoices">Invoices</Link>&nbsp;&nbsp; 
        <Link to="/expenses">Expenses</Link>
      </nav>
      <Outlet /> {/*根据路径决定渲染哪个组件*/}
    </div>
  );
}

3.路由传参

3.1 useParams
  • 通过path传递参数 <Route path=":invoiceId" element={<Invoice />} />

  • 接收参数

    import { useParams } from "react-router-dom"
    
    export default function Invoice() {
      let params = useParams()
      return <h2>Invoice: {params.invoiceId}</h2>
    }
    
3.2 useSearchParams
  • 传递参数 <Route path="/test?id=1&num=2" element={<Test />} />

  • 接收参数

    import { useSearchParams } from "react-router-dom"
    
    export default function Test() {
      let [searchParams, setSearchParams] = useSearchParams();
      let id=searchParams.get('id')
      let num=searchParams.get('num')
      //gitAll方法也可以用于获取,但是麻烦,不推荐
      //let id=searchParams.getAll('id')[0]
    }
    
    //补充:setSearchParams() 参数为一个对象,用于设置URL
    setSearchParams({id:1,num:2})  
    //可以将路径设为当前路径下 '/test?id=1&num=2'
    

4.编程式路由及其传参

  • 使用useNavigate钩子进行路由跳转及传递参数

    import {useNavigate} from 'react-router-dom'
    
    let navigate=useNavigate()
    
    const test =() =>{
        navigate("/test",{state:{  //第一个参数表示路径,第二个表示参数,为一个对象,对象中的state属性携带参数
            id:1,
            num:2
        }})
    }
    
  • 使用useLocation接收参数

    import {useLocation} from 'react-router-dom'
    
    let location=useLocation()
    
    let {id,num} = location.state
    

5.常用组件及钩子

组件名作用说明
<Routers>一组路由代替原有<Switch>,所有子路由都用基础的Router children来表示
<Router>基础路由Router是可以嵌套的,解决原有V5中严格模式,后面与V5区别会详细介绍
<Link>导航组件在实际页面中跳转使用
<Outlet/>自适应渲染组件根据实际路由url自动选择组件
hooks名作用说明
useParams返回当前参数根据路径读取参数
useNavigate返回当前路由代替原有V5中的 useHistory
useOutlet返回根据路由生成的element
useLocation返回当前的location 对象
useRoutes同Routers组件一样,只不过是在js中使用
useSearchParams用来匹配URL中?后面的搜索参数
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

YFFlame

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值