一、創(chuàng)建項(xiàng)目并啟動(dòng)
第一步:全局安裝:npm install -g create-react-app
第二步:切換到想創(chuàng)建項(xiàng)目的目錄,使用命令create-react-app hello-react
第三步:進(jìn)入項(xiàng)目目錄,cd hello-react
第四步:啟動(dòng)項(xiàng)目,npm start
二、目錄結(jié)構(gòu)
1、目錄結(jié)構(gòu)
其中,public/index.htm,src/App.js,src/index.js 三個(gè)是最重要的文件。
+ node_module ------ 第三方資源
+ public ------ 靜態(tài)資源文件夾
+ favicon.ico ------ 網(wǎng)站頁面圖標(biāo)
+ index.html ------ 主頁面
+ logo192.png ------ logo 圖
+ logo512.png ------ logo 圖
+ manifest.json ------ 應(yīng)用加殼的配置文件
+ robots.txt ------ 爬蟲協(xié)議文件
+ 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、文件內(nèi)容說明
public/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<!-- 開啟理想窗口,用于做移動(dòng)端網(wǎng)頁的適配 -->
<meta name="viewport" content="width=device-width, initial-scale=1" />
<!-- 用于配置瀏覽器頁簽+地址欄的顏色(僅支持安卓手機(jī)瀏覽器,兼容性較差) -->
<meta name="theme-color" content="#000000" />
<meta name="description" content="Web site created using create-react-app"/>
<!-- 用于指定網(wǎng)頁添加到手機(jī)主屏幕后到圖標(biāo)(僅支持 apple 手機(jī)) -->
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!-- 應(yīng)用加殼時(shí)到配置 -->
<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 標(biāo)簽會(huì)自動(dòng)校驗(yàn) react 語法,遇到一些將要遺棄或不推薦使用的語法,會(huì)提示
// 不加 React.StrictMode 標(biāo)簽,直接使用 App 組件也沒啥影響
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
reportWebVitals();
注意:舊版本的 src/index.js 中,渲染組件是通過方法ReactDOM.render(<App/>,el)
實(shí)現(xiàn)的:
import React from "react";
import App from "./App"
// 舊版本引入 ReactDOM ,然后 執(zhí)行 ReactDOM.render()
import ReactDOM from "react-dom"
ReactDOM.render(<App />, document.getElementById("root"))
而新版本18.0.2
是通過ReactDOM.createRoot(el).render(<App/>)
實(shí)現(xiàn)的:
import React from "react";
import App from "./App"
// 新版本引入方式,利用 ReactDOM.createRoot() 創(chuàng)建節(jié)點(diǎn),然后執(zhí)行 render 函數(shù)
import ReactDOM from "react-dom/client"
ReactDOM.createRoot(document.getElementById("root"))
.render(
<App/>
)
三、開發(fā)注意事項(xiàng)
1、組件命名
組件可以以js
為后綴,也可以以 jsx 為后綴,以 jsx 為后綴可以明顯區(qū)別于其他功能性的 js 文件。
2、引入 React 和 Component
1)只引 React,定義類組件時(shí)使用 React.Component
import React from "react";
// 定義類式組件
export default class Hello extends React.Component {
render() {
return (
<h1 className={hello.title}>Hello 組件</h1>
)
}
}
2)解構(gòu)引入 Component,定義類組件時(shí)直接使用 Component
// React 中使用了默認(rèn)暴露和分別暴露,所以可以使用下面的引入方式
// import React, { Component } from "react";
import { Component } from "react";
// 定義類式組件
export default class Welcome extends Component {
render() {
return (
<h1 className="title">Welcome組件</h1>
)
}
}
能使用以上引用方式是因?yàn)?React 中使用了 默認(rèn)暴露 和 分別暴露
class React {
}
// 分別暴露 Component
export class Component {
}
React.Component = Component
// 默認(rèn)暴露 React
export default React
--------------------------------------
// 其他文件引用時(shí)可以這樣:
import React, { Component } from "react";
import { Component } from "react";
3、引入 ReactDOM
1)新版本18.0.2
中,要從 react-dom/client
中引入 ReactDOM
,用法如下:
import React from "react";
// 新版本引入 ReactDOM,渲染節(jié)點(diǎn)時(shí)使用 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 模塊化
為什么
按照截圖方式在同一個(gè)組件中引用多個(gè)組件,如果 Hello 和 Welcome 組件存在相同類的不同樣式時(shí),后者會(huì)覆蓋前者,所以需要模塊化樣式,使其互不影響。
怎么做
- 將 .css 文件改為 .module.css 文件
- 引入 css 文件時(shí),使用
import hello from "./Hello.module.css"
代替import "./Hello.module.css"
- 組件標(biāo)簽中使用 hello.title,
<h1 className={hello.title}>Hello 組件</h1>
編譯出來是如下效果:
5、組件通信
父子通信:直接通過組件標(biāo)簽的屬性進(jìn)行傳值,子組件中通過 props 可以接受
祖孫通信:遵循狀態(tài)在哪里,操作狀態(tài)的方法就在哪里的原則,將所有修改 state 數(shù)據(jù)的方法都定義在 state 所在的組件中,給子組件標(biāo)簽添加(方法)屬性funcName={funcName}
-》孫組件標(biāo)簽添加(方法)屬性funcName={funcName}
-》孫組件內(nèi)部根據(jù)需要,調(diào)用傳過來的方法this.props.funcName()
6、跨域
前提:本地前端項(xiàng)目地址:http://localhost:3000
1)法一:配置在 packge.json 中
- 在
package.json
中配置"proxy": "http://localhost:5000"
- 組件中使用
axios.get("http://localhost:3000/students").then()
接口請(qǐng)求時(shí)會(huì)先在 3000 端口服務(wù)上找 /students 接口,找不到就去配置好的 5000 端口上找
說明:
1、優(yōu)點(diǎn):配置簡單,前端請(qǐng)求資源時(shí)可以不加任何前綴
2、缺點(diǎn):不能配置多個(gè)代理
3、工作方式:上述方式配置代理,當(dāng)前請(qǐng)求了3000不存在的資源時(shí),那么該請(qǐng)求會(huì)轉(zhuǎn)發(fā)給 5000(優(yōu)先匹配前端資源)
axios.get("http://192.168.31.229:3000/students").then(
(res) => {console.log("學(xué)生接口調(diào)用成功",res)},
(err) => {console.log("學(xué)生接口調(diào)用失敗", err)}
)
2)法二:配置在 setupProxy.js 中
1)第一步:創(chuàng)建代理配置文件:在 src 下創(chuàng)建配置文件:src/setupProxy.js
2)編寫 setupProxy.js 配置具體代理規(guī)則:
const { createProxyMiddleware } = require("http-proxy-middleware")
module.exports = function (app) {
app.use(
createProxyMiddleware("/api1", { // api1 是需要轉(zhuǎn)發(fā)的請(qǐng)求(所有帶有 /api1 前綴的請(qǐng)求都會(huì)轉(zhuǎn)發(fā)給5000)
target: "http://localhost:5000", // 配置轉(zhuǎn)發(fā)目標(biāo)地址(能返回?cái)?shù)據(jù)的服務(wù)器地址)
changeOrigin: true, // 控制服務(wù)器接收到的請(qǐng)求頭中 host 字段的值
/*
changeOrigin 為true時(shí),服務(wù)器收到的請(qǐng)求頭中的 host 為 http://localhost:5000
changeOrigin 為false時(shí),服務(wù)收到的請(qǐng)求頭中的 host 為前端工程的服務(wù)器的host(http://localhost:3000)
changeOrigin 默認(rèn)為false,但我們一般將changeOrigin的值設(shè)為true
*/
pathRewrite: {"^/api1":""} // 去除請(qǐng)求前綴,保證交給后臺(tái)服務(wù)器的是正常請(qǐng)求地址(必須配置)
}),
createProxyMiddleware("/api2", {
target: "http://localhost:5001",
changeOrigin: true,
pathRewrite: {"^/api2":""}
})
)
}
使用:
axios.get("http://192.168.31.229:3000/api1/students").then(
(res) => {console.log("學(xué)生接口調(diào)用成功",res)},
(err) => {console.log("學(xué)生接口調(diào)用失敗", err)}
)
說明:
1.優(yōu)點(diǎn):可以配置多個(gè)代理,可以靈活控制是否走代理
2.缺點(diǎn):配置繁瑣,前端請(qǐng)求資源時(shí)必須加前綴
7、組件通信
1、動(dòng)態(tài)初始化列表,如何確定將數(shù)據(jù)放在哪個(gè)組件的state中?
- 某個(gè)組件使用:放在自身的state中
- 某些組件使用:放在他們共同的父組件的state中(官方稱此操作為:狀態(tài)提升)
2、關(guān)于父子組件通信:
- 【父組件】給【子組件】傳遞數(shù)據(jù):通過props傳遞
- 【子組件】給【父組件】傳遞數(shù)據(jù):通過props傳遞,要求父組件提前給子組件傳遞一個(gè)函數(shù)func,子組件通過this.props.func調(diào)用
3、狀態(tài)在哪里,操作狀態(tài)的方法就在哪里
8、消息訂閱與發(fā)布(個(gè)組件間進(jìn)行通信)
1)下載 pubsub-js
npm i pubsub-js
2)消息訂閱
componentDidMount() {
// 消息訂閱
// 消息訂閱,回調(diào)里面接收兩個(gè)參數(shù),第一個(gè)是消息名,這里也就是 updateState,第二個(gè)是消息發(fā)布時(shí)攜帶的參數(shù)
this.token = PubSub.subscribe("updateState",(_, stateObj)=> {
this.setState(stateObj)
})
}
3)消息發(fā)布
PubSub.publish("updateState", {users: res.data.items, isLoadding: false})
4)取消訂閱
componentWillUnmount() {
// 取消訂閱
PubSub.unsubscribe(this.token)
}
9、路由 react-router-dom
基本使用:
- a 標(biāo)簽改為 Link 標(biāo)簽或者 NavLink
<Link className='menu-item' to="/home">home</Link>
<NavLink className='menu-item' to="/home">home</NavLink>
NavLink標(biāo)簽與Link相比,當(dāng)前頁面匹配的菜單會(huì)自動(dòng)增加一個(gè) active 類,或者可以使用 activeClassName 修改為需要的類名 - 展示區(qū)用 Route 標(biāo)簽進(jìn)行路徑的匹配
<Route path="/home" component={Home}></Route>
- 的最外側(cè)包裹一個(gè)
<BrowserRouter>
或<HashRouter>
10、路由組件與一般組件
- 寫法不同
一般組件:
路由組件: - 存放位置不同:
一般組件:components
路由組件:pages - 接收到的props不同:
一般組件:寫組件標(biāo)簽時(shí)傳遞了什么,就能接收到什么
路由組件:接收到三個(gè)固定屬性
- history:
go, goBack, goForward, push, replace (方法) - location:
pathname:“”, search:“”, state: “” - match:
params: {}, path: “”, url: “”
11、路由
路由渲染時(shí),若有多個(gè)相同 path,不同 component 路由,取并集,也就是如下代碼,沒有 Switch 標(biāo)簽包裹時(shí),/home 路由同時(shí)顯示 Home 組件和 Test 組件。
Switch 包裹時(shí),/home 路由顯示第一個(gè)匹配到的組件,也就是 Home 組件。
<Switch>
<Route path="/about" component={About}></Route>
<Route path="/home" component={Home}></Route>
<Route path="/home" component={Test}></Route>
</Switch>
react 封裝組件:
- 標(biāo)簽體內(nèi)容是一個(gè)特殊的標(biāo)簽屬性
- 子組件可以通過 this.props.children 可以獲取標(biāo)簽體內(nèi)容,可以直接通過屬性方式寫入標(biāo)簽體內(nèi)容。
<!-- 這里的 to="/about" 和 內(nèi)容 about 會(huì)以 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)多層路由時(shí),樣式丟失
<link rel="stylesheet" href="./css/base.css">
場景:例如路由前要加前綴,為 /qiao/about,如果頁面通過相對(duì)路徑,引入了 public 目錄中的樣式 base.css,頁面刷新時(shí),base.css 樣式會(huì)丟失。
原因:開發(fā)模式默認(rèn)打開 http://192.168.31.229:3000/,這時(shí)候 public/css/base.css 從 public 中能正常拿到,此時(shí)樣式是正常的。點(diǎn)擊路由跳轉(zhuǎn)到 http://192.168.31.229:3000/qiao/about,此時(shí)刷新頁面,因?yàn)閺?public 下找不到 qiao/about,會(huì)默認(rèn)渲染 index.html 頁面,而且樣式文件因?yàn)槭褂孟鄬?duì)路口,路徑轉(zhuǎn)化為 public/qiao/css/base.css,樣式路徑有誤,所以樣式獲取不到。
解決方法:
- index.html 中引入樣式使用絕對(duì)路徑,將
./XXX
改為/XXX
- index.html 中引入樣式使用
%PUBLIC_URL%
,將./XXX
改為%PUBLIC_URL%/XXX
- 使用 hash
HashRouter
模式代替 history 模式BrowserRouter
2)路由的嚴(yán)格匹配和模糊匹配
- 默認(rèn)使用的是模糊匹配(簡單記:輸入的路徑 必須要包含 匹配的路徑,且順序要一致)
- 開啟嚴(yán)格匹配
- 嚴(yán)格匹配不要隨便開啟,需要時(shí)再開,有些時(shí)候開啟會(huì)導(dǎo)致無法繼續(xù)匹配二級(jí)路由
<MyNavLink to="/home/a/b">home</MyNavLink>
<Route exact path="/home" component={Home}></Route>
文章來源:http://www.zghlxwxcb.cn/news/detail-823896.html
3)Redirect
- 一般寫在所有路由注冊(cè)的最下方,當(dāng)所有路由都無法匹配是,跳轉(zhuǎn)到 Redirect 指定的路由。
<Switch>
<Route path="/about" component={About}></Route>
<Route path="/home" component={Home}></Route>
<Redirect to="/about"/>
</Switch>
4)嵌套路由
- 注冊(cè)子路由時(shí)要寫上父路由的 path 的值(/home/news)
- 路由的匹配是按照注冊(cè)路由的順序進(jìn)行的 (先匹配home路由的組件,在匹配news路由的組件)
5)路由傳參
- params 參數(shù)
路由鏈接(攜帶參數(shù)):<Link to={
/home/message/detail/tom/18}>詳情</Link>
注冊(cè)路由(聲明接收):<Route path="/home/message/detail/:name/:age" component={Detail}/>
接收參數(shù):this.props.match.params
- search 參數(shù)
路由鏈接(攜帶參數(shù)):<Link to={
/home/message/detail?name=tom&age=18}>詳情</Link>
注冊(cè)路由(無需聲明,正常注冊(cè)即可):<Route path="/home/message/detail" component={Detail}/>
接收參數(shù):this.props.location.search
備注:獲取到的 search 是 urlencoded 編碼字符串,需要借助 qs 解析 - state 參數(shù)
路由鏈接(攜帶參數(shù)):<Link to={{pathname: "/home/message/detail", state: {name: "tom", age: 18}}}>{item.title}</Link>
注冊(cè)路由(無需聲明,正常注冊(cè)即可):<Route path="/home/message/detail" component={Detail}/>
接收參數(shù):this.props.location.state
備注:刷新也可以保留參數(shù),但是清除緩存,history所有數(shù)據(jù)就會(huì)被清空,刷新無法獲取參數(shù)
6)push和replace
Link 標(biāo)簽?zāi)J(rèn)是 push 默認(rèn),要打開 replace 模式,需要在標(biāo)簽中增加屬性 replace={true},或者簡寫 replace文章來源地址http://www.zghlxwxcb.cn/news/detail-823896.html
<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>
<!-- 默認(rèn)是push、 -->
<Link to={{pathname: "/home/message/detail", state: {id: item.id, title: item.title}}}>{item.title}</Link>
到了這里,關(guān)于使用 create-react-app 創(chuàng)建 react 應(yīng)用的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!