【前端学习】React学习【万字总结】

React学习

React组件与jsx

使用jsx编写,最纯粹的js开发

React组件可以是一个方法,也可以是一个class

可以直接在js文件里写一段html来作为一个组件,也可以写成一个单独的jsx或者js文件

import logo from './logo.svg'; // 导入一个 SVG 格式的 logo 图片
import './App.css' // 导入组件的样式文件,为当前组件添加视觉样式

function App() { // App组件

return ( // JSX 结构,描述页面 UI

<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>

</div>

);

}

export default App; // 是 ES6 模块系统中的语法,作用是将 App 这个组件设置为当前模块(即 App.js 文件)的默认导出项,方便其他模块导入使用

这是一个 函数式组件 (React 中定义组件的方式之一),组件的 UI 由返回的 JSX(JavaScript XML,一种类似 HTML 的语法,用于描述 React 组件的结构)描述

function App() {

return (

// JSX 结构,描述页面 UI

);

}

React组件分类

  1. 函数组件(配合Hook使用)
  1. Hello() {
    return (
    <div>
    <p> hello**</p>**
    </div>
    );
    }
  1. Class组件
  1. Hello extends React.Component { // 这个类继承自 React 内置的 Component 类
    render() {
    return ( // 返回组件要渲染的 UI 结构(通过 JSX 描述),这里返回的 JSX 结构是:一个 <div> 容器,里面包含一个 <p> 标签,标签内的文本是 hello
    <div>
    <p> hello**</p>**
    </div>
    )
    }
    }

React 组件的命名惯例是首字母大写

jsx的特点

  1. 可以直接写在js文件中
  1. 项目利用babel做了对js的编译,所以可以直接在js里写jsx的
  1. 写法接近js
  1. 几乎和js一样,不同点就在于,可以更方便地写html在js里

初始化react项目

创建react项目

npx create-react-app <projectName>

npx 是 npm 5.2版本后自带的一个工具,让我们可以不用安装脚手架直接去使用的一些包

npx之前:如果你想使用某个 Node 包的可执行命令(比如 create-react-app 来创建 React 项目),通常有两种麻烦的方式:

  1. 全局安装 :执行 npm install -g create-react-app,然后再运行 create-react-app my-app。但这样会导致:全局空间被占用,时间久了容易堆积大量 "只偶尔用一次" 的工具;不同项目可能需要不同版本的工具,全局安装无法灵活切换。
  2. 本地安装 + 手动执行:先在项目里 npm install create-react-app,再去 node_modules/.bin 目录里找可执行文件运行,步骤繁琐。

npx 的核心是 "临时执行,用完即走"

  1. 运行 npx <命令> 时,它会先在本地项目的 node_modules/.bin 目录中查找对应的可执行文件;
  2. 如果本地没有,就会去远程 npm 仓库下载对应的包(临时下载,执行完不会残留全局安装的包);
  3. 执行命令后,临时下载的包会被自动清理,不会占用本地空间。

创建完成后会得到如下内容:

默认目录结构

  1. node_modules :存放项目的所有依赖包(通过 npm install 安装的第三方库都会存放在这里),是项目运行的依赖基础
  2. public :存放静态资源 ,这些资源不会被 Webpack 编译,直接以原始形式被浏览器访问。常见内容包括:
    • index.html:React 应用的HTML 模板(应用最终会被注入到这个文件的 <div id="root"></div> 中);
    • favicon.ico:浏览器标签页的图标;
    • 其他静态文件(如图片、第三方脚本等)
  3. src:存放项目源码,是开发的核心区域,包含组件、样式、入口文件等。典型内容有:
    • index.js:应用的入口文件,负责将根组件渲染到 public/index.html 的 root 节点中;
    • 各种 React 组件(如 App.js);
    • 样式文件(如 index.css、App.css);
    • 资源文件(如图片、SVG 等)。
  4. .gitignore :配置 Git 版本控制的 "忽略规则",指定哪些文件 / 目录不需要被 Git 追踪
  5. package-lock.json锁定依赖版本的文件,确保在不同环境(如开发机、测试机、生产机)中安装的依赖版本完全一致,避免 "依赖版本不一致导致的 Bug"
  6. package.json :项目的核心配置文件 ,包含:
    • 项目信息(名称、版本、描述等);
    • 依赖包列表(dependencies 是生产依赖,devDependencies 是开发依赖);
    • 脚本命令(如 start、build、test 等,可通过 npm run <脚本名> 执行)'
  7. README.md :项目的说明文档,通常包含项目介绍、安装步骤、运行方式、功能说明等信息,方便其他开发者了解和使用项目

