使用 create-react-app 创建 react 应用
一、创建项目并启动
第一步:全局安装:npm install -g create-react-app
第二步:切换到想创建项目的目录,使用命令create-react-app hello-react
第三步:进入项目目录,cd hello-react
第四步:启动项目,npm start
二、目录结构
1、目录结构
其中,public/index.htm,src/App.js,src/index.js 三个是最重要的文件。
+ node_module ------ 第三方资源
+ public ------ 静态资源文件夹+ favicon.ico ------ 网站页面图标+ index.html ------ 主页面+ logo192.png ------ logo 图+ logo512.png ------ logo 图+ manifest.json ------ 应用加壳的配置文件+ robots.txt ------ 爬虫协议文件
+ src ------ 源码文件夹+ App.css ------ App 组件的样式+ App.js ------ App 组件+ App.test.js ------ 用于给 App 组件做测试+ index.css ------ 全局样式+ index.js ------ 入口文件+ logo.svg ------ logo 图+ reportWebVitals.js ------ 页面性能分析文件(需要 web-vitals 库的支持)+ setupTests.js ------ 组件单元测试的文件(需要 jest-dom 库的支持)
2、文件内容说明
public/index.html
<!DOCTYPE html>
<html lang="en"><head><meta charset="utf-8" /><link rel="icon" href="%PUBLIC_URL%/favicon.ico" /><!-- 开启理想窗口,用于做移动端网页的适配 --><meta name="viewport" content="width=device-width, initial-scale=1" /><!-- 用于配置浏览器页签+地址栏的颜色(仅支持安卓手机浏览器,兼容性较差) --><meta name="theme-color" content="#000000" /><meta name="description" content="Web site created using create-react-app"/><!-- 用于指定网页添加到手机主屏幕后到图标(仅支持 apple 手机) --><link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" /><!-- 应用加壳时到配置 --><link rel="manifest" href="%PUBLIC_URL%/manifest.json" /><title>React App</title></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body>
</html>
src/index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';const root = ReactDOM.createRoot(document.getElementById('root'));
// React.StrictMode 标签会自动校验 react 语法,遇到一些将要遗弃或不推荐使用的语法,会提示
// 不加 React.StrictMode 标签,直接使用 App 组件也没啥影响
root.render(<React.StrictMode><App /></React.StrictMode>
);
reportWebVitals();
注意:旧版本的 src/index.js 中,渲染组件是通过方法ReactDOM.render(<App/>,el)
实现的:
import React from "react";
import App from "./App"
// 旧版本引入 ReactDOM ,然后 执行 ReactDOM.render()
import ReactDOM from "react-dom"
ReactDOM.render(<App />, document.getElementById("root"))
而新版本18.0.2
是通过ReactDOM.createRoot(el).render(<App/>)
实现的:
import React from "react";
import App from "./App"
// 新版本引入方式,利用 ReactDOM.createRoot() 创建节点,然后执行 render 函数
import ReactDOM from "react-dom/client"
ReactDOM.createRoot(document.getElementById("root")).render(<App/>)
三、开发注意事项
1、组件命名
组件可以以js
为后缀,也可以以 jsx 为后缀,以 jsx 为后缀可以明显区别于其他功能性的 js 文件。
2、引入 React 和 Component
1)只引 React,定义类组件时使用 React.Component
import React from "react";
// 定义类式组件
export default class Hello extends React.Component {render() {return (<h1 className={hello.title}>Hello 组件</h1>)}
}
2)解构引入 Component,定义类组件时直接使用 Component
// React 中使用了默认暴露和分别暴露,所以可以使用下面的引入方式
// import React, { Component } from "react";
import { Component } from "react";
// 定义类式组件
export default class Welcome extends Component {render() {return (<h1 className="title">Welcome组件</h1>)}
}
能使用以上引用方式是因为 React 中使用了 默认暴露 和 分别暴露
class React {
}
// 分别暴露 Component
export class Component {
}
React.Component = Component
// 默认暴露 React
export default React
--------------------------------------
// 其他文件引用时可以这样:
import React, { Component } from "react";
import { Component } from "react";
3、引入 ReactDOM
1)新版本18.0.2
中,要从 react-dom/client
中引入 ReactDOM
,用法如下:
import React from "react";
// 新版本引入 ReactDOM,渲染节点时使用 ReactDOM.createRoo(el).render(<App/>)
import ReactDOM from "react-dom/client"
import App from "./App";
ReactDOM.createRoot(document.getElementById("root")).render(<App/>)
2)旧版本中,要从 react-dom
中引入 ReactDOM
,用法如下:
import React from "react";
import ReactDOM from "react-dom"
import App from "./App";
ReactDOM.render(<App />, document.getElementById("root"))
4、css 模块化
为什么
按照截图方式在同一个组件中引用多个组件,如果 Hello 和 Welcome 组件存在相同类的不同样式时,后者会覆盖前者,所以需要模块化样式,使其互不影响。
怎么做
- 将 .css 文件改为 .module.css 文件
- 引入 css 文件时,使用
import hello from "./Hello.module.css"
代替import "./Hello.module.css"
- 组件标签中使用 hello.title,
<h1 className={hello.title}>Hello 组件</h1>
编译出来是如下效果:
5、组件通信
父子通信:直接通过组件标签的属性进行传值,子组件中通过 props 可以接受
祖孙通信:遵循状态在哪里,操作状态的方法就在哪里的原则,将所有修改 state 数据的方法都定义在 state 所在的组件中,给子组件标签添加(方法)属性funcName={funcName}
-》孙组件标签添加(方法)属性funcName={funcName}
-》孙组件内部根据需要,调用传过来的方法this.props.funcName()
6、跨域
前提:本地前端项目地址:http://localhost:3000
1)法一:配置在 packge.json 中
- 在
package.json
中配置"proxy": "http://localhost:5000"
- 组件中使用
axios.get("http://localhost:3000/students").then()
接口请求时会先在 3000 端口服务上找 /students 接口,找不到就去配置好的 5000 端口上找
说明:
1、优点:配置简单,前端请求资源时可以不加任何前缀
2、缺点:不能配置多个代理
3、工作方式:上述方式配置代理,当前请求了3000不存在的资源时,那么该请求会转发给 5000(优先匹配前端资源)
axios.get("http://192.168.31.229:3000/students").then((res) => {console.log("学生接口调用成功",res)},(err) => {console.log("学生接口调用失败", err)})
2)法二:配置在 setupProxy.js 中
1)第一步:创建代理配置文件:在 src 下创建配置文件:src/setupProxy.js
2)编写 setupProxy.js 配置具体代理规则:
const { createProxyMiddleware } = require("http-proxy-middleware")module.exports = function (app) {app.use(createProxyMiddleware("/api1", { // api1 是需要转发的请求(所有带有 /api1 前缀的请求都会转发给5000)target: "http://localhost:5000", // 配置转发目标地址(能返回数据的服务器地址)changeOrigin: true, // 控制服务器接收到的请求头中 host 字段的值/*changeOrigin 为true时,服务器收到的请求头中的 host 为 http://localhost:5000changeOrigin 为false时,服务收到的请求头中的 host 为前端工程的服务器的host(http://localhost:3000)changeOrigin 默认为false,但我们一般将changeOrigin的值设为true*/pathRewrite: {"^/api1":""} // 去除请求前缀,保证交给后台服务器的是正常请求地址(必须配置)}),createProxyMiddleware("/api2", {target: "http://localhost:5001",changeOrigin: true,pathRewrite: {"^/api2":""}}))
}
使用:
axios.get("http://192.168.31.229:3000/api1/students").then((res) => {console.log("学生接口调用成功",res)},(err) => {console.log("学生接口调用失败", err)})
说明:
1.优点:可以配置多个代理,可以灵活控制是否走代理
2.缺点:配置繁琐,前端请求资源时必须加前缀
7、组件通信
1、动态初始化列表,如何确定将数据放在哪个组件的state中?
- 某个组件使用:放在自身的state中
- 某些组件使用:放在他们共同的父组件的state中(官方称此操作为:状态提升)
2、关于父子组件通信:
- 【父组件】给【子组件】传递数据:通过props传递
- 【子组件】给【父组件】传递数据:通过props传递,要求父组件提前给子组件传递一个函数func,子组件通过this.props.func调用
3、状态在哪里,操作状态的方法就在哪里
8、消息订阅与发布(个组件间进行通信)
1)下载 pubsub-js
npm i pubsub-js
2)消息订阅
componentDidMount() {// 消息订阅// 消息订阅,回调里面接收两个参数,第一个是消息名,这里也就是 updateState,第二个是消息发布时携带的参数this.token = PubSub.subscribe("updateState",(_, stateObj)=> {this.setState(stateObj)})
}
3)消息发布
PubSub.publish("updateState", {users: res.data.items, isLoadding: false})
4)取消订阅
componentWillUnmount() {// 取消订阅PubSub.unsubscribe(this.token)
}
9、路由 react-router-dom
基本使用:
- a 标签改为 Link 标签或者 NavLink
<Link className='menu-item' to="/home">home</Link>
<NavLink className='menu-item' to="/home">home</NavLink>
NavLink标签与Link相比,当前页面匹配的菜单会自动增加一个 active 类,或者可以使用 activeClassName 修改为需要的类名 - 展示区用 Route 标签进行路径的匹配
<Route path="/home" component={Home}></Route>
- 的最外侧包裹一个
<BrowserRouter>
或<HashRouter>
10、路由组件与一般组件
- 写法不同
一般组件:
路由组件: - 存放位置不同:
一般组件:components
路由组件:pages - 接收到的props不同:
一般组件:写组件标签时传递了什么,就能接收到什么
路由组件:接收到三个固定属性
- history:
go, goBack, goForward, push, replace (方法) - location:
pathname:“”, search:“”, state: “” - match:
params: {}, path: “”, url: “”
11、路由
路由渲染时,若有多个相同 path,不同 component 路由,取并集,也就是如下代码,没有 Switch 标签包裹时,/home 路由同时显示 Home 组件和 Test 组件。
Switch 包裹时,/home 路由显示第一个匹配到的组件,也就是 Home 组件。
<Switch><Route path="/about" component={About}></Route><Route path="/home" component={Home}></Route><Route path="/home" component={Test}></Route>
</Switch>
react 封装组件:
- 标签体内容是一个特殊的标签属性
- 子组件可以通过 this.props.children 可以获取标签体内容,可以直接通过属性方式写入标签体内容。
<!-- 这里的 to="/about" 和 内容 about 会以 props 传入到组件 MyNavLink 中,形式是:{to:"/about", children:"about"} -->
<MyNavLink to="/about">about</MyNavLink>
<MyNavLink to="/home">home</MyNavLink>
import React from "react";
import { NavLink } from "react-router-dom";
export default class MyNavLink extends React.Component {render() {console.log(this.props) //{to: '/about', children: 'about'}return (<NavLink activeClassName="active-menu" className='menu-item' {...this.props}/>)}
}
1)多层路由时,样式丢失
<link rel="stylesheet" href="./css/base.css">
场景:例如路由前要加前缀,为 /qiao/about,如果页面通过相对路径,引入了 public 目录中的样式 base.css,页面刷新时,base.css 样式会丢失。
原因:开发模式默认打开 http://192.168.31.229:3000/,这时候 public/css/base.css 从 public 中能正常拿到,此时样式是正常的。点击路由跳转到 http://192.168.31.229:3000/qiao/about,此时刷新页面,因为从 public 下找不到 qiao/about,会默认渲染 index.html 页面,而且样式文件因为使用相对路口,路径转化为 public/qiao/css/base.css,样式路径有误,所以样式获取不到。
解决方法:
- index.html 中引入样式使用绝对路径,将
./XXX
改为/XXX
- index.html 中引入样式使用
%PUBLIC_URL%
,将./XXX
改为%PUBLIC_URL%/XXX
- 使用 hash
HashRouter
模式代替 history 模式BrowserRouter
2)路由的严格匹配和模糊匹配
- 默认使用的是模糊匹配(简单记:输入的路径 必须要包含 匹配的路径,且顺序要一致)
- 开启严格匹配
- 严格匹配不要随便开启,需要时再开,有些时候开启会导致无法继续匹配二级路由
<MyNavLink to="/home/a/b">home</MyNavLink>
<Route exact path="/home" component={Home}></Route>
3)Redirect
- 一般写在所有路由注册的最下方,当所有路由都无法匹配是,跳转到 Redirect 指定的路由。
<Switch><Route path="/about" component={About}></Route><Route path="/home" component={Home}></Route><Redirect to="/about"/>
</Switch>
4)嵌套路由
- 注册子路由时要写上父路由的 path 的值(/home/news)
- 路由的匹配是按照注册路由的顺序进行的 (先匹配home路由的组件,在匹配news路由的组件)
5)路由传参
- params 参数
路由链接(携带参数):<Link to={
/home/message/detail/tom/18}>详情</Link>
注册路由(声明接收):<Route path="/home/message/detail/:name/:age" component={Detail}/>
接收参数:this.props.match.params
- search 参数
路由链接(携带参数):<Link to={
/home/message/detail?name=tom&age=18}>详情</Link>
注册路由(无需声明,正常注册即可):<Route path="/home/message/detail" component={Detail}/>
接收参数:this.props.location.search
备注:获取到的 search 是 urlencoded 编码字符串,需要借助 qs 解析 - state 参数
路由链接(携带参数):<Link to={{pathname: "/home/message/detail", state: {name: "tom", age: 18}}}>{item.title}</Link>
注册路由(无需声明,正常注册即可):<Route path="/home/message/detail" component={Detail}/>
接收参数:this.props.location.state
备注:刷新也可以保留参数,但是清除缓存,history所有数据就会被清空,刷新无法获取参数
6)push和replace
Link 标签默认是 push 默认,要打开 replace 模式,需要在标签中增加属性 replace={true},或者简写 replace
<Link replace={true} to={{pathname: "/home/message/detail", state: {id: item.id, title: item.title}}}>{item.title}</Link>
<Link replace to={{pathname: "/home/message/detail", state: {id: item.id, title: item.title}}}>{item.title}</Link><!-- 默认是push、 -->
<Link to={{pathname: "/home/message/detail", state: {id: item.id, title: item.title}}}>{item.title}</Link>
相关文章:

