Vue进阶(七十七):vue 路由的两种模式:hash与history_vue创建中的history

// 切换路由
history.listen(route => {
  this.apps.forEach((app) => {
    app._route = route
  })
})

}
// 一些钩子函数 beforeEach、 afterEach等
// …
// …

// VueRouter类暴露的以下方法实际是调用具体history对象的方法
push (location: RawLocation, onComplete?: Function, onAbort?: Function) {
this.history.push(location, onComplete, onAbort)
}

replace (location: RawLocation, onComplete?: Function, onAbort?: Function) {
this.history.replace(location, onComplete, onAbort)
}
}
// 相对于当前页面向前或向后跳转多少个页面,类似 window.history.go(n)。n可为正数可为负数。正数返回上一个页面
go (n: number) {
this.history.go(n)
}
// 后退到上一个页面
back () {
this.go(-1)
}
// 前进到下一个页面
forward () {
this.go(1)
}

// …
// …


代码解读:


* 浏览器默认是`hash`模式,当浏览器不支持html5的`history`模式时,也会强制为`hash`模式;当环境不是浏览器时,强制为`abstract`模式。
* 当创建`VueRouter`实例后,`VueRouter`构造函数会通过传入对象`options`的`mode`参数,来调用对应的(`HashHistory / HTML5History / AbstractHistory`)构造函数,进而创建对应的`history`实例对象。
* 创建相应的`history`实例后,可以看到`init`函数里面会有对应的初始化操作。


`$router`实例有两个常见的跳转方法:`push`与`replace`方法,源码的最下面,就暴露出来这两个方法。很显然这两种方法都是对不同模式下的方法的封装,本质还是执行的对应模式上的方法。


`HashHistory`中的`push()`方法:



push (location: RawLocation, onComplete?: Function, onAbort?: Function) {
// 执行transitionTo函数
this.transitionTo(location, route => {
// 改变hash值
pushHash(route.fullPath)
onComplete && onComplete(route)
}, onAbort)
}

function pushHash (path) {
window.location.hash = path
}


`pushHash`直接对`window.location.hash`赋值,`hash`值变化之后,浏览器访问历史中就会增加一个记录。


记录增加了,如何更新视图呢?接着看父类History中`transitionTo`函数是如何实现的。


`transtitionTo`函数具体实现如下:



transitionTo (location: RawLocation, onComplete?: Function, onAbort?: Function) {
//找到匹配路由(match函数)
const route = this.router.match(location, this.current)
this.confirmTransition(route, () => { //确认是否转化
this.updateRoute(route) //更新route
// …
})
}

//更新路由
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)
})
}
}

// this.cb函数的定义
listen (cb: Function) {
this.cb = cb
}


在`transitionTo`函数中,执行了`updateRoute`更新路由函数,这个函数中执行了`cb`这个函数,`cb`这个函数是在`VueRouter`实例中的`init`函数中通过`history.listen`传入的。



init (app: any /* Vue component instance */) {
// …
this.apps.push(app)
// …
history.listen(route => {
this.apps.forEach((app) => {
app._route = route
})
})
}


上面的代码,从`$router.push` 分析到了`cb()`函数, 再接着看cb函数,这个函数中有一个`_route`属性,并且将匹配的路由`route`赋给了`app._route`。(这里app是组件实例,apps是所有组件实例一个数组) 。


那么`_route`属性是什么,组件本身是没有定义这个属性的,那么这个属性从哪里来的呢?源码中在`install()`方法中使用`Vue.mixin()`方法添加一个全局的混合对象:


`install.js`代码里面做了四件事:


1. 混入`beforeCreate`函数,在里面定义`_route`这个响应式属性。
2. 混入`destroyed`函数。
3. 在`Vue.prototype`上定义`$router`、`$route`这两个对象,以便每个组件都可以获得。
4. 在Vue`上`注册`router-link`与`router-view`这两个组件。



