Vue进阶之Vue-Router
Vue-Router的使用
1、动态路由:
{
path: '/home/:id', //配置id参数便可实现动态路由
name: 'home',
// 开启 props,会把 URL 中的参数传递给组件
// 在组件中通过 props 来接收 URL 参数
props: true,
component: () => import('../views/Home.vue')
}
组件接受id参数
<template>
<div>
<!-- 方式1: 通过当前路由规则,获取数据 -->
通过当前路由规则获取:{{ $route.params.id }}
<br>
<!-- 方式2:路由规则中开启 props 传参 -->
通过开启 props 获取:{{ id }}
</div>
</template>
<script>
export default {
name: 'Detail',
props: ['id']
}
</script>
推荐使用第二种方式props方式获取参数,不强依赖router
2、Hash与History模式
Hash模式
- Hash模式,是基于锚点,以及onhashchange事件,将锚点的值作为路由地址,当地址发生变化时触发onhashchange事件,然后根据路径决定页面内容;
- History模式, 是基于HTML5中的History API实现的,
- history.pushState() IE10以后才支持,有兼容问题
- history.replaceState()
- 其特点就是,改变地址栏中的url但是不会向服务端发送请求,并将其添加到历史记录中。利用其便可实现前端路由;
History模式
使用
- History模式需要服务端支持;
- 单页面应用中,服务端不存在http://www.abc.com/login 这样的地址,访问会返回找不到该页面404;
- 在服务端应该除了静态资源外都返回单页面应用的index.html
在node服务中的处理
const path = require('path');
// 导入处理 history 模式的模块
const history = require('connect-history-api-fallback');
const express = require('express');
// 创建web服务器
const app = express();
// 注册处理 history 模式的中间件, 作用是当请求到服务器没有的地址时,都返回指定的index.html文件,再交给前端去判断路由
app.use(history());
// 加载制定地址的静态资源的中间件
app.use(express.static(path.join(__dirname, '../web')));
app.listen(8081, () => {
console.log('服务器开启,8081');
})
在nginx服务器中
Mac中nginx信息存放
配置文件路径:/usr/local/etc/nginx/nginx.conf
安装路径:/usr/local/Cellar/nginx/1.10.7
服务器默认路径(根目录):/usr/local/var/www
在location中配置,try_files属性
location / {
root html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
# vuerouter配置history模式当时请求地址不存在时,返回更目录的index.html文件
}
Vue-Router的实现
原理
hash模式
- 将URL中 # 后面的内容作为路劲地址;如果只改 # 后面的内容,浏览器不会向服务端请求这个地址,但是会将其放入浏览器的访问历史中;
- 当hash改变后,要监听 onhashchange事件;
- 当事件触发后,根据当前路由地址找到对应组件重新渲染;
history模式
- 通过调用history.pushState()方法改变地址栏,pushState只是改变地址栏,并记录历史记录中,不会向服务端发送请求;
- 监听popstate事件,找到改变后的地址(注意调用pushState或者replaceState时不会触发popstate事件,调用浏览器的back和forward,或者前进后退按钮时才会触发);
- 根据当前路由地址找到对应组件重新渲染;
分析
通过使用分析vueRouter构成,绘制简单类图
VueRouter
-------------
+ options //传入的参数
+ data //VueRouter自身的响应数据
+ routeMap //记录路由地址的组件的关系
-------------
+ Constructor(Options): VueRouter //构造函数
_ install(Vue): void //静态方法注册VueRouter
+ init(): void // 入口方法
+ initEvent(): void // 组成事件监听变化
+ createRouteMap(): void // 创建routeMap对象
+ initComponents(Vue): void // 创建组件<router-link>和<router-view>
实现时,知识点,Vue的构建版本;
- 运行时版:不支持template模板,需要打包的时候提前编译;打包时将template编译成render函数;
- 完整版:包含运行时和编译器,体积比运行时版大10k左右,程序运行的时候把模板转换成render函数;所有其性能不如运行时版;
VueRouter的简单实现
/*
类图
VueRouter
-------------
+ options //传入的参数
+ data //VueRouter自身的响应数据
+ routeMap //记录路由地址的组件的关系
-------------
+ Constructor(Options): VueRouter //构造函数
_ install(Vue): void //静态方法注册VueRouter
+ init(): void // 入口方法
+ initEvent(): void // 组成事件监听变化
+ createRouteMap(): void // 创建routeMap对象
+ initComponents(Vue): void // 创建组件<router-link>和<router-view>
*/
let _Vue = null;
export default class VueRouter {
static install(Vue) {
// 1、判断插件是否安装;
if(VueRouter.install.installed) return;
VueRouter.install.installed = true;
// 2、将vue的构造函数记得到全局变量中,将来在vuerouter的实例中还要使用这个vue的构造函数,比如在创建router-link这些组件时使用Vue.component方法;
_Vue = Vue
// 3、把创建的Vue实例时候的传入的router对象注入到所有的Vue实例上,创建$router
Vue.mixin({
beforeCreate() {
if(this.$options.router){ //有这个选项才添加,除去组件
Vue.prototype.$router = this.$options.router;
this.$options.router.init();
}
}
})
}
constructor(options) {
this.options = options;
this.routeMap = {};
// data需要是响应式的对象,通过vue的
this.data = _Vue.observable({
current: '/' //当前路径默认根目录
})
}
createRouteMap() {
// 遍历所有的路由规则 把路由规则解析成键值对的形式存储到routeMap中
this.options.routes.forEach(route => {
this.routeMap[route.path] = route.component
});
}
init() {
this.createRouteMap();
this.initComponents(_Vue);
this.initEvent()
}
initComponents(Vue) {
const _that = this;
Vue.component('router-link', {
props: {
to: String
},
// 运行时版本Vue,需直接写render函数渲染
render(h) {
return h('a', {
attrs: {
href: this.to
},
on: {
click: this.clickhandle
}
}, [this.$slots.default])
},
methods: {
clickhandle(e){
// 改变地址栏内容,第一个参数为,触发popstate事件时传入的对象
history.pushState({}, 'haha', this.to);
// _that.data.current = this.to;
this.$router.data.current = this.to; //记录跳转地址到当前router实例上的data.current属性上,存续当前地址,并且因为data是响应式的,它会自动更新我们的组件;
e.preventDefault() //阻止默认行为
}
}
// template 需要编译器用完整版Vue,在Vue.config.js中可配置
// template: '<a :href="to"><slot></slot></a>'
})
Vue.component('router-view', {
render(h) {
let component = _that.routeMap[_that.data.current];
return h(component); //直接渲染这个组件
}
})
}
initEvent() {
// 这里为了监听点击后退按钮的问题
window.addEventListener('popstate', () => {
this.data.current = window.location.pathname;
})
}
}