使用 create-react-app 创建 react 应用
一、创建项目并启动 第一步:全局安装:npm install -g create-react-app 第二步:切换到想创建项目的目录,使用命令create-react-app hello-react 第三步:进入项目目录,cd hello-react 第四步:启…...
obs-studio 源码学习 obs.h
obs.h 引用头文件介绍 c99defs.h:这个头文件提供了一些 C99 标准的定义和声明,包括一些常用的宏定义和类型定义,用于提高代码的可移植性和兼容性。 bmem.h:这个头文件提供了对内存分配和管理的功能,包括一些内存分配…...

C语言-指针的基本知识(上)
一、关于内存 存储器:存储数据器件 外存 外存又叫外部存储器,长期存放数据,掉电不丢失数据 常见的外存设备:硬盘、flash、rom、u盘、光盘、磁带 内存 内存又叫内部存储器,暂时存放数据,掉电数据…...

4核16G幻兽帕鲁服务器优惠价格表,阿里云和腾讯云报价
幻兽帕鲁服务器价格多少钱?4核16G服务器Palworld官方推荐配置,阿里云4核16G服务器32元1个月、96元3个月,腾讯云幻兽帕鲁服务器服务器4核16G14M带宽66元一个月、277元3个月,8核32G22M配置115元1个月、345元3个月,16核64…...

