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

基本使用:

  1. a 标签改为 Link 标签或者 NavLink
    <Link className='menu-item' to="/home">home</Link>
    <NavLink className='menu-item' to="/home">home</NavLink>
    NavLink标签与Link相比,当前页面匹配的菜单会自动增加一个 active 类,或者可以使用 activeClassName 修改为需要的类名
  2. 展示区用 Route 标签进行路径的匹配
    <Route path="/home" component={Home}></Route>
  3. 的最外侧包裹一个 <BrowserRouter><HashRouter>

10、路由组件与一般组件

  1. 写法不同
    一般组件:
    路由组件:
  2. 存放位置不同:
    一般组件:components
    路由组件:pages
  3. 接收到的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,样式路径有误,所以样式获取不到。

解决方法:

  1. index.html 中引入样式使用绝对路径,将 ./XXX改为 /XXX
  2. index.html 中引入样式使用 %PUBLIC_URL%,将./XXX改为%PUBLIC_URL%/XXX
  3. 使用 hash HashRouter模式代替 history 模式BrowserRouter

2)路由的严格匹配和模糊匹配

  1. 默认使用的是模糊匹配(简单记:输入的路径 必须要包含 匹配的路径,且顺序要一致)
  2. 开启严格匹配
  3. 严格匹配不要随便开启,需要时再开,有些时候开启会导致无法继续匹配二级路由

<MyNavLink to="/home/a/b">home</MyNavLink>
<Route exact path="/home" component={Home}></Route>

3)Redirect

  1. 一般写在所有路由注册的最下方,当所有路由都无法匹配是,跳转到 Redirect 指定的路由。
<Switch><Route path="/about" component={About}></Route><Route path="/home" component={Home}></Route><Redirect to="/about"/>
</Switch>

4)嵌套路由

  1. 注册子路由时要写上父路由的 path 的值(/home/news)
  2. 路由的匹配是按照注册路由的顺序进行的 (先匹配home路由的组件,在匹配news路由的组件)

5)路由传参

  1. params 参数
    路由链接(携带参数):<Link to={/home/message/detail/tom/18}>详情</Link>
    注册路由(声明接收):<Route path="/home/message/detail/:name/:age" component={Detail}/>
    接收参数:this.props.match.params
  2. 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 解析
  3. 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>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/652183.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java技术栈 —— Hadoop入门(二)

Java技术栈 —— Hadoop入门&#xff08;二&#xff09; 一、用MapReduce对统计单词个数1.1 项目流程1.2 可能遇到的问题1.3 代码勘误1.4 总结 一、用MapReduce对统计单词个数 1.1 项目流程 (1) 上传jar包。 (2) 上传words.txt文件。 (3) 用hadoop执行jar包的代码&#xff0c;…

RNN预测下一句文本简单示例

根据句子前半句的内容推理出后半部分的内容&#xff0c;这样的任务可以使用循环的方式来实现。 RNN&#xff08;Recurrent Neural Network&#xff0c;循环神经网络&#xff09;是一种用于处理序列数据的强大神经网络模型。与传统的前馈神经网络不同&#xff0c;RNN能够通过其…

第十二届“中关村青联杯”全国研究生数学建模竞赛-D题:面向节能的单/多列车优化决策问题(续)

目录 5.3 240 列列车节能运行优化控制模型 5.3.1 模型描述 5.3.2 模型求解 6 问题三分析

32GPIO输入LED闪烁蜂鸣器

一.GPIO简介 所有的GPIO都挂载到APB2上&#xff0c;每个GPIO有&#xff11;&#xff16;个引脚 内核可以通过APB&#xff12;对寄存器进行读写&#xff0c;寄存器都是32位的&#xff0c;但每个引脚端口只有&#xff11;&#xff16;位 驱动器用于增加信号的驱动能力 二.具体…

【Go】深入理解 Go map:赋值和扩容迁移 ①

文章目录 map底层实现hmapbmap map hash冲突了怎么办&#xff1f; map扩容触发扩容时机扩容小结为什么map扩容选择增量&#xff08;渐进式扩容&#xff09;&#xff1f;迁移是逐步进行的。那如果在途中又要扩容了&#xff0c;怎么办&#xff1f; map翻倍扩容原理 map写入数据内…

数据库查询3

目录 1. 多表查询 1.1.1 介绍 1.1.2 分类 1.2 内连接 1.3 外连接 1.4 子查询 1.4.1 介绍 1.4.2 标量子查询 1.4.3 列子查询 1.4.4 行子查询 1.4.5 表子查询 2. 事务 2.1 操作 2.2 四大特性 数据库总结2 数据库总结1 1. 多表查询 1.1.1 介绍 多表查询&#xff…

Compose | UI组件(四) | Icon(),Image() 图标和图片组件

