共计 3617 个字符,预计需要花费 10 分钟才能阅读完成。
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')
// 导入 express
const express = require('express')
const app = express()
// 注册解决 history 模式的中间件
app.use(history())
// 解决动态资源的中间件,网站根目录 ../web
app.use(express.static(path.join(__dirname, '../web')))
// 开启服务器,端口是 3000
app.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 = null
class 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