vue-router的使用

229 阅读5分钟

用 Vue + Vue Router 创建单页应用非常简单:通过 Vue.js,我们已经用组件组成了我们的应用。当加入 Vue Router 时,我们需要做的就是将我们的组件映射到路由上,让 Vue Router 知道在哪里渲染它们

view-router

router-view 将显示与 url 对应的组件。你可以把它放在任何地方,以适应你的布局

<template>
  <div class="container">
    <global-header :user="user"></global-header>
    <router-view></router-view>
  </div>
  
</template>

<script lang="ts">    
import { defineComponent } from 'vue';
import 'bootstrap/dist/css/bootstrap.min.css';
import GlobalHeader, { IUserProps } from './components/GlobalHeader.vue';

const userData: IUserProps = {
  isLogin: false,
  name: '马红',
  id: '1',
}

export default defineComponent({
  name: 'App',
  components: {
    GlobalHeader,
  },
  setup(){
    return {
      user: userData,
    }
  }
});
</script>

<style>
</style>

router 配置文件

import {createRouter, createWebHistory } from 'vue-router';
import store from './store';
import http from '@/utils/http'
import Home from './views/Home.vue';
import Login from './views/Login.vue';
import NotFound from './views/404.vue'
import Create from './views/Create.vue'

const routerHistory = createWebHistory();

const routes = [
  {
    path: '/',
    name: 'home', 
    component: Home
  },
  {
    path: '/login',
    name: 'login',
    component: Login,
    meta: {redirectToHome: true}
  },
  {
    path: '/404',
    name: 'NotFound',
    component: NotFound
  },
  {
    path: '/create',
    name: 'create',
    component: Create,
    meta: { requireLogin: true }
  }
];

const router = createRouter({
  history: routerHistory,
  routes: routes
})
// 一般的后端接口设置的token是有时效的,超时后就会失效
router.beforeEach((to, from, next) => {
  console.log('router to,,,', to);
  console.log('router from,,,', from)
  const {token, user} = store.state;
  const {redirectToHome, requireLogin} = to.meta
  if(!user.isLogin){
    if(token) {
      http.defaults.headers.common.Authorization = `Bearer ${token}`;
      store.dispatch('fetchCurrentUser')
      .then(() => {
        next('/')
      }).catch(()=>{
        next('login')
      })
      
    } else {
      if(requireLogin){
        next('login');
      } else {
        next();
      }
    }
  } else {
    if (redirectToHome) {
      next('/');
    } else {
      next();
    }
  }

})
export default router

路径规则

path: '/register/:id'

/register/123         ?   plan=private#777

path        params        query            hash

locahost:8080 与locahost:8080/ 意思相同 根目录 / 写不写都行

route 路由信息对象,只读对象

route.params.username

router 是路由操作对象,只写对象

router.push('/dashboard')

在整个文档中,我们会经常使用 router 实例,
请记住,this.$router 与直接使用通过 createRouter 创建的 router 实例完全相同。
我们使用 this.$router 的原因是,我们不想在每个需要操作路由的组件中都导入路由。

动态路由

const routes = [
  // 动态字段以冒号开始
  { path: '/users/:id', component: User,name: 'user' },
]
route.params.id

使用带有参数的路由时需要注意的是,当用户从 /users/johnny 导航到 /users/jolyne 时,相同的组件实例将被重复使用。因为两个路由都渲染同个组件,比起销毁再创建,复用则显得更加高效。不过,这也意味着组件的生命周期钩子不会被调用。

要对同一个组件中参数的变化做出响应的话,你可以简单地 watch route 对象上的任意属性,在这个场景中,就是 route.paramsthis.route.params : this.router.push({ name: ''user' ', params: { id: 123 }, })

路由跳转方式name 、 path 和传参方式params 、query的区别

router.vuejs.org/zh/guide/es…

通过params传参

而params相当于post请求,参数不会再地址栏中显示, 这个parmas和path中的params传递、展示不一样,但是获取是一样的

     this.$router.push({
        name: "newPage",     //params对应路由跳转为name方式
        params: {
          id: this.id
        }
    });
    
   另一个页面接收:
   url xxxxx/newPage
   console.log(this.$route.params.id)
