静态页面

在浏览器脚本的概念没有出现之前,所有的网页都是静态的。我们知道浏览器的工作模式是:

  1. 浏览器向网站服务器发起请求
  2. 网站接受浏览器的请求,返回一些字符串(比如一些组成页面的 HTML 字符串)
  3. 浏览器接收到网站返回的用于组成页面的字符串后,就可以关闭连接了
  4. 浏览器将组成页面的字符串渲染到屏幕上,使得用户可以看到一个可视化的结果

看起来就像下面这样:

                                                                                   Client Request                              +-------------+                                     +--------+
+------+      |  User Agent | +-------------------------------->  |        |
| User +------>             |                                     | Server |
+--^---+      |  (Browser)  | <--------------------------------+  |        |       |          +-------+-----+                                     +--------+       |                  |                Server Response                             |                  |                                                            |                  |                                                            |        +---------v--------+                                                   |        | Close Connection |                                                   |        +---------+--------+                                                   |                  |                                                            |                  |                                                            |         +--------v--------+                                                   ^---------+ Render response |                                                   +-----------------+
复制代码

我们看到,一旦用户代理(浏览器)关闭了和服务器之间的链接之后,客户端和服务器之间将不能继续通信。

动态页面

为了让页面可以给用户带来更多的交互,浏览器开发厂商们制造出了名为浏览器脚本的东西。比如你在浏览一个页面的时候,你觉得页面的字体太小了。在静态页面的时候,页面制作者在右上角给你提供了名为 “放大字体” 的按钮,你点击那个按钮,然后开启一轮新的请求,显著的说就是说你感觉到浏览器刷新了。这其实是浏览器重新从服务器加载页面的资源,只不过这一次的资源是用于显示字体放大后的页面。(尽管这个例子现在看来有些奇怪,因为如果仅仅是改变页面字体大小的话,似乎直接操作 element.style.fontSize 就可以了,不过请求另外一个包含更大字体的内联 CSS 页面在最终效果上也是说得通的,所以这里还请多多包含了。)

浏览器脚本就是一小段由浏览器执行的代码,页面制作者将这一小段代码,和网页面的内容(比如一篇优美的散文,和它右上角的 “放大字体” 按钮)一起返回给浏览器。浏览器接收到页面资源后,首先就是先将散文和 “放大字体” 按钮显示出来。注意到返回的内容实际上还有一段由浏览器执行的代码,页面制作者在这段带代码中告诉浏览器:如果用户点击了 “放大字体” 按钮,那么你就将页面的字体放大。于是,当你点击 “放大字体” 按钮之后,浏览器严格执行页面制作者在脚本中撰写的内容 - 将页面的字体放大。

异步加载

注意在静态页面中浏览器和服务器之间的通信过程。浏览器在向服务器发起了对页面的请求之后,在服务器没有将页面的内容返回之前,页面是无法被显示出来的,最显著的特征就是我们在点击了浏览器的 “刷新” 按钮之后,页面会 “白屏” 一小段时间。

起初浏览器脚本是没有网络通信的功能的,只能做一些页面的特效,比如“点击按钮放大了字体”。不过浏览器厂商发现,如果给脚本赋予网络通信的功能,将使得页面制作者可以给用户提供更好的页面交互体验。于是在早期的 IE 浏览器中,首先赋予了浏览器脚本的通信功能。

浏览器脚本可以和服务器进行网络通信之后,页面制作者可以做出具有更好体验的页面。比如你现在需要搜索商品,假设是要买一本编程的书,你在网页的搜索框中输入了 “编程的数”,很明显是输错了,你将 “书” 错输成了 “数”。在你点击了 “搜索” 按钮之后,进过短暂的白屏之后,页面中显示了:

找不到关于 “编程的数” 的产品,你是不是要找 “编程的书”

很不错,网站给了我们一个提示,这样我们就可以发现自己的输入错误。不过这个体验还是有待提高的,因为每一次的搜索都会有一个短暂的 “白屏”,在白屏期间用户只能等待。在浏览器脚本可以通信之后,搜索就可以以一个异步的方式进行:

  1. 用户在浏览器中输入搜索页面的地址 “search.shop.com”
  2. 浏览器会向网站请求搜索页面的内容,用于显示这个页面
  3. 网站在返回页面的显示内容的同时,包含了一小段脚本,脚本的内容是告诉浏览器 “用户在点击了搜索之后,你给用户一个提示,让用户知道服务器正在紧张的搜索用户所需的资源,然后你显示了提示后,你再向服务器请求搜索的结果,当得到搜索结果后,你再把搜索结果显示给用户”

