「前端早讀君003」手把手教你實現一個通用的jsonp跨域方法

什麼是jsonp

JSONP(JSON with Padding)是JSON的一種「使用模式」,可用於解決主流瀏覽器的跨域數據訪問的問題。因爲同源策略,通常來講位於 server1.example.com 的網頁沒法與不是 server1.example.com的服務器溝通,而 HTML 的<script> 元素是一個例外。利用 <script> 元素的這個開放策略,網頁能夠獲得從其餘來源動態產生的 JSON 資料,而這種使用模式就是所謂的 JSONP。用 JSONP 抓到的資料並非 JSON,而是任意的JavaScript,用 JavaScript 直譯器執行而不是用 JSON 解析器解析。前端

以上內容來自百度,不太理解沒關係,記住最重要的一點是:經過script標籤引入的連接,接口返回的內容是被當成js代碼來進行解析的。請你們記住這一點,很重要的特性。node

建立一個簡單的jsonp服務器

下面是一個基於nodejs的簡單的服務器,若是不瞭解nodejs也不要緊,你們看下注釋,理解下做用就能夠git

const http = require('http')
const url = require('url')

const jsonpServer = http.createServer((req, res) => {
    let data = {
        status: true,
        msg: 'hello jsonp'
    }
    const body = url.parse(req.url, true)
    // jsonp請求中會包含一個callback參數,例如 http://baidu.com.js?callback=hello
    // 獲取請求的url中的callback參數的值,callback是一個函數名
    const callback = body.query.callback
    // 將對象數據轉爲字符串
    data = JSON.stringify(data)
    // 拼接成js代碼
    // 舉個例子,假設這個callback回調的名字是 test
    // 拼接完就是 test({status: true,msg: 'hello jsonp'})
    // 顯然,就是一段js代碼,做用就是執行這個函數
    const js = `${callback}(${data})`
    // 返回js代碼給客戶端
    res.end(js)
})

jsonpServer.listen('3000', (err) => {
    if (!err) {
        console.log('server is running at localhost:3000')
    }
})

建立完服務器之後,咱們接下來開始書寫一個jsonp

/**
 * @description 建立一個隨機的函數名
 * @return {string}
 */
const createCallbackName = function () {
    return `callback${(Math.random() * 1000000).toFixed(0)}`
}
/**
 * @description 插入一個script標籤
 * @param url {string}
 */
const insertScript = function (url) {
    let script = document.createElement('script')
    script.onload = script.onerror = function () {
        document.body.removeChild(script)
    }
    script.setAttribute('src', url)
    document.body.appendChild(script)
}
/**
 * @description 拼接字符串參數
 * @param url {string} url
 * @param data {object} 要拼接的query數據
 * @return url {string} 拼接完成後的新url
 */
const setQuery = function (url, data) {
    const keys = Object.keys(data)
    if (keys.length === 0) {
        return url
    } else{
        const pairs = keys.map(key => `${key}=${data[key]}`)
        url = url.includes('?') ? url : `${url}?`
        url += pairs.join('&')
        return url
    }
}
/**
 * @description jsonp函數
 * @param url {string} 請求地址
 * @param config {object} 接口配置設置
 * @return {Promise}
 */
const jsonp = function (url, config = {}) {
    let data = config.data || {}
    let timeout = config.timeout || 5000
    let timer
    const funcName = createCallbackName()
    data.callback = funcName
    return new Promise((resolve, reject) => {
        window[funcName] = function (res) {
            if (timer) {
                clearTimeout(timer)
            }
            delete window[funcName]
            resolve(res)
        }
        url = setQuery(url, data)
        timer = setTimeout(() => {
            delete window[funcName]
            reject(new Error(`fetch ${url} fail`))
        }, timeout)
        insertScript(url)
    })
}
// 使用效果
jsonp('http://localhost:3000')
    .then(res => {
        console.log(res)
    })
    .catch(err => {
        console.log(err)
    })

圖片描述

項目演示地址請查看github: jsonp

早讀君
獲取更多知識,請微信掃碼關注公衆號關注早讀君,天天早晨爲你推送前端知識,度過擠地鐵坐公交的時光。
並且不定時舉辦活動贈送書籍哦github

相關文章
相關標籤/搜索