三、前端
1. 前端性能优化策略
- 减少 HTTP 请求
- CSS 放在头部、避免使用 CSS 表达式
- JS 放在尾部、精简 JS
- 使用外部 CSS 和 JS 文件
- 删除重复脚本、压缩组件
- 避免重定向
- 使用缓存
- 使用 CDN、减少 CDN 查找
2. 跨域解决方案及其原理
同源策略/SOP(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSFR等攻击。所谓同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个IP地址,也非同源。
常见的跨域场景(不允许通信):
(1)不同协议,同一域名:http://www.domain.com/a.js 和 https://www.domain.com/b.js
(2)同一域名,不同端口:http://www.domain.com:8000/a.js 和 http://www.domain.com/b.js
(3)不同域名:http://www.domain1.com/a.js 和 http://www.domain2.com/b.js
(4)主域相同,子域不同:http://www.domain.com/a.js 和 http://x.domain.com/b.js 和 http://domain.com/c.js
(5)域名和域名对应相同IP:http://www.domain.com/a.js 和 http://192.168.4.12/b.js
跨域解决方案:
(1)通过 jsonp 跨域
(2)document.domain + iframe 跨域:仅限主域相同,子域不同的跨域场景。
(3)location.hash + iframe 跨域:a欲与b跨域相互通信,通过中间页c来实现。
(4)window.name + iframe 跨域
(5)postMessage 跨域
(6)跨域资源共享(CORS):只需要服务端设置Access-Control-Allow-Origin即可。
(7)nginx 代理跨域
(8)nodejs 中间件代理跨域
(9)WebSocket 协议跨域
3. 防抖和节流
防抖函数的作用
防抖函数的作用就是控制函数在一定时间内的执行次数。防抖意味着 N 秒内函数只会被执行一次,如果 N 秒内再次被触发,则 重新 计算延迟时间。
防抖函数的实现
- 事件第一次触发时,timeout 是 null,调用 later(),若 immediate 为true,那么立即调用 func.apply(this, params);如果 immediate 为 false,那么过 wait 之后,调用 func.apply(this, params)。
- 事件第二次触发时,如果 timeout 已经重置为 null (即 setTimeout 的倒计时结束),那么流程与第一次触发时一样,若 timeout 不为 null (即 setTimeout 的倒计时未结束),那么清空定时器,重新开始计时。
function debounce(func, wait, immediate = true) {
let timeout, result;
// 延迟执行函数
const later = (context, args) => setTimeout(() => {
timeout = null;// 倒计时结束
if (!immediate) {
// 执行回调
result = func.apply(context, args);
context = args = null;
}
}, wait);
let debounced = function (...params) {
if (!timeout) {
timeout = later(this, params);
if (immediate) {
// 立即执行
result = func.apply(this, params);
}
} else {
clearTimeout(timeout);
// 函数在每个等待时延的结束被调用
timeout = later(this, params);
}
return result;
}
// 提供在外部清空定时器的方法
debounced.cancel = function () {
clearTimeout(timeout);
timeout = null;
};
return debounced;
};
immediate 为 true 时,表示函数在每个等待时延的开始被调用。immediate 为 false 时,表示函数在每个等待时延的结束被调用。
防抖的应用场景
搜索框输入查询,如果用户一直在输入中,没有必要不停地调用去请求服务端接口,等用户停止输入的时候,再调用,设置一个合适的时间间隔,有效减轻服务端压力。
表单验证。
按钮提交事件。
浏览器窗口缩放,resize 事件 (如窗口停止改变大小之后重新计算布局) 等。
节流函数的作用
节流函数的作用是规定一个单位时间,在这个单位时间内最多只能触发一次函数执行,如果这个单位时间内多次触发函数,只能有一次生效。
节流函数的实现
function throttle(func, wait, options = {}) {
var timeout, context, args, result;
var previous = 0;
var later = function () {
previous = options.leading === false ? 0 : (Date.now() || new Date().getTime());
timeout = null;
result = func.apply(context, args);
if (!timeout) context = args = null;
};
var throttled = function () {
var now = Date.now() || new Date().getTime();
if (!previous && options.leading === false) previous = now;
//remaining 为距离下次执行 func 的时间
//remaining > wait,表示客户端系统时间被调整过
var remaining = wait - (now - previous);
context = this;
args = arguments;
//remaining 小于等于 0,表示事件触发的间隔时间大于设置的 wait
if (remaining <= 0 || remaining > wait) {
if (timeout) {
// 清空定时器
clearTimeout(timeout);
timeout = null;
}
// 重置 previous
previous = now;
// 执行函数
result = func.apply(context, args);
if (!timeout) context = args = null;
} else if (!timeout && options.trailing !== false) {
timeout = setTimeout(later, remaining);
}
return result;
};
throttled.cancel = function () {
clearTimeout(timeout);
previous = 0;
timeout = context = args = null;
};
return throttled;
}
禁用第一次首先执行,传递 {leading: false} ;想禁用最后一次执行,传递 {trailing: false}。
节流的应用场景
按钮点击事件。
拖拽事件。
onScoll。
计算鼠标移动的距离 (mousemove)。
4. link 和 import 的区别
link 属于 XHTML 标签,而 @import 是 CSS 提供的。
(1)引用的方式不同:
link(外部引用): <link rel="stylesheet" href="xxx.css" type="text/css" />
@import(导入式): @import url(xxx.css);
(2)放置的位置不同:link 一般放在 head 标签中,而 @import 必须放在 <style type="text/css"> 标签中。
(3)加载方式不同:link 会和 dom 结构一同加载渲染,而 @import 只能等 dom 结构加载完成以后才能加载渲染页面。
(4)兼容性不同:@import 只能在 IE6 以上才能识别,而 link 是 XHTML 标签,无兼容问题。
(5)样式权重不同:link 方式的样式的权重高于 @import。
(6)改变样式:link 支持使用 JavaScript 改变样式,而 @import 不可以。
(7)加载内容不同:link 除了可以加载 css 文件以外,还可以加载 MIME 类型的文件;而 @import 只能加载 css 文件。
5. 浏览器内核
- IE、傲游、Avant、腾讯TT (trident内核)
- Mozilla Firefox (gecko内核)
- Safari (webkit内核)
- Chrome (Blink内核)
- Opera (原为Presto内核,现为Blink内核)
6. 浏览器兼容问题及其解决方案
问题一:在不加样式控制的情况下,标签默认的外补丁(margin)和内补丁(padding)不同
解决方案: CSS设置 *{ margin:0; padding:0; }
问题二:块级属性标签 float 之后,如果有左右 margin,那么在 IE6 中显示的 margin 比设置的大
解决方案: 在float的标签样式中加入 { display: inline; }, 将其转化为行内属性
问题三:行内属性标签,在设置 display:block 之后,如果采用 float 布局,同时又有左右 margin 的情况下,那么在 IE6 中显示的间距有 bug
解决方案: 在 { display:block; } 后面加入 { display:inline; display:table; }
问题四:如果给标签设置较小的高度(一般小于10px),那么在IE6,IE7和遨游中这个标签的高度会超出你设置的高度
在IE6、IE7和遨游中,这个标签的高度不受你设置的高度的控制,它会达到默认的行高。这是因为 IE8 之前的浏览器都会给标签一个最小默认的行高的高度,即使标签是空的,这个标签的高度还是会达到默认的行高。
解决方案: 给超出高度的标签设置 { overflow:hidden; } 或者设置行高 { line-height } 小于你设置的高度
问题五:如果给标签设置最低高度 min-height,会出现不兼容的情况
解决方案: 如果我们要设置一个标签的最小高度是200px,需要进行的设置为:
{
min-height:200px;
height:auto !important;
height:200px;
overflow:visible;
}
问题六:图片之间默认有间距
几个 img 标签放在一起的时候,有些浏览器会有默认的间距,加了问题一中提到的通配符也不起作用。
解决方案: 将 img 设置为 float 属性
问题七:格式为PNG24的图片在IE6浏览器上会出现背景
解决方案:把图片改成 PNG8 格式
问题八:设置透明度(兼容所有浏览器)
.tran_class {
filter: alpha(opacity=50); // IE浏览器 (trident内核)
-moz-opacity: 0.5; // 老版Mozilla firefox (gecko内核)
-khtml-opacity: 0.5; // 老版Safari (webkit内核)
opacity: 0.5; // 支持opacity的所有浏览器,如:Chrome (Blink内核)、opera (以前是presto内核,现在改用google chrome的Blink内核)
}
问题九: IE6,IE7中 ol 的序号全为1,不递增
解决方案: li设置样式 { display: list-item }
问题十: IE6,IE7不支持 display:inline-block
解决方案: 设置 inline, 并触发 haslayout
{ display:inline-block; *display:inline; *zoom:1 }
问题十一: 使用 hack 解决浏览器兼容问题
不同的浏览器对CSS的解析不同,会导致生成的页面效果不同。通过使用 hack 可以解决浏览器兼容问题(针对的更多是 老式/旧版本 浏览器),但不推荐使用,因为当代码量多时,编写复杂度会变高,还可能会在浏览器更新之后产生更多的兼容问题。
hack 可以理解为不同浏览器识别的标识符。
(1)五大浏览器的hack
div {
transform: rotate(90deg); /* 标准模式 */
-moz-transform: rotate(90deg); /* Firefox */
-webkit-transform: rotate(90deg); /* Safari and Chrome */
-o-transform: rotate(90deg); /* Opera */
-ms-transform: rotate(90deg); /* IE */
}
(2)CSS属性hack
IE6能识别 下划线_ 和 星号* ,但不能识别 !important
IE7能识别 星号* 和 !important ,但不识别 下划线_
Firefox 能识别 !important ,但不识别 下划线_ 和 星号*
书写顺序:FF IE7 IE6
比如这样一个CSS设置:
{
height:300px;
*height:200px;
_height:100px;
}
在IE6中从上往下读,直到_height,把高度设置成了100px;
在IE7中从上往下读,直到*height,把高度设置成了200px;
在其他浏览器中,把高度设置成了300px。
(3)CSS选择符hack
IE6能识别*html .class{}
IE7能识别*+html .class{} 或者 *:first-child+html .class{}
(4)条件注释法:这种方式是 IE 浏览器专有的 hack 方式,微软官方推荐使用。
问题十二:获取自定义属性
IE浏览器下,可以使用获取常规属性的方法来获取自定义属性,也可以使用getAttribute()获取自定义属性。
Firefox浏览器下,只能使用getAttribute()获取自定义属性。
解决方案:统一使用 getAttribute() 获取自定义属性
问题十三:Chrome 浏览器的中文界面下默认会将小于 12px 的文本强制按照 12px 显示
解决方案:加入 CSS 属性 {-webkit-text-size-adjust: none; }
问题十四:超链接访问过后 hover 样式就不出现了;被点击访问过的超链接样式不再具有 hover 和 active 了
解决方案:改变 CSS 属性的排列顺序:
L-V-H-A : a:link {} a:visited {} a:hover {} a:active {}
问题十五:even对象
IE浏览器下,even对象有x,y属性,但是没有pageX,pageY属性。
Firefox浏览器下,event对象有pageX,pageY属性,但是没有x,y属性。
解决方案:条件注释
缺点:在IE浏览器下可能会增加额外的 HTTP 请求。
7. 从输入网址到最后浏览器呈现页面内容,中间发生了什么?
(1)输入网址,浏览器,发送UDP包给DNS服务器,通过DNS解析得到网址的IP地址(即服务器的IP),并将IP地址缓存。
(2)客户端(浏览器)和服务器端之间建立TCP连接:由于TCP邮差需要知道4个东西(本机IP,本机端口,服务器IP,服务器端口),现在只知道了本机IP,服务器IP, 两个端口怎么办?其中,本机端口很简单,操作系统可以给浏览器随机分配一个, 服务器端口更简单,用的是一个“众所周知”的端口,HTTP服务就是80(HTTPS服务就是443), 我们直接告诉TCP邮差就行。经过三次握手以后,客户端和服务器端的TCP连接就建立起来了!
(3)浏览器发送HTTP请求。
(4)Web服务器处理请求,并将HTTP Response(一个HTML页面)返回给浏览器。
(5)浏览器再次发起请求:由于这个HTML页面中可能引用了大量其他资源,例如JS文件,CSS文件,图片等,这些资源也位于服务器端,并且可能位于另外一个域名下面。所以浏览器只好一个个地下载,从使用DNS获取IP开始,之前做过的事情还要再来一遍。
(6)当服务器把JS,CSS这些文件发送给浏览器时,会告诉浏览器这些文件什么时候过期(使用Cache-Control或者Expire),浏览器可以把文件缓存到本地,当第二次请求同样的文件时,如果不过期,直接从本地取就可以了。如果过期了,浏览器就可以询问服务器端,文件有没有修改过?(依据是上一次服务器发送的Last-Modified和ETag),如果没有修改过(304 Not Modified),还可以使用缓存。否则的话服务器就会把最新的文件发回到浏览器。
(7)现在浏览器拥有了 HTML、CSS 和 JavaScript(可以修改 DOM Tree),开始进行渲染:
- HTML,浏览器把它解析成 DOM Tree;CSS,浏览器把它解析成 CSS Rule Tree
- DOM Tree 和 CSS Rule Tree 解析完成之后,被附加到一起,形成渲染树 Render Tree
- (重排 - Reflow) 计算节点信息:根据渲染树计算每个节点的几何信息
- (重绘 - Repaint) 渲染绘制:根据计算好的节点信息,渲染绘制整个页面
最后,我们就能看到呈现的页面了。
8. 重排和重绘
重排:若渲染树的部分节点更新,且尺寸变化,就会发生重排。
重绘:若渲染树的部分节点更新,但不改变其他节点,就会发生重绘。
PS:重绘不一定导致重排,但重排一定会导致重绘。重排会产生比重绘更大的开销。
触发重排:
- (1)页面第一次渲染:在页面发生首次渲染的时候,所有组件都要进行首次布局,这是开销最大的一次重排
- (2)浏览器窗口尺寸改变
- (3)元素位置和尺寸(宽、高、内外边距、边框等)发生改变的时候
- (4)增加或删除DOM节点
- (5)内容发生改变(文字数量或图片大小等等)
- (6)元素字体大小变化
- (7)激活CSS伪类(例如::hover)
- (8)增加或修改样式,设置style属性
- (9)查询某些属性或调用某些方法,如:调用getComputedStyle方法,或者IE里的currentStyle时,也会触发重排
- (10)display:none —— 设置该属性的元素将会消失,不占据空间
触发重绘:
- (1)visibility:hidden —— visibility 属性规定元素是否可见。即使不可见的元素也会占据页面上的空间。
- (2)outline —— outline (轮廓)是绘制于元素周围的一条线,位于边框边缘的外围。轮廓线不会占据空间,也不一定是矩形。
- (3)背景颜色:color、background-color
如何减少重排和重绘以提升页面性能:
- (1)样式集中改变。
- (2)DOM离线化。
- (3)批量添加DOM。
- (4)复制节点,在副本中修改,然后直接替换当前的节点。
- (5)降低受影响的节点:在页面顶部插入节点将影响后续所有节点,而绝对定位元素的改变会影响较少的元素,将
position
属性设置为absolute或fixed。
- (6)分离读写操作。
- (7)缓存布局信息。
- (8)使用 css spirit,也叫 css 精灵,它将所有的图片放到一张图片上,然后通过定位来实现图片的使用。
参考:https://www.imooc.com/article/45936
END