1、事件机制
事件触发三个阶段
window 往事件触发处传播,遇到注册的捕获事件会触发
传播到事件触发处时触发注册的事件
从事件触发处往 window 传播,遇到注册的冒泡事件会触发。
注册事件:
通常用 addEventListener 注册事件,该函数的第三个参数可以是布尔值,也可以是对象。对于布尔值
useCapture 参数来说,该参数默认值为 false。 useCapture 决定了注册的事件是捕获事件还是冒泡事件。
对于对象参数来说,可以使用以下几个参数:
capture,布尔值,和 useCapture 作用一样
once,布尔值,值为true表示该回调只会调用一次,调用后会移除监听
passive,布尔值,表示永远不会调用preventDefault
stopPropagation 阻止事件的进一步传播。
stopImmediatePropagation 也可以实现阻止事件,但是还能阻止该事件目标执行别的注册事件。
node.addEventListener(
'click',
event =>{
event.stopImmediatePropagation()
console.log('冒泡')
},
false
)
事件代理:
如果一个节点中的子节点是动态生成的,那么子节点需要注册事件的话应该注册在父节点上。
例如:
<ul id="ul-container">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
<script>
let ulContainer = document.querySelector('#ul-container')
ulContainer.addEventListener('click',event=>{
console.log(event.target)
})
</script>
//事件代理方式优点:
节省内存,不需要给子节点注销事件。
2、跨域
因为浏览器出于安全考虑,有同源策略。也就是,如果协议,域名或者端口有一个不同就是跨域,Ajax请求会失败。
解决跨域的问题:
JSONP:
原理:利用<script>标签指向一个需要访问的地址并提供一个回调函数来接收数据当需要通讯时。
<script src="http://xxx/api?param1=a¶m2=aa&callback=jsonp"></script>
<script>
function jsonp(data){
console.log(data,'data')
}
</script>
JSONP使用简单且兼容性不错,但是只限于get请求。
CORS:
CORS需要浏览器和后端同时支持。
浏览器会自动进行cors通信,实现 cors通信的关键时后端。只有后端实现了cors,就实现了跨域。
服务端设置 Access-Control-Allow-Origin 就可以开启CORS.该属性表示哪些域名可以访问资源,
如果设置通配符则表示所有网站都可以访问资源。
document.domain
该方式只能用于二级域名相同的情况下,比如,a.test.com 和 b.test.com 适用于该方式。
只需要给页面添加 document.domain = 'test.com' 表示二级域名都相同就可以实现跨域。
postMessage
这种方式通常用于获取嵌入页面中的第三方数据。一个页面发送消息,另一个页面判断来源并接收消息。
// 发送消息端
window.parent.postMessage('message','http://aa.com')
//接收消息端
var mc = new MessageChannel()
mc.addEventListener('message',event=>{
var origin = event.origin || event.originalEvent.origin
if(origin === 'http://aa.com'){
console.log('success---验证通过')
}
})
3、Event loop
任务源可以分为 微任务(microtask) 和 宏任务(macrotask)
微任务包括:process.nextTick , promise , Object.observe , MutationObserver
宏仁务包括: script , setTimeout ,setInterval ,setImmediate ,I/O ,UI rendering。
因为宏任务包括了script,浏览器会先执行一个宏仁务,接下来有异步代码的话,就会先执行微任务。
Event loop 的执行顺序是这样的:
执行同步代码,这属于宏任务。
执行栈为空,查询是否有微任务需要执行
执行所有微任务
必要的话渲染UI
然后开始下一轮 Event loop,执行宏任务中的异步代码。
注意:如果宏任务中的异步代码有大量的计算并且需要操作DOM的话,为了更快的界面响应,我们可以把操作DOM放入
微任务中。
eg:
console.log('script start')
setTimeout(function(){
console.log('setTimeout')
},0)
new Promise(resolve=>{
console.log('promise')
resolve()
}).then(function(){
console.log('promise1')
}).then(function(){
console.log('promise2')
})
console.log('script end')
// script start => Promise => script end => promise1 => promise2 => setTimeout
4、存储
cookie , localStorage ,sessionStorage ,indexDB
cookie :
数据生命周期 一般由服务器生成,可以设置过期时间
数据存储大小 4K
与服务端通信 每次都会携带在 header 中,对于请求性能影响。
localStorage :
除非被清理,否则一直存在。
5M
不参与服务端通信
sessionStorage :
页面关闭就清理
5M
不参与服务端通信
indexDB:
除非被清理,否则一直存在。
无限
不参与服务端通信
对于 cookie,我们还需要注意安全性:
value:如果用于保存用户登录态,应该将该值加密,不能使用明文的用户标识。
http-only: 不能通过JS访问cookie,减少xss攻击
secure: 只能在协议为 https 的请求中携带
same-site: 规定浏览器不能在跨域请求中携带cookie,减少CSRF攻击
Service Worker
通常用来缓存文件,提高首屏速度。
5、渲染机制
浏览器的渲染机制一般分为以下步骤:
处理HTML并构建DOM树
处理CSS 构建 CSSDOM树
将DOM 与 CSSDOM 合并成一个渲染树
根据渲染树来布局,计算每个节点的位置。
调用GPU绘制,合成图层,显示在屏幕上。
图层:
通过以下几个常用属性可以生成新图层:
3D变换: translate3d , translateZ
will-change
video , iframe 标签
通过动画实现的 opacity 动画转换
position:fixed
重绘 repaint 和 回流 reflow :
重绘和回流是渲染步骤中的一小节,但是这两个步骤对于性能影响很大。
重绘是:当节点需要更改外观而不会影响布局的,比如改变 color 就叫重绘。
回流:是当布局或者几何属性需要改变就称为回流。
回流必定会发生重绘,重绘不一定会引发回流。回流所需的成本比重绘高的多,改变深层次的节点很可能
导致父节点的一系列回流。
以下操作可能会导致性能问题:
改变window大小
改变字体
添加或删除样式
文字改变
定位或者浮动
盒模型
减少重绘和回流:
使用 translate 替代 top 。
使用 visibility 替换 display:none ,因为前者只会引起重绘,后者会引发回流(改变了布局)。
不要使用table布局
动画实现可以使用 requestAnimationFrame
参考链接:https://yuchengkai.cn/docs/frontend/browser.html#%E4%BA%8B%E4%BB%B6%E6%9C%BA%E5%88%B6