Vue路由
注意:随着版本更新,vue版本需要对应合适的vue-router版本才能正常工作,比如这里当我安装vue-router时,
npm install vue-router
它默认安装的版本是4.0以上的,在我这个vue2.0版本会各种报错,跟vuex也要对应合适的版本一样,否则会报错,所以安装的时候需要指定版本
npm install vue-router@3.4.9
同vuex一样,在安装好后需要一个router文件夹管理路由,里面的index.js文件由于创建路由,通过VueRouter创建一个路由器。routes是定义的路由匹配规则,需要交给router管理。
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
export default new VueRouter({routes : [{path:"/about",component:AboutCom},{path:"/home",component:HomeCom,children:[{// path:'/news',这里的/一定要删除是个坑,不然子路由组件渲染不出来path:'news',component:NewCom},{// path:'/message',这里的/一定要删除是个坑,不然子路由组件渲染不出来path:'message',component:MessageCom,children:[{//传递params参数 需要路由定义时声明// path:"detail/:id/:title/:content",//传递query参数不需要声明path:"detail",component:DetailCom,name:'xiangqing' , props(route){console.log(route)const {id,title,content} = route.queryreturn {id,title,content}} }]},]},]
})
main.js里需要引入这个路由器并挂载到vue实例vc
import router from './router'new Vue({el:"#app",router,render: h => h(App),
})
在APP组件里 有两个链接,about和home,这解析出来其实就是a标签 About组件和Home组件由路由控制显示,所以这两个组件是路由组件,就需要在router里定义这两个组件,现在pages文件夹里定义好这两个组件 pages/Home和pages/About,然后在路由器里引入组件开始定义匹配规则
import AboutCom from '../pages/AboutCom'
import HomeCom from '../pages/HomeCom'routes : [{path:"/about",component:AboutCom},{path:"/home",component:HomeCom,
这样在页面里,通过router-link作为a标签,同时router-view作为占位,到时候路由匹配到哪个组件就展示哪个组件
<router-link to="/about">about</router-link> <!-- <a href="">home</a> --><router-link to="/home">home</router-link> <router-view></router-view>
这样一级路由就完成了。
接下来是二级路由Home组件里 ,又有导航区 和展示区
在home的路由里通过children定义子路由
{path:"/home",component:HomeCom,children:[{// path:'/news',这里的/一定要删除是个坑,不然子路由组件渲染不出来path:'news',component:NewCom},{// path:'/message',这里的/一定要删除是个坑,不然子路由组件渲染不出来path:'message',component:MessageCom,
注意
path:'news',
没有/,这是坑,加了的话组件等会儿就展示不出来,其余跟一级组件同理,接着是三级路由,
接着在message里通过children定义子路由
path:'message',component:MessageCom,children:[{//传递params参数 需要路由定义时声明// path:"detail/:id/:title/:content",//传递query参数不需要声明path:"detail",component:DetailCom,
其余地方跟一级路由同理,这样一个嵌套路由就完成了。
vue路由传参
1.传递params参数
params参数在地址栏表现为
传递params参数需要在路由定义时声明接收params参数
//传递params参数 需要路由定义时声明path:"detail/:id/:title/:content",
在页面的router-link里是使用模板字符串传递的
<router-link :to="`/home/message/detail/${msg.id}/${msg.title}/${msg.content}`">{{msg.title}}</router-link>
params参数藏在$sroute.params里,所以页面要获取这些params可以
<li>id:{{$route.params.id}}</li>
2.传递query参数
传递query参数在路由定义时不用声明,在浏览器地址栏表现为
在路由定义时
//传递query参数不需要声明path:"detail",
在页面的router-link也是模板字符串
<router-link :to="`/home/message/detail/?id={msg.id}&title=${msg.title}&content={msg.content}`">{{msg.title}}</router-link>
query参数藏在$sroute.query里,所以页面要获取这些params可以
<li>id:{{$route.query.id}}</li>
命名路由简化跳转方式
可以在路由定义时给路由命名,方式时name:"名字“
path:"detail",component:DetailCom,name:'xiangqing' ,
在页面的router-link进行跳转时就可以这样写
<!-- 通过命名路由的方式传参 --><router-link :to="{name:'xiangqing',query:{id:msg.id,title:msg.title,content:msg.content}}">{{msg.title}}</router-link>
当然这里也可以由params加入,具体是看路由定义时规定的时哪些值由params传递,哪些由query传递,这样就省略了path一长串。
路由的props配置
想象这样一个场景,点击一条消息进入消息的详情页,需要展示消息的id,title,content等,由于这些信息全保存在$route里所以可以在页面通过params或者query取出
params:
<li>id:{{$route.params.id}}</li><li>消息:{{$route.params.title}}</li><li>内容:{{$route.params.content}}</li>
query:
<li>id:{{$route.query.id}}</li><li>消息:{{$route.query.title}}</li><li>内容:{{$route.query.content}}</li>
这样代码过于冗余,可以在computed里进行处理再显示,不再赘述,另一种方法是在路由定义时通过props的方式传递,同时在组件里通过props[‘id’,‘title’,‘content’]接收,一般父组件给子组件传值就是这种方式,具体做法是在路由定义时
props:{carName:"奔驰"}这是通过props映射自定义的静态数据
props:true表示映射params参数为props传给路由组件
如果还想映射query参数为props传给路由组件,则必须把props写成props(route){const {id,title,content} = route.queryreturn {id,title,content}}
这里的route是直接放在router管理的,才能取到这个route,放在外面定义好交给router管理的取不到。在页面组件需要定义props接收参数
<script>export default {props:['id','title','content']}
</script>
在页面就可以直接用数据了
<li>id:{{id}}</li><li>消息:{{title}}</li><li>内容:{{content}}</li>
编程式路由导航
replace – 替换当前记录 不能返回 通过点击事件跳转
this.$router.push(path)、
this.$router.replace(path)、
this.$router.back()、
this.$router.go(-1)、
this.$router.go(1)
pushShow(msg){this.$router.push({ name:'xiangqing',query:{id:msg.id,title:msg.title,content:msg.content}})}
缓存路由组件
有这样一个需求,点击News 显示NewCom组件,点击Message显示MessageCom组件,在NewCom组件里输入东西后,点击Message后,再点击News会导致NewCom组件输入内容清空,这是因为在路由的切换过程中,伴随着组件的挂载和卸载,为了使NewCom组件输入框里的内容保持不变,就许哟啊一个keep-alive包裹这个组件的router-view,这样就行了
<keep-alive><router-view></router-view></keep-alive>
但是,这样做会使得MessageCom的组件也在切换过程中不销毁,为了解决这个问题 只保证NewComers组件不销毁,可以这样添加一个include
<keep-alive include="NewCom"><router-view></router-view></keep-alive>
这个NewCom对应的是这个组件的名字NewCom,NewCom.vue
export default({name:"NewCom",
})
React路由
说明:react的路由是安装react-router-dom包,现在已经更新到react-router-dom 6版本 因为本例中说明的是5版本的react-router-dom,所以必须安装对应的版本才能正确运行。
步骤:
第一步就是安装路由
npm install react-router-dom@5
拿到静态页面,观察到哪些是一般组件 ,哪些是路由组件,哪些是导航区,哪些是路由展示区
在App页引入页面,分离出导航区和组件展示区。原来的a标签在react里是LInk标签所以需要
import {Link} from 'react-router-dom'
然后把a标签改写成Link
<a className="list-group-item" href="./about.html">About</a>改写成
<Link className="list-group-item" to="/about">About</Link>
由to定义路由的路径,接着在路由展示区的地方注册路由,需要先引入Route
import {Link,Route} from 'react-router-dom'
<Route path='/about' component={About}></Route>
备注:需要把整个App组件包裹在BrowserRouter组件里,这样才不会错
import {BrowserRouter} from 'react-router-dom'ReactDOM.render( <BrowserRouter><App /></BrowserRouter>,document.getElementById('root'))
本例中使用到bootstrap 需要在index.html里引入
<link rel="stylesheet" href="%PUBLIC_URL%/css/bootstrap.css">
表示从本地路径下的css找到bootstrap.css
效果图:
NavLink的使用
一般导航区的导航点击了都会高亮显示,如果有这样的需求,那就需要把Link换为NavLink。它的原理是谁被点击了 就给当前这个导航链接加上active这个class
<NavLink className="list-group-item" to="/about">About</NavLink>
修改样式:如果需要自己定义active的样式。NavLink接受一个activeClassName=“myActive”,myActive是我自己定义的名字,随意。然后在index.html里定义自己的myActive
<style>.myActive{background-color: red !important;color: white !important;}</style>
<NavLink activeClassName="myActive" className="list-group-item" to="/about">About</NavLink><NavLink activeClassName="myActive" className="list-group-item" to="/home">Home</NavLink>
封装NavLink组件
应用场景:如果上面的NavLink有很多很多个
<NavLink className="list-group-item" to="/about">About</NavLink>
<NavLink className="list-group-item" to="/abou">abou</NavLink>
<NavLink className="list-group-item" to="/abo">abo</NavLink>
<NavLink className="list-group-item" to="/ab">ab</NavLink>
<NavLink className="list-group-item" to="/a">a</NavLink>
<NavLink className="list-group-item" to="/abouty">abouty</NavLink>
<NavLink className="list-group-item" to="/aboueet">aboueet</NavLink>
上面的NavLink除了to的值不一样其他都一样,这样写显得过于冗余,所以对NavLink进行二次封装
创建MyNavLink组件
import {NavLink} from 'react-router-dom'//使用原始的NavLink做改装
import React, { Component } from 'react'export default class MyNavLink extends Component {render() {console.log(this.props)return (<NavLink activeClassName="myActive" className="list-group-item" {...this.props}>{this.props.children}</NavLink>)}
}
<MyNavLink activeClassName="myActive" className="list-group-item" to="/about">About</MyNavLink>
中的activeClassName=“myActive” className=“list-group-item” to="/about"这些都是作为props传递给了NavLink,由于只有to的值不一样,所以在新的MyNavLink里只需要手动传入to的值。值得一提的是
<MyNavLink to="/about">about</MyNavLink>
中的about称为元素内容,它也是保存在props里的,为children属性,所以
<NavLink activeClassName="myActive" className="list-group-item" {...this.props}>{this.props.children}</NavLink>
可以写为自闭合
<NavLink activeClassName="myActive" className="list-group-item" {...this.props}/>
Switch的使用
Switch用于匹配路由,如果一个路径匹配到两个路由,那么会把两个组件都显示出来,如果这样
<MyNavLink to="/about">about</MyNavLink>注册路由<Route path='/about' component={About}></Route><Route path='/about' component={Test}></Route>
一个about匹配到两个路由会显示两个组件
这明显是不对的,这说明路由在匹配成功后还会向后继续匹配,这是损耗性能的,解决办法就是Switch
<Switch><Route path='/about' component={About}></Route><Route path='/about' component={Test}></Route>//这条不再匹配<Route path='/home' component={Home}></Route></Switch>
解决样式丢失问题
在路由里随便增加路径会导致bootstrap样式丢失,解决办法是在引入bootstrap的时候
<link rel="stylesheet" href="%PUBLIC_URL%/css/bootstrap.css">
或者
<link rel="stylesheet" href="/css/bootstrap.css">
或者使用HashRouter。第一种常用
<link rel="stylesheet" href="%PUBLIC_URL%/css/bootstrap.css">
路由的模糊匹配和严格匹配
假设有这样一个链接
<MyNavLink to="/about/a/b">about</MyNavLink>
然后有一些注册的路由
<Route path='/about' component={About}></Route>
这样也能匹配成功,成功显示About组件,这就是模糊匹配。为了解决这个问题就出现了严格匹配exact
<MyNavLink to="/about/a/b">about</MyNavLink><Route exact={true} path='/about' component={About}></Route>
这样就开启了严格匹配,但是这个exact对子路由的匹配有影响,导致无法继续匹配二级路由。需要谨慎使用。
Redirect的使用
Redirect是重定向,当前路由没有路由组件与其匹配时,去Redirect重定向的路由,一般写在路由注册的最后面
没有匹配的 则不显示组件,当Redirect重定向后
import {Route,Switch,Redirect} from 'react-router-dom'<Switch><Route path='/about' component={About}></Route><Route path='/home' component={Home}></Route><Redirect to="/about"></Redirect>
</Switch>
嵌套路由
嵌套路由的关键是分清哪个是导航区,哪个是路由组件展示区,导航区自然把a写成Link、NavLink等,那么自然需要从react-router-dom引入这些东西,路由组件展示区就是注册路由的地方,匹配到路由就展示相应的组件
<NavLink className="list-group-item " to="/home/news">News</NavLink><NavLink className="list-group-item " to="/home/message">Message</NavLink>
主要是to的嵌套,注册路由时也得对应
<Switch><Route path="/home/news" component={News}></Route><Route path="/home/message" component={Messages}></Route></Switch>
<Switch><Route path="/home/news" component={News}></Route><Route path="/home/message" component={Messages}></Route><Redirect to="/home/news"></Redirect></Switch>
Redirect是 to
react路由传参
react路由有三种传参方式,params、search、state,三种参数都保存在props属性里。
1.params参数
params参数在路由链接的形式是,vue一样
<li key={msgObj.id}><Link to={`/home/message/detail/${msgObj.id}/${msgObj.title}`}>{msgObj.title}</Link></li>
params参数需要在注册路由时声明接收params参数,跟vue一样
<Route path="/home/message/detail/:id/:title" component={Detail}></Route>
params保存在props.match.params里
2.search参数
search参数可以对标vue里的query参数,跟vue规则一样
<li key={msgObj.id}><Link to={`/home/message/detail/?id=${msgObj.id}&title=${msgObj.title}`}>{msgObj.title}</Link></li>
search参数不需要声明接收,跟vue一样。
<Route path="/home/message/detail" component={Detail}></Route>
search参数保存在props.location.search中。
?id=1&title="消息1"
但是这个结果是这种形式,用的时候需要自己使用方法切割,queryString是不错的选择
import qs from 'querystring'
const result = qs.parse(this.props.location.search)
3.state参数
state参数不会在浏览器地址栏显示参数信息,,传递的时候以对象形式。
<li key={msgObj.id}><Link to={{pathname:'/home/message/detail',state:{id:msgObj.id,title:msgObj.title}}}>{msgObj.title}</Link></li>
state参数不用声明接收
<Route path="/home/message/detail" component={Detail}></Route>
state保存在props.location.state里。
总结
向路由组件传递参数
1.params参数路由链接(携带参数):<Link to='/demo/test/tom/18'}>详情</Link>注册路由(声明接收):<Route path="/demo/test/:name/:age" component={Test}/>接收参数:this.props.match.params2.search参数路由链接(携带参数):<Link to='/demo/test?name=tom&age=18'}>详情</Link>注册路由(无需声明,正常注册即可):<Route path="/demo/test" component={Test}/>接收参数:this.props.location.search备注:获取到的search是urlencoded编码字符串,需要借助querystring解析3.state参数路由链接(携带参数):<Link to={{pathname:'/demo/test',state:{name:'tom',age:18}}}>详情</Link>注册路由(无需声明,正常注册即可):<Route path="/demo/test" component={Test}/>接收参数:this.props.location.state备注:刷新也可以保留住参数
编程式路由导航
主要是利用history的API,有以下几个方法
-this.prosp.history.push()
-this.prosp.history.replace()
-this.prosp.history.goBack()
-this.prosp.history.goForward()
-this.prosp.history.go()
这些历史记录以栈的形式保存,push可以前进后退,replace则不能。
this.props.history.push是默认的跳转方式
1.传递params参数:
<button onClick={()=>{this.pushShow(msgObj.id,msgObj.title)}}>push查看</button>pushShow=(id,title)=>{console.log(this.props)this.props.history.push(`/home/message/detail/${id}/${title}`)}params参数需要声明接收<Route path="/home/message/detail/:id/:title" component={Detail}></Route>
注意:这里pushShow要传参,所以写成一个函数的形式,onClick后面需要的是一个函数而不是一个函数值,所以用了()=>{}包裹。如果不这样this.pushShow()会直接调用(没有点击事件之前就调用)
this.props.history.repplace使用方法一样。
2.传递search参数
<button onClick={()=>{this.pushShow(msgObj.id,msgObj.title)}}>push查看</button>pushShow=(id,title)=>{this.props.history.push(`/home/message/detail/?id=${id}&title=${title}`){/* 接收search参数 */}}<Route path="/home/message/detail" component={Detail}></Route>
3.传递state参数
<button onClick={()=>{this.pushShow(msgObj.id,msgObj.title)}}>push查看</button>pushShow=(id,title)=>{对象形式传递statethis.props.history.push('/home/message/detail',{id,title})}{/* 接收state参数也不用声明 */}<Route path="/home/message/detail" component={Detail}></Route>
-this.prosp.history.goBack()—回退一步
-this.prosp.history.goForward()前进一步
-this.prosp.history.go() 传入数字 指定前进或后退步数
withRouter的使用
路由能够跳转全靠路由组件的history实现,那么一般组件里想要实现路由跳转怎么办呢?可以通过withRouter把这个一般组件转为一般组件,那么它就能使用history的API跳转链接了
在一般组件里
import { withRouter } from 'react-router-dom'
class Header extends Component {render() {return (<div><button onClick={this.back}>后退</button><div className="page-header"><h2>React Router Demo</h2></div></div>)}back=()=>{// console.log(99)this.props.history.goBack()}
}
export default withRouter(Header)
暴露这样一个新的组件就可以了
export default withRouter(Header)
BrowserRouter与HashRouter的区别
1.底层原理不一样:BrowserRouter使用的是H5的history API,不兼容IE9及以下版本。HashRouter使用的是URL的哈希值。2.path表现形式不一样BrowserRouter的路径中没有#,例如:localhost:3000/demo/testHashRouter的路径包含#,例如:localhost:3000/#/demo/test3.刷新后对路由state参数的影响(1).BrowserRouter没有任何影响,因为state保存在history对象中。(2).HashRouter刷新后会导致路由state参数的丢失!!!4.备注:HashRouter可以用于解决一些路径错误相关的问题。