内容简介:众所周知,统一资源定位符(或称统一资源定位器/定位地址、URL地址等,英语:Uniform Resource Locator,常缩写为URL)标准格式:
众所周知, vue-router
有三种模式 : hash
、 html5
、 abstract
, 一般的前端项目中会选择 hash
模式进行开发,最近做了一个运营活动就是基于 vue-router的hash模式
进行开发的。
- 项目注册了两个路由(抽象出来的Demo)
var router = new VueRouter({
routes: [{
name: 'index',
path: '',
component: indexcomponent
},{
name: 'demo',
path: '/demo',
component: democomponent
}]
});
复制代码
-
入口页面需要参数,所以提供URL:
https://www.xxx.com?from=weixin, 浏览器里输入URL回车后,页面自动增加一个#/变为https://www.xxx.com?from=weixin#/。 -
index页面中一个按钮点击后跳转demo,同时想携带index中获取的参数,看API选择了如下方式,结果URL变成了:
https://www.xxx.com?from=weixin#/test?userId=123
router.push({
path: 'demo',
query: {
plan: 'private'
}
})
复制代码
产生质疑
- URL有什么标准?(上面Demo页面跳转后URL看起来怪怪的)
- vue-router是如何控制URL的?
质疑探究
URL标准
统一资源定位符(或称统一资源定位器/定位地址、URL地址等,英语:Uniform Resource Locator,常缩写为URL)
标准格式: scheme:[//authority]path[?query][#fragment]
例子
下图展示了两个 URI 例子及它们的组成部分。
hierarchical part
┌───────────────────┴─────────────────────┐
authority path
┌───────────────┴───────────────┐┌───┴────┐
abc://username:password@example.com:123/path/data?key=value&key2=value2#fragid1
└┬┘ └───────┬───────┘ └────┬────┘ └┬┘ └─────────┬─────────┘ └──┬──┘
scheme user information host port query fragment
urn:example:mammal:monotreme:echidna
└┬┘ └──────────────┬───────────────┘
scheme path
URL中的『?』『#』
-
『?』
- 路径与参数分隔符
- 浏览器只识别url中的第一个『?』,后面的会当做参数处理
-
『#』
- 『#』一般是页面内定位用的,如我们最熟悉不过的锚点定位
- 浏览器可以通过『onhashchange』监听hash的变化
- http请求中不包含#
-
Request Headers中的Referer不包含# - 改变#不触发网页重载
-
url中#后面出现的任何字符都会被截断。(
http://www.xxx.com/?color=#fff发出请求是:/color=) - 改变#会改变history
-
window.location.hash读取#值
URL读取和操作
URL读取和操作涉及location和history两个对象,具体如下:
location API :
-
属性
- href = protocol + hostName + port + pathname + search + hash
- host
- origin
-
方法
- assign
- href
- replace ,不记录history
- reload
history API:
-
方法
- back()
- forward()
- go()
-
H5新增API
- pushState()
- replaceState()
- popstate监听变化
vue-router路由实现浅析
初始化router的时候,根据指定的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}`)
}
}
复制代码
我们选择 hash
模式进行深入分析,对应 HashHistory
模块,该模块是 history/hash.js
实现的,当被调用的时候,对全局路由变化进行了监听
window.addEventListener(supportsPushState ? 'popstate' : 'hashchange', () => {
...
})
复制代码
同时 hash.js
中也实现了 push
等api方法的封装,我们以 push
为例,根据源码可以看出,它的实现是基于基类 transitionTo
的实现,具体如下:
push (location: RawLocation, onComplete?: Function, onAbort?: Function) {
const { current: fromRoute } = this
this.transitionTo(location, route => {
pushHash(route.fullPath)
handleScroll(this.router, route, fromRoute, false)
onComplete && onComplete(route)
}, onAbort)
}
复制代码
既然调用了 transitionTo
那么来看它的实现,获取参数后调用 confirmTransition
transitionTo (location: RawLocation, onComplete?: Function, onAbort?: Function) {
// 获取URL中的参数
const route = this.router.match(location, this.current)
this.confirmTransition(route, () => {
this.updateRoute(route)
onComplete && onComplete(route)
this.ensureURL()
...
})
}
复制代码
同时 confirmTransition
里实现了一个队列,顺序执行, iterator
通过后执行 next
,进而志新 pushHash()
,实现页面hash改变,最终实现了 ${base}#${path}
的连接
function getUrl (path) {
const href = window.location.href
const i = href.indexOf('#')
const base = i >= 0 ? href.slice(0, i) : href
return `${base}#${path}`
}
function pushHash (path) {
if (supportsPushState) {
pushState(getUrl(path))
} else {
window.location.hash = path
}
}
复制代码
问题解决
https://www.xxx.com?from=weixin#/test?userId=123
总结
- 标准的URL应该是 search + hash ,不要被当下各种框架欺骗,误以参数应该在hash后面拼接
- URL中可以有多个问号,但为了便于理解,还是尽量避免这种写法
- 避免上面尴尬问题的一个方法是 HTML5 Histroy 模式,感兴趣的同学可以关注并实践一下
- 了解原理,了解设计模式,可以借鉴到平时开发项目中
参考文档
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。