vue使用jsonp请求

作者: small_zeo | 来源:发表于2021-08-03 16:19 被阅读0次

    Vue-jsonp

    用于处理 vue 发送 JSONP 请求的轻量js库。

    Install

    npm install vue-jsonp -S
    

    用法

      1. 在main.js中添加,注册为Vue的全局插件
    import { VueJsonp } from 'vue-jsonp'
    Vue.use(VueJsonp)
    

    现在你可以在Vue的组件中使用 this.$jsonp 发送 jsonp 请求了,因为Vue.use(VueJsonp)里把 $jsonp 赋给 vue 原型了:Vue.prototype.$jsonp = jsonp
    例如请求百度地图接口:

         this.$jsonp('https://apis.map.qq.com/ws/place/v1/suggestion', {
            region: "杭州",
            keyword: query,
            key: "CCWBZ-LZD6S-67COM-6GWDO-CHJ67-*****", 
            output: 'jsonp'
          }).then(res => {
            // ...
          })
    
      1. 在组件中直接使用函数:
          import { jsonp } from 'vue-jsonp'
    
          jsonp('https://apis.map.qq.com/ws/place/v1/suggestion', {
            region: "杭州",
            keyword: query,
            key: "CCWBZ-LZD6S-67COM-6GWDO-CHJ67-*****", 
            output: 'jsonp'
          }).then(res => {
            // ...
          })
    
    • 发送数据和设置查询参数 & 函数名
    // The request url will be "/some-jsonp-url?name=LancerComet&age=100&callback=jsonp_{RANDOM_STR}".
    jsonp('/some-jsonp-url', {
      name: 'LancerComet',
      age: 100
    })
    
    • 自定义查询和函数名
    // The request url will be "/some-jsonp-url?name=LancerComet&age=100&cb=jsonp_func".
    jsonp('/some-jsonp-url', {
      callbackQuery: 'cb',
      callbackName: 'jsonp_func',
      name: 'LancerComet',
      age: 100
    })
    

    部分源码分析

    image.png

    vue-jsonp是使用rollupjs作为打包工具,采用typescript语法进行开发。入口文件./lib/index.ts 核心代码也就185行代码,挺精简了。

    import Vue from 'vue'
    import { PluginObject } from 'vue/types/plugin'
    import { flatten, formatParams, randomStr } from './utils'
    
    const DEFAULT_TIMEOUT: number = 5000
    
    declare module 'vue/types/vue' {
      // tslint:disable-next-line:interface-name
      interface Vue {
        $jsonp: typeof jsonp
      }
    }
    
    /**
     * Vue JSONP.
     */
    // tslint:disable-next-line:variable-name
    const VueJsonp: PluginObject<never> = {
      install (V: typeof Vue) {
        V.prototype.$jsonp = jsonp
      }
    }
    
    
    // ....
    export {
      VueJsonp,
      jsonp
    }
    

    在 TypeScript 中制作插件需要类型声明,项目中使用TypeScript 模块补充 (module augmentation)的特性,在declare module 'vue/types/vue' 中声明了 Vue 要补充的 $jsonp 类型,注册VueJsonp插件, 将$jsonp 赋给 vue 原型,将jsonp 函数挂载到Vue.prototype.$jsonp上。

    const VueJsonp: PluginObject<never> = {
      install (V: typeof Vue) {
        V.prototype.$jsonp = jsonp
      }
    }
    

    jsonp()

    • jsonp函数就是动态创建了script标签,利用<script>的src不受同源策略约束来跨域获取数据
    • 在window对象上根据callbackName创建全局回调函数来接收数据,将请求地址拼接上请求参数,并挂载到document上,等服务端响应并返回约定的数据,返回一个promise实例, resolve(json)
    • JSONP 由两部分组成:回调函数和数据。回调函数是当响应到来时应该在页面中调用的函数。回调函数的名字一般是在请求中指定的。而数据就是传入回调函数中的 JSON 数据。
    function jsonp<T = any> (
      url: string,
      param: IJsonpParam = {},
      timeout?: number
    ): Promise<T> {
      if (typeof url !== 'string') {
        throw new Error('[Vue-jsonp] Type of param "url" is not string.')
      }
    
      if (typeof param !== 'object' || !param) {
        throw new Error('[Vue-jsonp] Invalid params, should be an object.')
      }
    
      timeout = typeof timeout === 'number'
        ? timeout
        : DEFAULT_TIMEOUT
    
      return new Promise<T>((resolve, reject) => {
        const callbackQuery = typeof param.callbackQuery === 'string'
          ? param.callbackQuery
          : 'callback'
        const callbackName = typeof param.callbackName === 'string'
          ? param.callbackName
          : 'jsonp_' + randomStr()
    
        param[callbackQuery] = callbackName
    
        // Remove callbackQuery and callbackName.
        delete param.callbackQuery
        delete param.callbackName
    
        // Convert params to querying str.
        let queryStrs: (string[])[] = []
        Object.keys(param).forEach(queryKey => {
          queryStrs = queryStrs.concat(formatParams(queryKey, param[queryKey]))
        })
    
        const queryStr = flatten(queryStrs).join('&')
    
        const onError = () => {
          removeErrorListener()
          clearTimeout(timeoutTimer)
          reject({
            status: 400,
            statusText: 'Bad Request'
          })
        }
    
        const removeErrorListener = () => {
          paddingScript.removeEventListener('error', onError)
        }
    
        const removeScript = () => {
          document.body.removeChild(paddingScript)
          delete window[callbackName]
        }
    
        // Timeout timer.
        let timeoutTimer = null
    
        // Setup timeout.
        if (timeout > -1) {
          timeoutTimer = setTimeout(() => {
            removeErrorListener()
            removeScript()
            reject({
              statusText: 'Request Timeout',
              status: 408
            })
          }, timeout)
        }
    
        // Create global function.
        window[callbackName] = (json: T) => {
          clearTimeout(timeoutTimer)
          removeErrorListener()
          removeScript()
          resolve(json)
        }
    
        // Create script element.
        const paddingScript = document.createElement('script')
    
        // Add error listener.
        paddingScript.addEventListener('error', onError)
    
        // Append to head element.
        paddingScript.src = url + (/\?/.test(url) ? '&' : '?') + queryStr
        document.body.appendChild(paddingScript)
      })
    }
    

    参考资料

    vue-jsonp - npm
    vue-jsonp - github

    相关文章

      网友评论

        本文标题:vue使用jsonp请求

        本文链接:https://www.haomeiwen.com/subject/guhevltx.html