这样的话,用户不必在搜索时面对页面的刷新时的 “白屏” 了,有一个提示框告诉用户稍等片刻。

同源策略

为了定位网络上的资源,我们采用了统一资源定位符 URL,就像是一个门牌号一样, URL 标识出资源在网络上的位置。我们浏览的网页,其中的内容可能会来自不同的提供者,比如散文来自一位作家,而其中的配图来自一位美术家。散文的 URL 是 http://writer.com/new-world,配图的 URL 是 http://artist.com/new-world

我们需要有一种方式将网络上的资源(比如散文和图画)标识出来,区别它们是来自于不同的作者。如果我们将颗粒度定位到每一个独立的资源,理论上是可行的,但是我们知道作家不可能只有一篇散文,而美术家也不会只有一幅画。于是我们选择了使用:通信协议,完整的域名,以及端口号去描述一个源,只有三者都相同,才标识两个资源是同源的。

下面的几个资源是同源的:

http://example.com/
http://example.com:80/
http://example.com/path/file
复制代码

下面的资源是不同源的:

http://example.com/
http://example.com:8080/
http://www.example.com/
https://example.com:80/
https://example.com/
http://example.org/
http://ietf.org/
复制代码

现在知道了同源,那么同源策略是什么意思呢?同源策略就是,两个不同源的资源相互是不能访问对方的资源的。同源策略主要就是限制脚本的网络访问。

比如我们打开了一个页面 http://example.com,这个页面有两段脚本,一个段使用的内联的方式称为 A,它主要就是在用户点击了按钮之后显示一段文字,告诉用户点击了按钮;另一段作为外部资源进行加载称为 B,B 是 A 的基础代码,比如 B 是 jQuery,它被放在了 http://cdn.jquery.com 上。首先我们知道,这两段代码如果按照同源的定义,肯定是不同源的。也就是说我们在 http://example.com 的页面上是不能加载 http://cdn.jquery.com 上的资源的。

好像与现实情况有点矛盾。之所以现在可以,是因为浏览器为了适应实际的生产情况,放宽了对同源策略的检查,因为我们知道,不可能将所有的资源都放在同一台机器上。那么在页面完全加载好之后,页面中的脚本(内联的和外部引入)的都被浏览器归纳到了和当前页面相同的源,都属于 http://example.com 了。这么做的意思就是,脚本无法访问与之不同源的资源,也就是此时的脚本(内联的和外部引入的)无法访问资源 https://example.com/user-info

绕过同源策略

有时比如上面的例子,我们确实需要在脚本中加载和当前页面不同源的资源,比如在 http://example.com 页面中使用脚本加载 https://example.com/user-info 中的内容。那么如何绕过浏览器的同源策略呢?

我们知道直接在页面中载入不同源的外部资源是可以的,那么我们就可以动态的载入一段外部的脚本。

首先,我们的 http://example.com 中有这么一段脚本:

(function () {window['showNickname'] = function (json) {alert(json['nickname']);};var userInfoServiceUrl = 'https://example.com/user-info';var doCrossSiteRequest = function (url, callback) {var script = document.createElement('script');script.src = url + '?callback=' + callback;var head = document.getElementsByTagName('head');if (head[0]) {head.append(script);}};document.querySelector('#btnShowNickName').addEventListener('click', function () {doCrossSiteRequest(userInfoServiceUrl, 'showNickname');});
})();
复制代码

https://example.com/user-info 的服务端内容为:

<?php$callback = isset($_GET['callback']) ? $_GET['callback'] : null;
if ($callback === null) die('invalid request');$userInfo = ['nickname' => 'net-user'
];
$json = json_encode($userInfo);echo "{$callback}({$json});";
复制代码

那么在浏览器加载了 https://example.com/user-info 的脚本为,得到的是:

showNickname({"nickname":"net-user"});
复制代码

这就和我们最先在 http://example.com 留下的 window['showNickname'] 对接上了。

