耘开源,致力于基础平台开发,点击上方“关注”,每天为你分享开发技巧和开源干货
其实vue也用了好几年了,都是从脚手架开始入手的,可能随着年纪,学习的欲望越来越少了,一直想看看vue-router是怎么运作的,但是一直都没有下手.
最近打算自己搞一个基础开发平台,需要自己下手了,这个地方总归是要懂的,花了几天时间,从迷糊到清晰,慢慢的了解了这块的运作方式,把这块原理试着阐释一下:
在我看来本质就是一个前端的拦截器,然后内部跳转,这也是单页应用的核心,不刷新可以完美地搞定所有事情,但是又和以前的ajax有所区别,可以更好的工程化的解决问题,用来满足现在越来越重的前端需求
2.vue的组件,这个算是一个成功的,不过以前我写过flex,可能现在大家不熟悉,那个时候是用actionScript和xml混合协作的是不是很熟悉,没错我感觉现在的这几个vue和angular都参考了那个,而且flash gameover之后那几个大神好像都去了google,没多久angular就面世了,一样的动态绑定,一样的动态刷新,但是组件算是前进了一大步.他脱胎于class但是优于class,让我们可以单个组件封装了一个完整的html和js以及css的功能
3.路由虽然官方的demo是
<router-link to="/foo">Go to Foo</router-link>
但是我感觉开发应该很少这样用,毕竟我们都是代码小能手,这种固定的东西如何我们天天改的需求,所以常用的还是
router.push({ path: 'register', query: { plan: 'private' }})// 我们可以自由的传参和修改地址
但是我们路由总要一个地方管理,这里我们的主角就要出现了VueRouter,我们可以定义好我们的全局路由管理用了吧我们整个项目的路由统一管理
const router=new VueRouter({
routes: [
{
path: '/user/:userId',
name: 'user',
component: User
}
]
})
但是我们的我们没办法把路由都写死在前端,当然我们也可以这样做,但是开始简单,后期维护就负责,而且各自权限管理,也把我们搞得昏头暗地的,我们自然而然地想到了,后台系统把路由组装好,然后再给前端,前端根据后台显示的路由加载界面,这样既可以节省性能,又可以完整的控制权限.
动态加载路由,需要使用官方的api,直接调用函数就可以了,一开始没找到,一直在自己添加原始的路由,结果路由一直匹配不到
router.addRoute
addRoute(route: RouteConfig): ()=> void
后台组装路由,包括基础的对象,主要是path和name,meta用来添加备用信息
@Data
public class IViewMenu {
/**
* 数据ID
*/
private String id;
/**
* 前端路径
*/
private String path;
/**
* 名称
*/
private String name;
private String redirect;
private String component;
private IViewMenuMeta meta;
private List<IViewMenu> children;
@Data
static private class IViewMenuMeta {
/**
* 访问权限
*/
private String access;
/**
* 图标
*/
private String icon;
/**
* 分组标题
*/
private String title;
/**
* 是否缓存
*/
private boolean notCache=false;
/**
* 关闭tab回调函数
*/
private String beforeCloseName;
}
}
4.import,组件的动态加载
我们每一个界面都是一个单独的组件,但是我们现在希望路由规则是后台传过来的,那我们的组件也是根据后台的规则动态加载的,import是我们的常用规则的
// 常用的组件就是这样加载的,其他都是正常属性,component需要我们到前端来加载
{
path: '/login',
name: 'login',
meta: {
title: 'Login - 登录',
hideInMenu: true
},
component: ()=> import('@/view/base/login/login.vue')
}
// 因为eslint 是不允许import在函数使用的,我们就要单独把这个建一个别名
export const _import=file=> ()=> import('@/view/' + file + '.vue')
然后在.eslintignore这个文件里面把这个文件给忽略,不然编译会一直gg
整体流程是就
登录--->加载菜单-->加载组件-->显示主页 核心代码如下:
import Vue from 'vue'
import Router from 'vue-router'
import {routers as routes, _import} from './routers'
import store from '@/store'
import iView from 'view-design'
import { getToken, canTurnTo } from '@/libs/util'
import { isURL } from '@/libs/validate'
import { getRouters } from '@/api/user'
import config from '@/config'
import Main from '@/components/main'
const { homeName }=config
Vue.use(Router)
const router=new Router({
routes,
isAddRouters: false,
mode: 'history'
})
const LOGIN_PAGE_NAME='login'
const turnTo=(to, access, next)=> {
if (canTurnTo(to.name, access, routes)) next() // 有权限,可访问
else next({ replace: true, name: 'error_401' }) // 无权限,重定向到401页面
}
const addRouters=(data)=> {
if (!router.isAddRouters) {
data.forEach(d=> {
loadComponent([d])
routes.push(d)
router.addRoutes([d]) // 动态添加路由
router.isAddRouters=true
})
}
}
const loadComponent=(data)=> {
data.forEach(route=> {
if (route.menu==='3') {
route['component']=Main
} else if (isURL(route.path)) {
route.meta.iframeUrl=route.href
route['meta']['type']='iframe'
} else if (route.target==='iframe') {
route['meta']['iframeUrl']=`${process.env.VUE_APP_SERVER_URL}${route.href}`
} else {
try {
route['component']=_import(route.path) || null
} catch (e) {
console.log(e)
}
}
if (route.children && route.children.length > 0) {
loadComponent(route.children)
}
})
}
const gotoAssess=(to, store, next)=> {
if (store.state.user.hasGetInfo) {
turnTo(to, store.state.user.access, next)
} else {
store.dispatch('getUserInfo', store.state.user.userId).then(user=> {
// 拉取用户信息,通过用户权限和跳转的页面的name来判断是否有权
//限访问;access必须是一个数组,如:['super_admin'] ['super_admin', 'admin']
turnTo(to, user.access, next)
}).catch(()=> {
next({
name: LOGIN_PAGE_NAME
})
})
}
}
/**
* 核心功能是beforeEach,全局的拦截器,可以帮助我们获取到用户跳转之前的所有请求
* 用户to过来大部分都是自动匹配过来的,匹配可以规矩path也可以根据name匹配
*匹配可以包括多个,上下级都有
*/
router.beforeEach((to, from, next)=> {
iView.LoadingBar.start()
const token=getToken()
if (!token && to.name !==LOGIN_PAGE_NAME) {
// 未登录且要跳转的页面不是登录页
next({
name: LOGIN_PAGE_NAME // 跳转到登录页
})
} else if (!token && to.name===LOGIN_PAGE_NAME) {
// 未登陆且要跳转的页面是登录页
next() // 跳转
} else if (token && to.name===LOGIN_PAGE_NAME) {
// 已登录且要跳转的页面是登录页
next({
name: homeName // 跳转到homeName页
})
} else {
if (router.isAddRouters) {
gotoAssess(to, store, next)
} else {
// 没获取用户信息,说明没有获取路由菜单
getRouters(store.state.user.userId).then(routerArr=> {
addRouters(routerArr)
gotoAssess(to, store, next)
})
}
}
})
router.afterEach(to=> {
iView.LoadingBar.finish()
window.scrollTo(0, 0)
})
export default router
描述的可能不是很全,自己遇到的几个问题都再重点突出以下
期待你的关注,这里是悟耘开源,开源项目包括EasyPoi(快速完成Office操作工具包,开源中国GVP项目),风铃(基于springboot的快速开发平台),代码生成器等开源项目,悟耘开源致力于基础平台开发,希望帮助大家提高开发速度,提升开发质量,让大家远离996。关注我,获取分享开源路上的感悟和开发相关技术分享。
<script src="./vue.js"></script>
// 千万注意 :引入路由一定要在引入vue之后,因为vue-router是基于vue工作的
<script src="./node_modules/vue-router/dist/vue-router.js"></script>
# 1. 入口
// 方式1 : url地址为入口 调试开发用
输入url地址 改变哈希值 `01-路由的基本使用.html#/one`
// 方式2 : 声明式导航 : router-link+to (见下面介绍)
# 2. 路由规则
// path : 路由路径
// component : 将来要展示的路由组件
routes: [
{ path: '/one', component: One },
{ path: '/two', component: Two }
]
# 3. 组件
// 使用返回值的这个组件名称
const One=Vue.component('one', {
template: ` <div> 子组件 one </div> `
})
# 4. 出口
<!-- 出口 组件要展示的地方-->
<router-view></router-view>
# 总结
拿到入口哈希路径, 根据路由匹配规则,找到对应的组件,显示到对应的出口位置
<!--
router-link 组件最终渲染为 a标签, to属性转化为 a标签的href属性
to 属性的值 , 实际上就是哈希值,将来要参与路由规则中进行与组件匹配
-->
<router-link to="/one">首页</router-link>
const One={
template: `<div> 子组件 one </div> `
}
<div id="app">
<!-- 1 路由入口:链接导航 -->
<router-link to="/one">One</router-link>
<router-link to="/two">Two</router-link>
<!-- 4 路由出口:用来展示匹配路由视图内容 -->
<router-view></router-view>
</div>
<!-- 导入 vue.js -->
<script src="./vue.js"></script>
<!-- 导入 路由文件 -->
<script src="./node_modules/vue-router/dist/vue-router.js"></script>
<script>
// 3 创建两个组件
const One={
template: '<h1>这是 one 组件</h1>'
}
const Two={
template: '<h1>这是 two 组件</h1>'
}
// 0 创建路由对象
const router=new VueRouter({
// 2. 路由规则
routes: [
{ path: '/one', component: One },
{ path: '/two', component: Two }
]
})
const vm=new Vue({
el: '#app',
//0. 不要忘记,将路由与vue实例关联到一起!
router
})
</script>
<a href="#/one" class="router-link-exact-active router-link-active">One</a>
<a href="#/two" class="">Two</a>
.router-link-exact-active,
.router-link-active {
color: red;
font-size: 50px;
}
const router=new VueRouter({
routes: [],
// 修改默认高亮的a标签的类名
// red 是已经存在过的
linkActiveClass: 'red'
})
导入 : 列表三个手机都要点击进去详情页, 只需要一个组件,显示不同的数据即可
# 入口
<router-link to="/detail/1">手机1</router-link>
<router-link to="/detail/2">手机2</router-link>
<router-link to="/detail/3">手机3</router-link>
<router-link to="/detail">手机4</router-link> 没有参数如何????
# 规则
routes: [
// 2 . 路由规则
{ path: '/detail/:id?', component: Detail }
]
# 获取参数的三种方式
const Detail={
template: `
// 方式1 : 组件中直接读取
<div> 显示详情页内容....{{ $route.params.id }} </div>
`,
created() {
// 方式2 : js直接读取
// 打印只会打印一次,因为组件是复用的,每次进来钩子函数只会执行一次
console.log(this.$route.params.id)
},
// 方式3 : 监听路由的参数,为什么不需要深度监听,因为一个路径变化,就会对应一个对新的路由对象(地址变)
watch: {
$route(to, from) {
console.log(to.params.id)
}
}
}
# 演示 :
<router-link to="/detail/4?age=21#one">detail</router-link>
{ path: '/detail/:id?', component: detail }
在组件内 created打印 this.$route
> fullPath: "/detail/4?id=001#one"
> hash : "#one"
> params : {id:'4'}
> query : {age : 21}
> path : '/detail/4'
导入 : url测试 parent 和child, 想让child 在 parent 中显示
const parent={
template: `<p>parent <router-view> </router-view> </p>`
}
const child={
template: `<p>child</p>`
}
const router=new VueRouter({
routes: [
{
path: '/parent',
component: parent,
children: [
{ path: '/child', component: child }
]
}
]
})
# 命名
routes: [
{
path: '/parent',
name: 'parent',
component: parent
}
]
# 入口链接 + 跳转 (使用 path 和 name 的转换)
<!-- 方式1 : url手动写 -->
<!-- 方式2 : 入口链接 声明式导航 -->
<router-link to="/parent">点击</router-link>
<router-link :to="{ name : 'parent' }">点击</router-link> # 忘了 带 : 原始对象类型
<!-- 方式3 : 编程式导航 -->
fn() {
// this.$router.push('/parent')
this.$router.push({
name: 'parent'
})
}
导入 : 有时候想同时 (同级) 展示多个视图,
需求 : 访问 / 根目录 同时展示以下三个组件
const header={
template: `<p>header </p>`
}
const main={
template: `<p>main </p>`
}
const footer={
template: `<p>footer </p>`
}
# 以前的那个方式只能显示三个 header
# 演示之前的效果
routes: [
{
path: '/',
components: {
default: header,
m: main,
f: footer
}
}
]
<router-view> </router-view>
<router-view name="m"> </router-view>
<router-view name="f"> </router-view>
redirect: '/header'
redirect: { name: 'header' }
redirect: to=> {
// console.log(to)
return {
name: 'about'
}
}
# 入口
<router-link to="/header/3">123</router-link>
# 规则
routes: [
{
path: '/header/:id',
component: header,
}
]
# 获取参数
const header={
template: `<p>header {{ $route.params.id }} </p>`
}
# 入口
<router-link to="/header/3">123</router-link>
# 规则
routes: [
{
path: '/header/:id',
component: header,
// 如果 props 被设置为 true,route.params 将会被设置为组件属性
props: true
}
]
# 获取参数
const header={
// 参数 id 当成参数
props: ['id'],
template: `<p>header {{ id }} </p>`
}
# 入口
<router-link to="/header">123</router-link>
# 规则
routes: [
{
path: '/header',
component: header,
props: { foo: '0000' }
}
]
# 组件
const header={
props: ['foo'],
template: `<p>header {{ foo }} </p>`
}
# 同对象模式一样
# 区别是props值不一样
props: to=> {
return { foo: '0000' }
}
实际生活中的应用界面,通常由多层嵌套的组件组合而成。同样地,URL 中各段动态路径也按某种结构对应嵌套的各层组件。
借助 vue-router,使用嵌套路由配置,就可以很简单地表达这种关系。
<div id="app">
<router-view></router-view>
</div>
const User={
template: '<div>User {{ $route.params.id }}</div>'
}
const router=new VueRouter({
routes: [
{ path: '/user/:id', component: User }
]
})
这里的 <router-view> 是最顶层的出口,渲染最高级路由匹配到的组件。同样地,一个被渲染组件同样可以包含自己的嵌套 <router-view>。例如,在 User 组件的模板添加一个 <router-view>
const router=new VueRouter({
routes: [
{ path: '/user/:id', component: User,
children: [
{
// 当 /user/:id/profile 匹配成功,
// UserProfile 会被渲染在 User 的 <router-view> 中
path: 'profile',
component: UserProfile
},
{
// 当 /user/:id/posts 匹配成功
// UserPosts 会被渲染在 User 的 <router-view> 中
path: 'posts',
component: UserPosts
}
]
}
]
})
注意,以 / 开头的嵌套路径会被当作根路径。 这让你充分的使用嵌套组件而无须设置嵌套的路径。
routes: [
{
path: '/header',
component: header,
meta: {
title: 'XXXX'
}
}
]
created() {
document.title=this.$route.meta.title
}
const one={
template: `
<div> <button @click="handleClick('back')">返回 上一页</button>
<button @click="handleClick('push')">跳转 two页</button>
<button @click="handleClick('replace')">替换 two页</button>
</div>`,
methods: {
handleClick(type) {
if (type=='back') {
// 返回
this.$router.back()
} else if (type=='push') {
// 跳转 有历史记录
this.$router.push('/two')
} else {
// 替换 没有历史记录
this.$router.replace('/two')
}
}
}
}
const two={
template: `<p>two </p>`
}
Vue 2 中配置路由是一个重要的步骤,它可以让你的单页应用 (SPA) 在不同的页面之间进行跳转而无需重新加载整个页面。
以下是 Vue 2 中配置路由的基本步骤:
安装 Vue Router
首先,你需要安装 Vue Router 包。可以使用以下命令通过 npm 或 yarn 进行安装:
Bash
npm install vue-router
或者
Bash
yarn add vue-router
创建路由配置
创建一个路由配置对象,该对象包含要定义的路由规则。路由规则是将 URL 与要显示的组件进行映射的规则。
JavaScript
import Vue from 'vue';
import VueRouter from 'vue-router';
Vue.use(VueRouter);
const routes=[
{ path: '/', component: Home },
{ path: '/about', component: About },
{ path: '/contact', component: Contact },
];
const router=new VueRouter({
routes,
});
export default router;
在这个例子中,我们定义了三个路由规则:
/:将映射到 Home 组件
/about:将映射到 About 组件
/contact:将映射到 Contact 组件
在根实例中注册路由
在你的 Vue 根实例中,你需要将路由实例注册到 Vue 实例上。这将使 Vue 能够感知路由并相应地更新视图。
JavaScript
import Vue from 'vue';
import App from './App.vue';
import router from './router';
new Vue({
router,
render: function (createElement) {
return createElement(App);
},
}).$mount('#app');
使用路由链接
要从一个路由导航到另一个路由,可以使用 router-link 组件。router-link 组件就像一个普通的 HTML 链接,但它会触发路由导航而不是重新加载页面。
HTML
Home
About
Contact
使用路由参数
可以使用路由参数来传递数据给路由组件。路由参数在 URL 中定义,可以使用 :param 语法。
JavaScript
const routes=[
{ path: '/user/:id', component: User },
];
在 User 组件中,可以使用 $route.params 对象访问路由参数。
JavaScript
export default {
template: `
User ID: {{ $route.params.id }}
`,
};
使用路由守卫
路由守卫可以用来在路由导航发生之前或之后执行代码。这可以用于诸如身份验证、授权和数据预取等任务。
JavaScript
const router=new VueRouter({
routes,
beforeResolve: (to, from, next)=> {
// 在导航发生之前执行代码
},
});
这些只是 Vue 2 中配置路由的基础知识。有关更高级的主题,请参阅 Vue Router 文档:
*请认真填写需求信息,我们会在24小时内与您取得联系。