使用 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:5000
        changeOrigin 为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>

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Pyecharts 风采:从基础到高级,打造炫酷象形柱状图的完整指南【第40篇—python:象形柱状图】

文章目录 引言安装PyechartsPyecharts象形柱状图参数详解1. Bar 类的基本参数2. 自定义图表样式3. 添加标签和提示框 代码实战&#xff1a;绘制多种炫酷象形柱状图进阶技巧&#xff1a;动态数据更新与交互性1. 动态数据更新2. 交互性设计 拓展应用&#xff1a;结合其他图表类型…

Android 基础技术——列表卡顿问题如何分析解决

笔者希望做一个系列&#xff0c;整理 Android 基础技术&#xff0c;本章是关于列表卡顿问题如何分析解决 onBindViewHolder 优化 是否有耗时操作、重复创建对象、设置监听器、findViewByID、局部的动画对象等操作 是否存在内存泄漏 发生内存泄露&#xff0c;会导致一些不再使用…

工业4.0开放平台通信 统一架构OPC UA的一种测试方法

工业4.0和工业物联网&#xff08;Industrial Internet of Things, IIoT&#xff09;的核心挑战在于设备、机器以及来自不同行业服务之间的安全和标准化的数据和信息交换。 2016年11月工业4.0平台发布了指导纲要《工业4.0产品需要实现哪些准则》&#xff0c;即对于所有位于工业…

概念抽取:构建认知基础的关键步骤

目录 前言1 概念抽取任务定义1.1 概念知识图谱的关系定义1.2 实体与概念的紧密关联1.3 多样的概念关系 2 概念在认知中的重要角色2.1 语言理解的基础2.2 上下位关系的深化理解 3 概念抽取方法3.1 基于模板的抽取3.2 基于百科的抽取3.3 基于机器学习的方法 4 应用4.1 自然语言理…

一行命令在 wsl-ubuntu 中使用 Docker 启动 Windows

在 wsl-ubuntu 中使用 Docker 启动 Windows 0. 背景1. 验证我的系统是否支持 KVM&#xff1f;2. 使用 Docker 启动 Windows3. 访问 Docker 启动的 Windows4. Docker Hub 地址5. Github 地址 0. 背景 我们可以在 Windows 系统使用安装 wsl-ubuntu&#xff0c;今天玩玩在 wsl-ub…

【Web前端实操16】雪碧图(CSS精灵图)

雪碧图 CSS Sprite也叫CSS精灵图、CSS雪碧图&#xff0c;是一种网页图片应用处理方式。它允许你将一个页面涉及到的所有零星图片都包含到一张大图中去 雪碧图一般会给一个完整的图片&#xff0c;主要利用background-position 属性设置背景图像的起始位置。 优点 减少图片的字…

API网关-Apinto压缩包方式自动化安装配置教程

文章目录 前言一、Apinto安装教程1. 复制脚本2. 增加执行权限3. 执行脚本4. Apinto命令4.1 启动Apinto4.2 停止Apinto4.3 重启Apinto4.4 查看Apinto版本信息4.5 加入Apinto集群4.6 离开Apinto集群4.7 查看Apinto节点信息 5. 卸载Apinto 二、Apserver(Apinto Dashboard V3)安装教…

【css揭秘】

文章目录 背景与边框半透明边框多重边框box-shadowoutline 背景定位background-positionbackground-origincalc() 条纹背景水平条纹 形状圆形圆柱自适应的椭圆半椭圆四分之一椭圆 背景与边框 半透明边框 目标&#xff1a;给一个容器设置一层白色背景和一道半透明白色边框 写…

树莓派基础应用:智能家居监控系统

引言&#xff1a; 随着智能家居的普及&#xff0c;家居安全与监控逐渐成为人们关注的焦点。树莓派作为一种功能强大的迷你计算机&#xff0c;为我们提供了实现智能家居监控系统的可能。在本篇博客中&#xff0c;我们将通过构建一个简单的智能家居监控系统&#xff0c;来探索树莓…

node学习过程中的终端命令

