NipGeihou's blog NipGeihou's blog
  • Java

    • 开发规范
    • 进阶笔记
    • 微服务
    • 快速开始
    • 设计模式
  • 其他

    • Golang
    • Python
    • Drat
  • Redis
  • MongoDB
  • 数据结构与算法
  • 计算机网络
  • 应用

    • Grafana
    • Prometheus
  • 容器与编排

    • KubeSphere
    • Kubernetes
    • Docker Compose
    • Docker
  • 组网

    • TailScale
    • WireGuard
  • 密码生成器
  • 英文单词生成器
🍳烹饪
🧑‍💻关于
  • 分类
  • 标签
  • 归档

NipGeihou

我见青山多妩媚,料青山见我应如是
  • Java

    • 开发规范
    • 进阶笔记
    • 微服务
    • 快速开始
    • 设计模式
  • 其他

    • Golang
    • Python
    • Drat
  • Redis
  • MongoDB
  • 数据结构与算法
  • 计算机网络
  • 应用

    • Grafana
    • Prometheus
  • 容器与编排

    • KubeSphere
    • Kubernetes
    • Docker Compose
    • Docker
  • 组网

    • TailScale
    • WireGuard
  • 密码生成器
  • 英文单词生成器
🍳烹饪
🧑‍💻关于
  • 分类
  • 标签
  • 归档
  • nodejs

  • css

  • 最佳实践

  • TypeScript

  • ajax

  • JavaScript

  • 前端工程化

  • React

    • React - 入门
      • 快速搭建
      • 最小环境
      • JSX
        • JS表达式
        • 列表渲染
        • 条件渲染
        • 复杂条件渲染
      • React的事件绑定
        • 基础实现
        • 使用事件参数
        • 传递自定义参数
        • 同时传递事件对象和自定义参数
      • 组件
        • 基础使用
      • 组件状态管理 - useState
        • 基础使用
        • 修改规则
        • 修改对象状态
        • 基础样式处理
      • 表单控制
        • 受控绑定(双向绑定)
        • 非受控绑定
      • 组件通信
        • 父子通信 - 父传子
        • 实现步骤
        • props说明
        • 特殊的prop-chilren
        • 父子通信 - 子传父
        • 兄弟通信
        • 跨层通信
      • 副作用管理-useEffect(生命周期)
        • 概念理解
        • 基础使用(渲染完毕事件、页面刷新事件)
        • 清除副作用(销毁事件)
      • 获取DOM - useRef
      • 自定义Hook实现
      • React Hooks使用规则
      • 渲染控制
        • 变量控制 - useMemo
        • 组件控制 - memo
        • 回调控制 - useCallback
      • 扩展库
        • Classnames类名控制
        • uuid
        • Day.js
        • craco
        • Normalize.css
    • React - Redux
    • ReactRouter
    • 规范
  • nextjs

  • Flutter

  • 笔记

  • 前端
  • React
NipGeihou
2024-08-12
目录

React - 入门

# 快速搭建

npx create-react-app <projectName>

cd <projectName>
npm start

# 最小环境

  • src 目录下最核心的 App.js 、 index.js ,其余都可以删掉

index.js:项目的入口

// 两个核心包
import React from 'react';
import ReactDOM from 'react-dom/client';
- import './index.css';
// 导入项目的根组件
import App from './App';
- import reportWebVitals from './reportWebVitals';

// 把App根组件渲染到id为root的dom节点上(在/public/index.html)
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
-  <React.StrictMode>
    <App />
-  </React.StrictMode>
);

- // If you want to start measuring performance in your app, pass a function
- // to log results (for example: reportWebVitals(console.log))
- // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
- reportWebVitals();

App.js :项目的根组件

- port logo from './logo.svg';
- port './App.css';

