之前准备面试做的笔记,从三件套到浏览器,应该算是比较全面的基础前端八股文知识整理

HTML

HTML 语义化

使用语义化标签而不是div, span

作用:

​ 1.使人更易理解

​ 2.SEO 使机器(搜索引擎 爬虫)能够理解

行级块级元素(块状,内联)

display:block/table

​ 有div h1 h2 table ul ol p等
display: inline/inline-block

​ 有 span img input button等

script 标签中 defer 和 async

普通的sript:阻止HTML解析,下载并加载完后才会继续解析HTML

defer:异步下载,同步加载(HTML加载完后才会加载)

async:异步下载和加载,可能会阻碍HTML解析

CSS

盒模型宽度计算

width+padding*2+boder*2

使宽度计算方式包括内边距和边框:box-sizing: border-box;

margin

重叠

相邻元素的margin-top和margin-bottom会重叠

空白内容也会发生重叠

负值

margin-top和margin-left负值,元素向上、向左移动
margin-right和margin-bottom负值,右侧元素左移,下方元素上移,自身不受影响

BFC

Block format context ,块级格式化上下文

一块独立渲染区域,内部元素的渲染不会影响边界以外的元素

形成BFC的常见条件
float不是none
position 是 absolute 或 fixed
overflow不是visible
display 是 flex inline-block等

float

两栏布局

(左侧固定 + 右侧自适应布局)

现在有以下 DOM 结构:

1
2
3
4
<div class="outer">
<div class="left">左侧</div>
<div class="right">右侧</div>
</div>
  1. 利用浮动,左边元素宽度固定 ,设置向左浮动。将右边元素的 margin-left 设为固定宽度 。注意,因为右边元素的 width 默认为 auto ,所以会自动撑满父元素。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
.outer {
height: 100px;
}
.left {
float: left;
width: 200px;
height: 100%;
background: lightcoral;
}
.right {
margin-left: 200px;
height: 100%;
background: lightseagreen;
}
  1. 同样利用浮动,左边元素宽度固定 ,设置向左浮动。右侧元素设置 overflow: hidden; 这样右边就触发了 BFCBFC 的区域不会与浮动元素发生重叠,所以两侧就不会发生重叠。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
.outer {
height: 100px;
}
.left {
float: left;
width: 200px;
height: 100%;
background: lightcoral;
}
.right {
overflow: auto;
height: 100%;
background: lightseagreen;
}
  1. 利用 flex 布局,左边元素固定宽度,右边的元素设置 flex: 1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
.outer {
display: flex;
height: 100px;
}
.left {
width: 200px;
height: 100%;
background: lightcoral;
}
.right {
flex: 1;
height: 100%;
background: lightseagreen;
}
  1. 利用绝对定位,父级元素设为相对定位。左边元素 absolute 定位,宽度固定。右边元素的 margin-left 的值设为左边元素的宽度值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.outer {
position: relative;
height: 100px;
}
.left {
position: absolute;
width: 200px;
height: 100%;
background: lightcoral;
}
.right {
margin-left: 200px;
height: 100%;
background: lightseagreen;
}
  1. 利用绝对定位,父级元素设为相对定位。左边元素宽度固定,右边元素 absolute 定位, left 为宽度大小,其余方向定位为 0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
.outer {
position: relative;
height: 100px;
}
.left {
width: 200px;
height: 100%;
background: lightcoral;
}
.right {
position: absolute;
left: 200px;
top: 0;
right: 0;
bottom: 0;
height: 100%;
background: lightseagreen;
}
三栏布局

圣杯:

1
2
3
4
5
6
7
<header style="background-color: rgb(233, 233, 233);">header</header>
<container class="container">
<div class="center">center</div>
<div class="left">left</div>
<div class="right">right</div>
</container>
<footer style="background-color: rgb(233, 233, 233);">footer</footer>
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
.container {
display: block;
padding-left: 200px;
padding-right: 300px;
}

.center {
float: left;
background-color: bisque;
width: 100%;
}

.left {
position: relative;
float: left;
background-color: aqua;
width: 200px;
margin-left: -100%;
right: 200px;
}

