vue-rouer实践使用

# 配置及跳转

# 基础的配置

import Vue from 'vue'
import Router from 'vue-router'
import routes from './routes';
Vue.use(Router)

export default new Router({
    mode: 'history', // 不使用#方式
    base: process.env.BASE_URL,
    routes
});

// routes.js文件是专门处理路由的 配置影射关系
export default  [
    {
        path:'/',
        redirect: {name:'home'} // 默认访问根路径时 可以重定向到home路由
    },
    {
        name:'home',
        path:'/home', //  实现了路由的懒加载
        component:()=>import('../views/Home.vue')
    },
    {
        path:'/profile',
        name:'profile',
        component:()=>import('../views/Profile.vue')
    },
    {
        path:'/user',
        name:'user',
        component:()=>import('../views/User.vue')
    },
    {
        path:'/login',
        name:'login',
        component:()=>import('../views/Login.vue')
    },
    {
        path:'*',
        component:()=>import('../views/404.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

# 路由的导航应用

引入bootstrap,进行安装

//yarn add bootstrap@3
import 'bootstrap/dist/css/bootstrap.css'
<template>
<div>
    <nav class="navbar navbar-default">
        <div class="container-fluid">
            <div class="navbar-header">
                <a class="navbar-brand" href="#">
                管理系统
                </a>
            </div>
            <ul class="navbar-nav nav">
                    <li>
                        <router-link :to="{name:'home'}">首页</router-link>
                    </li>
                    <li>
                        <router-link :to="{name:'profile'}">个人中心</router-link>
                    </li>
                    <li>
                        <router-link :to="{path:'/user'}">用户管理</router-link>
                    </li>
                    <li>
                        <router-link :to="{name:'login'}">登录</router-link>
                    </li>
            </ul>
        </div>
    </nav>
</div>
</template>
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

# 配置命名视图

# 一个路径匹配多个路由

在项目中一般情况是不使用命名视图的;实现了路由的懒加载;

{
    name:'home',
    path:'/home', //  实现了路由的懒加载
    components:{ // 当前路径 匹配对个router-view
        default:()=>import('../views/Home.vue'),
        name:()=>import('../components/Name.vue'),
        version:()=>import('../components/Version.vue')
    }
}
1
2
3
4
5
6
7
8
9

# 二级路由

# 配置

父路径跳转必须使用路径跳转使用name, 不显示默认孩子; 注意这里的path路由前面不用加/;

{
        path:'/user',
        name:'user',
        component:()=>import('../views/User.vue'),
        children:[ //  子路由配置
            {
                path:'', // 如果渲染默认路由 必须使用path跳转
                redirect:{name:'userAdd'}
            },
            {
                name:'userAdd',
                path:'userAdd',//注意这里的path路由前面不用加/
                component:()=>import('../views/UserAdd')
            },
            {
                name:'userList',
                path:'userList',
                component:()=>import('../views/UserList')
            }
        ]
},
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 跳转

// user.vue
<template>
<div class="row">
    <div class="col-md-3">
        <nav class="nav nav-stacked">
            <li>
                <router-link :to="{name:'userAdd'}">用户添加</router-link>
            </li>
            <li>
                <router-link :to="{name:'userList'}">用户列表</router-link>
            </li>
        </nav>
    </div>
    <div class="col-md-9">
        <router-view></router-view>
    </div>
</div>
</template>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 跳转及参数传递

# 通过方法跳转

# 编程式导航
<template>
    <div>
        <button class="btn btn-primary" @click="login">登录</button>
    </div>
</template>

<script>
export default {
    methods: {
        login(){
            this.$router.push({name:'login'})
        }
    },
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# to方式及参数处理

{
    name:'userDetail',
    path:'userDetail',
    component:()=>import('../views/UserDetail')
},
{
    name:'detail',
    path:'userDetail/:name', // 路径参数 必须通过name跳转
    component:()=>import('../views/UserDetail')
}
// 不同方式的传递参数
<td><router-link :to="{name:'userDetail',query:{id:u.id}}">{{u.id}}</router-link></td>
<td><router-link :to="{name:'detail',params:{name:u.username}}"> {{u.username}}</router-link></td>

// 取值方式
 查询字符串 {{this.$route.query && this.$route.query.id}} <br>
 查询参数 {{this.$route.params && this.$route.params.name}} <br>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# param方式
  • 配置路由格式:/router/:id;
  • 传递的方式:在path后面跟上对应的值;
  • 传递后形成的路径:/router/123

1)路由定义

//在APP.vue中
<router-link :to="'/user/'+userId" replace>用户</router-link>    
//在index.js
{
   path: '/user/:userid',
   component: User,
},
1
2
3
4
5
6
7

2)路由跳转

// 方法1:
<router-link :to="{ name: 'users', params: { uname: samy }}">按钮</router-link
// 方法2:
this.$router.push({name:'users',params:{uname:samy}})
// 方法3:
this.$router.push('/user/' + samy)
1
2
3
4
5
6

3)参数获取 通过 $route.params 获取传递的值

# query方式
  • 配置路由格式:/router,也就是普通配置
  • 传递的方式:对象中使用query的key作为传递方式
  • 传递后形成的路径:/route?id=123

1)路由定义

