Fork me on GitHub

面试杂项

防止网页被嵌入框架的代码

任何页面都不可嵌套

1
2
3
4
// 判断当前的window对象是否对顶层top对象还可以使用window.top !== window.self
if (window !== top)
// 如果不是,将top对象的网址自动导向被嵌入网页的网址
top.location.href = window.location.href;

本地域名可嵌套,其他域名不可

1
2
3
4
5
6
7
8
try{
  top.location.hostname;
  if (top.location.hostname !== window.location.hostname) {
    top.location.href = window.location.href;
  }
}catch(e){
  top.location.href = window.location.href;
}

PS:上面两种对于动态生产iframe标签和禁用js不会用效果。
别人可能这样禁用你的js
<noscript><iframe src=fillseo.html></iframe></noscript>

js如何判断是否在iframe中

1
2
3
4
5
6
7
8
9
10
11
12
//方式一 
if (self.frameElement && self.frameElement.tagName == "IFRAME") {
alert('在iframe中');
}
//方式二
if (window.frames.length != parent.frames.length) {
alert('在iframe中');
}
//方式三
if (self != top) {
alert('在iframe中');
}

比较可靠的方式

为了彻底防止别人用IFRAME框架嵌套调用自己的网页,如下方法是最可靠的.
这里赋值为空页面,也可赋值为你的页面的URL地址.

1
2
3
if(top != self){ 
location.href = "about:blank";
}

在meta中设置

<meta http-equiv="X-FRAME-OPTIONS" content="DENY">

在http的header做手脚

