css + html + 浏览器

1,012 阅读41分钟

css

CSS 选择器权重以及它如何工作

浏览器通过权重来判断哪些属性值与一个元素最为相关,从而在该元素上应用这些属性值。

  • !important优先级最高,但也会被权重高的important所覆盖
  • 行内样式总会覆盖外部样式表的任何样式,除了!important
  • 如果两个权重不同的选择器作用在同一个元素上,权重值高的css规则生效
  • 如果两个相同权重的选择器作用在同一个元素上,后面出现的选择器为最后规则
  • 权重相同时,与元素距离近的选择器生效

权重的5个等级

  1. !important不推荐使用
  2. 内联样式(1000)
  3. ID选择器(0100)
  4. 类选择器/属性选择器/伪类选择器(0010)
  5. 元素选择器/关系选择器/伪元素选择器(0001)
  6. 通配符选择器(0000)

盒模型

页面渲染时,dom 元素所采用的布局模型。可通过 box-sizing 进行设置。根据计算宽高的区域可分为:

  • content-box (W3C 标准盒模型) width/height 只是 width/height 值
  • border-box (IE 盒模型) width/height 包括了padding、border
  • 避免触发 IE 盒子的方法是使用 <!DOCTYPE html> 声明,告诉 IE 采用 w3c 盒子模型

重排和重绘

渲染页面浏览器从下载文件至本地到显示页面是个复杂的过程,这里面包含了重排和重绘。

  • 渲染引擎会解析HTML文档来构建DOM树
  • 渲染引擎也会用css解析器来解析css文档构建cssom树
  • 接下来DOM树和cssom树关联起来构成渲染树
  • 然后浏览器按照渲染树进行布局,计算节点在页面上的大小和位置
  • 把节点绘制到页面上

浏览器对重排重绘的优化

浏览器会对频繁的回流或重绘操作进行优化,浏览器会维护一个队列,把所有引起回流和重绘的操作放入队列中,如果队列中的任务数量或者时间间隔达到一个阈值的,浏览器就会将队列清空,进行一次批处理,这样可以把多次回流和重绘变成一次。当访问需要返回最新的布局信息时,浏览器会立刻清空队列。

触发重排: 渲染树中的一部分或全部因为元素的规模尺寸、布局、隐藏等改变,需要重新经过计算,重新页面布局

  • 页面渲染初始化
  • 元素尺寸的改变: 大小、内外边距、边框、内容的改变(文本的改变或图片被另一个不同尺寸的图片所替代)
  • 元素位置的改变、使用动画
  • 添加或删除可见的DOM元素
  • 浏览器窗口尺寸的变化
  • 激活 CSS 伪类(例如::hover)
  • 读取某些元素属性: offsetLeft/Top/Width/Height、clientTop/Left/Width/Height、scrollTop/Left/Width/Height、getComputedStyle()、getBoundingClientRect()、scrollTo()
getBoundingClientRect,方法返回元素的大小及其相对于视口的位置。
如果需要获取相对于整个页面的位置需要加上window.scrollXwindow.scrollY,
或者使用window.pageXOffsetwindow.pageYOffset

触发重绘: 当元素样式的改变不影响布局时,浏览器将使用重绘对元素进行更新

  • 改变元素的外观属性: color、background-color
  • visibility、outline等

避免重排和重绘

  • css:
    • 避免使用 table 布局
    • 避免 css 层级过多、避免设置多层内联样式
    • 避免使用 CSS 表达式(例如:calc())
    • 使用 visibility 替换 display: none
    • 将动画效果应用到 position 属性为 absolute 或fixed 的元素上
    • CSS3 硬件加速(GPU加速)
  • js:
    • 避免频繁使用会触发重绘的方法,可以使用变量存储
    • 避免频繁操作样式,最好一次性重写style属性,或者将样式列表定义为class并一次性更改class属性
    • 避免频繁操作DOM,创建一个documentFragment,在它上面应用所有DOM操作,最后再把它添加到文档中
    • 先为元素设置 display: none,操作结束后再把它显示出来

GPU加速

利用 CSS3 的transform、opacity、filter这些属性就可以实现合成的效果,也就是大家常说的GPU加速

GPU加速的原因

在合成的情况下,会直接跳过布局和绘制流程,直接进入非主线程处理的部分,即直接交给合成线程处理

好处

  • 能够充分发挥GPU的优势。合成线程生成位图的过程中会调用线程池,并在其中使用GPU进行加速生成,而GPU 是擅长处理位图数据的
  • 没有占用主线程的资源,即使主线程卡住了,效果依然能够流畅地展示

使用

添加 will-change: tranform,让渲染引擎为其单独实现一个图层,当这些变换发生时,仅仅只是利用合成线程去处理这些变换,而不牵扯到主线程,大大提高渲染效率。当然这个变化不限于tranform, 任何可以实现合成效果的 CSS 属性都能用will-change来声明

对BFC的理解

块级格式化上下文,是一个独立的块级渲染区域,让处于BFC内部的元素与外部的元素相互隔离,使内外元素的定位不会相互影响。IE下为 Layout,可通过 zoom:1 触发

如何触发BFC:

  • 根元素
  • float的值不为none
  • overflow的值不为visible
  • position的值为absolute或fixed
  • display的值为inline-block、table-cell、table-caption

BFC规则:

  • 属于同一个 BFC 的两个相邻 Box 垂直排列
  • BFC 中子元素的 margin box 的左边,与包含块 (BFC) border box的左边相接触 (子元素 absolute 除外)
  • 计算 BFC 的高度时,考虑 BFC 所包含的所有元素,连浮动元素也参与计算
  • 处于同一个BFC中的元素相互影响,会发生 margin 重叠
  • BFC 的区域不会与 float 的元素区域重叠

BFC可以解决的问题:

  • 清除浮动
  • 阻止元素被浮动元素覆盖
  • 垂直外边距重叠问题
  • 自适应两列布局(float + overflow)

浮动

浮动: 使元素脱离文档流,按照指定的方向发生移动,遇到父元素边界或者相邻的浮动元素停下来。

高度塌陷: 浮动元素父元素高度自适应(父元素不写高度时,子元素写了浮动,父元素会发生高度塌陷)

  • 创建父级 BFC
  • 父元素结束标签前添加br标签,br自带clear属性,
  • 通过增加伪元素清除浮动.clearfix:after{content:'';clear:both;display:block;}.clear{zoom: 1}

