从零实现一个简易版Vue-Router,太通俗易懂了
作者:互联网
1、含义:
浏览器无论访问什么地址,访问的真实页面始终是index.html
,vue
根据不同的地址,渲染不同的组件。由于真实页面是唯一的,用户看到的页面切换,实际上是组件的切换,这种应用称之为单页应用
2、开发单页应用涉及到两个核心问题:
- 在哪个位置切换组件
- 访问路径如何对应组件
二、初识vue-router
1、vue-router
是 Vue.js 官方的路由管理器,使用vue-router
可以非常轻松的构建单页应用程序。
官网地址:https://router.vuejs.org/zh/
2、路由实际上就是可以理解为指向,就是我在页面上点击一个按钮需要跳转到对应的页面,这就是路由跳转。
3、首先我们来学习三个单词:
route
:首先它是个单数,译为路由,即我们可以理解为单个路由或者某一个路由;routes
:它是个复数,表示多个的集合才能为复数;即我们可以理解为多个路由的集合,JS中表示多种不同状态的集合的形式只有数组和对象两种,事实上官方定义routes是一个数组;所以我们记住了,routes表示多个数组的集合;router
:译为路由器,上面都是路由,这个是路由器,我们可以理解为一个容器包含上述两个或者说它是一个管理者,负责管理上述两个;举个常见的场景的例子:当用户在页面上点击按钮的时候,这个时候router就会去routes中去查找route,就是说路由器会去路由集合中找对应的路由;
4、路由模式:
hash
:路径来自于地址栏中#后面的值,这种模式兼容性比较好,但是不好看,不符合用户习惯history
:路径来自于真实的地址路径,旧浏览器不兼容(我们这里使用这一种)abstract
:路径来自于内存(一般用于移动端)
二、Vue Router案例
1、、基于vue-cli创建名为router
的vue项目
2、打开router
项目终端,使用如下指令安装vue-router
插件库
npm install vue-router
3、在components
下新建Header.vue
、Footer.vue
公共组件
1)Header.vue
代码-主要实现菜单
<template>
<!-- 头部菜单 -->
<div class="header">
<!-- 使用 router-link 组件来导航. -->
<!-- 通过传入 `to` 属性指定链接. -->
<!-- <router-link> 默认会被渲染成一个 `<a>` 标签 -->
<router-link to="/">首页</router-link>
<router-link to="/blog">博客</router-link>
<router-link to="/about">关于我们</router-link>
</div>
</template>
<script>
export default {
}
</script>
<style scoped>
.header {
width:1000px;
margin:0 auto;
height:80px;
background:black;
display:flex;
justify-content: center;
align-items:center;
}
.header a{
font-size:18px;
text-decoration: none;
color:white;
margin:0 10px;
}
</style>
2)Footer.vue
代码
<template>
<div class="footer">
底部区域
</div>
</template>
<script>
export default {
}
</script>
<style scoped>
.footer{
width:1000px;
line-height:80px;
background: gray;
margin:0 auto;
text-align:center;
color:white;
}
</style>
4、在src下新建views
目录,主要放一些非公共页面组件
5、在views
下新建Home.vue
、Blog.vue
、About.vue
,分别代码主页、博客页和关于我们
1)Home.vue
代码
<template>
<div>
这里是首页内容
</div>
</template>
<script>
export default {
}
</script>
<style scoped>
</style>
2)Blog.vue
代码
<template>
<div>
这里是博客内容
</div>
</template>
<script>
export default {
}
</script>
<style scoped>
</style>
3)About.vue
代码
<template>
<div>
这里是关于我们内容
</div>
</template>
<script>
export default {
}
</script>
<style scoped>
</style>
6、在src下新建router
目录,存放路由配置,并在router
目录下新建config.js
和index.js
config.js
代码如下:
1)写法1——不推荐
原因:因为如果页面太多,依赖就会越多,会导致加载一个页面时将依赖的也加载进来,所以我们需要使用延时加载,访问哪个路径就导入哪个页面
//index.js
// 导入页面组件,@代表src目录,编译后代表根路径
import Home from '@/views/Home.vue'
import Blog from '@/views/Blog.vue'
import About from '@/views/About.vue'
// 导出路由配置
export default{
// 1、路由模式
mode:"history",
// 2、路径配置,数组形式,一个路径对应一个对象
routes:[
{
path:"/",
component:Home
},
{
path:"/blog",
component:Blog
},
{
path:"/about",
component:About
}
]
}
2)写法2——推荐-懒加载,用到哪个就加载哪个
// 导出路由配置
export default{
// 1、路由模式
mode:"history",
// 2、路径配置,数组形式,一个路径对应一个对象
routes:[
{
path:"/",
component:()=>import('@/views/Home.vue')
},
{
path:"/blog",
component:()=>import('@/views/Blog.vue')
},
{
path:"/about",
component:()=>import('@/views/About.vue')
}
]
}
index.js
代码如下:
// 1、导入vue-router
import VueRouter from "vue-router";
// 2、导入vue
import Vue from "vue";
// 3、安装
import config from "./config.js";
// 4、Vue安装vue-router插件
Vue.use(VueRouter);
// 5、创建路由对象
const router = new VueRouter(config);
// 6、导出
export default router;
当然你也可以将这两个js合并到index.js
文件中,如下:
// 1、导入vue-router
import VueRouter from 'vue-router'
// 2、导入vue
import Vue from 'vue'
// 3、安装
Vue.use(VueRouter)
// 4、创建VueRouter实例-路由对象
const router = new VueRouter({
// 5、配置路由
//5.1配置路模式
mode:"history",
// 5.2 配置路由对象数组
routes: [
{
path: "/",
name: "Home",
component: () => import("@/views/Home.vue"),
},
{
path: "/blog",
name: "Blog",
component: () => import("@/views/Blog.vue"),
},
{
path: "/about",
name: "About",
component: () => import("@/views/About.vue"),
},
]
})
// 6、导出路由对象
export default router;
7、在main.js
中使用路由
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
Vue.config.productionTip = false
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
通过注入路由器,我们可以在任何组件内通过 this.$router
访问路由器,也可以通过 this.$route
访问当前路由(后面演示)。
8、在App.vue
中导入、注册、使用相关组件
<template>
<div id="app">
<!-- 头部 -->
<Header/>
<!-- 中间内容区域 -->
<div class="container">
<!-- 该组件会根据不同的访问路径,渲染不同的组件 -->
<router-view></router-view>
</div>
<!-- 底部 -->
<Footer/>
</div>
</template>
<script>
// 导入组件
import Header from './components/Header.vue'
import Footer from './components/Footer.vue'
export default {
name: 'app',
components: {
// 注册组件
Header,
Footer
}
}
</script>
<style>
.container{
border: 1px solid gray;
margin:30px auto;
width:1000px;
min-height: 200px;
}
</style>
9、执行如下指令启动服务,运行至浏览器,访问
三、router-link声明式导航组件和router-view
1、Header.vue
中的template
中,不使用a
标签跳转原因:
因为如果使用a
标签跳转,虽然能实现页面跳转,但点击上面的菜单访问,会发现了一个问题,就是页面会刷新,这会导致网站响应速度过慢,因为页面刷新的过程是这样的:
浏览器输入地址–>请求至服务器–>服务器返回html+css+js –>浏览器渲染页面,执行js –>创建vue实例–>渲染根组件->router-view
标签根据不同的路径渲染不同的组件
而最佳的方式就是直接通过router-view
组件根据不同的路径渲染不同的组件,就是我们上面使用的router-link
声明式导航组件。
2、router-link
组件支持用户在具有路由功能的应用中点击导航。通过to
属性指定目标地址,默认渲染为带有正确连接的a
标签,可以通过配置tag
属性生成别的标签。另外,当目标路由成功激活时,链接元素自动设置一个表示激活的css类名。
3、router-link
组件的属性有(不作展开讲-了解):
to
(必选参数):类型string/location,表示目标路由的链接,该值可以是一个字符串,也可以是动态绑定的描述目标位置的对象replace
类型: boolean,默认值: false,设置 replace 属性的话,当点击时,会调用 router.replace() 而不是 router.push(),于是导航后不会留下 history 记录-
append
类型: boolean,默认值: false,设置 append 属性后,则在当前 (相对) 路径前添加基路径 -
tag
类型: string,默认值: “a”,指定router-link渲染成某种标签 active-class
类型: string,默认值: “router-link-active”,设置 链接激活时使用的 CSS 类名。默认值可以通过路由的构造选项 linkActiveClass 来全局配置exact
类型: boolean,默认值: false,”是否激活” 默认类名的依据是 inclusive match (全包含匹配)event
类型: string | Array<string>默认值: ‘click’,声明可以用来触发导航的事件,可以是一个字符串或字符串数组exact-active-class
类型: string,默认值: “router-link-exact-active”,配置当链接被精确匹配的时候应该激活的 class。注意默认值也是可以通过路由构造函数选项 linkExactActiveClass 进行全局配置的。
4、router-view
组件:写在组件想要渲染的地方,等组件跳转过来就渲染,该组件会根据不同的访问路径,渲染不同的组件
四、命名路由
以上Header.vue
中的template
中的router-link
中to
属性值,我们是写死的,效果不是太好,如果一旦菜单路由配置对应的跳转地址发生变化,则可能会需要做大量的修改,因此,我们可以做如下优化:
1)我们修改src\router
目录下config.js
,给每个路由配置使用name
进行命名,让名称与路由地址对应:
// 导出路由配置
export default{
// 1、路由模式
mode:"history",
// 2、路径配置,数组形式,一个路径对应一个对象
routes:[
{
path:"/",
name:"Home",
component:()=>import('@/views/Home.vue')
},
{
path:"/blog",
name:"Blog",
component:()=>import('@/views/Blog.vue')
},
{
path:"/about",
name:"About",
component:()=>import('@/views/About.vue')
}
]
}
2)修改Header.vue
中的template
,直接使用名称去路由配置中匹配路由地址,注意要使用:to
,值为一个对象:
<template>
<!-- 头部菜单 -->
<div class="header">
<router-link :to="{name:'Home'}">首页</router-link>
<router-link :to="{name:'Blog'}">博客</router-link>
<router-link :to="{name:'About'}">关于我们</router-link>
</div>
</template>
3)测试效果与之前一样
五、动态路由匹配
1、捕获所有路由或404 Not Found路由
如果我们浏览器输入的一个地址,在我们的项目中并不存在对应的具体页面,即404找不到,这种不存在的地址有无限多种可能,这些请求路径,我们都交给一个404页面处理,那么对于404页面而言,就需要匹配所有不存在请求地址。
1)在components
中新建NotFound.vue
组件
<!-- 纯css 3D立体样式 -->
<template>
<div class="container404">
<div class="page404">
<div class="f404"><span>4</span><span>0</span><span>4</span></div>
<div class="f404-des">
该页面不存在(´・ω・`)
</div>
</div>
</div>
</template>
<script>
export default {
}
</script>
<style scoped>
.container404 {
background-color: #ECECEC;
font-family:Arial, Helvetica, sans-serif;
font-size: 14px;
color: #3c3c3c;
padding:150px;
}
.page404 .f404{
text-align: center;
font-size: 150px;
font-weight: bold;
line-height: 100px;
letter-spacing: 5px;
color: #fff;
margin-bottom:60px;
}
.page404 .f404 span {
cursor: pointer;
text-shadow: 0px 0px 2px #686868,
0px 1px 1px #ddd,
0px 2px 1px #d6d6d6,
0px 3px 1px #ccc,
0px 4px 1px #c5c5c5,
0px 5px 1px #c1c1c1,
0px 6px 1px #bbb,
0px 7px 1px #777,
0px 8px 3px rgba(100, 100, 100, 0.4),
0px 9px 5px rgba(100, 100, 100, 0.1),
0px 10px 7px rgba(100, 100, 100, 0.15),
0px 11px 9px rgba(100, 100, 100, 0.2),
0px 12px 11px rgba(100, 100, 100, 0.25),
0px 13px 15px rgba(100, 100, 100, 0.3);
-webkit-transition: all .1s linear;
transition: all .1s linear;
}
.page404 .f404 span:hover {
text-shadow: 0px 0px 2px #686868,
0px 1px 1px #fff,
0px 2px 1px #fff,
0px 3px 1px #fff,
0px 4px 1px #fff,
0px 5px 1px #fff,
0px 6px 1px #fff,
0px 7px 1px #777,
0px 8px 3px #fff,
0px 9px 5px #fff,
0px 10px 7px #fff,
0px 11px 9px #fff,
0px 12px 11px #fff,
0px 13px 15px #fff;
-webkit-transition: all .1s linear;
transition: all .1s linear;
}
.page404 .f404-des{
text-align: center;
color: #666;
font-family: cursive;
font-size: 20px;
text-shadow: 0 1px 0 #fff;
letter-spacing: 1px;
line-height: 2em;
}
</style>
2)在src\router
目录下config.js
,新增匹配所有请求的路由配置:
// * 号通配符,其他配置都未匹配到都会走这个配置,/user-*'会匹配以 `/user-` 开头的任意路径
{
path:"*",
name:"404",
component:()=>import('@/components/NotFound.vue')
}
3)测试下效果:
补充:
1、高级匹配模式:vue-router
使用 path-to-regexp
作为路径匹配引擎,所以支持很多高级的匹配模式,例如:可选的动态路径参数、匹配零个或多个、一个或多个,甚至是自定义正则匹配。查看它的文档学习高阶的路径匹配,还有这个例子 展示 vue-router
怎么使用这类匹配
2、匹配优先级:
有时候,同一个路径可以匹配多个路由,此时,匹配的优先级就按照路由的定义顺序:谁先定义的,谁的优先级就最高。
2、响应路由参数的变化
1、我们经常需要把某种模式匹配到的所有路由,全都映射到同个组件。例如,我们有一个 User
组件,对于所有 username
各不相同的用户,都要使用这个组件来渲染。那么,我们可以在 vue-router
的路由路径中使用“动态路径参数”(dynamic segment) 来达到这个效果。
2、在views
目录下新建User.vue
组件
<template>
<div>
欢迎 {{ $route.params.username }} 用户回来!
</div>
</template>
<script>
export default {
}
</script>
<style scoped>
</style>
3、修改src\router
目录下config.js
,新增配置如下:
{
path: '/user/:username',
name:'User',
component:()=>import('@/views/User.vue')
}
现在呢,像 /user/foo
和 /user/bar
都将映射到相同的路由。一个“路径参数”使用冒号 :
标记。当匹配到一个路由时,参数值会被设置到 this.$route.params
,可以在每个组件内使用。
4、修改Header.Vue
的template
新增用户请求:
<!--params:传参 -->
<router-link :to="{name:'User',params:{username:'小明'}}">用户</router-link>
<!-- 或直接写地址然后动态拼接参数 -->
<!-- <router-link to="/user/小明">用户</router-link> -->
5、测试
6、上面的理解了,类似/user/:username/post/:post_id
多参数的应该也就会了
1)修改src\router
目录下config.js
,修改上面动态地址配置如下:
{
path: '/user/:username/post/:post_id',
name:'User',
component:()=>import('@/views/User.vue')
}
2)修改User.vue
的template
如下:
<template>
<div>
欢迎 {{ $route.params.username }} 用户回来,post_id: {{ $route.params.post_id }}
</div>
</template>
3)修改Header.Vue
的template
中动态地址:
<!--params:传参 -->
<router-link :to="{name:'User',params:{username:'小明',post_id:20}}">用户</router-link>
<!-- 或直接写地址然后动态拼接参数 -->
<!-- <router-link to="/user/小明/post/20">用户</router-link> -->
4)测试:
标签:Vue,import,简易版,vue,router,组件,Router,0px,路由 来源: https://blog.csdn.net/weixin_51646421/article/details/122866546