通过query传参

直白的来说query相当于get请求,页面跳转的时候,可以在地址栏看到请求参数

 this.$router.push({
        path: "/newPage",      //query对应路由跳转为path方式
        query: {
          id: res
        }
      });
      
  另一个页面接收:
  url  xxxxxxx/newPage?id=res
  console.log(this.$route.query.id)
 this.$router.push({
        path: `/development/${id}/approval_detail?tabId=${approvalName}`
      })
      
 另一个页面接收:
 url  xxxxxxx/development/111/approval_detail?tabId=名字
 this.$route.params.id this.$route.query.tabId

注意:params传参如果路由上面不写参数,也是可以传过去的,但不会在url上面显示出你的参数,并且当你跳到别的页面或者刷新页面的时候参数会丢失

path 和 Name路由跳转方式,都可以用query传参 params传参,push里面只能是 name:'xxx',不能是path:'/xxx',因为params只能用name来引入路由,如果这里写成了path,接收参数页面会是undefined!!!

vue router query中params改变不刷新页面

嵌套路由

在组件中再添加

// 组件
<div id="app">
  <router-view></router-view>
</div>
// 路由配置
const 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,
      },
    ],
  },
]

编程式导航

router.push('/resource/template/create') 
http://localhost:8080/#/resource/template/create
和
router.push('resource/template/create')
http://localhost:8080/#/resource/resource/template/create
不一样,区别 路由开始的 /

// 字符串路径
router.push('/users/eduardo')

// 带有路径的对象
router.push({ path: '/users/eduardo' })

// 命名的路由,并加上参数,让路由建立 url
router.push({ name: 'user', params: { username: 'eduardo' } })

// 带查询参数,结果是 /register?plan=private
router.push({ path: '/register', query: { plan: 'private' } })

// 带 hash,结果是 /about#team
router.push({ path: '/about', hash: '#team' })

如果提供了 path,params 会被忽略,上述例子中的 query 并不属于这种情况。取而代之的是下面例子的做法,你需要提供路由的 name 或手写完整的带有参数的 path
const username = 'eduardo'
// 我们可以手动建立 url,但我们必须自己处理编码
router.push(`/user/${username}`) // -> /user/eduardo
// 同样
router.push({ path: `/user/${username}` }) // -> /user/eduardo
// 如果可能的话,使用 `name` 和 `params` 从自动 URL 编码中获益
router.push({ name: 'user', params: { username } }) // -> /user/eduardo
// `params` 不能与 `path` 一起使用
router.push({ path: '/user', params: { username } }) // -> /user

命名路由 name

const routes = [
  {
    path: '/user/:username',
    name: 'user',
    component: User
  }
]

命名视图

有时候想同时 (同级) 展示多个视图,而不是嵌套展示,例如创建一个布局,有 sidebar (侧导航) 和 main (主内容) 两个视图,这个时候命名视图就派上用场了。你可以在界面中拥有多个单独命名的视图,而不是只有一个单独的出口。如果 router-view 没有设置名字,那么默认为 default。

<router-view class="view left-sidebar" name="LeftSidebar"></router-view>
<router-view class="view main-content"></router-view>
<router-view class="view right-sidebar" name="RightSidebar"></router-view>

不同的历史记录模式

推荐使用WebHistory

import { createRouter, createWebHistory } from 'vue-router'

const router = createRouter({
  history: createWebHistory(),
  routes: [
    //...
  ],
})

当使用这种历史模式时,URL 会看起来很 "正常",例如 example.com/user/id。漂亮!

不过,问题来了。由于我们的应用是一个单页的客户端应用,如果没有适当的服务器配置,用户在浏览器中直接访问 example.com/user/id,就会得… 404 错误。这就丑了。

不用担心:要解决这个问题,你需要做的就是在你的服务器上添加一个简单的回退路由。如果 URL 不匹配任何静态资源,它应提供与你的应用程序中的 index.html 相同的页面。漂亮依旧!

