有一段时间没写过技术干货文了,这两天刚好遇到一个以前没太在意的一个功能实现--
前端获取URL传递的参数
毕竟平时都是在后台处理,掉了一堆头发后,想想还是写出来跟你们分享一下,以后要是你们遇到了也有个参考
要只是获取一些常规字符串到没什么难的,关键还有些乱七八糟的需求,什么同一个参数名传递了多次啊,传数组啊。搞来搞去就写了一大堆
先说说思路吧,
如果你看这文章是想要解决问题,拿着代码直接用的话,就直接看最后面的Code实现以及使用方法吧
用框架思维分析问题
给你一个如下的URL:
NaoNao.com/?product=sh…
将URL里传递的参数转换为
object
对象,这样我们在使用参数的时候也更为方便
我曾多次强调框架思维
,现在遇到这个问题了,我们就拿框架思维来分析一下,该怎样才能快速解决
首先是要了解我们的
目的是什么
?目的很简单,取得URL内传递的参数,并且解析成对象
接着再分析
我们现在知道些什么
?有一串URL
我们再来分析,如果从URL中获得传递的参数,也就是
为了达到目的,我们该做些什么?
URL的特征我们大致都知道,就是第一个
?
后面的字符串,都是传递的参数,但是
有个特殊情况请不要忘记了
,URL后面有时候会带上一个
#
,而
#
后面的内容,并不是我们要传递的参数,而是网页位置的标识符
如果URL中包含了
#
,
我们只需要解析
?
到
#
之间的字符串就可以了
,如果不包含,那么第一个
?
后所有的内容都是我们需要解析的
你可能觉得我是在说废话,这么明显的事情,只要不是白痴都能看得懂
我当然知道,只要不是白痴都能看得懂,
但我为什么要强调呢?因为我们想要快速的解决问题,必须具备框架思维,也可以说是工程思维
你可能有会说,这么简单的问题需要这样分析么?我们一看就知道了,闹闹你这是杀鸡用牛刀
虽说是杀鸡用牛刀,可
要想培养自己的工程思维,那么必须保持刻意训练,直到随手拈来
好了,分析完后,我们按照上面的思路来逐步实现,实现的时候可能会遇到其它的问题,到时候再分析,再解决
毕竟再牛逼的工程师,也不会在动手前就想的面面俱到,只能是
在动手实现前尽可能的考虑周到,遇到问题时再快速的迭代更新
JS获取URL参数的过程
先用JS拿到URL,如果函数传参了URL,那就用参数。如果没传参,就使用当前页面的URL
var queryString = url ? url.split('?')[1] : window.location.search.slice(1);
复制代码
如果后面的字符串存在
#
,我们还得将
#
后面的字符串去掉,因为
#
后面的内容并不是我们需要获取的参数,而是网页位置的标识符
queryString = queryString.split('#')[0]
复制代码
好了,把干扰的部分都移除后,我们可以开始安心的解析参数了,先将传递的参数分成数组
var arr = queryString.split('&')
复制代码
现在我们可以获得一个字符串数组
['product=shirt', 'color=blue', 'newuser', 'size=m']
复制代码
将字符串拆分成数组后,我们通过创建一个对象,用来存储我们所有的参数
var obj = {}
复制代码
我们可以通过遍历数组
arr
,将它拆分成键值对。把这个字符串做成
key:value
的对象
var a = arr[i].split('=')
复制代码
接下来就是要为每一个变量
key
分配对应的值
value
,如果我们得到的
value
不是一个正确的参数,我们就用
true
来表示这个参数名存在,当然了,你也可以根据自己的实际情况来做改变
var paramName = a[0]
var paramValue = typeof(a[1]) === 'undefined' ? true : a[1]
复制代码
在这里我只是对
undefined
做了标记,如果是
NaN
,我是直接拿它当字符串处理了
在这里有一个小坑得提醒一下
,我们在调用函数,获取对象取值的时候,如果URL传递的
key
为大写,我们取对象时写的小写,那么结果就是为
undefined
比如URL为
http://NaoNao.com/?NamE=NaoNao
,如果不做大小写的处理,调用对象取值时
getAllUrlParams().NamE
才能取到值
NaoNao
,如果做了处理,我们使用时只需要全部写成小写/大写即可,例如
getAllUrlParams().name
我在这就全部转为小写了,如果你对大小写要求区分,那到时候把这段Code给去掉就好了
paramName = paramName.toLowerCase()
if (typeof paramValue === 'string') paramValue = paramValue.toLowerCase()
复制代码
接下来我们就要去处理我们接受到的
paramValue
,
这些参数可能是索引数组,非索引数组,又或者是常规字符串
如果是索引数组
,我们需要将
paramValue
转换成数组,并且将索引对应的值,放入索引对应的位置
如果是非索引数组
,我们就要将
paramValue
放到数组中
如果只是常规的字符串
,我们就需要为我们的对象
obj
创建一个常规的属性,并为其分配值。
如果这个key已经存在
,那么我们就要将现有的
paramValue
从
key:value
转换为数组,并将它放到数组中
拿几个实际案例,感受一下我们要做什么吧
// 索引数组
getAllUrlParams('http://NaoNao.com/?colors[0]=red&colors[2]=green&colors[6]=blue');
// { "colors": [ "red", null, "green", null, null, null, "blue" ] }
// 非索引数组
getAllUrlParams('http://NaoNao.com/?colors[]=red&colors[]=green&colors[]=blue');
// { "colors": [ "red", "green", "blue" ] }
// 多次传递同一个key
getAllUrlParams('http://NaoNao.com/?colors=red&colors=green&colors=blue');
// { "colors": [ "red", "green", "blue" ] }
// 传递了key,但是没传value
getAllUrlParams('http://NaoNao.com/?product=shirt&color=blue&newuser&size=m');
// { "product": "shirt", "color": "blue", "newuser": true, "size": "m" }
复制代码
我做这写判断时用的是正则表达式,在这里就不解释正则了。。。毕竟解释起来篇幅就太长了,能看懂就尽量看吧
每个正则要解析什么,在注释中都写了例子,
稍微了解点正则表达式的同学,多半也能看懂的
对应的代码实现如下:
// 如果paramName以方括号结束, e.g. colors[] or colors[2]
if (paramName.match(/\[(\d+)?\]$/)) {
// 如果paramName不存在,则创建key
var key = paramName.replace(/\[(\d+)?\]/, '')
if (!obj[key]) obj[key] = []
// 如果是索引数组 e.g. colors[2]
if (paramName.match(/\[\d+\]$/)) {
// 获取索引值并在对应的位置添加值
var index = /\[(\d+)\]/.exec(paramName)[1]
obj[key][index] = paramValue
} else {
// 如果是其它的类型,也放到数组中
obj[key].push(paramValue)
} else {
// 处理字符串类型
if (!obj[paramName]) {
// 如果如果paramName不存在,则创建对象的属性
obj[paramName] = paramValue
} else if (obj[paramName] && typeof obj[paramName] === 'string') {
// 如果属性存在,并且是个字符串,那么就转换为数组
obj[paramName] = [obj[paramName]]
obj[paramName].push(paramValue)
} else {
// 如果是其它的类型,还是往数组里丢
obj[paramName].push(paramValue)
复制代码
如果你的URL的传参包含了一些特殊字符,比如空格。例如
url="NaoNao.com/?name=Nao%20Nao"
,拿到对象值之后,是需要解码后才能获得正确的值的
var original = getAllUrlParams().name
var decode = decodeURIComponent(original)
复制代码
具体实现以及使用方式
下面是JS的具体的完整实现,你们复制回去就可以用
function getAllUrlParams(url) {
// 用JS拿到URL,如果函数接收了URL,那就用函数的参数。如果没传参,就使用当前页面的URL
var queryString = url ? url.split('?')[1] : window.location.search.slice(1)
// 用来存储我们所有的参数
var obj = {}
// 如果没有传参,返回一个空对象
if (!queryString) {
return obj
// stuff after
queryString = queryString.split('#')[0]
// 将参数分成数组
var arr = queryString.split('&')
for (var i = 0
// 分离成key:value的形式
var a = arr[i].split('=')
// 将undefined标记为true
var paramName = a[0]
var paramValue = typeof (a[1]) === 'undefined' ? true : a[1]
// 如果调用对象时要求大小写区分,可删除这两行代码
paramName = paramName.toLowerCase()
if (typeof paramValue === 'string') paramValue = paramValue.toLowerCase()
// 如果paramName以方括号结束, e.g. colors[] or colors[2]
if (paramName.match(/\[(\d+)?\]$/)) {
// 如果paramName不存在,则创建key
var key = paramName.replace(/\[(\d+)?\]/, '')
if (!obj[key]) obj[key] = []
// 如果是索引数组 e.g. colors[2]
if (paramName.match(/\[\d+\]$/)) {
// 获取索引值并在对应的位置添加值
var index = /\[(\d+)\]/.exec(paramName)[1]
obj[key][index] = paramValue
} else {
// 如果是其它的类型,也放到数组中
obj[key].push(paramValue)
} else {
// 处理字符串类型
if (!obj[paramName]) {
// 如果如果paramName不存在,则创建对象的属性
obj[paramName] = paramValue
} else if (obj[paramName] && typeof obj[paramName] === 'string') {
// 如果属性存在,并且是个字符串,那么就转换为数组
obj[paramName] = [obj[paramName]]
obj[paramName].push(paramValue)
} else {
// 如果是其它的类型,还是往数组里丢
obj[paramName].push(paramValue)
return obj
复制代码
这个函数该怎么使用呢?
直接把URL参数当成对象调用就OK咯~
以文章开篇的URL为例子
getAllUrlParams().product;
getAllUrlParams().color;
getAllUrlParams().newuser;
getAllUrlParams().NB;
getAllUrlParams('http://NaoNao.com/?NaoNao=shuai').NaoNao;
复制代码
不兼容IE的解决方案
如果我们不需要考虑IE这种妖娆贱货,以及一些非常老版本浏览器
,就用浏览器内
URLSearchParams
的接口吧。。。这个接口可以直接拿取URL内的参数
const urlParams = new URLSearchParams(window.location.search);
console.log(urlParams.has('product'));
console.log(urlParams.get('product'));
复制代码
这个接口还提供了更多成熟的方法,比如
keys()
,
Values()
,还有
entries()
,这个接口该怎么使用,直接去看官方文档就好了,用起来还是很虚浮的