.right {
float: left;
background-color: aquamarine;
width: 300px;
margin-right: -300px;
}

footer {
clear: both;
}

双飞翼:

1
2
3
4
5
6
7
8
9
10
11
<div id="main">
<div id="main-wrap">
main
</div>
</div>
<div id="left">
left
</div>
<div id="right">
right
</div>
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
#main {
width: 100%;
height: 200px;
background-color: greenyellow;
float: left;
}

#main-wrap {
margin-left: 200px;
margin-right: 250px;
}

#left {
width: 200px;
height: 200px;
background-color: aqua;
float: left;
margin-left: -100%;
}

#right {
width: 250px;
height: 200px;
background-color: lightgreen;
float: left;
margin-left: -250px;
}

clear fix:

1
2
3
4
5
6
7
8
.clearfix:after {
content: '' ;
display:table;
clear:both;
}
.clearfix {
*zoom:1;/*兼容IE低版本*/
}

flex

flex-direction 主轴方向

justify-content 主轴对齐方式

align-items 交叉轴对齐方式

flex-wrap 换行

align-self 子元素交叉轴对齐

画个色子

1
2
3
4
5
<div class="box">
<span class="item"></span>
<span class="item"></span>
<span class="item"></span>
</div>
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
.box {
width: 200px;
height: 200px;
border: 2px solid #ccc;
border-radius: 10px;
padding: 20px;

display: flex;
justify-content: space-between;
}

.item {
display: block;
width: 40px;
height: 40px;
border-radius: 50%;
background-color: palevioletred;
}

.item:nth-child(2) {
align-self: center;
}

.item:nth-child(3) {
align-self: flex-end;
}

position

absolute和relative分别依据什么定位?

relative依据自身定位

自身位移并作为子元素(如果有)的定位基准

absolute依据最近一层的定位元素定位

定位元素
absolute relative fixed
body(找不到absolute时)

居中对齐实现方式

水平居中
inline元素:text-align:center
block元素:margin:auto
absolute元素:left:50%+margin-left负值(必须知道子元素尺寸) -宽度/2

垂直居中
inline元素:line-height的值等于height值
absolute元素:top:50%+margin-top负值
absolute元素:transform(-50%,-50%)

​ absolute元素:top,left,bottom,right=0+margin:auto

line-height 继承方式:

具体数值,如30px,则继承该值
比例,如2/1.5,则继承该比例
百分比,如200%,则继承计算出来的值

响应式(rem/vw/vh)

px,绝对长度单位,最常用
em,相对长度单位,相对于父元素,不常用

root em rem,相对长度单位,相对于根元素,常用于响应式布局

vh网页视口高度的1/100
vw网页视口宽度的1/100
vmax 取两者最大值;vmin取两者最小值

响应式布局的常用方案

media-query,根据不同的屏幕宽度设置根元素 font-size
rem,基于根元素的相对单位

网页视口尺寸

window.screen.height//屏幕高度
window.innerHeight//网页视口高度
document.body.clientHeight//body高度

JS

变量类型和计算

值类型和引用类型的区别

引用类型vs值类型

引用:储存内存地址(对象,数组,函数等)

typeof 能判断哪些类型

识别所有值类型
识别函数
判断是否是引用类型(object)(不可再细分)

变量计算-类型转换

字符串拼接: 拼接时有字符串则会转换为字符串 如 a = true + ‘wdnmd’ //‘truewdnmd’

何时使用===何时使用==

除==null,用===
==: ‘’ == false -> true; 0 == false -> true; null == undifined -> true

if 语句和逻辑运算 : !!(0/NaN/‘’/null/undefined/false) 为falsely变量,其他为truly

深拷贝

handle\1-深拷贝.ts

原型原型链

class 和继承

类型判断 instanceof

原型和原型链

如何准确判断一个变量是不是数组?

val instanceof Array // true/false

手写一个简易的jQuery,考虑插件和扩展性

class jQuery {}
jQuery.prototype.chajian = function(args){}
class jjQuery extends jQuery {}

class 的原型本质,怎么理解?

