一、jsonp

首先我们来想一想

为什么会有跨域这个名词的出现呢?

跨域又是什么呢?为何要跨域?

浏览器的同源策略又是什么?怎么解决?

jsonp又是什么?

跨域的原理又是什么呢?

名词解释:

跨域:

浏览器对于javascript的同源策略的限制,例如a.cn下面的js不能调用b.cn中的js,对象或数据(因为a.cn和b.cn是不同域),所以跨域就出现了.

上面提到的,同域的概念又是什么呢??? 简单的解释就是相同域名,端口相同,协议相同

同源策略:

请求的url地址,必须与浏览器上的url地址处于同域上,也就是域名,端口,协议相同.

比如:我在本地上的域名是study.cn,请求另外一个域名一段数据

这个时候在浏览器上会报错:

这个就是同源策略的保护,如果浏览器对javascript没有同源策略的保护,那么一些重要的机密网站将会很危险~

study.cn/json/jsonp/jsonp.html
请求地址 形式 结果
http://study.cn/test/a.html 同一域名,不同文件夹 成功
http://study.cn/json/jsonp/jsonp.html 同一域名,统一文件夹 成功
http://a.study.cn/json/jsonp/jsonp.html 不同域名,文件路径相同 失败
http://study.cn:8080/json/jsonp/jsonp.html 同一域名,不同端口 失败
https://study.cn/json/jsonp/jsonp.html 同一域名,不同协议   失败

jsonp:

jsonp 全称是JSON with Padding,是为了解决跨域请求资源而产生的解决方案,是一种依靠开发人员创造出的一种非官方跨域数据交互协议。

一个是描述信息的格式,一个是信息传递双方约定的方法。

jsonp的产生:

1.AJAX直接请求普通文件存在跨域无权限访问的问题,不管是静态页面也好.

2.不过我们在调用js文件的时候又不受跨域影响,比如引入jquery框架的,或者是调用相片的时候

3.凡是拥有scr这个属性的标签都可以跨域例如<script><img><iframe>

4.如果想通过纯web端跨域访问数据只有一种可能,那就是把远程服务器上的数据装进js格式的文件里.

5.而json又是一个轻量级的数据格式,还被js原生支持

6.为了便于客户端使用数据,逐渐形成了一种非正式传输协议,人们把它称作JSONP,该协议的一个要点就是允许用户传递一个callback 参数给服务端,

demo1:基于script标签实现跨域

举个例子:我在http://study.cn/json/jsonp/jsonp_2.html下请求一个远程的js文件

1 <!DOCTYPE html>
2 <html>
3 <head>
4 <metacharset="UTF-8">
5 <title>Insert title here</title>
6
7 <scripttype="text/javascript">
8     varmessage= function(data) {9 alert(data[1].title);10 };11 </script>
12
13 <scripttype="text/javascript"src="http://web.cn/js/message.js"></script>
14 </head>
15 <body>
16 <divid='testdiv'></div>
17 </body>
18 </html>

远程的message.js文件是

1 message([2      {"id":"1", "title":"天津新闻联播,雷人搞笑的男主持人"},3      {"id":"2", "title":"楼市告别富得流油 专家:房价下跌是大概率事件"},4      {"id":"3", "title":"法国人关注时事 八成年轻人每天阅读新闻"},5      {"id":"4", "title":"新闻中的历史,历史中的新闻"},6      {"id":"5", "title":"东阳新闻20140222"},7      {"id":"6", "title":"23个职能部门要增加新闻发布频次"},8      {"id":"7", "title":"《贵州新闻联播》 中国美丽乡村"},9      {"id":"8", "title":"朝韩离散家属团聚首轮活动结束"},10      {"id":"9", "title":"索契冬奥会一天曝出两例兴奋剂事件"},11      {"id":"10", "title":"今天中国多地仍将出现中度霾"}12  ]);

这个时候我们得到的相应头是:

这样就实现跨域成功了,因为服务端返回数据时会将这个callback参数(message)作为函数名来包裹住JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。

demo2: 基于script标签实现跨域

让远程js知道它应该调用的本地函数叫什么名字,只要服务端提供的js脚本是动态生成的就好了,这样前台只需要传一个callback参数过去告诉服务端,我需要XXX代码,于是服务端就会得到相应了.

例如 在http://study.cn/json/jsonp/jsonp_3.html页面请求 http://192.168.31.137/train/test/jsonpthree