浏览器异步加载和同源策略相关推荐

  1. 异步加载js的三种方法

    js加载时间线 : 它是根据js出生的那一刻开始记录的一系列浏览器按照顺序做的事,形容的就是加载顺序,可以用来优化什么东西,理论基础,背下来. 1.创建Document对象,开始解析web页面.解析H ...

  2. javascript异步加载

    这里先解释html在浏览器的一般加载流程. 用户输入搜索内容,浏览器发送搜索内容到服务器,服务器返回html文件.这个过程十分复杂,包含多个层次的数据传输.这里只讨论js代码加载,不再展开. 浏览器加 ...

  3. 7.JavaScript异步加载与加载时间线

    JavaScript异步加载与加载时间线 上一篇 JavaScript浅层克隆和深层克隆 下一篇 JavaScript事件处理模型 - 事件冒泡,捕获 文章目录 JavaScript异步加载与加载时间 ...

  4. js如何使浏览器允许脚本异步加载

    js如何使浏览器允许脚本异步加载 如果脚本体积很大,下载和执行的时间就会很长,因此造成浏览器堵塞,用户会感觉到浏览器"卡死"了,没有任何响应.这显然是很不好的体验,所以浏览器允许脚 ...

  5. 运用单例模式、建造者模式和策略模式实现异步加载Android联系人资料

    学完设计模式很久了,最近又在看Android联系人提供程序的官方文档,于是就想实现一个方便的联系人管理程序demo,而联系人管理程序demo的核心就是要实现一个异步加载联系人资料的类,于是就有了下文. ...

  6. 浏览器渲染阻塞与优化-详解推迟加载、异步加载。

    我认为一个前端工程师是否优秀,很大程度上取决于对前端性能上优化的功力.所以性能优化对前端真的很重要!!! 本文介绍了什么是阻塞.为什么会阻塞?阻塞优化常用的5种方式以及他们的注意事项. 浏览器渲染阻塞 ...

  7. 浏览器svg插件_Archer-svgs: 异步加载svg方案

    Github地址: ShanaMaid/archer-svgs 哇哦!无限的svgs!你可以使用archer-svgs去异步加载svg并将它缓存在localStorage里,当你再次使用已经加载过的s ...

  8. vue异步加载amap高德地图,解决刷新浏览器地图不显示问题

    创建amap.js /** 异步创建script标签*/ export default function MapLoader () {return new Promise((resolve, reje ...

  9. 腾讯X5 浏览器内核加载

    1.腾讯X5浏览器 sdk 官网 地址 腾讯浏览服务 2.腾讯X5 浏览器 sdk 加载 需要内存卡权限 加载成功率才高,因为有了内存读取权限,可以读取腾讯QQ 和微信的X5 内核,共用他们的内核,不 ...

最新文章

  1. 如何定义和搭建可靠人工智能系统的规则?
  2. .NET设计模式(2):单件模式(Singleton Pattern)
  3. LCS2005客户端配置详解:LCS2005系列之二
  4. 情怀满满 新一代观影神器坚果1895
  5. sortable 拖拽时互换目标的位置_双端通用型JS拖拽插件的封装与应用
  6. LeetCode 881. 救生艇(贪心,双指针)
  7. JavaScript异步编程【下】 -- Generator、Async/await
  8. html页面在f5刷新后把所有值清零,页面刷新列表内容不丢失
  9. android内存泄漏MAT,利用Android Studio、MAT对Android进行内存泄漏检测
  10. python 操作ps脚本_脚本之王python完美实现换脸技术,毫无PS痕迹!
  11. Excel把表中一个单元格对应多个数据汇总到一个单元格内
  12. Python实现将人脸表情数据集fer2013转化为图像形式存储本地
  13. java发邮件要收费吗,2年以上经验必看
  14. [Unity-25] Unity中让游戏对象消失或隐藏的几种方法
  15. 头歌-信息安全技术-Linux之用户管理
  16. ElasticSearch分页查询的3个坑
  17. 什么是JWT及在JAVA中如何使用?
  18. linux 限速命令,Linux路由器限速设置进程
  19. Android实现发短信,打电话
  20. linux xhci源码,第四十六篇:Linux中的USB XHCI HOST TRANSFER RING的相关数据结构 (1)

热门文章

  1. freemarker 解析对象的某元素_Freemarker常用技巧(三)
  2. Error while waiting for device: The emulator process for AVD Pixel_2_XL_API_28 was killed.
  3. Java的Socket编程C/S小例子
  4. RabbitMQ 与 Erlang 安装与配置
  5. mysql使用联合唯一索引会影响查询效率吗_想进大厂,这些Mysql索引底层知识你是必须知道的。...
  6. 广数系统980tdb系统说明书_不同数控系统中各个倒角指令的用法
  7. 云丁智能锁使用说明书_出门不再带钥匙 云丁D2F智能指纹锁新体验
  8. python成语接龙代码_#python# 成语接龙(二)
  9. esp分区创建 linux_Elementary OS - 号称最漂亮的 Linux 发行版
  10. MySQL数据库反向生成powerdesigner模型