每个class都有显示原型prototype
每个实例都有隐式原型__proto__
实例的__proto__指向对应class的prototype

作用域和闭包

作用域和自由变量

全局作用域
函数作用域
块级作用域(ES6新增)

一个变量在当前作用域没有定义,但被使用了
向上级作用域,一层一层依次寻找,直至找到为止
如果到全局作用域都没找到,则报错xx is not defined

闭包

函数中自由变量的查找在函数定义的地方而不是执行的地方
在函数定义的时候,如果当前作用域有被引用的变量,则会形成闭包

this

取值是在函数执行时确定的,而不是定义时确定的
作为对象方法执行作用域为上级对象
setTimeout的this指向window
箭头函数this的取值始终为上级作用域

this 的不同应用场景,如何取值?

当做普通函数被调用: 执行时作用域
使用call apply bind: 指定作用域
作为对象方法调用: 上级对象
在class的方法中调用: 对象本身
箭头函数: 上级作用域

手写 bind 函数

handle\2-bind.ts

实际开发中闭包的应用场景,举例说明

隐藏数据,只提供api

异步

单线程和异步

JS是单线程语言,只能同时做一件事儿
浏览器和 nodejs 已支持JS启动进程,如Web Worker
JS和DOM渲染共用同一个线程,因为JS可修改DOM结构

应用场景

网络请求,如ajax图片加载
定时任务,如 setTimeout

callback hell 和 Promise

同步和异步的区别是什么?

基于JS是单线程语言
异步不会阻塞代码执行
同步会阻塞代码执行

手写用 Promise 加载一张图片

promise.ts

前端使用异步的场景有哪些?

网络请求,如ajax图片加载
定时任务,如setTimeout

event loop 事件循环

JS如何执行?

从前到后,一行一行执行
如果某一行执行报错,则停止下面代码的执行
先把同步代码执行完,再执行异步

event loop

同步代码,一行一行放在Call Stack执行
遇到异步,会先“记录”下,等待时机(定时、网络请求等)
时机到了,就移动到 Callback Queue
如 Call Stack为空(即同步代码执行完)Event Loop开始工
轮询查找 Callback Queue,如有则移动到 Call Stack 执行
然后继续轮询查找(永动机一样)

DOM 事件和 event loop

JS是单线程的
异步(setTimeout,ajax等)使用回调,基于event loop
DOM 事件也使用回调,基于 event loop

Promise

三种状态

pending resolved rejected
pending -> resolved 或 pending -> rejected
变化不可逆

状态的表现和变化

pending状态,不会触发then和catch
resolved 状态,会触发后续的 then 回调函数
rejected状态,会触发后续的catch回调函数

then 和 catch 对状态的影响

then 正常返回 resolved,里面有报错则返回 rejected
catch 正常返回 resolved,里面有报错则返回 rejected

async/await

异步回调 callback hell
Promise then catch 链式调用,但也是基于回调函数
async/await是同步语法,彻底消灭回调函数

async/await和Promise的关系

执行async函数,返回的是Promise对象 (值封装为Promise对象,Promise直接返回)
await 相当于 Promise 的 then
try…catch 可捕获异常,代替了 Promise 的 catch

for … of

for…in(以及forEach for)是常规的同步遍历
for…of 常用于异步的遍历

宏任务 macroTask和微任务 microTask

什么是宏任务,什么是微任务

宏任务:setTimeout,setInterval,Ajax,DOM事件
微任务:Promise async/await
微任务执行时机比宏任务要早

event loop 和 DOM 渲染
每次Call Stack清空(即每次轮询结束),即同步任务执行完
都是DOM重新渲染的机会,DOM结构如有改变则重新渲染
然后再去触发下一次 Event Loop

微任务和宏任务的区别

宏任务:DOM渲染后触发,如setTimeout
微任务:DOM渲染前触发,如Promise

微任务是ES6语法规定的
宏任务是由浏览器规定的

宏任务有哪些?微任务有哪些?微任务触发时机更早
微任务、宏任务和DOM渲染的关系
微任务、宏任务和DOM渲染,在eventloop的过程

手写promise

3-promise.ts

JS Web API

DOM