1 <!DOCTYPE html>
2 <html>
3 <head>
4 <metacharset="UTF-8">
5 <title>Insert title here</title>
6
7 <scripttype="text/javascript">
8     varmessagetow= function(data){9 alert(data);10 };11     varurl= "http://192.168.31.137/train/test/jsonpthree?callback=messagetow";12     varscript=document.createElement('script');13 script.setAttribute('src', url);14 document.getElementsByTagName('head')[0].appendChild(script);15 </script>
16 </head>
17 <body>
18 </body>
19 </html>

得到的响应头是:

demo3:  基于jquery跨域

那么如何用jquery来实现我们的跨域呢???jquery已经把跨域封装到ajax上了,而且封装得非常的好,使用起来也特别方便

如果是一般的ajax请求:

1 $.ajax({2 url:'http://192.168.31.137/train/test/testjsonp',3 type : 'get',4 dataType : 'text',5 success:function(data){6 alert(data);7 },8 error:function(data){9 alert(2);10 }11     });

那么在浏览器中会报错:

jsonp形式的ajax请求:并且通过get请求的方式传入参数,注意:跨域请求是只能是get请求不能使用post请求

1 <!DOCTYPE html>
2 <html>
3 <head>
4 <metacharset="UTF-8">
5 <title>Insert title here</title>
6 <scripttype="text/javascript"src="./js/jquery.js"></script>
7 <scripttype="text/javascript">
8 $(document).ready(function(){9     varname= 'chenshishuo';10     varsex= 'man';11     varaddress= 'shenzhen';12     varlooks= 'handsome';13 $.ajax({14 type :'get',15 url:'http://192.168.31.137/train/test/testjsonp',16 data : {17 name : name,18 sex : sex,19 address : address,20 looks : looks,21 },22 cache :false,23 jsonp:"callback",
24 jsonpCallback:"success",
25 dataType :'jsonp',26 success:function(data){27 alert(data);28 },29 error:function(data){30 alert('error');31 }32 });33 });34 </script>
35 </head>
36 <body>
37 <inputid='inputtest'value='546'name='inputtest'>
38 <divid='testdiv'></div>
39 </body>
40 </html>

jsonp 传递给请求处理程序或页面的,用以获得jsonp回调函数名的参数名(默认为:callback)
jsonpCallback 自定义的jsonp回调函数名称,默认为jQuery自动生成的随机函数名

看看请求头和相应头吧

请求头:jquery会自动带入callback参数,当服务端获取到这个参数后,返回回来.(响应头)

现在是不是明白了跨域的基本原理,和基本的使用方法呢??

上面我们说到img中的src可以自动调用远程图片的(这个比较简单我在这里就不说了)

还有ifram请求:

基于iframe实现的跨域要求两个域具有aa.xx.com,bb.xx.com 这种特点,

也就是两个页面必须属于一个基础域(例如都是xxx.com),使用同一协议和同一端口,这样在两个页面中同时添加document.domain,就可以实现父页面调用子页面的函数

要点就是 :通过修改document.domain来跨子域

demo4:通过iframe来跨子域

http://a.study.cn/a.html 请求 http://b.study.cn/b.html

在a.html:

1 <!DOCTYPE html>
2 <html>
3 <head>
4 <metacharset="UTF-8">
5 <title>Insert title here</title>
6         <scripttype="text/javascript">
7 document.domain= 'study.cn';8             functiontest() {9 alert(document.getElementById('a').contentWindow);10 }11         </script>
12 </head>
13 <body>
14     <iframeid='a'src='http://b.study.cn/b.html'onload='test()'>
15 </body>
16 </html>

在b.html:

1 <!DOCTYPE html>
2 <html>
3 <head>
4 <metacharset="UTF-8">
5 <title>Insert title here</title>
6
7 <scripttype="text/javascript">
8 document.domain= 'study.cn';9 </script>
10 </head>
11 <body>
12 我是b.study.cn的body13 </body>
14 </html>

运行效果截图:

我们就可以通过js访问到iframe中的各种属性和对象了

如果你想在http://a.study.cn/a.html页面中通过ajax直接请求页面http://b.study.cn/b.html,即使你设置了相同的document.domain也还是不行的.

所以修改document.domain的方法只适用于不同子域的框架(父类与子类)间的交互。

如果想通过使用ajax的方法去与不同子域间的数据交互或者是js调用,只有两种方法,一种是使用jsonp的方法外,还有一种是使用iframe来做一个代理。

原理就是让这个 iframe载入一个与你想要通过ajax获取数据的目标页面处在相同的域的页面,所以这个iframe中的页面是可以正常使用ajax去获取你要的数据 的,

