打造属于前端的Uri解析器
今天和大家一起讨论一下如何打造一个属于前端的url参数解析器。如果你是一个Web开发工程师,如果你了解过后端开发语言,譬如:PHP,Java等,那么你对下面的代码应该不会陌生:
$kw = $_GET['keyword']; // PHP
String kw = request.getParameter("keyword"); // JSP
对于后端语言,通过上面的代码我们可以很方便的获取到一个url请求中的参数值。但是,当我们在一个Web前端工程中需要使用到url参数的时候,我们熟悉的JavaScript却没有提供类似方便的使用方法。那么,我们前端开发工程师该如何去获取url参数呢?方法挺多的,咱一个一个来看。
使用字符串的split方法
基本思路:首先我们通过参数连接符 & 将整个search串split成类似 key=value 的子串数组,然后遍历得到的数组元素,根据 = 运算符将每个子串拆分为 key 和 value ,最后将结果存储到一个json对象中,就得到我们的结果了。取值操作只需要从最终得到的json对象中取响应key对应的值就好了。原理很简单,我们来看下具体代码:
function query(search) {
var s = search || location.search,
str = s && /^\?/.test(s) ? s.slice(1) : s,
r = {},
kvs = str.split("&");
for (var i = 0, len = kvs.length; i < len; i++) {
var kv = kvs[i].split("=");
r[kv[0]] = kv[1];
}
return r;
}
// use
query("a=1&b=2&c=3"); // {"a":"1","b":"2","c":"3"}
当然,如果想更直接一点可以写成下面这种:
function query(search, key) {
var s = search || location.search,
str = s && /^\?/.test(s) ? s.slice(1) : s,
r = {},
kvs = str.split("&");
for (var i = 0, len = kvs.length; i < len; i++) {
var kv = kvs[i].split("=");
r[kv[0]] = kv[1];
}
return r[key];
}
// use
query("a=1&b=2&c=3", "a"); //
不过,显然第二种方式不好,每取一次值都得去跑一遍循环,太浪费资源。那么如果非得这么用,有没有什么更简洁一点的方式呢?答案是肯定的。
使用字符串的match方法
基本思路:使用match方法,从目标字符串中匹配与key对应的参数的值并返回。使用正则表达式匹配,可以省去循环,可以说是第二种split用法的升级版(仅从省代码考虑,性能恐怕未必),具体代码如下:
function query(search, key) {
var reg = new RegExp("(^|\\?|\\&)" + key + "=([^&$]*)", ""),
match = null;
match = search.match(reg);
return match && match[2] ? match[2] : undefined;
}
// use
query("a=1&b=2&c=3", "a"); //
query("a=1&b&c=3", "b"); // undefined
从代码实现来看,好像是比使用split来实现简单了很多,从测试结果看,二者效果完全一致,看来是没什么问题。接下来我们看一个和第一种split实现结果一致的另一种实现方法。
使用正则表达式的exec方法
基本思路:思路和split实现的思路大同小异,只是我们不在根据特殊符号进行字符串拆分,转而使用正则表达式对特征字符串进行匹配,再从匹配结果中获取我们需要的内容。代码如下:
function query(search){
var search = search || location.search,
reg = /([?&])?([^=]+?)(?=(=|&|$))(([^&$]*))?/g,
r = {},
match = null;
while(match = reg.exec(search)){
r[match[2]] = match[4].replace(/^=/, "");
}
return r;
}
// use
query("a=1&b=2&c=3"); // {a: "1", b: "2", c: "3"}
query("a=1&b&c=2"); // {a: "1", b: undefined, c: "2"}
到此,看起来一切都很顺利,也没出现什么问题。然而,事实真的如此吗?
潜藏的那些坑
首先,我们得考虑一个问题,大多真实情况下我们都是从浏览器地址栏直接拿search串来获取参数值,并不是像上面我们测试写的那样手动准备 a=1&b=2&c=3 ,而我们又知道浏览器自身有对中文和一些特殊符号进行encode的功能,那么问题来了,当出现这种情况的时候,我们将会得到什么呢?
// 准备一个中文串(a=中国&b=China)
// 将中文encode一下(a=%E4%B8%AD%E5%9B%BD&b=China)
var p = query("a=%E4%B8%AD%E5%9B%BD&b=China"); // {a: "%E4%B8%AD%E5%9B%BD", b: "China"}
console.log(p.a); // %E4%B8%AD%E5%9B%BD(看不懂啊!看不懂!)
这样肯定不行,我们得想办法搞定它,以exec的方式为例,我们代码稍作调整:
function query(search){
var search = search || location.search,
reg = /([?&])?([^=]+?)(?=(=|&|$))(([^&$]*))?/g,
r = {},
match = null;
while(match = reg.exec(search)){
r[match[2]] = decodeURIComponent(match[4]).replace(/^=/, "");
}
return r;
}
现在再来一遍:
var p = query("a=%E4%B8%AD%E5%9B%BD&b=China"); // {a: "中国", b: "China"}
这就对了,终于能看懂了!
然后,我们再来看一种情况。以登录为例,通常我们登录成功后希望能跳转回到来源页面。为了达到这个目的,一般我们会为登录页面添加一个redirect的url参数,形如:
http://www.xxx.com/login.jsp?a=1&b=2&redirect=http://www.yyy.cn/index.html
我们先来试下,看看上面那个链接中我们的参数能不能正常解析:
// 查询串为:a=1&b=2&redirect=http://www.yyy.cn/index.html
query('a=1&b=2&redirect=http://www.yyy.cn/index.html');
// {a: "1", b: "2", redirect: "http://www.yyy.cn/index.html"}
OK,执行正常,没有问题,接下来我们稍微对我们的需求做点加工,我希望登录成功后调回 http://www.yyy.cn/index.html?sub=search&keyword=中国&lang=cn ,那么我们的查询串就应该是下面这个结果:
a=1&b=2&redirect=http://www.yyy.cn/index.html?sub=search&keyword=中国&lang=cn
按照顺理成章的逻辑,似乎没有问题吧?我们再来执行一下我们的query方法:
query("a=1&b=2&redirect=http://www.yyy.cn/index.html?sub=search&keyword=中国&lang=cn");
/*
* {
* a: "1",
* b: "2",
* redirect: "http://www.yyy.cn/index.html?sub=search",
* keyword: "中国",
* lang: "cn"
* }
*/
发现问题了吗?对!咱的跳转链接的被拆分成几个url参数了,显然咱达不到跳转回来源链接的目的了。那这个问题如何解决呢?从我们方法实现的角度去考虑,暂时还想不到解决办法,翻查了一下淘宝KISSY框架的Uri.Query类,简单的测试了下,结果和上面是一样的。倒是从使用咱方法的角度去着手,有一个解决方法——将redirect链接做一次encode,如下:
query("a=1&b=2&redirect=" + encodeURIComponent("http://www.yyy.cn/index.html?sub=search&keyword=中国&lang=cn"));
/*
* {
* a: "1",
* b: "2",
* redirect: "http://www.yyy.cn/index.html?sub=search&keyword=中国&lang=cn"
* }
*/
OK,结果正常了!由此,也可以映射一个问题,当我们在地址栏传递数据时,还是尽可能的encode好后再使用,这样可以避免一些不必要的麻烦。
封装
笔者喜欢把玩正则表达式(虽然还玩得不好),就以exec方式为例,对url参数解析的功能做了一下简单的封装,欢迎读者朋友批评指正:
(function(window, undefined){
var URI = {};
URI.query = function(search){
var s = search || location.search,
reg = /([?&])?([^=]+?)(?=(=|&|$))(([^&$]*))?/g,
r = {},
match = null,
total = 0;
var _remove = function(key) {
// r[key] = undefined;
delete r[key];
total--;
};
while(match = reg.exec(s)){
var val = decodeURIComponent(match[4]).replace(/^=/, "");
if (match[2].indexOf('[]') !== -1) {
var k = match[2].replace('[]', '');
if (typeof r[k] === 'undefined') {
r[k] = [val];
total++;
} else {
r[k].push(val);
}
} else {
r[match[2]] = val;
total++;
}
}
return {
get: function(key) {
return r[key];
},
keys: function() {
var keys = [];
if ('keys' in Object) {
keys = Object.keys(r);
} else {
for (var key in r) {
keys.push(key);
}
}
return keys;
},
remove: _remove,
count: function() {
return total;
}
};
};
window.Uri = window.Uri || URI;
})(window);
用法当然很简单:
var q = Uri.query('a=person&b=人&c=people&d=中国人');
q.keys(); // ["a", "b", "c", "d"]
q.get('d'); // 中国人
q.count(); //
至此,我们今天讨论的话题就完成了。以上只是一个雏形,有兴趣的朋友可以进行扩展优化。欢迎大家发表各自的意见,多多交流,共同进步!
作者博客:百码山庄
打造属于前端的Uri解析器的更多相关文章
- XML技术之SAX解析器
1.解析XML文件有三种解析方法:DOM SAX DOM4J. 2.首先SAX解析技术只能读取XML文档中的数据信息,不能对其文档中的数据进行添加,删除,修改操作:这就是SAX解析技术的一个缺陷. 3 ...
- SpringMVC入门案例及请求流程图(关于处理器或视图解析器或处理器映射器等的初步配置)
SpringMVC简介:SpringMVC也叫Spring Web mvc,属于表现层的框架.Spring MVC是Spring框架的一部分,是在Spring3.0后发布的 Spring结构图 Spr ...
- atitit.java解析sql语言解析器解释器的实现
atitit.java解析sql语言解析器解释器的实现 1. 解析sql的本质:实现一个4gl dsl编程语言的编译器 1 2. 解析sql的主要的流程,词法分析,而后进行语法分析,语义分析,构建sq ...
- android XML解析器全解案例
1.使用pull解析 package com.example.myxml; import java.io.InputStream; import java.util.ArrayList; import ...
- dom4j解析器 基于dom4j的xpath技术 简单工厂设计模式 分层结构设计思想 SAX解析器 DOM编程
*1 dom4j解析器 1)CRUD的含义:CreateReadUpdateDelete增删查改 2)XML解析器有二类,分别是DOM和SAX(simple Api for xml). ...
- SAXParser 解析器和 XMLEventReader(读取XML文档)
import javax.xml.parsers.ParserConfigurationException;import javax.xml.parsers.SAXParser;import java ...
- 一步一步自定义SpringMVC参数解析器
随心所欲,自定义参数解析器绑定数据. 题图:from Zoommy 干货 SpringMVC解析器用于解析request请求参数并绑定数据到Controller的入参上. 自定义一个参数解析器需要实现 ...
- 使用Vitamio打造自己的Android万能播放器(5)——在线播放(播放优酷视频)
前言 为了保证每周一篇的进度,又由于Vitamio新版本没有发布, 决定推迟本地播放的一些功能(截图.视频时间.尺寸等),跳过直接写在线播放部分的章节.从Vitamio的介绍可以看得出,其支持http ...
- 使用Vitamio打造自己的Android万能播放器(2)—— 手势控制亮度、音量、缩放
前言 本章继续完善播放相关播放器的核心功能,为后续扩展打好基础. 声明 欢迎转载,但请保留文章原始出处:) 博客园:http://www.cnblogs.com 农民伯伯: http://ove ...
随机推荐
- hadoop-2.0.0-cdh4.2.1源码编译总结
经过一个星期多的努力,这两个包的编译工作总算告一段落. 首先看一下这一篇文章: 在eclipse下编译hadoop2.0源码 http://www.cnblogs.com/meibenjin/arch ...
- Dynamics CRM 2011编程系列(60):JS编程之CRUD辅助类(JQuery版)
今天给大家分享一个JQuery版的REST辅助类,在一年前我分享过一个只能在IE环境下运行的REST辅助类:<JS编程之实体CRUD辅助类 >.为什么要推出JQuery版的CRUD辅助类呢 ...
- 怒刷BZOJ记录(二)1038~10xx
我实在是太弱了...不滚粗只能刷BZOJ了...这里来记录每天刷了什么题吧. 2015-8-13: 正式开始! 1030[JSOI2007]文本生成器 | ...
- Solr 多核(MultiCore)配置
Solr Multicore意义 Solr Multicore 是 solr 1.3 的新特性.其目的一个solr实例,可以有多个搜索应用.< xmlnamespace prefi ...
- 构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(6)-Unity 2.x依赖注入by运行时注入[附源码]
原文:构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(6)-Unity 2.x依赖注入by运行时注入[附源码] Unity 2.x依赖注入(控制反转)IOC,对 ...
- [转] 用管道获得shell 命令的输出
用管道: 通过fgets(buf, n, ptr)buf就可以得到命令“ps -ef"一样的信息, 读帮助”man popen": char *cmd = "ps -ef ...
- 详细分享UICollectionView的自定义布局(瀑布流, 线性, 圆形…)
前言: 本篇文章不是分享collectionView的详细使用教程, 而是属于比较’高级’的collectionView使用技巧, 阅读之前, 我想你已经很熟悉collectionView的基本使用, ...
- Android性能优化典范 - 第5季
这是Android性能优化典范第5季的课程学习笔记,拖拖拉拉很久,记录分享给大家,请多多包涵担待指正!文章共10个段落,涉及的内容有:多线程并发的性能问题,介绍了AsyncTask,HandlerTh ...
- Android制作粒子爆炸特效
简介 最近在闲逛的时候,发现了一款粒子爆炸特效的控件,觉得比较有意思,效果也不错. 但是代码不好扩展,也就是说如果要提供不同的爆炸效果,需要修改的地方比较多.于是我对源代码进行了一些重构,将爆炸流程和 ...
- Eclipse中文语言包下载
Kepler .Juno . Indigo语言包: http://www.eclipse.org/babel/downloads.php 其他低版本Eclipse语言包下载: http://archi ...