之前用Vue开发单页应用,发现不管路由怎么变化,浏览器地址栏总是会有一个'#'号。
当时检查自己的代码,没有发现请求的地址带'#',当时也很纳闷,但是由于没有影响页面的渲染以及向后台发送请求,当时也没有在意。最近看了一下vue-router的实现原理,才逐渐揭开了这个谜题。
vue-router 的两种方式(浏览器环境下)
1. Hash (对应HashHistory)
hash(“#”)符号的本来作用是加在URL中指示网页中的位置:
http://www.example.com/index.html#print
#符号本身以及它后面的字符称之为hash(也就是我之前为什么地址栏都会有一个‘#'),可通过window.location.hash属性读取。它具有如下特点:
hash虽然出现在URL中,但不会被包括在HTTP请求中。它是用来指导浏览器动作的,对服务器端完全无用,因此,改变hash不会重新加载页面
2.可以为hash的改变添加监听事件:
window.addEventListener("hashchange", funcRef, false)
每一次改变hash(window.location.hash),都会在浏览器的访问历史中增加一个记录
利用hash的以上特点,就可以来实现前端路由“更新视图但不重新请求页面”的功能了。
2. History (对应HTML5History)
History接口 是浏览器历史记录栈提供的接口,通过back(), forward(), go()等方法,我们可以读取浏览器历史记录栈的信息,进行各种跳转操作。
从HTML5开始,History interface提供了两个新的方法:pushState(), replaceState()使得我们可以对浏览器历史记录栈进行修改:
window.history.pushState(stateObject, title, URL) window.history.replaceState(stateObject, title, URL)
stateObject: 当浏览器跳转到新的状态时,将触发popState事件,该事件将携带这个stateObject参数的副本 title: 所添加记录的标题 URL: 所添加记录的URL
这两个方法有个共同的特点:当调用他们修改浏览器历史记录栈后,虽然当前URL改变了,但浏览器不会刷新页面,这就为单页应用前端路由“更新视图但不重新请求页面”提供了基础。 浏览器历史记录可以看作一个「栈」。栈是一种后进先出的结构,可以把它想象成一摞盘子,用户每点开一个新网页,都会在上面加一个新盘子,叫「入栈」。用户每次点击「后退」按钮都会取走最上面的那个盘子,叫做「出栈」。而每次浏览器显示的自然是最顶端的盘子的内容。
vue-router 的作用
vue-router的作用就是通过改变URL,在不重新请求页面的情况下,更新页面视图。简单的说就是,虽然地址栏的地址改变了,但是并不是一个全新的页面,而是之前的页面某些部分进行了修改。
export default new Router({ // mode: 'history', //后端支持可开 routes: constantRouterMap })
这是Vue项目中常见的一段初始化vue-router的代码,之前没仔细研究过vue-router,不知道还有一个mode属性,后来看了相关文章后了解到,mode属性用来指定vue-router使用哪一种模式。在没有指定mode的值,则使用hash模式。
源码分析
首先看一下vue-router的构造函数
constructor (options: RouterOptions = {}) { this.app = null this.apps = [] this.options = options this.beforeHooks = [] this.resolveHooks = [] this.afterHooks = [] this.matcher = createMatcher(options.routes || [], this) 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 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}`) } } }
主要是先获取mode的值,如果mode的值为 history 但是浏览器不支持 history 模式,那么就强制设置mode值为 hash 。如果支持则为 history 。接下来,根据mode的值,来选择vue-router使用哪种模式。
case 'history': this.history = new HTML5History(this, options.base) break case 'hash': this.history = new HashHistory(this, options.base, this.fallback) break
这样就有了两种模式。确定好了vue-router使用哪种模式后,就到了init。 先来看看router 的 init 方法就干了哪些事情,在 src/index.js 中
init (app: any /* Vue component instance */) { // .... const history = this.history if (history instanceof HTML5History) { history.transitionTo(history.getCurrentLocation()) } else if (history instanceof HashHistory) { const setupHashListener = () => { history.setupListeners() } history.transitionTo( history.getCurrentLocation(), setupHashListener, setupHashListener ) } history.listen(route => { this.apps.forEach((app) => { app._route = route }) }) } // .... // VueRouter类暴露的以下方法实际是调用具体history对象的方法 push (location: RawLocation, onComplete"htmlcode">history.transitionTo(history.getCurrentLocation())如果是Hash模式,则执行
const setupHashListener = () => { history.setupListeners() } history.transitionTo( history.getCurrentLocation(), setupHashListener, setupHashListener )可以看出,两种模式都执行了transitionTo( )函数。 接下来看一下两种模式分别是怎么执行的,首先看一下Hash模式
HashHistory.push()
我们来看HashHistory中的push()方法:
push (location: RawLocation, onComplete"htmlcode">transitionTo (location: RawLocation, onComplete"htmlcode">init (app: any /* Vue component instance */) { this.apps.push(app) history.listen(route => { this.apps.forEach((app) => { app._route = route }) }) }代码中的app指的是Vue的实例,._route本不是本身的组件中定义的内置属性,而是在Vue.use(Router)加载vue-router插件的时候,通过Vue.mixin()方法,全局注册一个混合,影响注册之后所有创建的每个 Vue 实例,该混合在beforeCreate钩子中通过
Vue.util.defineReactive()
定义了响应式的_route。所谓响应式属性,即当_route值改变时,会自动调用Vue实例的render()方法,更新视图。vm.render()是根据当前的 _route 的path,name等属性,来将路由对应的组件渲染到. 所以总结下来,从路由改变到视图的更新流程如下:this.$router.push(path) --> HashHistory.push() --> History.transitionTo() --> const route = this.router.match(location, this.current)会进行地址匹配,得到一个对应当前地址的route(路由信息对象) --> History.updateRoute(route) --> app._route=route (Vue实例的_route改变) 由于_route属性是采用vue的数据劫持,当_route的值改变时,会执行响应的render( ) -- > vm.render() 具体是在<router-view></router-view> 中render --> window.location.hash = route.fullpath (浏览器地址栏显示新的路由的path)
HashHistory.replace()
说完了HashHistory.push(),该说HashHistory.replace()了。
replace (location: RawLocation, onComplete"color: #ff0000">监听地址栏可以看出来,上面的过程都是在代码内部进行路由的改变的,比如项目中常见的
this.$router.push(),
等方法。然后将浏览器的地址栏置为新的hash值。那么如果直接在地址栏中输入URL从而改变路由呢,例如我将dashboadr删除,然后置为article/hotSpot,然后回车,vue又是如何处理的呢?
setupListeners () { window.addEventListener('hashchange', () => { if (!ensureSlash()) { return } this.transitionTo(getHash(), route => { replaceHash(route.fullPath) }) }) }该方法设置监听了浏览器事件hashchange,调用的函数为replaceHash,即在浏览器地址栏中直接输入路由相当于代码调用了replace()方法.后面的步骤自然与HashHistory.replace()相同,一样实现页面渲染。
HTML5History
HTML5History模式的vue-router 代码结构以及更新视图的逻辑与hash模式基本类似,和HashHistory的步骤基本一致,只是HashHistory的push和replace()变成了HTML5History.pushState()和HTML5History.replaceState()
在HTML5History中添加对修改浏览器地址栏URL的监听是直接在构造函数中执行的,对HTML5History的popstate 事件进行监听:
constructor (router: Router, base: "color: #ff0000">总结以上所述是小编给大家介绍的vue-router实现原理及两种模式分析,希望对大家有所帮助!
DDR爱好者之家 Design By 杰米广告合作:本站广告合作请联系QQ:858582 申请时备注:广告合作(否则不回)
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!DDR爱好者之家 Design By 杰米暂无评论...
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。
更新日志
- 凤飞飞《我们的主题曲》飞跃制作[正版原抓WAV+CUE]
- 刘嘉亮《亮情歌2》[WAV+CUE][1G]
- 红馆40·谭咏麟《歌者恋歌浓情30年演唱会》3CD[低速原抓WAV+CUE][1.8G]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[320K/MP3][193.25MB]
- 【轻音乐】曼托凡尼乐团《精选辑》2CD.1998[FLAC+CUE整轨]
- 邝美云《心中有爱》1989年香港DMIJP版1MTO东芝首版[WAV+CUE]
- 群星《情叹-发烧女声DSD》天籁女声发烧碟[WAV+CUE]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[FLAC/分轨][748.03MB]
- 理想混蛋《Origin Sessions》[320K/MP3][37.47MB]
- 公馆青少年《我其实一点都不酷》[320K/MP3][78.78MB]
- 群星《情叹-发烧男声DSD》最值得珍藏的完美男声[WAV+CUE]
- 群星《国韵飘香·贵妃醉酒HQCD黑胶王》2CD[WAV]
- 卫兰《DAUGHTER》【低速原抓WAV+CUE】
- 公馆青少年《我其实一点都不酷》[FLAC/分轨][398.22MB]
- ZWEI《迟暮的花 (Explicit)》[320K/MP3][57.16MB]