用 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 对象上的任意属性,在这个场景中,就是 router.push({ name: ''user' ', params: { id: 123 }, })
路由跳转方式name 、 path 和传参方式params 、query的区别
通过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 部署
# 工作进程的数量
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`