function App() {
  return (
    <div className="App">
-     <header className="App-header">
-       <img src={logo} className="App-logo" alt="logo" />
-       <p>
-         Edit <code>src/App.js</code> and save to reload.
-       </p>
-       <a
-         className="App-link"
-         href="https://reactjs.org"
-         target="_blank"
-       rel="noopener noreferrer"
-       >
-         Learn React
-       </a>
-     </header>
+		this is App
    </div>
  );
}

export default App;

# JSX

快速入门 – React 中文文档 (opens new window)

概念:JSX 是 JavaScript 和 XML (HTML) 的缩写,表示在 JS 代码中编写 HTML 模版结构,它是 React 中编写 UI 模版的方式

优势:

  1. HTML 的声明式模版写法
  2. JS 的可编程能力

JSX 的本质

JSX 并不是标准的 JS 语法,它是 JS 的语法扩展,浏览器本身不能识别,需要通过解析工具做解析之后才能在浏览器中运行

可在 Babel・The compiler for next generation JavaScript (opens new window) 体验解析过程

# JS 表达式

const message = 'this is message'

function getAge(){
  return 18
}

function App(){
  return (
    <div>
      <h1>this is title</h1>
      {/* 字符串识别 */}
      {'this is str'}
      {/* 变量识别 */}
      {message}
      {/* 变量识别 */}
      {message}
      {/* 函数调用 渲染为函数的返回值 */}
      {getAge()}
    </div>
  )
}

# 列表渲染

const list = [
  {id:1001, name:'Vue'},
  {id:1002, name: 'React'},
  {id:1003, name: 'Angular'}
]

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

# 条件渲染

const flag = true
const loading = false

function App(){
  return (
    <>
      {flag && <span>this is span</span>}
      {loading ? <span>loading...</span>:<span>this is span</span>}
    </>
  )
}

# 复杂条件渲染

const type = 1  // 0|1|3

function getArticleJSX(){
  if(type === 0){
    return <div>无图模式模版</div>
  }else if(type === 1){
    return <div>单图模式模版</div>
  }else(type === 3){
    return <div>三图模式模版</div>
  }
}

function App(){
  return (
    <>
      { getArticleJSX() }
    </>
  )
}

# React 的事件绑定

# 基础实现

React 中的事件绑定,通过语法 on + 事件名称 = { 事件处理程序 } ,整体上遵循驼峰命名法

function App(){
  const clickHandler = ()=>{
    console.log('button按钮点击了')
  }
  return (
    <button onClick={clickHandler}>click me</button>
  )
}

# 使用事件参数

在事件回调函数中设置形参 e 即可

function App(){
  const clickHandler = (e)=>{
    console.log('button按钮点击了', e)
  }
  return (
    <button onClick={clickHandler}>click me</button>
  )
}

# 传递自定义参数

语法:事件绑定的位置改造成箭头函数的写法,在执行 clickHandler 实际处理业务函数的时候传递实参

function App(){
  const clickHandler = (name)=>{
    console.log('button按钮点击了', name)
  }
  return (
    <button onClick={()=>clickHandler('jack')}>click me</button>
  )
}

注意

注意:不能直接写函数调用 onClick={clickHandler('jack')} ,这里事件绑定需要一个函数引用

# 同时传递事件对象和自定义参数

语法:在事件绑定的位置传递事件实参 e 和自定义参数,clickHandler 中声明形参,注意顺序对应

function App(){
  const clickHandler = (name,e)=>{
    console.log('button按钮点击了', name,e)
  }
  return (
    <button onClick={(e)=>clickHandler('jack',e)}>click me</button>
  )
}

# 组件

# 基础使用

// 1. 定义组件
function Button(){
  return <button>click me</button>
}

// 2. 使用组件
function App(){
  return (
    <div>
      {/* 自闭和 */}
      <Button/>
      {/* 成对标签 */}
      <Button></Button>
    </div>
  )
}

# 组件状态管理 - useState

# 基础使用

function App(){
  const [ count, setCount ] = React.useState(0)
  return (
    <div>
      <button onClick={()=>setCount(count+1)}>{ count }</button>
    </div>
  )
}

