图表的能力,跨域访谈和防盗链基本原理

日期:2019-10-04编辑作者:美高梅老虎机平台

跨域访谈和防盗链基本原理(二)

2015/10/18 · HTML5 · 跨域, 防盗链

原著出处: 童燕群 (@童燕群)   

JavaScript 深远之类数组对象与 arguments

2017/05/27 · JavaScript · arguments

初稿出处: 冴羽   

Web质量优化种类(2):分析页面绘制时间

2015/04/15 · CSS, HTML5, JavaScript · 属性优化

本文由 伯乐在线 - 刘健超-J.c 翻译,sunbiaobiao 校稿。未经许可,幸免转载!
匈牙利语出处:www.deanhume.com。应接出席翻译组。

不久前,笔者加入了在London实行的脸谱移动开采者大会。在那天时期,有多数的攀谈,但真正让自己关注的是一场关于质量的,名称叫“让m.facebook.com更快”的交流会,它的大旨是关于推特(Twitter)如何不断大力改正网页质量和从当中得出的阅历。

Facebook付出团队是使用Chrome Cannry来测量试验网页CSS性能的。Google Chrome Canary具备Chrome的新颖天性,并同意试用一些快要成为Chrome标准版本的,可行的新型性子。牵挂到Chrome Canary作为二个为开采者和尝鲜者特意规划的“预览版”,所以不时会因Chrome开辟公司的高速迭代而招致有个别B UG。固然如此,它依旧有局地很棒的开辟者工具帮衬你测量检验网页性能

美高梅老虎机平台 1

在那篇作品里,小编出示怎样行使Chrome Canary的开拓者工具去稳定你的CSS中的一片段,这一部分CSS恐怕会产生页面滚动缓慢和震慑页面包车型大巴绘图时间。当浏览器加载和制图页面时,为了“绘制”并让内容体今后显示屏上,需求遍历全部可知成分。由于那依赖于布局和复杂性的CSS,你只怕会发觉绘制时间会很短。那会导致网页看起来忽动忽停和响应一点也不快。这种缓慢滚动也称为jank(jank是Android系统的一个专门的事业术语,指的是显示器上朗朗上口动态画面中断的卡顿现象)。在运动器材上滚动页面时,浏览器会极力地绘制复杂的CSS,那时这种情形越来越精通。

即使页面包车型大巴加载时间非常快,也仍旧值得去斟酌页面的绘图时间。不相同器具对CSS属性有着不雷同的反射,但好歹,能拉长品质总是一件很好的事。为了扩充测量试验,首先得去Google Chrome网址下载Chrome Canary。一旦设置到位,就足以张开你想测量检验的网页。HTML5 Rocks网址里有二个很好的案例网址,大家应用它来表明高耗电CSS属性的操作,会增添页面包车型客车绘图时间。

美高梅老虎机平台 2

倘使您张开到这些网页,按下F12,会弹出Chrome的开采者工具。然后在开采者工具的平底左边点击设置按键,开启测量检验页面渲染质量的装置。

美高梅老虎机平台 3

点击后会显示三个同意你转移设置的调整板。

美高梅老虎机平台 4

因为大家要测量试验页面包车型地铁渲染质量,所以选取“Enable continuous page repainting(页面持续重新绘制)“和 “Show FPS meter(显示FPS仪表)”**。即使您关闭设置面板,查看你的网页,你应当会看出上面包车型客车图样在页面右上角。

美高梅老虎机平台 5

该表展现以皮秒为单位的此时此刻页面绘制所需时日,而左边彰显了近期图表的微小与最大值。别的,也显得了近年80帧的树状图。那些图形的强劲之处是它不断试图重新绘制页面,使得页面好疑似首先次加载。这允许你准确定位因CSS影响的绘图难点,而不用每一次重复加载页面。无论你的改观是或不是发生影响,树状图都会不停监测。

若是我们详细查看这一个页面包车型客车HTML和CSS,你会看出里边三个div增加了一部分CSS效果。

美高梅老虎机平台 6

那个div有border-radius(圆角)和投影属性。当移除box-shadow属性,再观望FPS meter在绘制时间的转移。

美高梅老虎机平台 7

哇!正如你从图片可知到,页面绘制时间有三个令人关心的调换。通过不难地将border-radius属性移除,就能够作证这一个改动能让页面包车型大巴绘图时间明显降低。当你更新或退换CSS品质时,那个图形就立刻下落。在同八个因素上还要选用box-shadowborder-radius,会促成比较重的绘图担当,那是因为浏览器不可能为之做出优化。假使有贰个因素要求频仍的再度绘制,你应当在创立网页时时刻记住这一点。

那是贰个很好的,在Google IO 网站上的摄像,它更加尖锐地演说绘制时间,并介绍部分削减网页“jank(卡顿)”的本事。

想更上一层楼深造绘制时间的优化,看看这一个链接。

祝测验开心!

打赏援助小编翻译越多好小说,感谢!

打赏译者

戏说HTML5

2015/12/23 · HTML5 · HTML5

初稿出处: 木的树的博客   

借使有非技术职员问您,HTML5是哪些,你会怎么应答?

 

新的HTML规范。。。

给浏览器提供了牛逼本事,干以前无法干的事。。。(确切地说应该是给浏览器规定了重重新的接口规范,须求浏览器达成牛逼的职能。。。 这里谢谢红枫一叶)

给浏览器暴光了过多新的接口。。。

加了大多新的功效。。。

问的人实在并不掌握她想问的确实难题,回答的人相似掌握,但又就好像少了点什么。牛逼的技术、新的接口、炫彩的功能,首先应对的人温馨正是晕晕乎乎。什么是HTML、什么是CSS、什么是DOM、什么是JavaScript,超越58%的前端开采每一日都在用那一个,但少之甚少会有人去看法一下他们中间的关系。

率先,HTML的完备是超文本标志语言,是一种标识方式的计算机语言。将这种标记语言给特地的剖判器,就能够深入分析出确定的分界面效果。浏览器就是特别分析这种标志语言的剖析器。大家说她最终的功能是在显示屏上海展览中心示出特定的分界面,那么浏览器肯定要把一个个的记号转变到内部的一种数据结构,这种数据结构就是DOM成分。譬喻,叁个<a>标签在浏览器内部的世界中便是贰个HTMLAnchorElement类型的一个实例。