定位

  • static是position属性的默认值,表示没有定位,遵循正常的文档流对象,忽略top、bottom、left、right
  • relative相对于默认位置进行偏移,原本所占的空间不会改变
  • absolute相对于最近的不是static的父元素进行偏移(否则相对body),与文档流无关不占空间,可能和其他元素重合,元素变成块级元素
  • fixed相对于窗口进行偏移,与文档流无关不占空间,可能和其他元素重合,元素变成块级元素
  • sticky基于用户滚动位置来定位,在跨越阈值前为relative,超过阈值为fixed,必须有top、left、right、bottom之一

元素隐藏

  • display:none
    • 让元素从渲染树中消失,但是仍在dom树中
    • 是非继承属性,子孙节点消失是由于元素从渲染树中消失,通过修改子孙节点的属性无法显示
    • 修改属性,渲染树会发生变化,引起重排和重绘
    • 可以通过js获取,且不耽误form表单提交数据
    • 鼠标是能够接触到元素: 不能
    • 在冒泡阶段: 不能
    • transition支持: 不支持
  • visibility:hidden
    • 不会让元素从渲染树中消失,渲染时占据空间仍在只是不可见
    • 是继承属性,子孙节点消失是由于继承了hidden,通过设置visibility:visible,可以让子孙节点显示。
    • 不引起重排,引起重绘
    • 可以通过js获取,且不耽误form表单提交数据
    • 鼠标是能够接触到元素: 不能
    • 在冒泡阶段: 能
    • transition支持: 支持
  • opacity: 0
    • 只是透明度为100%,元素只是隐身了,它依旧在页面中,并且占据空间
    • 会被子元素继承,且子元素不能通过opacity=1进行显示
    • 不引起重排,不一定引起重绘
    • 可以通过js获取,且不耽误form表单提交数据
    • 鼠标是能够接触到元素: 能
    • 在冒泡阶段: 能
    • transition支持: 支持

css动画

window.requestAnimationFrame() 告诉浏览器你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。

transform: 形状(旋转、缩放、移动、倾斜)

  • transform:translate(10px,10px)沿着X,Y轴偏移
  • transform:scale(0.8,0.5)设置X,Y轴的值进行缩放
  • transform:rotate(45deg)沿着X,Y轴旋转
  • transform:skew(30deg, 45deg)沿着X,Y轴扭曲

transition: 过渡

  • transition-property: 属性
  • transition-duration: 间隔
  • transition-timing-function: 曲线
    • linear:匀速
    • ease-in:加速
    • ease-out:减速
    • cubic-bezier函数:自定义速度模式
  • transition-delay: 延迟
  • 常用钩子: transitionend

animation: 动画

  • animation-name: 动画名称,对应@keyframes
  • animation-duration: 间隔
  • animation-timing-function: 曲线
  • animation-delay: 延迟
  • animation-iteration-count: 次数
    • infinite: 循环动画
  • animation-direction: 方向
    • normal: 开始 -> 结束-> 开始 -> 结束
    • reverse: 结束-> 开始 -> 结束 -> 开始
    • alternate: 开始 -> 结束 -> 结束 -> 开始
    • alternate-reverse: 结束-> 开始 -> 开始 -> 结束
  • animation-fill-mode: 静止模式
    • forwards: 停止时,保留最后一帧
    • backwards: 停止时,回到第一帧
    • both: 同时运用 forwards / backwards
  • 常用钩子: animationend

导入样式时,link和@import的区别

  • link 除了加载CSS外, 还能用于定义rss, 定义rel连接属性等作用; 而@import是CSS提供的,只能用于加载CSS;
  • 当解析到link时,页面会同步加载所引的 css,而@import所引用的 css 会等到页面加载完才被加载
  • import是CSS2.1 提出的,只在IE5以上才能被识别,而link是XHTML标签,无兼容问题
  • link可以使用 js 动态引入,@import不行

HTML和XHTML区别

  • HTML是基于标准通用标记语言(SGML),XHTML是基于可扩展标记语言(XML)
  • XHTML语法严格,存在DTD定义规则,HTML语法要求比较松散
  • XHTML对大小写敏感,标签名必须用小写;HTML对大小写不敏感
  • XHTML可以混合各种XML应用,HTML不能混合其它XML应用
  • XHTML标签必须成双成对

居中

水平居中

  • 行内元素: text-align: center
  • 块级元素: width: 360px;margin: 0 auto;
  • 父元素添加样式 display: flex;justify-content: center;

垂直居中

  • height: 40px;line-height: 40px;vertical-align: middle;
  • 父元素添加样式 display: flex;align-items: center;

水平垂直居中:

  • 子绝父相对, 四方向: {position:absolute;top:0;left:0;right:0;bottom:0;margin:auto;}
  • 子绝父相对,margin负值: {position:absolute;top:50%;left:50%;margin-top:-50%;margin-left:-50%;}
  • 子绝父相对,平移: {position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}
  • 弹性盒: 父元素添加样式 {justify-content: center;align-items: center;}

css布局: 左侧宽度固定,右侧自适应

<div class="box">
  <div class="box-left"></div>
  <div class="box-right"></div>
</div>
.box {
 height: 200px;
}
.box > div {
  height: 100%;
}
.box-left {
  width: 200px;
  float: left;
  background-color: blue;
}
  • 利用float + margin实现
.box-right {
  margin-left: 200px;
  background-color: red;
}
  • 利用float + overflow实现
.box-right {
  overflow: hidden;
  background-color: red;
}
  • 利用calc计算宽度
.box-right {
  width: calc(100% - 200px);
  float: right;
  background-color: red;
}
  • 利用Flex实现
.box {
 height: 200px;
 display: flex;
}
.box > div {
  height: 100%;
}
.box-left {
  width: 200px;
  background-color: blue;
}
.box-right {
  flex: 1; // 设置flex-grow属性为1,默认为0
  overflow: hidden;
  overflow: hidden;
  background-color: red;
}

层叠上下文

元素提升为一个比较特殊的图层,在三维空间中 (z轴) 高出普通元素一等。

触发条件

  • 根层叠上下文(html)
  • position
  • css3属性:flex、transform、opacity、filter、will-change、-webkit-overflow-scrolling

层叠等级: 层叠上下文在z轴上的排序

  • 在同一层叠上下文中,层叠等级才有意义
    • 谁大谁上:当具有明显的层叠水平标示的时候,如识别的z-indx值,在同一个层叠上下文领域,层叠水平值大的那一个覆盖小的那一个
    • 后来居上:当元素的层叠水平一致、层叠顺序相同的时候,在DOM流中处于后面的元素会覆盖前面的元素
  • z-index的优先级最高

