JSONP是什么

前端操作数据库

首先,我们试着通过前端来操作一个数据库。
数据库是什么?只要能长久的存数据,就是数据库。文件系统是一种数据库。
首先我们来做一个简单的加法运算:
打开我们自己写的server
自己写server的方法
Node.js脚本

if (path === '/') {  // 如果用户请求的是 / 路径
        var string = fs.readFileSync('./index.html')  // 就读取 index.html 的内容
        response.setHeader('Content-Type', 'text/html;charset=utf-8')  // 设置响应头 Content-Type
        response.end(string)   // 设置响应消息体
    }else if (path === '/style.css') {   // 如果用户请求的是 /style.css 路径
        var string = fs.readFileSync('./style.css')
        response.setHeader('Content-Type', 'text/css')
        response.end(string)
    }else if (path === '/main.js') {  // 如果用户请求的是 /main.js 路径
        var string = fs.readFileSync('./main.js')
        response.setHeader('Content-Type', 'application/javascript')
        response.end(string)
    }else {  // 如果上面都不是用户请求的路径
        response.statusCode = 404
        response.setHeader('Content-Type', 'text/html;charset=utf-8')  // 设置响应头 Content-Type
        response.end('找不到对应的路径,你需要自行修改 index.js')
    }

编辑index.html

<title>首页</title>
<link rel="stylesheet" href="/style.css">

<h5>您的账户余额是<span id="amount">100</span></h5>
<button id="button">付款1块钱</button>
<script>
    button.addEventListener('click',(e) => {
        amount.innerText = amount.innerText - 1
    })
</script>

访问server给出的地址得到如下页面


页面1.JPG

每次点击按钮,余额减1。
但是这只是改变了页面上的显示,数据没有长久的保存下来,刷新页面,余额又变回100。为了长久地保存数据,我们需要使用数据库。

进入server所在的目录(我们所有的操作都在这个安全的目录里进行),touch db,创建一个文件db,内容为100,这个db就是我们的数据库,里面存着我们的余额。

将HTML中固定的余额100换成一个占位符,我这里用&&&amount&&&代替:

<h5>您的账户余额是<span id="amount">&&&amount&&&</span></h5>

让server可以读取到数据库,并将数据放到页面中

var amount = fs.readFileSync('./db', 'utf8')
string = string.replace('&&&amount&&&',amount)

现在,当我们点击按钮时,不能再自己改页面上的余额,而是应该告诉服务器去改变数据库,然后再刷新页面,让服务器告诉我们最新的结果。

这就需要向服务器发起POST请求。

我们用form发起请求,请求路径到/pay

<title>首页</title>
<link rel="stylesheet" href="/style.css">

<h5>您的账户余额是<span id="amount">&&&amount&&&</span></h5>

<form action="/pay" method="post">
    <input type="submit" value="付款1块钱">
</form>

让server对/pay做出响应:

else if (path === '/pay' && method.toUpperCase() === 'POST'){
        var amount = fs.readFileSync('./db','utf8')
        var newAmount = amount - 1
        fs.writeFileSync('./db',newAmount)
        response.end('success')
    } //从数据库拿到数据,减一,再存到数据库,返回success

重启服务器,刷新页面。(每次修改server.js都要进行这一步)
现在,我们每次点击按钮,都会在/pay页面返回一个success,返回上一个页面再刷新,可以看到余额少了1块。这次便是数据库中的余额少了1,我们实现了对数据库的操作。

SRJ方案

我们已经实现了对数据库的操作,但是用户体验并不好,每次付款后还要再返回上一页面并刷新才能看到余额,而且form每次提交时都刷新当前页面。所以我们需要优化用户体验。

  1. 我们可以用一个iframe来避免刷新当前页面,在iframe里刷新:
<title>首页</title>
<link rel="stylesheet" href="/style.css">

<h5>您的账户余额是<span id="amount">&&&amount&&&</span></h5>

<form action="/pay" method="post" target="result">
    <input type="submit" value="付款1块钱">
</form>
<iframe name="result" src="about:blank" frameborder="0" height="200"></iframe>
  1. 我们可以通过动态创建一个script发请求:
<title>首页</title>
<link rel="stylesheet" href="/style.css">

<h5>您的账户余额是<span id="amount">&&&amount&&&</span></h5>
<button id="button">打1块钱</button>
<script>
    button.addEventListener('click',function(){
        let script = document.createElement('script')
        script.src = '/pay'
        document.body.appendChild(script)
        amount.innerText = amount.innerText - 1 //局部刷新页面,优化体验
        script.onload = function(e){
            e.currentTarget.remove()
        } //每次点击按钮页面中都会出现一个script,移除它
    })
</script>

这样每次点击按钮的时候会动态创建一个script,src指向响应方(/pay);
由于页面出现指向/pay的script,浏览器先执行/pay里的响应内容,/pay里可以直接alert("success"),所以不需要在html里再写script.onload = function(){alert('success')}(但还是要监听它,移除每次点击在页面里产生的script)。
动态创建script的时候只能发送GET请求,不能发送POST请求。
修改server.js:

