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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
2
3
4
5
6
7
8
9
10
11
12
13
14
# express/koa中间件
# 洋葱模型
解题思路:
node本身并无“中间件”的概念,在express/koa中的中间件往往是一个函数,接收request
(请求对象)、response
(响应对象)、next
(express内部传入的一个函数,用于把控制权从上一个中间件传到下一个中间件)作为参数,接收请求,处理响应,然后将控制权交给下一个中间件。
function middlewareA(req, res, next) {
next();
}
1
2
3
2
3
比如一个自定义打印日志中间件:
const app = express();
const myLogger = function (req, res, next) {
console.log('LOGGED');
next(); // 将控制权传递给下一个中间件
};
app.use(myLogger); // 注册中间件
1
2
3
4
5
6
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
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
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
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
2
3
4
5
6
7
8
9
10
11
# vue-router与react-router的区别
详见文章【前端路由的原理及自定义】中对比;
上次更新: 2022/04/15, 05:41:28