内联元素的层叠顺序要比浮动元素和块状元素都高

  • border/background一般为装饰属性
  • 浮动和块状元素一般用作布局
  • 内联元素都是内容

z-index:0 / auto

  • z-index:0实际上和z-index:auto单纯从层叠水平上看,是可以看成是一样的 *z-index:0所在的<div>元素是层叠上下文元素
  • z-index:auto所在的<div>元素是一个普通的元素

css预处理器(Sass/Less/Postcss)

CSS 预处理器的原理: 是将类 CSS 语言通过 Webpack 编译转成浏览器可读的真正 CSS。在这层编译之上,便可以赋予 CSS 更多更强大的功能。

常用功能:

  • 嵌套
  • 变量
  • 循环语句
  • 条件语句
  • 自动前缀
  • 单位转换
  • mixin复用

手机端适配

  • 页面mata标签 设置viewport用户网页的可视区域<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
  • 使用rem 单位,rem是指相对于根元素(即html元素)的字体大小的单位
  • 使用flex布局及响应式布局,Flex弹性布局能够非常灵活的应对各种屏幕分辨率的适配,响应式布局兼容性好,可以跨平台,但是代码冗余,浪费流量,在大型网站不建议使用

移动端1px边框

  • 使用图片做边框border-img
  • 伪类,设置1px然后缩放50%
  • box-shadow 模拟边框

图片居中

segmentfault.com/a/119000001…

background-size 设置背景图片大小。图片可以保有其原有的尺寸,或者拉伸到新的尺寸,或者在保持其原有比例的同时缩放到元素的可用空间的尺寸。

cover: 缩放背景图片以完全覆盖背景区,可能背景图片部分看不见。

contain: 缩放背景图片以完全装入背景区,可能背景区部分空白。

  • img 标签的形式
    • 图片宽高都小于容器时垂直水平居中
    • 图片宽度都大于容器时保持宽高比将width或height充满容器
img {
	max-width: 100%;
    max-height: 100%;
    display: block;
    margin: auto;
}
  • 背景图的方式
div {
	background-size: contain;
    background-position: center;
    background-repeat: norepeat;
    background-img: url('pagh.png');
}

html

Doctype

HTML中Doctype的作用

  • <!DOCTYPE>声明的作用为了告诉浏览器该文件的类型,让浏览器解析器知道应该用哪个DTD 来解析文档
  • <!DOCTYPE>声明必须在HTML文档的第一行,此声明不是HTML标签
  • DTD(文档类型定义)是一组机器可读的规则,他们定义 XML 或 HTML 的特定版本中允许有什么,不允许有什么

严格模式和混杂模式

  • 包含严格 DTD 的 DOCTYPE 以标准模式呈现。浏览器按照W3C标准解析代码
  • 没有使用或格式错误的DOCTYPE以怪异模式呈现。浏览器用自己的方式解析代码

HTML5

  • HTML5不基于SGML,因此不需要对DTD进行引用,但是需要doctype来规范浏览器的行为,所以只写 <!DOCTYPE html>

语义化的HTML

用正确的标签做正确的事

* <title>:页面主体内容
* <header>:页眉
* <footer>:页脚
* <nav>:标记导航
* <section></section>  定义文档中的节(section、区段)
* <article></article>  定义页面独立的内容区域
* <aside></aside> 定义页面的侧边栏内容

优点:

  • 代码结构清晰,便于团队开发和维护
  • 当页面加载失败的时候,还能呈现出清晰的结构
  • 有利于被搜索引擎搜索
  • 有利于seo优化
  • 方便其他设备解析(如屏幕阅读器、盲人阅读器),以语义化的方式来渲染网页

web页面重构怎么操作

  • 页面重构就是根据原有页面内容和结构的基础上,通过div+css写出符合web标准的页面结构
  • 功能不全页面的重构页面功能不符合用户体验、用户交互结构完整,可通过标准验证
  • 代码重构:
    • 代码质量
    • SEO优化
    • 页面性能
    • 更好的语义化
    • 浏览器兼容
    • CSS优化
  • 充分考虑到页面在站点中的“作用和重要性”,并对其进行有针对性的优化

网页标准和标准制定机构重要性的理解

网页标准和标准制定机构都是为了能让web发展的更‘健康’。首先约束浏览器开发者遵循统一的标准,其次约束网站开发者,这样降低开发难度,开发成本,SEO也会更好做,也不会因为滥用代码导致各种BUG、安全问题,最终提高网站易用性

浏览器

浏览器内核

内核理解

  • 主要分成两部分: 渲染引擎、js引擎
  • 渲染引擎负责取得网页的内容、加入css、计算网页的显示方式、然后输出至显示器。浏览器的内核的不同对于网页的语法解释会有不同,所以渲染的效果也不相同
  • js引擎解析和执行js来实现网页的动态效果

常见内核

  • Trident内核:IE,MaxThon,TT,The Word,360,搜狗浏览器等。[又称为MSHTML]
  • Gecko内核:Netscape6及以上版本,FF,MozillaSuite/SeaMonkey等;
  • Presto内核:Opera7及以上。[Opera内核原为:Presto,现为:Blink]
  • Webkit内核:Safari,Chrome等。
  • Blink内核:[Chrome的:(Webkit的分支)]

跨标签页通讯

参考 跨页面通信的各种姿势

不同标签页间的通讯,本质原理就是去运用一些可以 共享的中间介质

  • 通过父页面window.open()打开新页面newHtml,通过newHtml.postMessage()发送消息,子页面通过window.onmessage()接收消息
    • 异步下,通过 window.open('about: blank')tab.location.href = '*'
  • 设置同域下共享的localStorage与监听window.onstorage
    • 重复写入相同的值无法触发
    • 会受到浏览器隐身模式等的限制
  • 设置共享cookie与不断轮询脏检查(setInterval)
  • 借助服务端或者中间层实现

从输入 url 到展示的过程

  • 在浏览器输入 URL
  • DNS解析URL对应的IP
  • TCP 三次握手
  • 送请求,分析 url,设置请求报文(头,主体)
  • 服务器返回请求的文件 (html)
  • 浏览器渲染
    • HTML parser --> DOM Tree
    • CSS parser --> Style Tree
    • 结合 dom树 与 style树,生成渲染树
    • layout: 布局
    • GPU painting: 像素绘制页面
  • 关闭TCP连接(四次挥手)

