在单页应用中常常要用到路由。
传统的页面跳转是浏览器请求新的页面,渲染整个新的页面。
单页应用是把要跳转的页面的以组件的形式集成在当前页面中,跳转时浏览器不用发起新请求,因为目标页面是当前页面的一部分,直接显示目标页面那一部分即可。
demo 在单页应用中使用路由
1、下载路由插件
npm install vue-router -S
install可以简写为i
我们要使用的是里面的vue-router.js文件
2、写一个test.html
<!DOCTYPE html><html> <head> <meta charset="utf-8" /> <title>title> 引入vue.js --> <script src="js/vue.js">script> 引入路由插件。上线时均要换为xxx.min.js --> <script src="js/vue-router.js">script> head> <body> <div id="app">div> <script> // 首页组件 var Index={ template:` <div> <p>this is the index page</p> <p><a href="#/login">login</a></p> 注意url写法,#/开头 --> <p><a href="#/register">register</a></p> </div> ` } // 登录组件 var Login={ template:` <div> <p>this is the login page</p> <p><a href="#/index">index</a></p> <p><a href="#/register">register</a></p> </div> ` } // 注册组件 var Register={ template:` <div> <p>this is the register page</p> <p><a href="#/index">index</a></p> <p><a href="#/register">register</a></p> </div> ` } // 安装路由插件 Vue.use(VueRouter); // 创建路由对象 var router=new VueRouter({ // 配置路由规则 routes:[ //对象数组 {path:'/index',name:'index',component:Index}, //path指定映射地址,注意没有#,component指定对应的组件 {path:'/login',name:'login',component:Login}, {path:'/register',name:'register',component:Register}, ] }); new Vue({ el:'#app', router, //启用路由。原本是router:router,可以简写 template:` <div> 路由页面只是当前页面的一部分,当前页面可以写一些其他内容,写的内容是所有路由页面--> <p>this is common area</p> <router-view></router-view> </div> ` }) script> body>html>
3、运行
假设test.html的地址是:http://127.0.0.1:8848/vue/test.html#/ 注意后面有个#
则index的地址是:http://127.0.0.1:8848/vue/test.html#/index
login的地址是:http://127.0.0.1:8848/vue/test.html#/login
register的地址是:http://127.0.0.1:8848/vue/test.html#/register
3个页面,但实际上路由的3个页面都在test.html中。
是一个路由容器,用来容纳路由页面。
单页应用的两种路由模式
- 哈希模式(利用hashchange 事件监听 url的hash 的改变)
- history模式(需要后台配合把接口都打到我们打包后的.html文件上,比如上用test.html打包路由页面,test.html相当于一个容器)
demo中使用的是哈希模式
哈希模式实现路由跳转的原理
// 监听地址栏url的改变,haschange是预定义事件 window.addEventListener("hashchange",function(e){ //haschange事件发生时,会封装事件以参数的形式传给处理函数 console.log(e); //这个对象的部分信息: {..., oldURL: "http://127.0.0.1:8848/vue/test.html#/index", newURL: "http://127.0.0.1:8848/vue/test.html#/register", type: "hashchange", …} console.log(location.hash); //地址栏的url已改变,获取新的url的hash。带有#/,比如#/index,#/login // console.log(location); //location是不带#/的,比如index、login switch(location.hash){ //根据路由配置决定和哪些常量比较。这些常量就是路由配置中的path case '#/index': //... //如果匹配就用对应的组件替换部分 break; case '#/login': break; case '#/register': break; } })
路由跳转的3种方式
- 链接
- $router对象
new Vue({ el:'#app', router, template:` index login <button @click="goRegister">register `, methods:{ goRegister(){ this.$router.push({path:'/register'}); //不带# } } })
是html的标签,不知道url中有没有#号,所以需要我们自己加上。
$router的常用方法
- push() 跳转到指定的页面,会往history中插入一条新纪录
- replace() 和push()的用法、作用相同,只是replace()不会往history中插入一条新纪录
- go(-1) 跳转到history中的上一条记录,相当于点击浏览器的后退箭头。
- forward(1) 跳转到history中的下一条记录,相当于点击浏览器的前进箭头。
$router还有个兄弟$route,和$router不同,$route封装了路由信息,只有属性(可以理解为是只读的),常用的属性比如hash、path、query、params。
路由的传参和取参
1、链接方式
// 首页组件 var Index={ template:` this is the index page{{this.$route.query.username}} {{this.$route.query.username}}{{username}} {{password}} `, data(){ return{ username:'', password:'' } }, created(){ //路由到此组件|页面时,会新建此组件的实例,在created()中获取传来的数据 this.username=this.$route.query.username; //$route,没有r,a链接只能用query来取 this.password=this.$route.query.password; } } // 安装路由插件 Vue.use(VueRouter); // 创建路由对象 var router=new VueRouter({ // 配置路由规则 routes:[ {path:'/index',name:'index',component:Index}, ] }); new Vue({ el:'#app', router, template:` index ` })
参数以查询字符串的形式拼接在url中:http://127.0.0.1:8848/vue/test.html#/index?username=chy&password=abcd
2、
(1)query
to前面有冒号,我这里使用的是路由配置里的name。query --> <router-link :to="{name:'index',query:{username:'chy',password:'abcd'}}">indexrouter-link>
查询字符串的形式拼接参数,获取时也是$route.query的方式,url中会带有参数:http://127.0.0.1:8848/vue/test.html#/index?username=chy&password=abcd
(2)params
params,post方式--><router-link :to="{name:'index',params:{username:'chy',password:'abcd'}}">indexrouter-link>
要用$route.params来接收,用什么传递就用什么接收。
url中不显示参数,更安全:http://127.0.0.1:8848/vue/test.html#/index
params方式的路由配置还可以这样写:
// 创建路由对象 var router=new VueRouter({ // 配置路由规则 routes:[ {path:'/index/:username',name:'index',component:Index}, ] });
:参数名 可以获取对应的参数值,http://127.0.0.1:8848/vue/test.html#/index/chy,url是RESTful风格
3、$router对象方式
new Vue({ el:'#app', router, template:` index `, methods:{ goIndex(){ this.$router.push({name:'index',query:{username:'chy',password:'abcd'}}); } } })
说明
如果路由配置的path是: {path:'/index/:username',name:'index',component:Index} 这种随参数的变化而变化的,那就使用name。
路由传参参数不刷新的问题
现象
<router-link :to="{name:'index',params:{username:'chy1',password:'abcd1'}}">indexrouter-link> <router-link :to="{name:'index',params:{username:'chy2',password:'abcd2'}}">indexrouter-link>
比如第一次路由到index页面,携带的参数是{username:'chy1',password:'abcd1'},
后续再路由到此页面时,比如要携带的参数是{username:'chy2',password:'abcd2'},新的参数传过去了,但使用的参数是第一次路由到此页面时携带的参数。
即传到同一路由页面的参数不会刷新,3种方式都存在这个问题。
原因
Vue路由会复用组件,我把变量赋值写在created()中,这个钩子函数只在组件创建时执行,就是说变量赋值只执行1次(第一次路由到此页面时)。
后续再次路由到此页面时,新的参数是传过去了,但变量赋值不会再执行,新的参数也就没有赋给变量。
如果只在传过去的时候使用参数,后面不再使用参数,可以 {{this.$route.query|params.参数名}} 直接取。
如果要把参数赋给变量,方便后续使用,该怎么做?
2种解决方案
1、使用 :key 唯一标识一次路由
// 创建路由对象 var router=new VueRouter({ // 配置路由规则 routes:[ {path:'/index',name:'index',component:Index}, ] }); new Vue({ el:'#app', router, template:` indexindex<router-view :key="$route.fullPath"> `, })
query方式(链接也是query方式),路由配置的写为/index的形式
因为根据url的hash来判断是否是同一个路由页面,默认的:key是/index这种形式,会认为是同一个路由页面;
现在设置:key="$route.fullPath"(注意P是大写),以完整的url来判断,query方式会在url中拼接参数,能区分不同的参数传递。
// 创建路由对象 var router=new VueRouter({ // 配置路由规则 routes:[ {path:'/index/:username',name:'index',component:Index}, ] }); new Vue({ el:'#app', router, template:` indexindex<router-view :key="$route.fullPath"> `, })
params方式,因为使用post传递参数,url中不带参数,url(的hash)都是一样的,怎么区别?
路由配置的 path:'/index/:username' 中带上唯一的参数标识,比如username、uid等,这样url就不同了
说明
(1)此种方式,:key能唯一标识一次参数传递即可,不一定要是$route.fullPath,比如可以是:
:key="$route.fullPath" //完整的url :key="$route.query|params.username|uid" //能唯一标识一次参数传递的参数 :key="new Date().getTime()" //时间戳(这个是毫秒级的)。使用时间戳时,就算2次路由的参数完全相同,也会重新创建路由页面的组件
(2)此种方式,只要:key的值不同,就会重新创建路由页面的组件,性能影响大,不太推荐。
2、数据监听
// 首页组件 var Index={ template:` this is the index page{{username}} {{password}} `, data(){ return{ username:'', password:'', } }, // created(){ //可以不要created() // this.username=this.$route.params.username; // this.password=this.$route.params.password; // }, watch:{ //监听$route.query|params,当然直接监听$route也行 '$route.params'(){ //因为有.号,所以要要引起来,不然识别不了 this.username=this.$route.params.username; //参数变化时就重新获取参数 this.password=this.$route.params.password; } }, } // 安装路由插件 Vue.use(VueRouter); // 创建路由对象 var router=new VueRouter({ // 配置路由规则 routes:[ {path:'/index/:username',name:'index',component:Index}, //query方式写成/inedx,params方式写成/index/:username ] }); new Vue({ el:'#app', router, template:` indexindex `, })
这种方式,路由到同一个页面时,会复用组件,不重新创建组件,开销小,推荐。