index.html 和 index.js 是启动应用的核心文件

  1. public/index.html:HTML 模板载体:位置 :位于 public 目录下。作用:是 React 应用在浏览器中渲染的 "容器页面"
    1. 它包含一个关键的 <div id="root"></div>,React 应用的所有组件最终都会注入到这个 div ,形成完整的页面 UI。
    2. 还可用于配置页面元信息(如 <meta> 标签、标题)、静态资源(如 favicon.ico、第三方脚本)等
    3. <!DOCTYPE html> // 声明文档类型为 HTML5,用于规范浏览器的解析行为
      <html >
      <head > // 用于定义页面的元信息(不直接显示在页面主体的内容)
      <title >React App</title > // 设置浏览器标签页的标题
      </head >
      <body > // 包含页面的可见内容
      <!-- React 应用的注入点 -->
      <div id="root"></div > // React 应用的挂载点,React 通过 JavaScript 代码生成的所有组件、交互内容,最终都会被 "注入" 到这个 div 中,成为页面的实际内容。
      </body >
      </html >
  2. src/index.js:应用入口与渲染桥梁:位置 :位于 src 目录下。作用:是 React 应用的 "启动入口" ,负责将 React 组件渲染到index.html中。
    1. 它通过 ReactDOM.render() 方法,把根组件(通常是 App.js)"挂载" 到 index.html 的 root 节点上。

    2. 是连接 "React 组件逻辑" 和 "HTML 页面" 的桥梁,应用的执行流程从这里开始。

    3. import React from 'react'; // 导入 react 库的核心模块
      import ReactDOM from 'react-dom/client'; // 导入 react-dom/client 模块,它提供了将 React 组件渲染到浏览器 DOM 中的工具
      import App from './App'; // 导入根组件

      const root = ReactDOM.createRoot(document.getElementById('root')); // 获取 HTML 页面中 id="root" 的 DOM 元素,基于这个 DOM 元素创建一个 React 根节点(root),后续所有 React 组件都会通过这个根节点渲染到页面上
      root.render( // 调用根节点的 render 方法,将组件渲染到 DOM 中
      <React.StrictMode> // React 的 "严格模式"
      <App /> // 被渲染的根组件,App 组件的内容会被解析为 DOM 元素,并最终显示在 id="root" 的 div 中
      </React.StrictMode>
      );

当启动 React 项目时:

  1. Webpack 会从 src/index.js 开始打包所有源码;
  2. index.js 会把根组件 <App /> 渲染到 public/index.html 的 root 节点中;
  3. 最终在浏览器中,你看到的页面就是 index.html 承载的、由 React 组件渲染出的完整 UI。

Webpack 是一个前端模块打包工具 ,核心作用是将项目中分散的JS、CSS、图片、字体 等各类资源(都视为 "模块"),经过处理后打包成一个或多个静态文件 ,从而优化前端项目的加载性能、管理模块依赖,并支持各种高级特性。它会从一个 "入口文件"开始,递归分析所有依赖的模块,然后将这些模块合并、转换、优化 **,最终输出少数几个(甚至一个)"打包后的文件",让浏览器可以高效加载。

像 Create React App、Vue CLI 这类脚手架工具,底层都是基于 Webpack 封装的。它们把 Webpack 的复杂配置隐藏起来,让开发者可以 "开箱即用",但核心的打包、资源处理逻辑还是由 Webpack 完成的。

  1. 在App.js中创建App组件:
  2. 在index.js中引入App组件
  3. 将App组件渲染到index.html中

package.json文件:

"scripts": {

"start": "react-scripts start",

"build": "react-scripts build",

"test": "react-scripts test",

"eject": "react-scripts eject"
}

package.json 中用于定义npm 脚本命令的配置

  1. start :执行 react-scripts start启动本地开发服务器,开启热更新功能。开发时运行 npm run start,可以在浏览器中实时预览代码修改的效果。
  2. build :执行 react-scripts build构建生产环境代码。它会对项目进行压缩、优化、打包,最终生成可部署的静态文件(存放在 build 目录),这些文件可直接部署到服务器上运行。
  3. test :执行 react-scripts test启动测试运行器(默认是 Jest),用于执行项目中的单元测试和集成测试,确保代码逻辑的正确性。
  4. eject :执行 react-scripts eject这是一个不可逆操作 。Create React App 封装了 Webpack、Babel 等工具的配置,执行 npm run eject 会将所有隐藏的配置文件暴露出来,让你可以完全自定义构建流程。但执行后无法再回到 "封装状态",需谨慎使用。