文章目录 Icon 图标组件简介Icon 图标组件例子 Image 图片组件简介Image 图片组件例子colorFilter 实现不同效果 总结 Icon 图标组件简介 显示一系列图标 Composable fun Icon(imageVector: ImageVector, //矢量图,可以显示SVG格式的图标contentDescription: …

1170. 排队布局(差分约束,spfa,负环)

1170. 排队布局 - AcWing题库 当排队等候喂食时&#xff0c;奶牛喜欢和它们的朋友站得靠近些。 农夫约翰有 N 头奶牛&#xff0c;编号从 1 到 N&#xff0c;沿一条直线站着等候喂食。 奶牛排在队伍中的顺序和它们的编号是相同的。 因为奶牛相当苗条&#xff0c;所以可能有两…

研发日记,Matlab/Simulink避坑指南(七)——数据溢出钳位Bug

文章目录 前言 背景介绍 问题描述 分析排查 解决方案 总结归纳 前言 见《研发日记&#xff0c;Matlab/Simulink避坑指南(二)——非对称数据溢出Bug》 见《研发日记&#xff0c;Matlab/Simulink避坑指南(三)——向上取整Bug》 见《研发日记&#xff0c;Matlab/Simulink避坑…

【每日一题】YACS 243:5G通讯

题目描述 这是上海计算机学会竞赛 P 243 P243 P243&#xff1a;5G通讯&#xff08; 2020 2020 2020年 9 9 9月月赛 乙组 T 2 T2 T2&#xff09;标签&#xff1a;二分查找题意&#xff1a;给定 n n n个点&#xff0c;第 i i i个点的坐标为 x i x_i xi​。给定限制 d d d&#…

C语言第十一弹---函数(下)

​ ✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】 函数 1、嵌套调用和链式访问 1.1、嵌套调用 1.2、链式访问 2、函数的声明和定义 2.1、单个文件 2.2、多个文件 2.3、static 和 extern 2.3.1、static…

速盾网络:CDN防盗链策略有哪些

随着互联网的迅猛发展&#xff0c;内容盗链问题也日益严重。为了保护网站内容的安全和版权&#xff0c;CDN防盗链策略成为了许多网站和内容提供者的首选。本文将介绍CDN防盗链的概念和原理&#xff0c;并详细介绍了几种常见的CDN防盗链策略和实施方法。 随着技术的进步和互联网…

【自然语言处理】【深度学习】文本向量化、one-hot、word embedding编码

因为文本不能够直接被模型计算&#xff0c;所以需要将其转化为向量 把文本转化为向量有两种方式&#xff1a; 转化为one-hot编码转化为word embedding 一、one-hot 编码 在one-hot编码中&#xff0c;每一个token使用一个长度为N的向量表示&#xff0c;N表示词典的数量。 即&…

dos攻击与ddos攻击的区别

①DOS攻击&#xff1a; DOS&#xff1a;中文名称是拒绝服务&#xff0c;一切能引起DOS行为的攻击都被称为dos攻击。该攻击的效果是使得计算机或网络无法提供正常的服务。常见的DOS攻击有针对计算机网络带宽和连通性的攻击。 DOS是单机于单机之间的攻击。 DOS攻击的原理&#…

[lighttpd]lighttpd配置http强制跳转https

参考链接 HowToRedirectHttpToHttps - Lighttpd - lighty labs lighttpd版本 / # lighttpd -h lighttpd/1.4.59 (ssl) - a light and fast webserver usage:-f <name> filename of the config-file-m <name> module directory (default: /usr/lib)-i <secs…

【GitHub项目推荐--常见的国内镜像】【转载】

由于国内网络原因&#xff0c;下载依赖包或者软件&#xff0c;对于不少互联网从业者来说&#xff0c;都有不小的挑战&#xff0c;时间浪费在这上边&#xff0c;实在可惜。这个项目介绍了常见依赖&#xff0c;软件的国内镜像&#xff0c;助力大家畅爽编码。 这是一个归纳梳理类…

C# 将HTML网页、HTML字符串转换为PDF

将HTML转换为PDF可实现格式保留、可靠打印、文档归档等多种用途&#xff0c;满足不同领域和情境下的需求。本文将通过以下两个示例&#xff0c;演示如何使用第三方库Spire.PDF for .NET和QT插件在C# 中将Html 网页&#xff08;URL&#xff09;或HTML字符串转为PDF文件。 HTML转…

【C语言/数据结构】排序(选择排序,推排序,冒泡排序)

&#x1f308;个人主页&#xff1a;秦jh__https://blog.csdn.net/qinjh_?spm1010.2135.3001.5343&#x1f525; 系列专栏&#xff1a;《数据结构》https://blog.csdn.net/qinjh_/category_12536791.html?spm1001.2014.3001.5482 ​​​​ 目录 选择排序 选择排序 ​编辑…

JAVA学习笔记6(常用结构与数组)

1.选择结构 1.if语句&#xff1a; if(条件表达式){ 一条或多条语句 }; 2.if else语句&#xff1a; if(条件表达式) {语句块1} else {语句块2} 3.switch语句&#xff1a; switch(表达式){​​​​​​​ case 常量表达式1:语句组;break;​​​​​​​ ​​​​…

js实现动漫拼图2.0版

比较与1.0版&#xff0c;2.0版就更像与华容道类似的拼图游戏&#xff0c;从头到尾都只能控制白色块移动&#xff0c;而且打乱拼图和求助的实现与1.0都不相同 文章目录 1 实现效果2 实现思路2.1 打乱拼图2.2 求助功能2.3 判赢 3 代码实现 js实现动漫拼图1.0版 https://blog.csdn…