浏览器架构

  • 用户界面
  • 主进程
  • 内核
    • 渲染引擎
    • JS 引擎
      • 执行栈
    • 事件触发线程
      • 消息队列
        • 微任务
        • 宏任务
    • 网络异步线程
    • 定时器线程

事件循环(Event Loop)

事件循环是指: 执行一个宏任务,然后执行清空微任务列表,循环再执行宏任务,再清微任务列表

宏任务: script整体代码、postMessage、setTimeout、setInterval、setImmediate、I/O、UI交互事件

微任务: Promist.then、process.nextTick、Object.observe(异步地监视一个对象的修改)、 MutationObserver(监视对DOM树所做更改的能力)

async function 定义异步函数,调用异步函数返回一个Promise对象,当一个async函数返回一个值时,Promise对象的resolve方法负责传递这个值

await 操作符等待一个Promise对象

  • js只有一个主线程,所有同步任务都在主线程上执行,形成一个执行栈,当执行到异步任务的时候,就将他丢给WebAPIs,接着执行同步任务,直到Stack为空;
  • 在此期间WebAPIs完成这个事件,把回调函数放入task queue中等待;
  • 当执行栈为空时,Event Loop把task Queue中的第一个任务放入Stack中,回到第1步。

Node 的 Event Loop: 6个阶段

  • timer 阶段: 执行到期的setTimeout / setInterval队列回调
  • I/O 阶段: 执行上轮循环残流的callback
  • idle, prepare
  • poll: 等待回调
    • 执行回调
    • 执行定时器
      • 如有到期的setTimeout / setInterval, 则返回 timer 阶段
      • 如有setImmediate,则前往 check 阶段
  • check
    • 执行setImmediate
  • close callbacks。

缓存

缓存的作用:重用已获取的资源,减少减少网络带宽消耗、降低服务器压力、减少网络延迟,加开页面打开速度。数据库(数据缓存)、服务器缓存(代理服务器缓存、CDN缓存)、浏览器缓存(HTTP缓存,数据缓存,离线缓存)

web缓存

  • 短暂性的时候,我们只需要将数据存在内存中,只在运行时可用
  • 持久性存储,可以分为 浏览器端 与 服务器端
    • 浏览器:
      • cookie: 通常用于存储用户身份,登录状态等, http 中自动携带,体积上限为 4K,可自行设置过期时间
      • localStorage / sessionStorage: 长久储存/窗口关闭删除, 体积限制为 4~5M
      • 离线缓存(application cache):将大部分图片资源、js、css等静态资源放在manifest文件配置中
      • Web SQL: 关系数据库,通过SQL语句访问,它是一个独立的规范,引入了一组使用 SQL 操作客户端数据库的 APIs
      • IndexedDB: 是索引数据库 (IndexedDB) API,是能够在客户端存储可观数量的结构化数据,并且在这些数据上使用索引进行高性能检索
      • File System API: 它为 Web App 提供了一个虚拟的文件系统,就像 Native App 访问本地文件系统一样
      • cacheStorage是在ServiceWorker的规范中定义的,可以保存每个serverWorker申明的cache对象
      • flash缓存这种方式基本不用,这一方法主要基于flash有读写浏览器端本地目录的功能。
    • 服务器:
      • 分布式缓存 redis
      • 数据库

浏览器缓存

在加载资源时,先根据请求头的expires和cache-control判断是否命中强缓存,是则直接从缓存读取资源,不会发请求到服务器。如果没有命中强缓存,浏览器会发送一个请求到服务器,通过last-modified和etag验证资源是否命中协商缓存,如果命中,服务器会将这个请求返回,但是不会返回这个资源的数*据,依然是从缓存中读取资源。如果前面两者都没有命中,直接从服务器加载资源。

  • 分类
    • HTTP 1.0 Pragma
      • HTTP1.0时的遗留字段,当值为"no-cache"时强制验证缓存
    • HTTP 1.0 Expires
      • 是较老的强缓存管理header,由于它是服务器返回的一个绝对时间
      • 在服务器时间与客户端时间相差较大时,缓存管理容易出现问题。
    • HTTP 1.1 Cache-Control
      • 这是一个相对时间,在配置缓存的时候,以秒为单位,用数值表示
      • private:客户端可以缓存
      • public:客户端和代理服务器都可以缓存
      • max-age=t:缓存内容将在t秒后失效
      • no-cache:需要使用协商缓存来验证缓存数据
      • no-store:所有内容都不会缓存
    • 【Last-Modified,If-Modified-Since】
      • 在response的header加上Last-Modified的header,这个header表示这个资源在服务器上的最后修改时间
      • 在request的header上加上If-Modified-Since的header,这个header的值就是上一次请求时返回的Last-Modified的值
    • 【ETag、If-None-Match】
      • 在response的header加上ETag的header,这个header是服务器根据当前请求的资源生成的一个唯一标识
      • 在request的header上加上If-None-Match的header。
  • 注意点:
    • 协商缓存需要配合强缓存使用。
    • 修改资源url以试跳过强缓存。
    • 分布式系统里多台机器间文件的Last-Modified必须保持一致,以免负载均衡到不同机器导致比对失败;分布式系统尽量关闭掉ETag(每台机器生成的ETag都会不一样);
    • 存在周期性重写某些资源,但资源实际包含的内容并无变化;被修改的信息并不重要,如注释等;Last-Modified无法精确到毫秒,但有些资源更新频率有时会小于一秒。
  • 资源请求
    • 览器地址栏中写入URL,回车浏览器发现缓存中有这个文件了,不用继续请求了,直接去缓存拿(最快)
    • F5就是告诉浏览器,别偷懒,好歹去服务器看看这个文件是否有过期了。于是浏览器就胆胆襟襟的发送一个请求带上If-Modify-since
    • Ctrl+F5告诉浏览器,你先把你缓存中的这个文件给我删了,然后再去服务器请求个完整的资源文件下来。于是客户端就完成了强行更新的操作
  • 缓存场景
    • 对于某些不需要缓存的资源,可以使用 Cache-control: no-store ,表示该资源不需要缓存
    • 对于频繁变动的资源,可以使用 Cache-Control: no-cache 并配合 ETag 使用,表示该资源已被缓存,但是每次都会发送请求询问资源是否更新
    • 对于代码文件来说,通常使用 Cache-Control: max-age=31536000 并配合策略缓存使用,然后对文件进行指纹处理(hash、chunkhash、contenthash),一旦文件名变动就会立刻下载新的文件

