声明式路由与编程式路由

  • 在原生的js中,声明式函数式是有着一条界限的,我们可以认为声明式就是html标签实现一个功能,而编程式就是使用原生js实现一个功能,就拿页面跳转为例,声明式就是使用a标签来实现页面跳转的,而编程式则是使用location.herf来实现页面跳转的, 代码如下所示:
<!DOCTYPE html>
<html lang="en">
<body>
<!-- 声明式: 使用a标签实现页面的跳转 -->
<a style="color: red;" href="http://localhost:3000/#/home/info">
(声明式)点击跳转
</a><hr>

<!-- 编程式: 使用点击函数调用实现页面跳转 -->
<button id="go">(编程式)点击跳转</button>

<script>
// 获取dom节点
var a = document.querySelector('#go')

// 注册点击事件
a.onclick = ()=>{
// 使用window内置api实现页面跳转
window.location.href = 'http://localhost:3000/#/home/info'
}
</script>
</body>
</html>

结果展示:

image

由上述的案例我们可以得出,无论是声明式还是编程式,我们使用原生的js同样可以实现路由的跳转(只不过路由url上需要拼接#/),但是我们一般在项目当中使用路由的跳转都需要知道当前路径更改时的状态,也就是我们能够监听到路径的修改从而做出对应的动作,如当点击对应的按钮跳转页面时,对应的按钮有高亮样式等…当然我们也可以使用原生的js来实现(使用window.onhashchange监听url上的hash变化,随后使用location.hash来获取对应的url路径 , 这其实就是React路由的实现原理), 但其实React已经为我们造好轮子了!!

案例展示:

  • 使用声明式编程式路由实现页面的跳转,声明式路由负责一级路由的跳转(home页面和about页面), 编程式路由则负责二级路由的跳转,两种路由的跳转均携带参数
  • 官方文当

  • NavLink 导航组件,它编译生成后的html标签只能是 a,但是它有激活样式,也就是activeClassName(地址栏中的地址和to属性匹配,就有内置样式名)

  • 切记: NavLink只能用在Router的包裹中

父组件(声明式)

// 声明式路由和编程式路由(一级路由[声明式])
// 引入路由相关的组件
import{HashRouter as Router,Route,NavLink,Redirect,Switch} from 'react-router-dom'
import React from 'react'

// 引入对应的组件
import About from './testPages/about.js'//关于页面
import Home from './testPages/home.js'//首页
import NotFound from './testPages/404.js'//404页面

export default function App(props) {

return (
<div>
<Router>
{/* 声明式路由跳转(可以携带参数!) */}
<NavLink to={`/user/about?params=${'携带过去的参数'}`}>点击跳转到关于页面(声明式)</NavLink>

{/* 使用Switch来解决每次页面刷新,重定向功能执行的bug(模糊匹配) */}
<Switch>
{/* 多级路由 */}
<Route path="/user/about" component={About}/>
{/* 一级路由 */}
<Route path="/home" component={Home}/>

{/* 路由的重定向(使用exact实现精确匹配) */}
<Redirect from="/" to="/home" exact/>
{/* 匹配不到的页面则展示404 */}
<Route component={NotFound}/>
</Switch>
</Router>
</div>
)
}

props.history(编程式路由)

  • 官方文档
  • 编程式路由跳转一般用于嵌套路由,一级路由(父组件)通过props将路由跳转的方法(history)传给二级路由(子组件),随后在二级路由中调用对应的方法来进行页面的跳转

子组件(编程式)

// 路由的嵌套(二级路由 [编程式])
import React from 'react'
// 引入路由相关的组件
import{HashRouter as Router,Route,Redirect,Switch,useHistory} from 'react-router-dom'
// 引入对应的组件
import About from './about.js'//关于页面
import Info from './info.js'//个人信息页面
import NotFound from './404.js'//404页面

export default function Home(props) {//形参props接收一级路由(父组件)传过来的路由方法

// React-Router中为hooks提供的一个方法让hooks也讷讷感实现路由跳转
const history = useHistory()

// 设置一个路由跳转的方法(接收跳转路径)
const goRoute = (route)=>{
console.log('父组件提供过来的router方法,用于路由跳转',props);

// 1. 类组件写法(当前是函数式组件)
// this.props.history.push(route)// 跳转到info组件

// 2. 函数式组件写法(当前是函数式组件)
// props.history.push(route)// 跳转到info组件

// 3. hooks写法(当前是函数式组件)
history.push(route)// 跳转到info组件
}
return (
<div>
首页
{/* 编程式路由跳转 */}
<ul>
<li>
<button onClick={()=>goRoute(`/home/info?params=${'携带过去的参数'}`)}>info(编程式)</button>
</li>
<li>
<button onClick={()=>goRoute(`/home/about?params=${'携带过去的参数'}`)}>about(编程式)</button>
</li>
</ul>

{/* 在子组件下设置二级路由调用 */}
<div style={{width:'200px',height:'600px',backgroundColor:'skyblue'}}>
<Router>
{/* 使用Switch来解决每次页面刷新,重定向功能执行的bug(模糊匹配) */}
<Switch>
{/* 多级路由 */}
<Route path="/home/about" component={About}/>
{/* 一级路由 */}
<Route path="/home/info" component={Info}/>

{/* 路由的重定向(使用exact实现精确匹配),一进入home页面默认跳转到info页面 */}
<Redirect from="/home/" to="/home/info" exact/>
{/* 匹配不到的页面则展示404 */}
<Route component={NotFound}/>
</Switch>
</Router>
</div>
</div>
)
}

结果展示:

image

动态路由(路由传参)

  • 由上面的案例中我们可以看到,路由的跳转的确可以通过url路径来传递参数,但是这样传递参数的话,我们是很难获取到的,需通过字符串截取的方式来获取参数,非常的麻烦,由此便衍生出动态路由这一说法!

设置方法:

  1. Route 组件的“path”属性中,我们使用了“users/:params”,“:”冒号用于使路由动态化,并且可以使用冒号后的路由路径中提供的名称“params”访问参数.
{/* 多级路由 设置动态路由(携带参数) */}
<Route path="/user/about/:params" component={About}/>
  1. 在对应的二级路由(子组件)中使用props接收并读取
// 类组件使用this.props , 函数组件则使用形参接收props
console.log('一级路由(父组件)传过来的数据',this.props.match.params);

// 函数组件则使用形参接收父组件传过来的props
console.log('上级路由传过来的数据',props);

案例展示:

  • 复刻上面的案例,在功能基本一致的情况下,子组件(下级路由)接收并展示父组件(上级路由)传过来的数据并展示出来
  1. 一级路由(爷爷级)
// 引入路由相关的组件
import{HashRouter as Router,Route,NavLink,Redirect,Switch} from 'react-router-dom'
import React from 'react'

// 引入对应的组件
import About from './testPages/about.js'//关于页面
import Home from './testPages/home.js'//首页
import NotFound from './testPages/404.js'//404页面

export default function App(props) {

return (
<div>
<Router>
{/* 声明式路由跳转(可以携带参数!) */}
<NavLink to={`/user/about/${'关于数据(声明式)'}`}>点击跳转到关于页面(声明式)</NavLink>

{/* 使用Switch来解决每次页面刷新,重定向功能执行的bug(模糊匹配) */}
<Switch>
{/* 多级路由 设置动态路由(携带参数) */}
<Route path="/user/about/:params" component={About}/>
{/* 一级路由 */}
<Route path="/home" component={Home}/>

{/* 路由的重定向(使用exact实现精确匹配) */}
<Redirect from="/" to="/home" exact/>
{/* 匹配不到的页面则展示404 */}
<Route component={NotFound}/>
</Switch>
</Router>
</div>
)
}
  1. 二级路由(爸爸级)
// 路由的嵌套(二级路由) - home组件
import React from 'react'
// 引入路由相关的组件
import{HashRouter as Router,Route,Redirect,Switch,useHistory} from 'react-router-dom'
// 引入对应的组件
import About from './about.js'
import Info from './info.js'
import NotFound from './404.js'

export default function Home(props) {
// React-Router中为hooks提供的一个方法让hooks也讷讷感实现路由跳转
const history = useHistory()

// 路由跳转的方法(接收跳转路径)
const goRoute = (route)=>{
console.log('父组件提供过来的router方法,用于路由跳转',props);

// 1. 类组件写法(当前是函数式组件)
// this.props.history.push(route)// 跳转到info组件

// 2. 函数式组件写法(当前是函数式组件)
// props.history.push(route)// 跳转到info组件

// 3. hooks写法(当前是函数式组件)
history.push(route)// 跳转到info组件
}
return (
<div>
首页
{/* 编程式路由跳转 */}
<ul>
<li>
<button onClick={()=>goRoute(`/home/info/${'个人信息数据(编程式)'}`)}>info(编程式)</button>
</li>
<li>
<button onClick={()=>goRoute(`/home/about/${'关于数据(编程式)'}`)}>about(编程式)</button>
</li>
</ul>

{/* 在子组件下设置二级路由调用 */}
<div style={{width:'200px',height:'600px',backgroundColor:'skyblue'}}>
<Router>
{/* 使用Switch来解决每次页面刷新,重定向功能执行的bug(模糊匹配) */}
<Switch>
{/* 多级路由 设置动态路由(携带参数) */}
<Route path="/home/about/:params" component={About}/>
{/* 多级路由 设置动态路由(携带参数) */}
<Route path="/home/info/:params" component={Info}/>

{/* 路由的重定向(使用exact实现精确匹配),一进入home页面默认跳转到info页面 */}
<Redirect from="/home/" to="/home/info/:params" exact/>
{/* 匹配不到的页面则展示404 */}
<Route component={NotFound}/>
</Switch>
</Router>
</div>
</div>
)
}
  1. 三级路由(儿子级)
// info组件(类组件)
import React, { Component } from 'react'

// 类组件
export default class App extends Component {
render() {
// 类组件使用this.props , 函数组件则使用形参接收props
console.log('一级路由(父组件)传过来的数据',this.props.match.params);
return (
<div>个人信息页-{this.props.match.params.params}</div>
)
}
}

// about(函数组件)
import React from 'react'

export default function App(props) {
console.log('上级路由传过来的数据',props);
return (
<div>关于页面-{props.match.params.params}</div>
)
}

结果展示:

image

剩余两种路由传参的方法:(不推荐使用)

(1) 
this.props.history.push({ pathname : '/user' ,query : { day: 'Friday'} }) // 传递
this.props.location.query.day // 调用
(2)
this.props.history.push({ pathname:'/user',state:{day : 'Friday' } })
this.props.location.state.day

以上两种方法是不推荐使用的,因为他们都存在问题,那就是他们是通过在浏览器混村中创建一个新的变量来保存你想要在路由url中传递的参数的,也就是说不同的浏览器之间,不同的硬件设备之间,是无法实现数据共享的,也就是说,如果使用以上两种方法来写项目,那么项目完成之后,你通过分享网址的方法将携带参数的网址分享给他人, 他人是无法获取的(进入你分享的网站中!)