然后就是通过我们刚刚讲得修改document.domain的方法,让我们能通过js完全控制这个iframe,这样我们就可以让iframe去发 送ajax请求,然后收到的数据我们也可以获得了。

二、jsonp用不用

即将接手一个前端项目。
这项目之前是用 Rails + Angular1 做的,现在我们打算把 Rails 干掉,所有数据都让前端直接从 api.example.com 取。
后端 api.example.com 一定是我们自己 serve ,但前端的话:

  1. 可能部署 NGINX 到各个客户服的务器上
  2. 也可能是我们自己的 admin.example.com
  3. 当然更有可能同时会有好几套,这还不确定

之前的思路是,前端判断一下其所处的环境是否支持 CORS ,不行的话用 JSONP 当做 fallback 。至于 GET 以外的请求,这边用 JSONP 模拟了一套实现,看起来相当完善,前后端都有支持。并且我们的请求都很小,似乎不用考虑 413 的问题。

而现在我个人的想法是,无论部署在哪,都让跑这个项目的那个 NGINX 做一次 proxy_pass ,反代到 api.example.com (对应 1 ),或是反代到我们私有网络跑 api 的那台机器上(对应 2 )。这样的优点至少有:

  1. 跨域这件事情根本不存在了,也不用去考虑 CORS 的各种坑
  2. 客户方面,只需要跑 NGINX 的那台机器连得到 api.example.com 就行,跑浏览器的机器可以是纯内网环境

缺点:

  1. 多了一次代理转发,做反代的机器机器会有额外性能开销(如果和 api 不是同一个 NGINX 的话)
  2. 本来前端所有的文件可以都交给 CDN ,现在则必须要一台服务器了

总之在我印象中,JSONP 这玩意就是落后于时代(亦或是超前于时代)的一个 hack 。以前、现在、未来都未曾、没有、不会出现在 W3C 或是 RFC 标准中,应该被抵制才对。

三、CORS

CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。

它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。

本文详细介绍CORS的内部机制。

(图片说明:摄于阿联酋艾因(Al Ain)的绿洲公园)

一、简介

CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。

整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。

因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。

二、两种请求

浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。

只要同时满足以下两大条件,就属于简单请求。

(1) 请求方法是以下三种方法之一:

  • HEAD
  • GET
  • POST

(2)HTTP的头信息不超出以下几种字段:

  • Accept
  • Accept-Language
  • Content-Language
  • Last-Event-ID
  • Content-Type:只限于三个值application/x-www-form-urlencodedmultipart/form-datatext/plain

凡是不同时满足上面两个条件,就属于非简单请求。

浏览器对这两种请求的处理,是不一样的。

三、简单请求

3.1 基本流程

对于简单请求,浏览器直接发出CORS请求。具体来说,就是在头信息之中,增加一个Origin字段。

下面是一个例子,浏览器发现这次跨源AJAX请求是简单请求,就自动在头信息之中,添加一个Origin字段。

GET /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

上面的头信息中,Origin字段用来说明,本次请求来自哪个源(协议 + 域名 + 端口)。服务器根据这个值,决定是否同意这次请求。

如果Origin指定的源,不在许可范围内,服务器会返回一个正常的HTTP回应。浏览器发现,这个回应的头信息没有包含Access-Control-Allow-Origin字段(详见下文),就知道出错了,从而抛出一个错误,被XMLHttpRequestonerror回调函数捕获。注意,这种错误无法通过状态码识别,因为HTTP回应的状态码有可能是200。

如果Origin指定的域名在许可范围内,服务器返回的响应,会多出几个头信息字段。

Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8

上面的头信息之中,有三个与CORS请求相关的字段,都以Access-Control-开头。

(1)Access-Control-Allow-Origin

该字段是必须的。它的值要么是请求时Origin字段的值,要么是一个*,表示接受任意域名的请求。

(2)Access-Control-Allow-Credentials

该字段可选。它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。设为true,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。这个值也只能设为true,如果服务器不要浏览器发送Cookie,删除该字段即可。

(3)Access-Control-Expose-Headers

该字段可选。CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma。如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。上面的例子指定,getResponseHeader('FooBar')可以返回FooBar字段的值。

3.2 withCredentials 属性

上面说到,CORS请求默认不发送Cookie和HTTP认证信息。如果要把Cookie发到服务器,一方面要服务器同意,指定Access-Control-Allow-Credentials字段。

Access-Control-Allow-Credentials: true

另一方面,开发者必须在AJAX请求中打开withCredentials属性。

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