运行react项目

npm run start

启动本地开发服务器,跑开发模式

在App.js中定义的内容

React的state

state是组件要用的数据

前端框架(React、Vue)实现 "数据响应式展示 :前端的核心任务之一是把从后端(或其他渠道)获取的数据呈现到页面上,且当数据更新时,页面要自动响应变化(无需手动操作 DOM)

这类框架的价值就在于 "让数据和页面联动"------ 把请求到的数据渲染到页面,且数据更新时,页面能自动同步新数据

React 中的state是组件的 "响应式数据容器",当state里的数据变化时,组件会重新渲染,页面随之更新。

Vue 中的data是实例的 "响应式数据对象",当data里的数据变化时,Vue 会自动更新对应的 DOM。

两者作用一致:让数据变化驱动页面自动更新,实现 "响应式" 的交互体验。

src/components文件夹放组件

src/components/test1.jsx文件放test1组件;src/components/test2.jsx文件放test2组件(组件写成js也是OK的)

test1.jsx文件写函数组件

  1. 组件首字母必须大写
  2. App.js文件中引入test1组件
  1. './App.css';
    import Test1 from "./components/test1";

    function App() {
    return (
    <div className="App">
    <Test1></Test1>
    </div>
    );
    }

    export default App;

  1. state的编写需要结合Hook,使用 useState Hook 管理组件状态;useState 是 React 提供的状态 Hook ,用于在函数组件中声明 "响应式状态";objA:状态变量,初始值是一个对象{ a: 123 };setA:更新状态的函数,当调用 setA(newValue) 时,会修改 objA 的值,并触发组件重新渲染;普通变量的修改不会触发组件重新渲染,页面上显示的 b 值不会更新;而状态变量通过 setA 修改后,会自动重新渲染组件,页面同步更新
  1. React, { useState } from "react" // 导入 React 核心库和 useState Hook
    //函数组件
    function Test1() {
    let [objA, setA] = useState({
    a: 123 // objA:状态变量
    })
    let b = "i am b"; // 普通的 JavaScript 变量
    return ( // 组件返回的 JSX 结构,定义了页面要显示的内容
    <div>
    <p> {objA.a}</p>
    <p> {b}</p>
    </div>
    )
    }
    export default Test1 // 将 Test1 组件设置为默认导出

test,jsx文件写class组件

  1. 继承React的Component,所以要引入React
  2. 写一个构造函数,构造函数是类组件初始化时第一个被调用的方法 ,用于初始化组件的状态(state)、绑定事件处理函数等;接收父组件传递给当前组件的属性(props),用于组件间的数据传递;super() 用于调用父类(React.Component)的构造函数,这是子类构造函数中必须执行的步骤 ,传递 props 作为参数,是为了在构造函数中能通过 this.props 访问父组件传递的属性;this.state 是类组件用于存储内部可变数据的对象,当 state 中的数据发生变化时,组件会自动重新渲染(响应式更新)。
  3. React 类组件必须实现 render() 方法(用于返回要渲染的 JSX 结构),否则组件无法正常渲染到页面
  4. App.js文件中也引入test2组件
  1. './App.css';
    import Test1 from "./components/test1";
    import Test2 from "./components/test2";

    function App() {
    return (
    <div className="App">
    <Test1></Test1>
    <Test2></Test2>
    </div>
    );
    }

    export default App;

  2. React from "react";
    class Test2 extends React.Component {
    constructor(props) {
    super (props);
    this .state = {
    a: 111,
    arr: [1, 23, 3]
    }
    }

    render() {
    return (
    <div>
    <p> {this .state.a}</p>
    </div>
    );
    }
    }

    export default Test2

state的特点:

  1. 直接修改数据是无效的,必须通过对应的方法修改 state 才能触发视图更新;在 React 中,无论是类组件的 this.state 还是函数组件的 useState 状态直接修改状态变量本身不会触发组件重新渲染
  • useState 返回的 "更新函数"(如 setCount);类组件:使用 this.setState() 方法;
  1. 是通过 "状态更新方法" 来感知状态变化的,只有通过这些方法,React 才会触发组件重新渲染,进而更新页面视图。
  1. State 的修改是一个浅合并的过程,State 的修改是与原 state 对象合并的过程;当修改 state 时,React 会将新状态与原状态进行 "浅层次合并"(只更新状态中指定的属性,未被指定的属性会保留原状态,且仅合并第一层属性)。