一个HTML文件就好比用超文本标志语言写的一篇小说,小说平时是有结构的,在浏览器眼里它正是DOM。DOM描述了一雨后玉兰片档案的次序化的节点树。(但那时的DOM依旧存在于浏览器内部是C++语言编写的)

 

乘势历史的前行,当大伙儿不在满足轻便的彰显文本,对于一些文本需求非常重申可能给增多特殊格式的要求,逐步的冒了出来。面对民众需求调控呈现效果的急需,最早想到的也最简易的艺术正是加标记。加一些体裁调控的标识。那时候就出现了像<font>、<center>这种样式调节的符号。然而那样一来,全部的符号就能够分成两大类:一种是说本身是怎样,一种是说自家怎么显得。那还不是大难题,标志不难,不过浏览器要剖析标识可就不那么粗略了。想一想,那样干的话DOM也就要分成两大类,一类属于描述成分的DOM节点,一类属于描述显示效果的DOM节点。多少个DOM节点大概意味着多个要素,也恐怕是意味着一种突显效果。怎么看都认为别扭呀。

最终大家决定吐弃样式标签,给成分标签增多贰个style本性,style本性调节作而成分的体裁(最先的样式申明语法肯定很简短)。原来的体制标签的性状,今后变为了体制性情的语法,样式标志产生了体制天性。这样逻辑上就清楚多了。那么难点来了:

  • 一篇作品如果修辞过多,必然会孳生读者的嫌恶。假设把成分和突显效果都坐落多个文本中,必然不平价阅读。
  • 设若有10个因素都亟需贰个功用,是或不是要把贰个style重复写14遍呢
  • 父成分的装置作用对子成分有未有震慑,让不让拼爹
  • 。。。。。。。。。

看似的主题材料早晚有广大,所以出来了CSS,层叠样式表,带来了css准则、css选拔器、css表明、css属性等,那样以来就消除了以上痛点。标志语言这层消除了,可是浏览器就不能够干坐着游戏了,必然得提供匡助。所以浏览器来分析一个静态html文件时,遍历整个html文书档案生成DOM树,当全体样式能源加载完毕后,浏览器开头创设显示树。显示树正是依据一七种css申明,经历了层叠之后,来规定三个无不DOM元素应该怎么绘制。那时候其实页面上还尚无体现另外部面,渲染树也是浏览器内部存款和储蓄器里面包车型地铁一种数据结构。渲染树完结之后,起首进行布局,那就好比已经知道三个矩形的宽高,今后要在画布量一量该画在哪,具体占多大地点。这些历程完了将来正是绘制的进程,然后我们便有了大家看来的突显分界面了。

给标志加点效果的主题材料消除了,历史的车轮又起来向上了。渐渐的群众不再满足轻易的显得效果,大家愿意来点交互。那年写HTML的比很多并不懂软件开采,开玩笑嘛,我一写活动页的您让作者用C++?C++干这件事的确是高射炮打蚊子——黄钟毁弃。那正规军不屑干的事就付出行击队吧,那时候网景公司支付出了JavaScript语言,那时候的JavaScript根本未有今日如此火,一土鳖脚本语言,哪像先天那般牛逼哄哄统一宇宙。

JavaScript本是运作在浏览器的言语,HTML文本是静态的,不容许让JavaScript修改静态文件,但能够跟浏览器内部打交道。不过那个时候的DOM并非明日的DOM,他们是C++对象,要么把JavaScript调换来C++指令操作这个C++对象,要么把那个C++对象包装成JavaScript原生对象。历史选取了前面一个,那时候也就标记着今世DOM的正式落地。可是历史一时候会冒出退化,历史上海市总会并发几个奇葩,举例IE,IE奇葩他全家,包蕴Edge!

马克思是个江湖骗子,但恩Gus是个好同志。自然辩证法与历史唯物主义是好东西。从历史的角度大家得以见见。CSS、DOM、JavaScript的产出于发展最后的源头都在HTML,超文本标志语言。大家对web的必要最后都聚焦在HTML上。所以一旦历史产生新的急需,最终的浮动都首首发出在HTML规范上。

当交互性不能在知足大家需要时,web迎来了新的须要:webapp。要迎合新的要求,首先要改动的正是HTML规范,今年已部分HTML4.0,已经心有余而力不足满意大家日益增加的急需,所以HTML5迎着历史的急需,经过四年的辛苦努力,终于在二〇一四年行业内部杀青!HTML5必将是要加入新标签,然对于价值观HTML来讲,HTML5算是一个背叛。全数在此以前的本子对于JavaScript接口的描述都只是三言两语,重要篇幅都用于定义标志,与JavaScript相关内容一律交由DOM标准去定义。而HTML5专门的工作,则围绕着哪些使用新扩充标志定义了大气JavaScript API(所以里面有一部分API是与DOM重叠,定义了浏览器应该帮衬的DOM扩充,因此能够看到HTML5也必然不是HTML的最终版)。

 

后记—— 本文只是贰个生人以线性的不二法门来读书HTML的发展史,但历史更疑似晴空上赫然的晴朗霹雳,一声过后,有人哀嚎遍野,有人高歌入云。以此回忆曾红极一时的Silverlight、Flex,以此回想广大学一年级线开辟者活到老学到老的意志力精神、曾经开销的生命力、曾经逝去的年青。

1 赞 1 收藏 评论

美高梅老虎机平台 8

websocket索求其与话音、图片的力量

2015/12/26 · JavaScript · 3 评论 · websocket

初稿出处: AlloyTeam   

谈到websocket想比大家不会面生,固然不熟悉的话也没涉及,一句话回顾

“WebSocket protocol 是HTML5一种新的契约。它完成了浏览器与服务器全双工通讯”

WebSocket相相比较古板那么些服务器推手艺差不离好了太多,我们可以挥手向comet和长轮询那些能力说拜拜啦,庆幸大家生存在享有HTML5的一时~

那篇文章大家将分三有的查究websocket

首先是websocket的大范围使用,其次是截然自个儿创设服务器端websocket,最后是不能缺少介绍利用websocket制作的八个demo,传输图片和在线语音聊天室,let’s go

一、websocket常见用法

