- window往事件处发处传播遇到注册的捕获事件会触发
- 传播到事件触发处时触发注册的事件
- 从事件触发处往window传播,形成冒泡,注册的冒泡事件会被触发
通常用addEventListener()
,
node.addEventListener(
'click', // 事件类型
() => {}, // 回调函数
Boolean || { // 第三个参数可以是布尔值,也可以是对象, 布尔true是捕获, false是冒泡, 默认false
capture: true || false, // 和布尔意思一样
once: Boolean, // 是不是只执行一次
passive: Boolean // 是不是可以取消默认行为
}
)
<ul id="ul">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
<script>
let ul = document.querySelector('#ul')
ul.addEventListener('click', event => { // 只监听父元素
console.log(event.target)
}) // 优点: 节省内存,不需要关心子组件的事件
</script>
原理:
利用<script>
标签没有跨域限制的漏洞
仅限于 get 请求
自己封装JSONP
function JSONP(url, jsonpCallBack, success) {
let script = document.createElement('script');
script.url = url;
script.async = true;
window[jsonpCallBack] = function (data) {
success && (data);
}
document.body.appendChild(script);
}
jsonp('http://', 'callback', function (value) {
console.log(value)
})
使用JSONP时对服务端传递的数据也是有要求的
CORS 需要浏览器和后端同时支持。IE 8 和 9 需要通过 XDomainRequest
来实现。
浏览器会自动进行 CORS 通信,实现 CORS 通信的关键是后端。只要后端实现了 CORS,就实现了跨域。
服务端设置 Access-Control-Allow-Origin
就可以开启 CORS。 该属性表示哪些域名可以访问资源,如果设置通配符则表示所有网站都可以访问资源
该方式只能用于二级域名相同的情况下,比如 a.test.com
和 b.test.com
适用于该方式。
只需要给页面添加 document.domain = 'test.com'
表示二级域名都相同就可以实现跨域
这种方式通常用于获取嵌入页面中的第三方页面数据。一个页面发送消息,另一个页面判断来源并接收消息
// 接收消息
window.parent.postMessage('message', 'http://test.com')
// 接收消息端
var mc = new MessageChannel()
mc.addEventListener('message', event => {
var origin = event.origin || event.originalEvent.origin
if (origin === 'http://test.com') {
console.log('验证通过')
}
})
因为JS当时设计是为操作DOM的,所以设计为单线程比较方便,比如如果一个线程创建DOM,另一个线程删除DOM,就搞笑了
JS在执行时会有执行环境, 这些执行环境会按规则加入到执行栈中,如果遇到异步代码,则挂起到task中,如果执行栈为空,就会从task里面取出异步代码执行
掌握事件环,一定掌握
-
宏任务:
script
,setTimeout
,setInterval
,setImmediate
,I/O
,UI rendering
-
微任务:
process.nextTick
,promise
,Object.observe
,MutationObserver
先执行同步代码,然后微任务,然后宏任务
1. 函数入栈,当Stack中执行到异步任务的时候,就将他丢给WebAPIs,接着执行同步任务,直到Stack为空;
2. 此期间WebAPIs完成这个事件,把回调函数放入队列中等待执行(微任务放到微任务队列,宏任务放到宏任务队列)
3. 执行栈为空时,Event Loop把微任务队列执行清空;
4. 微任务队列清空后,进入宏任务队列,取队列的第一项任务放入Stack(栈)中执行,回到第1步。
cookie, localStorage, sessionStorage, IndexDB
特性 | Cookie | localStorage | sessionStroage | IndexDB |
---|---|---|---|---|
数据生命周期 | 服务器生成,可以设置过期时间 | 除非被清理,否则一直存在 | 页面关闭就清理 | 除非被清理,否则一直存在 |
数据储存大小 | 4k | 5m | 5m | 无限 |
与服务端通讯 | 强缓存不会在请求头里出现, 协商缓存每一次会在请求头出现,如果服务器显示缓存可用,则会返回代码304,因为每一次都在请求头里,所以会有性能影响 | 不参与 | 不参与, 不能在同源窗口共享 | 不参与 |
session 是存储在服务器中的
service worker充当应用程序和浏览器之间的代理服务器,也可以在网络可用时充当浏览器和网络的代理,目前该技术用作缓存文件提高首屏速度
浏览器渲染分为以下几个步骤
- 处理html构建DOM树
- 处理CSS构建CSSOM树
- 将DOM和CSSOM合并为一个渲染树
- 根据渲染树布局,并计算元素位置
- 调用GPU绘制,显示在屏幕上
注意事项:
- 构建CSSOM树会阻塞渲染,并且CSSOM树的绘制十分消耗性能,越具体的选择器执行速度越慢,所以要保持扁平选择
- 当HTML渲染到script标签时,会暂停构建DOM,JS解析完成后再从暂停地方开始,所以首屏想要更快,就别在首屏加载JS,
了解过PS的都知道图片是一层一层堆积的,浏览器也是这样,并且图层越多性能越差
一行代码让chrome崩溃:
body {
transform:scale(10000)
}
以下几个属性可以生成新图层,慎用
- 3D变换: translate3D, translateZ, transform等
- will-change
- video, iframe 标签
- 通过动画实现的opacity转换
- position: fixed
重绘与回流是渲染机制中的一部分
- 重绘就是不改变布局,元素大小等,只改变元素表现的比如颜色,透明度等
- 回流就是改变元素大小的,整个网页需要重新绘制
回流性能成本比重绘高得多
重绘不一定导致回流,回流一定导致重绘
以下几个动作会导致重绘或回流
- window大小改变
- 改变字体
- 添加或删除样式
- 文字改变
- 定位或浮动
- 盒模型
重绘和回流与Event Loop有关
- 当 Event loop 执行完 Microtasks 后,会判断 document 是否需要更新。因为浏览器是 60Hz 的刷新率,每 16ms 才会更新一次。
- 然后判断是否有
resize
或者scroll
,有的话会去触发事件,所以resize
和scroll
事件也是至少 16ms 才会触发一次,并且自带节流功能。 - 判断是否触发了 media query
- 更新动画并且发送事件
- 判断是否有全屏操作事件
- 执行
requestAnimationFrame
回调 - 执行
IntersectionObserver
回调,该方法用于判断元素是否可见,可以用于懒加载上,但是兼容性不好 - 更新界面
- 以上就是一帧中可能会做的事情。如果在一帧中有空闲时间,就会去执行
requestIdleCallback
回调。
- 使用translate代替top
- 使用visibility代替display:none
- 把DOM离线后修改(先设置为display: none, 然后修改完成后插入)
- 不要把DOM节点属性放到循环里去
- 尽量不要使用table布局
- 实现动画,动画越快,回流次数越多,可以使用requestAnimationFrame
- CSS 选择符从右往左匹配查找,避免 DOM 深度过深
- 将频繁运行的动画变为图层,图层能够阻止该节点回流影响别的元素。比如对于
video
标签,浏览器会自动将该节点变为图层。