【二】JSONP原理及promise封装

关于专题【vue开发音乐App】 在我们想要获取其他网站数据的时候,浏览器的同源策略(Same origin policy)会禁止此项行为,但有时不得不实现这一操作,就会涉及跨域的问题。解决跨域也就成了前端必须掌握的技能,其中JSONP就是一种解决该问题的好方法。 一、JSONP跨域原理 由于script标签不受浏览器同源策略的影响,允许跨域引用资源。因此可以通过动态创建script标签,然后利用src属性进行跨域,这就是JSONP跨域的基本原理。 JSONP通过script标签的src属性发送请求,src请求地址与普通ajax请求地址的不同之处在于其后面会加一段类似“callback=a”的字符串,服务端接收到这段加了特殊后缀的url后就会用a方法包裹(浏览器所请求的)目标数据(返回给前端)。此时,前端并没有声明a方法,所以在script发送请求之前,应该在window上注册a方法,以在接收到后端数据时用此方法解析数据。 // 1\. 定义一个回调函数a用来接收返回的数据 function a(data) { // 处理数据的代码 console.log(data) } // 2\. 动态创建一个script标签,并且告诉后端回调函数名叫a let body = document.getElementsByTagName('body')[0] let script = document.createElement('script') script.src = 'https://wy310.cn/get_auth_name?callback=a' body.appendChild(script) // 3\. 通过script.src请求'https://wy310.cn/get_auth_name?callback=a' // 4\. 后端识别该URL格式并处理该请求,然后返回a({"name": "大海"})给浏览器 // 5\. 浏览器在接收到a({"name": "大海"})之后立即执行,也就是执行a方法获得后端返回的数据,完成一次跨域请求 二、封装promise型JSONP 实际开发中我们选择github上的第三方JSOP库(具体实现可以查阅index.js),先来看看API: jsonp(url, opts, fn) url (String) url to fetch opts (Object), optional param (String) name of the query string parameter to specify the callback (defaults to callback) timeout (Number) how long after a timeout error is emitted. 0 to disable (defaults to 60000) prefix (String) prefix for the global callback functions that handle jsonp responses (defaults to __jp) name (String) name of the global callback functions that handle jsonp responses (defaults to prefix + incremented counter) fn callback 调用jsonp(url, opts, fn),在回调函数fn中就可以拿到目标数据data。但现在采用ES6开发很少使用回调函数的形式,而是采用promise,下面看看怎么将其封装成promise风格: 1.安装jsonp 在vue项目中引入jsonp,项目根目录下执行命令: cnpm i jsonp -S 2.promise封装 像jsonp这种经常使用的工具,应该单独抽象出来,便于以后在项目开发过程中调用。所以在src/common/js中新建jsonp.js: // 引入上一步从github安装的jsonp, // 即“原始jsonp”(与下面自己封装的“jsonp”区分开) import originJSONP from 'jsonp' // param1:我们希望url仅仅是一个纯净的地址 // param2:后面的各种参数通过data传入,然后拼接在一起 // param3:option对应原始jsonp的第二个参数:opts export default function jsonp (url, data, option) { // 拼接url时判断是否已有问号 url += (url.indexOf('?') > -1) ? '&' : '?' + param(data) return new Promise((resolve, reject) => { originJSONP(url, option, (err, data) => { // 如果没错误,就resolve(data) if (!err) { resolve(data) } else { reject(err) } }) }) } // 将data(参数对象)封装到url里面 function param (data) { let url = '' for (let i in data) { let value = data[i] !== undefined ? data[i] : '' // url拼接参数,参数之间用&隔开 url += `&${i}=${encodeURIComponent(value)}` } // 如果url有data,将第一个"&"删掉 return url ? url.substring(1) : '' } 3.测试 测试之前,提一个“配置别名”的知识点,build/webpack.base.conf.js文件内的alias意为别名,通过配置alias,可以在今后使用import x from '../../x'时省去计算层级的烦恼: module.exports = { ... resolve: { alias: { '@': resolve('src'), 'api': resolve('src/api'), 'common': resolve('src/common'), 'components': resolve('src/components') } }, ... } // 配置别名common前 import jsonp from '../common/js/jsonp' // 配置别名common后 import jsonp from 'common/js/jsonp' 尝试使用上面封装的jsonp获取腾讯网页版QQ音乐的推荐歌单数据,src/api/recommend.js: import jsonp from 'common/js/jsonp' export function getRecommend () { const url = 'https://c.y.qq.com/musichall/fcgi-bin/fcg_yqqhomepagerecommend.fcg' const data = { g_tk: 1928093487, inCharset: 'utf-8', outCharset: 'utf-8', notice: 0, format: 'jsonp', platform: 'h5', uin: 0, needNewCode: 1 } // 此'jsonpCallback'就是上文说的'a' const options = { param: 'jsonpCallback' } return jsonp(url, data, options) } 在的created钩子里调用getRecommend(),将其中的slider数据渲染到轮播图组件中去,src/components/recommend/recommend.vue: import { getRecommend } from 'api/recommend' export default { data () { return { recommends: [] } }, created () { this._getRecommend() }, methods: { _getRecommend () { // 调用promise风格的getRecommend() getRecommend().then(res => { if (res.code === 0) { this.recommends = res.data.slider } }) } } } 成功获取推荐歌单数据:

本文章由javascript技术分享原创和收集

发表评论 (审核通过后显示评论):