// install.js代码:
export function install (Vue) {
if (install.installed && _Vue === Vue) return
install.installed = true

_Vue = Vue

const isDef = v => v !== undefined

const registerInstance = (vm, callVal) => {
let i = vm.$options._parentVnode
if (isDef(i) && isDef(i = i.data) && isDef(i = i.registerRouteInstance)) {
i(vm, callVal)
}
}

// 使用mixin会在每个.vue文件中进行beforeCreate和destroyed
Vue.mixin({
//对每个Vue实例混入beforeCreate钩子操作
//验证Vue实例是否有router对象了,如果有,就不再初始化了
beforeCreate () {
// 如果没有router对象(this. o p t i o n . r o u t e r 来自于 V u e R o u t e r 实例 r o u t e r 对象 ) i f ( i s D e f ( t h i s . option.router来自于VueRouter实例router对象) if (isDef(this. option.router来自于VueRouter实例router对象)if(isDef(this.options.router)) {
// 将_routerRoot指向当前组件
this._routerRoot = this
// 将router对象挂载到根组件的_router上
this._router = this.KaTeX parse error: Expected 'EOF', got '}' at position 228: …current) }̲ else { // 如果…parent && this.KaTeX parse error: Expected 'EOF', got '}' at position 35: … || this }̲ // 注册vue…router、 r o u t e 属性(方便所有组件可以获取这两个属性) O b j e c t . d e f i n e P r o p e r t y ( V u e . p r o t o t y p e , ′ route 属性(方便所有组件可以获取这两个属性) Object.defineProperty(Vue.prototype, ' route属性(方便所有组件可以获取这两个属性)Object.defineProperty(Vue.prototype,router’, {
get () { return this._routerRoot._router }
})

Object.defineProperty(Vue.prototype, ‘$route’, {
get () { return this._routerRoot._route }
})
//Vue上注册router-link和router-view两个组件
Vue.component(‘RouterView’, View)
Vue.component(‘RouterLink’, Link)

const strats = Vue.config.optionMergeStrategies
// use the same hook merging strategy for route hooks
strats.beforeRouteEnter = strats.beforeRouteLeave = strats.beforeRouteUpdate = strats.created
}


在`beforeCreate`钩子中,通过`Vue.util.defineReactive()`创建一个响应式`_route`属性。`router-view`组件挂载时,会生成一个`watcher`,当`hash`值变化时,匹配到新的路由`route`后,`_route`属性跟着改变,触发`_route`属性的`getter`,进行依赖收集`watcher`,最后触发`_route`的`setter`,执行`watcher`的更新函数,进而触发`<router-view>`的`render`函数,更新视图。


大致流程为:



$router.push-> history.push -> transitionTo->updateRoute->cb(即 app._route = route) -> render


1. 触发`$router.push()`。
2. 触发 `HashHistory.push()`。
3. 触发`transitionTo()` ,根据传入的`location`找到匹配的`route`,触发`updateRoute()`。
4. 触发`updateRoute()`, 将匹配的`route`赋给实例`app`的`_route`响应式属性, 当`_route`属性变化时,就会触发实例的`render`函数,更新视图。
**ES6**

*   列举常用的ES6特性:

*   箭头函数需要注意哪些地方?

*   let、const、var

*   拓展:var方式定义的变量有什么样的bug?

*   Set数据结构

*   拓展:数组去重的方法

*   箭头函数this的指向。

*   手写ES6 class继承。



![](https://img-blog.csdnimg.cn/img_convert/aac1740e50faadb9a6a7a5b97f9ccba8.png)



**微信小程序**

*   简单描述一下微信小程序的相关文件类型?

*   你是怎么封装微信小程序的数据请求?

*   有哪些参数传值的方法?

*   你使用过哪些方法,来提高微信小程序的应用速度?

*   小程序和原生App哪个好?

*   简述微信小程序原理?

*   分析微信小程序的优劣势

*   怎么解决小程序的异步请求问题?



![](https://img-blog.csdnimg.cn/img_convert/60b1dbe5c76e264468aa993416a9a031.png)



**其他知识点面试**

*   webpack的原理

*   webpack的loader和plugin的区别?

*   怎么使用webpack对项目进行优化?

*   防抖、节流

*   浏览器的缓存机制

*   描述一下二叉树, 并说明二叉树的几种遍历方式?

*   项目类问题

*   笔试编程题:



![](https://img-blog.csdnimg.cn/img_convert/aec12fc95e5722b9f2f1f22eeb5e67bd.png)



#### 最后



技术栈比较搭,基本用过的东西都是一模一样的。快手终面喜欢问智力题,校招也是终面问智力题,大家要准备一下一些经典智力题。如果排列组合、概率论这些基础忘了,建议回去补一下。
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值