首页 >公司新闻

聊聊 JSONP 的 P

发布时间:2013-08-13     发布者:本地    

JSONP 是 JSON With Padding 的缩写。一个典型的 JSONP 请求如下
<script src="http://server.example.com/users/abc?callback=parseResponse"></script>
返回值为
parseResponse({"name": "Foo", "id": 1234, "rank": 7});

其中 parseResponse 是由 callback 参数指定的,也就是 JSONP 中的 P(adding)。这个 P 可不简单,第一个想到这么用的人,绝对是个大牛。因为有了这个 P,解决了好些难题:
1. 回调问题。只要发出 script 请求,自动等待 callback 回调就好,并且精准得能秒杀 `onload` 等方案。
2. 跨域问题。因为浏览器的同源策略,跨域一直是前端的难题。跨域与安全息息相关,JSONP 没有破坏安全性,同时却具备了全面跨域能力。巧妙而实用。
3. 协作问题。后端专注与数据处理与输出,前端专注与数据展现。除了 JSON 数据本身的格式约定,其他约定仅仅需要一个 P 就好。
好了,再说下去,估计很多人会不乐意了。JSONP 的劣势也很明显:
1. 无中间状态。不像 XHR(XMLHttpRequest) 那样,有丰富的 status 、readyState 等属性,可以很精准地知道各种状态。
2. 只能 GET。不像 XHR 那样,可以全方位支持 GET、POST、PUT、DELETE。
3. 只能异步。XHR 是可以同步的,估计很多人没用过,同步其实是 XHR 的默认行为(省略 open 的第三个参数就代表同步)。
本文不聊其他的,专注聊聊那个P。
回到开头那个例子:
http://server.example.com/users/abc?callback=parseResponse
parseResponse 是一个全局函数。当页面上很多 JSONP 请求时,就有可能出现很多全局函数。虽然全局变量并不一定是恶魔,但不好好管理的全局变量真的有可能成为恶魔。管理的方案之一是给 P 引入命名空间:
http://server.example.com/users/abc?callback=JSONP.parseResponse
这样,所有 JSONP 的回调处理函数就都有了根,可以避免与其他函数的潜在冲突。
可是,这还得绞尽脑汁给 JSONP.xxx 命名,经常大家会想到一块,比如 xxx 经常会不谋而合:
callback
handle
parseData
_Callback
jsonp

这种不谋而合,虽然代表着英雄所见略同式的欣慰,但更多的是:我靠,你怎么也取这个名字?这可怎么解决呢?来试试 jQuery 类库。jQuery 的优秀绝对不仅仅是给你一个 $ 符号,jQuery 在很多方面考虑得非常周到:
$.getJSON("http://server.example.com/users/abc?callback=?",
function(data) {
  console.log(data.name); // => Foo
});

很神奇的,只需要指定下 callback=? 就好,再也不用为命名犯愁了。原理是 jQuery 帮你随机命名了一个当前页面唯一的全局 callback 函数,并且与 success 调用串接了起来。这样,就可以像使用 XHR 一样直接在回调中拿到 data 数据。jQuery 再好,也抵挡不住 winter 等神人的不喜欢。这时,我们可以用各种 loader 来实现,比如:

seajs.use("http://server.example.com/users/abc?callback=define",
function(data) {
  console.log(data.name); // => Foo
});

通过 RequireJS、SeaJS、OzJS 等 XMD loader,我们也不再需要为回调函数名烦恼,只要给 callback 传入 define 就好。如果你有全站使用 loader 方案,更进一步,可以约定没有传 callback 参数时,默认就是 define. 这意味着对于静态 JSON 数据来说,可以静态化:

seajs.use("http://server.example.com/users/abc.js",
function(data) {
  console.log(data.name); // => Foo
});

这样,服务端的 JSONP 数据直接可以是静态文件:

define({"name": "Foo", "id": 1234, "rank": 7});

这带来的好处看起来很小,实际上很有用。因为这意味着数据可以提前处理好,可以提前缓存,甚至可以静态化存储到 CDN 上,可以缓存到用户浏览器上。静态化的好处,谁用谁知道。用 define 固定住 callback 名,配合浏览器端的 loader,我们可以传输各种数据:
define([ ... ]); // 数组格式,比如搜索提示数据
define("...."); // 直接字符串,比如模板
这个方案,鄙人很无耻,还申请过一个专利,叫 JSONM 协议,M 是 Module(模块)。不过以上用法不在专利保护内容里,大家可以放心大胆用。

P 还可以摇身一变,从 JSONP 变成 XHR,比如
http://server.example.com/users/abc?type=xhr
通过 type 参数,服务端就会直接返回 JSON 数据,这样就可用在 XHR 调用中。Google 的 API 中有类似的设计,很灵活。
JSONP 的这个 P,到此就谈完了。除了这个 P,更有挑战的是 JSON 数据本身的设计。这里面学问大了去,等有时间再细说。

上一篇:什么是 JSONP 下一篇:解析xml时如何防范dtd xsd访问外网

相关新闻