react项目详细搭建教程
react项目搭建create-router-dom,redux详细解说
1.搭建react脚手架
首先选择脚手架,dav-cli,create-react-app,Ant-Design-Pro-cli。脚手架即为代码层次。这里我们选用create-react-app脚手架
打开我们的cmd,window+R输入cmd进入终端,然后安装我们的脚手架
项目实例:https://gitee.com/getluoaxios/raect_dom
npm install -g create-react-app
脚手架安装完成后,我们开始创建react新项目,稍微等一下,创建比较慢
create-react-app 名字
进入到当前项目
cd dome-react
启动项目,下面红框包裹的为显示页面的链接
npm start
到此一个基本的脚手架就已经创建好了
目录:
node_modules
存放项目依赖的第三方库和模块。这个目录是由 npm 自动生成的,不需要手动管理。public
存放我们的静态资源src
存放项目源代码的目录。这是你主要的开发目录。index
入口文件,react的页面会渲染到index.html,root元素里面package.json
项目的配置文件,包含项目的依赖、脚本、项目元数据等。你可以在这里定义项目的各种设置和依赖。APP.js
这是一个示例组件,作为应用的主要组件。通常,这个文件会包含应用的主要结构和逻辑。
2.React-route-dom路由使用
npm i react-router-dom
路由的基本使用
- 明确好界面中那块是导航区那一块是展示区
- 导航区使用Link不再使用a标签
<Link to="/xxxxx">Demo</Link>
- 展示区写Route标签进行路径的匹配
<Route path='/xxxx' component={Demo}/>
path为路径 component为展示的页面路径
<App>
的最外侧包裹了一个<BrowserRouter>
或<HashRouter>
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import {BrowserRouter} from "react-router-dom"const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<React.StrictMode><BrowserRouter><App/></BrowserRouter></React.StrictMode>
);reportWebVitals();
HashRouter和BrowserRouter一样,只不过HashRoute在url里是有hash值的
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import {HashRouter} from "react-router-dom"const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<React.StrictMode><HashRouter><App/></HashRouter></React.StrictMode>
);reportWebVitals();
路由组件与一般组件
1.写法不同
一般组件:< Dome />
路由组件:< Route path='/xxxx' component={Demo} />
2.存放位置不同
一般组件:components
路由组件:pages,views
3.接收参数不同
一般组件:组件传什么过来,就接收什么过来
路由组件:接收固定的三个属性值
history:go: ƒ go(n)goBack: ƒ goBack()goForward: ƒ goForward()push: ƒ push(path, state)replace: ƒ replace(path, state)
location:pathname: "/about"search: ""state: undefined
match:params: {}path: "/about"url: "/about"
Switch
在React中相同路径可以加载多个组件的,假如你不想,你可以用
Switch
包裹一下Route
,这样即使相同路径下,也只是加载最上面的组件
<Switch><Route path="/home/news" component={News}/><Route path="/home/massage" component={Message}/><Route path="/home/massage" component={Detail}/><Redirect to="/home/news"/>
</Switch>
Redirect
在react中我们想在一开始默认加载哪个组件或者路径不适配的时候我们可以用
Redirect
标签,可以给我们重定向加载任何一个组件,但Redirect
一般出现在Route的最下面
<Switch><Route path="/home/news" component={News}/><Route path="/home/massage" component={Message}/><Redirect to="/home/news"/>
</Switch>
路由的嵌套
NavLink组件
假设现在我们需要给予选中路由一个高亮我们可以使用
NavLink
标签,我们可以把他单独封装成一个公用的路由组件
import React, {Component, Fragment} from "react"
import {NavLink} from 'react-router-dom'
import "./MyNavLink.css"export default class MyNavLink extends Component {render() {return (<Fragment><div className="btn-div"><NavLink activeClassName="active" className="btn-a" {...this.props}/></div></Fragment>)}
}
注意高亮默认在React-Router中是action属性
如果我们想在react项目中,写入多级路由的话,我们需要在
Link,Route以及Redirect
中加入父级路由
父级路由组件
import {Fragment} from "react"
import {Route, Switch, Redirect} from "react-router-dom"
import Title from "./component/Title";
import About from "./pages/About";
import Home from "./pages/Home";
import MyNavLink from "./MyNavLink";import './App.css';function App() {return (<Fragment><Title/><MyNavLink to="/about">About</MyNavLink><MyNavLink to='/home'>Home</MyNavLink><Switch><Route path="/about" component={About}/><Route path="/home" component={Home}/><Redirect to="/about"/></Switch></Fragment>);
}export default App;
子组件路由
import React, {Component, Fragment} from "react"
import {Switch, Route, Redirect} from 'react-router-dom'
import MyNavLink from "../../MyNavLink";
import Message from "./Message";
import News from "./News";export default class Home extends Component {render() {return (<Fragment><h1>我是Home</h1><br/><MyNavLink to="/home/news">News</MyNavLink><MyNavLink to="/home/massage">Massage</MyNavLink><Switch><Route path="/home/news" component={News}/><Route path="/home/massage" component={Message}/><Redirect to="/home/news"/></Switch></Fragment>)}
}
路由的模糊匹配
<Fragment><Title/><MyNavLink to="/about">About</MyNavLink><MyNavLink to='/home/a/b'>Home</MyNavLink><Switch><Route path="/about" component={About}/><Route path="/home" component={Home}/><Redirect to="/about"/></Switch>
</Fragment>
像这样的路由我们多传,虽然路径不匹配但在模糊匹配下可以让react渲染Home组件,但是不可以头路径不正确
例如:
<Fragment><Title/><MyNavLink to="/about">About</MyNavLink><MyNavLink to='/a/home'>Home</MyNavLink><Switch><Route path="/about" component={About}/><Route path="/home" component={Home}/><Redirect to="/about"/></Switch>
</Fragment>
这样前面a/home
是错误的,匹配不到的
路由的严格匹配
假如现在我们不希望进行模糊匹配,严格按照路径的话我们就可以开启严格匹配模式
<Fragment><Title/><MyNavLink to="/about">About</MyNavLink><MyNavLink to='/a/home'>Home</MyNavLink><Switch><Route exact={true} path="/about" component={About}/><Route exact path="/home" component={Home}/><Redirect to="/about"/></Switch>
</Fragment>
路由的参数
传递Params参数
this.state.massageArr.map(item => {return <li key={item.id}><Link to={`/home/massage/detail/${item.id}/${item.title}`}>{item.title}</Link></li>})
我们通过数组的映射以及模版字符串的方式传递了两个Params参数
接收Params参数
<Route path="/home/massage/detail/:id/:title" component={Detail}/>
调用Params参数
export default class Detail extends Component {render() {const {id, title} = this.props.match.paramsconst findResult = data.find((_item) => {return _item.id === id})return (<Fragment><ul><li>id:{id}</li><li>title:{title}</li><li>content:{findResult.content}</li></ul></Fragment>)}
}
我们可以通过props里面的match里面封装的params
里面将传递的值解构出来使用
传递Search参数
this.state.massageArr.map(item => {return <li key={item.id}><Link to={`/home/massage/detail?id=${item.id}&title=${item.title}`}>{item.title}</Link></li>})
同样我们也传递了两个Search参数,但注意Search参数不需要接收
querystring库
urlencoded编码
key=value&key=value
像这样的编码格式被称为urlencoded
编码格式
利用 querystring转化格式
import qs from "querystring"qs.stringify() 将对象转化为urlencoded
qs.parse() 将urlencoded转化为js中的对象
调用Search参数
import React, {Component, Fragment} from "react"
import qs from "querystring"const data = [{id: "01", content: "你好,宁夏"},{id: "02", content: "你好,吉林"},{id: "03", content: "你好,陕西"},
]export default class Detail extends Component {render() {const {search} = this.props.locationconst {id, title} = qs.parse(search.slice(1))const findResult = data.find((_item) => {return _item.id === id})return (<Fragment><ul><li>id:{id}</li><li>title:{title}</li><li>content:{findResult.content}</li></ul></Fragment>)}
}
如果没有这个第三方库我们可以安装
npm i querystring
state参数
this.state.massageArr.map(item => {return <li key={item.id}>
<Link to={{pathname: "/home/massage/detail", state: {id: item.id, title: item.title}}}>{item.title}</Link></li>})
调用state
import React, {Component, Fragment} from "react"const data = [{id: "01", content: "你好,宁夏"},{id: "02", content: "你好,吉林"},{id: "03", content: "你好,陕西"},
]export default class Detail extends Component {render() {const {id, title} = this.props.location.stateconst findResult = data.find((_item) => {return _item.id === id})return (<Fragment><ul><li>id:{id}</li><li>title:{title}</li><li>content:{findResult.content}</li></ul></Fragment>)}
}
编程式路由导航
编程式路由导航push是压栈式的,浏览器可以回退,有保存记录。而replace是覆盖浏览器的栈顶不能回退
replace编程式路由导航
const replaceShow = (id, title) => { this.props.history.replace(`/home/massage/detail/${id}/${title}`)}
注意只有在路由组件里面才可以使用这两个方法
push编程式路由导航
const pushShow = (id, title) => { this.props.history.push(`/home/massage/detail/${id}/${title}`)}
withRouter
如果我们想在非路由组件里调用路由组件的方法我们就可以用withRouter
import React, {Component, Fragment} from "react"
import {withRouter} from "react-router-dom"class Title extends Component {back = () => {this.props.history.goBack()}forWord = () => {this.props.history.goForward()}render() {return (<Fragment><h2 style={{marginLeft: "50px"}}>React Router Demo</h2><button onClick={this.forWord}>前进</button><button onClick={this.back}>后退</button><hr/></Fragment>)}
}export default withRouter(Title)
注意:goBack为历史记录的后退,而goForward为前进,两个函数后可以在路由组件找到
React-Router-Dom6
安装
npm i react-router-dom@6
内置组件以及写法有些改变,函数式组件被官方明确推荐,又新增10个hooks
删除Switch,新增Routes
import {NavLink, Routes, Route} from "react-router-dom"<Routes><Route path="/about" element={<About/>}/><Route path="/home" element={<Home/>}/>
</Routes>
重定向
import {NavLink, Routes, Route} from "react-router-dom"<Routes><Route path="/about" element={<About/>}/><Route path="/home" element={<Home/>}/><Route path="/" element={<Navigate to="/about"/>}/>
</Routes>
高亮
<NavLink to="/about" className={({isActive}) => isActive ? "list-group-item active":"list-group-item"}>About</NavLink>
在react-router-dom6中可以用函数的方式返回出样式
路由表
import {NavLink, useRoutes} from "react-router-dom"
import route from "@/route/index"
const element = useRoutes(route)
我们可以通过路由表的方式生成路由
route/index
import About from "../pages/About";
import Home from "../pages/Home";
import {Navigate} from "react-router-dom";export const route = [{path: "/about",element: <About/>},{path: "/home",element: <Home/>},{path: "/",element: <Navigate to="/about"/>}
]
多级路由也可以写在路由表里,但是需要Outlet配合,类似Vue的roter-view
多级路由的路由表
import About from "../pages/About";
import Home from "../pages/Home";
import Message from "../pages/Message";
import News from "../pages/News";
import {Navigate} from "react-router-dom";export const route = [{path: "/about",element: <About/>},{path: "/home",element: <Home/>,children: [{path: "news",element: <News/>},{path: "message",element: <Message/>}]},{path: "/",element: <Navigate to="/about"/>}
]
子路由可以用children来写,类型也是数组,注意路径不要加 “/”
Outlet是子路由的出口
import React, {Fragment, useState} from "react"
import {Navigate,Outlet,NavLink} from "react-router-dom"export default function Home() {const [sum, setSum] = useState(1)return (<Fragment><h3>Home</h3>{sum === 2 ? <Navigate to="/about"/> : <h4>当前sum为{sum}</h4>}<button onClick={() => setSum(sum + 1)}>点我加一</button><NavLink to="message">message</NavLink><NavLink to='news'>news</NavLink><Outlet/></Fragment>)
}
Params参数和之前的一样传,接收参数有所改变
import React, {Fragment} from "react"
import {useParams} from "react-router-dom"export default function Detail() {const {id, title, content} = useParams()return (<Fragment><ol><li>{id}</li><li>{title}</li><li>{content}</li></ol></Fragment>)
}
Search参数接收
import React, {Fragment} from "react"
import {useSearchParams} from "react-router-dom"export default function Detail() {const [search, setSearch] = useSearchParams()const id = search.get("id")const title = search.get("title")const content = search.get("content")return (<Fragment><ol><li>{id}</li><li>{title}</li><li>{content}</li></ol><button onClick={() => {setSearch('id=008&title=哈哈&content=嘻嘻')}}>点我更新Search</button></Fragment>)
}
state参数
import React, {Fragment, useState} from "react"
import {Link, Outlet} from "react-router-dom"export default function Message() {const [message] = useState([{id: "001", title: "消息1", content: "锄禾日当午"},{id: "002", title: "消息2", content: "汗滴禾下土"},{id: "003", title: "消息3", content: "谁知盘中餐"},{id: "004", title: "消息4", content: "粒粒皆辛苦"},])return (<Fragment><ul>{message.map((_item) => {return <li key={_item.id}><Linkto="detail"state={{id: _item.id,title: _item.title,content: _item.content}}>{_item.title}</Link></li>})}</ul><hr/><Outlet/></Fragment>)
}
useLocation
import React, {Fragment} from "react"
import {useLocation} from "react-router-dom"export default function Detail() {const {state: {id, title, content}} = useLocation()return (<Fragment><ol><li>{id}</li><li>{title}</li><li>{content}</li></ol></Fragment>)
}
我们需要连续解构赋值,在useLocation中将状态state解构,再将想要的状态从stae中结构
编程式路由导航
useNavigate
import React, {Fragment, useState} from "react"
import {Link, Outlet, useNavigate} from "react-router-dom"export default function Message() {const navigate = useNavigate()function showDetail(item) {navigate("detail", {replace: false,state: {id: item.id,title: item.title,content: item.content}})}const [message] = useState([{id: "001", title: "消息1", content: "锄禾日当午"},{id: "002", title: "消息2", content: "汗滴禾下土"},{id: "003", title: "消息3", content: "谁知盘中餐"},{id: "004", title: "消息4", content: "粒粒皆辛苦"},])return (<Fragment><ul>{message.map((_item) => {return <li key={_item.id}><Linkto="detail"state={{id: _item.id,title: _item.title,content: _item.content}}>{_item.title}</Link><button onClick={() => {showDetail(_item)}}>查看详情</button></li>})}</ul><hr/><Outlet/></Fragment>)
}
前进和后退
import React, {Fragment} from "react";
import {useNavigate} from "react-router-dom"export default function Title() {const navigate = useNavigate()function back() {navigate(-1)}function forWord() {navigate(1)}return (<Fragment><h2 style={{marginLeft: "50px"}}>React Router Demo</h2><button onClick={forWord}>前进-》</button><button onClick={back}>《-后退</button><hr/></Fragment>)
}
Redux的基本使用
介紹:
- redux是一个专门用于做
状态管理
的JS库(不是react插件库)
。 - 它可以用在
react, angular, vue
等项目中, 但基本与react配合使用
。 - 作用: 集中式管理react应用中
多个组件共享的状态
什么情况下需要使用redux
- 某个组件的状态,需要让其他组件可以
随时拿到(共享)
。 - 一个组件需要改变另一个组件的状态(
通信
)。 - 总体原则:
能不用就不用
, 如果不用比较吃力才考虑使用
。
二,redux的三个核心概念
1,action
动作的对象
包含2个属性
type:标识属性, 值为字符串, 唯一, 必要属性
data:数据属性, 值类型任意, 可选属性例子
:{ type: ‘ADD_STUDENT’,data:{name: ‘tom’,age:18} }
2,reducer
- 用于初始化状态、加工状态。
- 加工时,根据旧的state和action, 产生新的state的纯函数。
3,store
-
将state、action、reducer联系在一起的对象
-
如何得到此对象?
-
import {createStore} from 'redux' import reducer from './reducers' const store = createStore(reducer)
-
此对象的功能?
getState(): 得到state
dispatch(action): 分发action, 触发reducer调用, 产生新的state
subscribe(listener): 注册监听, 当产生了新的state时, 自动调用
redux的核心API
1.createstore
作用:创建包含指定reducer的store对象
2,store对象
-
作用: redux库最核心的管理对象
-
它内部维护着:
state
reducer -
核心方法:
-
store.getState()
store.dispatch({type:‘INCREMENT’, number})
store.subscribe(render)
3,applyMiddleware()
作用:应用上基于redux的中间件(插件库)
4,combineReducers()
作用:合并多个reducer函数
使用redux编写应用
安装:
npm install react-redux
npm install @reduxjs/toolkit
2,新建文件
src路径下,新建store文件
3,定义仓库:
新建src / store / index.js
// 定义仓库
// 引入configureStore 定义仓库
import { configureStore } from "@reduxjs/toolkit";
// 导入counterSlice
import counter from "./counterSlice";
// 导出
export const store = configureStore({// 数据处理reducer: {counter}
});
3,定义仓库:
新建src / store / index.js
/** @Author: hukai huzhengen@gmail.com* @Date: 2024-11-04 15:12:39* @LastEditors: hukai huzhengen@gmail.com* @LastEditTime: 2024-11-04 15:12:51* @FilePath: \dome-react\src\store\index.js* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE*/
// 定义仓库
// 引入configureStore 定义仓库
import { configureStore } from "@reduxjs/toolkit";
// 导入counterSlice
import counter from "./counterSlice";
// 导出
export const store = configureStore({// 数据处理reducer: {counter}
});
4,创建计数器数据,及修改数据的方法:
新建src / store / counterSlice.js
/** @Author: hukai huzhengen@gmail.com* @Date: 2024-11-04 15:13:24* @LastEditors: hukai huzhengen@gmail.com* @LastEditTime: 2024-11-04 15:13:32* @FilePath: \dome-react\src\store\counterSlice.js* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE*/
// 创建计数器切片slice
// 导入创建切片的函数
import { createSlice } from "@reduxjs/toolkit";
// 定义初始化状态
const initialState = { value: 0 };
// 创建切片
const counterSlice = createSlice({// 切片名称name: "counter",// 初始化状态initialState,// 定义处理器reducers: {// 处理加法increment: state => {state.value += 1;},// 处理减法decrement: state => {state.value -= 1;},// 处理加法addValue: (state, action) => {state.value += action.payload;}}
});// 导出动作
export const { increment, decrement, addValue } = counterSlice.actions;
// 导出处理器
export default counterSlice.reducer;
// 导出异步操作动作
export const syncAddvalue = value => dispatch => {setTimeout(() => {dispatch(addValue(value));}, 2000);
};
5.在组件中使用redux
src / APP.js 文件:
import {increment,decrement,addValue,syncAddvalue
} from "./store/counterSlice";
import { useSelector, useDispatch } from "react-redux";
const APP = () => {// 获取仓库数据const count = useSelector(state => state.counter.value);// 获取修改仓库数据的工具const dispatch = useDispatch();return (<div><p>仓库数据:{count}</p><button onClick={() => dispatch(increment())}>+1</button><button onClick={() => dispatch(decrement())}>-1</button><button onClick={() => dispatch(addValue(5))}>+5</button><button onClick={() => dispatch(syncAddvalue(10))}>两秒后+10</button></div>);
};export default APP;
效果图