否则,即使服务器同意发送Cookie,浏览器也不会发送。或者,服务器要求设置Cookie,浏览器也不会处理。

但是,如果省略withCredentials设置,有的浏览器还是会一起发送Cookie。这时,可以显式关闭withCredentials

xhr.withCredentials = false;

需要注意的是,如果要发送Cookie,Access-Control-Allow-Origin就不能设为星号,必须指定明确的、与请求网页一致的域名。同时,Cookie依然遵循同源政策,只有用服务器域名设置的Cookie才会上传,其他域名的Cookie并不会上传,且(跨源)原网页代码中的document.cookie也无法读取服务器域名下的Cookie。

四、非简单请求

4.1 预检请求

非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUTDELETE,或者Content-Type字段的类型是application/json

非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)。

浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。

下面是一段浏览器的JavaScript脚本。

var url = 'http://api.alice.com/cors';
var xhr = new XMLHttpRequest();
xhr.open('PUT', url, true);
xhr.setRequestHeader('X-Custom-Header', 'value');
xhr.send();

上面代码中,HTTP请求的方法是PUT,并且发送一个自定义头信息X-Custom-Header

浏览器发现,这是一个非简单请求,就自动发出一个"预检"请求,要求服务器确认可以这样请求。下面是这个"预检"请求的HTTP头信息。

OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

"预检"请求用的请求方法是OPTIONS,表示这个请求是用来询问的。头信息里面,关键字段是Origin,表示请求来自哪个源。

除了Origin字段,"预检"请求的头信息包括两个特殊字段。

(1)Access-Control-Request-Method

该字段是必须的,用来列出浏览器的CORS请求会用到哪些HTTP方法,上例是PUT

(2)Access-Control-Request-Headers

该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段,上例是X-Custom-Header

4.2 预检请求的回应

服务器收到"预检"请求以后,检查了OriginAccess-Control-Request-MethodAccess-Control-Request-Headers字段以后,确认允许跨源请求,就可以做出回应。

HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain

上面的HTTP回应中,关键的是Access-Control-Allow-Origin字段,表示http://api.bob.com可以请求数据。该字段也可以设为星号,表示同意任意跨源请求。

Access-Control-Allow-Origin: *

如果浏览器否定了"预检"请求,会返回一个正常的HTTP回应,但是没有任何CORS相关的头信息字段。这时,浏览器就会认定,服务器不同意预检请求,因此触发一个错误,被XMLHttpRequest对象的onerror回调函数捕获。控制台会打印出如下的报错信息。

XMLHttpRequest cannot load http://api.alice.com.
Origin http://api.bob.com is not allowed by Access-Control-Allow-Origin.

服务器回应的其他CORS相关字段如下。

Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 1728000

(1)Access-Control-Allow-Methods

该字段必需,它的值是逗号分隔的一个字符串,表明服务器支持的所有跨域请求的方法。注意,返回的是所有支持的方法,而不单是浏览器请求的那个方法。这是为了避免多次"预检"请求。

(2)Access-Control-Allow-Headers

如果浏览器请求包括Access-Control-Request-Headers字段,则Access-Control-Allow-Headers字段是必需的。它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段,不限于浏览器在"预检"中请求的字段。

(3)Access-Control-Allow-Credentials

该字段与简单请求时的含义相同。

(4)Access-Control-Max-Age

该字段可选,用来指定本次预检请求的有效期,单位为秒。上面结果中,有效期是20天(1728000秒),即允许缓存该条回应1728000秒(即20天),在此期间,不用发出另一条预检请求。

4.3 浏览器的正常请求和回应

一旦服务器通过了"预检"请求,以后每次浏览器正常的CORS请求,就都跟简单请求一样,会有一个Origin头信息字段。服务器的回应,也都会有一个Access-Control-Allow-Origin头信息字段。

下面是"预检"请求之后,浏览器的正常CORS请求。

PUT /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
X-Custom-Header: value
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

上面头信息的Origin字段是浏览器自动添加的。

下面是服务器正常的回应。

Access-Control-Allow-Origin: http://api.bob.com
Content-Type: text/html; charset=utf-8

上面头信息中,Access-Control-Allow-Origin字段是每次回应都必定包含的。

五、与JSONP的比较

CORS与JSONP的使用目的相同,但是比JSONP更强大。

JSONP只支持GET请求,CORS支持所有类型的HTTP请求。JSONP的优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据。

