vue-router是vue我的项目的重要组成部分,用于构建单页利用。单页利用是基于路由和组件的,路由用于设定拜访门路,并将门路和组件映射起来。路由的实质就是建设url和页面之间的映射关系。
hash模式
hash模式是vue-router的默认模式。hash指的是url描点,当描点发生变化的时候,浏览器只会批改拜访历史记录,不会拜访服务器从新获取页面。因而能够监听描点值的变动,依据描点值渲染指定dom。
实现原理
- 扭转描点
能够通过location.hash = "/hashpath"
的形式批改浏览器的hash值。
- 监听描点变动
能够通过监听hashchange事件监听hash值的变动。
window.addEventListener('hashchange', () => { const hash = window.location.hash.substr(1) // 依据hash值渲染不同的dom})
history模式
hash模式下,url可能为以下模式:
http://localhost:8080/index.html#/book?bookid=1
下面的url中既有#又有?,会让url看上去很奇怪,因而,能够应用history模式,在此模式下,url会如上面所示:
http://localhost:8080/book/1
实现原理
- 扭转url
H5的history对象提供了pushState和replaceState两个办法,当调用这两个办法的时候,url会发生变化,浏览器拜访历史也会发生变化,然而浏览器不会向后盾发送申请。
// 第一个参数:data对象,在监听变动的事件中可能获取到// 第二个参数:title题目// 第三个参数:跳转地址history.pushState({}, "", '/a')
- 监听url变动
能够通过监听popstate事件监听history变动,也就是点击浏览器的后退或者后退性能时触发。
window.addEventListener("popstate", () => { const path = window.location.pathname // 依据path不同可渲染不同的dom})
服务端反对
当应用hash模式的时候,如果手动刷新浏览器,页面也可能失常显示。然而在history模式下,刷新浏览器就会呈现问题。
如拜访http://localhost:8080/book/1
时,服务端会查找是否有相应的html可能匹配此门路,在单页利用下,服务端只有一个index.html,所以此时匹配不到,会提醒404。针对这个问题,须要服务端进行history模式反对。
node服务
在nodejs服务中,能够引入connect-history-api-fallback
插件:
const path = require('path')// 导入解决 history 模式的模块const history = require('connect-history-api-fallback')// 导入 expressconst express = require('express')const app = express()// 注册解决 history 模式的中间件app.use(history())// 解决动态资源的中间件,网站根目录 ../webapp.use(express.static(path.join(__dirname, '../web')))// 开启服务器,端口是 3000app.listen(3000, () => { console.log('服务器开启,端口:3000')})
nginx服务
在nginx服务中,能够如下形式批改配置文件,增加history模式反对:
location / { root html; index index.html index.htm; #新增加内容 #尝试读取$uri(以后申请的门路),如果读取不到读取$uri/这个文 件夹下的首页 #如果都获取不到返回根目录中的 index.html try_files $uri $uri/ /index.html;}
实现自定义VueRouter
VueRouter外围是,通过Vue.use注册插件,在插件的install办法中获取用户配置的router对象。当浏览器地址发生变化的时候,依据router对象匹配相应路由,获取组件,并将组件渲染到视图上。
次要有三个重要点:
- 如何在install办法中获取vue实例上的router属性。
能够利用Vue.mixin混入申明周期函数beforeCreate,在beforeCreate函数中能够获取到Vue实例上的属性并赋值到Vue原型链上。
_Vue.mixin({ beforeCreate () { if (this.$options.router) { _Vue.prototype.$router = this.$options.router } }})
- 如何触发更新
hash模式下:
- 通过location.hash批改hash值,触发更新。
- 通过监听hashchange事件监听浏览器后退或者后退,触发更新。
history模式下:
- 通过history.pushState批改浏览器地址,触发更新。
- 通过监听popstate事件监听浏览器后退或者后退,触发更新。
- 如何渲染router-view组件
- 通过Vue.observable在router实例上创立一个保留以后路由的监控对象current。
- 当浏览器地址变动的时候,批改监控对象current。
- 在router-view组件中监听监控对象current的变动,当current变动后,获取用户注册的相应component,并利用h函数将component渲染成vnodes,进而更新页面视图。
完整版
// 存储全局应用的Vue对象let _Vue = nullclass VueRouter { // vue.use要求plugin具备一个install办法 static install (Vue) { // 判断插件是否曾经装置过 if (VueRouter.install.installed) { return } VueRouter.install.installed = true _Vue = Vue // 将main文件中实例化Vue对象时传入的router对象增加到Vue的原型链上。 _Vue.mixin({ beforeCreate () { if (this.$options.router) { _Vue.prototype.$router = this.$options.router } } }) } constructor (options) { this.options = options // 用于疾速查找route this.routeMap = {} this.data = _Vue.observable({ current: window.location.hash.substr(1) }) this.init() } init () { this.createRouteMap() this.initComponents(_Vue) this.initEvent() } createRouteMap () { // 遍历所有的路由规定 吧路由规定解析成键值对的模式存储到routeMap中 this.options.routes.forEach(route => { this.routeMap[route.path] = route.component }) } initComponents (Vue) { // 注册router-link组件 Vue.component('router-link', { props: { to: String }, methods: { clickHandler (e) { // 批改hash location.hash = this.to // 批改current,触发视图更新 this.$router.data.current = this.to e.preventDefault() } }, render (h) { return h('a', { attrs: { href: this.to }, on: { click: this.clickHandler } }, [this.$slots.default]) } }) const that = this // 注册router-view插件 Vue.component('router-view', { render (h) { const component = that.routeMap[that.data.current] return h(component) } }) } initEvent () { // 在hash产生更改的时候,批改current属性,触发组件更新 window.addEventListener('hashchange', () => { this.data.current = window.location.hash.substr(1) }) }}export default VueRouter