1
2
header(‘X-Frame-Options:Deny');
header("X-XSS-Protection: 0");

在Apache、IIS、Nginc主机中设置

X-Frame-Options "SAMEORIGIN";

BFC

BFC的定义

块格式化上下文(Block Formatting Context,BFC) 是Web页面的可视化CSS渲染的一部分,是布局过程中生成块级盒子的区域(可以理解为独立的布局作用域),也是浮动元素与其他元素的交互限定区域。
具有 BFC 特性的元素可以看作是隔离了的独立容器,容器里面的元素不会在布局上影响到外面的元素,并且 BFC 具有普通容器所没有的一些特性。

BFC的触发

body 根元素或包含根元素的元素
浮动元素:float 除 none 以外的值
绝对定位元素:position (absolute、fixed)
display 为 inline-block、table-cells、flex
overflow 除了 visible 以外的值 (hidden、auto、scroll)

BFC的运用

同一个 BFC 下外边距会发生折叠
BFC 可以包含浮动的元素(清除浮动)(父容器设置为overflow: hidden;即可清除浮动)
BFC 可以阻止元素被浮动元素覆盖(为原本被覆盖的元素设置overflow: hidden;是该元素触发BFC解决问题)
10 分钟理解 BFC 原理

Banner如何平滑的从最后一张过渡到第一张

1.如有1,2,3,4张banner需要轮播,对应图片顺序为:4,1,2,3,4,1
2.动画结束后改变left定位到前面的1,这个过程页面看不出变化。
图片轮播,第一张图片和最后一张图片怎么过渡?

Event Loop(浏览器环境)

定义:为了协调事件(event),用户交互(user interaction),脚本(script),渲染(rendering),网络(networking)等,用户代理(user agent)必须使用事件循环(event loops)。即事件循环,是JavaScript引擎处理异步任务的方式。为了让单线程的JavaScript通畅的跑起来,所有的异步操作都要被合适的处理,这个处理逻辑就叫做Event Loop。
HTML标准-Event loops

堆、栈、队列

堆(heap)

堆(heap)是指程序运行时申请的动态内存,在JS运行时用来存放对象。

栈(stack)

栈(stack)遵循的原则是“先进后出”,JS种的基本数据类型与指向对象的地址存放在栈内存中,此外还有一块栈内存用来执行JS主线程–执行栈(execution context stack),这里只考虑执行栈。

队列(queue)

队列(queue)遵循的原则是“先进先出”,JS中除了主线程之外还存在两个“任务队列”(微任务队列microTask和宏任务队列macroTask)。

js中的两个队列

在JavaScript中,任务被分为Task(又称为MacroTask,宏任务)和MicroTask(微任务)两种。
MicroTask: process.nextTick(node独有), Promises, Object.observe(废弃), MutationObserver
MacroTask: script(同步代码), setTimeout, setInterval, setImmediate(node独有), I/O, UI rendering(浏览器独有)
javascript执行:总的执行顺序为同步代码script—>microTask—>其他macroTask,在执行microTask、macroTask是产生新的异步操作,如此一来就形成了循环。
具体来说,浏览器会不断从task队列中按顺序取task执行,每执行完一个task都会检查microtask队列是否为空(执行完一个task的具体标志是函数执行栈为空),如果不为空则会一次性执行完所有microtask。然后再进入下一个循环去task队列中取下一个task执行,以此类推。

Philip Roberts的演讲《Help, I’m stuck in an event-loop》的event-loop 图

Philip Roberts的演讲《Help, I'm stuck in an event-loop》
PS:在执行MicroTask和MacroTask时,如果产生了新的MicroTask则会在本次Event Loop中执行,产生的MacroTask会在下个Event Loop中执行。(更新于2018-09-12)
带你彻底弄懂Event Loop

解释

当主线程运行的时候,JS会产生堆和栈(执行栈)
主线程中调用的webaip所产生的异步操作(dom事件、ajax回调、定时器等)只要产生结果,就把这个回调塞进“任务队列”中等待执行。
当主线程中的同步任务执行完毕,系统就会依次读取“任务队列”中的任务,将任务放进执行栈中执行。
执行任务时可能还会产生新的异步操作,会产生新的循环,整个过程是循环不断的。
JavaScript 运行机制–Event Loop详解
一篇文章教会你Event loop——浏览器和Node

Event Loop(Node环境)

Node使用了libuv库来实现Event loop。

Event Loop顺序

nodejs的event loop分为6个阶段,它们会按照顺序反复运行,分别如下:

  1. timers:执行setTimeout() 和 setInterval()中到期的callback。
  2. I/O callbacks:上一轮循环中有少数的I/Ocallback会被延迟到这一轮的这一阶段执行
  3. idle, prepare:队列的移动,仅内部使用
  4. poll:最为重要的阶段,执行I/O callback,在适当的条件下会阻塞在这个阶段
  5. check:执行setImmediate的callback
  6. close callbacks:执行close事件的callback,例如socket.on(“close”,func)
    Node-Event Loop
    不同于浏览器的是,在每个阶段完成后,而不是MacroTask任务完成后,microTask队列就会被执行。这就导致了同样的代码在不同的上下文环境下会出现不同的结果。
    另外需要注意的是,如果在timers阶段执行时创建了setImmediate则会在此轮循环的check阶段执行,如果在timers阶段创建了setTimeout,由于timers已取出完毕,则会进入下轮循环,check阶段创建timers任务同理。
    一篇文章教会你Event loop——浏览器和Node

RegExp相关知识点

RegExp 构造函数创建了一个正则表达式对象,用于将文本与一个模式匹配[2018-03-27]。

正则flags的说明

g–全局匹配;找到所有匹配,而不是在第一个匹配后停止
i–忽略大小写
m–多行; 将开始和结束字符(^和$)视为在多行上工作(也就是,分别匹配每一行的开始和结束(由 \n 或 \r 分割),而不只是只匹配整个输入字符串的最开始和最末尾处
u–Unicode; 将模式视为Unicode序列点的序列
y–粘性匹配; 仅匹配目标字符串中此正则表达式的lastIndex属性指示的索引(并且不尝试从任何后续的索引匹配)

正则表达式中特殊字符的含义

字符类别(Character Classes)

.

点号,小数点,匹配任意单个字符(但不包括行结束符\n \r等)。
例如,/.y/ 匹配 “yes make my day” 中的 “my” 和 “ay”,但是不匹配 “yes”。

\d

匹配任意阿拉伯数字。等价于[0-9]。
例如,/\d/ 或 /[0-9]/ 匹配 “B2 is the suite number.” 中的 ‘2’。

\D

匹配任意一个不是阿拉伯数字的字符。等价于[^0-9]。
例如,/\D/ 或 /[^0-9]/ 匹配 “B2 is the suite number.” 中的 ‘B’。
PS:由于没有全局匹配,只能匹配到一个’B’就结束匹配。

\w

匹配任意来自基本拉丁字母表中的字母数字字符,还包括下划线。等价于 [A-Za-z0-9_]。
例如,/\w/ 匹配 “apple” 中的 ‘a’,”$5.28” 中的 ‘5’ 和 “3D” 中的 ‘3’。

\W

匹配任意不是基本拉丁字母表中单词(字母数字下划线)字符的字符。等价于 [^A-Za-z0-9_]。
例如,/\W/ 或 /[^A-Za-z0-9_]/ 匹配 “50%” 中的 ‘%’。

\s

匹配一个空白符,包括空格、制表符、换页符、换行符和其他 Unicode 空格。
例如 /\s\w*/ 匹配 “foo bar” 中的 ‘ bar’。

\S

匹配一个非空白符。
例如,/\S\w*/ 匹配 “foo bar” 中的 ‘foo’。
PS:经常使用[\s\S]来匹配所有字符。

其他

\t 匹配一个水平制表符(tab)
\r 匹配一个回车符(carriage return)
\n 匹配一个换行符(linefeed)
\v 匹配一个垂直制表符(vertical tab)
\f 匹配一个换页符(form-feed)
[\b] 匹配一个退格符(backspace)(不要与 \b 混淆)
\0 匹配一个 NUL 字符。不要在此后面跟小数点。
\xhh 匹配编码为 hh (两个十六进制数字)的字符。
\uhhhh 匹配 Unicode 值为 hhhh (四个十六进制数字)的字符。
\ 发生转义
例如, 是一个特殊字符,表示匹配某个字符 0 或多次,如 /a/ 意味着 0 或多个 “a”。 为了匹配字面意义上的 ,在它前面加上一个反斜杠,例如,/a\/匹配 ‘a*’。

字符集合(Character Sets)

[xyz]

匹配集合中的任意一个字符。你可以使用连字符’-‘指定一个范围。
例如,[abcd] 等价于 [a-d],匹配”brisket”中的’b’和”chop”中的’c’。

[^xyz]

一个反义或补充字符集,也叫反义字符组。
例如,[^abc] 等价于 [^a-c]。 第一个匹配的是 “bacon” 中的’o’ 和 “chop” 中的 ‘h’。

边界(Boundaries)

^

匹配输入开始。如果多行(multiline)标志被设为 true,该字符也会匹配一个断行(line break)符后的开始处。
例如,/^A/ 不匹配 “an A” 中的 “A”,但匹配 “An A” 中的 “A”。

$

匹配输入结尾。如果多行(multiline)标志被设为 true,该字符也会匹配一个断行(line break)符的前的结尾处。
例如,/t$/ 不匹配 “eater” 中的 “t”,但匹配 “eat” 中的 “t”。

\b

如果符合要求就一直往后匹配,一直到无法匹配为止,这就是贪婪模式。所谓的惰性模式就是一旦匹配到合适的就结束,不在继续匹配下去了。

分组(Grouping)与反向引用(back references)

先行断言(lookahead)

先行肯定断言

x(?=y)
只有当 x 后面紧跟着 y 时,才匹配 x。
/Jack(?=Sprat|Frost)/ 只有在 ‘Jack’ 后面紧跟着 ‘Sprat’ 或 ‘Frost’ 时,才会匹配它。然而,’Sprat’ 或 ‘Frost’ 都不是匹配结果的一部分。

先行否定断言

x(?!y)
只有当 x 后面不是紧跟着 y 时,才匹配 x。
只有当 x 后面不是紧跟着 y 时,才匹配 x。例如,/\d+(?!.)/ 只有当一个数字后面没有紧跟着一个小数点时,才会匹配该数字。/\d+(?!.)/.exec(“3.141”) 匹配 141 而不是 3.141。

后行断言(lookbehind)

这是ES2018新加的标准

后行肯定断言

(?<=y)x
只有当 x 前面紧跟 y 时,才匹配 x。
例如:/(?<=\$)\d+/.exec(‘$1000’) // 1000

后行否定断言

(?<!y)x
只有当 x 前面紧不是跟 y 时,才匹配 x。
例如:/(?<!\$)\d+/.exec(‘$99¥1000’) // 99

获取元素的尺寸

window.getComputedStyle(element, [pseudoElt])

定义:Window.getComputedStyle() 方法给出应用活动样式表后的元素的所有CSS属性的值,并解析这些值可能包含的任何基本计算。
参数:
element
用于获取计算样式的Element
pseudoElt 可选
指定一个要匹配的伪元素的字符串。必须对普通元素省略(或null)。
返回:返回的样式是一个实时的 CSSStyleDeclaration 对象,当元素的样式更改时,它会自动更新本身。
兼容性处理:window.getComputedStyle ? window.getComputedStyle(ele, null) : ele.currentStyle
可以通过height属性和getPropertyValue(“height”)获取height值。

elem.getBoundingClientRect()

定义:Element.getBoundingClientRect()方法返回元素的大小及其相对于视口的位置。
返回:top、right、bottom、left(相对于视口)width、right。

元素的属性

content+padding:clientWidth\clientHeight
content+padding+border: offsetWidth\offsetHeight

埋点

定义:埋点分析,是网站分析的一种常用的数据采集方法。数据埋点分为初级、中级、高级三种方式。数据埋点是一种良好的私有化部署数据采集方式。
实现步骤:埋点阶段 -> 数据收集阶段 -> 后端处理阶段

买点分类

适合前端埋点

  1. 运营初级阶段,产品功能相对简单(无明确业务数据、交易数据,仅通过UV、PV、点击量等基本指标分析即可满足需求)
  2. 需求分析与后端没有交互的前端行为(运营人员工作需要判断前端界面设计是否合理)

    适合后端埋点

  3. 追求精细化运营,需要进行多维度数据分析
  4. 包含用户资产、用户账户、风控辅助数据等重点业务数据
  5. 对数据安全性要求比较高
  6. 前端和后端都可以采集到的数据,应优先考虑后端埋点

    前端埋点(实现步骤)

    埋点

    以Google Analytics分析为例
    在页面上插入一段js代码,被称为埋点代码。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <script type="text/javascript">  
    var _maq = _maq || []; // 全局的数组,放采集到的数据
    _maq.push(['_setAccount', 'uuid']); // 默认放了_setAccount用于设置网站标识ID,这个标识ID是在注册GA时分配的。
    (function () {
    var ma = document.createElement('script');
    ma.type = 'text/javascript';
    ma.async = true;
    ma.src = "http://localhost:8091/data/js/ma.js";
    var s = document.getElementsByTagName('script')[0];
    s.parentNode.insertBefore(ma, s);
    })();
    </script>

PS:匿名函数,这段代码的主要目的就是引入一个外部的js文件(ga.js),方式是通过document.createElement方法创建一个script并根据协议(http或https)将src指向对应的ga.js,最后将这个element插入页面的dom树上。
注意ga.async = true的意思是异步调用外部js文件,即不阻塞浏览器的解析,待外部js下载完成后异步执行。这个属性是HTML5新引入的。

数据采集(之前动态插入的ma.js)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
(function () {
var params = {};
//Document对象数据
if(document) {
params.domain = document.domain || '';
params.url = document.URL || '';
params.title = document.title || '';
params.referrer = document.referrer || '';
}
//Window对象数据
if(window && window.screen) {
params.sh = window.screen.height || 0;
params.sw = window.screen.width || 0;
params.cd = window.screen.colorDepth || 0;
}
//navigator对象数据
if(navigator) {
params.lang = navigator.language || '';
}
//解析_maq配置
if(_maq) {
for(var i in _maq) {
switch(_maq[i][0]) {
case '_setAccount':
params.account = _maq[i][1];
break;
default:
break;
}
}
}
//拼接参数串
var args = '';
for(var i in params) {
if(args != '') {
args += '&';
}
args += i + '=' + encodeURIComponent(params[i]);
}

//通过Image对象请求后端脚本
var img = new Image(1, 1);
img.src = 'http://analytics.codinglabs.org/1.gif?' + args;
})();

PS:为什么使用图片请求后端controller而不是ajax直接访问?原因在于ajax不能跨域请求,ma.js和后端分析的代码可能不在相同的域内,ajax做不到,而将image对象的src属性指向后端脚本并携带参数,就轻松实现了跨域请求。

后端处理

后端脚本一般做下面几件事:
(1)解析http请求参数的到信息。
(2)从服务器(WebServer)中获取一些客户端无法获取的信息,如访客ip等。
(3)将信息按格式写入log。
(4)生成一副1×1的空gif图片作为响应内容并将响应头的Content-type设为image/gif。
(5)在响应头中通过Set-cookie设置一些需要的cookie信息(作为跟踪唯一访客)。
前端埋点和后端埋点,哪个更科学?
用户行为日志-js埋点(一)实现整体流程
开源埋点库MixPanel

File

文件(File) 接口提供有关文件的信息,并允许网页中的 JavaScript 访问其内容。

files属性

用于获取当前文件数组相关的信息,每个元素有下列属性(均为只读属性)。
File.lastModified
File.name
File.size 单位为B。
File.webkitRelativePath
File.type 返回文件的 多用途互联网邮件扩展类型

FileReader() 构造函数

FileReader 对象允许Web应用程序异步读取存储在用户计算机上的文件(或原始数据缓冲区)的内容,使用 File 或 Blob 对象指定要读取的文件或数据。
FileReader.abort()
该方法可以取消 FileReader 的读取操作,触发之后 readyState 为已完成(DONE)。
FileReader.readAsArrayBuffer()
开始读取指定的 Blob中的内容, 一旦完成, result 属性中保存的将是被读取文件的 ArrayBuffer 数据对象.
FileReader.readAsBinaryString()
开始读取指定的Blob中的内容。一旦完成,result属性中将包含所读取文件的原始二进制数据。
FileReader.readAsBinaryString()
开始读取指定的Blob中的内容。一旦完成,result属性中将包含所读取文件的原始二进制数据。
FileReader.readAsText()
开始读取指定的Blob中的内容。一旦完成,result属性中将包含一个字符串以表示所读取的文件内容。

前端错误收集

window.onerror(大部分的错误)

window.onerror = function(message, source, lineno, colno, error) { … }
PS:

  1. 由于网络请求异常事件不会冒泡,需要在捕获阶段进行处理
  2. 不能捕获promise的错误信息
  3. 跨域资源需要专门处理,需要在script标签加上crossorigin属性,服务器设置Access-Control-Allow-Origin
  4. window.onerror 函数只有在返回 true 的时候,异常才不会向上抛出,否则即使是知道异常的发生控制台还是会显示 Uncaught Error: xxxxx。

promise的错误处理

promise除了使用catch方法来捕获错误,还可以使用window的unhandledrejection事件捕获异常

1
2
3
4
window.addEventListener("unhandledrejection", function (event) {
console.warn("WARNING: Unhandled promise rejection. Shame on you! Reason: "
+ event.reason);
});

try catch

try catch只能捕获同步代码的异常,对回调,setTimeout,promise等无能为力

上报错误

  1. 后端提供接口,前端ajax上传
  2. 创建一个新的图片,url参数带上错误信息(跨域)
1
2
3
4
function report(error) {
var reportUrl = 'http://xxxx/report';
new Image().src = reportUrl + 'error=' + error;
}

阿里UED前端监控

npx

引入这个命令的目的是为了提升开发者使用包内提供的命令行工具的体验。

1
npx create-react-app my-cool-new-app

这条命令会临时安装 create-react-app 包,命令完成后 create-react-app 会删掉,不会出现在 global 中。下次再执行,还是会重新临时安装。

判断浏览器对某个css3是否支持

‘样式属性名’ in document.documentElement.style

‘transform’ in document.documentElement.style // true
PS:兼容性较好,ie6+

@supports

此方法为css属性

1
2
3
4
5
6
@supports (display: flex) {
div { display: flex; }
}
@supports not (display: flex) {
div { float: left; } /* 替换样式 */
}

通常关系链接符有 not、or、and等

CSS.supports

CSS.supports(propertyName, value) || CSS.supports(supportCondition)
CSS.supports() 静态方法返回一个Boolean值,用来校验浏览器是否支持一个给定的CSS特性。
该方法有两种参数形式:

  1. propertyName:属性名,value:属性值。
  2. supportCondition:需要一个匹配@supports条件的参数。
    1
    2
    result = CSS.supports('(--foo: red)'); // true第一种形式
    result = CSS.supports("( transform-origin: 5% 5% )"); // true第二种形式

装饰者模式

装饰者模式(Decorator Pattern):在不改变原类和继承的情况下动态扩展对象功能,通过包装一个对象来实现一个新的具有原对象相同接口的新的对象。
装饰者用于通过重载方法的形式添加新功能,该模式可以在被装饰者前面或者后面加上自己的行为以达到特定的目的。
装饰者模式是为已有功能动态地添加更多功能的一种方式,把每个要装饰的功能放在单独的函数里,然后用该函数包装所要装饰的已有函数对象,因此,当需要执行特殊行为的时候,调用代码就可以根据需要有选择地、按顺序地使用装饰功能来包装对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//需要装饰的类(函数)
function Macbook() {
this.cost = function () {
return 1000;
};
}
//计算商品的包装费
function PackagingFee(macbook) {
this.cost = function () {
return macbook.cost() + 75;
};
}
//计算商品的运费
function Freight(macbook) {
this.cost = function () {
return macbook.cost() + 300;
};
}

let money1 = new Macbook() // 只包含mac 1000
let money2 = new PackagingFee(new Macbook()) // 只包含mac + 包装费 1075
let money4 = new Freight(new Macbook()) // 只包含mac + 运费 1300
let money3 = new Freight(new PackagingFee(new Macbook())) // 包含mac + 包装费 + 运费 1375

touchstart 和 click 的坑

产生原因:

1
2
3
4
var tap = 'ontouchstart' in window ? 'touchstart' : 'click';
wrap.addEvenListener(tap, function(){
signUp();
});

通过上面代码简单为用户设置事件类型,但我们忽略了‘联想触控笔记本’,由于该笔记本支持‘touchstart’事件,但当用户使用鼠标时却不能触发‘touchstart’事件,造成不能触发对应的事件处理函数。

  1. 有些 PC 设备屏幕为触摸屏,同时支持touchstart和click事件;
  2. 用户触发touchstart事件,默认必然会导致触发click事件,但是触发click事件,不一定会导致touchstart事件被触发;
  3. 此类设备外接鼠标时,通过上面的绑定方式,会绑定touchstart事件,但是鼠标操作只能触发click,导致touchstart不触发;

解决方案

均使用click事件(移动端存在300ms延迟)

通过UA判断设备是否为移动端,再确认事件类型

1
2
3
4
function isMobile() {
return navigator.userAgent.match(/(blackberry|configuration\/cldc|hp |hp-|htc |htc_|htc-|iemobile|kindle|midp|mmp|motorola|mobile|nokia|opera mini|opera |Googlebot-Mobile|YahooSeeker\/M1A1-R2D2|android|iphone|ipod|mobi|palm|palmos|pocket|portalmmm|ppc;|smartphone|sonyericsson|sqh|spv|symbian|treo|up.browser|up.link|vodafone|windows ce|xda |xda_)/i) ? true : false;
}
var tap = isMobile() ? 'touchstart' : 'click';

全部使用click,通过FastClick解决300毫秒的延迟问题

不再绑定的时候进行判断,而是对touchstart 和 click 同时绑定,但是在 touchstart 触发的时候暂停取消后续 click 的响应。

具体实现:

  1. 在touchstart事件响应中调用preventDefault()方法,阻止后续click事件的触发(也会阻止多个事件绑定叠加和多人合作项目,导致以来 click 事件出现bug)
  2. 在touchstart事件中设置一些标记,或者取消click事件的绑定,使得click事件触发时不会触发我们绑定的逻辑,在一段时间(例如300-500ms)后再恢复
  3. 直接对事件处理函数进行节流(throttle),保证在一段时间内(300ms - 500ms),事件处理函数只触发一次

defer和async

defer:载入 JavaScript 文件时不阻塞 HTML 的解析(并行),执行阶段被放到 HTML 标签解析完成之后(DOMContentLoaded之前)。
async:与 defer 的区别在于,如果已经加载好,就会开始执行——无论此刻是 HTML 解析阶段还是 DOMContentLoaded 触发之后,但一定在load触发之前执行。

从输入url到页面渲染优化

大值步骤

DNS域名解析->建立TCP连接->下载资源->解析页面

DNS域名解析

将人们习惯使用的域名,映射为对应的ip,机器之间只能识别ip地址。
域名解析的步骤:以www.baidu.com为例
1、浏览器缓存:浏览器会按照一定的频率缓存DNS记录。
2、操作系统缓存:如果浏览器缓存中找不到需要的DNS记录,那就去操作系统中找(hosts文件)。
3、路由缓存:路由器也有DNS缓存(可能存在)。
4、ISP的DNS服务器:ISP是互联网服务提供商(Internet Service Provider)的简称,ISP有专门的DNS服务器应对DNS查询请求。
5、根服务器:ISP的DNS服务器还找不到的话,它就会向根服务器发出请求,进行递归查询(DNS服务器先问根域名服务器.域名服务器的IP地址,然后再问.com顶级域名服务器,依次类推)。
PS:以www.baidu.com为例说明根域名、顶级域名、一级域名等
根域名:为’.’,全球共13个ip地址(并不是13台机器)。
顶级域名(一级域名):为www.baidu.com的顶级域名为baidu.com,主机名为www。
二级域名:a.www.baidu.com的二级域名为www.baidu.com,主机名为a。
优化方案:域名预解析

1
2
3
4
// 告知浏览器, 当前页面要做DNS预解析
<meta http-equiv="x-dns-prefetch-control" content="on" />
// 强制对DNS预解析(本网页需要跳转的页面,注意避免多页面重复DNS解析)
<link rel="dns-prefetch" href="http://bdimg.share.baidu.com" />

建立TCP连接

经过域名解析,浏览器拿到了服务器的IP。
TCP建立连接的三次握手:
客户端创建socket,向服务器目标端口发送连接建立请求,数据段包含位码SYN(建立联机标志位) = 1,随机数seq(顺序号码)= x,和其他TCP标志和选项。
服务器有一个专门处理连接请求的welcome socket,接收到连接建立请求,置位码SYN和ACK(确认标志位)为1,ack(确认号码)= x + 1,随机数seq = y,并返回。
客户端检查ack是否等于x + 1,等于时,将ACK置为1,SYN置为0,将ack置为y + 1发送至服务器端。
welcome socket检查ack等于y + 1和ACK等于1后,创建新的socket,此socket由源IP/源端口、目标IP/目标端口标识,之后客户端发送的数据都被引导向此新的socket,至此,TCP连接建立。
优化方案:
减少数据往返延迟(地理位置远近关系)可以使用CDN(客户端访问域名到距离最近的服务器)。
使用HTTP2(多路复用 (Multiplexing))、header压缩(减少传输数据大小)、服务端推送(server push)(有时只需要服务器发送数据到客户端,不需要客户端->服务端—>客户端这样一个来回)
减少cookie:每次请求都携带了本域的cookie
PS:虽然HTTP 1.1有长链接,但资源下载事串行的,所以推荐使用HTTP2的多了复用。

下载资源(接收响应)

避免不必要的重定向(非缓存重定向),重定向会导致浏览器再进行一次(DNS域名解析->建立TCP连接->下载资源->解析页面)。
避免超出并行连接阀值(浏览器会在同域名并行连接限制,一般为6个)

解析页面(webkit工作原理)

处理HTML标记,构建DOM树。
处理CSS标记,构建CSSOM树。
将DOM树和CSSOM树融合成渲染树(会忽略不需要渲染的dom)。
根据渲染树来布局,计算每个节点的几何信息。
在屏幕上绘制各个节点。
中间遇到各种资源时,会进行资源的下载。
优化:
css放head中,js放body尾部,均以外链形式
压缩js、css等文件
使用CDN技术
使用图片精灵、webp、图片base64等
首屏优化(按需加载、首屏资源优先加载)
避免重排(开启复合层,如使用3d变换、opacity等3d硬件加速的坑),减少重绘
缓存技术:强缓存(Expires、Cache-Control:max-age=seconds)协商缓存(Last-Modified/If-Modified-Since、ETag/If-None-Match)

CSS2Dtransform和Matrix的关系

这里transform包括:translate、scale、rotate、skew
没有任何变换时:matrix(1, 0, 0, 1, 0, 0)、matrix(a, b, c, d, e, f)
translate(30, 30) 等价于 matrix(1, 0, 0, 1, 30, 30)
ps:e、f分别表示x轴和y轴偏移距离
scale(2, 3) 等价于 matrix(2, 0, 3, 1, 0, 0)
ps:a、c分别表示x轴和y轴缩放比例
rotate(θ) 等价于 matrix(cosθ,sinθ,-sinθ,cosθ,0,0)
ps:a、b、c、d共同决定旋转角度θ
skew(θx, θy) 等价于 matrix(1,tan(θy),tan(θx),1,0,0)
ps:θx表示x轴倾斜的角度,θy表示y轴

硬编码和非硬编码(软编码)的区别

软编码可以在运行时确定,修改;而硬编码是不能够改变的。
硬编码是指将可变变量用一个固定值来代替的方法,使用这样方法,编译后,如果想更改词变量就变的非常困难。因此, 在大部分的程序语言中,可以将一个固定值定义为一个标记, 然后用这个特殊的标记来取代变量名称。 当标记名称发生改变时, 变量名不变。这样在程序进行编译时,所有的变量都不再是固定值 这样就更容易实现改变变量的目的。

幂等

在编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。幂等函数,或幂等方法,是指可以使用相同参数重复执行,并能获得相同结果的函数。这些函数不会影响系统状态,也不用担心重复执行会对系统造成改变。

Intersection Observer

2018-05-28添加
定义:IntersectionObserver构造函数为开发者提供了一种可以异步监听目标元素与其祖先或视窗(viewport)交叉状态的手段。祖先元素与视窗(viewport)被称为根(root)。
语法:let io = new new IntersectionObserver(callback(entries), options)
用法:

1
2
3
4
5
6
7
8
9
10
11
var intersectionObserver = new IntersectionObserver(function(entries) {
// entries 时一个数组,每个成员都是一个[IntersectionObserverEntry对象](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserverEntry)。
if (entries[0].intersectionRatio <= 0) return;

// 元素与祖先元素交叉状态发生改变时触发
loadItems(10);
console.log('Loaded new items');
});

// start observing
intersectionObserver.observe(document.querySelector('.scrollerFooter'));

属性(options):
root – 所监听对象的具体祖先元素(element)。如果未传入任何值或值为null,则默认使用viewport。
rootMargin – 计算交叉时添加到根(root)边界盒bounding box的矩形偏移量, 可以有效的缩小或扩大根的判定范围从而满足计算需要。此属性返回的值可能与调用构造函数时指定的值不同,因此可能需要更改该值,以匹配内部要求。所有的偏移量均可用像素(pixel)(px)或百分比(percentage)(%)来表达, 默认值为”0px 0px 0px 0px”。
thresholds – 一个包含阈值的list, 升序排列, list中的每个阈值都是监听对象的交叉区域与边界区域的比率。当监听对象的任何阈值被越过时,都会生成一个通知(Notification)。如果构造器未传入值, 则默认值为0.
方法:
observe(element) – 使IntersectionObserver开始监听一个目标元素。
unobserve(element) – 使IntersectionObserver停止监听特定目标元素。
takeRecords() – 为所有监听目标返回一个IntersectionObserverEntry对象数组并且停止监听这些目标。
disconnect() – 使IntersectionObserver对象停止监听工作。
应用:惰性加载(lazy load)、无限滚动等
MDN-Intersection Observer
IntersectionObserver API 使用教程
查看浏览器支持情况

MutationObserver(异步的)

2018-05-28添加
定义:MutationObserver给开发者们提供了一种能在某个范围内的DOM树发生变化时作出适当反应的能力.该API设计用来替换掉在DOM3事件规范中引入的Mutation事件.
语法:var observer = new MutationObserver(callback(mutations, observer));
PS:mutations 为DOM变动数组,observer 为观察器实例。
用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var observer = new MutationObserver(function (mutations, observer) {
mutations.forEach(function(mutation) {
console.log(mutation);
});
});


var article = document.querySelector('article');
var options = {
'childList': true,
'attributes':true
} ;

// start Observer
observer.observe(article, options);

方法:
observe(element, options) – 给当前观察者对象注册需要观察的目标节点,在目标节点(还可以同时观察其后代节点)发生DOM变化时收到通知.
PS:target 为观察该节点是否会发生DOM变化.options 为一个MutationObserverInit对象,指定要观察的DOM变化类型.
disconnect() – 让该观察者对象停止观察指定目标的DOM变化.直到再次调用其observe()方法,该观察者对象包含的回调函数都不会再被调用.
takeRecords() – 清空观察者对象的记录队列,并返回里面的内容.
MDN-MutationObserver
Mutation Observer API

参考文档:
HTTP/2
DNS域名解析的过程
从输入url开始能做哪些优化
DNS解析过程详解

-------------本文结束感谢您的阅读,如果本文对你有帮助就记得给个star-------------
Donate comment here