此间介绍两种本身以为大面积的websocket完结……(专一:本文创建在node上下文情况

1、socket.io

先给demo

JavaScript

var http = require('http'); var io = require('socket.io'); var server = http.createServer(function(req, res) { res.writeHeader(200, {'content-type': 'text/html;charset="utf-8"'}); res.end(); }).listen(8888); var socket =.io.listen(server); socket.sockets.on('connection', function(socket) { socket.emit('xxx', {options}); socket.on('xxx', function(data) { // do someting }); });

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var http = require('http');
var io = require('socket.io');
 
var server = http.createServer(function(req, res) {
    res.writeHeader(200, {'content-type': 'text/html;charset="utf-8"'});
    res.end();
}).listen(8888);
 
var socket =.io.listen(server);
 
socket.sockets.on('connection', function(socket) {
    socket.emit('xxx', {options});
 
    socket.on('xxx', function(data) {
        // do someting
    });
});

信赖领悟websocket的同校不恐怕不知晓socket.io,因为socket.io太盛名了,也很棒,它本人对过期、握手等都做了管理。小编估计那也是兑现websocket使用最多的措施。socket.io最最最卓越的一点正是高雅降级,当浏览器不支持websocket时,它会在里头温婉降级为长轮询等,客商和开拓者是无需关怀具体贯彻的,很便宜。

可是事情是有两面性的,socket.io因为它的无所不至也带来了坑的地点,最重大的就是臃肿,它的包裹也给多少拉动了非常多的广播发表冗余,並且高雅降级这一独到之处,也陪同浏览器规范化的举办逐步失去了伟大

Chrome Supported in version 4+
Firefox Supported in version 4+
Internet Explorer Supported in version 10+
Opera Supported in version 10+
Safari Supported in version 5+

在此处不是责备说socket.io倒霉,已经被淘汰了,而是一时候大家也得以设想部分其余的达成~

 

2、http模块

正要说了socket.io臃肿,那未来就来讲说便捷的,首先demo

JavaScript

var http = require(‘http’); var server = http.createServer(); server.on(‘upgrade’, function(req) { console.log(req.headers); }); server.listen(8888);

1
2
3
4
5
6
var http = require(‘http’);
var server = http.createServer();
server.on(‘upgrade’, function(req) {
console.log(req.headers);
});
server.listen(8888);

很轻易的落到实处,其实socket.io内部对websocket也是那样完结的,不过前面帮大家封装了部分handle管理,这里大家也能够团结去充足,给出两张socket.io中的源码图

美高梅老虎机平台 9

美高梅老虎机平台 10

 

3、ws模块

末端有个例子会用到,这里就提一下,前面具体看~

 

二、自身完成一套server端websocket

凑巧说了三种分布的websocket完结方式,未来大家记挂,对于开采者来讲

websocket相对于守旧http数据交互形式以来,扩大了服务器推送的风波,客户端接收到事件再拓宽对应管理,开拓起来差异并不是太大啊

那是因为这三个模块已经帮大家将数码帧分析那边的坑都填好了,第二有的大家将尝试自身创设一套简便的服务器端websocket模块

多谢次碳酸钴的商量扶助,自个儿在此间那有的只是简短说下,若是对此风野趣好奇的请百度【web本领商量所】

温馨姣好服务器端websocket首要有两点,一个是选拔net模块接受数据流,还会有二个是相比较官方的帧结构图剖析数据,落成这两有些就曾经做到了一切的平底专业

第一给一个客户端发送websocket握手报文的抓包内容

顾客端代码不会细小略

JavaScript

ws = new WebSocket("ws://127.0.0.1:8888");

1
ws = new WebSocket("ws://127.0.0.1:8888");

美高梅老虎机平台 11

劳务器端要对准那么些key验证,就是讲key加上三个一定的字符串后做二次sha1运算,将其结果调换为base64送回去

JavaScript

var crypto = require('crypto'); var WS = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'; require('net').createServer(function(o) { var key; o.on('data',function(e) { if(!key) { // 获取发送过来的KEY key = e.toString().match(/Sec-WebSocket-Key: (.+)/)[1]; // 连接上WS那几个字符串,并做叁遍sha1运算,最终调换来Base64 key = crypto.createHash('sha1').update(key+WS).digest('base64'); // 输出再次回到给客商端的多少,这个字段都以必得的 o.write('HTTP/1.1 101 Switching Protocolsrn'); o.write('Upgrade: websocketrn'); o.write('Connection: Upgradern'); // 这些字段带上服务器管理后的KEY o.write('Sec-WebSocket-Accept: '+key+'rn'); // 输出空行,使HTTP头停止 o.write('rn'); } }); }).listen(8888);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var crypto = require('crypto');
var WS = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
 
require('net').createServer(function(o) {
var key;
o.on('data',function(e) {
if(!key) {
// 获取发送过来的KEY
key = e.toString().match(/Sec-WebSocket-Key: (.+)/)[1];
// 连接上WS这个字符串,并做一次sha1运算,最后转换成Base64
key = crypto.createHash('sha1').update(key+WS).digest('base64');
// 输出返回给客户端的数据,这些字段都是必须的
o.write('HTTP/1.1 101 Switching Protocolsrn');
o.write('Upgrade: websocketrn');
o.write('Connection: Upgradern');
// 这个字段带上服务器处理后的KEY
o.write('Sec-WebSocket-Accept: '+key+'rn');
// 输出空行,使HTTP头结束
o.write('rn');
}
});
}).listen(8888);

如此那般握手部分就已经到位了,前面就是多少帧剖判与转换的活了

先看下官方提供的帧结构暗暗表示图

美高梅老虎机平台 12

美高梅老虎机平台,简简单单介绍下

FIN为是不是得了的标示

锐界SV为留住空间,0

opcode标志数据类型,是不是分片,是不是二进制剖判,心跳包等等

提交一张opcode对应图

美高梅老虎机平台 13

MASK是不是选用掩码

Payload len和前面extend payload length表示数据长度,那几个是最坚苦的

PayloadLen唯有7位,换来无符号整型的话唯有0到127的取值,这么小的数值当然不可能描述不小的数量,因而规定当数码长度小于或等于125时候它才作为数据长度的汇报,假设那个值为126,则时候背后的五个字节来积累数据长度,如果为127则用前边七个字节来囤积数据长度

Masking-key掩码

下边贴出分析数据帧的代码

JavaScript

function decodeDataFrame(e) { var i = 0, j,s, frame = { FIN: e[i] >> 7, Opcode: e[i++]图表的能力,跨域访谈和防盗链基本原理。 & 15, Mask: e[i] >> 7, PayloadLength: e[i++] & 0x7F }; if(frame.PayloadLength === 126) { frame.PayloadLength = (e[i++] << 8) + e[i++]; } if(frame.PayloadLength === 127) { i += 4; frame.PayloadLength = (e[i++] << 24) + (e[i++] << 16) + (e[i++] << 8)

  • e[i++]; } if(frame.Mask) { frame.MaskingKey = [e[i++], e[i++], e[i++], e[i++]]; for(j = 0, s = []; j < frame.PayloadLength; j++) { s.push(e[i+j] ^ frame.MaskingKey[j%4]); } } else { s = e.slice(i, i+frame.PayloadLength); } s = new Buffer(s); if(frame.Opcode === 1) { s = s.toString(); } frame.PayloadData = s; return frame; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
function decodeDataFrame(e) {
var i = 0,
j,s,
frame = {
FIN: e[i] >> 7,
Opcode: e[i++] & 15,
Mask: e[i] >> 7,
PayloadLength: e[i++] & 0x7F
};
 
if(frame.PayloadLength === 126) {
frame.PayloadLength = (e[i++] << 8) + e[i++];
}
 
if(frame.PayloadLength === 127) {
i += 4;
frame.PayloadLength = (e[i++] << 24) + (e[i++] << 16) + (e[i++] << 8) + e[i++];
}
 
if(frame.Mask) {
frame.MaskingKey = [e[i++], e[i++], e[i++], e[i++]];
 
for(j = 0, s = []; j < frame.PayloadLength; j++) {
s.push(e[i+j] ^ frame.MaskingKey[j%4]);
}
} else {
s = e.slice(i, i+frame.PayloadLength);
}
 
s = new Buffer(s);
 
if(frame.Opcode === 1) {
s = s.toString();
}
 
frame.PayloadData = s;
return frame;
}

接下来是浮动数据帧的

JavaScript

function encodeDataFrame(e) { var s = [], o = new Buffer(e.PayloadData), l = o.length; s.push((e.FIN << 7) + e.Opcode); if(l < 126) { s.push(l); } else if(l < 0x10000) { s.push(126, (l&0xFF00) >> 8, l&0xFF); } else { s.push(127, 0, 0, 0, 0, (l&0xFF000000) >> 24, (l&0xFF0000) >> 16, (l&0xFF00) >> 8, l&0xFF); } return Buffer.concat([new Buffer(s), o]); }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function encodeDataFrame(e) {
var s = [],
o = new Buffer(e.PayloadData),
l = o.length;
 
s.push((e.FIN << 7) + e.Opcode);
 
if(l < 126) {
s.push(l);
} else if(l < 0x10000) {
s.push(126, (l&0xFF00) >> 8, l&0xFF);
} else {
s.push(127, 0, 0, 0, 0, (l&0xFF000000) >> 24, (l&0xFF0000) >> 16, (l&0xFF00) >> 8, l&0xFF);
}
 
return Buffer.concat([new Buffer(s), o]);
}

都是遵照帧结构暗中表示图上的去管理,在此地不细讲,小说主要在下一些,假若对那块感兴趣的话能够运动web技术商量所~

 

三、websocket传输图片和websocket语音聊天室

正片环节到了,那篇小说最要害的依旧显示一下websocket的局地运用处境

1、传输图片

大家先考虑传输图片的步调是如何,首先服务器收到到客商端要求,然后读取图片文件,将二进制数据转载给客商端,客商端如什么地点理?当然是运用FileReader对象了

先给客商端代码

JavaScript

var ws = new WebSocket("ws://xxx.xxx.xxx.xxx:8888"); ws.onopen = function(){ console.log("握手成功"); }; ws.onmessage = function(e) { var reader = new FileReader(); reader.onload = function(event) { var contents = event.target.result; var a = new Image(); a.src = contents; document.body.appendChild(a); } reader.readAsDataUPRADOL(e.data); };

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var ws = new WebSocket("ws://xxx.xxx.xxx.xxx:8888");
 
ws.onopen = function(){
    console.log("握手成功");
};
 
ws.onmessage = function(e) {
    var reader = new FileReader();
    reader.onload = function(event) {
        var contents = event.target.result;
        var a = new Image();
        a.src = contents;
        document.body.appendChild(a);
    }
    reader.readAsDataURL(e.data);
};

接到到信息,然后readAsDataU宝马7系L,直接将图片base64增加到页面中

转到服务器端代码

JavaScript

fs.readdir("skyland", function(err, files) { if(err) { throw err; } for(var i = 0; i < files.length; i++) { fs.readFile('skyland/' + files[i], function(err, data) { if(err) { throw err; } o.write(encodeImgFrame(data)); }); } }); function encodeImgFrame(buf) { var s = [], l = buf.length, ret = []; s.push((1 << 7) + 2); if(l < 126) { s.push(l); } else if(l < 0x10000) { s.push(126, (l&0xFF00) >> 8, l&0xFF); } else { s.push(127, 0, 0, 0, 0, (l&0xFF000000) >> 24, (l&0xFF0000) >> 16, (l&0xFF00) >> 8, l&0xFF); } return Buffer.concat([new Buffer(s), buf]); }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
fs.readdir("skyland", function(err, files) {
if(err) {
throw err;
}
for(var i = 0; i < files.length; i++) {
fs.readFile('skyland/' + files[i], function(err, data) {
if(err) {
throw err;
}
 
o.write(encodeImgFrame(data));
});
}
});
 
function encodeImgFrame(buf) {
var s = [],
l = buf.length,
ret = [];
 
s.push((1 << 7) + 2);
 
if(l < 126) {
s.push(l);
} else if(l < 0x10000) {
s.push(126, (l&0xFF00) >> 8, l&0xFF);
} else {
s.push(127, 0, 0, 0, 0, (l&0xFF000000) >> 24, (l&0xFF0000) >> 16, (l&0xFF00) >> 8, l&0xFF);
}
 
return Buffer.concat([new Buffer(s), buf]);
}

注意s.push((1 << 7) + 2)这一句,这里格外直接把opcode写死了为2,对于Binary Frame,那样客商端接收到数量是不会尝试举行toString的,不然会报错~

代码很简短,在那边向大家享用一下websocket传输图片的进度怎样

测验非常多张图纸,总共8.24M

平凡静态财富服务器须求20s左右(服务器较远)

cdn需要2.8s左右

那我们的websocket格局啊??!

答案是均等需求20s左右,是还是不是很失望……速度就是慢在传输上,并非服务器读取图片,本机上等同的图片财富,1s左右足以做到……那样看来数据流也无从冲破距离的界定提升传输速度

下边我们来看看websocket的另三个用法~

 

用websocket搭建语音聊天室

先来照拂一下口音聊天室的效劳

客户进入频道随后从迈克风输入音频,然后发送给后台转载给频道里面包车型大巴别的人,别的人接收到音信进行播报

看起来困难在七个地点,第三个是音频的输入,第二是接受到数量流实行播放

先说音频的输入,这里运用了HTML5的getUserMedia方法,可是注意了,这一个方法上线是有石硖尾的,最终说,先贴代码

JavaScript

if (navigator.getUserMedia) { navigator.getUserMedia( { audio: true }, function (stream) { var rec = new SRecorder(stream); recorder = rec; }) }

1
2
3
4
5
6
7
8
if (navigator.getUserMedia) {
    navigator.getUserMedia(
        { audio: true },
        function (stream) {
            var rec = new SRecorder(stream);
            recorder = rec;
        })
}

率先个参数是{audio: true},只启用音频,然后创造了二个SRecorder对象,后续的操作基本上都在这么些指标上进展。此时如若代码运营在地头的话浏览器应该晋升您是不是启用Mike风输入,分明之后就开动了

接下去大家看下SRecorder构造函数是啥,给出主要的有的

JavaScript

var SRecorder = function(stream) { …… var context = new AudioContext(); var audioInput = context.createMediaStreamSource(stream); var recorder = context.createScriptProcessor(4096, 1, 1); …… }

1
2
3
4
5
6
7
var SRecorder = function(stream) {
    ……
   var context = new AudioContext();
    var audioInput = context.createMediaStreamSource(stream);
    var recorder = context.createScriptProcessor(4096, 1, 1);
    ……
}

奥迪(Audi)oContext是叁个旋律上下文对象,有做过声音过滤处理的同校应该明白“一段音频到达扬声器实行播报以前,半路对其开展拦阻,于是大家就取得了旋律数据了,那一个拦截工作是由window.奥迪(Audi)oContext来做的,大家具备对旋律的操作都依照那一个指标”,咱们得以经过奥迪(Audi)oContext创造差异的奥迪(Audi)oNode节点,然后增加滤镜播放非常的动静

录音原理一样,我们也急需走奥迪oContext,然则多了一步对迈克风音频输入的收纳上,并非像未来管理音频一下用ajax央浼音频的ArrayBuffer对象再decode,迈克风的收受需求用到createMediaStreamSource方法,注意这么些参数便是getUserMedia方法第2个参数的参数

再则createScriptProcessor方法,它官方的表明是:

Creates a ScriptProcessorNode, which can be used for direct audio processing via JavaScript.

——————

包涵下正是其一方法是应用JavaScript去管理音频收集操作

好不轻便到点子搜集了!胜利就在日前!

接下去让大家把Mike风的输入和音频搜聚相连起来

JavaScript

audioInput.connect(recorder); recorder.connect(context.destination);

1
2
audioInput.connect(recorder);
recorder.connect(context.destination);

context.destination官方表达如下

The destination property of the AudioContext interface returns an AudioDestinationNoderepresenting the final destination of all audio in the context.

——————

context.destination再次回到代表在情形中的音频的最终目标地。

好,到了那儿,我们还索要一个监听音频搜聚的平地风波

JavaScript

recorder.onaudioprocess = function (e) { audioData.input(e.inputBuffer.getChannelData(0)); }

1
2
3
recorder.onaudioprocess = function (e) {
    audioData.input(e.inputBuffer.getChannelData(0));
}

audioData是多个对象,那么些是在网络找的,作者就加了贰个clear方法因为背后会用到,首要有那多少个encodeWAV方法极棒,旁人举办了往往的旋律压缩和优化,那些最终会陪伴完整的代码一同贴出来

那时候全数客户走入频道随后从Mike风输入音频环节就已经产生啦,上面就该是向劳动器端发送音频流,稍微有一些蛋疼的来了,刚才大家说了,websocket通过opcode不一样能够表示回去的多少是文本照旧二进制数据,而作者辈onaudioprocess中input进去的是数组,最后播放声音须要的是Blob,{type: ‘audio/wav’}的对象,那样大家就不能够不要在出殡和埋葬在此之前将数组调换来WAV的Blob,此时就用到了地点说的encodeWAV方法

服务器就好像很轻便,只要转载就行了

本地质度量试确实能够,不过天坑来了!将前后相继跑在服务器上时候调用getUserMedia方法提醒作者必得在多少个安全的情形,也正是急需https,这代表ws也无法不换到wss……据此服务器代码就向来不行使我们同心协力包装的抓手、深入分析和编码了,代码如下

JavaScript

var https = require('https'); var fs = require('fs'); var ws = require('ws'); var userMap = Object.create(null); var options = { key: fs.readFileSync('./privatekey.pem'), cert: fs.readFileSync('./certificate.pem') }; var server = https.createServer(options, function(req, res) { res.writeHead({ 'Content-Type' : 'text/html' }); fs.readFile('./testaudio.html', function(err, data) { if(err) { return ; } res.end(data); }); }); var wss = new ws.Server({server: server}); wss.on('connection', function(o) { o.on('message', function(message) { if(message.indexOf('user') === 0) { var user = message.split(':')[1]; userMap[user] = o; } else { for(var u in userMap) { userMap[u].send(message); } } }); }); server.listen(8888);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
var https = require('https');
var fs = require('fs');
var ws = require('ws');
var userMap = Object.create(null);
var options = {
    key: fs.readFileSync('./privatekey.pem'),
    cert: fs.readFileSync('./certificate.pem')
};
var server = https.createServer(options, function(req, res) {
    res.writeHead({
        'Content-Type' : 'text/html'
    });
 
    fs.readFile('./testaudio.html', function(err, data) {
        if(err) {
            return ;
        }
 
        res.end(data);
    });
});
 
var wss = new ws.Server({server: server});
 
wss.on('connection', function(o) {
    o.on('message', function(message) {
if(message.indexOf('user') === 0) {
    var user = message.split(':')[1];
    userMap[user] = o;
} else {
    for(var u in userMap) {
userMap[u].send(message);
    }
}
    });
});
 
server.listen(8888);

代码依旧很简短的,使用https模块,然后用了始于说的ws模块,userMap是仿照的频段,只兑现转载的宗旨功能

采用ws模块是因为它相当https完成wss实在是太方便了,和逻辑代码0争辩

https的搭建在此地就不提了,主借使急需私钥、CSCRUISER证书具名和证书文件,感兴趣的同班能够领会下(不过不领悟的话在现网碰着也用持续getUserMedia……)

上边是一体化的前端代码

JavaScript

var a = document.getElementById('a'); var b = document.getElementById('b'); var c = document.getElementById('c'); navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia; var gRecorder = null; var audio = document.querySelector('audio'); var door = false; var ws = null; b.onclick = function() { if(a.value === '') { alert('请输入顾客名'); return false; } if(!navigator.getUserMedia) { alert('抱歉您的装备无保加利亚语音聊天'); return false; } SRecorder.get(function (rec) { gRecorder = rec; }); ws = new WebSocket("wss://x.x.x.x:8888"); ws.onopen = function() { console.log('握手成功'); ws.send('user:' + a.value); }; ws.onmessage = function(e) { receive(e.data); }; document.onkeydown = function(e) { if(e.keyCode === 65) { if(!door) { gRecorder.start(); door = true; } } }; document.onkeyup = function(e) { if(e.keyCode === 65) { if(door) { ws.send(gRecorder.getBlob()); gRecorder.clear(); gRecorder.stop(); door = false; } } } } c.onclick = function() { if(ws) { ws.close(); } } var SRecorder = function(stream) { config = {}; config.sampleBits = config.smapleBits || 8; config.sampleRate = config.sampleRate || (44100 / 6); var context = new 奥迪oContext(); var audioInput = context.createMediaStreamSource(stream); var recorder = context.createScriptProcessor(4096, 1, 1); var audioData = { size: 0 //录音文件长度 , buffer: [] //录音缓存 , inputSampleRate: context.sampleRate //输入采集样品率 , inputSampleBits: 16 //输入采集样品数位 8, 16 , outputSampleRate: config.sampleRate //输出采集样品率 , oututSampleBits: config.sampleBits //输出采集样品数位 8, 16 , clear: function() { this.buffer = []; this.size = 0; } , input: function (data) { this.buffer.push(new Float32Array(data)); this.size += data.length; } , compress: function () { //合併压缩 //合併 var data = new Float32Array(this.size); var offset = 0; for (var i = 0; i < this.buffer.length; i++) { data.set(this.buffer[i], offset); offset += this.buffer[i].length; } //压缩 var compression = parseInt(this.inputSampleRate / this.outputSampleRate); var length = data.length / compression; var result = new Float32Array(length); var index = 0, j = 0; while (index < length) { result[index] = data[j]; j += compression; index++; } return result; } , encodeWAV: function () { var sampleRate = Math.min(this.inputSampleRate, this.outputSampleRate); var sampleBits = Math.min(this.inputSampleBits, this.oututSampleBits); var bytes = this.compress(); var dataLength = bytes.length * (sampleBits / 8); var buffer = new ArrayBuffer(44 + dataLength); var data = new DataView(buffer); var channelCount = 1;//单声道 var offset = 0; var writeString = function (str) { for (var i = 0; i < str.length; i++) { data.setUint8(offset + i, str.charCodeAt(i)); } }; // 能源交流文件标志符 writeString('RubiconIFF'); offset += 4; // 下个地点开始到文件尾总字节数,即文件大小-8 data.setUint32(offset, 36 + dataLength, true); offset += 4; // WAV文件注解 writeString('WAVE'); offset += 4; // 波形格式标记 writeString('fmt '); offset += 4; // 过滤字节,常常为 0x10 = 16 data.setUint32(offset, 16, true); offset += 4; // 格式连串 (PCM方式采集样品数据) data.setUint16(offset, 1, true); offset += 2; // 通道数 data.setUint16(offset, channelCount, true); offset += 2; // 采集样品率,每秒样本数,表示各种通道的播报速度 data.setUint32(offset, sampleRate, true); offset += 4; // 波形数据传输率 (每秒平均字节数) 单声道×每秒数据位数×每样本数据位/8 data.setUint32(offset, channelCount * sampleRate * (sampleBits / 8), true); offset += 4; // 快数据调节数 采集样品一遍占用字节数 单声道×每样本的数量位数/8 data.setUint16(offset, channelCount * (sampleBits / 8), true); offset += 2; // 每样本数量位数 data.setUint16(offset, sampleBits, true); offset += 2; // 数据标志符 writeString('data'); offset += 4; // 采集样品数据总量,即数据总大小-44 data.setUint32(offset, dataLength, true); offset += 4; // 写入采集样品数据 if (sampleBits === 8) { for (var i = 0; i < bytes.length; i++, offset++) { var s = Math.max(-1, Math.min(1, bytes[i])); var val = s < 0 ? s * 0x8000 : s * 0x7FFF; val = parseInt(255 / (65535 / (val + 32768))); data.setInt8(offset, val, true); } } else { for (var i = 0; i < bytes.length; i++, offset += 2) { var s = Math.max(-1, Math.min(1, bytes[i])); data.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true); } } return new Blob([data], { type: 'audio/wav' }); } }; this.start = function () { audioInput.connect(recorder); recorder.connect(context.destination); } this.stop = function () { recorder.disconnect(); } this.getBlob = function () { return audioData.encodeWAV(); } this.clear = function() { audioData.clear(); } recorder.onaudioprocess = function (e) { audioData.input(e.inputBuffer.getChannelData(0)); } }; SRecorder.get = function (callback) { if (callback) { if (navigator.getUserMedia) { navigator.getUserMedia( { audio: true }, function (stream) { var rec = new SRecorder(stream); callback(rec); }) } } } function receive(e) { audio.src = window.URL.createObjectURL(e); }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
var a = document.getElementById('a');
var b = document.getElementById('b');
var c = document.getElementById('c');
 
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia;
 
var gRecorder = null;
var audio = document.querySelector('audio');
var door = false;
var ws = null;
 
b.onclick = function() {
    if(a.value === '') {
        alert('请输入用户名');
        return false;
    }
    if(!navigator.getUserMedia) {
        alert('抱歉您的设备无法语音聊天');
        return false;
    }
 
    SRecorder.get(function (rec) {
        gRecorder = rec;
    });
 
    ws = new WebSocket("wss://x.x.x.x:8888");
 
    ws.onopen = function() {
        console.log('握手成功');
        ws.send('user:' + a.value);
    };
 
    ws.onmessage = function(e) {
        receive(e.data);
    };
 
    document.onkeydown = function(e) {
        if(e.keyCode === 65) {
            if(!door) {
                gRecorder.start();
                door = true;
            }
        }
    };
 
    document.onkeyup = function(e) {
        if(e.keyCode === 65) {
            if(door) {
                ws.send(gRecorder.getBlob());
                gRecorder.clear();
                gRecorder.stop();
                door = false;
            }
        }
    }
}
 
c.onclick = function() {
    if(ws) {
        ws.close();
    }
}
 
var SRecorder = function(stream) {
    config = {};
 
    config.sampleBits = config.smapleBits || 8;
    config.sampleRate = config.sampleRate || (44100 / 6);
 
    var context = new AudioContext();
    var audioInput = context.createMediaStreamSource(stream);
    var recorder = context.createScriptProcessor(4096, 1, 1);
 
    var audioData = {
        size: 0          //录音文件长度
        , buffer: []     //录音缓存
        , inputSampleRate: context.sampleRate    //输入采样率
        , inputSampleBits: 16       //输入采样数位 8, 16
        , outputSampleRate: config.sampleRate    //输出采样率
        , oututSampleBits: config.sampleBits       //输出采样数位 8, 16
        , clear: function() {
            this.buffer = [];
            this.size = 0;
        }
        , input: function (data) {
            this.buffer.push(new Float32Array(data));
            this.size += data.length;
        }
        , compress: function () { //合并压缩
            //合并
            var data = new Float32Array(this.size);
            var offset = 0;
            for (var i = 0; i < this.buffer.length; i++) {
                data.set(this.buffer[i], offset);
                offset += this.buffer[i].length;
            }
            //压缩
            var compression = parseInt(this.inputSampleRate / this.outputSampleRate);
            var length = data.length / compression;
            var result = new Float32Array(length);
            var index = 0, j = 0;
            while (index < length) {
                result[index] = data[j];
                j += compression;
                index++;
            }
            return result;
        }
        , encodeWAV: function () {
            var sampleRate = Math.min(this.inputSampleRate, this.outputSampleRate);
            var sampleBits = Math.min(this.inputSampleBits, this.oututSampleBits);
            var bytes = this.compress();
            var dataLength = bytes.length * (sampleBits / 8);
            var buffer = new ArrayBuffer(44 + dataLength);
            var data = new DataView(buffer);
 
            var channelCount = 1;//单声道
            var offset = 0;
 
            var writeString = function (str) {
                for (var i = 0; i < str.length; i++) {
                    data.setUint8(offset + i, str.charCodeAt(i));
                }
            };
 
            // 资源交换文件标识符
            writeString('RIFF'); offset += 4;
            // 下个地址开始到文件尾总字节数,即文件大小-8
            data.setUint32(offset, 36 + dataLength, true); offset += 4;
            // WAV文件标志
            writeString('WAVE'); offset += 4;
            // 波形格式标志
            writeString('fmt '); offset += 4;
            // 过滤字节,一般为 0x10 = 16
            data.setUint32(offset, 16, true); offset += 4;
            // 格式类别 (PCM形式采样数据)
            data.setUint16(offset, 1, true); offset += 2;
            // 通道数
            data.setUint16(offset, channelCount, true); offset += 2;
            // 采样率,每秒样本数,表示每个通道的播放速度
            data.setUint32(offset, sampleRate, true); offset += 4;
            // 波形数据传输率 (每秒平均字节数) 单声道×每秒数据位数×每样本数据位/8
            data.setUint32(offset, channelCount * sampleRate * (sampleBits / 8), true); offset += 4;
            // 快数据调整数 采样一次占用字节数 单声道×每样本的数据位数/8
            data.setUint16(offset, channelCount * (sampleBits / 8), true); offset += 2;
            // 每样本数据位数
            data.setUint16(offset, sampleBits, true); offset += 2;
            // 数据标识符
            writeString('data'); offset += 4;
            // 采样数据总数,即数据总大小-44
            data.setUint32(offset, dataLength, true); offset += 4;
            // 写入采样数据
            if (sampleBits === 8) {
                for (var i = 0; i < bytes.length; i++, offset++) {
                    var s = Math.max(-1, Math.min(1, bytes[i]));
                    var val = s < 0 ? s * 0x8000 : s * 0x7FFF;
                    val = parseInt(255 / (65535 / (val + 32768)));
                    data.setInt8(offset, val, true);
                }
            } else {
                for (var i = 0; i < bytes.length; i++, offset += 2) {
                    var s = Math.max(-1, Math.min(1, bytes[i]));
                    data.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
                }
            }
 
            return new Blob([data], { type: 'audio/wav' });
        }
    };
 
    this.start = function () {
        audioInput.connect(recorder);
        recorder.connect(context.destination);
    }
 
    this.stop = function () {
        recorder.disconnect();
    }
 
    this.getBlob = function () {
        return audioData.encodeWAV();
    }
 
    this.clear = function() {
        audioData.clear();
    }
 
    recorder.onaudioprocess = function (e) {
        audioData.input(e.inputBuffer.getChannelData(0));
    }
};
 
SRecorder.get = function (callback) {
    if (callback) {
        if (navigator.getUserMedia) {
            navigator.getUserMedia(
                { audio: true },
                function (stream) {
                    var rec = new SRecorder(stream);
                    callback(rec);
                })
        }
    }
}
 
function receive(e) {
    audio.src = window.URL.createObjectURL(e);
}

注意:按住a键说话,放开a键发送

和睦有品味不按钮实时对讲,通过setInterval发送,但意识杂音有一点重,效果不佳,那个必要encodeWAV再一层的卷入,多去除情状杂音的意义,自身采取了进一步方便人民群众的按钮说话的格局

 

那篇小说里首先展望了websocket的前程,然后根据专门的学问大家友好尝尝深入分析和转移数据帧,对websocket有了越来越深一步的刺探

末段通过四个demo见到了websocket的潜在的能量,关于语音聊天室的demo涉及的较广,未有接触过奥迪(Audi)oContext对象的同窗最棒先理解下奥迪(Audi)oContext

小聊起这边就得了啦~有何主张和主题素材款待大家提议来一同座谈研究~

 

1 赞 11 收藏 3 评论

美高梅老虎机平台 14

二、跨域访问基本原理

在上一篇,介绍了盗链的基本原理和防盗链的减轻方案。这里更加深远解析一下跨域访谈。先看看跨域访谈的相干原理:跨网址指令码。维基上边给出了跨站访谈的风险性。从此处能够整理出跨站访谈的定义:JS脚本在浏览器端发起的呼吁别的域(名)下的网址数据的HTTP需要。

那边要与referer区分开,referer是浏览器的一颦一笑,全数浏览器发出的呼吁都不会存在安全危害。而由网页加载的本子发起呼吁则会不可控,以至能够收获客户数量传输到另外站点。referer情势拉取其余网址的数目也是跨域,可是那几个是由浏览器必要整个能源,财富央浼到后,客商端的本子并不可能决定那份数据,只好用来展现。可是洋洋时候,大家都亟需倡导呼吁到其余站点动态获取数据,并将获得到底多少开展更为的管理,那也正是跨域访问的须求。

 

今昔从手艺上有多少个方案去消除这些主题材料。

类数组对象

所谓的类数组对象:

抱有贰个 length 属性和若干索引属性的对象

举个例证:

var array = ['name', 'age', 'sex']; var arrayLike = { 0: 'name', 1: 'age', 2: 'sex', length: 3 }

1
2
3
4
5
6
7
8
var array = ['name', 'age', 'sex'];
 
var arrayLike = {
    0: 'name',
    1: 'age',
    2: 'sex',
    length: 3
}

纵然如此,为何叫做类数组对象啊?

那让大家从读写、获取长度、遍历八个方面看看那五个指标。

打赏协理我翻译更加多好文章,感谢!

任选一种支付办法

美高梅老虎机平台 15 美高梅老虎机平台 16

赞 2 收藏 评论

1、JSONP跨域访谈

使用浏览器的Referer情势加载脚本到客户端的措施。以:

<script type="text/javascript" src=";

1
<script type="text/javascript" src="http://api.com/jsexample.js"></script>

这种办法得到并加载其余站点的JS脚本是被允许的,加载过来的本子中一经有定义的函数只怕接口,能够在本地使用,那也是大家用得最多的台本加载格局。可是那些加载到本地脚本是无法被修改和拍卖的,只好是援引。

而跨域采访须求便是访谈远端抓取到的数额。那么是不是扭转,本地写好三个数目管理函数,让伏乞服务端协助成功调用进度?JS脚本允许这样。

<script type="text/javascript"> var localHandler = function(data) { alert('小编是地面函数,能够被跨域的remote.js文件调用,远程js带来的多少是:'

  • data.result); }; </script> <script type="text/javascript" src=";
1
2
3
4
5
6
7
<script type="text/javascript">
var localHandler = function(data)
{
    alert('我是本地函数,可以被跨域的remote.js文件调用,远程js带来的数据是:' + data.result);
};
</script>
<script type="text/javascript" src="http://remoteserver.com/remote.js"></script>

远端的服务器上边定义的remote.js是那样的:

JavaScript

localHandler({"result":"笔者是远程js带来的多少"});

1
localHandler({"result":"我是远程js带来的数据"});

地方首先在地方定义了一个函数localHandler,然后远端再次来到的JS的源委是调用这些函数,重返到浏览器端实践。相同的时候在JS内容大校顾客端须要的数量重返,那样数据就被传输到了浏览器端,浏览器端只必要修改管理措施就能够。这里有一点限制:1、客户端脚本和服务端需求部分相称;2、调用的数额必得是json格式的,不然客户端脚本不能管理;3、只可以给被援用的服务端网站发送get央浼。

<script type="text/javascript"> var localHandler = function(data) { alert('作者是地面函数,能够被跨域的remote.js文件调用,远程js带来的数据是:'

  • data.result); }; </script> <script type="text/javascript" src=";
1
2
3
4
5
6
7
<script type="text/javascript">
var localHandler = function(data)
{
    alert('我是本地函数,可以被跨域的remote.js文件调用,远程js带来的数据是:' + data.result);
};
</script>
<script type="text/javascript" src="http://remoteserver.com/remote.php?callBack=localHandler"></script>

服务端的PHP函数可能是这么的:

PHP

<?php $data = "......."; $callback = $_GET['callback']; echo $callback.'('.json_encode($data).')'; exit; ?>

1
2
3
4
5
6
7
8
<?php
 
$data = ".......";
$callback = $_GET['callback'];
echo $callback.'('.json_encode($data).')';
exit;
 
?>

这么就能够根据顾客端钦定的回调拼装调用进程。

读写

console.log(array[0]); // name console.log(arrayLike[0]); // name array[0] = 'new name'; arrayLike[0] = 'new name';

1
2
3
4
5
console.log(array[0]); // name
console.log(arrayLike[0]); // name
 
array[0] = 'new name';
arrayLike[0] = 'new name';

关于笔者:刘健超-J.c

美高梅老虎机平台 17

前端,在路上... 个人主页 · 笔者的篇章 · 19 ·     

美高梅老虎机平台 18

本文由澳门美高梅老虎机平台发布于美高梅老虎机平台,转载请注明出处:图表的能力,跨域访谈和防盗链基本原理

关键词:

由一道题通透到底弄懂,轻巧监听其余App自带再

3、笔者那边根本介绍下自家具体是怎么监听别的App自带的重回键,以及安卓机里的物理再次回到键。 这干什么笔者要...

详细>>

开源中最棒的Web开荒财富汇总【美高梅老虎机平

JavaScript 深远之功效域链 2017/05/14 · JavaScript·效果域链 原稿出处: 冴羽    给列表项目增加动画 2015/05/08 · CSS,HTML...

详细>>

创设高品质WEB之HTTP首部优化,Chrome开拓者工具不

前端当半夏件操作与上传 2017/12/07 · JavaScript· 1 评论 ·文件 最先的作品出处:人人网FED博客    前端不可能像原生应...

详细>>

【美高梅老虎机平台】娱乐开采,玩的方法知多

后期预备性研讨 在感受过 AppStore 上一点款推金币游戏 App后,开采游戏大旨模型照旧挺容易的,可是 H5本子的兑以往...

详细>>