# 修改规则

在 React 中状态被认为是只读的,我们应该始终替换它而不是修改它,直接修改状态不能引发视图更新

const [ count, setCount ] = React.useState(0)

const handleClick() =>{
	// count++ // 直接修改,视图无法更新
    setCount(count +1)
}

# 修改对象状态

const [ form, setForm ] = React.useState({
    name: 'jack',
})

const handleChangeName= ()=>{
    // form.name = 'john'  // 错误操作!!
    
    setForm({
        ...form,
        name: 'john',
    })
    
}

# 基础样式处理

方式一:行内样式(不推荐)

App.js

<div style={{ color:'red'}}>this is div</div>

方式二:class 类名控制(推荐)

index.css

.foo{
  color: red;
}

App.js

import './index.css'

function App(){
  return (
    <div>
      <span className="foo">this is span</span>
    </div>
  )
}

# 表单控制

# 受控绑定(双向绑定)

image-20240812102911170

function App(){
  const [value, setValue] = useState('')
  return (
    <input 
      type="text" 
      value={value} 
      onChange={e => setValue(e.target.value)}
    />
  )
}

# 非受控绑定

获取 DOM,使用 useRef 钩子函数

function App(){
  const inputRef = useRef(null)  // 1

  const onChange = ()=>{
    console.log(inputRef.current.value)  // 3 渲染完毕之后才可用
  }
  
  return (
    <input 
      type="text" 
      ref={inputRef}
      onChange={onChange}  // 2
    />
  )
}

# 组件通信

# 父子通信 - 父传子

# 实现步骤

  1. 父组件传递数据 - 在子组件标签上绑定属性
  2. 子组件接收数据 - 子组件通过 props 参数接收数据(属性传递)
function Son(props){
  return <div>{ props.name }</div>
}


function App(){
  const name = 'this is app name'
  return (
    <div>
       <Son name={name}/>
    </div>
  )
}

# props 说明

props 可以传递任意的合法数据,比如 数字、字符串、布尔值、数组、对象、函数、JSX

image-20240812112429110

props 是只读对象 子组件只能读取 props 中的数据,不能直接进行修改,父组件的数据只能由父组件修改

# 特殊的 prop-chilren

场景:当我们把内容嵌套在组件的标签内部时,组件会自动在名为 children 的 prop 属性中接收该内容

image-20240812112544822

使用 ts 定义时为 React.ReactNode 类型

# 父子通信 - 子传父

image-20240812173156567

核心思路:在子组件中调用父组件中的函数并传递参数

function Son({ onGetMsg }){
  const sonMsg = 'this is son msg'
  return (
    <div>
      {/* 在子组件中执行父组件传递过来的函数 */}
      <button onClick={()=>onGetMsg(sonMsg)}>send</button>
    </div>
  )
}


function App(){
  const getMsg = (msg)=>console.log(msg)
  
  return (
    <div>
      {/* 传递父组件中的函数到子组件 */}
       <Son onGetMsg={ getMsg }/>
    </div>
  )
}

# 兄弟通信

image-20240812174113894

实现思路:借助 状态提升 机制,通过共同的父组件进行兄弟之间的数据传递

  1. A 组件先通过子传父的方式把数据传递给父组件 App
  2. App 拿到数据之后通过父传子的方式再传递给 B 组件

// 1. 通过子传父 A -> App
// 2. 通过父传子 App -> B

import { useState } from "react"

function A ({ onGetAName }) {
  // Son组件中的数据
  const name = 'this is A name'
  return (
    <div>
      this is A compnent,
      <button onClick={() => onGetAName(name)}>send</button>
    </div>
  )
}

function B ({ name }) {
  return (
    <div>
      this is B compnent,
      {name}
    </div>
  )
}

function App () {
  const [name, setName] = useState('')
  const getAName = (name) => {
    setName(name) // 2
  }
  return (
    <div>
      this is App
      <A onGetAName={getAName} /> // 1
      <B name={name} />  // 3
    </div>
  )
}