else if (path === '/pay'){
        var amount = fs.readFileSync('./db','utf8')
        var newAmount = amount - 1
        fs.writeFileSync('./db',newAmount)
        response.setHeader('Content-Type', 'application/javascript')
        response.end('alert("success")')
    }

现在每次点击按钮都会弹出success,并且余额减1。
也可以在服务器里修改页面中的余额:

else if (path === '/pay'){
        var amount = fs.readFileSync('./db','utf8')
        var newAmount = amount - 1
        fs.writeFileSync('./db',newAmount)
        response.setHeader('Content-Type', 'application/javascript')
        response.end('amount.innerText = amount.innerText - 1') //不再弹出success,直接让余额减1。作为javascript执行,一是上面Content-Type规定,另外是由一个script标签引到这里来的。}

现在每次点击按钮,余额减1,页面没有刷新。用户感觉不到这是在操作远程,用户体验已经得到了很好的提升。

这种服务器返回JavaScript的方案就叫做SRJ方案(server rendered javascript),是Ajax出现之前的无刷新局部更新页面内容方案。

请求另一个网站的script

修改hosts:
127.0.0.1 frank.com
127.0.0.1 jack.com
现在在本地访问frank.com和jack.com都是访问127.0.0.1
开两个server,分别给端口号8001,8002
PORT=8001 node server.js
PORT=8002 node server.js
分别访问 frank.com:8001 和 jack.com:8002(这是两个网站,只不过它们的源代码是一样的)
我们可以让 frank.com的前端去访问jack.com的后端:

script.src = 'http://jack.com:8002/pay'

JSONP

上面我们用frank.com的前端去访问jack.com的后端,依然可以使页面上的余额发生改变。这是因为jack.com的后端有这样的代码:

response.end('amount.innerText = amount.innerText - 1')

这就要求jack.com的后端程序员对frank.com的页面细节了解很清楚,前后端代码无法分离。(耦合)
我们不需要让jack.com的后端程序员了解frank.com的页面,只需要让他调用由frank.com前端传给他的一个函数,作为响应,函数由frank.com的前端写在页面里。
frank.com的前端:

window.xxx = function(result){
    alert('得到的结果是${result}')
}

jack.com的后端:

response.end(
    xxx.call(undefined, 'success')
) //调用frank.com前端给的函数xxx,并传参success

这样,jack.com的后端程序员不需要直到任何页面细节,只需调用函数xxx,成功给参数success,失败给参数fail。 (解耦)

现在不能让jack.com的后端和frank.com的前端有任何耦合,把函数名xxx作为参数传过去:

script.src = 'http://jack.com:8002/pay?callbackName=xxx'
response.end(
    `${query.callbackName}.call(undefined, 'success')` //这就是JSONP JSON(这里是个字符串success,特殊情况这个字符串由JSON替代)+padding
)

jack.com的后端调用函数,传了一个参数’success‘。特殊情况这个参数是JSON,这种方案就叫JSONP。(动态标签跨域请求)

文字叙述JSONP

请求方:一个网站(frank.com)的前端 (浏览器)
响应方:另一个网站(jack.com)的后端 (服务器)

  1. 请求方创建script,src指向响应方。同时传一个查询参数?callbackName=xxx
  2. 响应方根据查询参数callbackName,构造形如xxx.call(undefined, '你要的数据')xxx('你要的数据')这样的响应
  3. 浏览器收到响应,就会执行xxx.call(undefined, '你要的数据')
  4. 那么请求方就直到了他要的数据

这就是JSONP。

约定
  1. callbackName 用 callback
  2. xxx(函数名)用随机数(不会污染全局变量)
button.addEventListener('click',function(){
        let script = document.createElement('script')
        let functionName = 'jack' + parseInt(Math.random() * 100000000, 10) //取随机数
        window[functionName] = function(result){
            if(result === 'success'){
            amount.innerText = amount.innerText - 1
            }
        }
        script.src = 'http://jack.com:8002/pay?callback=' + functionName //随机数作函数名
        document.body.appendChild(script)
        script.onload = function(e){
            e.currentTarget.remove()
            delete window[functionName] //随机函数名用完就删掉
        }
        script.onerror = function(e){
            alert('fali')
            e.currentTarget.remove()
            delete window[functionName] //随机函数名用完就删掉
        }
 })
jQuery API
  $.ajax({
        url: "http://jack.com:8002/pay",
        dataType: "jsonp",
        success: function(response){
            if(response === 'success'){
                amount.innerText = amount.innerText - 1
            }
        }
    })
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,470评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,393评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,577评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,176评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,189评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,155评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,041评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,903评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,319评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,539评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,703评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,417评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,013评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,664评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,818评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,711评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,601评论 2 353

推荐阅读更多精彩内容