vue-router基础回顾
使用步骤
- 引入vue-router,注册路由插件
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
- 定义路由规则,创建router对象
// 路由规则
const routes = [
{
path: '/',
name: 'Index',
component: Index
},
{
path: '/blog',
name: 'Blog',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "blog" */ '../views/Blog.vue')
},
{
path: '/photo',
name: 'Photo',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "photo" */ '../views/Photo.vue')
}
]
// 创建 router 对象
const router = new VueRouter({
routes
})
export default router
- 注册 router 对象
import Vue from 'vue'
import App from './App.vue'
import router from './router'
Vue.config.productionTip = false
new Vue({
// 3. 注册 router 对象
router,
render: h => h(App)
}).$mount('#app')
- 创建路由组件的占位
<template>
<div id="app">
<div>
<img src="@/assets/logo.png" alt="">
</div>
<div id="nav">
<router-link to="/">Index</router-link> |
<router-link to="/blog">Blog</router-link> |
<router-link to="/photo">Photo</router-link>
</div>
<!-- 创建路由组建的占位 -->
<router-view/>
</div>
</template>
- 创建链接
<template>
<div id="app">
<div>
<img src="@/assets/logo.png" alt="">
</div>
<div id="nav">
<!-- 创建链接 -->
<router-link to="/">Index</router-link> |
<router-link to="/blog">Blog</router-link> |
<router-link to="/photo">Photo</router-link>
</div>
<router-view/>
</div>
</template>
动态路由
// router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Index from '../views/Index.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'Index',
component: Index
},
{
// 动态路由
// :id就是一个占位符,匹配变化的位置
path: '/detail/:id',
name: 'Detail',
// 开启 props,会把 URL 中的参数传递给组件
// 在组件中通过 props 来接收 URL 参数
props: true,
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "detail" */ '../views/Detail.vue')
}
]
const router = new VueRouter({
routes
})
export default router
// Detail.vue
<template>
<div>
<!-- 方式1: 通过当前路由规则,获取数据 -->
<!-- 这种方法不太好,因为当前组件这么写会强依赖于路由,也就是使用这个组件的时候必须有路由给传递对应的参数 -->
通过当前路由规则获取:{{ $route.params.id }}
<br>
<!-- 方式2:路由规则中开启 props 传参 【推荐】 -->
通过开启 props 获取:{{ id }}
</div>
</template>
<script>
export default {
name: 'Detail',
props: ['id']
}
</script>
<style>
</style>
// App.vue
<template>
<div id="app">
<div id="nav">
<router-link to="/">Index</router-link> |
<router-link to="/detail/11">Detail</router-link>
</div>
<router-view/>
</div>
</template>
嵌套路由
// router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
// 加载组件
import Layout from '@/components/Layout.vue'
import Index from '@/views/Index.vue'
import Login from '@/views/Login.vue'
Vue.use(VueRouter)
const routes = [
{
name: 'login',
path: '/login',
component: Login
},
// 嵌套路由
{
path: '/',
component: Layout,
children: [
{
name: 'index',
path: '',
component: Index
},
{
name: 'detail',
path: 'detail/:id',
props: true,
component: () => import('@/views/Detail.vue')
}
]
}
]
const router = new VueRouter({
routes
})
export default router
编程式导航
// 跳转到的某个路由
// 参数可以接收:字符串 或 对象
this.$router.push('/')
this.$router.push({ name: 'Detail', params: { id: 1 } })
// 跳转到的某个路由,不会记录当前历史,直接改变当前地址
// 参数可以接收:字符串 或 对象
this.$router.replace('/login')
this.$router.replace({ name: 'Detail', params: { id: 1 } })
// 跳转到某个路由
// 参数:正值--前进 负值--后退
this.$router.go(-2)
Hash和History模式区别
Hash模式:
- URL中#后面的内容最为路径地址
- 监听hashchange事件
- 根据当前路由地址找到对应组件重新渲染 History模式:
- 通过history.pushState()方法改变地址栏
- 监听popstate事件
- 根据当前路由地址找到对应组件重新渲染
- 表现形式 Hash带着#:music.163.com/#/playlist?…
History:music.163.com/playlist/31…
- 原理 Hash模式基于锚点以及onhashchange事件
History模式是基于HTML5中的History API
-
- history.pushState() 不会触发页面刷新,只是导致history对象发生变化,地址栏会有反应,并把地址记录到历史记录中,不会向服务器发送请求,可以实现无刷新的更新。 【IE10以后才支持】
-
- history.replaceState() 将当前的历史记录给替换掉,并把地址记录到历史记录中,不会向服务器发送请求
Vue Router实现原理
let _Vue = null;
export default class VueRouter {
static install(Vue) {
// 1、判断当前插件是否已经被安装,如果已经被安装就不用重复安装了
if (VueRouter.install.installed) {
return;
}
VueRouter.install.installed = true;
// 2、把Vue构造函数定义到全局变量中
_Vue = Vue;
// 3、把创建Vue实例时传递的router对象传递到所有Vue实例上
// 混入
_Vue.mixin({
beforeCreate() {
if (this.$options.router) {
_Vue.prototype.$router = this.$options.router;
}
},
});
}
// 构造函数
constructor(options) {
// 记录构造函数中传入的options
this.options = options;
// routeMap存储路由规则
this.routeMap = {};
// 响应式对象
this.data = _Vue.observable({
current: "/", // 记录当前路由地址
});
this.init();
}
init() {
this.createRouteMap();
this.initComponent(_Vue);
this.initEvent();
}
createRouteMap() {
//遍历所有的路由规则,把路由规则解析成键值对的形式,存储到routeMap中【键是路由地址,值是路由组件】
this.options.routes.forEach((route) => {
this.routeMap[route.path] = route.component;
});
}
initComponent(Vue) {
const _this = this;
Vue.component("router-link", {
// 接收传入的参数to
props: {
to: String,
},
render(h) {
return h(
"a",
{
attrs: {
href: _this.mode === "hash" ? "#" + this.to : this.to,
},
on: {
click: this.clickhander,
},
},
[this.$slots.default]
);
},
methods: {
clickhander(e) {
// hash 模式下,a 标签改变地址栏,但不会触发页面加载请求
// 只有 history 模式,才需阻止 a 标签的默认行为,并通过 history.pushState 修改地址栏,然后触发 popstate
if (self.mode !== "hash") {
e.preventDefault(); // 阻止 a 标签的默认行为
history.pushState({}, "", this.to);
}
// 修改当前组件的路径标识
this.$router.data.current = this.to;
},
},
// 运行时版:不支持template模板,需要打包的时候提前编译
// 完整版:包含运行时和编译器,体积比运行时版大10k左右,程序运行的时候把模板转换成render函数
// 可以通过在vue.config.js中设置runtimeCompiler:true; 将Vue的构建版本设置为完整版
// template:"<a :href='to'><slot></slot></a>"
});
Vue.component("router-view", {
render(h) {
const cm = _this.routeMap[_this.data.current];
return h(cm);
},
});
}
initEvent() {
// 为 hash、history 模式分别设置监听地址栏变化事件,从而加重目标组件
if (this.mode !== "hash") {
window.addEventListener("popstate", () => {
this.data.current = window.location.pathname;
});
} else {
window.addEventListener("load", () => {
this.data.current = window.location.hash.slice(1);
if (!this.data.current) {
this.data.current = "/";
window.location.hash = "#/";
}
});
window.addEventListener("hashchange", () => {
this.data.current = window.location.hash.slice(1);
});
}
}
}