export default App

# 跨层通信

image-20240812175005856

实现步骤:

  1. 使用 createContext 方法创建一个上下文对象 Ctx
  2. 在顶层组件(App)中通过 Ctx.Provider 组件提供数据
  3. 在底层组件(B)中通过 useContext 钩子函数获取消费数据

笔记

创建一个全局的上下文对象 createContext() ,通过它来传递数据

// App -> A -> B

import { createContext, useContext } from "react"

// 1. createContext方法创建一个上下文对象
const MsgContext = createContext()

function A () {
  return (
    <div>
      this is A component
      <B />
    </div>
  )
}

function B () {
  // 3. 在底层组件 通过useContext钩子函数使用数据
  const msg = useContext(MsgContext)
  
  return (
    <div>
      this is B compnent,{msg}
    </div>
  )
}

function App () {
  const msg = 'this is app msg'
  return (
    <div>
      {/* 2. 在顶层组件 通过Provider组件提供数据 */}
      <MsgContext.Provider value={msg}>
        this is App
        <A />
      </MsgContext.Provider>
    </div>
  )
}

export default App

# 副作用管理 - useEffect(生命周期)

# 概念理解

useEffect 是一个 React Hook 函数,用于在 React 组件中创建不是由事件引起而是由渲染本身引起的操作(副作用), 比 如发送 AJAX 请求,更改 DOM 等等

image-20240812183359516

注意

说明:上面的组件中没有发生任何的用户事件,组件渲染完毕之后就需要和服务器要数据,整个过程属于 “只由渲染引起的操作”

# 基础使用(渲染完毕事件、页面刷新事件)

需求:在组件渲染完毕之后,立刻从服务端获取平道列表数据并显示到页面中

useEffect( () => {})
useEffect( () => {},[])
useEffect( () => {},[count])

说明:

  • 参数 1 是一个函数,可以把它叫做副作用函数,在函数内部可以放置要执行的操作
  • 参数 2 是一个数组(可选参),在数组里放置依赖项,不同依赖项会影响第一个参数函数的执行,当是一个空数组的时候,副作用函数只会在组件渲染完毕之后执行一次
    • 没有依赖项 不传 :组件初始渲染 + 组件更新(任何 state 更新都会触发)时执行,
    • 空数组依赖 [] :只在初始渲染时执行一次
    • 添加特定依赖项 [state变量] :组件初始渲染 + 依赖项变化(特定 state 更新都会触发)时执行

# 清除副作用(销毁事件)

import { useEffect, useState } from "react"

function Son () {
  // 1. 渲染时开启一个定时器
  useEffect(() => {
    const timer = setInterval(() => {
      console.log('定时器执行中...')
    }, 1000)

    // 相当于是组件的finally事件函数
    return () => {
      // 清除副作用(组件卸载时)
      clearInterval(timer)
    }
  }, [])
  return <div>this is son</div>
}

function App () {
  // 通过条件渲染模拟组件卸载
  const [show, setShow] = useState(true)
  return (
    <div>
      {show && <Son />}
      <button onClick={() => setShow(false)}>卸载Son组件</button>
    </div>
  )
}

export default App

# 获取 DOM - useRef

const domRef = useRef<HTMLInputElement>(null)

useEffect(()=>{
    domRef.current?.foucus()
},[])

...
<input ref{domRef} />

# 自定义 Hook 实现

概念:自定义 Hook 是以 use打头的函数 ,通过自定义 Hook 函数可以用来 实现逻辑的封装和复用

封装自定义 hook 通用思路

  1. 声明一个以 use 打头的函数
  2. 在函数体内封装可复用的逻辑(只要是可复用的逻辑)
  3. 把组件中用到的状态或者回调 return 出去(以对象或者数组)
  4. 在哪个组件中要用到这个逻辑,就执行这个函数,解构出来状态和回调进行使用
// 封装自定义Hook