错误的:

import React, { useState } from "react"
//函数组件
function Test1() {

let [objA, setA] = useState({

a: 123

})

let b = "i am b";

setTimeout(()=> { // 错误的状态更新方式,必须通过 useState 返回的更新函数(如 setA)修改状态,直接修改状态变量 objA 不会触发组件重新渲染,页面也不会更新

objA.a=999

},1000)

return (

<div>

<p> {objA.a}</p>

<p> {b}</p>

</div>

)

}
export default Test1

import React from "react";
//class组件
class Test2 extends React.Component {

constructor(prop) {

super (prop);

this .state = {

a: 111,

arr: [1, 23, 3]

}

}

render() {

setTimeout(()=> { // 错误的状态更新方式,在 React 类组件中,不能直接修改 this.state,这种操作不会触发组件重新渲染,页面也不会同步更新

this .state.a = 888

}, 1000)

return (

<div>

<p> {this .state.a}</p>

</div>

)

}

}
export default Test2

正确的:

import React, { useState } from "react"

function Test1() {

const [objA, setA] = useState({

a: 123

})

const b = "i am b";

setTimeout(()=> {

// 通过 setA 函数更新状态,触发重新渲染;浅合并:prev 代表原状态对象,...prev 是 ES6 展开运算符,用于复制原状态的所有第一层属性;然后用 a: 999 覆盖原 a 属性

setA(prev => ({ ...prev, a: 999 }))

},1000)

return (

<div>

<p> {objA.a}</p>

<p> {b}</p>

</div>

)

}

export default Test1

import React from "react";
//class组件
class Test2 extends React.Component {

constructor(prop) {

super (prop);

this .state = {

a: 111, // 第一层属性,值

arr: [1, 23, 3], // 另一个第一层属性,数组

user: { name: "张三", age: 18 }, // 嵌套对象(深层属性),对象

}

}

render() {

setTimeout(()=> {

this .setState({ a: 888 }); // 通过 setState 修改状态;浅合并:只更新了 a 这个第一层属性,其他第一层属性(arr、user)完全保留原状态,没有被清空或替换

}, 1000)

return (

<div>

<p> {this .state.a}</p>

</div>

)

}

}
export default Test2

// 原状态:info: { city: "北京", zip: "100000" }
setA(prev => ({

info: { zip: "200000" } // 直接替换 info 对象(city 丢失),同样需要手动复制深层属性:info: { ...prev.info, zip: "200000" }

}));

// 原状态:user: { name: "张三", age: 18 }
this .setState({

user: { age: 19 } // 直接替换整个 user 对象(而非仅修改 age);合并后 user 会变成 { age: 19 }(name 属性丢失),因为 setState 只浅合并第一层,不会自动保留深层属性。必须手动复制深层属性:user: { ...prev.user, age: 19 }

});

数组的state修改:

render() {

setTimeout(() => {

let _arr = this .state.arr; // 创建新数组引用

_arr.push(1);

this .setState({

arr: _arr

})

}, 1000)

return (

<div>

<p> {this .state.a}</p>

<p> {this .state.arr[0]}</p>

<p> {this .state.arr[1]}</p>

<p> {this .state.arr[2]}</p>

<p> {this .state.arr[3]}</p>

</div>

)

}

对象的state修改:

render() {

setTimeout(() => {

let _obj = this .state.obj; // 创建新对象引用

_obj.obj1 = 777;

this .setState({

obj: _obj

})

}, 1000)

return (

<div>

<p> {this .state.a}</p>

<p> {this .state.obj.obj1}</p>

</div>

)

}

循环渲染和条件渲染

数组的所有值都渲染,需要用到循环渲染,jsx赋予html使用js结合的能力,可以直接在返回的jsx结构中使用循环,循环需要给一个key值,会让循环性能变高

// class组件的循环渲染
return (

<div>

<p> {this .state.a}</p>

<p> {this .state.obj.obj1}</p>

{

this .state.arr.map((item) => { // map可以将所有东西变成一个数组

return (

<div>

<h1> {item.title}</h1>

<p> {item.content}</p>

</div>

)

})

}

</div>

)

