目录
router
介绍
- 当应用变得复杂的时候,就需要分块的进行处理和展示,传统模式下,我们是把整个应用分成了多个页面,然后通过 URL 进行连接。
这种方式也有一些问题,每次切换页面都需要重新发送所有请求和渲染整个页面,不止性能上会有影响,同时也会导致整个 JavaScript 重新执行,丢失状态,所以就出现了路由 - 优势:有更好的用户体验(减少请求和渲染和页面跳转产生的等待与空白),页面切换快
- 劣势:首次进入处理慢
基本跳转
它是基于 Web 的 React Router
所以使用的时候需要安转react-router
安装命令:npm i -S react-router-dom
- 引入组件BrowserRouter 它是用来在项目中控制路由跳转
index.js文件
import ReactDOM from 'react-dom';
import App from './app'
import { BrowserRouter } from 'react-router-dom';
ReactDOM.render(
<BrowserRouter>
<App/>
</BrowserRouter>,
document.getElementById('root')
);
跟BrowserRouter 相对应的组件还有HashRouter
import ReactDOM from 'react-dom';
import App from './app'
import { HashRouter} from 'react-router-dom';
ReactDOM.render(
<HashRouter>
<App/>
</HashRouter>,
document.getElementById('root')
);
HashRouter跟BrowserRouter 的区别
- 在url中可以看出HashRouter是包含#号的,而BrowerRouter是包含/号的
- BrowerRouter类似于Vue中的history模式,HashRouter类似于Vue中的hash模式
app.js文件(配置对应的url地址跳转)
import React from 'react'
import {Route} from 'react-router-dom'
import Home from './view/index'
import About from './view/about'
//引入Nav组件 (这个组件里面是配置跳转后对应的信息的组件)
import Nav from './component/nav'
function App(){
return (
<div className="App">
{/*exact : 属性表示路由使用 精确匹配模式,非 exact 模式下 '/' 匹配所有以 '/' 开头的路由 */}
{/* path 匹配对应的URL */}
<Route path='/' exact component={Home}/>
{/* render 属性来指定渲染函数,render 属性值是一个函数,当路由匹配的时候指定该函数进行渲染 */}
<Route path='/About' exact render={()=>{
return <About/>
}}/>
{/* link组件,他解析出来就是a标签,但是他自带的阻止a标签的默认行为,改变了url由路由处理*/}
<Nav></Nav>
</div>
)
}
export default App
Nav文件
import React from 'react'
// import {Link} from 'react-router-dom'
// NavLink 与 Link 类似,但是它提供了两个特殊属性用来处理页面导航
// 1.activeStyle:当前 URL 与 NavLink 中的 to 匹配的时候,激活 activeStyle 中的样式
// 2.activeClassName :当前 URL 与 NavLink 中的 to 匹配的时候,激活 className 中的样式
import {NavLink} from 'react-router-dom'
export default function Nav(){
return (
<nav>
{/* <Link to="/">首页</Link>
<span>|</span>
<Link to="/About">关于</Link> */}
<NavLink
to="/"
activeStyle={ {color:'red'} }
activeClassName="home"
exact
>首页</NavLink>
<span>|</span>
<NavLink
to="/About"
exact
activeStyle={ {color:'yellow'} }
activeClassName="about"
>关于</NavLink>
</nav>
)
}
到这里基本的路由跳转就完了
Switch属性
创建一个page404.js组件(随便弄一点内容来显示)
import React from 'react'
function a(){
return <div>页面没了</div>
}
export default a
引入到app.js文件中
import React,{useState} from 'react'
// 引入Switch:该组件只会渲染首个被匹配的组件
import {Route,Switch} from 'react-router-dom'
import Home from './view/index'
import About from './view/about'
import Nav from './component/nav'
import Page404 from './view/page'
function App(){
let [user, setUse] = useState('router')
return (
<div className="App">
<Switch>
{/* 只渲染page页面 */}
<Page404 component={Page404}/>
<Route path='/' exact component={Home}/>
<Route path='/About' exact render={()=>{
return <About/>
}}/>
<Nav></Nav>
</Switch>
</div>
)
}
export default App
props传参
import React,{useState} from 'react'
let [user, setUse] = useState('router')
<Route path='/About' exact render={()=>{
return <About user={user} setUse={setUse} />
}}/>
接收
这是在About组件中传的参所以去About组件中接收
import React from 'react'
function About(props) {
console.log(props);
let {user, setuse} = props;
return <h1>关于</h1>
}
export default About
路由参数
- history : 历史记录以及路由给我们的一些操作
- location : 获取当前URL的一些信息
pathname : 当前的URL
search : 接口参数
state : 跳转路由时,传递的参数 - match : 当前路由匹配的相关规则
params : 动态路由传过来的参数
import React from 'react'
function Home(props){
console.log(props);
let {history}=props
return <div>
<h1>首页</h1>
<button onClick={()=>{
goBack()
// history里面的方法
// history.go(-1)
// goBack 后退一步(常用)
// goForword 前进一步
history.goBack()
// history.push('url')
// push:修改当前的url(常用)
// replace:修改当前的url
// history.push("/Cont")
// history.replace('/About')
}
}>点击</button>
</div>
}
export default Home
hooks (Router5.0之后出现的)
- 不能在类组件内使用
- useHistory : 获取History对象
- useLocation : 获取Location对象
- useParams : 获取Params
- useRouteMatch : 获取Match
withRouter(高阶组件,高阶函数,高阶路由)
作用:不是刚创建的组件都会有路由对象,刚创建的路由里面打印props是空对象 如果你加上withRouter组件后就会有路由对象
import React from 'react'
// 如果一个组件不是路由绑定组件,那么该组件的 props 中是没有路由相关对象的,
// 虽然我们可以通过传参的方式传入,但是如果结构复杂,这样做会特别的繁琐。
// 我们可以通过 withRouter 方法来注入路由对象
import {withRouter,useHistory,useLocation,useParams,useRouteMatch} from 'react-router-dom'
import Inner from './inner'
function Cont(props){
console.log(props);
console.log(useHistory());//获取History对象
console.log(useLocation());//获取Location对象
console.log(useParams());//获取Params
console.log(useRouteMatch());//获取Match
let {goBack}=useHistory()
return <div>
<Inner/>
<h1>详情</h1>
<button onClick={
()=>{
goBack()
}
}>点击触发goBack</button>
</div>
}
export default withRouter(Cont)
介绍Redux
- redux与vuex一样是一个组件的状态(数据)管理器,当我们需要在项目各组件中共享数据时可以使用
- redux是一个第三方的库,本身和react没有任何关系,react-redux也是一个第三方库,可以帮助我们在react项目中更好的使用redux
Redux的使用
组成:
- action 操作方法(用户通过界面进行触发方法)
- reducer 方法具体实现(用来改变state数据)
- state 存放数据 (所有数据集中存放的地方)
- view(整个应用组件界面)
简单使用
-
安装命令
npm install redux
-
安装完成后你需要引入redux文件
引入redux
createStore是存放数据的地方
import {createStore} from 'redux'
创建一个仓库
//reducer是下面创建的reducer纯函数
let store=createStore(reducer)
创建一个reducer的纯函数
- 纯函数:给固定的输入,就一定会有固定的输出,并且不会有任何副作用 所以对于异步函数(定时器、ajax数据请求等)、动态时间都不适意在reducer里使用
- reducer的参数1:state的初始值 参数2:action 修改的值
// function reducer(state=默认值(也可以是外面传的值), action){}
function reducer(state={name:'王',age:20},action){
return state
}
打印store
console.log(store)
//会有这4种方法
// dispatch: ƒ dispatch(action) :会发起一次修改(同步,会立即执行)
// getState: ƒ getState():获取状态
// replaceReducer: ƒ replaceReducer(nextReducer)
// subscribe: ƒ subscribe(listener):监听状态的修改
到这里基本的了解就ok了 ,下面我们来看一个案例理解一下
import React from 'react'
import {createStore} from 'redux'
function reducer(state={name:'王',age:20},action){
// 做判断
switch(action.type){
// 如果是name
case 'name':
// 返回你修改后的name值
return{
...state,
name:action.name
}
// 如果是age
case 'age':
// 返回你修改后的age值
return{
...state,
age:action.age
}
}
return state
}
// createStore(reducer)用来创建一个仓库,在仓库中对我们的状态进行管理
let store=createStore(reducer)
console.log(store);
// state改变时触发subscribe
// 它里面的参数是 触发时执行的回调
store.subscribe(()=>{
console.log(store.getState());
})
// 修改name
store.dispatch({
type:'name',
name:'李'
})
// 修改age
store.dispatch({
type:'age',
age:21
})
function App(){
return <div>哈哈</div>
}
export default App;
在这里看到的打印效果是 李 和 21 ,因为的默认值是 王 20 之后你做了修改 就变成了 李 和 21
combineReducers合并
- 随着应用变得复杂,需要对 reducer 函数 进行拆分,拆分后的每一块独立负责管理 state 的一部分
- 最后拆分的部分都要在reducer中使用所以你就得把你拆分的部分个合并到一起
看个案例理解一下
import React from 'react'
import {createStore,combineReducers} from 'redux'
function index(state={
info:'首页'
},action){
switch(action.type){
case 'index_info':
return{
...state,
info:action.info
}
}
return state
}
function list(state={info:'列表'},action){
switch(action.type){
case 'index_list':
return{
...state,
info:action.info
}
case 'index_message':
return{
...state,
message:action.message
}
}
return state
}
// 原生的合并方法
// function reducer(state={},action){
// return {
// index:index(state.index,action),
// list:list(state.list,action),
// }
// }
// 自带的合并方法
// 参数是:传入一个对象,这个对象中的属性,就是要合并的reducer
// 注意:对象中的属性名,要和函数名相同
let reducer=combineReducers({
index,
list
})
let store=createStore(reducer)
store.subscribe(()=>{
console.log(store.getState());
})
store.dispatch({
type:'index_info',
info:'我是index的info'
})
store.dispatch({
type:'index_list',
info:'我是list的info'
})
store.dispatch({
type:'index_message',
message:'我是list的message'
})
function App(){
return <div>哈</div>
}
export default App;
以上是redux的简单使用,我们接下来看下redux跟react的联合使用
redux和react的联合使用
-
因为react-redux也是一个第三方库所以它也需要安转一个插件
react-redux 提供了两个重要的对象, Provider(传值) 和 connect (接收Provider传的值)
安装命令npm install --save react-redux
你可以创建一个store库的文件
index.js
import {createStore,combineReducers} from 'redux'
//引入数据文件
import data from './reducer'
export default createStore(combineReducers({data}))
reducer.js
function data(state={
name:'王',
age:20
},action){
switch(action.type){
case "name":
return{
...state,
name:action.name
}
}
return state
}
export default data
在入口文件中引入react-redux
import ReactDOM from 'react-dom';
import App from './App';
import {Provider} from 'react-redux'
// 引入合并文件
import store from './store/index'
ReactDOM.render(
// 使用store属性接收
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
App.js
import React,{Fragment} from 'react'
import {connect} from 'react-redux'
function Inner(props){
console.log(props);
let {name,age,dispatch}=props
return (
<Fragment>
<p>name:{name}</p>
<p>age:{age}</p>
<button onClick={
()=>{
dispatch({
type:'name',
name:'我被修改了'
})
}
}>修改</button>
</Fragment>
)
}
// 哪个组件中需要使用redux中的数据的话,就在哪个组件中使用
// connect(state的处理函数)(组件)
// state的处理函数:(state,[props])=>{
// return 要传给组件的数据
// }
export default connect((state,props)=>{
console.log(state);
return state.data
})(Inner)
因为以上的方式有可能会绕晕你所有react-redux还出了一下的方式
Hoosk react-redux 7之后才有hooks
- useSelector(回调函数) 通过回调函数的返回值,来获取state
- useStore() 直接获取整个仓库
- useDispatch 获取仓库中的dispatch()方法
使用
跟上面是一样的效果
import React,{Fragment} from 'react'
import {useSelector,useStore,useDispatch} from 'react-redux'
function Inner(props){
console.log(props);
// useSelector个方法里面必须传一个参数 打印出来你会看到数据在data中存着
// console.log(useSelector((state)=>{
// return state
// }));
let {name,age}=useSelector((state)=>{
return state.data
})
// console.log(useDispatch);
let dispatch=useDispatch()
return (
<Fragment>
<p>name:{name}</p>
<p>age:{age}</p>
<button onClick={
()=>{
dispatch({
type:'name',
name:'我被修改了'
})
}
}>修改</button>
</Fragment>
)
}
export default Inner
总结:
- redux的使用就是简单的几部
- 引入
import {createStore} from 'redux'
- 创建store
let store=createStore(reducer)
- 声明reducer函数
function reducer(state=默认值(也可以是外面传的值), action){}
就ok了