关于专题【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://blog.wy310.cn/get_auth_name?callback=a'
body.appendChild(script)

// 3. 通过script.src请求'https://blog.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
        }
      })
    }
  }
}

成功获取推荐歌单数据:

0
浏览:2,514

0 条评论

发表评论

电子邮件地址不会被公开。

你必须允许浏览器启用JavaScript才能看到验证码

Scroll Up