cookie缺点

  • Cookie数量和长度的限制。部分浏览器每个domain最多只能有50条cookie,基本所有浏览器中每个cookie长度不能超过4KB,否则会被截掉。
  • 安全性问题。如果cookie被人拦截了,那人就可以取得所有的session信息。即使加密也与事无补,因为拦截者并不需要知道Cookie的意义,他只要原样转发cookie就可以达到目的了。
  • 有些状态不可能保存在客户端。例如,为了防止重复提交表单,我们需要在服务器端保存一个计数器。如果我们把这个计数器保存在客户端,那么它起不到任何作用。
  • 占用网络上传带宽。每次请求服务器资源时,都会携带 cookie 信息向服务器传递。

cookie session 区别

由于HTTP协议是无状态的协议,所以服务端需要记录用户的状态。每创建一个新的Session,服务器端都会分配一个唯一的ID,并且把这个ID保存到客户端的Cookie中。

  • cookie和session都是用来跟踪浏览器用户身份的会话方式。
  • cookie数据存放在客户的浏览器上,session数据放在服务器上。
  • cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗。
  • session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能。
  • 将登陆信息等重要信息存放为SESSION;其他信息如果需要保留,可以放在COOKIE中。

垃圾回收和内存泄漏

V8垃圾回收机制

垃圾回收: 将内存中不再使用的数据进行清理,释放出内存空间。V8 将内存分成 新生代空间 和 老生代空间。

  • 新生代空间: 用于存活时间较短的对象
  • 老生代空间: 用于存活时间较长的对象

垃圾回收两种方式

  1. 标记清除,当变量进入环境时,将其标志"进入环境",当变量离开环境时,将其标志为"离开环境"。某一时刻,垃圾回收器会过滤掉环境中的变量以及被环境变量引用的变量,剩下的是被视为准备回收的变量。

  2. 引用计数,跟踪记录每个值被引用的次数,被引用一次则加1,当这个引用计数为0时,被视为准备回收对象。

什么时候触发垃圾回收?

垃圾回收器周期性运行,如果分配的内存非常多,那么回收工作也会很艰巨,确定垃圾回收时间间隔就变成了一个值得思考的问题。

  • 合理的GC方案:
    • 遍历所有可访问的对象;
    • 回收已不可访问的对象。
  • GC缺陷:
    • 停止响应其他操作;
  • GC优化策略:
    • 分代回收(Generation GC);
    • 增量GC

内存泄露的情况

程序不需要的内存,由于某些原因其不会返回到操作系统或者可用内存池中。 内存泄露会导致(运行缓慢,高延迟,崩溃)的问题。

  • 被遗忘的定时器或者回调
    • 原因:定时器中有dom的引用,即使dom删除了,但是定时器还在,所以内存中还是有这个dom
    • 解决:手动删除定时器和dom
  • 意外的全局变量。
    • 原因:全局变量,不会被回收
    • 解决:使用严格模式避免
  • 脱离文档的DOM的引用
    • 原因:虽然别的地方删除了,但是对象中还存在对dom的引用
    • 解决:手动删除
  • 闭包
    • 原因:闭包可以维持函数内局部变量,使其得不到释放
    • 解决:将事件处理函数定义在外部,解除闭包, 或者在定义事件处理函数的外部函数中,删除对变量的引用
  • 子元素存在引用引起的内存泄漏
    • 原因:div中的ul li,引用div,会间接引用li,因为div间接引用li,即使li被清空,也还是在内存中,并且只要li不被删除,他的父元素都不会被删除
    • 解决:手动删除清空

避免内存泄漏的方式

总而言之需要遵循一条原则:不用了的东西要及时归还

  • 减少不必要的全局变量,或者生命周期较长的对象,及时对无用的数据进行垃圾回收
  • 注意程序逻辑,避免“死循环”之类的
  • 免创建过多的对象

垃圾回收的使用场景优化

1.数组array优化

将[]赋值给一个数组对象,是清空数组的捷径(例如: arr = [];), 但是需要注意的是,这种方式又创建了一个新的空对象,并且将原来的数组对象变成了一小片内存垃圾!实际上,将数组长度赋值为0(arr.length = 0)也能达到清空数组的目的,并且同时能实现数组重用,减少内存垃圾的产生。

const arr = [1, 2, 3, 4];
arr.length = 0  // 可以直接让数字清空,而且数组类型不变。
// arr = []; 虽然让a变量成一个空数组,但是在堆上重新申请了一个空数组对象。

2. 对象尽量复用

对象尽量复用,尤其是在循环等地方出现创建新对象,能复用就复用。不用的对象,尽可能设置为null,尽快被垃圾回收掉

var t = {} // 每次循环都会创建一个新对象。
for (var i = 0; i < 10; i++) {
  // var t = {};// 每次循环都会创建一个新对象。
  t.age = 19
  t.name = '123'
  t.index = i
  console.log(t)
}
t = null //对象如果已经不用了,那就立即设置为null;等待垃圾回收。

3.在循环中的函数表达式,能复用最好放到循环外面

// 在循环中最好也别使用函数表达式。
for (var k = 0; k < 10; k++) {
  var t = function(a) {
    // 创建了10次  函数对象。
    console.log(a)
  }
  t(k)
}
// 推荐用法
function t(a) {
  console.log(a)
}
for (var k = 0; k < 10; k++) {
  t(k)
}
t = null

Web Worker

现代浏览器为JavaScript创造的 多线程环境。可以新建并将部分任务分配到worker线程并行运行,两个线程可 独立运行,互不干扰,可通过自带的 消息机制 相互通信。

基本用法:

// 创建 worker
const worker = new Worker('work.js');
// 向主进程推送消息
worker.postMessage('Hello World');
// 监听主进程来的消息
worker.onmessage = function (event) {
  console.log('Received message ' + event.data);
}

限制:

  • 同源限制
  • 无法使用 document / window / alert / confirm
  • 无法加载本地资源

服务端与网络

http

1.0 协议缺陷:

  • 无法复用链接,完成即断开,重新慢启动和TCP 3次握手
  • head of line blocking: 线头阻塞,导致请求之间互相影响

1.1 改进

  • 长连接(默认开启Connection: keep-alive),一定程度上弥补了HTTP1.0每次请求都要创建连接的缺点
  • Host头处理,指定对应的虚拟站点
  • 新增功能:
    • 断点续传
    • 身份认证
    • 状态管理
    • cache 缓存,Cache-Control

