React 快速入门

react#

JSX 语法#

几乎和 html 一样,有些属性名称不同

class => className
<div style={{ background: "red" }}>  # 内嵌样式

return (
        <ul>
          {list.map((item) => (
            <li key={item.id}>{item.name}</li>
          ))}
        </ul>
      )

组件#

函数组件(无状态组件)#
约定1:函数名字必须大写字母开头
约定2:函数必须有返回值
返回null表示不渲染内容
类组件(有状态组件)#

可以通过类属性(state)保存状态

约定1:类名必须大写字母开头
约定2:类组件必须继承React.Compont
约定3:类组件必须提供render(),返回值就是组件的jsx
import 使用#

如果组件是 export default 导出,就不用 {},一个模块中只能有一个默认导出 export default

如果是具名导出 export user; 就需要 {user}, 名字保持一致

import Home from './pages/Home'
import {user} from './pages/CityList'
import './index.scss'  // 直接引入样式文件

事件处理#

事件绑定#

在 jsx 中,对元素标签绑定事件

语法:on + 事件 ={事件处理程序}, 比如 onClick = {handleClick}

事件对象#

在事件处理程序中,可以设置第一个参数获取事件对象。react 事件对象兼容所有的浏览器,不需要额外处理

function handleClick(e){e.preventDefault()}
OnClick 函数传参#
<button onClick={jumpToDeposit(item)}>Deposit</button> # 这样写,button渲染的时候就执行了jumpToDeposit(),点击的时候不会执行
<button onClick={(e)=>{jumpToDeposit(item)}}>Deposit</button> # 这样写才行,button渲染时得到闭包函数,函数里面执行jumpToDeposit()

组件的 state#

只有类组件有 state,state 是私有的,只有组件内部能使用,this.state 获取当前主机的所有 state 数据。

修改 state 的值,setState () 作用:1. 修改 state 值,2. 更新 UI

this.setState({count:this.state.count+1})
//不能直接修改
this.state.count++ //错误

组件通信 ——props#

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

函数组件通过 props 参数实现组件数据接收,类组件通过 this.props 属性获取数据

props 是只读对象,不能修改

props 可以接受任意类型的数据

props.children 属性特指组件标签包含的子节点

props 可以设置默认值,

<Hello name="jack" age={19}>这是组件子节点</Hello>

func Hello(props) {
    return (
    <div>
        接收到数据{props.name}
        组件中的子节点{props.children}
        组件的默认值{props.pageSize}
    </div>
    )    
}

// 给组件添加默认值
Hello.defaultPros={
    pageSize:10
}
父组件传递数据给子组件#

父组件把传递的数据放在 state 中

给子组件添加属性,并且使用 state 赋值

子组件通过 props 获取数据

class Hello extends React.Component{    
    state = {
        list:[]
    }  
    render(){
        return (<Child list={this.state.list} />)
    }
}
子组件传递数据给父组件#

父组件通过回调函数接收数据,父组件先把回调函数传递给子组件。

子组件通过 props 获取父回调函数

子组件调用父回调函数,并且传递数据即可。

class Hello extends React.Component{ 
    receiveMsg =(data)=>{
        console.log("接收到数据", data)
    }
    render(){
        return (<Child receiveMsg={this.receiveMsg} />)
    }
}

class Child extends React.Component{
    state={
        msg:"hello world"
    }
    handleClick = ()=>{
        this.props.receiveMsg(this.state.msg)
    }
    render(){
        return (<div onClick={this.handleClick}>发送数据给父组件</div>)
    }
}
兄弟组件通信#

状态提升:把需要传递的数据放在共同的父组件中

父组件把状态传递给读子组件

父组件把修改状态方法传递给写子组件

跨组件通信#

Context 提供了一种在组件之间共享此类值的方式,而不必显式地通过组件树的逐层传递 props。

组件的生命周期#

只有类组件才有生命周期,每个阶段都有钩子函数,方便我们在不同时期做操作。

1. 创建时(加载时), 会执行三个函数

constructor() -> render() ->componentDidMount()
钩子函数 触发时机 一般使用
constructor 创建组件时 初始化 state
render() 每次组件渲染时触发 渲染 UI, 不能执行 setState ()
componentDidMount() 组件挂载(完成 DOM 渲染)后 1. 发送网络请求
2.DOM 操作
  1. 更新时

组件接收 props, setState (), forceUpdate () 执行这些操作时,会触发更新

render() -> componentDidUpdate()
钩子函数 触发时机 一般使用
render() 每次组件渲染时触发 渲染 UI, 不能执行 setState ()
componentDidMount() 组件挂载(完成 DOM 渲染)后 1. 发送网络请求
2.DOM 操作
3. 执行 setState 必须放在 if 条件中,不然会死循环

3. 卸载时

componentWillUnmount(), 一般是执行清理工作,比如定时器

组件复用#

比如复用 state 和操作 state 的方法

两种处理方法,

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

1.render props 模式

调用组件时,从外面传入 render 逻辑,不同的 render 逻辑,就可以实现同一个组件,但是不同的展示。实现了 state 共用,但是 ui 渲染不同的写法。

2. 高阶组件(HOC)

React 原理#

setState () 的说明#

这是异步函数,使用该语法,在 setState () 后面的代码注意 state 的值不会马上修改的。