location / {
  try_files $uri $uri/ /index.html;
}

导航守卫


const router = createRouter({ ... })

router.beforeEach((to, from) => {
  // ...
  // 返回 false 以取消导航
  return false
})

router.beforeEach((to, from, next) => {
  console.log('router to,,,', to);
  console.log('router from,,,', from)
  const {token, user} = store.state;
  const {redirectToHome, requireLogin} = to.meta
  if(!user.isLogin){
    if(token) {
      http.defaults.headers.common.Authorization = `Bearer ${token}`;
      store.dispatch('fetchCurrentUser')
      .then(() => {
        next('/')
      }).catch(()=>{
        next('login')
      })
      
    } else {
      if(requireLogin){
        next('login');
      } else {
        next();
      }
    }
  } else {
    if (redirectToHome) {
      next('/');
    } else {
      next();
    }
  }

})

路由元信息

有时,你可能希望将任意信息附加到路由上,如过渡名称、谁可以访问路由等。这些事情可以通过接收属性对象的meta属性来实现,并且它可以在路由地址和导航守卫上都被访问到。定义路由的时候你可以这样配置 meta 字段:

const routes = [
  {
    path: '/posts',
    component: PostsLayout,
    children: [
      {
        path: 'new',
        component: PostsNew,
        // 只有经过身份验证的用户才能创建帖子
        meta: { requiresAuth: true }
      },
      {
        path: ':id',
        component: PostsDetail
        // 任何人都可以阅读文章
        meta: { requiresAuth: false }
      }
    ]
  }
]

路由懒加载

nginx 部署

2ef137e67753235b4b0f1e64092ac28.jpg

# 工作进程的数量
worker_processes  1;
events {
    worker_connections  1024; # 每个工作进程连接数
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    # 日志格式
    log_format  access  '$remote_addr - $remote_user [$time_local] $host "$request" '
                  '$status $body_bytes_sent "$http_referer" '
                  '"$http_user_agent" "$http_x_forwarded_for" "$clientip"';
    access_log  /srv/log/nginx/access.log  access; # 日志输出目录
    gzip  on;
    sendfile  on;

    # 链接超时时间,自动断开
    keepalive_timeout  60;

    # 虚拟主机
    server {
        listen       8080;
        server_name  localhost; # 浏览器访问域名

        charset utf-8;
        access_log  logs/localhost.access.log  access;

        # 路由
        location / {
            root   www; # 访问根目录
            index  index.html index.htm; # 入口文件
        }
    }

    # 引入其他的配置文件
    include servers/*;
}

菜单和路由

菜单和路由是分开的

通过点击菜单 调用router.push('') 实现路由跳转

所有的路由关系写在前端router.js文件

可以根据router.js文件中的路由生成menus(注意menus中的path要是完整的path,是路由中父子path拼接而成),也可以根据后端给的数据生成menus(注意要和路由中的path对应)

Vue几种监听路由变化的方式

1.通过watch

// 1.1.返回 “从哪里来” 和 “哪里去” 的路由信息
watch:{
	$route(to,from){
	  console.log(from.path) // 从哪来
	  console.log(to.path) // 到哪去
	}
}
// 1.2. 返回 “新” “老” 路由信息
watch: {
  $route: {
    handler: function(val, oldVal){
      console.log(val);
    },
    // 深度观察监听
    deep: true
  }
},

2.通过钩子函数 beforeRouteEnter, beforeRouteUpdate, beforeRouteLeave

 // 监听,当路由发生变化的时候执行
    beforeRouteEnter (to, from, next) {
     // 需要注意这里不能使用this,
     // 因为我们使用的是进入路由之前,那会组件还没创建,
          next(()=>{
               // 所有我们只能使用过vm异步语句来让节点上树;
          })
    },
    beforeRouteUpdate (to, from, next) {
      // 在当前路由改变,但是该组件被复用时调用
      // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
      // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
      // 可以访问组件实例 `this`
    },
    beforeRouteLeave (to, from, next) {
      // 导航离开该组件的对应路由时调用
      // 可以访问组件实例 `this`