引言
各位前端小伙伴们,今天咱们来聊聊一个开发中让人头疼的老朋友------跨域 !😩 别怕,这次咱们不硬刚,要用一种"曲线救国"的方式来解决它!💪 没错,我说的就是 JSONP (JSON with Padding)!
为什么会有跨域这玩意?🤔
在开始我们的"救国"之路前,先来简单回顾一下为什么会有跨域这回事。
-
安全第一!🔒: 想象一下,如果你随便打开一个网页,它就可以随意访问你浏览过的其他网站的信息,这得多可怕!😱 为了保护我们(前端)的隐私,浏览器就搞了个叫做 同源策略(Same-Origin Policy) 的机制。简单来说,同源策略 就是指浏览器只允许来自 相同源(Origin) 的 Web 应用之间进行资源共享。
-
什么是 "同源"?🤔: 所谓 "同源" 就是指 协议(Protocol)+ 域名(Domain)+ 端口(Port) 这三者都相同,比如:
http://127.0.0.1:5502
和http://127.0.0.1:5502
是同源的。✅http://127.0.0.1:5502
和https://127.0.0.1:5502
不是同源的。❌ (协议不同)http://127.0.0.1:5502
和http://localhost:5502
不是同源的。❌ (域名不同,127.0.0.1
和localhost
虽然都指向本地,但浏览器会认为它们是不同的域名)http://127.0.0.1:5502
和http://127.0.0.1:3000
不是同源的。❌ (端口不同)
-
前端: 我们平时在浏览器里看到的页面,都是由 HTML、CSS 和 JavaScript 构成的。
-
后端: 后端嘛,就是那些运行在服务器上的代码,通常用 Node.js、Java、Python 等来实现。
-
前后端分离: 现在主流的前后端分离开发模式下,前端和后端通常运行在不同的服务器,因此跨域问题几乎是每天都要面对的!😩
CORS?No!JSONP走起!🏃♀️
面对跨域,通常我们首先会想到 CORS (Cross-Origin Resource Sharing) 跨域资源共享 。这是一种更标准的解决方法,需要后端配合设置 Access-Control-Allow-Origin
等响应头。 但是,如果我们后端暂时不支持 CORS 怎么办呢?别急!JSONP 闪亮登场!✨
JSONP 的核心思想:
就是利用 <script>
标签 的一个特性:script
标签的 src
属性不受同源策略的限制,可以跨域请求资源。
-
"曲线救国"的逻辑:
- 我们把要请求的数据,伪装成 JavaScript 代码。
- 让浏览器用
script
标签去请求它。 - 浏览器会像执行 JavaScript 代码一样执行它,从而获得数据。
JSONP 是啥? 🤔
JSONP 全称是 JSON with Padding,中文名直译过来就是 "带填充的 JSON"。
- "JSON" :还是我们熟悉的 JSON 数据,只不过它被"包裹"起来了。
- "Padding" : 这里的 "Padding" 指的是在 JSON 数据外面加上 一个函数调用 ,像这样:
callback({"name": "小明", "age": 18})
。
这样一来,浏览器会把服务器返回的 callback(JSON数据)
当成 JavaScript 代码执行,就能够成功获取数据啦!🥳
动手实践!写一个 JSONP 请求!👩💻
别光说不练!下面我们来实操一下,用 JS 封装一个 JSONP 请求函数,让你亲身体验一下 JSONP 的魔力!✨
步骤一:后端准备:
- 首先,你要有一个 Node.js 环境。没有的话,快去官网下载一个:nodejs.org/
- 运行下面的代码之前,记得在你的终端安装一下 nodemon,它可以帮你自动重启服务器,让你在开发时更加方便!😎 运行
npm install -g nodemon
安装吧! - 然后,在你的项目目录下,创建一个名为
server.js
的文件,把下面的代码粘进去:
javascript
// server.js
const http = require('http')
const users = [
{
id: 1,
name: 'zs'
},
{
id: 2,
name: 'ls'
},
{
id: 3,
name: 'ww'
},
]
const server = http.createServer((req, res) => {
res.end('callback(' + JSON.stringify(users) + ')')
})
server.listen(3000, () => {
console.log('server is running at port 3000')
})
-
记得在你的终端中运行
nodemon server.js
启动你的服务器!- 你的后端服务器就准备好啦! 😎 它会在
http://localhost:3000/
返回一个包含用户信息的 JSONP 响应,别忘了!
- 你的后端服务器就准备好啦! 😎 它会在
步骤二:前端代码!
-
创建一个
index.html
文件,内容如下:html<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <ul id="list"> </ul> <script> let list = document.getElementById('list'); let jsonp = (url, callback) => { let oScript = document.createElement('script'); oScript.src = url; document.body.appendChild(oScript); window.callback = callback; } jsonp('http://localhost:3000/', (data) => { list.innerHTML = data.map(user => ` <li>${user.name} </li>`).join('') }); </script> <!-- <script src="http://localhost:3000/"></script> --> <!-- 小贴士:这里曾经尝试直接引入 <script src="http://localhost:3000/"> 来请 求数据,但是发现它并没有按照我们的预期执行,这是因为直接引入并不会执行返回的代码 ,所以最后我们还是采用动态添加 script 的方式 --> </body> </html>
展示一下页面效果:
-
代码解释一下:
-
我们首先获取页面上的
<ul>
标签,用于显示数据。 -
我们定义了一个
jsonp
函数,它接受一个url
和一个callback
作为参数。 -
在
jsonp
函数内部:- 我们创建一个
<script>
标签,设置其src
为传入的url
。 - 我们将
callback
函数赋值给window.callback
,这很重要!为什么?稍后会解释! - 我们把这个
<script>
标签添加到页面,发起请求。
- 我们创建一个
-
最后,我们调用
jsonp
函数,并传入请求地址和处理数据的回调函数。
-
步骤三:浏览器运行!
- 用浏览器打开你的
index.html
文件,你应该能看到成功获取的数据,并且页面上展示了用户列表!🥳
你可能会好奇:
-
为什么需要
window.callback
?- 这里用到了一个关键点:服务器返回的是
callback(JSON数据)
格式的 JS 代码。 - 浏览器执行这段代码的时候需要找到全局的
callback
函数,才能正确调用它,并把数据传进去。所以,必须把回调函数挂载到window
对象上,让它成为全局函数。
- 这里用到了一个关键点:服务器返回的是
-
callback
可以换成其他名字吗?-
可以!
callback
只是一个约定俗成的名字,你可以使用其他名字,比如handleData
。 -
但是,你要确保 前端和服务端使用相同的名字,即:
- 前端要定义:
window.handleData = (data) => {...}
- 后端要返回:
handleData(JSON数据)
- 前端要定义:
-
JSONP 的限制 🤨
虽然 JSONP 很好用,但它也有一些局限性:
- 只支持 GET 请求: 因为
<script>
标签只能发起 GET 请求。 - 安全性问题: 由于 JSONP 实际上是执行服务器返回的 JavaScript 代码,如果服务器返回的脚本包含恶意代码,可能会导致安全问题。
- 处理错误困难: JSONP 请求的错误处理不如 XMLHttpRequest 和 fetch 方便。
总结 🎉
JSONP 是一种巧妙的跨域解决方案,它利用 <script>
标签的特性,通过"曲线救国"的方式实现了跨域数据请求。虽然有一些局限性,但在某些特定场景下,它仍然是一个很有用的技术。
希望这篇文章能帮助你更好地理解 JSONP 的原理和使用方法!如果你还有任何疑问,欢迎在评论区留言!我们一起学习,一起进步!💪
温馨提示:
别忘了下载全局 nodemon
,它可以让你的开发更轻松!npm install -g nodemon
快去安装吧!