这是我参与8月更文挑战的第14天,活动详情查看:8月更文挑战
实例化VueRouter对象时,以构造函数的方式传入mode参数决定Router模式。 默认为hash模式,通过supportPushState判断是否支持history模式。 不支持则回滚为hash模式,如果不在浏览器下运行强制为abstract模式。 根据mode确定并类型实例化路由。通过类型初始化操作和监听。 其中暴露了两个方法push(),replace()。
Hash模式:
路径上的#用于指示网页中的位置,#符号以及后面的字符称为hash。 可通过window.location.hash属性读取。有以下特点: 1.不包括在HTTP请求中,用来指导浏览器动作,对服务器无用。改Hash不会重新加载页面。 2.可以为Hash的改变添加监听事件// window.addEventListener("hashchange",funcRef,false) 3.每次改变Hash,都会在浏览器访问历史增加一个记录。
Hash.push()//函数原理
push (location: RawLocation, onComplete?: Function, onAbort?: Function) {
this.transitionTo(location, route => { //该方法用于处理路由变化,父类定义。
pushHash(route.fullPath)
onComplete && onComplete(route)
}, onAbort)
}
function pushHash (path) { //push函数直接对Hash进行赋值
window.location.hash = path
}
transitionTo()//函数原理
transitionTo (location: RawLocation, onComplete?: Function, onAbort?: Function) {
const route = this.router.match(location, this.current)
this.confirmTransition(route, () => {
this.updateRoute(route)
...
})
}
updateRoute (route: Route) {
this.cb && this.cb(route)
}
listen (cb: Function) { //路由变化调用this.cd方法,通过History.listen(cb)设置
this.cb = cb //this == history(路由对象) 初始化时有调用
}
初始化时,在插件加载的时候,VueRouter的install()方法中混入Vue对象,通过调用Vue.minxin()方法,全局注册一个mixin对象,影响每个Vue实例,混合在beforeCreated钩子中,通过Vue.util.defineReactive()定义_route属性。当_route值改变时,会自动调用Vue实例的render()方法,更新视图。//即为响应行为
$router.push() --> HashHistory.push() --> History.transitionTo() --> History.updateRoute() --> {app._route = route} --> vm.render() //路由改变到更新的流程
Hash.replace()//函数原理 调用replaceHash()//window.location.replace方法进行替换
监听地址栏
setupListeners () { //通过添加监听事件,监听地址栏中路由的变化,其余响应与Hash相同。
window.addEventListener('hashchange', () => {
if (!ensureSlash()) {
return
}
this.transitionTo(getHash(), route => {
replaceHash(route.fullPath)
})
})
}
History模式:
History interface是浏览器历史记录栈提供的接口,通过back(),forward(),go()等方法进行各种跳转。并且可以读取浏览器历史记录栈的信息。 History提供了两个方法:
window.history.pushState(stateObject, title, URL) //pushState() window.history.replaceState(stateObject, title, URL)//replaceState() stateObject:当浏览器跳转到新的状态时,将触发popState事件,该事件将携带这个StateObject参数的副本 title:所添加记录的标题 URL:所添加记录的URL
两个方法虽然改变了URL,但不会立即发送请求该URL。 两个方法的逻辑与Hash相似,不过将hash赋值改为history.pushState()和replaceState() History模式中添加浏览器地址栏的监听是直接在构造函数中执行的:
constructor (router: Router, base: ?string) {
window.addEventListener('popstate', e => {
const current = this.current
this.transitionTo(getLocation(this.base), route => {
if (expectScroll) {
handleScroll(router, route, current, true)
}
})
})
}
History模式需要浏览器版本的支持。
Vue-router两种模式总结
都是通过浏览器接口实现的,除此之外还有一个abstract模式,原理是使用数组stack模拟出浏览器历史记录栈的功能。 hash模式与history模式差距不大,就差一个#导航。 pushState()方法有以下优势: 1.pushState设置的新URL可以是与当前URL同源的任意URL,Hash只能修改#后面的部分,故只可设置与当前同文档的URL
2.pushState设置的新URL可以与当前URL一模一样,也会添加记录到栈中,而Hash必须不同才会触发添加记录
3.pushState通过StateObject可以添加任意类型的数据到记录中,Hash只可添加短字符串
4.pushState可额外设置title属性供后续使用
History模式的一个问题: 用户直接输入地址并回车,history模式会将URL修改,如正常请求后端,并无经过index.html页面,而打包后请求的文件通常需要经过index.html页面去访问。如未配置对应处理会返回404错误。 如果使用Hash模式并注意打包后配置路径,可以直接从文件系统直接加载Vue单页应用。
Vuex
1.State:包含了store中存储的各个状态。
2.getter:类似于Vue中的计算属性,根据其他getter或state计算返回值。
3.mutation:一组方法,是改变store中状态的执行者,只能是同步操作。
4.action:一组方法,其中可以包含异步操作。 action类似于mutation,提交mutation(调用mutation),而不是直接变更状态。 1.mapState 2.mapGetters 3.mapMutations 4.mapActions//辅助函数方法 响应式的原因是因为将state存入Vue实例组件中的data,Vuex的getters借助vue的计算属性computed实现数据实时监听。Vuex通过全局注入store对象,实现组件间的状态共享。 多个组件自动获取更改后的数据进行业务逻辑处理,这时候使用vuex比较合适。
Vue生命周期
钩子函数的this指向Vue实例 除beforeUpdate/updated钩子函数外,其余钩子在初始化和销毁时只会执行一次。
beforeCreate():实例初始化之后调用,不能访问到data,computed,watch,methods上的方法和数据。//常用于初始化非响应式变量
created():实例创建完成,可访问data,computed,watch,methods上的方法和数据,未挂载到DOM,不能访问到ref属性内容为空数组。//常用与简单的Ajax请求,页面的初始化。
beforeMount():在挂载开始之前被调用,beforeMounted之前,会找到对应的template,并编译成render函数。 mounted():实例挂载到DOM上,此时可以通过DOM API获取到DOM节点,$ref属性可以访问。//常用于获取VNode(类,用于创建DOM,不同实例代表不同类型的真实DOM)信息和操作,Ajax请求。
beforeupdate():响应式数据更新时调用,发生在虚拟DOM打补丁之前,适合在更新之前访问现有的DOM。//比如手动移除已添加的事件监听器
updated():虚拟DOM重新渲染和打补丁之后调用,组件DOM已经更新。//可执行依赖于DOM的操作,避免在这个钩子函数中操作数据,可能陷入死循环。
beforeDestroy():实例销毁之前调用。这一步,实例仍然完全可用,this仍能获取到实例。//常用与销毁定时器,解绑全局事件,销毁插件对象等操作。
destoryed():实例销毁后调用,Vue实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。
注意: 1.created阶段的Ajax与mounted请求的区别:created页面视图未出现,如果请求信息过多,页面会长时间处于白屏状态。
2.mounted不会承诺所有的子组件也都一起被挂载。此时调用子组件refs可能会报错,可以通过调用 this.$nextTick()函数等到视图渲染完毕。
父子组件的生命周期:
1.仅当子组件完成挂载后,父组件才会挂载
2.当子组件完成挂载后,父组件会主动执行一次beforeUpdate/updated钩子函数(一次)
3.父子组件在data变化中是分别监控的,但是在更新props中的数据是关联的(可实践)
4.会先销毁子组件再销毁父组件