首先让我们来先写一个server
var http = require('http')
var fs = require('fs')
var url = require('url')
var port = process.argv[2]
if(!port){
console.log('请指定端口号好不啦?\nnode server.js 8888 这样不会吗?')
process.exit(1)
}
var server = http.createServer(function(request, response){
var parsedUrl = url.parse(request.url, true)
var pathWithQuery = request.url
var queryString = ''
if(pathWithQuery.indexOf('?') >= 0){ queryString = pathWithQuery.substring(pathWithQuery.indexOf('?')) }
var path = parsedUrl.pathname
var query = parsedUrl.query
var method = request.method
/******** 从这里开始看,上面不要看 ************/
console.log('包含查询字符串的路径\n' + pathWithQuery)
if(path === '/'){
var string = fs.readFileSync('./index.html','utf8')
response.setHeader('Content-Type', 'text/html;charset=utf-8')
response.write(string)
response.end()
}else if(path === '/style.css'){
var string = fs.readFileSync('./style.css','utf8')
response.setHeader('Content-Type', 'text/html;charset=utf-8')
response.write(string)
response.end()
}else if(path === '/main.js'){
var string = fs.readFileSync('./main.js','utf8')
response.setHeader('Content-Type','application/javascript')
response.write(string)
response.end()
}else{
response.statusCode = 404
response.setHeader('Content-Type','text/html;charset=utf-8')
response.write('找不到对应的路径,需要先自行修改')
response.end()
}
/******** 代码结束,下面不要看 ************/
})
server.listen(port)
console.log('监听 ' + port + ' 成功\n请用在空中转体720度然后用电饭煲打开 http://localhost:' + port)
html代码
<p>您的余额是<span id="amount">&&amount&&</span></p>
<button id='click'>Click</button>
<script src="./main.js"></script>
main.js代码
click.addEventListener('click',function(e){
amount.innerText = amount.innerText - 1
})
返回html会同时返回里面内联的main.js文件和style.css
ASYUJJ.png
当我们点击一下余额数字就会减一,但是如果刷新页面,数字就会重新回到100,所以我需要一个数据库来存放余额
ASYhQI.png
我们给余额添加一个数据库,首先新建一个bd文件,然后把100写进去,然后修改server.js
if(path === '/'){
var string = fs.readFileSync('./index.html','utf8')
var amount = fs.readFileSync('./bd','utf-8') //读取bd文件里面的amount值
string = string.replace('&&amount&&',amount) //用amount来替换html里面的&&amount&&
response.setHeader('Content-Type', 'text/html;charset=utf-8')
response.write(string)
response.end()
}
<p>您的余额是<span id="amount">&&amount&&</span></p>
接着我们要告诉服务器,每当我点一下button你都去拿bd里的数字,那么我们就要发一个post请求了,那我们用form来发一个请求吧
<form action="/pay" method="post">
<input type="submit" value="付款">
</form>
然后server处理这个请求
else if (path === "/pay" && method.toUpperCase() === 'POST') {
var amount = fs.readFileSync('./bd', 'utf-8')
var newAmount = amount - 1
if (Math.random() > 0.5) {
fs.writeFileSync('./bd', newAmount)
response.write('success')
} else {
response.write('fail')
}
response.end()
}
form表单提交,每次点击后success和fail刷新后显示,想要看到余额变化需要返回再刷新,于是我们可以添加一个iframe来承载success和fail,让他在iframe里面刷新,而不是当前页面
<form action="/pay" method="post" target="result">
<input type="submit" value="付款">
</form>
<iframe src="about:blank" name="result" frameborder="0"></iframe>
ASwkss.png
但是这样体验还是很差,用户仍需要刷新才能看到余额变化,而且还要加一个iframe,那么能不能不用iframe,发请求除了用form还能用什么呢?css的link,a标签,img标签和script标签都可以发请求。
先尝试动态创建一个图片来发请求。
main.js 代码
click.addEventListener('click',function(e){
let image = document.createElement('img')
image.src = '/pay'
})
这种方法没用办法去post,一定会去get.而且我们还不知道他成功没成功,我们可以通过image.onload()和image.onerror()方法,并且在方法内部判断返回的状态码来确定成功和失败
main.js代码
click.addEventListener('click',function(e){
let image = document.createElement('img')
image.src = '/pay'
image.onload = function(){
alert('打钱成功')
amount.innerText = amount.innerText - 1
}
image.onerror = function(){
alert('打钱失败')
}
})
server代码
else if (path === "/pay") {
var amount = fs.readFileSync('./bd', 'utf-8')
var newAmount = amount - 1
if (Math.random() > 0.5) {
fs.writeFileSync('./bd', newAmount)
response.setHeader('Content-Type','image/jpg')
response.statusCode =200
response.write(fs.readFileSync('./dog.jpg'))
} else {
response.statusCode =400
response.write('fail')
}
response.end()
}
现在实现了无刷新的更新,但是没有办法post。我们再来尝试一下script发请求
main.js代码
click.addEventListener('click',function(e){
let script = document.createElement('script')
script.src = '/pay'
document.body.appendChild(script)
script.onload = function(){
alert('success')
}
script.onerror = function(){
alert('fail')
}
})
server代码
else if (path === "/pay") {
var amount = fs.readFileSync('./bd', 'utf-8')
var newAmount = amount - 1
fs.writeFileSync('./bd', newAmount)
response.setHeader('Content-Type', 'application/javascript')
response.statusCode = 200
response.write('alert("我是pay")')
response.end()
}
但是每次都会在html里面创建一个会执行的script
ASBHRU.png
我们可以把onload()的内容写到server里面
response.write(`
alert("success")
window.location.reload()
`)
这样服务器返回了一个在浏览器里执行的代码,但是刷新体验不好,那么我们可以在服务器上改amount
response.write(`
alert("success")
amount.innerText = amount.innerText - 1
`)
每次打钱都会新建一个script,我们当然要删掉他们
script.onload = function(e){
e.currentTarget.remove()
}
script.onerror = function(e){
alert('fail')
e.currentTarget.remove()
}
这里有一个重要的概念,就是script的src可以请求不同的域名,我们做两个网站,让他们彼此交流 frank.com:8001和jack.com:8002
click.addEventListener('click',function(e){
let script = document.createElement('script')
script.src = 'http://jack.com:8002/pay'
document.body.appendChild(script)
script.onload = function(e){
e.currentTarget.remove()
}
script.onerror = function(e){
alert('fail')
e.currentTarget.remove()
}
})
ASs6G6.png
jack.com操作它的数据库打钱
ASy9J0.png
但是这里有一个问题,就是jack.com的程序员需要对frank.com的页面细节了解的很彻底,有很大的耦合性
else if (path === "/pay") {
var amount = fs.readFileSync('./bd', 'utf-8')
var newAmount = amount - 1
fs.writeFileSync('./bd', newAmount)
response.setHeader('Content-Type', 'application/javascript')
response.statusCode = 200
response.write(`
${query.callbackName}.call(undefined,'success')
`)
response.end()
}
window.yyy = function(result){
alert(`我得到的结果是${result}`)
}
click.addEventListener('click',function(e){
let script = document.createElement('script')
script.src = 'http://jack.com:8002/pay?callbackName=yyy'
document.body.appendChild(script)
script.onload = function(e){
e.currentTarget.remove()
}
script.onerror = function(e){
alert('fail')
e.currentTarget.remove()
}
})
AS6sgK.png
我们把回调函数的名字写在callbackName里面,服务器通过query.callbackName.call()来调用前端写好的函数,如果把'success'换成JSON那么就是JSONP了!
response.write(`
${query.callbackName}.call(undefined,{
"success":true,
"left":${newAmount}
})
`) // JSON + Padding 叫做JSONP
xxx一般用一个随机数代替
let script = document.createElement('script')
let functionName = 'frank'+parseInt(Math.random()*1000000,10)
window[functionName] = function(result){
if(result === 'success'){
amount.innerText = amount.innerText - 1
}else{
}
}
当我调用完这个函数后,就删除这个函数
script.onload = function(e){
e.currentTarget.remove()
delete window[functionName]
}
script.onerror = function(e){
alert('fail')
e.currentTarget.remove()
delete window[functionName]
}
其实jQuery已经封装好了
click.addEventListener("click", function(e) {
// 使用jQuery
$.ajax({
url: "http://jack.com:8002/pay",
jsonp: "callback",
dataType: "jsonp",
success: function( response ) {
if(response === 'success') {
amount.innerText = amount.innerText - 1
}
}
});
问:JSONP为什么不支持POST?
答:因为JSONP是通过动态创建script来实现的,动态创建script的时候只能用get没有办法用post
网友评论