DOM 本质

从HTML解析出来的一棵树

DOM 节点操作

获取DOM节点

1
2
3
4
5
6
const div1 = document.getElementById('div1')//元素
const divList = document.getElementsByTagName('div')//集合
const containerList = document.getElementsByClassName('.container')//集合
const pList = document.querySelectorAll('p')//集合

const p = pList[0]

property:修改对象属性,不会体现到html结构中

1
2
3
4
5
6
7
8
9
console.log(p.style.width)//获取样式
p.style.width='100px'//修改样式

console.log(p.className)//获取class
p.className='p1'//修改 class

//获取 nodeName 和 nodeType
console.log(p.nodeName)
console.log(p.nodeType)

attribute:修改html属性,会改变html结构

1
2
3
4
p.setAttribute('data-name','imooc')
o.setAttribute('style','font-size:30px;')
p.getAttribute('data-name')//imooc
p.getAttribute('style')//font-size:30px;

两者都有可能引起DOM重新渲染

DOM 结构操作

新增/插入节点

1
2
3
4
5
6
7
8
9
//新建节点
const newP = document.createElement('p')
newP.innerHTML='this is newp'
//插入节点
div1.appendChild(newP)

//移动节点(已在DOM中的节点appendChild不会复制)
const oldp = document.getElementById('oldp')
div2.appendChild(oldp)

获取父元素,子元素列表

1
2
3
4
5
6
7
8
9
10
//获取父元素
const divl = document.getElementById('div1')
const parent = divl. parentNode
//获取子元素列表
const divl = document.getElementById('divl')
const child = div1.childNodes
const child=Array.prototype.slice.call(div1.childNodes).filter(child => {
if(child.nodeType === 1) return true;
return false;
})

删除子元素

1
div1.removeChild(child[0])

DOM 性能

DOM操作非常“昂贵”,避免频繁的DOM操作
对DOM查询做缓存
频繁操作改为一次性操作

BOM

navigator

如何识别浏览器的类型
navigator.userAgent

screen

screen.width
screen.height

location

网址:location.href
协议:location.protocol
域名:location.host
协议+ 域名:location.origin
路径:location.pathname
参数:location.search
锚点:location.hash

history

后退:history.back
前进:history.forward

事件

事件绑定

1
2
3
4
5
const btn = doucument.getElementById('btn');
btn.setEventListener('click',e => {alert('click')})

// console.log(e.target)//获取触发的元素
// event.preventDefault()//阻止默认行为

什么是事件流

事件流描述的就是从页面中接收事件的顺序。而早期的IE和Netscape提出了完全相反的事件流概念,IE事件流是事件冒泡,而Netscape的事件流就是事件捕获。

事件冒泡

事件会传递给父元素

1
// e.stopPropagation() //阻止冒泡

事件代理

代码简洁
减少浏览器内存占用
但是,不要滥用

1
2
3
4
5
6
7
div.addEventListener('click', e => {
event.preventDefault();
const target = event.target
if(target.nodeName === 'A'){
alert(target.innerHTML)
}
})

编写一个通用的事件监听函数

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
// function bindEvent(ele, type, fn){
// ele.addEventListener(type,fn)
// }

function bindEvent(ele, type, selector, fn){
if(!fn){
fn = selector
selector = null
}
ele.addEventListener(type,e => {
const target = e.target
if(selector){
if(target.matches(selector)){
fn.call(target, e)
}
} else {
fn.call(target, e)
}
})
}

//普通绑定
bindEvent(btn, 'click', function(e) {
e.preventDefault();
alert(this.innerHTML)
})
//代理绑定
bindEvent(div, 'click', 'a', function(e) {
e.preventDefault();
alert(this.innerHTML)
})

描述事件冒泡的流程

基于DOM树形结构
事件会顺着触发元素往上冒泡
应用场景:代理

无限下拉的图片列表,如何监听每个图片的点击?

事件代理
用 e.target获取触发元素
用matches来判断是否是触发元素

ajax

XMLHttpRequest

