前端路由简介以及vue-router实现原理( 二 )

current.route 内存放当前路由的配置信息 , 所以我们只需要监听 current.route 的变化来动态 render 页面便可 。
接着我么需要监听不同的路由变化 , 做相应的处理 。 以及实现 hash 和history 模式 。
据驱动这里我们延用 vue 数据驱动模型 , 实现一个简单的数据劫持 , 并更新视图 。 首先定义我们的 observer
class Observer {constructor (value) {this.walk(value)}walk (obj) {Object.keys(obj).forEach((key) => {// 如果是对象 , 则递归调用walk , 保证每个属性都可以被defineReactiveif (typeof obj[key] === 'object') {this.walk(obj[key])}defineReactive(obj, key, obj[key])})}}function defineReactive(obj, key, value) {let dep = new Dep()Object.defineProperty(obj, key, {get: () => {if (Dep.target) {// 依赖收集dep.add()}return value},set: (newValue) => {value = http://kandian.youth.cn/index/newValue// 通知更新 , 对应的更新视图dep.notify()}})}export function observer(value) {return new Observer(value)}再接着 , 我们需要定义 Dep 和 Watcher:
export class Dep {constructor () {this.deppend = []}add () {// 收集watcherthis.deppend.push(Dep.target)}notify () {this.deppend.forEach((target) => {// 调用watcher的更新函数target.update()})}}Dep.target = nullexport function setTarget (target) {Dep.target = target}export function cleanTarget() {Dep.target = null}// Watcherexport class Watcher {constructor (vm, expression, callback) {this.vm = vmthis.callbacks = []this.expression = expressionthis.callbacks.push(callback)this.value = http://kandian.youth.cn/index/this.getVal()}getVal () {setTarget(this)// 触发 get 方法 , 完成对 watcher 的收集let val = this.vmthis.expression.split('.').forEach((key) => {val = val[key]})cleanTarget()return val}// 更新动作update () {this.callbacks.forEach((cb) => {cb()})}}到这里我们实现了一个简单的订阅-发布器 , 所以我们需要对 current.route做数据劫持 。 一旦current.route更新 , 我们可以及时的更新当前页面:
// 响应式数据劫持observer(this.current)// 对 current.route 对象进行依赖收集 , 变化时通过 render 来更新new Watcher(this.current, 'route', this.render.bind(this))恩....到这里 , 我们似乎已经完成了一个简单的响应式数据更新 。 其实 render 也就是动态的为页面指定区域渲染对应内容 , 这里只做一个简化版的 render:
render() {let iif ((i = this.history.current)--tt-darkmode-color: #9B9B9B;">接下来是 hash 和history模式的实现 , 这里我们可以沿用 vue-router的思想 , 建立不同的处理模型便可 。 来看一下我实现的核心代码:
this.history = this.mode === 'history' ? new HTML5History(this) : new HashHistory(this)当页面变化时 , 我们只需要监听 hashchange 和 popstate 事件 , 做路由转换 transitionTo:
/*** 路由转换* @param target 目标路径* @param cb 成功后的回调*/transitionTo(target, cb) {// 通过对比传入的 routes 获取匹配到的 targetRoute 对象const targetRoute = match(target, this.router.routes)this.confirmTransition(targetRoute, () => {// 这里会触发视图更新this.current.route = targetRoutethis.current.name = targetRoute.namethis.current.path = targetRoute.paththis.current.query = targetRoute.query || getQuery()this.current.fullPath = getFullPath(this.current)cb--tt-darkmode-color: #9B9B9B;">这样我们一方面通过 this.current.route = targetRoute 达到了对之前劫持数据的更新 , 来达到视图更新 。 另一方面我们又通过任务队列的调度 , 实现了基本的钩子函数beforeEach、beforeLeave、beforeEnter、afterEnter 。