冷的哥们手真tm冷&#xff0c;打字都是僵的&#xff0c;屮 目录 一、在学习nodejs过程中用到的终端命令总结 一、在学习nodejs过程中用到的终端命令 node -v nvm install 20.11.0 nvm list nvm list available nvm on nvm -v nvm use 20.11.0 node加要运行的js文件路径 ps&a…

docker compose实现mysql一主多从

参考了很多博客&#xff0c;死磕了几天&#xff0c;最终跑起来了&#xff0c;不容易&#xff0c;晚上喝瓶82年可乐庆祝下。 1、整体文件结构&#xff0c;这里忽略log、conf、data映射目录 2、docker-compose.yml文件内容如下&#xff1a; version: 3.3 services:mysql-master…

黑马程序员-瑞吉外卖-day5

修改实体类 package com.itheima.reggie.entity;import com.baomidou.mybatisplus.annotation.FieldFill; import com.baomidou.mybatisplus.annotation.TableField; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode;i…

JS之隐式转换与布尔判定

大家思考一下 [ ] [ ] &#xff1f; 答案是空字符串 为什么呢&#xff1f; 当做加法运算的时候&#xff0c;发现左右两端存在非原始类型&#xff0c;也就是引用类型对象&#xff0c;就会对对象做隐式类型转换 如何执行的&#xff1f;或者说怎么查找的&#xff1f; 第一步&…

中国新能源汽车持续跑出发展“加速度”,比亚迪迎来向上突破

2023年已经过去&#xff0c;对于汽车圈而言&#xff0c;2023年是中国车市的分水岭&#xff0c;在这一年&#xff0c;中国汽车工业70年以来首次进入全球序列&#xff0c;自主品牌强势霸榜&#xff0c;销量首次超过合资车。要知道&#xff0c;这是自大众于1984年进入中国市场成立…

Android App开发-简单控件(3)——常用布局

3.3 常用布局 本节介绍常见的几种布局用法&#xff0c;包括在某个方向上顺序排列的线性布局&#xff0c;参照其他视图的位置相对排列的相对布局&#xff0c;像表格那样分行分列显示的网格布局&#xff0c;CommonLayouts以及支持通过滑动操作拉出更多内容的滚动视图。 3.3.1 线…

Jenkins上跑自动化项目,case出现错误时,导致项目运行时间过长,该如何处理?

1、方案一&#xff1a;Jenkins上调整 进入配置&#xff1a; 构建环境&#xff1a; 自行选择超时时间即可&#xff5e; 2、方案二&#xff1a;代码调整【python】 安装插件&#xff1a;pytest-timeout 选择一&#xff1a;装饰器用法&#xff1a;将单个测试用例标记为超时&…

Qt6入门教程 12:QAbstractButton

目录 一.状态 二.信号 三.使用 1.自定义按钮 2.多选 3.互斥 QAbstractButton类实现了一个抽象按钮&#xff0c;并且让它的子类来指定如何处理用户的动作&#xff0c;并指定如何绘制按钮。QAbstractButton类是所有按钮控件的基类。 QAbstractButton提供…

【Docker】数据持久化 挂载

Docker的镜像是只读的&#xff0c;但是容器是可写的&#xff0c;我们可以将数据写入到容器&#xff0c;不过一旦容器删除数据将会丢 失&#xff0c;那么有什么办法能将数据进行持久化存储呢&#xff1f; ——在宿主机上开辟一块地方&#xff0c;存储内容和docker容器的存储内…

TCP 三次握手 四次挥手以及滑动窗口

TCP 三次握手 简介&#xff1a; TCP 是一种面向连接的单播协议&#xff0c;在发送数据前&#xff0c;通信双方必须在彼此间建立一条连接。所谓的 “ 连接” &#xff0c;其实是客户端和服务器的内存里保存的一份关于对方的信息&#xff0c;如 IP 地址、端口号等。 TCP 可以…

HCIP复习课(bgp选路实验)

1.如图连接网络&#xff0c;合理规格IP地址&#xff0c;AS200内IGP协议为OSPF 2.R1属于AS 100:R2-R3-R4小AS 234 R5-R6-R7/AS567&#xff0c;同时声明大AS 200&#xff0c;R8属于AS300 3.R2-R5 R4-R7之间为联邦EBGP邻居关系 4.R1-R8之间通信 1、ip配置&#xff1a; R1: R2:…