xhr.readyState
0-UNSET尚未调用open方法
1-OPENED open方法已被调用
2-HEADERS_RECEIVED send方法已被调用,header已被接收
3-LOADING 下载中,responseText已有部分内容
4-DONE 下载完成

状态码

xhr.status
·2xx - 表示成功处理请求,如200
·3xx - 需要重定向,浏览器直接跳转,如301 302 304
·4xx - 客户端请求错误,如404 403
·5xx - 服务器端错误

手写一个简易的 ajax

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function ajax(url: string) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', url, true)
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
resolve(xhr.responseText)
} else if (xhr.status === 404) {
reject(new Error('404NotFound'))
}
}
}
xhr.send(null)
})

}

跨域:同源策略,跨域解决方案

什么是跨域(同源策略)

ajax请求时,浏览器要求当前网页和server必须同源(安全)
同源:协议、域名、端口,三者必须一致
前端:http://a.com:8080/ server:https://b.com/api/xxx

**<img/>**可用于统计打点,可使用第三方统计服务
**<link/><script>**可使用CDN,CDN一般都是外域
**<script>**可实现JSONP

所有的跨域,都必须经过 server 端允许和配合
未经server端允许就实现跨域,说明浏览器有漏洞,危险信号

JSONP

服务器可以返回任何内容<script>可绕过跨域限制
服务器可以任意动态拼接数据返回
所以,<script>就可以获得跨域的数据,只要服务端愿意返回

CORS(服务端支持)