2.0:

  • 支持多路复用
  • 支持二进制传送(实现方便且健壮),HTTP1.x是字符串传送
  • 采用HPACK压缩算法压缩头部,减小了传输的体积
  • 支持服务端推送

对http和https的理解

  • Http:超文本传输协议(Http,HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议;
  • Https:是以安全为目标的Http通道,是Http的安全版。Https的安全基础是SSL。
  • HTTP连接是无状态的,HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,安全性高于HTTP协议
  • HTTP和HTTPS使用的是完全不同的连接方式,用的端口也不一样。前者是80,后者是443

CA 与 SSL 的区别 CA 是负责发放和管理数字证书的权威机构,其颁发的证书也叫 CA 证书。CA证书的本质是利用SSL/TLS协议保护传输数据的安全,因此又称为SSL证书。

SSL证书是数字证书的一种。CA机构除了可以颁发SSL证书之外,还可以颁发其他数字证书,比如:邮件证书、加密证书、软件数字证书等等。

HTTPS协议需要ca申请证书,一般免费证书少,因而需要一定费用 http是明文传输,这是相当危险的。存在泄密、篡改、假冒等安全问题。

https 对需要传输的数据进行加密处理,加密算法可以分两大类,一类是对称加密算法,还有一类是非对称加密算法。

  • 对称加密算法的加密和解密都是用同一个密钥
    • 对称加密算法性能又比较好
    • 密钥传输时被劫持,客户端和服务端的数据都会被“破解”
    • 服务器端需要为每个用户单独生成一个秘钥并且管理,秘钥的管理代价就会很大
  • 加密和解密使用的是两个不同的密钥,私钥由服务器自己保存,公钥发送给客户端
    • 效率很低,加密和解密花费时间长、速度慢
    • 公钥在传输中如果被劫持,服务器发送的数据会被“破解”
  • 数字证书
    • 解决的问题
      • CA机构数字证书验证服务端传送的公钥,解决公钥传输的信任问题
      • 非对称加密只用来传输密码本,实际数据传输用的对称加密
    • 流程
      • 服务端填写基本信息提交申请
      • CA将基本信息hash后得到证书摘要
      • CA机构生成数字签名(CA私钥对称加密证书摘要)、公钥、私钥
      • 服务端将CA返回的数字签名及公钥传给客户端,同时还有申请CA时提交的基本信息
      • 客户端通过操作系统或者浏览器内置的CA公钥解密数字签名得到证书摘要
      • 客户端将服务端发送的基本信息hash后得到证书摘要
      • 比对服务端发送的证书摘要和客户端生成的摘要,如果一致则表明服务端可信任
      • 客户端生成用于数据传输的对称密钥,然后将密钥用服务端传输的公钥进行加密
      • 服务端用私钥解密后得到用于数据传输的对称密钥

TCP

建立连接前,客户端和服务端需要通过握手来确认对方;断开连接前,通过4次挥手。

  • 概念
    • ACK —— 确认,使得确认号有效
    • SYN —— 用于初如化一个连接的序列号
    • FIN —— 该报文段的发送方已经结束向对方发送数据
  • 三次握手
    • 客户端发送一个SYN段,并指明客户端的初始序列号,即ISN(c)
    • 服务端发送自己的SYN段作为应答,同样指明自己的ISN(s)。为了确认客户端的SYN,将ISN(c)+1作为ACK数值
    • 为了确认服务器端的SYN,客户端将ISN(s)+1作为返回的ACK数值
  • 四次挥手
    • 客户端发送一个FIN段,并包含一个希望接收者看到的自己当前的序列号K
    • 服务端将K值加1作为ACK序号值,表明收到了上一个包
    • 服务端发起自己的FIN段,ACK=K+1, Seq=L
    • 客户端确认,ACK=L+1
  • 为什么建立连接是三次握手,而关闭连接却是四次挥手呢?
    • 这是因为服务端在LISTEN状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端
    • 而关闭连接时,当收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还能接收数据,己方是否现在关闭发送数据通道,需要上层应用来决定,因此,己方ACK和FIN一般都会分开发送。

常见的HTTP状态码

  • 1xx(临时响应)
    • 100 (继续) 请求者应当继续提出请求
    • 101 (切换协议) 请求者已要求服务器切换协议,服务器已确认并准备切换。
  • 2xx(请成功)
    • 200 (成功) 请求正常处理完毕
    • 204 (无内容) 服务器成功处理了请求,但没有返回任何内容
    • 206 (部分内容) 服务器成功处理了部分 GET 请求,响应中必须包含Content-Range、Date以及ETag或Content-Location首部。
  • 3xx(请求被重定向)
    • 301 (永久移动) 永久重定向,资源已永久分配新URI
    • 302 (临时移动)临时重定向,资源已临时分配新URI
    • 303 (查看其他位置)临时重定向,期望使用GET定向获取
    • 304 (未修改)资源最近未被修改,响应不包含任何响应主体
    • 305(使用代理)使用代理来访问资源
    • 307(临时重定)POST不会变成GET
  • 4xx(请求错误)
    • 400 (错误请求) 请求报文存在语法错误或参数错误,服务器不理解
    • 401 (未授权) 请求要求身份验证
    • 403 (禁止) 对请求资源的访问被服务器拒绝了
    • 404 (未找到) 无法找到请求资源(服务器无理由拒绝)
    • 408 (请求超时) 服务器等候请求时发生超时。
  • 5xx(服务器错误)
    • 500 (服务器内部错误) 表示服务器执行请求的时候出错了
    • 501 (尚未实施) 服务器不具备完成请求的功能
    • 502 (错误网关) 服务器作为网关或代理,从上游服务器收到无效响应
    • 503 (服务不可用) 服务器目前无法使用(由于超载或停机维护)。

http请求方式

  • Options:返回服务器针对特定资源所支持的HTML请求方法或web服务器发送测试服务器功能(允许客户端查看服务器性能)
  • Get:向特定资源发出请求(请求指定页面信息,并返回实体主体)
  • Post:向指定资源提交数据进行处理请求(提交表单、上传文件),又可能导致新的资源的建立或原有资源的修改
  • Put :向指定资源位置上上传其最新内容(从客户端向服务器传送的数据取代指定文档的内容)
  • Head:向服务器索与get请求一致的相应,响应体不会返回,获取包含在小消息头中的原信息(与get请求类似,返回的响应中没有具体内容,用于获取报头)
  • Delete:请求服务器删除request-URL所标示的资源(请求服务器删除页面)
  • Trace:回显服务器收到的请求,用于测试和诊断
  • Connect:HTTP/1.1协议中能够将连接改为管道方式的代理服务器

get / post 的区别

  • get: 缓存、请求长度受限、会被历史保存记录
    • get 的请求放在 URL 上, 参数之间以 & 相连
    • 无副作用(不修改资源),幂等(请求次数与资源无关)的场景
    • 服务器端用Request.QueryString获取变量的值
  • post: 安全、大数据、更多编码类型
    • post 请求放在 http body 内
    • 服务器端用Request.Form获取提交的数据

Websocket

Websocket 是一个 持久化的协议, 基于 http , 服务端可以 主动 push

new WebSocket(url)
ws.onerror = fn
ws.onclose = fn
ws.onopen = fn
ws.onmessage = fn
ws.send()

跨域

什么是同源策略

所谓"同源"指的是"三个相同",协议相同、域名相同、端口相同。同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能。同源策略是浏览器的行为,是为了保护本地数据不被JavaScript代码获取回来的数据污染。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。

同源策略的限制

  • Cookie、LocalStorage、IndexedDB 等存储性内容
  • DOM 节点
  • AJAX 请求发送后,结果被浏览器拦截了
  • 但是有三个标签是允许跨域加载资源: <img src=XXX>、<link href=XXX>、<script src=XXX>

什么是跨域

只要协议、域名、端口有任何一个不同,都被当作是不同的域,之间的请求就是跨域操作。

跨域并非浏览器限制了发起跨站请求的这种能力,恰恰相反,我们可以发出请求,服务端也可以接收到请求并正常返回数据,只不过在返回之后浏览器会阻止非同源数据(response),从而在控制台打出一系列报错信息。

为什么要跨域

因为有时候公司内部会有多个不同子域,比如 https://hotels.ctrip.com/https://vacations.ctrip.com/, 从 hotels 访问 vacations 的资源就会跨域。

还有就是调用一些外部的API,也需要跨域。

跨域方式 参考 设置document.domain

两个网页一级域名相同,只是二级域名不同,浏览器允许通过设置document.domain共享 Cookie

举例来说,A网页是w1.example.com/a.html,B网页是…

iframe跨域

window.name + iframe

浏览器窗口有window.name属性,这个属性的最大特点是,无论是否同源,只要在同一个窗口里,前一个网页设置了这个属性,后一个网页可以读取它

  • 页面A打开不同源的子窗口页面B
  • 页面B将信息写入window.name属性
  • 将修改页面B的src修改为与页面A同源的页面C的地址
  • 页面A可以读取子窗口window.name的值

location.hash + iframe

  • 父窗口可以把信息,写入子窗口的片段标识符
  • 子窗口通过监听hashchange事件得到通知

window.postMessage

HTML5为了解决跨域,引入了一个全新的API:window.postMessage方法,允许跨窗口通信,不论这两个窗口是否同源

父窗口调用postMessage方法向子窗口发消息

var popup = window.open('http://bbb.com', 'title'); 
popup.postMessage('Hello World!', 'http://bbb.com');

子窗口通过message事件,监听对方的消息

window.addEventListener('message', function(event) {
    /**
     * event.source:发送消息的窗口
     * event.origin: 消息发向的网址
     * event.data: 消息内容
     */
    console.log(event.data); 
},false);

JSONP跨域

原理

原理是script标签不受同源策略限制,并且请求得到script资源后会立即执行

步骤

  • 网页通过添加一个<script>元素,向服务器请求JSON数据
  • 服务器收到请求后,将数据放在一个指定名字的回调函数里传回来
  • 由于<script>元素请求的脚本,直接作为代码运行,浏览器会立即调用回调函数

与ajax的区别

  • ajax的核心是通过xmlHttpRequest获取非本页内容
  • jsonp的核心是动态添加script标签调用服务器提供的js脚本
  • jsonp仅支持get方法具有局限

安全隐患

  • CSRF攻击
    • 原因
      • 前端构造一个恶意页面,请求JSONP接口,收集服务端的敏感信息
      • 如果JSONP接口还涉及一些敏感操作或信息(比如登录、删除等操作),那就更不安全了
    • 解决
      • 验证JSONP的调用来源(Referer)
      • 服务端判断Referer是否是白名单
      • 部署随机Token来防御
  • XSS攻击
    • 例子1:不严谨的 content-type导致的 XSS 漏洞
      • 请求 http://youdomain.com?callback=douniwan, 然后返回 douniwan({ data })
      • http://youdomain.com?callback=<script>alert(1)</script>
      • 返回 <script>alert(1)</script>({ data })
    • 解决:
      • 严格定义 Content-Type: application/json
      • 然后严格过滤 callback 后的参数并且限制长度(进行字符转义,例如<换成&lt>换成&gt)等
    • 例子2:服务器被黑,返回一串恶意执行的代码
    • 解决:可以将执行的代码转发到服务端进行校验JSONP内容校验,再返回校验结果

WebSocket

  • WebSocket是一种通信协议,使用ws://(非加密)和wss://(加密)作为协议前缀
  • 该协议不实行同源政策,只要服务器支持,就可以通过它进行跨源通信。
  • 浏览器发出的WebSocket请求的头信息中有有一个字段是Origin,表示该请求的请求源(origin),因为服务器可以根据这个字段,判断是否许可本次通信。

webpack 方式

module.exports = {
  //...
  devServer: {
    proxy: {
      '/api': {
        target: 'http://www.baidu.com/', // 表示的是代理到的目标地址
        pathRewrite: {'^/api' : ''}, // 默认情况下,我们的/api-hy也会被写到RUL 中,如果希望删除,可以使用 pathRewrite
        changeOrigin: true, // target是域名的话,需要这个参数,它是表示是否更新代理后请求的 headers 中的 host 地址
        secure: false, // 设置支持https协议的代理,默认情况下不接受转发到 https 的服务器上的,如果希望支持,可以设置为 false
      },
      '/api2': {
          .....
      }
    }
  }
};

Node中间件代理(两次跨域)

同源策略是浏览器需要遵循的标准,而如果是服务器向服务器请求就无需遵循同源策略

  • 接受客户端请求
  • 将请求转发给服务器
  • 拿服务器响应的数据
  • 将响应转发给客户端。
// include dependencies
const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');

// proxy middleware options
const options = {
  target: 'http://www.example.org', // target host
  changeOrigin: true, // needed for virtual hosted sites
  ws: true, // proxy websockets
  pathRewrite: {
    '^/api/old-path': '/api/new-path', // rewrite path
    '^/api/remove/path': '/path', // remove base path
  },
  router: {
    // when request.headers.host == 'dev.localhost:3000',
    // override target 'http://www.example.org' to 'http://localhost:8000'
    'dev.localhost:3000': 'http://localhost:8000',
  },
};

// create the proxy (without context)
const exampleProxy = createProxyMiddleware(options);

// mount `exampleProxy` in web server
const app = express();
app.use('/api', exampleProxy);
app.listen(3000);

nginx反向代理

通过nginx配置一个代理服务器(域名与domain1相同,端口不同)做跳板机反向代理访问domain2接口,并且可以顺便修改cookie中domain信息,方便当前域cookie写入,实现跨域登录

nginx代理跨域,实质和CORS跨域原理一样,通过配置文件设置请求响应头Access-Control-Allow-Origin...等字段

  • 正向代理代理的对象是客户端
  • 反向代理代理的对象是服务端
server {
        listen       9000;
        server_name  localhost;
				
	access_log  logs/fzzprj_access.log;
        error_log   logs/fzzprj_error.log;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / {
	  root html;
	  index index.html index.html;
          proxy_pass  http://localhost:3000;
        }
				
	location /api {
          proxy_pass  https://fzzprj.yjbtest.com:9977/api;
        }
}

cors跨域

CORS需要浏览器和服务器同时支持

  • 所有浏览器都支持该功能,IE浏览器不能低于IE10
  • 服务端设置 Access-Control-Allow-Origin 就可以开启 CORS

请求步骤

  • 简单请求跨源,会自动在头信息之中增加一个Origin字段
  • 非简单请求会多出一次"预检"请求
    • "预检"请求
      • 浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中
      • 可以使用哪些HTTP动词和头信息字段
      • 只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错
    • 如果Origin指定的源,不在许可范围内
      • 服务器会返回一个正常的HTTP回应
      • 浏览器发现,这个回应的头信息没有包含Access-Control-Allow-Origin字段
      • 然后会抛出一个错误,被XMLHttpRequest的onerror回调函数捕获
      • 这种错误无法通过状态码识别,因为HTTP回应的状态码有可能是200

简单请求满足条件

  • 请求方法是以下三种方法之一:HEAD、GET、POST
  • HTTP的头信息不超出以下几种字段:Accept、Accept-Language、Content-Language、Last-Event-ID、Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain

简单请求

  • 请求头: Origin
  • 服务器返回响应头:
    • Access-Control-Allow-Origin: https://api.qiutc.me
    • Access-Control-Allow-Credentials: true
    • Access-Control-Expose-Headers: Info
    • Content-Type: text/html; charset=utf-8

预检请求

  • 预检请求头
    • Origin: https://api.qiutc.me
    • Access-Control-Request-Method: PUT
    • Access-Control-Request-Headers: X-Custom-Header
  • 预检请求回应 Access-Control-Allow-Origin: https://api.qiutc.me Access-Control-Allow-Methods: GET, POST, PUT Access-Control-Allow-Headers: X-Custom-Header

cookie

  • CORS请求默认不发送Cookie和HTTP认证信息
  • 如果要把Cookie发到服务器
    • 一方面要服务器同意,指定Access-Control-Allow-Credentials: true
    • 还需在AJAX请求中打开withCredentials属性。var xhr = new XMLHttpRequest();xhr.withCredentials = true
  • 如果要发送Cookie
    • Access-Control-Allow-Origin就不能设为*,必须指定明确的、与请求网页一致的域名
    • 同时,Cookie依然遵循同源政策,只有用服务器域名设置的Cookie才会上传,其他域名的Cookie并不会上传
    • 且原网页代码中的document.cookie也无法读取服务器域名下的Cookie

网络安全及攻击

XSS 攻击

  1. 概念

Cross-Site Scripting(跨站脚本攻击)简称 XSS,是一种代码注入攻击。XSS 攻击利用了浏览器对于从服务器所获取的内容的信任,攻击者在目标网站上注入恶意脚本,使之在用户的浏览器上运行。利用这些恶意脚本,攻击者可获取用户的敏感信息如 Cookie、SessionID 等,会危害数据安全或者利用这些信息冒充用户向网站发起攻击者定义的请求。

  1. 分类

    • 存储型 XSS: 常见于有保存用户数据功能的网站,如论坛发帖、商品评论、用户私信等。属于服务端的安全漏洞
    • 反射型 XSS:常见于通过 URL 传递参数的功能,如网站搜索、跳转等。属于服务端的安全漏洞
    • DOM 型 XSS: 取出和执行恶意代码由浏览器端完成。属于前端 JavaScript 自身的安全漏洞
  2. 防御

    • cookie 设置 httpOnly
    • 转码<div></div> 利用模板引擎,开启模板引擎自带的 HTML 转义功能)
    • 白名单<script>, javascript:
    • 避免内联事件尽量不要使用 onLoad="onload('{{data}}')"、onClick="go('{{action}}')"
    • 主动检测和发现(使用 XSS 攻击字符串和自动扫描工具 xssfork 寻找潜在的 XSS 漏洞)
    • CSP 浏览器的内容安全策略
      • 作用
        • 限制可以加载内容的域
        • 指明允许使用哪种协议(HTTPS)
      • Content-Security-Policy
        • default-src、style-src、img-src、media-src 限制可以加载内容的域
        • report-uri 启用发送违规报告
      • Content-Security-Policy-Report-Only
        • CSP策略不是强制性的,但是任何违规行为将会报告给一个指定的 URI 地址

<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src https://*; child-src 'none';">

CSRF 攻击

  1. 概念 CSRF 既跨站请求伪造。攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求。利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的。

  2. 防御

  • 同源检测(Origin 和 Referer 会被篡改)(用户可以设置浏览器使在发送请求时不带 Referer)
  • 在请求地址中添加 token 并验证
  • 双重 cookie 验证
  • 验证码(每次的用户提交都需要用户在表单中填写一个图片上的随机字符串,易用性不太好,验证码图片的使用涉及了一个被称为MHTML的Bug)
  • cookie samesite 属性
    • strict 完全禁止第三方 cookies
    • lax 在当前用户使用时被自动发送 cookies(默认值)
    • None 同站请求、跨站请求都发送 cookies

遇到的例子

  • base67 加密
  • post 请求
  • 参数 + 签名
  • 服务端传参

参考

中高级前端大厂面试秘籍,为你保驾护航金三银四,直通大厂(共三篇)

面试分享:两年工作经验成功面试阿里P6总结

高先生前端题库