setState () 会执行两个步骤,1. 修改 state 的值 2. 更新 render 渲染, 可以多次执行 setState (),但是 ui 只会触发一次重新渲染。

// 通过传入回调函数来更新state
this.setState((state,props)=>{
    return {count: state.count+1}
})
// 想在状态更新后并且重新渲染后,立即执行逻辑,可以在setState第二个参数传入callback
this.setState(
    (state,props)=>{},
    ()=>{
       document.title="更新state后" 
    }
)
JSX 语法的转化过程#

JSX 仅仅是 createElement () 方法的语法糖,JSX 语法会被 @babel/preset-react 插件编译为 createElement () 方法。

createElement () 方法返回一个 React 元素 (一个 js 对象),描述页面展示的内容。

组件更新机制#

父组件重新渲染时,也会重新渲染子组件。

组件性能优化#

减轻 state#

只存储组件渲染相关的数据,可以放到 this 对象中。

避免不必要的重新渲染#
shoudComponentUpdate(nextprops,nextState) //钩子函数,返回fasle表示组件不会重新渲染,可以对比this.state和nextState的值,判断有些情况是不用重新渲染的
虚拟 DOM 和 Diff 算法#

React 的 state 修改后,会重新渲染整个组件,实际在底层,只会修改变化的虚拟 DOM (就是 js 对象,描述 DOM 的一些属性),虚拟 DOM 可以让 React 脱离浏览器环境,做到跨平台应用。

React 路由#

现代的前端大多数 SPA (单页应用程序),为了有效的使用单个页面来管理原来多个页面的功能,前端路由应运而生。

前端路由功能:让用户从一个视图导航到另外一个视图。

前端路由是一套映射规则,在 React 中,URL 路径组件的对应关系

使用步骤#
yarn add react-router-dom

导入路由的三个核心组件 Router,Route,Link

import {BrowserRouter as Router, Routes, Route, Link} from 'react-router-dom'

使用 Router 包裹整个应用

<Router>
        <Link to="/first">页面一 </Link>
        <Link to="/second">页面二</Link>
        <Routes>
            <Route path="/first" element={<Page1/>} />
            <Route path="/second" element={<Page2/>} />
        </Routes>
</Router>

Link 传参,通过 useLocation 读取参数

<Link to='/params' state={{id:1}} />

// 接受路由参数
import {useLocation} from 'react-router-dom'
function UseParams() {
    const location = useLocation()
    console.log(location.state.id)
    return (
        <div>UseParams</div>
    )
}
export default UseParams
常用组件的说明#

BrowserRouter: 包裹整个应用,一个 React 应用只需要使用一次。

Link 组件:路由的入口,会被编译为一个 a 标签。

<Link to="/first" 页面一>

Route: 路由的出口,展示对应的组件的视图。

<Routes>
    <Route path="/first" element={<Page1/>} />
</Routes>
路由的执行过程#
  1. 点击 Link 会修改浏览器地址栏的 url
  2. React 路由监听浏览器的 url
  3. React 路由遍历所有的 Route 组件,使用路由规则(path)与 pathname 进行匹配
  4. 当路由规则能够找到对应的 pathname 是,就展示对应的 Route 组件的内容
编程式导航:#

通过 JS 代码实现页面跳转

嵌套路由#

页面中嵌套页面

layout 页面是一级路由,about,content 是二级路由

layout
    about
  content

创建一级路由页面 Layout

import {Outlet} from 'react-router-dom'

function Layout() {
    return (
        <div>
            Layout
            <Outlet/> // 二级路由出口
        </div>
    )
}

export default Layout;

定义嵌套路由

 // 1.定义嵌套路由
<Route path='/' element={<Layout />}>
    <Route path='about' element={<About />} />
    <Route path='content' element={<Content />} />
</Route>
  1. 默认二级路由

    添加 index 属性,删除自己的 path, 这样访问 / 时,默认显示的二级路由是 About

<Route path='/' element={<Layout />}>
    <Route index element={<About />} /> 
    <Route path='content' element={<Content />} />
</Route>
  1. 404 路由
<Routes>
   <Route path='/login' element={<Login/>} />
   {/* 所有路由都没有匹配时,显示nofound组件 */}
   <Route path='*' element={<NoFound />} />
 </Routes>
编程式导航#

在组件中进行路由跳转

// 1.import 跳转函数
import { useNavigate } from 'react-router-dom';
function Login() {
    // 2. 获取跳转函数
    const navigate = useNavigate()
    function goAbout() {
        // 3.执行跳转
        navigate('/about')
    }
    return (
        <div>
            Login
            <button onClick={goAbout}>跳转到about</button>
        </div>
    )
}
export default Login;

跳转传参数

1.searchParams传参
navigate(/about?id=1)
读取取参数
const [params] = useSearchParams() // 返回的是数组
let id = params.get('id')

2. 通过state传参
const jumpToDeposit = (item)=>{
    navigate('/deposit', {state:{id:item.ID,
      playA:item.PlayA,
      playB:item.PlayB,
      playAID:item.PlayAID,
      startTime:item.StartTime,
      contractAddr:item.ContractAddr
    }})
  }
读取参数
const location = useLocation()
console.log(location.state) // 读取到传递的对象
本作品采用《CC 协议》,转载必须注明作者和本文链接
走出舒适区