常见方式:

  1. jsop
    html中的script src属性获取其他源的数据
    <script>
    function getData(res){
    console.Log(res)
    </script>
    <script src=”http://www.baidu.com/news?callback=getData">

2.cors跨域资源共享支持所有的主流浏览器ie9+
XMLHttpRequest发送请求的时候,如果不同源,heaters{Origin}
后台处理:Access-control-allow-origin:

3.h5 window.postMessage跨域 主流浏览器 ie8+
window.postMessage(“字符串”,”*”)

注意:vue中跨域::代理proxy··本质上·cors跨域
vue.config.js.
proxy:{
target:
changeorigin:
pathRewrite:

存储

cookie

本身用于浏览器和 server 通讯
被“借用”到本地存储来
可用 document.cookie = … 来修改

cookie 的缺点

存储大小,最大4KB
http请求时需要发送到服务端,增加请求数据量
只能用 document.cookie = …来修改,太过简陋

localStorage 和 sessionStorage

HTML5专门为存储而设计,最大可存5M
API简单易用 setItem getItem
不会随着http请求被发送出去

localStorage 数据会永久存储,除非代码或手动删除
sessionStorage 数据只存在于当前会话,浏览器关闭则清空
一般用localStorage会更多一些

描述cookie localStorage sessionStorage 区别

容量
API易用性
是否跟随http请求发送出去

HTTP

前端工程师开发界面
需要调用后端的接口,提交/获取数据——http协议
要求事先掌握好ajax

http 状态码

状态码分类

1xx服务器收到请求
2xx请求成功,如200
3xx重定向,如302
4xx客户端错误,如404
5xx服务端错误,如500

常见状态码

200成功
201请求成功并创建新资源 POST/PUT 请求
204满足请求 不需要刷新表单或页面 表单或者删除
301永久重定向(配合location,浏览器自动处理
302临时重定向(配合location,浏览器自动处理)
304资源未被修改 浏览器缓存
400服务器由于客户端错误而无法理解和处理请求
401当需要身份验证但失败或未提供身份验证
403没有权限
404资源未找到
409同时更新或版本相互冲突
410请求的资源不再可用
500服务端错误
504网关超时

关于协议和规范

就是一个约定
要求大家都跟着执行
不要违反规范,例如IE浏览器

http methods

传统的methods

get获取服务器的数据
post像服务器提交数据
简单的网页功能,就这两个操作

现在的methods

get 获取数据
post 新建数据
patch/put更新数据
delete删除数据

Restful API

一种新的API设计方法(早已推广使用)
传统API设计:把每个url当做一个功能
Restful API设计:把每个url当做一个唯一的资源

如何设计成一个资源?

不用url参数

传统API设计:/api/list?pageIndex=2
Restful API设计:/api/list/2

method表示操作类型

传统API设计

post请求/api/create-blog
post请求/api/update-blog?id=100
get请求/api/get-blog?id=100

Restful API设计

post请求/api/blog
patch 请求 /api/blog/100
get 请求 /api/blog/100

http headers
常见的 Request Headers

Accept 浏览器可接收的数据格式
Accept-Encoding浏览器可接收的压缩算法,如gzip
Accept-Languange 浏览器可接收的语言,如 zh-CN
Connection:keep-alive一次TCP连接重复使用
cookie
Host
User-Agent(简称UA)浏览器信息
Content-type 发送数据的格式,如 application/json

常见的 Response Headers

Content-type 返回数据的格式,如 application/json
Content-length 返回数据的大小,多少字节
Content-Encoding 返回数据的压缩算法,如 gzip
Set-Cookie

缓存相关的headers

Cache-Control Expires
Last-Modified If-Modified-Since
If-None-Match
Etag

http缓存

什么是缓存?
为什么需要缓存?
哪些资源可以被缓存?一静态资源(js css img)

强缓存与协商缓存

强缓存

​ 如果没有过期,浏览器可以直接使用该缓存而不需要重新向服务器请求

​ Expires 存在于响应头,为缓存过期时间点

​ Cache-Control 存在于响应头和请求头,为缓存过期时常

Cache-Control

Response Headers 中
控制强制缓存的逻辑
例如 Cache-Control:max-age=31536000(单位是秒)

max-age 过期时长
no-cache 不用强制缓存
no-store 不用任何缓存

private 只允许最终用户缓存
public 允许路由/代理缓存

Expires

同在 Response Headers 中
同为控制缓存过期
已被 Cache-Control 代替

协商缓存

​ 不能直接使用该缓存,始终向服务器询问是否需要更新

服务器端缓存策略
服务器判断客户端资源,是否和服务端资源一样
一致则返回304,否则返回200和最新的资源

在Response Headers中,有两种

Last-Modified 资源的最后修改时间
Etag 资源的唯一标识(一个字符串,类似人类的指纹)

版本

HTTP/0.9

  • 只有一个命令GET
  • 响应类型: 仅超文本
  • 没有header等描述数据的信息
  • 服务器发送完毕,就关闭TCP连接

HTTP/1.0

  • 增加了很多命令(post HESD )
  • 增加status codeheader
  • 多字符集支持、多部分发送、权限、缓存等
  • 响应:不再只限于超文本 (Content-Type 头部提供了传输 HTML 之外文件的能力 — 如脚本、样式或媒体文件)

HTTP/1.1

  • 持久连接。TCP三次握手会在任何连接被建立之前发生一次。最终,当发送了所有数据之后,服务器发送一个消息,表示不会再有更多数据向客户端发送了;则客户端才会关闭连接(断开 TCP)
  • 支持的方法: GET , HEAD , POST , PUT ,DELETE , TRACE , OPTIONS
  • 进行了重大的性能优化和特性增强,分块传输、压缩/解压、内容缓存磋商、虚拟主机(有单个IP地址的主机具有多个域名)、更快的响应,以及通过增加缓存节省了更多的带宽

HTTP2

  • 所有数据以二进制传输。HTTP1.x是基于文本的,无法保证健壮性,HTTP2.0绝对使用新的二进制格式,方便且健壮
  • 同一个连接里面发送多个请求不再需要按照顺序来
  • 头信息压缩以及推送等提高效率的功能

HTTP3

  • QUIC“快速UDP互联网连接”(Quick UDP Internet Connections)

HTTP3 的主要改进在传输层上。传输层不会再有我前面提到的那些繁重的 TCP 连接了。现在,一切都会走 UDP。

HSTS

HTTP Strict Transport Security,简单说就是强制客户端使用 HTTPS 访问页面

原理:

  • 在服务器响应头中添加 Strict-Transport-Security,可以设置 max-age
  • 用户访问时,服务器种下这个头
  • 下次如果使用 http 访问,只要 max-age 未过期,客户端会进行内部跳转,可以看到 307 Redirect Internel 的响应码
  • 变成 https 访问源服务器

HTTP协议特点

  1. 支持客户/服务器模式。
  2. 简单快速客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有 GET、HEAD、POST。每种方法规定了客户与服务器联系的类型不同。由于 HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。
  3. 灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type(Content-Type是HTTP包中用来表示内容类型的标识)加以标记。
  4. 无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
  5. 无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。

TCP

TCP(Transmission Control Protocol)传输控制协议

TCP/IP协议将应用层、表示层、会话层合并为应用层,物理层和数据链路层合并为网络接口层

连接三次握手

第一次握手:主机A发送位码为SYN=1,随机产生Seq number=1234567的数据包到服务器,主机B由SYN=1知道,A要求建立联机;(第一次握手,由浏览器发起,告诉服务器我要发送请求了)

第二次握手:主机B收到请求后要确认联机信息,向A发送ack number=(主机A的seq+1)SUN=1,ACK=1234567 + 1,随机产生Seq=7654321的包;(第二次握手,由服务器发起,告诉浏览器我准备接受了,你赶紧发送吧)

第三次握手:主机A收到后检查ack number是否正确,即第一次发送的seq number+1,以及位码SYN是否为1,若正确,主机A会再发送ack number=(主机B的seq+1)ack=7654321 + 1,主机B收到后确认Seq值与ACK=7654321+ 1则连接建立成功;(第三次握手,由浏览器发送,告诉服务器,我马上就发了,准备接受吧)

断开四次分手

刚开始双方都处于established状态,假如是客户端先发起关闭请求

第一次挥手:客户端发送一个FIN报文,报文中会指定一个序列号。此时客户端处于FIN_WAIT1状态

第二次挥手:服务端收到FIN之后,会发送ACK报文,且把客户端的序列号值+1作为ACK报文的序列号值,表明已经收到客户端的报文了,此时服务端处于CLOSE_WAIT状态

第三次挥手:如果服务端也想断开连接了,和客户端的第一次挥手一样,发送FIN报文,且指定一个序列号。此时服务端处于LAST_ACK的状态

需要过一阵子以确保服务端收到自己的ACK报文之后才会进入CLOSED状态,服务端收到ACK报文之后,就处于关闭连接了,处于CLOSED状态。

开发环境

git

常用git命令

git add . 添加
git checkout xxx 还原
git commit - m “ xxx “ 提交
git push origin master 推送
git pull origin master 拉去
git branch 分支
git checkout -b xxx 新建分支 / git checkout XXX 切换分支
git merge xxx 合并分支
git stash 暂存 git stash pop 恢复暂存

抓包

移动端h5页,查看网络请求,需要用工具抓包
windows 一般用 fiddler
Mac OS 一般用 charles

webpack babel

ES6模块化,浏览器暂不支持
ES6语法,浏览器并不完全支持
压缩代码,整合代码,以让网页加载更快

运行环境

运行环境即浏览器(server端有nodejs)
下载网页代码,渲染出页面,期间会执行若干JS
要保证代码在浏览器中:稳定且高效

网页加载过程

加载资源的形式

html代码
媒体文件,如图片、视频等
javascript css

加载过程

DNS解析:域名->IP地址
浏览器根据IP地址向服务器发起http请求
服务器处理http请求,并返回给浏览器

渲染过程

根据HTML代码生成DOM Tree
根据CSS代码生成CSSOM
将DOM Tree和CSSOM整合行程Render Tree
根据Render Tree 渲染页面
遇到<script>则暂停渲染,优先加载并执行JS代码,完成再继续
直至把 Render Tree 渲染完成

页面渲染

HTML生成DOM树,CSS生成对象模型(CSSDOM)

DOM树与CSSDOM合并生成渲染树

浏览器根据渲染树计算后生成布局树Layout Tree

最后将画面绘制到屏幕上

window.onload 和 DOMContentLoaded 区别

window.onload资源全部加载完才能执行,包括图片
DOMContentLoaded DOM 渲染完成即可,图片可能尚未下载

性能优化

是一个综合性问题,没有标准答案,但要求尽量全面
某些细节问题可能会单独提问:手写防抖、节流

性能优化原则

多使用内存、缓存或其他方法
减少CPU计算量,减少网络加载耗时
(适用于所有编程的性能优化——空间换时间)

让加载更快
减少资源体积:压缩代码
减少访问次数:合并代码,SSR服务器端渲染,缓存
使用更快的网络:CDN

缓存
静态资源加 hash 后缀,根据文件内容计算 hash
文件内容不变,则hash不变,则url不变
url和文件不变,则会自动触发http缓存机制,返回304

SSR
服务器端渲染:将网页和数据一起加载,一起渲染
非SSR(前后端分离):先加载网页,再加载数据,再渲染数据
早先的JSP ASP PHP,现在的vue React SSR

让渲染更快
CSS 放在 head ,JS 放在 body 最下面
尽早开始执行JS,用DOMContentLoaded触发
懒加载(图片懒加载,上滑加载更多)
对DOM查询进行缓存
频繁DOM操作,合并到一起插入DOM结构
节流 throttle 防抖 debounce

1
const p = document.getElementsByTagName('p');

防抖 debounce

监听一个输入框的,文字变化后触发change事件
直接用keyup事件,则会频发触发change事件
防抖:用户输入结束或暂停时,才会触发change事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const debounce = (fn: Function, delay: number = 500): EventListener => {
let timer: NodeJS.Timeout;
return function () {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
//@ts-ignore
fn.apply(this, arguments);
}, delay)
}
}

p[0].addEventListener('click', debounce(function () {
console.log(p[0]);
}, 600))

节流 throttle

拖拽一个元素时,要随时拿到该元素被拖拽的位置
直接用drag事件,则会频发触发,很容易导致卡顿
节流:无论拖拽速度多快,都会每隔100ms触发一次

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const throttle = (fn: Function, delay: number = 100): EventListener => {
let timer: NodeJS.Timeout | null = null;
return function () {
if (timer) return;
timer = setTimeout(() => {
//@ts-ignore
fn.apply(this, arguments);
timer = null;
}, delay)
}
}


p[0].addEventListener('drag', throttle(
function (e: any) {
console.log(e.offsetX, e.offsetY)
}, 500)
);

浏览器

URL

协议://主(ex.com)机:端口/路(a/b)径 ?查询(k=v&k=v)参数#锚点

DNS

从域名获取IP地址

根域名服务器,顶级域名服务器,主域名服务器

递归查询

​ 客户端只发一次请求,要求对方给出最终结果。

​ 浏览器缓存,系统缓存,路由器缓存,ISP(运营商)DNS缓存:统称为DNS高速缓存

​ 若当前DNS服务器没有,则服务器会继续向其他DNS服务器查询

迭代查询

​ 客户端发出一次请求,对方如果没有授权回答,它就会返回一个能解答这个查询的其它名称服务器列表,客户端会再向返回的列表中发出请求,直到找到最终负责所查域名的名称服务器,从它得到最终结果。

​ 当根域名服务器收到本地域名服务器发出的迭代查询请求报文时,要么给出所要查询的IP地址,要么告诉本地服务器:“你下一步应当向哪一个域名服务器进行查询”。

安全

XSS跨站请求攻击

一个博客网站,我发表一篇博客,其中嵌入<script>脚本
脚本内容:获取cookie,发送到我的服务器(服务器配合跨域)
发布这篇博客,有人查看它,窃取访问者的cookie

XSS 预防(xss 工具)

替换特殊字符,如<变为<>变为>
<script>变为 &lt;script&gt;,直接显示,而不会作为脚本执行
前端要替换,后端也要替换,都做总不会有错

XSRF跨站请求伪造

你正在购物,看中了某个商品,商品id是100
付费接口是xxx.com/pay?id=100,但没有任何验证
我是攻击者,我看中了一个商品,id是200
我向你发送一封电子邮件,邮件标题很吸引人
但邮件正文隐藏着<img src=xxx.com/pay?id=200/>
你一查看邮件,就帮我购买了id是200的商品

XSRF 预防

使用post接口
增加验证,例如密码、短信验证码、指纹等