defer和async
两者都是异步加载js,加载阶段不会阻塞HTML解析,执行时机不同
- defer 加载完js会在HTML解析完之后执行,多个defer可以保证执行顺序
- async 加载完js会立刻执行,多个async不能保证执行顺序
加载阶段:浏览器把资源下载到本地的过程
解析阶段:意思就是将一个元素转换成另一个形式的过程,比如HTML解析,文件下载到本地就是包含字符串的文件,浏览器将字符串读取到内存,对其编译,将字符串转换成一种易于表达的数据结构(DOM树)。
DOMContentLoaded和load
- DOMContentLoaded当HTML加载并解析完后执行,无需等待样式表、图片等资源的加载
- load需要等待页面的HTML、JS、CSS、图片等资源全部加载完之后执行
浏览器输入URL发生了什么
- 浏览器进程通过进程间通信(IPC)把URL发送给网络进程
- 网络进程判断该URL是否在缓冲当中,有缓存直接将结果返回给浏览器进程,
- 没有缓存进行DNS解析,建立TCP连接,发送请求,最后返回数据,如果数据是下载内容会提交给浏览器的下载管理器
- 准备渲染进程:默认一个标签一个渲染进程,如果是同一站点就复用同一个渲染进程
- 提交文档:网络进程接收完数据会通知浏览器进程,浏览器进程会通知渲染进程和网络进程进行数据传输,此时会建立管道进行数据通信,传输完之后渲染进程会通知浏览器进程(管道就是操作系统在内核中开辟的一段缓冲区,进程A可以将需要交互的数据拷贝到这段缓冲区,进程B就可以读取了)
- 渲染阶段:DOM树、CSS树、Layout布局树、Layer图层树
- 关闭TCP连接
图层:拥有层叠上下文元素(设置定位、opacity、will-change等)、需要裁剪的地方
图层树构建:渲染进程将每个图层的绘制顺序放置在图层列表中,列表交给合成线程来绘制,合成线程将图层分成图块(图层大视口小),并在光栅化线程池将图块转换成位图(GPU加速),最后将位图发送给合成线程
重排和重绘
XSS攻击
Cross Site Scripting 跨站脚本攻击,通过巧妙的方法注入恶意脚本使用户加载执行。可盗取用户信息(窃取Cookie),分为存储型、反射型和文档型。
- 存储型:恶意脚本存储在服务端,客户端获取后执行
- 反射型:恶意脚本是通过作为网络请求的参数,经过服务器,然后再反射到客户端执行
- 文档型:数据传输过程劫持网络数据包,修改HTML文档。
预防XSS攻击
- 对用户输入转码
- 开启CSP,本质是开启白名单,告诉浏览器哪些资源可以加载。
1.设置 HTTP 首部中的 Content-Security-Policy
2.设置meta标签:<meta http-equiv="Content-Security-Policy">
- Cookie设置HttpOnly:JavaScript 便无法读取 Cookie 的值
CSRF攻击
CSRF(Cross-site request forgery), 即跨站请求伪造,指的是攻击者诱导用户点击链接,打开第三方网站,然后利用用户目前的登录状态发起跨站请求。主要有三种攻击类型
- GET类型:img的src属性是会进行请求的,黑客可以利用这点去进行攻击,同时Cookie又是紧跟域名的,发起请求的同时Cookie也会被带上
- POST类型:构建一个表单,默认隐藏,用户进入页面自动提交表单
- 链接类型:在a标签构建一个请求,诱导用户点击
预防CSRF攻击
- 设置Cookie的SameSite属性为严格模式Strict,禁止第三方获取Cookie
- CSRF Token验证,服务器向浏览器返回一个随机字符串作为token,后面请求每次请求都携带这个token给服务端验证
跨域常用解决方案
浏览器同源策略:协议、域名、端口都相同,否则就会跨域
JSONP
利用<script>
标签没有跨域限制,src带有callback参数,服务端将结果数据拼接到回调函数中,返回给浏览器,浏览器执行。缺点就是只能发生GET请求。
跨域资源共享(CORS)
如果是普通跨域:服务端设置Access-Control-Allow-Origin为*
或Origin
值
如果需要传递Cookie:
- 前端设置withCredentials:true
- 服务端设置Access-Control-With-Credentials:true
- 服务端设置Access-Control-Allow-Origin设置为
Origin
值(非*)
nginx代理跨域
通过Nginx配置一个代理服务器域名与domain1相同,端口不同)做跳板机,反向代理访问domain2接口,并且可以顺便修改cookie中domain信息,方便当前域cookie写入,实现跨域访问。
server {
listen 81;
server_name www.domain1.com;
location / {
proxy_pass http://www.domain2.com:8080; #反向代理
proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie里域名
index index.html index.htm;
# 当用webpack-dev-server等中间件代理接口访问nignx时,此时无浏览器参与,故没有同源限制,下面的跨域配置可不启用
add_header Access-Control-Allow-Origin http://www.domain1.com; #当前端只跨域不带cookie时,可为*
add_header Access-Control-Allow-Credentials true;
}
}
中间件
node中间件实现跨域代理,原理大致与nginx相同,都是通过启一个代理服务器,实现数据的转发
存储
- Cookie:用于记录用户状态的一种方式,最大容量4k,紧跟域名
- LocalStorage:HTML5提供的本地存储方式,最大容量5M
- SessionStorage:HTML5提供的本地存储方式,最大容量5M,窗口关闭就失效了
- indexDB:一种本地的数据库存储机制,能存储大量数据(取决硬盘容量)
事件
事件机制
分为三阶段:捕获阶段、目标阶段、冒泡阶段
- 捕获阶段(外到内):事件从window流向目标节点,在途径节点触发捕获事件,直到目标节点
- 目标阶段:事件到目标节点就触发
- 冒泡阶段(内到外):事件在目标节点触发后不会停止,往window冒泡
addEventListener
监听事件第三个参数默认是false,代表冒泡事件。设置true代表捕获事件。
也可以设置成一个对象,有三个属性:
- capture:布尔值,同上
- once:布尔值,为true表示监听函数执行一次就解绑
- passive:布尔值,为true表示永远不会调用
preventDefault
事件委托(事件代理)
事件的委托是指将事件统一绑定给元素的共同的祖先元素,这样当后代元素上的事件触发时,会一直冒泡到祖先元素,从而通过祖先元素的响应函数来处理事件。事件委派是利用了冒泡,通过委派可以减少事件绑定的次数,提高程序的性能,节省内存。
例:给下述代码的p标签绑定点击事件
<div>
<p>1</p>
<p>2</p>
<p>3</p>
</div>
给每个p绑定监听事件
[].map.call(document.querySelectorAll('div p'),function(item){
item.addEventListener('click',function(e){
console.log(e.target.innerText);
})
})
使用事件委托
document.querySelector('div').addEventListener('click',function(e){
console.log(e.target.innerText);
})