//方式1:直接在router-link 标签上以对象的形式
<router-link :to="{path:'/profile',query:{name:'why',age:28,height:188}}">档案</router-link>
// 方式2:写成按钮以点击事件形式
<button @click='profileClick'>我的</button>    
profileClick(){
  this.$router.push({
    path: "/profile",
    query: {
        name: "kobi",
        age: "28",
        height: 198
    }
  });
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

2)跳转方法

// 方法1:
<router-link :to="{ name: 'users', query: { uname: james }}">按钮</router-link>
// 方法2:
this.$router.push({ name: 'users', query:{ uname:james }})
// 方法3:
<router-link :to="{ path: '/user', query: { uname:james }}">按钮</router-link>
// 方法4:
this.$router.push({ path: '/user', query:{ uname:james }})
// 方法5:
this.$router.push('/user?uname=' + jsmes)

1
2
3
4
5
6
7
8
9
10
11

3)获取参数; 通过$route.query获取传递的值

# 钩子及权限

# 权限校验

beforeEach上处理;

router.beforeEach((to,from,next)=>{
  let needLogin = to.matched && to.matched.some(({meta})=>{
   return meta && meta.needLogin
  });
  let isLogin = localStorage.getItem('login')
  if(needLogin){
    if(isLogin){
      next();
    }else{
      next({name:'login'});
    }
  }else{
    // 如果不需要登录 并且是登录页面
    if(!(to.name == 'login' && isLogin)){
      next();
    }
  }
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 组件内的钩子

beforeRouteEnter(to,from,next){
  // 在进入路由之前可以对数据校验 ajax获取
  let user = JSON.parse(localStorage.getItem('user')) || [];
  if(user.length){
    next();
  }else{
    next({name:'userAdd'})
  }
},
  beforeRouteLeave (to, from, next) {
    if(this.username){
      let confirm = window.confirm('确认离开吗');
      if(confirm) return next();
    }else{
      next();
    }
  }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

路由更新变化

watch:{
  $route(){
    alert('参数变化')
  }
}

beforeRouteUpdate(){ // 如果组件被复用 唯一的方式 就是监听路由的更新方法
  console.log('路由更新了')
}
1
2
3
4
5
6
7
8
9

# 完整的导航解析流程

runQueue

  • ①导航被触发。
  • ②在失活的组件里调用 beforeRouteLeave 守卫。
  • ③调用全局的 beforeEach 守卫。
  • ④在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
  • ⑤在路由配置里调用 beforeEnter
  • ⑥解析异步路由组件。
  • ⑦在被激活的组件里调用 beforeRouteEnter
  • ⑧调用全局的 beforeResolve 守卫 (2.5+)。
  • ⑨导航被确认。
  • ⑩调用全局的 afterEach 钩子。
  • ⑪触发 DOM 更新。
  • ⑫调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。

# 导航激活样式

.router-link-active{
  color:red!important
}
.nav-stacked .router-link-active{
  color:blue !important
}
1
2
3
4
5
6

# 路由动画

<div class="container">
      <transition-group name="fade" >
        <router-view key="1"></router-view>
        <router-view key="2" name="name"></router-view>
        <router-view key="3" name="version"></router-view>
      </transition-group>
</div>

.fade-enter{ opacity:0}
.fade-enter-active{ transition:all 0.2s linear;}
.fade-enter-to{opacity:1}
.fade-leave{opacity:1}
.fade-leave-active{transition:all .2s linear}
.fade-leave-to{opacity:0}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# express/koa中间件

# 洋葱模型

解题思路:

node本身并无“中间件”的概念,在express/koa中的中间件往往是一个函数,接收request(请求对象)、response(响应对象)、next(express内部传入的一个函数,用于把控制权从上一个中间件传到下一个中间件)作为参数,接收请求,处理响应,然后将控制权交给下一个中间件。

image-20211211162406854

function middlewareA(req, res, next) {
  next();
}
1
2
3

比如一个自定义打印日志中间件:

const app = express();
const myLogger = function (req, res, next) {
  console.log('LOGGED');
  next(); // 将控制权传递给下一个中间件
};
app.use(myLogger); // 注册中间件
1
2
3
4
5
6

koa的执行顺序是这样的:

const middleware = async function (ctx, next) {
  console.log(1)
  await next()
  console.log(6)
}
const middleware2 = async function (ctx, next) {
  console.log(2)
  await next()
  console.log(5)
}
const middleware3 = async function (ctx, next) {
  console.log(3)
  await next()
  console.log(4)
} 
const all = compose([middleware, middleware2, middleware3]);
app.use(all);
//会依次打印1,2,3,4,5,6
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

Koa-compose的实现:

Koa 中间件的实现原理,也就是洋葱模型的实现原理,核心在于next的实现。next需要依次调用下一个middleware,当到最后一个的时候结束,这样后面middleware的promise先resolve,然后直到第一个,这样的流程也就是洋葱模型的流程了。

Ps: 一个是递归最好做成尾递归的形式,而是用异步递归而不是同步递归,第三就是形式上用函数复合的形式,这样复合之后的中间件还可以继续复合。

function compose(middleware) {
  return function (context, next) {
    let index = -1
    return dispatch(0)

    function dispatch(i) {
      index = i
      let fn = middleware[i]
      if (i === middleware.length) fn = next
      if (!fn) return Promise.resolve()
      try {
        return Promise.resolve(fn(context, dispatch.bind(null, i + 1)))
      } catch (err) {
        return Promise.reject(err)
      }
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 组合中间件
// 和express中的next函数意义一样
function compose(middlewareList){
	// return function意思是返回一个函数
	return function(ctx,next){
		let index=-1;
		// 各种中间件调用的逻辑
		function dispatch(i){
			if(i<=index) return Promise.reject(new Error('next() called multiple times'))
			index=i
			const fn=middlewareList[i] || next
			if(fn){
				try{
					// koa中都是async,其返回的是一个promise(对象)
					return Promise.resolve(fn(ctx,function next(){
						return dispatch(i+1)
					}))
				}catch(err){
					return Promise.reject(err)
				}
			}else{
				return Promise.resolve()
			}
		}
		return dispatch(0)
	}
}
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

其功能可以表示为这样(非源码):

async function middleware1() {
  //...
  await (async function middleware2() {
    //...
    await (async function middleware3() {
      //...
    });
    //...
  });
  //...
}
1
2
3
4
5
6
7
8
9
10
11

# vue-router与react-router的区别

详见文章【前端路由的原理及自定义】中对比;

上次更新: 2022/04/15, 05:41:28
×