CSS
几种布局
- 正常布局流,display 属性为 block inline inline-block 这些标准属性,是浏览器默认的HTML布局方式
- table 布局,优点是兼容性好,缺点是需要等内容全部加载完才可以展示
- 浮动布局,通过 float 属性,例如 float: left 可以让块级元素并排而不是堆叠
- position 属性布局
- 静态定位 static:默认属性
- 相对定位 relative:允许元素的相对移动
- 静态定位 absolute:相对第一个非 static 的父类元素定位
- 固定定位 fixed:相对浏览器视图固定
- 粘性布局 sticky:指定阈值后,未越过阈值为 relative,越过阈值为 fixed
- CSS Grid,display 属性为 grid,兼容性差
- Flexbox,display 属性为 flex
盒模型
- content、padding、border、border
- 标准盒模型(content-box)的宽高就是 content 的宽高,IE 盒模型(border-box)的宽高是 content+padding+border 宽高的总和
选择器及其优先级
- !important
- 内联样式 style=""
- ID 选择器 #id
- 类选择器/属性选择器/伪类选择器
- 元素选择器/关系选择器/伪元素选择器
- 通配符选择器
BFC
Formatting Context 指一个独立的渲染区域,或者说是一个隔离的独立容器。
常见的 Formatting Context:
- BFC(Block formatting contexts):块级格式上下文
- IFC(Inline formatting contexts):内联格式上下文
- GFC(GrideLayout formatting contexts):网格布局格式化上下文
- FFC(Flex formatting contexts):自适应格式上下文
BFC(Block Formatting Context)是一个用来管理块级元素的容器,是 Web 页面中盒模型布局的 CSS 渲染模式。
- 内部的 Box 会在垂直方向上一个接一个地放置,垂直方向的距离由 margin 决定
- 属于同一个 BFC 的两个相邻的块级元素的 margin 会发生重叠
- 每个元素的 margin box 的左边, 与包含块 border box 的左边相接触。即使存在浮动也是如此
- BFC 的区域不会与 float 的元素区域重叠
- 计算 BFC 的高度时,浮动子元素也参与计算
- BFC 是隔离的独立容器,容器里面的子元素不会影响到外面元素
BFC 主要的作用是:
- 清除浮动,比如实现左边侧边栏的效果
- 防止同一 BFC 容器中的相邻元素间的外边距重叠问题
伪类和伪元素
伪类(一个冒号)
- 获取不存在与DOM树中的信息。比如a标签的:link、visited等,这些信息不存在与DOM树结构中,只能通过CSS选择器来获取;
- 获取不能被常规CSS选择器获取的信息。比如:要获取第一个子元素,我们无法用常规的CSS选择器获取,但可以通过 :first-child 来获取到。
伪元素(两个冒号,CSS3 中区分开)
- 伪元素用于创建一些不在文档树中的元素,并为其添加样式。比如说,我们可以通过:before来在一个元素前增加一些文本,并为这些文本添加样式。虽然用户可以看到这些文本,但是这些文本实际上不在文档树中。常见的伪元素有 ::before、::after 等
清理浮动
- 原因:浮动会导致父容器高度塌陷
- 方案1:在父元素末尾添加冗余的块级元素,设置
clear: both
- 方案2:通过伪元素
:after
添加一个看不见的块元素,推荐方案
- 方案3:给父元素设置
overflow: hidden
,将它变成 BFC,可以包含浮动
移动端适配
- rem 做单位,根据视图大小来改变根元素字体大小
- vw 做单位,可以按设计稿的像素开发,缺点是跟随 viewport 放大缩小,没有最大最小值的限制
setInterval 和 requestAnimationFrame
- setInterval 是从开始时间计时,无法确保执行间隔。由定时触发器线程执行定时,由事件触发线程塞进任务队列的尾部,由 JS 引擎的主线程来执行,不会重复放入事件队列中,如果主线程阻塞,且队列中已经存在了一个定时器,则下一次会直接跳过计时。主线程执行结束后,会立即执行队列中的定时器。
- requestAnimationFrame 由系统来决定回调函数的执行时机,能保证回调函数在屏幕每一次的刷新间隔中只被执行一次。有两个优点:与屏幕渲染一致,所以更加节能;有函数节流的效果。
JavaScript
七种数据类型
- Undefined
- Null
- Boolean
- String
- Number
- Symbol
- Object(包括数组、函数、正则、日期)
判断数据类型的方法(重要)
- typeof:JS 在底层存储变量的时候,会在变量的机器码的低位 1-3 位存储其类型信息,number(010), string(100), object(000), boolean(110), undefined(-2^30),null :所有机器码为0
- 优点:能够快速区分基本数据类型,包括 function(通过是否实现 call 方法判断)
- 缺点:不能将 Object、Array 和 Null 区分,都返回 object
- instanceof:判断一个实例是否属于某种类型,检查左边实例的
__proto__
和右边类的 prototype 是否在同一条原型链上
- 优点:可以用来判断对象的具体类型
- 缺点:Number,Boolean,String 基本数据类型不能判断;多个 iframe 中判断不准确
- constructor:通过构造函数比较来判断
- 优点:可以判断基本数据类型
- 缺点:无法判断 null 和 undefined,构造函数本身可被覆盖,不够稳定
Object.prototype.toString.call
准确判断对象实例的类型
原型对象
每个函数都会有一个 prototye 属性指向函数的原型对象,每个原型对象都会获取一个 constructor 属性指向构造函数,即:Function.prototype.constructor == Function
new 运算符的原理
- 创建一个空对象,它的
__proto__
等于构造函数的原型对象(可以用Object.create()完成)
- 构造函数以第1步创建的对象做为上下文,是否会返回一个对象
- 若第2步返回了对象,则使用该对象作为新实例,否则用第1步创建的对象作为新实例
function myNew(func) {
var o = Object.create(func.prototype)
var i = func.call(o)
return typeof i === 'object' ? i : o
}
继承的几种实现方式(重要)
- 原型链继承:
B.prototype = new A()
- 缺点:引用类型的属性会被所有子类共享,创建子类实例时父类构造函数无法传参
- 构造函数继承:
function B(params) { A.call(this, params) }
- 缺点:只能继承构造函数内的实例属性;无法获取父类原型链的属性;无法复用函数,每个子类都是独立副本
- 原型链+构造函数
- Class 实现继承,需要 ES6 支持
- 原型链+构造函数优化
function B(params) {
A.call(this, ...params)
}
B.prototype = Object.create(A.prototype)
B.prototype.constructor = B
call、apply 和 bind 区别
三个函数的作用都是将函数绑定到上下文中,用来改变函数中 this 的指向
// call 方法接受的是若干个参数列表
fun.call(thisArg[, arg1[, arg2[, ...]]])
// apply 接收的是一个包含多个参数的数组
fun.apply(thisArg, [argsArray])
// bind 会创建一个新的函数
var bindFn = fun.bind(thisArg[, arg1[, arg2[, ...]]])
bindFn()
防抖(Debouncing)和节流(Throttling)(重要)
- 防抖(Debouncing):将触发频繁的事件合并成一次执行。适用场景: input 实时反馈、scroll 事件优化。
- 节流(Throttling): 设置一个阀值,在阀值内,将触发的事件合并成一次执行,且当到达阀值,必定执行一次事件。防止浏览器频繁响应事件,严重拉低性能。适用场景:resize 事件、鼠标移动事件
模块化的实现
- CommonJS:主要用于 NodeJS,同步加载,文件即模块,导出的是值的拷贝。通过
exports
require
导出和加载,加载后会在内存里生成一个对象,引入时会去 exports
属性上取值,所以只会加载一次。浏览器端需要通过 browserify 进行打包。输出的是值的拷贝。
- AMD:在模块开始时异步加载所有依赖模块,全量加载,可以并行
- CMD:类似 CommonJS 的风格,动态引入,按需加载,延迟执行
- ES6:在语言标准的层面实现了模块功能,模块是一个单例,通过
import
export
导出和加载,输出的是值的引用。
其他
浏览器缓存策略(重要)
缓存流程:
- 浏览器每次发起请求,都会先在浏览器缓存中查找该请求的结果以及缓存标识
- 浏览器每次拿到返回的请求结果都会将该结果和缓存标识存入浏览器缓存中
缓存策略分为两种:强缓存和协商缓存,并且缓存策略都是通过设置 HTTP Header 来实现的。
- 强制缓存:不发送请求,直接读取缓存,通过设置两种 HTTP Header 控制:Expires(HTTP/1.0)和 Cache-Control(HTTP/1.1),强制缓存生效会返回 200 状态码。
- 协商缓存:强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存
- 两种结果:
- 生效返回 304,还是由客户端从缓存读取
- 失效返回 200,服务器返回最新结果
- 两种方式:
- Last-Modified 和 If-Modified-Since:标记最后修改时间,缺点是不够精确,只能精确到秒
- ETag 和 If-None-Match:由服务器生成当前资源的唯一标识
Web Worker
Web Worker 为 JavaScript 创造多线程环境,允许主线程创建 Worker 线程,将一些任务分配给后者运行。在主线程运行的同时,Worker 线程在后台运行,两者互不干扰。等到 Worker 线程完成计算任务,再把结果返回给主线程。这样的好处是,一些计算密集型或高延迟的任务,被 Worker 线程负担了,主线程(通常负责 UI 交互)就会很流畅,不会被阻塞或拖慢。
- 同源限制:Worker 线程的脚本文件必须和主线程脚本文件同源
- DOM 限制,无法获取 document window ,可以获取 navigator 和 location
- 通信限制:无法与主线程直接通信,必须通过消息完成,主线程
postMessage
,worker 线程 onmessage
监听
- 脚本限制:无法执行 alert confirm 函数,但是可以发送 ajax 请求
- 文件限制:无法读取本地文件,脚本来源自网络
- 应用场景:后台轮询
PWA - Progressive Web App
PWA 经过应用一些新技术进行改进,在安全、性能和体验三个方面提升 Web 体验,本质上还是 Web App。
改造方式:
- 全站 HTTPS 化,这是 PWA 的基础,没有 HTTPS 就没有 Service Worker
- 通过 Service Worker 提升基础性能,离线提供静态文件,提升用户首屏体验
- App Manifest 同步进行
- 考虑其他的特性,离线消息推送等
script 标签解析
- 默认情况,会立即加载并执行指定的脚本,不等待标签后面的文档加载完毕
- async defer 共同点:开新线程并行下载,不会阻塞解析
- async:下载完成后立即执行,执行时会阻塞文档解析,无法确保顺序,适合 GA 这种无依赖脚本
- defer:所有文档元素加载完成之后按照顺序执行,在 DOMContentLoaded 事件触发之前完成
- 动态添加的 script 标签隐含 async 属性
preload 和 prefetch
- preload 预加载本次导航可能用到的资源,优先级根据 as 属性决定,加载资源存储在缓存里,下次请求直接读取缓存,不会重复请求资源,跳转的时候未完成的任务会取消,主要目的是提前做好缓存。
- prefetch 预加载下次导航可能用到的资源,优先级最低,不保证缓存资源,所以多次请求会重复请求,导航到其他页面的时候未完成的请求也会保持。
Cookie 特性
- 通过 domain 限制域名,通过 path 限制路径,通过 Expires/Max-Age 限制有效期
- cookie 本身没有删除,通过设置 Max-Age 为 0 可以让 cookie 失效删除
- HttpOnly 字段设置为 true ,则 JS 无法获取 cookie 的值,可以防止 XSS 攻击
- document.cookie 获取和修改 cookie ,是一个字符串,分号分割
性能优化方案(重要)
- 网络优化
- 使用 CDN
- 升级 HTTP/2
- 使用缓存(强制缓存和协商缓存)
- 资源优化
- preloader、prefetch、async、defer 等相关指令
- 资源域名拆分,避免浏览器 TCP 连接数限制
- 资源压缩与合并
- 渲染优化
图片懒加载实现原理
- 用 data-src 存储图片地址,初始化的时候图片无内容,监听 scroll 事件,执行防抖函数滞后执行避免掉帧,通过 getBoundingClientRect 判断图片是否出现在屏幕中,在展示的时候再加载图片。
- 判断图片是否展示也可以通过 IntersectionObserver 实现。
- Chrome 已经官方支持 Lazy Load,在 img 标签加上 loading="lazy" 即可。
CDN 的工作原理
Content Delivery Network,缩写 CDN,将网站的内容发布到最接近用户的网络节点,使用户可以就近取得所需内容,提高用户访问网站的响应速度。一般通过修改 DNS 实现,利用 CNAME 将域名和目标 IP 之间进行解耦。
核心:
- 缓存:将从根服务器请求来的资源按要求缓存
- 回源:当有用户访问某个资源的时候,如果被解析到的那个 CDN 节点没有缓存响应的内容,或者是缓存已经到期,就会回源站去获取。没有人访问,CDN 节点不会主动去源站请求资源。
优点:
- 加速:通过靠近用户的网络节点降低访问延时
- 负载:分流减轻源站的负载
安全概念(重要)
- CSRF:Cross-Site Request Forgery,跨站请求伪造,挟持用户在当前已登录的 Web 应用程序上执行非本意的操作,比如通过
<img src="https://t.cn/withdraw?account=xx&amount=xx">
伪造请求
- 防御措施:Token 验证和 Referer 验证
- XSS:Cross Site Script,跨域脚本攻击,类似 SQL 注入
- 三种方式:
- DOM based XSS:DOM 型,不经过后端,URL -> 浏览器
- Reflected XSS:反射型,不经过数据库,浏览器 -> 后端 -> 浏览器
- Stored XSS:存储型,经过数据库,浏览器 -> 后端 -> 数据库 -> 后端 -> 浏览器
- 防御措施:cookie 设置 httpOnly 防止读取和篡改,对服务端请求做过滤和转义
为什么会有 OPTIONS 请求
规范要求,对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是 GET 以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求),浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。服务器确认允许之后,才发起实际的 HTTP 请求。
- 获取服务器支持的 HTTP 请求方法;
- 用来检查服务器的性能。例如:AJAX 进行跨域请求时的预检,需要向另外一个域名的资源发送一个 HTTP OPTIONS 请求头,用以判断实际发送的请求是否安全。
当请求满足下述任一条件时,即应首先发送预检请求(使用 OPTIONS):
- 使用了下面任一 HTTP 方法:
- PUT
- DELETE
- CONNECT
- OPTIONS
- TRACE
- PATCH
- 人为设置了对 CORS 安全的首部字段集合之外的其他首部字段。该集合为:
- Accept
- Accept-Language
- Content-Language
- Content-Type (but note the additional requirements below)
- DPR
- Downlink
- Save-Data
- Viewport-Width
- Width
- Content-Type 的值不属于下列之一:
- application/x-www-form-urlencoded
- multipart/form-data
- text/plain
参考资料