是什么?
比如 URL1 :domain?a=1&b=2 ,URL2:domain?a=1&c=3 ,合并之后是 domain?a=1&b=2&c=3 。就是URL1的参数和URL2参数取出来,去重,再合并在一起,生成一个新的URL。
为什么?
「为什么」其实也就是场景了。比如a页面里有m个链接,点击这些链接的时候,希望a页面的URL参数追加到这些链接里。目的是把上游的URL参数传递下去,做到一些线索或者数据的追踪。
一些知识点
以下列举本方案用到一些的知识点,简单做些说明。
string相关函数
1. substring(start, stop)
该函数截取字符串的一个子段,两个参数的含义都是指字符串的下标,但返回的是下标之间的子串。第二个参数可选,不写默认到结尾。比如
'abcd'.substring(1, 2) // b
'abcd'.substring(2) // cd
2. substr(start, length)
第一个参数也是下标,第二个参数指想要的长度。第二个可选,不写也是到结尾。效果同上。
'abcd'.substr(1, 2) // bc
'abcd'.substr(2) // cd
Array相关函数
1. [].filter
作用是对数组进行过滤,接受一个回调函数,回调函数返回false的话,过滤掉该项。通常如下使用:
// 过滤掉空字符串
['a', '', 'c', '', 'e'].filter(it => it) // ['a', 'c', 'e']
// 过滤掉奇数
[1, 2, 3, 4, 5].filter(it => it%2 === 0) // [2, 4]
2. [].map
遍历数组,返回一个新的数组,通常使用如下:
[1, 3, 5, 7].map(it => it*2) // [2, 6, 10 ,14]
'a=1&b=2&c=3'.split('&') // ['a=1', 'b=2', 'c=3']
.map(it => it.split('=')) // [['a', '1'], ['b', '2'], ['c', 3]]
3. [].reduce
遍历数组,返回你想要的任何数据类型,可以这样用:
// 求和
[1, 2, 3].reduce((o, i) => o+i, 0)
// 分组
[1, 3, 4, 8, 9].reduce((o, i) => {
i % 2 === 0 ? o.even.push(i) : o.odd.push(i)
return o
}, {odd: [], even: []})
// 代码对数组奇偶分组,结果如下
// {"odd":[1, 3, 9], "even":[4, 8]}
这个函数真的是一个好东西,特别棒,有时间可以多品品,对以后写代码绝对受益。
object相关函数
1. Object.keys
获取对象的所有keys,如下:
Object.keys({a:1, b:2}) // ['a', 'b']
2. Object.entries
获取对象的属性与对应的值组成的数组。如下:
Object.entries({a:1, b:2}) // [['a', 1], ['b', 2]]
该需求会使用到以上这些知识点,而且用到恰到好处。怎么做呢?我们一步步做,且看分享:
如何做?
1. 获取URL的query部分
主要逻辑:
- 没有query,也就是没有?号,直接返回空
- 有hash且在query之后,返回两者直接的部分
- 其余,返回query之后的部分。
function findUrlQuery (url) {
const queryIndex = url.indexOf('?') + 1
if (queryIndex < 1) return '' // 没有query直接返回空
const hashIndex = url.indexOf('#')
if (hashIndex > 0 && hashIndex > queryIndex) { // 如果有hash且在query之后
return url.substring(queryIndex, hashIndex) // 获取?号与#之间的字符串。eg: a?m=1#cc
} else { // 没有hash或者hash在query之前直接取query之后的部分
return url.substring(queryIndex)
}
}
注释已经写的很清楚,如何获取URL里的query部分,获取之后我们就可以解析各个参数了。
2. 解析query为对象
主要逻辑:
- 获取query
- 以&符分割为数组
- 过滤掉空的
- map成键值对
- 过滤掉空的建或者空的值
- 转化为对象
function parseUrlQuery (url) {
if (!url) return {}
return findUrlQuery(url) // 找到URL的query部分
.split('&') // 按照&符分割。eg:a=1&b=2&c=3&&m=&
.filter(it => it) // 过滤掉分割出来的空字符串
.map(it => it.split('=')) // 再次分割。上面有例子
.filter(kv => kv[0] && kv[1]) // 过滤掉key或者value为空的query
.reduce((o, kv) => { // 转化为对象
o[kv[0]] = kv[1] // 2. 给对象加属性及值
return o // 3. 放入下一次迭代
}, {}) // 1. 传入一个{}没有属性的对象。
}
这是相当清晰且简便的解析query的方法了,而且代码读起来朗朗上口。这是函数式编程的魅力。
3. 合并两个URL的query部分
主要逻辑:
- 获取目标URL的所有keys
- 获取源URL的所有键值对
- 过滤掉已经在目标URL里存在的key
- 把剩余的源键值对转化为query
- 如果目标URL存在query
- 直接把源query追加到目标URL的?号之后。
- 如果目标URL里只有hash,追加到hash之前。
- 除上,直接追加
/**
* 合并两个URL的query部分
* @param {string} target 被合并的URL
* @param {string} source 待合并的URL
*/
function mergeUrlQuery (target, source) {
if (!source) return target
if (!target) return ''
const targetQueryKeys = Object.keys(parseUrlQuery(target)) // 获取目标URL所有key
const sourceQuery = Object.entries(parseUrlQuery(source)) // 获取源URL所有键值对
.filter(kv => !targetQueryKeys.includes(kv[0])) // 过滤掉与目标URL重复的key
.map(kv => kv.join('=')) // 重新把键值join在一起
.join('&') // 再次join
if (!sourceQuery) return target
const queryIndex = target.indexOf('?') + 1
if (queryIndex > 0) { // 如果目标URL里已有query,直接在?号后加新的query
return target.substr(0, queryIndex) + sourceQuery + '&' + target.substr(queryIndex)
} else { // 如果没有query
const hashIndex = target.indexOf('#')
if (hashIndex > 0) { // 且有hash。那就在hash之前追加新的query
return target.substr(0, hashIndex) + '?' + sourceQuery + target.substr(hashIndex)
} else { // 除上,直接追加query
return target + '?' + sourceQuery
}
}
}
注释清晰明了。代码也是清晰明了。需要解释什么吗?不需要了,上面的知识点能够理解,这些代码就能理解。
总结
通过这篇文章,你至少了解
如何获取URL的参数?
这个是再常用不过的功能了,我这里的实现可以说是相当简单,可以细品。
还有另一个
如果把对象转换为URL参数?
例子中有,就在第三个函数里,相当简洁,可以细品。
当然,那些知识点还有整个思路也是相当受用的。