// 问题: 布尔切换的逻辑 当前组件耦合在一起的 不方便复用

// 解决思路: 自定义hook

import { useState } from "react"

+function useToggle () {
+  // 可复用的逻辑代码
+  const [value, setValue] = useState(true)
+
+  const toggle = () => setValue(!value)
+
+  // 哪些状态和回调函数需要在其他组件中使用 return
+  return {
+    value,
+    toggle
+  }
+}


function App () {
-  const [value, setValue] = useState(true)
-  const toggle = () => setValue(!value)
    
+  const { value, toggle } = useToggle()
  return (
    <div>
      {value && <div>this is div</div>}
      <button onClick={toggle}>toggle</button>
    </div>
  )
}

export default App

# React Hooks 使用规则

  1. 只能在组件中或者其他自定义 Hook 函数中调用
  2. 只能在组件的顶层调用,不能嵌套在 if、for、其它的函数中

# 渲染控制

# 变量控制 - useMemo

当页面中有任意 state 变化时,页面都会触发重新渲染,进而导致所有值都需要重新计算,存在一种场景,页面中有一变量计算十分耗时,但在页面操作中都不会导致这个值变化,可使用 useMemo 函数缓存计算的值

const result = useMemo(()=>{
 return fib(count1)
},[count1])
// 只有在count1变化时才会触发计算

# 组件控制 - memo

场景:某个子组件渲染开销大,默认下操作父组件时,会导致子组件重新渲染。

const MemoSon = memo(function Son(){
    return <div>this is son</div>
})

...
<div className="App">
	<MemoSon/>
</div>

只有子组件的 prop 改变时才会渲染

# 回调控制 - useCallback

const changeHandler = useCallback((value) => console.log(value),[])

# 扩展库

# Classnames 类名控制

JedWatson/classnames: A simple javascript utility for conditionally joining classNames together (opens new window)

让 active calss 变得更优雅

npm install classnames

App.js

+const classNames = require('classnames');
...

<span
    key={item.key}
-    className={`nav-item ${type === item.type && 'active'}`}
+    className={classNames('nav-item',{active: type === item.type})}
    >
    {item.text}
</span>

# uuid

uuidjs/uuid: Generate RFC-compliant UUIDs in JavaScript (opens new window)

npm install uuid
import { v4 as uuidv4 } from 'uuid';
uuidv4(); // ⇨ '9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d'

# Day.js

Day.js・中文文档 - 2kB 大小的 JavaScript 时间日期库 (opens new window)

npm install dayjs
const dayjs = require('dayjs')
//import dayjs from 'dayjs' // ES 2015
dayjs().format()

# craco

Configure CRA without ejecting. | CRACO (opens new window)

npm i -D @craco/craco

/craco.config.js

设置 @ 别名

const path = require('path');

module.exports = {
  webpack: {
    alias: {
      '@': path.resolve(__dirname, 'src/'),
    }
  }
}

package.json :修改启动配置

"scripts": {
-  "start": "react-scripts start"
+  "start": "craco start"
-  "build": "react-scripts build"
+  "build": "craco build"
-  "test": "react-scripts test"
+  "test": "craco test"
}

# Normalize.css

Normalize.css: Make browsers render all elements more consistently. (opens new window)

浏览器默认存在一些默认样式,如 body 的边距,使用此库可方便清除。

npm install normalize.css

src/index.js

...
import 'normalize.css


...

src/index.scss

html,
body {
  margin: 0;
  height: 100%;
}

#root {
  height: 100%;
}

这个配置的作用域是 /public/index.html ,里面有一个 #root 元素,想要它 100%,那么需要先让他的父节点 100%

上次更新: 2024/08/28, 15:20:29
vite
React - Redux

← vite React - Redux→

最近更新
01
Docker Swarm
04-18
02
安全隧道 - gost
04-17
03
Solana最佳实践
04-16
更多文章>
Theme by Vdoing | Copyright © 2018-2025 NipGeihou | 友情链接
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式