跨域请求——jsonp与cors相关推荐

  1. 循序渐进Python3(十一) --6--  Ajax 实现跨域请求 jsonp 和 cors

    Ajax操作如何实现跨域请求? Ajax (XMLHttpRequest)请求受到同源策略的限制. Ajax通过XMLHttpRequest能够与远程的服务器进行信息交互,另外XMLHttpReque ...

  2. ajax跨域请求Jsonp实现,使用jsonp实现ajax跨域请求

    Jsonp(JSON with Padding)是资料格式 json 的一种"使用模式",可以让网页从别的网域获取资料. 由于同源策略,一般来说位于 server1.example ...

  3. jQuery中的ajax、jquery中ajax全局事件、load实现页面无刷新局部加载、ajax跨域请求jsonp、利用formData对象向服务端异步发送二进制数据,表单序列化(异步获取表单内容)

    jQuery中使用ajax: 在jQuery中使用ajax首先需要引入jQuery包,其引入方式可以采用网络资源,也可以下载包到项目文件中,这里推荐下载包到文件中:市面上有多个版本的jQuery库,这 ...

  4. AJAX 跨域请求 - JSONP获取JSON数据

    Asynchronous JavaScript and XML (Ajax ) 是驱动新一代 Web 站点(流行术语为 Web 2.0 站点)的关键技术.Ajax 允许在不干扰 Web 应用程序的显示 ...

  5. 【综合】JS跨域方案JSONP与CORS跨域

    缘由: 因为浏览器的同源策略,即是浏览器之间要隔离不同域的内容,禁止互相操作,提高安全性. 为何要跨域: 有时候你想通过自己的网站去获取另一个自己的网站的一些资料信息,但是由于两者域名不同,所以就被同 ...

  6. 跨域与jsonp及cors解决

    1. 什么是跨域? 跨域一词从字面意思看,就是跨域名嘛,但实际上跨域的范围绝对不止那么狭隘.具体概念如下:只要协议.域名.端口有任何一个不同,都被当作是不同的域.之所以会产生跨域这个问题呢,其实也很容 ...

  7. 使用fetch轻松解决JS跨域请求问题——无需CORS,jsonp

    fetch(url, {         method: "POST",   mode: "no-cors",   headers: {     "C ...

  8. asp.net core 创建允许跨域请求的api, cors.

    配置应用方域名. 在webapi中引用cors包,在startup的Configure\ConfigServices中增加启动项配置,在api中增加EnableCors的Attribute属性.即可. ...

  9. 前端跨域请求get_前端跨域请求jsonp实现

    第一步:编写被调用项目的controller. 1.需要集成springmvc,springboot项目集成web就包含springmvc,普通的servlet需要: PrintWriter w = ...

最新文章

  1. “-bash: !”: event not found、echo sudo permission denied
  2. Openfiler的安装和配置
  3. RTMPdump使用相关
  4. linux系统如何打开python_手把手教你在Linux系统下使用Python虚拟环境
  5. datetime方法
  6. mysql数据库表子查询语句_MySQL使用子查询教程
  7. 微信公号“架构师之路”学习笔记(二)-高可用高并发负载均衡的架构设计(冗余+自动故障转移、水平扩展等)
  8. 智能寻迹(循迹)小车项目思路 + 代码
  9. 简单的U盘病毒清理工具 v0.5
  10. oracle数据对应函数,Oracle函数取得姓名对应的拼音
  11. IOS越狱插件汉化工具
  12. Speex manul中文版
  13. 【.Net】asp.net 把图片从CMYK印刷模式转换为RGB原色模式
  14. 吃吃川味水煮鱼 体验别种川菜
  15. 品铂平板电脑刷linux,教你如何把品铂PIPO W8平板电脑升级到win10系统
  16. pythontrycatch所有异常状态_python 一篇搞定所有的异常处理
  17. cesiumjs坐标经纬度转换
  18. 简单使用:eq和.eq
  19. Js逆向——捅了【马蜂窝】的ob混淆与加速乐
  20. 推荐4款能够极大地你改善工作体验的PC软件

热门文章

  1. 两轮差速驱动机器人运动模型及应用分析
  2. java super实例_Java 的 super 学习的一个实例
  3. html背景颜色白色半透明,HTML – 背景颜色:透明意味着什么?
  4. html2canvas在iphone7 iphonex都生成不了
  5. vue 引入自定义js方法并调用
  6. 交换机的VLAN与Trunk配置
  7. 这款折纸机器人玩具,可以帮助孩子学习编程和Robot知识
  8. js文件上传不用点击上传按钮自动上传
  9. @wraps 修饰器:让你的 Python 代码更加简短可爱 | 从简单实例来认识它
  10. 「项目已被 macOS 使用,不能打开」的处理办法