入门 | Vue Router (vuejs.org)
安装
1
| yarn add vue-router@next ##@next表示安装最新版本
|
vue3.0默认是router4+的版本而vue2使用的是3的版本
查看安装的router包
1 2
| yarn list | findstr router ##windos用这种即可 yarn list | grep router ##windos上好像并不支持grep
|
路由初始化
在src/router/index.js
文件内
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| import {createRouter, createWebHistory} from "vue-router"; import Home from "@/View/Home.vue";
export default createRouter({
history: createWebHistory(),
routes: [ { path: "/", component: Home, name: "Home" }, { path: "/register", component: () => import( '../View/Register.vue'), name: "Register" }, ] } );
|
在App.vue
,代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <template> <router-view></router-view> </template>
<style> ##app { font-family: Avenir, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: ##2c3e50; margin-top: 60px; }
|
页面跳转
测试
在Menu.vue
中进行测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| 组件标签跳转[本质上还是js跳转] <router-link to="/">Home</router-link> | <router-link to="/register">Register</router-link>
js代码跳转 <template> <button @click.prevent="goto('/')">主页</button> <button @click.prevent="goto('/register')">注册</button> </template> <script> const Menu = { name: "Menu", methods: { goto(url){ this.$router.push(url); //跳转到指定的url地址对应的页面中 this.$router.back(); //返回上一个页面 this.$router.go(1); //跳转到指定的n个页面,正数表示前进,负数表示后退 this.$router.forward(); //前进下一个 } } } export default Menu; </script> html原生href跳转 <p><a href="/">html-Home</a></p> <p><a href="/register">html-Register</a></p>
|
tag
1
| <router-link class="col02" tag="li" :to="'/goods/'+sku.id"><img :src="sku.default_image_url"></router-link>
|
vue-router 3.1.x 以上版本 也是建议使用v-slot处理 原来的tag形式浏览器中没有效果
1 2 3
| <router-link :to="'/goods/'+sku.id" custom v-slot="{ navigate }"> <li @click="navigate" @keypress.enter="navigate" role="link" class="col02"><img :src="sku.default_image_url"></li> </router-link>
|
不同页面的参数传递
你可以在同一个路由设置有多个参数,它们将会映射到$route.params
相应的字段。列如
匹配模式 |
匹配路径 |
$route.params |
/users/:username |
/user/eduardo |
{ username : ‘eduardo’ } |
/users/:username/posts/:postId |
/user/eduardo/post/123 |
{username:’eduardo’,postId:’123’} |
除了$route.params
之外,$route
对象还公开了其他信息,如$route.query
(如果URL存在参数)、$route.hash等
Menu.vue
1 2 3 4 5 6 7 8 9 10 11
| <template> <nav> <router-link to="/">Home</router-link> | <router-link to="/register">Register</router-link> | <router-link to="/goods">Goods</router-link> | <router-link to="/list">List</router-link> </nav> </template>
|
发送和接受查询字符串参数
Goods.vue
数据获取部分均在这一个页面不多写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| <template> <h1>Goods页面</h1> <p>{{goods_id_query}}号商品的详细信息-query</p> <p>{{goods_id_params}}号商品的详细信息-params{{cat}}</p> <nav> <router-link to="/">Home</router-link> | <router-link to="/register">Register</router-link> | <router-link to="/goods">Goods</router-link> | <router-link to="/list">List</router-link> </nav> </template>
<script> export default { name: 'GoodsPage', data(){ return{ goods_id_query:0, goods_id_params:Number, cat:Number } }, created() { //接受地址栏的查询字符串 //router用于跳转,router由于获取url的查询参数 this.goods_id_query=this.$route.query.id//id就是查询参数,这一点和express很相 console.log(this.$route.query.cat); //接受路由参数 this.goods_id_params=this.$route.params.id; this.cat=this.$route.params.cat; } } </script>
|
List.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| <template> <h1>List页面</h1> <nav> <router-link to="/">Home</router-link> | <router-link to="/register">Register</router-link> | <router-link to="/goods">Goods</router-link> | <router-link to="/list">List</router-link> </nav> <ul> <li><router-link to="/goods?id=1">1号商品</router-link></li> <li><router-link to="/goods?id=5">5号商品</router-link></li> <li><router-link to="/goods?id=14&cat=7">14号商品</router-link></li> </ul> <br>
</template>
<script> export default { name:'ListPage', } </script>
|
发送和接受路由参数
Goods.vue
List.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <template> <h1>List页面</h1> <nav> <router-link to="/">Home</router-link> | <router-link to="/register">Register</router-link> | <router-link to="/goods">Goods</router-link> | <router-link to="/list">List</router-link> </nav> <ul> <li><router-link to="/goods/1">商品1</router-link></li> <li><router-link to="/goods/5">商品5</router-link></li> <li><router-link to="/goods/14/7">商品14幸运7</router-link></li> </ul> </template>
<script> export default { name:'ListPage', } </script>
|
命名路由(反向解析name)
命名路由 | Vue Router (vuejs.org)
除了 path
之外,你还可以为任何路由提供 name
。这有以下优点:
- 没有硬编码的 URL
params
的自动编码/解码。
- 防止你在 url 中出现打字错误。
- 绕过路径排序(如显示一个)
1 2 3 4 5 6 7
| const routes = [ { path: '/user/:username', name: 'user', component: User, }, ]
|
要链接到一个命名的路由,可以向 router-link
组件的 to
属性传递一个对象:
1 2 3
| <router-link :to="{ name: 'user', params: { username: 'erina' }}"> User </router-link>
|
这跟代码调用 router.push()
是一回事:
1
| router.push({ name: 'user', params: { username: 'erina' } })
|
在这两种情况下,路由将导航到路径 /user/erina
。
完整的例子在这里.
嵌套路由
嵌套路由是vue-router提供给开发者应用在同一个页面组件下切换不同自组建的时候用的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| routes: [ { path: "/user", component: UserSpace, children:[ { path:'friend', component:UserFriend }, { path:'order', component:UserOrder }, ] }, ]
|
在views/UserSpace.vue
中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| <template> <h1>用户公共代码[页面头部]</h1> <Menu></Menu> <nav v-if="$route.path==='/user'"><a>当前时间:{{ currentTime }}欢迎回来:阳光</a></nav> <nav> <router-link to="/user">个人主页</router-link> | <router-link to="/user/friend">好友列表</router-link> | <router-link to="/user/order">我的订单</router-link> </nav> <router-view></router-view> </template> <script> import Menu from "@/components/Menu.vue";
export default { name: 'UserSpace', data() { return { currentTime: Date } }, methods: { getCurrentTime() { const data = new Date(); const hours = data.getHours(); const minutes = data.getMinutes()<10 ? '0'+data.getMinutes() : data.getMinutes(); const seconds = data.getSeconds()<10 ? '0'+data.getSeconds() : data.getSeconds(); this.currentTime = `${hours}:${minutes}:${seconds}` } }, mounted() { this.getCurrentTime();//进入页面调用该方法获取当前时间 clearInterval(myTimeDisplay );//销毁之前定时器 var myTimeDisplay = setInterval(() => { this.getCurrentTime(); //每秒更新一次时间 }, 1000); }, components:{ Menu, } } </script>
|
编写这样一段代码,实现子路由嵌套父路由的组件和url,并且判断在父路由则显示用户信息和当前时间
(http://127.0.0.1:8080/user)
(http://127.0.0.1:8080/user/friend)
src/views/UserOrder.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <template> <h2>Order</h2> <ul> <li>上课</li> <li>上课</li> <li>上课</li> <li>上课</li> <li>上课</li> </ul> </template>
<script setup>//setup-vue3的语法糖 </script>
|
src/views/UserFriend
1 2 3 4 5 6 7 8 9 10 11 12 13
| <template> <h2>Friend</h2> <ul> <li>小红</li> <li>小白</li> <li>小绿</li> <li>小紫</li> </ul> </template>
<script setup>//setup-vue3的语法糖 </script>
|
路由守卫
导航守卫 | Vue Router (vuejs.org)
路由守卫:也叫导航守卫,功能作用类似于上面的axios的拦截器。我们可以基于导航守卫编写一些在页面跳转过程中需要编写的公共代码,例如:权限的验证,页面的公共初始化,页面跳转之前的一些公共逻辑。
前置守卫
在前置路由打印对应的方法信息
1 2 3 4 5 6 7 8 9 10 11
| router.beforeEach((to, from, next) => { console.log(to) console.log(from) console.log(next) console.log(next()) return next()
|
1 2 3 4 5
| router.beforeEach((to, from, next) => { console.log(to) //下一个页面的路由对象 console.log(to.name) //to.name获取下一个路由定义的别名即name console.log(to.query) //下一个页面的路由的查询参数 为键值对 console.log(to.query.id) //下一个页面的路由的查询参数的id键的值 为值
|
前置路由拦截用户的应用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| import {createRouter, createWebHistory} from "vue-router"; import Home from "@/views/Home.vue"; import Goods from "@/views/Goods.vue"; import List from "@/views/List.vue"; import UserSpace from "@/views/UserSpace.vue"; import UserFriend from "@/views/UserFriend.vue"; import UserOrder from "@/views/UserOrder.vue";
const routes = [ ] const router = createRouter({ history: createWebHistory(process.env.BASE_URL), routes })
router.beforeEach((to, from, next) => { if ((to.name != "Home") && to.path != 'Login'){ if (to.query.user=='小明'){ next(); }else { next({name:"Home"}) } }
else next() }) export default router
|
(http://127.0.0.1:8080/user?user=小明)不这样填写内容无非正常访问user页面,或者只能访问登陆和主页面
后置首位
1 2 3 4
| router.afterEach((to, from, failure) => { if (!failure) console.log('对不起你没有权限进入') })
|
(路由)独享守卫
1 2 3 4 5 6 7 8 9 10 11
| const routes = [ { path: "/goods/:id", component: Goods, beforeEnter: (to, from) => { console.log(to); console.log(from); return true }, ]
|
问题
vue-router能不能根据当前访问的路由修改对应的组件,指在router的配置文件index.js实施
大致意识未动态路由匹配动态路径,因为我想后匹配到端渲染的SSR静态模板文件,省去一部分的渲染功夫
解决思路
一、直接在路由配置中进行动态导入路(失败,可跳过)
1 2 3 4 5 6
| { path: '/user/:id', component: () => import((`@/views/User${route.params.id}.vue`)),
}
|
为定义?那我就拿出来
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| const User = (to, from, next) => { const id = to.params.id return import(`../views/User${id}.vue`) } ...
{ path: '/user/:id', component: User
}
|
这种根本就四不像,像是路由守卫(导航守卫),又没有对应的方法和实例调用
总而言之在router和组件的script之外没有办法拿到router,使用了useRouter不知道能不能行,反正我当初在试了半天也不行
那使用导航守卫吧
二、使用前置守卫进行拦截然后更改成对应的组件
1.直接替换组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| import {createRouter, createWebHistory} from "vue-router"; const router = createRouter({ history: createWebHistory(), routes: [ { path: '/user/:id', component: User } ] }) router.beforeEach( (to, from, next) => { console.log(to.params) console.log(to.query) console.log(from.params) console.log(from.query) to.matched[0].components.default = User2 next(); }) export default router
|
2.动态导入组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| import {createRouter, createWebHistory} from "vue-router"; const router = createRouter({ history: createWebHistory(), routes: [ { path: '/user/:id', component: User } ] }) router.beforeEach( async (to, from, next) => { console.log(to.params) console.log(to.query) console.log(from.params) console.log(from.query) const dynamic_component =await import(`../views/User${to.params.id}.vue`) to.matched[0].components.default = dynamic_component.default next(); }) export default router
|
三、vue的vue-loader(vite默认携带)可以分离组件的template、script、style,通过src导入那么能不能在template初始化之前修改src的属性(失败)
假如使用这一个方法组件就可以固定动态改变的就是页面内容,上面的方法反之,页面内容不会动态改变