router回顾
- router的使用步骤:
- 创建路由相关的组件(视图)
- 注册路由插件,调用vue.use注册VueRouter
- 创建router对象,同时配置路由规则
- 注册router对象,就是在创建vue实例的时候,在选项里面配置创建好的router对象
- 通过router-view设置占位,当路径匹配成功之后会将匹配到的组件替换router-view的位置
- 通过router-link创建链接
- 补充:Vue.use的作用是注册组件,内部接收一个参数,如果参数是函数,则vue.use直接调用函数注册组件,如果传入的是对象的话。vue.use会调用对象的install方法注册插件。
- 补充:如果这里配置了router选项,那此时会给vue实例注入两个选项 r o u t e 路 由 规 则 , route路由规则, route路由规则,router路由对象,通过路由对象可以调用一些相应的方法,如push等
- 补充:vue中 r o u t e r 和 router和 router和route的区别: r o u t e : : 当 前 激 活 的 路 由 的 信 息 对 象 。 每 个 对 象 都 是 局 部 的 , 可 以 获 取 当 前 路 由 的 p a t h , n a m e , p a r a m s , q u e r y 等 属 性 。 route::当前激活的路由的信息对象。每个对象都是局部的,可以获取当前路由的 path, name, params, query 等属性。 route::当前激活的路由的信息对象。每个对象都是局部的,可以获取当前路由的path,name,params,query等属性。router::全局的 router 实例。通过 vue 根实例中注入 router 实例,然后再注入到每个子组件,从而让整个应用都有路由功能。其中包含了很多属性和对象(比如 history 对象),任何页面也都可以调用其 push(), replace(), go() 等方法。
- Hash模式和History模式
两种都是客户端路由的实现方式,也就是当路径发生变化的时候不会向服务端发送请求,使用js监视路径的变化,然后根据不同的地址渲染不同的内容,如果需要服务端内容会发送ajax请求来获取。- 两者表现形式的区别
- Hash模式:路径中带着‘#’,#后面的内容作为路由地址,可以通过‘?’携带参数,这种url形式很丑
- History模式:url是正常的url,但需要服务端配置支持。
- 原理的区别
- Hash模式:基于锚点,以及onhashchange事件。通过锚点的参数作为路由地址,当路由地址放生变化的时候出发onhashchange事件,然后根据路径决定页面呈现内容。
- History模式:是基于HTML5中的History API
- history.pushState() IE10以后才支持
- history.replaceState()
pushState和push的区别是:当调用 history.push()的时候路径会发生变化,会向服务器发送请求,而调用history.pushState()的时候,不会向服务器发送请求,只会改变浏览器地址栏中的地址,并且把地址记录到历史记录中。所以通过pushState可以实现客户端路由,pushState有兼容性问题,如果想要兼容ie9以前的浏览器只能使用hash模式。
- 两者表现形式的区别
- History模式的使用
- History需要服务器的支持
- 单页应用中,只有一个页面index.html,服务端不存在客户端路由这样的地址会返回找不到该页面 .
- 在服务器端应该除了静态资源外都返回单页应用的index.html
- 在nodejs中配置history模式
- 在Nginx中配置history模式
执行过程:刷新浏览器之后会向服务器去请求路由地址,服务器接收到请求之后回去找路径对应的文件,当配置文件加了try_files之后,如果找不到对应的文件,会默认返回网站根目录中的index.html,但浏览器接收到网页之后会再去判断路由地址,然后在客户端解析路由地址对应的组件。
- 回顾vue-router核心代码
- 导入VueRouter模块,因为VueRouter是Vue的插件。所以用vue.use注册这个插件,这里传入的是一个对象,所以需要实现install方法
- 创建路由对象。VueRouter应该是一个构造函数后或者是一个类,这个类应该有一个静态的install方法
- VueRouter的构造函数需要接受一个参数,参数是一个对象形式,对象里面传入的是理由的规则,路由规则核心记录的是路由的地址和核心的组件
- 创建一个vue的实例,在实例中传入创建好的router对象
- VueRouter的类图
三个属性:- options:记录构造函数中传入的对象
- data:是一个对象,里面有一个属性current,用来记录当前路由地址的,目的是设置一个响应式的对象,路由地址发生变化时,对应的组件要相应的更新
- routeMap:是一个对象,用来记录路由地址和组件的对应关系
方法:
+Constructor(Options) 初始化属性
_install 静态方法,用来实现vue的插件机制
+init用来调用下面的三个方法
+initEvent 用来注册pushState事件,监听浏览器的变化
+createRouteMap 初始化routemap属性,把构造函数中传入的路由规则转换成键值对的形式,赋值给routerMap
+initComponents 用来创建router-link和router-view这两个组件的
手写VueRouter
history模式
let _Vue = null;
export default class VueRouter {
// vue:vue的构造函数
static install (vue) {
// 1. 判断当前插件是否已经被安装
if (VueRouter.install.installed) return;
VueRouter.install.installed = true;
// 2.把Vue构造函数记录到全局变量,将来在vuerouter中的一些实例方法还要使用构造函数
_Vue = vue;
// 3.把创建Vue实例时候传入的router对象注入到Vue实例上
// _Vue.prototype.$router = this.$options.router
// 混入
_Vue.mixin({
beforeCreate () { // 因为beforeCreate除了vue实例会执行,组件中也会执行,所以会执行很多次
if (this.$options.router) { // 只有vue实例中的$options中才有router属性,可以排除其他组件进入
_Vue.prototype.$router = this.$options.router;
this.$options.router.init();
}
}
})
}
constructor (options) {
this.options = options;
this.routeMap = {};
this.data = _Vue.observable({ // 用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) {
// 传vue构造方法的目的是减少这个方法和外部的依赖
Vue.component('router-link', {
props: {
to: String
},
// h函数的作用是帮助创建虚拟dom
// 三个参数 第一个参数:创建元素对应的选择器(这里使用标签选择器),第二个参数:标签的属性(对象),第三个参数:生成的子元素(数组)
render (h) {
return h('a', {
attrs: {
href: this.to
},
on: {
click: this.clickHandler
}
}, [this.$slots.default])
},
methods: {
clickHandler (e) {
// pushState三个参数:1:data是将来触发popState的时候传给popState对象的参数,2:网页的标题,3.当前要跳转的地址
history.pushState({}, '', this.to)
// this指的是router-link组件也是一个vue实例
this.$router.data.current = this.to
e.preventDefault()
}
}
// template: '<a :href="to"><slot></slot></a>'
})
const self = this
Vue.component('router-view', {
render (h) {
const component = self.routeMap[self.data.current]
return h(component)
}
})
}
initEvent () {
window.addEventListener('popstate', () => {
this.data.current = window.location.pathname
})
}
}
hash模式
let _Vue = null;
export default class VueRouter {
static install (vue) {
// 1.判断当前插件是否已经被安装
if (VueRouter.install.installed) return;
VueRouter.install.installed = true;
// 2.把vue构造函数记录到全局变量,将来在vuerouter中的一个实例方法还要使用全局对象
_Vue = vue;
// 3.把创建vue实例时传入的router对象注入到vue实例中
_Vue.mixin({
beforeCreate () {
if (this.$options.router) {
_Vue.prototype.$router = this.$options.router
this.$options.router.init();
}
}
});
}
constructor (options) {
this.options = options;
this.routeMap = {};
this.data = _Vue.observable({
current: '/'
})
this.addSign(); // 增加锚点标识,以及注册锚点监听
}
addSign () {
const hash = window.location.hash;
if (!hash.startsWith('#/')) { // 如果没有锚点标识,则增加锚点标识
window.location.hash = '#/'
} else { // 如果有锚点标识,则直接替换当前为获取到的锚点标识后的内容
this.data.current = hash.substr(1);
}
// 监听页面路由变化
window.onhashchange = () => {
this.data.current = window.location.hash.substr(1)
}
}
init () {
this.createRouteMap();
this.initComponents(_Vue);
}
createRouteMap () {
this.options.routes.forEach(route => {
this.routeMap[route.path] = route.component
})
}
initComponents (vue) {
vue.component('router-link', {
props: {
to: String
},
render(h) {
return h('a', {
attrs: {
href: '#' + this.to
}
}, [this.$slots.default] )
}
})
const self = this;
vue.component('router-view', {
render (h) {
const component = self.routeMap[self.data.current];
return h(component)
}
})
}
}
以上就是vue-router的相关学习内容啦,手写出VueRouter还是很有成就感的~