跨域
跨域本质是浏览器基于同源策略的一种安全手段
同源策略
浏览器的安全功能,协议、域名、端口一致则是同源,非同源的限制:
- 无法通过 XMLHttpRequest 或 fetch 发起跨域请求
- 无法访问不同源的文档的 DOM
- 无法读取其他域的 Cookie、Storage
简单请求
- 使用下列方法之一:
GET
、POST
、HEAD
- 标头字段自定义的只能有:
Accept
、Accept-Language
、Content-Type
、Content-Language
、Range
Content-Type
所指定的媒体类型的值仅限于下列三者之一:text/plain
、multipart/form-data
、application/x-www-form-urlencoded
针对简单请求,浏览器不会拦截。但是非简单请求,浏览器会发送 预检请求(OPTIONS
)
解决跨域方法
1.CORS (Cross-Origin Resource Sharing,跨域资源共享)
服务端设置响应头:Access-Control-Allow-Origin
,来允许或限制资源的跨域访问
2.代理服务器
在服务器端设置代理,将前端请求先发送到同源的后端服务器,然后由后端服务器转发请求到目标服务器
3.JSONP
通过创建 <script>
标签来请求跨域数据,然后服务器响应时将数据包装在回调函数中,实现跨域的原理是利用 script 标签没有跨域限制,通过 src 指向一个 URL,最后跟一个回调函数 callback(仅支持 GET 请求)
4.WebSocket
WebSocket 连接不受同源策略限制
安全问题
1.XSS (Cross-site scripting) 跨站脚本攻击
是一种代码注入攻击。攻击者将恶意代码植入到提供给其它用户使用的页面中,盗取存储在客户端的 cookie 或者用于识别客户端身份的敏感信息
根据攻击的来源,XSS 攻击可分为:
- 存储型:攻击者将恶意代码提交到目标网站的数据库中
- 反射型:攻击者构造出特殊的 URL,网站服务端将恶意代码从 URL 中取出,拼接在 HTML 中返回给浏览器
- DOM 型:攻击者构造出特殊的 URL,前端 JS 执行恶意代码
预防
- 对用户输入和提交的内容进行转义和过滤
- 在使用 .innerHTML、.outerHTML、document.write() 时要注意,不要把不可信的数据作为 HTML 插到页面上
httpOnly
Cookie: 禁止 JavaScript 读取某些敏感 Cookie
2.CSRF (Cross-site request forgery) 跨站请求伪造
用户在已登录网站的情况下,攻击者通过引诱用户访问一个恶意网站或链接,发送伪造的请求到已登录的网站地址。由于浏览器会自动附带当前用户的身份验证信息(如 Cookies),目标应用程序会误以为该请求是用户自身发起的合法请求
预防
- 请求信息中携带 token
- 将 Cookie 的
SameSite
属性设置为Strict
或Lax
,防止跨站请求携带 Cookies
3.点击劫持
攻击者将需要攻击的网站通过 iframe 嵌套的方式嵌入自己的网页中,并将 iframe 设置为透明,在页面中透出一个按钮诱导用户点击
预防
设置 HTTP 响应头 X-Frame-Options
:
DENY
:禁止任何页面通过 iframe 嵌入当前页面SAMEORIGIN
:允许相同源的页面通过 iframe 嵌入当前页面
性能优化
资源方面:
- 图片压缩
- gzip 压缩
打包方面:
- tree-shaking 去除无用代码
- 使用 plugin 压缩代码
- 代码进行拆分分离
- 进行并发构建(HappyPack 插件)
代码方面:
- 路由懒加载
- 按需引入第三方包代码
- HTML 中的 JS 文件使用 defer 或 async 加载
- 大数据使用懒加载
- 减少全局引入资源(如字体文件),页面访问时再加载
- CSS 合理使用选择器,不要嵌套太深
- 减少 DOM 操作
- 使用预加载 prefetch 对将来可能用到的资源进行提前缓存
网络方面:
- HTTP 缓存
- 使用 CDN 存放资源
地址栏输入 URL 敲下回车后发生了什么
事件循环
JavaScript 是单线程的语言,所有任务(同步/异步)都在主线程执行,事件循环是实现单线程非阻塞的机制
JavaScript 执行的异步代码分为宏任务和微任务
- 宏任务(MacroTask):
script
、setTimeout
、setInterval
、I/O 操作 - 微任务(MicroTask):
Promise.then/catch/finally
、MutationObserver
requestAnimationFrame 发生的顺序会是在下次页面重绘之前操作
事件循环流程
- 所有任务都会在主线程上执行,形成一个执行栈,执行整个脚本的同步代码
- 遇到异步任务,放入任务队列
- 执行完执行栈的同步代码
- 执行所有微任务,直到微任务队列清空
- 再执行一个宏任务
- 重复步骤 4~5,直到所有任务完成
请说明浏览器中的事件循环 (Event Loop)|ExplainThis
最常见的事件循环 (Event Loop) 面试题目汇整|ExplainThis
JS: 一战吃透Promise精修版 - 掘金
Tip
await
执行完后面的代码之后,会将它下面的代码放入微任务队列.then
后面再接.then
会继续放入微任务队列末尾,一次执行完
IntersectionObserver
检测目标元素与视口(viewport)或某个祖先元素交集变化
应用:懒加载图片、无限滚动