GitHub 上传文件夹到远程仓库、再次上传修改文件、如何使用lfs上传大文件、github报错一些问题
按照大家的做法,把自己遇到的问题及解决方案写出来(注意:Error里面有些方法有时候我用可以成功,有时候我用也不能成功,写出来仅供参考,实在不行重头再clone,add,commit,p…...

一些es的基本操作
目录 给索引增加字段:给索引删除字段[^1]:创建索引:插入document删除document(应该是按ID) : 给索引增加字段: 用postMan: 给名为population_portrait_hash_seven的索引增加了一个text类型的字段。 用chrome插件Elasticvue 的Re…...

酒鬼酒2024年展望:稳发展动能,迈入恢复性增长轨道
文 | 琥珀酒研社 作者 | 渡过 最近几个月来,白酒估值回落到近十年来低位,反映出了整个白酒行业的市场低迷和虚弱现状。不管是头部企业五粮液、泸州老窖,还是区域酒企口子窖、金种子酒等,最近都通过“回购”或“增持”࿰…...

1002. HarmonyOS 开发问题:鸿蒙 OS 技术特性是什么?
1002. HarmonyOS 开发问题:鸿蒙 OS 技术特性是什么? 硬件互助,资源共享 分布式软总线 分布式软总线是多种终端设备的统一基座,为设备之间的互联互通提供了统一的分布式通信能力,能够快速发现并连接设备,高效地分发…...
vue-cli 无法安装问题解决
在macOS上安装vue-cli,但一直都失败,最后终于解决。 先后报错了2个问题。 报错无法安装 其实原因是源被切断,默认的源是官方的,但在CN是无法正常访问,各种问题。直接将源修改才可以。但可能需要试多次。 npm config…...

spring-bus消息总线的使用
文章目录 依赖bus应用接口用到的封装参数类 接收的应用监听器定义的事件类 使用bus定义bus远程调用A应用数据更新后通过bus数据同步给B应用 依赖 <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-bus-amqp…...

isctf---re
crackme 解压得到crackme.exe 运行得到flag babyRe 先猜e65537的rsa 先用Z3强行求出p、q,算出常规rsa中的phi,然后套用公式求出m exp #babyre wp from z3 import * import libnum from Crypto.Util.number import * p,q,cInts(p q c) S Solver() S…...

C语言第十二弹--扫雷
✨个人主页: 熬夜学编程的小林 💗系列专栏: 【C语言详解】 【数据结构详解】 扫雷 1、扫雷游戏分析和设计 1.1、扫雷游戏的功能说明 1.2 游戏的分析和设计 1.2.1、数据结构的分析 1.2.2、文件结构设计 2、扫雷游戏的结构分析 2.1、用…...
网路服务器——线程池技术
文章目录 一、线程池技术二、使用原理三、优点总结 一、线程池技术 预创建原则,线程池内部准备线程备用, 不宜过多。线程应该重用性,可以一对多处理任务或服务不同的客户端。处理单元(线程)数量并不固定,动态扩容与缩减(任务量)。…...

探索设计模式的魅力:深入了解适配器模式-优雅地解决接口不匹配问题
设计模式专栏:http://t.csdnimg.cn/nolNS 目录 一、引言 1. 概述 2. 为什么需要适配器模式 3. 本文的目的和结构 二、简价 1. 适配器模式的定义和特点 定义 特点 2. 适配器模式的作用和适用场景 作用 适用场景 3. 适配器模式与其他设计模式的比较 三、适配…...

matlab窗函数-hann窗和hamming窗函数
窗函数的作用 在时域上,窗函数可以看作是对原始信号进行截断或调制的加权函数。这些窗函数通常在时域上是有限的宽度,并且具有对称性,如矩形窗、汉宁窗、汉明窗和布莱克曼窗等。例如,汉明窗是一种对称窗函数,它可以用…...

Java项目实战--瑞吉外卖DAY03
目录 P22新增员工_编写全局异常处理器 P23新增员工_完善全局异常处理器并测试 p24新增员工_小结 P27员工分页查询_代码开发1 P28员工分页查询_代码开发2 P22新增员工_编写全局异常处理器 在COMMON新增全局异常捕获的类,其实就是代理我们这些controlle。通过aop把…...
docker 里使用vcs 2018 verdi等eda 图形界面
书接上文。之前借用别人的docker,使用EDA工具,苦于没有图形界面。如果只是编码,编译可能问题不大,但是如果要看波形之类的,就没法实现了。 docker 使用 vcs/2018 Verdi等 eda 软件-CSDN博客https://blog.csdn.net/guy…...

OpenHarmony—不支持解构赋值
规则:arkts-no-destruct-assignment 级别:错误 ArkTS不支持解构赋值。可使用其他替代方法,例如,使用临时变量。 TypeScript let [one, two] [1, 2]; // 此处需要分号 [one, two] [two, one];let head, tail [head, ...tail]…...

让AI帮你说话--GPT-SoVITS教程
有时候我们在录制视频的时候,由于周边环境嘈杂或者录音设备问题需要后期配音,这样就比较麻烦。一个比较直观的想法就是能不能将写好的视频脚本直接转换成我们的声音,让AI帮我们完成配音呢?在语音合成领域已经有很多这类工作了&…...
线性回归需要满足的几个假设
线性回归模型是基于一些假设构建的,这些假设有助于确保模型的有效性和可解释性。以下是线性回归需要满足的几个主要假设: 线性关系假设(Linearity): 线性回归假设因变量(目标变量)与自变量(特征…...

wordpress后台更新后 前端没变化的解决方法
使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…...

XML Group端口详解
在XML数据映射过程中,经常需要对数据进行分组聚合操作。例如,当处理包含多个物料明细的XML文件时,可能需要将相同物料号的明细归为一组,或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码,增加了开…...

华为云AI开发平台ModelArts
华为云ModelArts:重塑AI开发流程的“智能引擎”与“创新加速器”! 在人工智能浪潮席卷全球的2025年,企业拥抱AI的意愿空前高涨,但技术门槛高、流程复杂、资源投入巨大的现实,却让许多创新构想止步于实验室。数据科学家…...

springboot 百货中心供应链管理系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,百货中心供应链管理系统被用户普遍使用,为方…...
【Java学习笔记】Arrays类
Arrays 类 1. 导入包:import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序(自然排序和定制排序)Arrays.binarySearch()通过二分搜索法进行查找(前提:数组是…...
css的定位(position)详解:相对定位 绝对定位 固定定位
在 CSS 中,元素的定位通过 position 属性控制,共有 5 种定位模式:static(静态定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和…...

用docker来安装部署freeswitch记录
今天刚才测试一个callcenter的项目,所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...

Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
Java求职者面试指南:计算机基础与源码原理深度解析
Java求职者面试指南:计算机基础与源码原理深度解析 第一轮提问:基础概念问题 1. 请解释什么是进程和线程的区别? 面试官:进程是程序的一次执行过程,是系统进行资源分配和调度的基本单位;而线程是进程中的…...
C#学习第29天:表达式树(Expression Trees)
目录 什么是表达式树? 核心概念 1.表达式树的构建 2. 表达式树与Lambda表达式 3.解析和访问表达式树 4.动态条件查询 表达式树的优势 1.动态构建查询 2.LINQ 提供程序支持: 3.性能优化 4.元数据处理 5.代码转换和重写 适用场景 代码复杂性…...