使用 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 三个是最重要的文件。

md 复制代码
+ 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

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

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)实现的:

js 复制代码
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/>)实现的:

js 复制代码
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

js 复制代码
import React from "react";
// 定义类式组件
export default class Hello extends React.Component {
  render() {
    return (
      <h1 className={hello.title}>Hello 组件</h1>
    )
  }
}

2)解构引入 Component,定义类组件时直接使用 Component

js 复制代码
// React 中使用了默认暴露和分别暴露,所以可以使用下面的引入方式
// import React, { Component } from "react";
import { Component } from "react";
// 定义类式组件
export default class Welcome extends Component {
  render() {
    return (
      <h1 className="title">Welcome组件</h1>
    )
  }
}

能使用以上引用方式是因为 React 中使用了 默认暴露分别暴露

js 复制代码
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,用法如下:

js 复制代码
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,用法如下:

js 复制代码
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(优先匹配前端资源)

js 复制代码
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 配置具体代理规则:

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":""}
    })
  )
}

使用:

js 复制代码
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

js 复制代码
npm i pubsub-js

2)消息订阅

js 复制代码
componentDidMount() {
 	// 消息订阅
 	// 消息订阅,回调里面接收两个参数,第一个是消息名,这里也就是 updateState,第二个是消息发布时携带的参数
    this.token = PubSub.subscribe("updateState",(_, stateObj)=> {
      this.setState(stateObj)
    })
}

3)消息发布

js 复制代码
PubSub.publish("updateState", {users: res.data.items, isLoadding: false})

4)取消订阅

js 复制代码
componentWillUnmount() {
 	 // 取消订阅
     PubSub.unsubscribe(this.token)
}

9、路由 react-router-dom

基本使用:

  1. a 标签改为 Link 标签
    <Link className='menu-item' to="/home">home</Link>
  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: ""
相关推荐
qq_3901617715 分钟前
防抖函数--应用场景及示例
前端·javascript
John.liu_Test44 分钟前
js下载excel示例demo
前端·javascript·excel
Yaml41 小时前
智能化健身房管理:Spring Boot与Vue的创新解决方案
前端·spring boot·后端·mysql·vue·健身房管理
PleaSure乐事1 小时前
【React.js】AntDesignPro左侧菜单栏栏目名称不显示的解决方案
前端·javascript·react.js·前端框架·webstorm·antdesignpro
哟哟耶耶1 小时前
js-将JavaScript对象或值转换为JSON字符串 JSON.stringify(this.SelectDataListCourse)
前端·javascript·json
getaxiosluo1 小时前
react jsx基本语法,脚手架,父子传参,refs等详解
前端·vue.js·react.js·前端框架·hook·jsx
理想不理想v1 小时前
vue种ref跟reactive的区别?
前端·javascript·vue.js·webpack·前端框架·node.js·ecmascript
知孤云出岫1 小时前
web 渗透学习指南——初学者防入狱篇
前端·网络安全·渗透·web
贩卖纯净水.1 小时前
Chrome调试工具(查看CSS属性)
前端·chrome
栈老师不回家2 小时前
Vue 计算属性和监听器
前端·javascript·vue.js