代碼 歡迎followhtml
先列一段最簡單的router代碼vue
import VueRouter from 'vue-router'
Vue.use(VueRouter)
export default new VueRouter({
routes: [
{
path: '/',
name: 'home',
component: Home,
beforeEnter(from,to,next){
console.log(`beforEnterHome from ${from} to ${to}`)
setTimeout(()=>{
next()
},1000)
// next()
}
},
{
path: '/about',
name: 'about',
component: () => import(/* webpackChunkName: "about" */ './views/About.vue')
}
]
})
複製代碼
初始化的時候,直接把這個返回的對象傳遞到new Vue
中webpack
new Vue({
router,
render: h => h(App)
}).$mount('#app')
複製代碼
模板層自帶了router-link和router-view兩個組件git
<div id="nav">
<button @click="about">about</button>
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
</div>
<router-view/>
複製代碼
咱們新建vue-router文件夾 新建index.js, 你們都已是黃金水平了,就不介紹太多基礎知識了,基本上迷你的router,這幾塊就能夠構成github
export default class Router{
constructor(){
}
init(){
// 初始化
}
bindEvents(){
// 綁定事件
}
createRouteMap(){
// 初始化路由表
}
initComponent(){
// 註冊router-link和router-view路由
}
}
複製代碼
咱們使用Vue.use(VueRouter)來註冊和啓動路由, 這個我們整vuex源碼的時候整過了,帶上一個install方法就能夠,先註冊一個簡單的調試信息看下web
let Vue
class Router {
static install(_Vue) {
Vue = _Vue
Vue.mixin({
beforeCreate() {
if(this.$options.router){
// new Vue的時候傳遞的
Vue.prototype.$routerMsg = '路由安裝完畢'
}
}
})
}
}
複製代碼
app.vue使用$routerMsg能夠直接顯示出信息,bingovue-router
咱們在install的時候,執行init 啓動整個路由, 監聽onload和hashchange事件,觸發後,根據當前的hash,找到須要渲染的組件,而後去渲染router-view的內容就歐克了vuex
::: tip hash變化後,通知到router-view渲染,須要借用Vue自己的響應式能力 :::typescript
static install(_Vue) {
Vue = _Vue
Vue.mixin({
beforeCreate() {
if(this.$options.router){
// new Vue的時候傳遞的
Vue.prototype.$routerMsg = '路由安裝完畢'
Vue.prototype.$router = this.$options.router
this.$options.router.init()
}
}
})
}
init() {
this.bindEvents()
this.createRouteMap(this.$options)
this.initComponent(Vue)
}
bindEvents(){
window.addEventListener('load', this.onHashChange.bind(this), false)
window.addEventListener('hashchange', this.onHashChange.bind(this), false)
}
複製代碼
上面說的有一步,就是根據當前hash路由,找到須要渲染的組件,我們傳遞進來的事數組,查找起來費勁,轉成對象,方便查找和定位組件,咱們稱之爲路由映射表redux
constructor(options) {
this.$options = options
this.routeMap = {}
}
createRouteMap(options) {
options.routes.forEach(item => {
this.routeMap[item.path] = item
})
}
複製代碼
在組件的render函數裏,根據this.app.current裏面存儲的路由,查找到組件 而後渲染便可,
::: tip 這裏的h,就是React裏面的createElement一個概念,之後給你們寫虛擬dom源碼的時候,你們會有更深入的理解 :::
Vue.component('router-view', {
render:h=>{
var component = this.routeMap[this.app.current].component
return h(component)
}
})
複製代碼
router-link整成a標籤就能夠,記得帶上插槽 ::: tip 這裏面的寫法 就是傳說中的JSX :::
Vue.component('router-link', {
props: {
to: String
},
render(h){
return <a href={this.to}>{this.$slots.default}</a>
}
})
複製代碼
具體處理hash變化的邏輯,其實很easy,由於咱們利用Vue的響應式原理來存儲當前路由,咱們獲取當前的hash,而後直接利用Vue響應式機制通知router-view便可
constructor(options) {
this.$options = options
this.routeMap = {}
this.app = new Vue({
data: {
current: '/'
}
})
}
onHashChange(e) {
let hash = this.getHash()
let router = this.routeMap[hash]
this.app.current = this.getHash()
}
複製代碼
註冊路由的時候,能夠帶上生命週期 而且生命週期的參數next,執行後才跳轉,能夠作路由守衛的工做,好比我們小小的模擬一下,2秒後再跳轉
{
path: '/',
name: 'home',
component: Home,
beforeEnter(from,to,next){
console.log(`beforEnterHome from ${from} to ${to}`)
setTimeout(()=>{
next()
},1000)
// next()
}
},
複製代碼
實際的Router中,路由守衛的邏輯很複雜,是一個異步隊列的依次執行,有點像koa或者redux的中間件執行邏輯,我們只考慮一個 簡化一下邏輯
if(router.beforeEnter){
router.beforeEnter(from, to, ()=>{
this.app.current = this.getHash()
})
}else{
this.app.current = this.getHash()
}
複製代碼
實際的vue-router代碼,要複雜不少,不少容錯的處理,有幾個擴展點你們能夠重點關注
let Vue
class Router {
static install(_Vue) {
Vue = _Vue
Vue.mixin({
beforeCreate() {
if(this.$options.router){
// new Vue的時候傳遞的
Vue.prototype.$routerMsg = '路由安裝完畢'
Vue.prototype.$router = this.$options.router
this.$options.router.init()
}
}
})
}
constructor(options) {
this.$options = options
this.routeMap = {}
this.app = new Vue({
data: {
current: '/'
}
})
}
// 綁定事件
init() {
this.bindEvents()
this.createRouteMap(this.$options)
this.initComponent(Vue)
}
bindEvents(){
window.addEventListener('load', this.onHashChange.bind(this), false)
window.addEventListener('hashchange', this.onHashChange.bind(this), false)
}
// 路由映射表
createRouteMap(options) {
options.routes.forEach(item => {
this.routeMap[item.path] = item
})
}
// 註冊組件
initComponent(Vue) {
Vue.component('router-link', {
props: {
to: String
},
render(h){
return <a href={this.to}>{this.$slots.default}</a>
}
})
Vue.component('router-view', {
render:h=>{
var component = this.routeMap[this.app.current].component
return h(component)
}
})
}
getHash() {
return window.location.hash.slice(1) || '/'
}
push(url){
window.location.hash = url
}
getFrom(e){
let from, to
if(e.newURL){
from = e.oldURL.split('#')[1]
to = e.newURL.split('#')[1]
}else{
from = ''
to = location.hash
}
return {from,to}
}
// 設置當前路徑
onHashChange(e) {
let {from, to} = this.getFrom(e)
let hash = this.getHash()
let router = this.routeMap[hash]
if(router.beforeEnter){
router.beforeEnter(from, to, ()=>{
this.app.current = this.getHash()
})
}else{
this.app.current = this.getHash()
}
}
}
export default Router
複製代碼
各位小老弟 下次再見,瞭解完這些原理,須要的是實戰,如今Vue的最佳實踐我列個任務,慢慢寫
歡迎你們提需求