javascript代码实例教程-分析js框架如何实现JSONP之kissy

发布时间:2019-01-22 发布网站:脚本宝典
脚本宝典收集整理的这篇文章主要介绍了javascript代码实例教程-分析js框架如何实现JSONP之kissy脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。
小宝典致力于为广大程序猿(媛)提供高品质的代码服务,请大家多多光顾小站,小宝典在此谢过。 JSONP原理简介(知晓的同学就当复习一下

&nbsp;       同策略的限制让程序员想到了利用不受同源策略影响的<script>进行跨域请求。而单纯的JSON数据仅仅只是数据,被<script>加载入页面没有任何意义,因此需要一个变量作为函数名,也就是那个“P”,然后JSON数据作为函数参数传递过来。之后当浏览器加载完成后,函数执行。因此这个函数必须是个动态创建的全局变量。而JSONP其实就是动态加载js脚本。要传递变量给后端,我们需要一个参数,常用有jsonp,jsonpCallback,callback,当然这都需要后端配合。另外,Xhr2 CORS支持跨域,可以看看这里

 

js库Ajax模块实现过程简介

     总体分为三部分,XHR对象、传送器 transport、转换器 converters。

首先是创建一个XHR对象,也可能会是一个程序员为了在各浏览器中实现XHR2大部分接口而创建的伪XHR,但不管怎么说,开始必然是先创建一个XHR对象。之后以form或script形式向服务器端发送请求,这里就有一个传送器的概念,它负责发送请求,视情况采用不同的传送器。之后我们需要将返回的数据转换为程序员所需的形式,这就需要一个转换器。

 

kissy框架相关知识(了解一下就差不多了

     util.js: kissy的一个模块,可能叫utils更加合适,它主要提供一些kissy框架常用的辅助函数。辅助函数对于一个框架而言十分重要,像each,filter,isArray这些常见的辅助函数。还有就是和这个博客相关的util.parseJson,我觉得十分重要,不知道为什么kissy 5.0官方提供的API中没有这个,但是我在gIThub的源码是找到了的,util/lib/util/json.js 这里就可以看见。下文中常用的util.mix除了很实用以外还很有趣,它可以添加白名单,被复制对象的属性在白名单内才会被复制,underscore也有个类似参数是做黑名单用的。(吾辈认为因为使用场景的不同,两种方式的效率高低也会不同,所以util这个应该是什么改进,纯粹是为了和underscore不一样(如果是我错了,请大家留言轻点教育...

    

     event-custom.js:自定义事件在jQuery这个业界巨头手中发光发热(jquery.on),kissy自然也要有。就是event-custom这个kissy模块。它用的是PubSub模式(其实就是观察者模式),一个自定义事件类型发布(Pub)给多个负责事件处理的回调函数订阅(Sub),触发事件的时候在进行回调。PubSub模式用来处理这样的异步事件,但它和异步没有关系,它是同步的,自定义事件也并不会进入js的Event Loop。node十分喜爱这个模式,events的EventEmitter对象用的就是PubSub模式,我用它做了简单的测试。

          

 

复制代码

VAR events = require("events");

var emitter = new events.EventEmitter();

var async = false;

emitter.on(&#39;eventTyPE', function(){

     console.assert(async);

});

emitter.emit('eventType'); //报错false == true

async = true;

复制代码

然后是setTimeout实现的异步

 

 

var async = false;

setTimeout(function(){

     console.assert(async); //没有报错

}, 0);

async = true;

 

     promise:kissy的PRomise模块用的是Promise/A+规范,它是对Promise/A的迭代版。promise可以让那些富含Ajax的页面拜托回调的金字塔噩梦,也可以让js程序员轻松的使用起异步来~ 具体就不在这里讲述了。

 

 

正文

     首先我们要明确jsonP实现的几个步骤,简介中也提过,首先生成URL,然后生成一个scirpt标签发送请求,接着将数据转换出来,最后回调。

     那接下来,我来一步步看看kissy jsonp实现的代码

     直接上  IO.jsonp( url, data, callback )

     我们在github找到kissy IO模块,首先是io.js,可以看见这些CommonJS风格的        

 

复制代码

var serializer = require('./io/form-serializer');

var IO = require('./io/base');

var util = require('util');

require('./io/xhr-transport');

require('./io/script-transport');

require('./io/jsonp');

require('./io/form');

require('./io/iframe-transport');

require('./io/methods');

复制代码

 

 

     可以看见xhr-transport,script-transport,iframe-transport这是在加载各种传送器,我们需要注意的是script-transport,jsonp会用到它。base.js是IO模块的基础。后面我们还会去看。

     这个API自然调用的是IO,一如jQuery.get其实就是jQuery.ajax的简写。具体看它如何执行,

              

复制代码

jsonp: function (url, data, callback) {

       if (typeof data === 'function') {

         callback = data;

         data = undefined;

       }

       return get(url, data, callback, 'jsonp')

}

复制代码

         

 

getScript: require.load

 

 

            

复制代码

function get(url, data, callback, dataType, type) {

// data 参数可省略

if (typeof data === 'function') {

    dataType = callback;

    callback = data;

    data = undefined;

}

 

return IO({

    type: type || 'get',

    url: url,

    data: data,

    complete: callback,

    dataType: dataType

});

}

复制代码

意料之中的调用IO,dataType设置为jsonp,get请求。其它参数都和一般ajax参数一致,dataType显然线索啦。特地在getScript:require.load留了心,因为getScript负责script发送工作,而jsonp它本身也是以一种getScript。

var IO = require('./base'); 那就去base.js中看看

总结:IO.js作用就是将各个js文件载入,接着给IO添加几个便捷入口,最后统一调用IO函数。

 

 

base.js

     首先我们肯定会被defaultconfig吸引目光,因为这里设置了Ajax所需的各种默认配置,HTTP请求mothods、MIME类型、编码等,一般的转换器设置也都在这个对象了。

     接着是设置配置的函数SETUPConfig(c),在我们不知道会传入什么参数前还是不看了(其实看看也可以,看看代码也能猜个八九不离十)。

     那么直接找到IO函数,参数是c。self就是IO函数的一个实例。然后给IO套上promise模式,可以用起链式操作来啦~(我也是看yiminghe菊苣的注释乐的! ‘2012-2-07 yiminghe@gmail.COM - 返回 Promise 类型对象,可以链式操作啦!’卧槽....好萌!)。self.userConfig = c;c = setUpConfig(c); 我们的参数现在传给了setConfig并生成新的参数对象。为了不中断对IO函数的分析。我们先往后分析。util.mix是将后面的对象clone到self,然后就添加了一些诸如config,transport,timeoutTimer一堆属性。再往后,声明传送器构造函数、传送器变量,然后触发start事件!这标志着IO的开始!后面接着是根据dataType选择对应的传送器并创建实例。然后是Ajax相关的Header、readyStatestatus等设置,这和jsonp没有关系。然后它用setTimeout模仿了Timeout功能。再往后也没什么了。base.js的最后我们又往IO函数添加了像setupConfig,setupTransport,getTransport这些方法,来实现config设置等功能。而我们在意的是setupTransport,传送器设置。

     现在我们回过头来看setupConfig。它的返回值是一个新的config对象。

在一开始用把参数c深度clone到defaultConfig,结合之后产生所需的config赋值给c,像这样

 

     var context = c.context;

     delete c.context;

     c = util.mix(util.clone(defaultConfig), c, {

     deep: true

     });

     c.context = context || c;

 

 

context代表的是请求后回调函数的“环境”(context傻瓜翻译)——success,error,complete。后面声明变量什么的略过,uri = c.uri,是一个包含url所有相关参数的url对象。uri.query由url中querystring的名/值对构成JSON格式对象/数组。我们排除与跨域无关的处理过程,jsonp大多都是没有的啊~ 

 

dataType = c.dataType = util.trim(dataType || '*').split(rspace);

if (!('cache' in c) && util.inArray(dataType[0], ['script', 'jsonp'])) {

c.cache = false;

}

 

 

util.trim去掉dataType前后空格,rspace = //s+/,split按空格分割dataType成子字符串数组。接着确认dataType为jsonp。c.cache标识是否是调用script或jsonp发送器,false即代表是.它的作用:url会添加uri.query._ksTS = util.now() + '_' + util.guid(); 也就是 ksTS=时间戳_全局唯一标示符,guid函数的作用就是生成一个全局唯一标示符,英文一般叫UUID或是GUID。

到这里,url处理就完成了。现在还是一个对象。

接着就start! start事件有很多事件处理器,它们在执行前都会有判断,我们手持dataType['jsonp']开始jsonp.js部分。

 

 

jsonp.js

首先需要注意的是

复制代码

IO.setupConfig({

    jsonp: 'callback',

    jsonpCallback: function () {

    // 不使用 now() ,极端情况下可能重复

    return util.guid('jsonp');

    }

});

复制代码

调用了setupConfig,显然函数参数名初始化为callback。 jsonpCallback是初始化一个全局唯一标识符的函数。后面util.guid是生成全局唯一标示符,加在'jsonp'后面作为函数名,而全局唯一可以止这个函数名和其它变量重名。后面start事件的回调函数中首先生成函数名字符串,然后纳入uri.query中c.uri.query[c.jsonp] = jsonpCallback;。

之后函数执行了jsonp常见的一系列程序。创建一个全局函数 ,绑定在全局对象 win[ jsonpCallback ],并设置请求完成后清除 delete win[ jsonpCallback ];。这里要注意的是要绑定在全局对象上,因为直接var声明的全局变量是无法清除的。而kissy在这儿用了一个小技巧,它创建一个临时的全局函数给了一个叫previous的变量,然后在请求结束后回调函数时,win[ jsonpCallback ] = previous 。如果成功加载的话,就会执行previous将所需的json格式数据保存起来。之后定义的是转换器converters.script.json。其中return response[0], 返回值在之前已经保存response = [r],[r]为全局函数的参数,转换器就这样实现了!而失败也有相对应处理机制  throw new Error('not call jsonpCallback: ' + jsonpCallback);。最后将dataType修改为 dataType[0] = 'script'; dataType[1] = 'json'; 结合注释可以轻而易举地知道,这是在给调用script发送器做准备呢。

总结:jsonp.js中定义了,添加URL中全局函数参数名/值,全局函数定义、调用、删除,script到json转换器实现这一系列的行为,并绑定在了start事件。

 

我们回到base.js,下面就是准备发送了。

TransportConstructor = transports[c.dataType[0]] || transports['*'];

transport = new TransportConstructor(self);

之前jsonp.js中已经知道了最后会是script传送器负责发送请求。其实jsonp和getScript发送请求的原理都是一样的,动态加载js脚本,所以最后统一使用script传送器一点不奇怪。而IO.setupTransport工作早在IO.js加载*-transport.js的时候就已经完成了,像这样IO.setupTransport('script', ScriptTransport);这样IO.setupTransport('iframe', IframeTransport);(不要问我怎么知道的,到了发送器部分了,我当然会打开看看啦~)。看一下setupTransport啦

dataType = c.dataType = util.trim(dataType || '*').split(rspace);

if (!('cache' in c) && util.inArray(dataType[0], ['script', 'jsonp'])) {

c.cache = false;

}

 

 

之后显然就是打开script-transport.js

 

script-transport.js

打开首先看见的依旧是初始化。然后是判断浏览器是否支持XHR2 CORS,支持的话就更换发送器,Ajax请求。这个我们这次不谈,Ajax模块有些复杂,详细解读的话篇幅太长。之后就是传统的script发送了,为script发送器添加send,_callback,abort方法。send定义io.script为require.load(用法可参照jQuery.load),url为io._getUrlForSend()。base.js在注释中有提到这个方法在methods.js中。我们待会儿再去看。之后是编码设置,还有各种状态的回调传参。_callback将io.script删除,防止重复调用,然后根据状态对io._ioReady传递对应的参数。abort顾名思义,中断函数。

总结:script-transport.js中定义了script发送器的发送和回调。

 

回过头去看methods.js

methods.js

开头是处理响应数据函数handleResponseData。jsonp要注意的是这些

复制代码

    var prevType = dataType[0];

    for (var i = 1; i < dataType.length; i++) {

        type = dataType[i];

        var converter = converts[prevType] && converts[prevType][type];

        if (!converter) {

            throw new Error('no covert for ' + prevType + ' => ' + type);

        }

        responseData = converter(responseData);

        prevType = type;

    }

    io.responseData = responseData;

复制代码

 

 

jsonp.js最后设置 dataType[0] = 'script'; dataType[1] = 'json'; 在这里转换器选定converters.script.json,然后执行将接收的json数据给io.responseData。到了这里,jsonp的数据处理就算彻底完成了。

和script没有关联。script的转换器也已经在jsonp.js中实现了。util.extend实现IO继承promise对象,并添加一些方法到原型。大多是对header的处理,而我们关心的只有_ioReady, _getUrlForSend。回调函数_ioReady根据传入的状态码和状态文本(success,error)做出响应的处理,成功就调用handleResponseData,失败就抛出。这里有个setTimeout,因为throw e会让后面的代码无法继续执行,所以需要通过异步解决这个问题。Ajax Timeout的实现也是这样的!)。_getUrlForSend就是将url对象转换为url字符串!url在这里出现啦!(kissy APIDoc中没有url.stringify,其实url.stringify = url.format,不要怀疑!我特地看了util源码的!)。

总结:methods.js负责处理响应数据以及url生成。

 

最后回到base.js

yiminghe菊苣在后面自立flag(// flag as sendingf! l! a! g! 看见顿时笑)!然后ransport.send()!后面的错误处理在前面已经提过了,和_ioReady一样的疑问。End.

 

下面是kissy io模块的简单实例,实现了对flickr API的调用(国内外都喜欢用这个测)

  <script>

          (function() {

               var APIurl = 'http://api.flickr.com/services/feeds/photos_public.gne';

               var onSuccess = function (data) {

                    console.LOG('get data:', data);

               }

               require(['io'], function(IO){

                    IO({

                         url: APIurl,

                         type: 'GET',

                         dataType: 'jsonp',

                         jsonp: 'jsoncallback',

                         jsonpCallback: 'ctest',

                         data: {

                              tags: "water",

                             tagmode: "any",

                             format: "json"

                         },

                         complete: onSuccess

                    });

               });

          })()

     </script>

复制代码

 

 

然后是这个程序实现的全过程示意图,让大家回顾一下前面的内容,梳理一下思路。

 

 

分析总结

     优点:Chinese English与中文相结合,让注释的可读性大大增强!代码结构十分清晰,前后阅读十分顺畅,基本没有什么阻碍。在jsonp的实现上有优先尝试CORS,回调函数的处理对于我来说也是个小亮点。CommonJS规范模块加载与众不同,也让模块开发变得十分算是间接体现了阿里前端菊苣们对nodejs的强力推崇。

     缺点:类似util.parseJson、url.stringify这些在API文档未提及,导致需要去其它模块查看源代码,增加了分析耗时。这算是一个缺点吧!希望kissy的菊苣们能完善一些文档。然后就是有爱的英文注释啦~ 虽然很好玩~ 不过这也给了我给菊苣提意见的途径!(我找到了明显的单词错误,已反馈)

 

最后的碎碎念

 

     这次分析的深度肯定不够,因为吾辈也就是js入门生一枚。所以希望大家发现什么写的不对,一定要告诉我啊!如果菊苣路过提点意见,那是再好不过。也希望我的博客能给想要阅读框架源码却又迟迟不敢向前的同学们,一点思路和信心(我这么菜都能看!还敢发博客!)。最后,有些同学猜的没错,这会是系列文!

觉得可用,就经常来吧! 脚本宝典 欢迎评论哦! js脚本,巧夺天工,精雕玉琢。小宝典献丑了!

脚本宝典总结

以上是脚本宝典为你收集整理的javascript代码实例教程-分析js框架如何实现JSONP之kissy全部内容,希望文章能够帮你解决javascript代码实例教程-分析js框架如何实现JSONP之kissy所遇到的问题。

如果觉得脚本宝典网站内容还不错,欢迎将脚本宝典推荐好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。