本文总结一些 HTML 相关的知识,留作以后查看,包括同源策略、跨域、HTML 语义化等。
同源策略
同源策略是浏览器的一种安全策略,什么是同源呢?同时满足以下几个要求的域,就是同源:
- 域名相同
- 协议相同
- 端口相同
那么如果满足以下一个或多个要求的域,就是不同源:
- 域名不相同
- 协议不相同
- 端口不相同
同源策略的限制
同源策略作为一种安全策略,从浏览器层面限制了不同域之间的资源访问,这是即为重要的。设想,如果某个域能够轻易拿到另一个域的 DOM、Cookie 等信息,还有什么安全可言呢?
具体说来,同源策略限制了不同域之间以下这些资源的访问:
- Cookie、LocalStorage、IndexDB 等本地储存
- DOM
- AJAX
如果访问不同源域中的资源,我们就称之为跨域。
跨域 Cookie 共享
举个例子:http://zhaopin.baidu.com/
和 https://tieba.baidu.com/
这两个域,从同源策略的角度来讲,他们是不同源的,因此无法在 https://tieba.baidu.com/
域中访问 http://zhaopin.baidu.com/
域中的受限资源。
但 Cookie 是个特例,默认情况下 https://tieba.baidu.com/
域是无法访问 http://zhaopin.baidu.com/
域中的 Cookie 的,但是由于二者的 一级域名相同,因此在设置 Cookie 的时候可以指定 domain
属性,将该属性设置为二者共同的一级域名 baidu.com
,那么在就可以在这两个域之间共享 Cookie 了。
你可以自己做个测试,打开 http://tieba.baidu.com/,在控制台中输入:
document.cookie = "test1=memeda1"
document.cookie = "test2=memeda2;domain=baidu.com"
打开 http://zhaopin.baidu.com/ ,在控制台中输入(最好先清除该域下的 Cookie,这样更明显一些):
document.cookie
你可以看到如下输出:
"test2=memeda2"
至此,我们通过在设置 Cookie 时,将 domain
设置为一级域名,实现了与其他同一级域名的域的 Cookie 共享。
当然,这种方式也只限于同一级域名的域之间使用。
不受同源策略限制的 HTML 元素
上面说到的 Cookie 共享,就属于规避同源策略的一种方式。那么在 HTML 标签中,就存在一些标签不受同源策略的影响:
<img />
<script></script>
<link />
<iframe></iframe>
- 一些 CSS 属性,如
background:url()
关于不受同源策略限制的标签,我暂时也就想起了这么多,如果您还知道其他的标签,欢迎补充。
现在想一想,我们能在自己的网站上通过 <img />
标签引入其他网站的图片,通过 <script></script>
标签和 <link />
标签引用其他网站或 CDN 上的脚本和样式文件,以及通过 <iframe></iframe>
标签在我们的网站中嵌入其他的网站等等,这些技术的背后都是同源策略。可见,同源策略在为我们关闭了一扇门的同时,又为我们打开了一扇窗,如果没有这些开放原则,我们网站上用到的所有资源都需要从本域中引用,也就没有了 CDN,互联网也就没有那么丰富多彩了。
JSONP
前面说过,AJAX 是受同源策略的限制的,因此我们要请求其他域中的资源,就不能使用 AJAX 了。
前面说到 <script></script>
标签不受同源策略的影响,因此可以利用这个“漏洞”来实现跨域资源访问。
首先,当我们从别的域中引入一个脚本文件,浏览器会去下载这个脚本文件,并解释执行。那么,如果这个脚本文件中是一个函数调用,恰巧我们本域中也有一个对应的函数声明呢?显然,浏览器会在下载完这个脚本后执行函数调用,也就会调用我们本域中声明的函数了。
这种跨域请求的方式也叫作 JSONP。
具体说来,如果我们向一个域中发起这样的请求:
<script src="www.memeda.com/api?callback=jsonp_callback"></script>
同时我们本域中也定义了一个 jsonp_callback
函数:
<script>
function jsonp_callback(data){
doSomethingWithData(data)...
}
</script>
www.memeda.com
的服务器在收到 HTTP 请求后,发现有个 callback=jsonp_callback
的 Query String,就知道这 JSONP,服务器响应时就这样响应:
ctx.body = `jsonp_callback(${data})`;
那么浏览器在下载完脚本文件后,就会执行 jsonp_callback
函数,该函数的参数就是从后端请求到的数据,然后对该数据进行相应处理,至此一个 JSONP 过程就完成了。
其他跨域方案
跨域的方案还有很多,比如:
- HTML5 新规范中的
postMessage
API - Websocket 协议,该协议不受同源策略的影响
- 服务端设置 CORS 跨源资源
- 反向代理
网络测速
在移动开发中,网络请求弥足珍贵,当用户的网速不好时,为了最大限度的提高用户体验,可以向其展示一个只具备基本功能的简版网站,当用户网速较好时,向其展示功能完全的网站,这就需要用到网络测速。
前端常用的网络测速,是下载一个 1KB 的图片,根据下载时长来判断用户的网速:
<script>
function testSpeed(url){
const img = new Image();
const startTime = +new Date();
let requestTime;
img.src = url;
img.onload = () => {
const endTime = +new Date();
// 将毫秒转换为秒
requestTime = (endTime - startTime) / 1000;
doSomethingWithRequestTime(requestTime)
}
}
</script>
当然这种方式不精确,但也能为之一用,大多数情况下是够用了,想要更精确的话可以试试 Web 端的多普勒测速。
HTML 语义化
所谓 HTML 语义化,就是在写 HTML 的时候使用正确的 HTML 标签,尽可能的有语义,这样能让网站的可读性更高,有利于 SEO。
此外,在写 HTML 文件时,尽量少用 HTML 元素,能用一个元素解决的尽量使用一个元素解决。
比如,一个 <div></div>
元素至少可以拆分成三个元素:该元素本身,:before
伪元素,:after
伪元素,如果再加上 box-shadow
等样式,就可以将一个元素展现成很多个元素。(我见过仅用一个元素,通过不同的 box-shadow
的应用画出一个蒙拉丽莎的,真是厉害)
节约 HTML 元素,有以下几个好处:
- 减少 HTML 文件大小,加快访问速度
- 减少 DOM 树渲染的时间,加快访问速度
- 减少页面的重排和重绘,加快访问速度
另外,关于 HTML 的语义化,有两篇参考文章:
最后,提供一个我常用的 HTML 布局风格:
<!-- 头部和导航 -->
<header>
<nav></nav>
</header>
<!-- 内容主体 -->
<div class="content">
<!-- 区块1 -->
<section></section>
<!-- 区块2 -->
<section></section>
<!-- 侧边栏 -->
<aside></aside>
</div>
<!-- 页面底部 -->
<footer></footer>
附:参考资料
浏览器同源政策及其规避方法
HTML语义化
HTML语义化
完。