// 或者
render() {

let arr = []; // 创建新数组

this .state.arr.forEach((item) => {

arr.push(

<div key={item.title}>

<h1> {item.title}</h1>

<p> {item.content}</p>

</div>

);

});

}

// 或者
render() {

let arr = [];

this .state.arr.forEach((item) => { // 为每个 item 创建包含 title 和 content 的 JSX 结构

let _itemText = <div key={item.title}>

<h1> {item.title}</h1>

<p> {item.content}</p>

</div> ;

arr.push(_itemText);

});

}

// 或者
function createArrList() { // 定义一个新函数

let arr = [];

this .state.arr.forEach((item) => {

let _itemText = (

<div key={item.title}>

<h1> {item.title}</h1>

<p> {item.content}</p>

</div>

);

arr.push(_itemText);

});

return arr;

}

// 函数组件的条件渲染
import { react, useState } from "react"
function Test1() {

let [objA, setA] = useState({

a: 123

})

let [show, changeShow] = useState(true );

function showDiv(){ // 写一个函数

if (show){

return <div> 123**</div>**

}

}

let b = "i am b";

setTimeout(() => {

setA({ a: 999 })

}, 1000)

return (

<div>

<p> {objA.a}</p>

<p> {b}</p>

{showDiv()}

</div>

)

}
export default Test1

// 或者
return (
<div>

{show?<div> 123**</div>** :""};

</div>

)

// 或者
return ( // && 第一个成立了才会执行后面的操作
<div>

{show && <div> 123**</div>** };

</div>

)

// let [show, changeShow] = useState(0);面板的切换选择
function showTab(){ // 写一个函数

if (show == 0){

return <div> i am 0**</div>**

}else if (show == 1){

return <div> i am 1**</div>**

}else {

return <div> i am 2**</div>**

}

}
setTimeout(() => {

changeShow(1) // 1秒钟后0会变成1

},1000)

事件绑定

给用户操作界面,需要给元素绑定一些事件让用户进行操作时会有一些对应的响应

设置一个按钮,点击按钮,会将false变成true

import React, { useState } from "react"
function Test1() {

let [objA, setA] = useState({

a: 123

})

let [show, changeShow] = useState(false );

function showTab() {

if (show) return <div> 123**</div>**

}

function changeDiv(){ // 写一个函数,切换show的值

let _show = !show

changeShow(!show);

}

return ( // 绑定事件onClick与函数changeDiv

<div>

<p> {objA.a}</p>

{showTab()}

<button onClick={changeDiv}> {show ? '隐藏' : '显示'}</button>

</div>

)

}
export default Test1

// 绑定事件时如何传参
import { react, useState } from "react"
function Test1() {

let [objA, setA] = useState({

a: 123

})

let [show, changeShow] = useState(false );

function showTab() {

if (show) return <div> 123**</div>**

}

function changeDiv(num) { // 事件处理函数,传参

console.log(num); // 打印传入的参数

let _show = !show;

changeShow(_show);

}

return ( // 通过 bind(this, 123) 传递参数 123 并绑定函数 changeDiv

<div>

<p> {objA.a}</p>

{showTab()}

<button onClick={changeDiv.bind(this , 123)}> {show ? '隐藏' : '显示'}</button>

</div>

)

}
export default Test1

//
function changeDiv(num, num2, e) { // 函数接收 num、num2和事件对象 e(会自动加上e)

e.stopPropagation(); // 阻止事件冒泡,阻止当前事件向父元素传播

let show = !show;

changeShow(show);

}

相关推荐
百***84452 小时前
Webpack、Vite区别知多少?
前端·webpack·node.js
Mintopia2 小时前
零信任架构下的 WebAIGC 服务安全技术升级方向
前端·人工智能·trae
AA陈超2 小时前
ASC学习笔记0019:返回给定游戏属性的当前值,如果未找到该属性则返回零。
c++·笔记·学习·游戏·ue5·虚幻引擎
知南x3 小时前
【STM32MP157 异核通信框架学习篇】(10)Linux下Remoteproc相关API (下)
linux·stm32·学习
敏姐的后花园4 小时前
模考倒计时网页版
java·服务器·前端
AiXed5 小时前
PC微信WDA算法
前端·javascript·macos
博客zhu虎康6 小时前
React+Ant design
javascript·react.js·ecmascript
一只小阿乐10 小时前
react 封装弹框组件 传递数据
前端·javascript·react.js
533_10 小时前
[element-plus] el-tree 动态增加节点,删除节点
前端·javascript·vue.js