-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
495 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
--- | ||
layout: post | ||
title: 2.2.Vue-router使用过程分析 | ||
category: 写教程 | ||
tag: vue-router | ||
exception: | ||
readtime: 12 | ||
--- | ||
|
||
## 注册 | ||
|
||
## 组件调用 | ||
|
||
## 对象调用 | ||
|
||
## 小结 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,235 @@ | ||
--- | ||
layout: post | ||
title: 2.3.Vue-router实现原理 | ||
category: 写教程 | ||
tag: vue-router | ||
exception: | ||
readtime: 8 | ||
--- | ||
|
||
## vue-router使用示例 | ||
|
||
## vue-router路由原理 | ||
在解析源码前,先来了解下前端路由的实现原理。 前端路由实现起来其实很简单,本质就是监听 URL 的变化,然后匹配路由规则, | ||
显示相应的页面,并且无须刷新。目前单页面使用的路由就只有两种实现方式:hash 模式和history 模式。 | ||
hash 模式:即使用 URL 的 hash 来模拟一个完整的 URL ,于是当 URL 改变时,页面不会重新加载。 | ||
history 模式:利用了 HTML5 新特性 history.pushState 来完成 URL 跳转。在浏览器不支持的情况下,vue-router会自动退到 hash 模式。 | ||
|
||
## 根据mode确定类型 | ||
```vuejs | ||
import { HashHistory } from './history/hash' | ||
import { HTML5History } from './history/html5' | ||
import { AbstractHistory } from './history/abstract' | ||
// ... more | ||
constructor(options: RouterOptions = {}) { | ||
// ... more | ||
// 默认hash模式 | ||
let mode = options.mode || 'hash' | ||
// 是否降级处理 | ||
this.fallback = mode === 'history' && !supportsPushState && options.fallback !== false | ||
// 进行降级处理 | ||
if (this.fallback) { | ||
mode = 'hash' | ||
} | ||
if (!inBrowser) { | ||
mode = 'abstract' | ||
} | ||
this.mode = mode | ||
// 根据不同的mode进行不同的处理 | ||
switch (mode) { | ||
case 'history': | ||
this.history = new HTML5History(this, options.base) | ||
break | ||
case 'hash': | ||
this.history = new HashHistory(this, options.base, this.fallback) | ||
break | ||
case 'abstract': | ||
this.history = new AbstractHistory(this, options.base) | ||
break | ||
default: | ||
if (process.env.NODE_ENV !== 'production') { | ||
assert(false, `invalid mode: ${mode}`) | ||
} | ||
} | ||
} | ||
``` | ||
|
||
可以看到,会判断是否支持 history , 然后根据 fallback 来确定是否要降级。然后,根据不同的 mode , 分别实例化不同的 history 。 (HTML5History、HashHistory、AbstractHistory) | ||
|
||
├── history // 操作浏览器记录的一系列内容 | ||
│ ├── abstract.js // 非浏览器的history | ||
│ ├── base.js // 基本的history | ||
│ ├── hash.js // hash模式的history | ||
│ └── html5.js // html5模式的history | ||
|
||
## 不同模式的history的公共实现 | ||
```javascript | ||
export class History { | ||
router: Router; //router对象 | ||
base: string; //基准路径 | ||
current: Route; //当前的路由 | ||
pending: ?Route; | ||
cb: (r: Route) => void; //回调 | ||
ready: boolean; | ||
readyCbs: Array<Function>; | ||
readyErrorCbs: Array<Function>; | ||
errorCbs: Array<Function>; | ||
|
||
// 子类实现 | ||
+go: (n: number) => void; | ||
+push: (loc: RawLocation) => void; | ||
+replace: (loc: RawLocation) => void; | ||
+ensureURL: (push?: boolean) => void; | ||
+getCurrentLocation: () => string; | ||
|
||
constructor (router: Router, base: ?string) { | ||
this.router = router | ||
this.base = normalizeBase(base) //返回基准路径 | ||
this.current = START //route 设置当前route | ||
this.pending = null | ||
this.ready = false | ||
this.readyCbs = [] | ||
this.readyErrorCbs = [] | ||
this.errorCbs = [] | ||
} | ||
|
||
listen (cb: Function) { | ||
this.cb = cb | ||
} | ||
|
||
onReady (cb: Function, errorCb: ?Function) { | ||
//***** | ||
} | ||
|
||
onError (errorCb: Function) { | ||
//***** | ||
} | ||
|
||
//路由转化操作 | ||
transitionTo (location: RawLocation, onComplete?: Function, onAbort?: Function) { | ||
const route = this.router.match(location, this.current) //找到匹配路由 | ||
this.confirmTransition(route, () => { //确认是否转化 | ||
this.updateRoute(route) //更新route | ||
onComplete && onComplete(route) | ||
this.ensureURL() | ||
|
||
// fire ready cbs once | ||
if (!this.ready) { | ||
this.ready = true | ||
this.readyCbs.forEach(cb => { cb(route) }) | ||
} | ||
}, err => { | ||
if (onAbort) { | ||
onAbort(err) | ||
} | ||
if (err && !this.ready) { | ||
this.ready = true | ||
this.readyErrorCbs.forEach(cb => { cb(err) }) | ||
} | ||
}) | ||
} | ||
//确认是否转化路由 | ||
confirmTransition (route: Route, onComplete: Function, onAbort?: Function) { | ||
const current = this.current | ||
const abort = err => { | ||
if (isError(err)) { | ||
if (this.errorCbs.length) { | ||
this.errorCbs.forEach(cb => { cb(err) }) | ||
} else { | ||
warn(false, 'uncaught error during route navigation:') | ||
console.error(err) | ||
} | ||
} | ||
onAbort && onAbort(err) | ||
} | ||
//判断如果前后是同一个路由,不进行操作 | ||
if ( | ||
isSameRoute(route, current) && | ||
route.matched.length === current.matched.length | ||
) { | ||
this.ensureURL() | ||
return abort() | ||
} | ||
//下面是各类钩子函数的处理 | ||
//********************* | ||
}) | ||
} | ||
|
||
//更新路由 | ||
updateRoute (route: Route) { | ||
const prev = this.current //跳转前路由 | ||
this.current = route //装备跳转路由 | ||
this.cb && this.cb(route) //回调函数,这一步很重要,这个回调函数在index文件中注册,会更新被劫持的数据 _router | ||
this.router.afterHooks.forEach(hook => { | ||
hook && hook(route, prev) | ||
}) | ||
} | ||
} | ||
``` | ||
## hash模式的功能实现 | ||
```javascript | ||
//push方法 | ||
push (location: RawLocation, onComplete?: Function, onAbort?: Function) { | ||
this.transitionTo(location, route => { | ||
pushHash(route.fullPath) | ||
onComplete && onComplete(route) | ||
}, onAbort) | ||
} | ||
//replace方法 | ||
replace (location: RawLocation, onComplete?: Function, onAbort?: Function) { | ||
this.transitionTo(location, route => { | ||
replaceHash(route.fullPath) | ||
onComplete && onComplete(route) | ||
}, onAbort) | ||
} | ||
``` | ||
## html5模式的功能实现 | ||
```javascript | ||
//push | ||
push (location: RawLocation, onComplete?: Function, onAbort?: Function) { | ||
const { current: fromRoute } = this | ||
this.transitionTo(location, route => { | ||
pushState(cleanPath(this.base + route.fullPath)) //保存当前的位置信息,用于返回时候复位 | ||
handleScroll(this.router, route, fromRoute, false) | ||
onComplete && onComplete(route) | ||
}, onAbort) | ||
} | ||
|
||
replace (location: RawLocation, onComplete?: Function, onAbort?: Function) { | ||
const { current: fromRoute } = this | ||
this.transitionTo(location, route => { | ||
replaceState(cleanPath(this.base + route.fullPath)) //保存当前的位置信息,用于返回时候复位 | ||
handleScroll(this.router, route, fromRoute, false) | ||
onComplete && onComplete(route) | ||
}, onAbort) | ||
} | ||
``` | ||
## abstract模式的功能实现 | ||
```javascript | ||
push (location: RawLocation, onComplete?: Function, onAbort?: Function) { | ||
this.transitionTo(location, route => { | ||
this.stack = this.stack.slice(0, this.index + 1).concat(route) | ||
this.index++ | ||
onComplete && onComplete(route) | ||
}, onAbort) | ||
} | ||
|
||
replace (location: RawLocation, onComplete?: Function, onAbort?: Function) { | ||
this.transitionTo(location, route => { | ||
this.stack = this.stack.slice(0, this.index).concat(route) | ||
onComplete && onComplete(route) | ||
}, onAbort) | ||
} | ||
``` | ||
## 小结 |
Oops, something went wrong.