前端面试知识点
2020-09-07 18:40:32 7 举报
AI智能生成
个人的前端面试体系图
作者其他创作
大纲/内容
原型
一个原型的实际应用
zepto
jquery
总结
描述jQuery如何使用原型
描述一下zepto如何使用原型
结合自己的项目经验
原型如何体现它的扩展性
插件机制
为什么jquery或者zepto将原型方法放在jquery.fn后者$.fn上
为了扩展插件
实际上就是扩展原型
好处1:只有$会暴露在window全局变量
好处2:将插件扩展统一到$.fn.xxx这一个接口,方便使用
实际上就是在构造函数上添加属性
virtual dom
什么是vdom?为什么会存在vdom?
用JS模拟DOM结构
DOM变化的对比,放在JS层来做,因为JS是图灵完备语言
提高重绘性能
传统的方式遇到的问题
DOM操作是“昂贵”的,JS的效率更高
期望尽量减少DOM操作,而不是“推到重来”
项目越复杂,影响越严重
vdom如何应用,核心API是什么
snabbdom库,开源,vue2采用了该方式
snabbdom.js
snabbdom-class.js
snabbdom-props.js
snabbdom-style.js
snabbdom-eventlisteners.js
snabbdom-h.js
核心API
h函数
patch函数
diff算法
diff对比工具linux时代起就存在
diff算法非常复杂,实现难度很大,源码量很大,需要去繁就简,理解核心流程
vdom为什么使用diff算法
DOM操作是“昂贵”的
找出本次DOM必须更新的节点来更新,其他不更新
“找出”的过程,就需要diff算法
实现过程,关注2个核心用法
patch(container, vnode)
patch(vnode, newVnode)
节点新增和删除
节点重新排序
节点属性、样式、事件绑定
如何极致压榨性能
。。。
MVVM
jQuery和框架的区别
数据和视图的分离
jquery的代码和数据混合在一起
分离的重点:解耦,开放封闭原则
对扩展开放
对修改封闭
以数据驱动视图
jquery是操作DOM完成的,不是数据驱动视图
MVVM的理解
MVC
model
数据源
view
界面
controller
控制器、逻辑处理
用户 - view - controller - model - view
MVVM
model
实质是JS对象
viewModel
view通过事件绑定操作model
model通过数据绑定操作view
view
最终指向DOM
hybrid
概念
混合开发
可能涉及到server端
价值
可以快速迭代更新,跨过app审核
体验流畅
减少开发和沟通成本,双端一套代码
webview
是app中的一个组件
用于加载h5页面,一个小型的浏览器内核
file协议
本地的
快
file://和整个url的关系
http(s)协议
网络
慢
使用场景
使用Native:体验要就极致,变化不频繁(首页)
使用Hybrid:体验要求高,变化频繁(详情页)
使用H5:体验无要求,不常用(举报、反馈页)
具体实现
前端做好静态页面,将文件交给客户端
客户端拿到前端静态页面,以文件的形式存储在app中
客户端在一个webview中使用file协议加载静态页面
更新流程
替换每个客户端的静态文件
只能客户端来做
客户端去server下载最近的静态文件
前端负责server内的静态文件处理
完整过程
前端在server内上传静态文件的zip包
并且通过对zip包名的修改来控制版本
native打开后对比本地zip包名和线上的包名是否一致
下载完解压并覆盖已有文件
hybird和h5的区别
优点
体验好
迭代快
子主题
缺点
开发成本高。联调测试比较麻烦
运维成本高
使用场景
hybird:产品稳定功能,体验要求高,迭代频繁
h5:单次的运营活动或者不常用功能
前端和客户端通讯
新闻详情页使用hybird如何获取新闻内容
不能使用ajax,第一跨域,第二速度慢
客户端获取新闻内容,然后JS通讯拿到内容,在渲染
基本形式
JS访问客户端传递参数和回调函数
客户端通过回调函数返回内容
schema 协议
前端和客户端通讯的约定
weixin:// 这是自行约定的
举例
子主题
封装
子主题
基本形式:调用能力,传递参数,监听回调
内置上线
将以上封装的代码打包,叫做invoke.js,内置到客户端
客户端每次启动webview,都默认执行invoke.js
本地加载,免去网络加载的时间,更快
本地加载,没有网络请求,黑客看不到schema协议,更安全
前端面试点总结一
1、简述让一个元素在窗口中消失以及垂直水平居中的方法,还有Flex布局的理解
元素消失
css
display:none
visibility:hidden
vue
v-if
v-show
回流和重绘
回流必将引起重绘,重绘不一定引起回流
回流
部分或全部元素的尺寸、结构或某些属性发生改变时,
浏览器重新渲染部分或全部文档的过程称为回流
浏览器重新渲染部分或全部文档的过程称为回流
举例
页面首次渲染
浏览器窗口大小发生改变
元素尺寸或位置发生改变
元素内容变化(文字数量或图片大小等等)
元素字体大小变化
添加或者删除可见的DOM元素
激活CSS伪类(例如::hover)
常用且会导致回流的属性和方法
clientWidth、clientHeight、clientTop、clientLeft
offsetWidth、offsetHeight、offsetTop、offsetLeft
scrollWidth、scrollHeight、scrollTop、scrollLeft
scrollIntoView()、scrollIntoViewIfNeeded()
getComputedStyle()
getBoundingClientRect()
scrollTo()
重绘
当页面中元素样式的改变并不影响它在文档流中的位置时
(例如:color、background-color、visibility等),
浏览器会将新样式赋予给元素并重新绘制它,这个过程称为重绘。
(例如:color、background-color、visibility等),
浏览器会将新样式赋予给元素并重新绘制它,这个过程称为重绘。
回流比重绘的代价更高
元素居中的方案
水平居中
1、行内元素
text-align:center
2、块级元素
margin:0 auto
3、子元素包含 float:left 属性
父元素width设置为fit-content,
并且配合margin:0 auto
并且配合margin:0 auto
4、flex 2012
justify-content: center;
5、flex 2009
父元素display: box;
box-pack: center;
box-pack: center;
6、transform
.son{
position:absolute;
left:50%;
transform:translate(-50%,0);
}
position:absolute;
left:50%;
transform:translate(-50%,0);
}
7、绝对定位方式
.son{
position:absolute;
width:固定;
left:50%;
margin-left:-0.5 * 宽度;
}
position:absolute;
width:固定;
left:50%;
margin-left:-0.5 * 宽度;
}
8、绝对定位方式
.son{
position:absolute;
width:固定;
left:0;
right:0;
margin:0 auto;
}
position:absolute;
width:固定;
left:0;
right:0;
margin:0 auto;
}
垂直居中
1、单行文本
line-height 等于父元素高度
2、行内块级元素
.parent::after, .son{
display:inline-block;
vertical-align:middle;
}
.parent::after{
content:'';
height:100%;
}
display:inline-block;
vertical-align:middle;
}
.parent::after{
content:'';
height:100%;
}
很流行的方法,兼容IE7
3、元素高度不固定
vertical-align
vertical-align只有在父层为 td 或者 th 时,才会生效,
对于其他块级元素, 例如 div、p 等, 默认情况是不支持的.
为了使用vertical-align, 我们需要设置父元素display:table,
子元素 display:table-cell;vertical-align:middle;
对于其他块级元素, 例如 div、p 等, 默认情况是不支持的.
为了使用vertical-align, 我们需要设置父元素display:table,
子元素 display:table-cell;vertical-align:middle;
优点
元素高度可以动态改变, 不需再CSS中定义,
如果父元素没有足够空间时, 该元素内容也不会被截断
如果父元素没有足够空间时, 该元素内容也不会被截断
缺点
IE6~7, 甚至IE8 beta中无效.
4、flex 2012
.parent {
display: flex;
align-items: center;
}
display: flex;
align-items: center;
}
优点
内容块的宽高任意, 优雅的溢出.
可用于更复杂高级的布局技术中.
可用于更复杂高级的布局技术中.
缺点
IE8/IE9不支持
需要浏览器厂商前缀
渲染上可能会有一些问题
需要浏览器厂商前缀
渲染上可能会有一些问题
5、flex 2009
.parent {
display: box;
box-orient: vertical;
box-pack: center;
}
display: box;
box-orient: vertical;
box-pack: center;
}
优点
简单、扩展性强
缺点
兼容性差,不支持IE
6、transform
.son{
position:absolute;
top:50%;
transform: translate(0,-50%);
}
position:absolute;
top:50%;
transform: translate(0,-50%);
}
缺点
IE8不支持, 属性需要追加浏览器厂商前缀,
可能干扰其他 transform 效果,
某些情形下会出现文本或元素边界渲染模糊的现象.
可能干扰其他 transform 效果,
某些情形下会出现文本或元素边界渲染模糊的现象.
7、绝对定位
.son{
position:absolute;
top:50%;
height:固定;
margin-top:-0.5高度;
}
position:absolute;
top:50%;
height:固定;
margin-top:-0.5高度;
}
8、绝对定位方式
.son{
position:absolute;
height:固定;
top:0;
bottom:0;
margin:auto 0;
}
position:absolute;
height:固定;
top:0;
bottom:0;
margin:auto 0;
}
缺点
没有足够空间时, 子元素会被截断, 但不会有滚动条.
Flex布局
Flex 是 Flexible Box 的缩写,意为"弹性布局",用来为盒状模型提供最大的灵活性。
采用 Flex 布局的元素,称为 Flex 容器(flex container),简称"容器"。
它的所有子元素自动成为容器成员,称为 Flex 项目(flex item),简称"项目"。
容器的属性
flex-direction
决定主轴的方向(即项目的排列方向)。
row(默认值):主轴为水平方向,起点在左端。
row-reverse:主轴为水平方向,起点在右端。
cloumn:主轴为垂直方向,起点在上沿
column-reverse:主轴为垂直方向,起点在下沿
flex-wrap
决定是否换行
nowrap(默认):不换行
warp:第一行在上
wrap-reverse:第一行在下
flex-flow
是flex-direction属性和flex-wrap属性的简写形式,默认值为row nowrap
justify-content
项目在主轴上的对齐方式。
flex-start
flex-end
center
space-between
两端对齐,项目之间的间隔都相等
space-around
每个项目两侧的间隔相等。所以,项目之间的间隔比项目与边框的间隔大一倍
align-items
定义项目在交叉轴上如何对齐
flex-start
flex-end
center
baseline
项目的第一行文字的基线对齐
stretch
如果项目未设置高度或设为auto,将占满整个容器的高度
align-content
定义了多根轴线的对齐方式
flex-start:与交叉轴的起点对齐。
flex-end:与交叉轴的终点对齐。
center:与交叉轴的中点对齐。
space-between:与交叉轴两端对齐,轴线之间的间隔平均分布。
space-around:每根轴线两侧的间隔都相等。所以,轴线之间的间隔比轴线与边框的间隔大一倍。
stretch(默认值):轴线占满整个交叉轴。
flex-end:与交叉轴的终点对齐。
center:与交叉轴的中点对齐。
space-between:与交叉轴两端对齐,轴线之间的间隔平均分布。
space-around:每根轴线两侧的间隔都相等。所以,轴线之间的间隔比轴线与边框的间隔大一倍。
stretch(默认值):轴线占满整个交叉轴。
项目的属性
order
定义项目的排列顺序。数值越小,排列越靠前,默认为0
flex-grow
定义项目的放大比例,默认为0
所有项目的flex-grow属性都为1,则它们将等分剩余空间
如果一个项目的flex-grow属性为2,其他项目都为1,则前者占据的剩余空间将比其他项多一倍
flex-shrink
定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小。
所有项目的flex-shrink属性都为1,当空间不足时,都将等比例缩小。
如果一个项目的flex-shrink属性为0,其他项目都为1,则空间不足时,前者不缩小
如果一个项目的flex-shrink属性为0,其他项目都为1,则空间不足时,前者不缩小
flex-basis
在分配多余空间之前,项目占据的主轴空间(main size)
可以设为跟width或height属性一样的值(比如350px),则项目将占据固定空间
flex
是flex-grow, flex-shrink 和 flex-basis的简写,默认值为0 1 auto
该属性有两个快捷值:auto (1 1 auto) 和 none (0 0 auto)
建议优先使用这个属性,而不是单独写三个分离的属性,因为浏览器会推算相关值
flex:1
flex:1 1 auto
align-self
允许单个项目有与其他项目不一样的对齐方式,可覆盖align-items属性
默认值为auto,表示继承父元素的align-items属性,如果没有父元素,则等同于stretch
2、对This了解吗,有自己实现过call,apply,bind吗
this永远指向最后调用它的那个对象
this所在的函数被普通调用时指向 window,严格模式情况下,指向undefined
this所在函数被以obj.fn()形式调用时,指向obj
使用call和apply之后this指向第一个参数
使用bind之后this指向第一个参数
3、如何减少重绘和回流
css
避免使用table布局
尽量在DOM树的末端改变class
避免设置多层内联样式
将动画效果应用到position属性为absolute或者fixed的元素上
避免使用css表达式(calc())
js
避免频繁操作样式,最好一次性重写style属性,
或者将样式列表定义为class一次性更改class属性
或者将样式列表定义为class一次性更改class属性
避免频繁操作DOM,创建一个documentFragment,
在它上面应用所有DOM操作,最后把它添加到文档中
在它上面应用所有DOM操作,最后把它添加到文档中
可先给元素设置display:none,操作结束后在显示出来。
属性为none时进行dom操作不会引发回流和重绘
属性为none时进行dom操作不会引发回流和重绘
避免频繁读取会引发回流和重绘的属性
对复杂动画的元素使用绝对定位,使他脱离文本流
4、前端的异步编程有哪些
传统的
setTimeout()
setInterval()
缺点
当同步的代码比较多的时候,不确定异步定时器的任务时候能在指定的时间执行
es6
promise
generator
async/await
requestAnimationFrame和requestIdleCallback
解决传统js动画使用setTimeout 或者 setInterval存在两个问题
动画的循时间环间隔不好确定,设置长了动画显得不够平滑流畅,
设置短了浏览器的重绘频率会达到瓶颈,推荐的最佳循环间隔是17ms
设置短了浏览器的重绘频率会达到瓶颈,推荐的最佳循环间隔是17ms
定时器第二个时间参数只是指定了多久后将动画任务添加到浏览器的UI线程队列中,
如果UI线程处于忙碌状态,那么动画不会立刻执行
如果UI线程处于忙碌状态,那么动画不会立刻执行
优势
requestAnimationFrame的回调会在每一帧确定执行,属于高优先级任务,
而requestIdleCallback的回调则不一定,属于低优先级任务。
而requestIdleCallback的回调则不一定,属于低优先级任务。
fps跟接近60
5、EventLoop
浏览器中
同步任务 - 异步任务
异步任务
宏任务
setTimeout
setInterval
……
微任务
Promise
……
Node.js中
6、闭包与V8垃圾回收机制
7、熟悉哪些通信协议
TCP/IP
TCP/IP模型
TCP/IP 是互联网相关的各类协议族的总称
TCP,UDP,IP,FTP,HTTP,ICMP,SMTP 等都属于 TCP/IP 族内的协议
是互联网的基础,它是一系列网络协议的总称。
这些协议可以划分为四层,分别为链路层、网络层、传输层和应用层。
这些协议可以划分为四层,分别为链路层、网络层、传输层和应用层。
链路层
负责封装和解封装IP报文,发送和接受ARP/RARP报文等
网络层
负责路由以及把分组报文发送给目标网络或主机
传输层
负责对报文进行分组和重组,并以TCP或UDP协议格式封装报文
应用层
负责向用户提供应用程序,比如HTTP、FTP、Telnet、DNS、SMTP等
UDP
UDP协议全程是用户数据报协议
与TCP协议一样用于处理数据包,是一种无协议的连接
特点
1. 面向无连接
不需要和 TCP一样在发送数据前进行三次握手建立连接的,想发数据就可以开始发送
只是数据报文的搬运工,不会对数据报文进行任何拆分和拼接操作
在发送端,应用层将数据传递给传输层的 UDP 协议,
UDP 只会给数据增加一个 UDP 头,标识下是 UDP 协议,然后就传递给网络层了
UDP 只会给数据增加一个 UDP 头,标识下是 UDP 协议,然后就传递给网络层了
在接收端,网络层将数据传递给传输层,UDP 只去除 IP 报文头就传递给应用层,不会任何拼接操作
2. 有单播,多播,广播的功能
不止支持一对一的传输方式,同样支持一对多,多对多,多对一的方式,
3. UDP是面向报文的
发送方的UDP对应用程序交下来的报文,在添加首部后就向下交付IP层。
UDP对应用层交下来的报文,既不合并,也不拆分,而是保留这些报文的边界。
因此,应用程序必须选择合适大小的报文
UDP对应用层交下来的报文,既不合并,也不拆分,而是保留这些报文的边界。
因此,应用程序必须选择合适大小的报文
4. 不可靠性
体现在无连接上,通信都不需要建立连接,想发就发,这样的情况肯定不可靠
并且收到什么数据就传递什么数据,并且也不会备份数据,发送数据也不会关心对方是否已经正确接收到数据了
网络环境时好时坏,但是 UDP 因为没有拥塞控制,一直会以恒定的速度发送数据。
即使网络条件不好,也不会对发送速率进行调整。这样实现的弊端就是在网络条件不好的情况下可能会导致丢包,
但是优点也很明显,在某些实时性要求高的场景(比如电话会议)就需要使用 UDP 而不是 TCP
即使网络条件不好,也不会对发送速率进行调整。这样实现的弊端就是在网络条件不好的情况下可能会导致丢包,
但是优点也很明显,在某些实时性要求高的场景(比如电话会议)就需要使用 UDP 而不是 TCP
5. 头部开销小,只有八字节,传输数据报文时是很高效的。
UDP 头部包含了以下几个数据
两个十六位的端口号,分别为源端口(可选字段)和目标端口
整个数据报文的长度
整个数据报文的检验和(IPv4 可选 字段),该字段用于发现头部信息和数据中的错误
TCP
全称是传输控制协议
是一种面向连接的、可靠的、基于字节流的传输层通信协议
TCP 是面向连接的、可靠的流协议
TCP连接过程
第一次握手
客户端向服务端发送连接请求报文段。该报文段中包含自身的数据通讯初始序号。
请求发送后,客户端便进入 SYN-SENT 状态
请求发送后,客户端便进入 SYN-SENT 状态
第二次握手
服务端收到连接请求报文段后,如果同意连接,则会发送一个应答,
该应答中也会包含自身的数据通讯初始序号,发送完成后便进入 SYN-RECEIVED 状态
该应答中也会包含自身的数据通讯初始序号,发送完成后便进入 SYN-RECEIVED 状态
第三次握手
当客户端收到连接同意的应答后,还要向服务端发送一个确认报文。
客户端发完这个报文段后便进入 ESTABLISHED 状态,服务端收到这个应答后也进入 ESTABLISHED 状态,
此时连接建立成功
客户端发完这个报文段后便进入 ESTABLISHED 状态,服务端收到这个应答后也进入 ESTABLISHED 状态,
此时连接建立成功
原因
是为了防止出现失效的连接请求报文段被服务端接收的情况,从而产生错误
TCP断开过程
第一次挥手
客户端 A 认为数据发送完成,则它需要向服务端 B 发送连接释放请求
第二次挥手
B 收到连接释放请求后,会告诉应用层要释放 TCP 链接。
然后会发送 ACK 包,并进入 CLOSE_WAIT 状态,
此时表明 A 到 B 的连接已经释放,不再接收 A 发的数据了。
但是因为 TCP 连接是双向的,所以 B 仍旧可以发送数据给 A
然后会发送 ACK 包,并进入 CLOSE_WAIT 状态,
此时表明 A 到 B 的连接已经释放,不再接收 A 发的数据了。
但是因为 TCP 连接是双向的,所以 B 仍旧可以发送数据给 A
第三次挥手
B 如果此时还有没发完的数据会继续发送,完毕后会向 A 发送连接释放请求,然后 B 便进入 LAST-ACK 状态
第四次挥手
A 收到释放请求后,向 B 发送确认应答,此时 A 进入 TIME-WAIT 状态。
该状态会持续 2MSL(最大段生存期,指报文段在网络中生存的时间,超时会被抛弃) 时间,
若该时间段内没有 B 的重发请求的话,就进入 CLOSED 状态。当 B 收到确认应答后,也便进入 CLOSED 状态
该状态会持续 2MSL(最大段生存期,指报文段在网络中生存的时间,超时会被抛弃) 时间,
若该时间段内没有 B 的重发请求的话,就进入 CLOSED 状态。当 B 收到确认应答后,也便进入 CLOSED 状态
TCP 是全双工的,在断开连接时两端都需要发送 FIN 和 ACK
特点
面向连接
指发送数据之前必须在两端建立连接
仅支持单播传输
只能进行点对点的数据传输,不支持多播和广播传输方式
面向字节流
在不保留报文边界的情况下以字节流方式进行传输
可靠传输
对于可靠传输,判断丢包,误码靠的是TCP的段编号以及确认号
提供拥塞控制
网络出现拥塞的时候,TCP能够减小向网络注入数据的速率和数量,缓解拥塞
TCP提供全双工通信
允许通信双方的应用程序在任何时候都能发送数据,因为TCP连接的两端都设有缓存,用来临时存放双向通信的数据
HTTP 1.0
无状态、无连接的应用层协议
HTTP 1.1
HTTP 2.0
HTTPS
8、从输入url地址栏,发生了什么?由此来介绍如何性能优化
浏览器向DNS服务器请求解析该URL中的域名所对应的IP地址
DNS解析:将域名解析为ip地址 ,由上往下匹配,只要命中便停止
浏览器DNS缓存
本机HostsDNS缓存
路由器DNS缓存
网络运营商服务器DNS缓存(80%的DNS解析在这里完成)
根域名服务器
顶级域名服务器
主域名服务器
保存结果至本地
三次握手建立TCP连接
浏览器发出请求文件的HTTP请求,该请求报文作为TCP第三次握手的数据发给服务器
服务器对请求作出相应,并把对应的HTML文本发送给浏览器
浏览器解析数据并绘制渲染页面
预解析(将需要发送请求的标签的请求发出去)
从上到下解析html文件
遇到HTML标签,调用html解析器将其解析DOM树
遇到css标记,调用css解析器将其解析CSSOM树
link 阻塞 - 为了解决闪屏,所有解决闪屏的样式
style 非阻塞,与闪屏的样式不相关的
将DOM树和CSSOM树结合在一起,形成render树
layout布局 render渲染
遇到script标签,阻塞,调用js解析器解析js代码,可能会修改DOM树,也可能会修改CSSOM树
四次挥手释放TCP连接
9、同源策略是什么,跨域解决办法,cookie可以跨域吗
为什么有跨域
出于浏览器的同源策略限制,浏览器会拒绝跨域请求
实际上拒绝的是跨域的读操作
浏览器同源政策
通常浏览器允许跨域资源嵌入(Cross-origin embedding),如 img、script 标签
通常浏览器允许进行跨域写操作(Cross-origin writes),如链接,重定向
通常浏览器不允许跨域读操作(Cross-origin reads)
什么情况才算作跨域
非同源请求,均为跨域。名词解释:同源 —— 如果两个页面拥有相同的协议(protocol),端口(port)和主机(host),那么这两个页面就属于同一个源(origin)
为什么有跨域需求
工程服务化后,不同职责的服务分散在不同的工程中,往往这些工程的域名是不同的,但一个需求可能需要对应到多个服务,这时便需要调用不同服务的接口
跨域的解决方法
JSONP
CORS
postmessage
websocket
nginx反代理
iframe + document.domain
iframe + location.hash
iframe + window.name
nodejs中间件
漏洞与防范
XSS
跨站脚本攻击
Cross Site Scripting<br>
Cross Site Scripting<br>
通过存在安全漏洞的Web网站注册用户的浏览器内运行非法的非本站点HTML标签或<br>JavaScript进行的一种攻击
可能造成的影响
利用虚假输入表单骗取用户个人信息
利用脚本窃取用户的Cookie值
显示伪造的文章或图片
XSS攻击分类
反射型
url参数直接注入
存储型
存储到DB后读取时注入
危害
Scripting能干啥就能干啥
获取页面数据
获取Cookies
劫持前端逻辑
发送请求
偷取网站的任意数据
偷取用户的资料
偷取用户的秘密和登录态
欺骗用户
防范手段
ejs转义小知识
在HEAD内设置请求头
ctx.set('X-XSS-Protection', 0) // 禁止XSS过滤
如果检测到跨站脚本攻击,浏览器将清除页面(删除
不安全的部分)。
不安全的部分)。
CSP
内容安全策略 (CSP, Content Security Policy)
本质上就是建立白名单
转义字符
黑名单
HttpOnly Cookie
是预防XSS攻击窃取用户cookie最有效的防御手段
response.addHeader("Set-Cookie", "uid=112; Path=/; HttpOnly")
防范手段
ejs转义小知识
在HEAD内设置请求头
ctx.set('X-XSS-Protection', 0) // 禁止XSS过滤
如果检测到跨站脚本攻击,浏览器将清除页面(删除
不安全的部分)。
不安全的部分)。
CSP
内容安全策略 (CSP, Content Security Policy)
本质上就是建立白名单
转义字符
黑名单
HttpOnly Cookie
是预防XSS攻击窃取用户cookie最有效的防御手段
response.addHeader("Set-Cookie", "uid=112; Path=/; HttpOnly")
OS命令注入
OS命令注入和SQL注入差不多,只不过SQL注入是针对数据库的,而OS命令注入是针对操作系统的
攻击方式
通过Web应用,执行非法的操作系统命令达到攻击的目的
只要在能调用Shell函数的地方就有存在 被攻击的风险。倘若调用Shell时存在疏漏,就可以执行插入的非法命令
DDOS
distributed denial of service,是一大类攻击的总称
攻击方式
SYN Flood
击通过向目标发送具有欺骗性源IP地址的大量TCP“初始连接请求”SYN数据包来利用TCP握手
HTTP Flood
此攻击类似于同时在多个不同计算机上反复按Web浏览器中的刷新 - 大量HTTP请求泛滥服务器
防御
备份网站
备份网站不一定是全功能的,如果能做到全静态浏览,就能满足需求。最低限度应该可以显示公告,告诉 用户,网站出了问题,正在全力抢修
HTTP 请求的拦截
靠谱的运营商
多个 Docker
带宽扩容 + CDN
提高犯罪成本
CSRF
跨站请求伪造
Cross Site Request Forgery
Cross Site Request Forgery
它利用用户已登录的身份,在用户毫不知情的情况下,
以用户的名义完成非法操作
以用户的名义完成非法操作
攻击方式
用户已经登录了站点 A,并在本地记录了 cookie
在用户没有登出站点 A 的情况下(也就是 cookie 生效的情况下),
访问了恶意攻击者提供的引诱危险站点 B (B 站点要求访问站点A)
访问了恶意攻击者提供的引诱危险站点 B (B 站点要求访问站点A)
站点 A 没有做任何 CSRF 防御
危害
利用用户登录态
用户不知情
完成业务请求
盗取用户资金(转账,消费)
冒充用户发帖背锅
损害网站声誉
防御
禁止第三方网站带Cookie - 有兼容性问题
Referer Check - Https不发送referer
验证码
SQL注入
攻击方式
填入特殊密码导致拼接后的SQL出现漏洞
危害
伪造用户登录
篡改网站重要信息
防御
查询语句建议使用数据库提供的参数化查询接口,
参数化的语句使用参数而不是将用户输入变量嵌入到 SQL 语句中,
即不要直接拼接 SQL 语句
参数化的语句使用参数而不是将用户输入变量嵌入到 SQL 语句中,
即不要直接拼接 SQL 语句
严格限制Web应用的数据库的操作权限
后端代码检查输入的数据是否符合预期
对进入数据库的特殊字符(',",\,<,>,&,*,; 等)进行转义处理,或编码转换
请求劫持
攻击方式
DNS劫持
DNS服务器(DNS解析各个步骤)被篡改,修改了域名解析的结果,使得访问到的不是预期的ip
HTTP劫持
运营商劫持
防御
升级HTTPS
Node全栈部署的思考
构建高可复用的node环境
使用内置cluster包开启多线程
故障恢复
监测到故障后停掉故障worker并开启一个新的worker
多核利用
多进程共享端口
使用fork也可以开启多线程但是不能共享端口
服务端部署
服务:使用PM2 维护
内建负载均衡
线程守护
0秒停机重载,维护升级的时候不需要停机
多平台支持
停止不稳定的进程
控制台检测
pm2 monit
提供 HTTP API
支持脚本启动
开启自动启动
前端:nginx反向代理 + 前端打包Dist
config.d 进行服务配置
sites-enable 设置不同静态路由
ES6语法
ES6模块化如何使用,开发环境如何打包
模块化的基本语法
export语法
export default
export
import语法
开发环境配置
babel编译ES6语法
npm init 建立 package.json
npm install --save-dev babel-core babel-preset-es2015 babel-preset-latest
创建 .babelrc
presets的配置
plugins的配置
npm install -g babel-cli
关于JS众多模块化标准
webpack
npm install webpack babel-loader --save-dev
配置 webpack.config.js
entry
output
path
filename
module
配置package.json中的scripts
增加一个"start":"webpack"
运行npm start
rollup
npm init
npm i rollup rollup-plugin-node-resolve rollup-plugin-babel babel-plugin-external-helpers babel-preset-latest --save-dev
配置.babelrc
配置rollup.config.js
增加一个"start":"webpack"
配置package.json中的scripts
Class和普通构造函数有何区别
JS构造函数
Class的基本语法
语法糖
class MathHandle {}
MathHandle === MathHandle.prototype.constructor
MathHandle === MathHandle.prototype.constructor
var m = new MathHandle()
m.__proto__ === MathHandle.prototype
m.__proto__ === MathHandle.prototype
继承
总结
Class在语法上更加贴合面向对象的写法
Class实现继承更加易读、易理解
更易于写JAVA等后端语言的使用
Class本质还是语法糖,使用了prototype
Promise的基本使用和原理
Callback Hell 回调地狱
promise语法
总结ES6其他常用功能
let/const
多行字符串/模板变量
解构赋值
块级作用域
函数默认参数
箭头函数
异步
什么是单线程,和异步有什么关系
只有一个线程,同时只能做一件事
原因:避免DOM渲染的冲突
浏览器需要渲染DOM
js可以修改DOM结构
JS执行的时候,浏览器DOM渲染会暂停
两端JS也不能同时执行(都修改DOM就冲突了)
webworker支持多线程,但是不能访问DOM
解决方案:异步
问题1:没有按照书写的方式执行,可读性差
问题2:callback中不容易模块化
什么是event-loop
事件循环,JS实现异步的具体解决方案
同步代码(非异步代码,不是ajax中,不是setTimeout等之中),直接执行
异步函数先放在异步队列中
待同步函数执行完毕,轮询执行 异步队列 的函数
是否用过jquery的deferred(jq的异步解决方案)
jq 1.5的变化
1.5之前的
var ajax= $.ajax({
url: 'data.json',
success: function () {},
error: function () {}
})
console.log(ajax) // 返回一个 XHR 对象
url: 'data.json',
success: function () {},
error: function () {}
})
console.log(ajax) // 返回一个 XHR 对象
1.5之后
var ajax = $.ajax('data.json')
ajax.done(function() {})
.fail(function() {})
.done(function () {}
)
console.log(ajax) // 返回一个 deferred 对象
ajax.done(function() {})
.fail(function() {})
.done(function () {}
)
console.log(ajax) // 返回一个 deferred 对象
// 很像 Promise 的写法
var ajax = $.ajax('data.json')
ajax.then(function() {// success1},
function(){// error1})
.then(function(){// success2},
function(){// error2})
var ajax = $.ajax('data.json')
ajax.then(function() {// success1},
function(){// error1})
.then(function(){// success2},
function(){// error2})
无法改变JS异步和单线程的本质
只能从写法上杜绝callback的这种形式
是一种语法糖形式,但是解耦了代码
很好的体现了开放封闭原则(对扩展开放对修改封闭)
表现出来就是ajax.then()这种写法,可以随时扩展,同时不用修改之前的代码
使用jq deferred
function waitHandle() {
var dtd = $.Deferred()
var wait = function (dtd) {
var task = function () {
console.log('执行完成)
dtd.resolve() // 异步任务已完成
// dtd.reject() // 异步任务失败或出错
}
setTimeout(task, 2000)
return dtd // 要求返回 deferred 对象
}
// 注意,这里一定要有返回值
return wait(dtd)
}
var dtd = $.Deferred()
var wait = function (dtd) {
var task = function () {
console.log('执行完成)
dtd.resolve() // 异步任务已完成
// dtd.reject() // 异步任务失败或出错
}
setTimeout(task, 2000)
return dtd // 要求返回 deferred 对象
}
// 注意,这里一定要有返回值
return wait(dtd)
}
var w = waitHandle()
w.then(function () {
console.log('ok1')
}, function () {
console.log('err1')
})
w.then(function () {
console.log('ok2)
}, function () {
console.log('err2')
})
// 还可以用w.done w.fail
w.then(function () {
console.log('ok1')
}, function () {
console.log('err1')
})
w.then(function () {
console.log('ok2)
}, function () {
console.log('err2')
})
// 还可以用w.done w.fail
dtd的api分成两类,用意不同
第一类:dtd.resolve dtd.reject
主动执行
第二类:dtd.then dtd.done dtd.fail
被动监听
两类应该分开使用,防止出现较严重的后果
引入promise概念,从deferred演化而来
使用dtd.promise()
function waitHandle() {
var dtd = $.Deferred()
var wait = function (dtd) {
var task = function () {
console.log('执行完成)
dtd.resolve() // 异步任务已完成
// dtd.reject() // 异步任务失败或出错
}
setTimeout(task, 2000)
return dtd.promise()
// 注意这里返回的是promise 而不是直接返回 deferred 对象
}
// 注意,这里一定要有返回值
return wait(dtd)
}
var dtd = $.Deferred()
var wait = function (dtd) {
var task = function () {
console.log('执行完成)
dtd.resolve() // 异步任务已完成
// dtd.reject() // 异步任务失败或出错
}
setTimeout(task, 2000)
return dtd.promise()
// 注意这里返回的是promise 而不是直接返回 deferred 对象
}
// 注意,这里一定要有返回值
return wait(dtd)
}
// 经过上面的改动,w 接收的就是一个 promise 对象
var w = waitHandle()
$.when(w)
.then(function () {
console.log('ok1')
})
.then(function () {
console.log('ok2)
})
// w.reject() // 执行这句话会直接报错
var w = waitHandle()
$.when(w)
.then(function () {
console.log('ok1')
})
.then(function () {
console.log('ok2)
})
// w.reject() // 执行这句话会直接报错
dtd.promise()只支持dtd.then dtd.done dtd.fail
promise的基本使用和原理
基本语法
旧浏览器兼容Promise可以使用bluebird.js
异常捕获
语法报错:不在then接收失败的函数,最后统一用 catch 捕获异常
reject报错:reject(xxx)后也可以用catch捕获
多个串联
promise.then里面return另一个promise,新的promise则会在链式的第二个.then中处理
Promise.all 和 Promise.race
Promise.all
接收 promise 对象的数组,待全部完成后统一执行success
.then接收的是一个数组datas,依次包含 promise 数组的返回内容
.then接收的是一个数组datas,依次包含 promise 数组的返回内容
Promise.race
接收 promise 对象数组,只要有一个完成就执行success
.then接收的是最先执行完成的 promise 的返回值
.then接收的是最先执行完成的 promise 的返回值
Promise标准
状态变化
初始:pending
=> 成功:fulfilled
=> 失败:rejected
变化不可逆
then
promise实例必须实现then方法
then必须接收两个函数作为参数
then的返回必须是一个Promise实例
介绍下asyn/awati,以及和Promise的区别和联系
then只是将callback拆分了,但还是异步写法
async/await是最直接的同步写法
使用了 Promise,并没有与其冲突
用法
使用await,函数必须使用 async 标识
await后面跟的是一个 Promise 实例
需要 babel-polyfill
当前异步解决方案
jQuery Deferred
Promise
Async/Await
Generator
vue题目选摘
v-if和v-for哪个优先级更高?如果两个同时出现,应该怎么优化
源码位置:compiler/codegen/index.js
v-for的优先级高于v-if
两者同级时渲染函数内是_l包裹isFolder
_l:列表渲染函数
idFolder:条件判断
这样每次渲染都会先执行循环在判断条件,浪费了性能
更高效的方式:在外层嵌套template,在这一层进行v-if条件判断,然后内部进行v-for循环
必要情况下配合computed整合判断条件提高效率
Vue组件data为什么必须是函数而Vue的根实例则没有此限制
src\core\instance\state.js
initData()
如果data是函数,则执行之并将其结果作为data选项的值
如果使用对象形式定义data,则会导致它们共用一个data对象,那么状态
变更将会影响所有组件实例
变更将会影响所有组件实例
采用函数形式定义,在initData时会将其作为工厂函数返
回全新data对象,有效规避多实例之间状态污染问题
回全新data对象,有效规避多实例之间状态污染问题
在Vue根实例创建过程中则不存在该限制,也
是因为根实例只能有一个,不需要担心这种情况
是因为根实例只能有一个,不需要担心这种情况
根实例为单例
你知道vue中key的作用和工作原理吗?说说你对它的理解
src\core\vdom\patch.js
updateChildren()
作用
为了高效的更新虚拟DOM,其原理是vue在patch过程中通过key可以精准判断两
个节点是否是同一个,从而避免频繁更新不同元素,使得整个patch过程更加高效,减少DOM操
作量,提高性能
个节点是否是同一个,从而避免频繁更新不同元素,使得整个patch过程更加高效,减少DOM操
作量,提高性能
vue中在使用相同标签名元素的过渡切换时,也会使用到key属性,其目的也是为了让vue可以区分
它们,否则vue只会替换其内部属性而不会触发过渡效果。
它们,否则vue只会替换其内部属性而不会触发过渡效果。
另外,若不设置key还可能在列表更新时引发一些莫名bug
你怎么理解vue中的diff算法?
1.diff算法是虚拟DOM技术的必然产物:通过新旧虚拟DOM作对比(即diff),将变化的地方更新在真
实DOM上;另外,也需要diff高效的执行对比过程,从而降低时间复杂度为O(n)。
实DOM上;另外,也需要diff高效的执行对比过程,从而降低时间复杂度为O(n)。
2.vue 2.x中为了降低Watcher粒度,每个组件只有一个Watcher与之对应,只有引入diff才能精确找到
发生变化的地方。
发生变化的地方。
3.vue中diff执行的时刻是组件实例执行其更新函数时,它会比对上一次渲染结果oldVnode和新的渲染
结果newVnode,此过程称为patch。
结果newVnode,此过程称为patch。
4.diff过程整体遵循深度优先、同层比较的策略;两个节点之间比较会根据它们是否拥有子节点或者文
本节点做不同操作;比较两组子节点是算法的重点,首先假设头尾节点可能相同做4次比对尝试,如果
没有找到相同节点才按照通用方式遍历查找,查找结束再按情况处理剩下的节点;借助key通常可以非
常精确找到相同节点,因此整个patch过程非常高效。
本节点做不同操作;比较两组子节点是算法的重点,首先假设头尾节点可能相同做4次比对尝试,如果
没有找到相同节点才按照通用方式遍历查找,查找结束再按情况处理剩下的节点;借助key通常可以非
常精确找到相同节点,因此整个patch过程非常高效。
谈一谈对vue组件化的理解?
1. 组件是独立和可复用的代码组织单元。组件系统是 Vue 核心特性之一,它使开发者使用小型、独
立和通常可复用的组件构建大型应用;
立和通常可复用的组件构建大型应用;
2. 组件化开发能大幅提高应用开发效率、测试性、复用性等;
3. 组件使用按分类有:页面组件、业务组件、通用组件;
4. vue的组件是基于配置的,我们通常编写的组件是组件配置而非组件,框架后续会生成其构造函
数,它们基于VueComponent,扩展于Vue;
数,它们基于VueComponent,扩展于Vue;
5. vue中常见组件化技术有:属性prop,自定义事件,插槽等,它们主要用于组件通信、扩展等;
6. 合理的划分组件,有助于提升应用性能;
7. 组件应该是高内聚、低耦合的;
8. 遵循单向数据流的原则。
谈一谈对vue设计原则的理解
渐进式JavaScript框架
Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层
还便于与第三方库或既有项目整合
易用
vue提供数据响应式、声明式模板语法和基于配置的组件系统等核心特性。这些使我们只需要关注应用
的核心业务即可,只要会写js、html和css就能轻松编写vue应用
的核心业务即可,只要会写js、html和css就能轻松编写vue应用
灵活
渐进式框架的最大优点就是灵活性,如果应用足够小,我们可能仅需要vue核心特性即可完成功能;随
着应用规模不断扩大,我们才可能逐渐引入路由、状态管理、vue-cli等库和工具,不管是应用体积还是
学习难度都是一个逐渐增加的平和曲线
着应用规模不断扩大,我们才可能逐渐引入路由、状态管理、vue-cli等库和工具,不管是应用体积还是
学习难度都是一个逐渐增加的平和曲线
高效
超快的虚拟 DOM 和 diff 算法使我们的应用拥有最佳的性能表现。
vue3中引入Proxy对数据响应式改进以及编译器中对于静态内容编译的改进
都会让vue更加高效。
都会让vue更加高效。
vue为什么要求组件模板只能有一个根元素
new Vue({el: '#app'})
如果body下有多个div时,Vue其实并不知道哪一个是入口
单文件组建中,template下的元素div,其实就是“树”状数据结构中的“根”,作为遍历起点
template标签
隐藏性
该标签不会显示在页面的任何地方,display:none
任意性
可以写在任何地方,包括header,body,script
无效性
该标签里的任何HTML内容都是无效的,不起任何作用,只能通过innerHTML来获取里面的内容
一个vue单文件就是一个vue实例,如果template下有多个div那么如何执行vue的实例入口呢,
为了让组件可以正常生成一个vue实例,这个div会自然的处理成程序入口,通过这个根节点,来递归遍历
整个Vue树下的所有节点,并处理为vdom,最后渲染成真正的HTML,插入到正确的位置
为了让组件可以正常生成一个vue实例,这个div会自然的处理成程序入口,通过这个根节点,来递归遍历
整个Vue树下的所有节点,并处理为vdom,最后渲染成真正的HTML,插入到正确的位置
diff算法要求的
谈谈你对MVC、MVP和MVVM的理解
这三者都是框架模式,它们设计的目标都是为了解决Model和View的耦合问题
MVC模式出现较早主要应用在后端,如Spring MVC、ASP.NET MVC等,在前端领域的早期也有应
用,如Backbone.js。它的优点是分层清晰,缺点是数据流混乱,灵活性带来的维护性问题。
用,如Backbone.js。它的优点是分层清晰,缺点是数据流混乱,灵活性带来的维护性问题。
MVP模式在是MVC的进化形式,Presenter作为中间层负责MV通信,解决了两者耦合问题,但P层
过于臃肿会导致维护问题。
过于臃肿会导致维护问题。
MVVM模式在前端领域有广泛应用,它不仅解决MV耦合问题,还同时解决了维护两者映射关系的
大量繁杂代码和DOM操作代码,在提高开发效率、可读性同时还保持了优越的性能表现。
大量繁杂代码和DOM操作代码,在提高开发效率、可读性同时还保持了优越的性能表现。
Vue组件之间的通信
props
父组件通过props向下传递数据给子组件
$emit/$on
子组件通过events给父组件发送消息,实际上就是子组件把自己的数据发送到父组件
eventBus
父子组件、兄弟组件、跨级组件
$parent/$children与ref
$attrs/$listeners
`$attrs与$listeners` 是两个对象,`$attrs` 里存放的是父组件中绑定的非 `Props` 属性,`$listeners`里存放的是父组件中绑定的非原生事件
vuex
provide/inject
这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。一言而蔽之:祖先组件中通过provider来提供变量,然后在子孙组件中通过inject来注入变量。
Vue性能优化
路由懒加载
component: () => import('xxxx')
keep-alive
include
exclude
使用v-show复用DOM
对于频繁调用的大型组件,用v-show提前渲染性能要比v-if高
v-for遍历避免同时使用v-if
长列表性能优化
如果列表是纯粹的数据展示,不会有任何改变,就不需要做响应化
Object.freeze(users); 实现数据冻结
如果是大数据长列表,可采用虚拟滚动,只渲染少部分区域的内容
vue-virtual-scroller
vue-virtual-scroll-list
事件的销毁
手动注销定时器等
图片懒加载
vue-lazyload
第三方插件按需引入
无状态的组件标记为函数式组件
<template functional>
子组件分割
变量本地化
SSR
SEO
首屏
但也有问题
你对Vue3.0的新特性有没有了解
更快
虚拟DOM重写
优化slots的生成
静态树提升
静态属性提升
基于Proxy的响应式系统
更小
通过摇树优化核心库体积
更容易维护
TypeScript + 模块化
更加友好
跨平台:编译器核心和运行时核心与平台无关,使得Vue更容易与任何平台(Web、
Android、iOS)一起使用
Android、iOS)一起使用
更容易使用
改进的TypeScript支持,编辑器能提供强有力的类型检查和错误及警告
更好的调试支持
独立的响应化模块
Composition API
类似于React的Hook,便于逻辑复用
vue如果想要扩展某个组件现有组件时怎么做?
使用Vue.mixin全局混入
全局注册一个混入,影响注册之后所有创建的每个 Vue 实例
执行顺序:全局混入 => 组件内混入 => 组件方法
加slot扩展
watch和computed的区别以及怎么选用
定义/语法区别
computed需要return
功能区别
watch更通用
computed底层来自于watch,但是增加缓存等功能,派生功能都能实现
watch触发后对应的方法必定执行一次
computed有缓存功能,依赖为发生变化不会重新计算,会返回缓存的数据
用法区别
computed更简单、高效,应该优先使用
watch单纯监听作用
简单说一说vue生命周期的理解
1. 实例化
new Vue()
初始化参数、生命周期、事件等一系列过程
2. 初始化事件、生命周期函数
3. beforeCreate创建前
beforeCreate
候拿不到 data及props 里边的数据
4. 注射响应
injections(注射器) reactivity(响应) 给数据添加观察者
5. created创建后
可以访问到 data 里的数据
如果要调用methods中的方法或者操作data中数据,要
在created里操作。要因为请求数据是异步的,所以发送请求宜早不宜迟
在created里操作。要因为请求数据是异步的,所以发送请求宜早不宜迟
挂载阶段还没开始, $el 尚不可用
6. 是否存在 el
判断一下是否有写 el ,如果没有el就判断有没有调用实例上的
$mount('') 方法调用
$mount('') 方法调用
7. 判断是否有 template
如果有 template 则渲染 template 里的内容
如果没有 则渲染 el 指明的挂载对象里的内容
8. beforeMount挂载前
相关的render函数首次被调用
9. 替换 el
这个时候会在实例上创建一个 el ,替换掉原来的 el 。也是真正的挂载
10. mounted挂载后
候 DOM 已经加载完成了,可以操作 DOM
注意mounted不会保证所有子组件一起挂载
11. dataChange
当数据有变动时,会触发下面这两个钩子
beforeUpdate
updated
在 beforeUpdate更新前 和 updated更新后 之间会进行 DOM 的重新渲染和补全。
12. callDestroy
beforeDestroy 销毁前触发
移除数据劫持、事件监听、子组件属性 所有的东西还保留 只是不能修改
destroy 销毁后触发
13.keep-alive钩子函数
activated
keep-alive 组件激活时调用
deactivated
keep-alive 组件停用时调用
简单说一说vuex使用及其理解
vuex是vue中状态管理
实现了一个单向数据流,在全局拥有一个 State 存放数据,当组件要更
改 State 中的数据时,必须通过 Mutation 提交修改信息, Mutation 同时
提供了订阅者模式供外部插件调用获取 State 数据的更新
改 State 中的数据时,必须通过 Mutation 提交修改信息, Mutation 同时
提供了订阅者模式供外部插件调用获取 State 数据的更新
所有异步操作(常见于调用后端接口异步获取更新数据)或批量的同步操作
需要走 Action ,但 Action 也是无法直接修改 State 的,还是需要通过
Mutation 来修改State的数据
需要走 Action ,但 Action 也是无法直接修改 State 的,还是需要通过
Mutation 来修改State的数据
state
vuex 的唯一数据源,如果获取多个 state ,可以使用 ...mapState
getter
理解为计算属性, getter 的返回值根据他的依赖缓存起来,依赖发
生变化才会被重新计算,获取多个getter可以使用...mapGetters
生变化才会被重新计算,获取多个getter可以使用...mapGetters
mutation
更改 state 中唯一的方法是提交 mutation 都有一个字符串和一个回调函数。
回调函数就是使劲进行状态修改的地方。并且会接收 state 作为第一个参数 payload 为第二
个参数, payload 为自定义函数, mutation 必须是同步函数
回调函数就是使劲进行状态修改的地方。并且会接收 state 作为第一个参数 payload 为第二
个参数, payload 为自定义函数, mutation 必须是同步函数
action
类似 mutation 都是修改状态
action 提交的 mutation 不是直接修改状态
action 可以包含异步操作,而 mutation 不行
action 中的回调函数第一个参数是 context ,是一个与 store 实例具有相同属性的
方法的对象
action 通过 store.dispatch 触发, mutation 通过 store.commit 提交
action 可以包含异步操作,而 mutation 不行
action 中的回调函数第一个参数是 context ,是一个与 store 实例具有相同属性的
方法的对象
action 通过 store.dispatch 触发, mutation 通过 store.commit 提交
module
vuex 允许我们将 store
分割成模块,每个模块拥有自己的 state,mutation,action,getter ,甚至是嵌套子模块从
上至下进行同样方式分割
分割成模块,每个模块拥有自己的 state,mutation,action,getter ,甚至是嵌套子模块从
上至下进行同样方式分割
你知道nextTick的原理吗
可以在DOM更新完毕之后执行一个回调,以来eventLoop的微任务
首先尝试使用Promise
如果Promise不存在则尝试使用MutationObserver
如果MutationObserver不存在则尝试setImmediate
如果都不行,则采用宏任务的setTimeout
你知道vue双向数据绑定的原理吗
概念:数据变化更新视图,视图变化更新数据
输入框内容变化时,data中的数据同步变化 view => model
data中的数据变化时,文本节点的内容同步变化 model => view
设计思想:观察者模式
实现过程
设置监听器Observer来监听所有属性
Object.defineProperty( )通过getter和setter监听所有属性值
如果有变动的,就通知订阅者
设置容纳订阅者的消息订阅器Dep
订阅器Dep主要负责收集订阅者,然后再属性变化的时候执行对应订阅者的更新函数
订阅器Dep中会添加一个订阅者在getter里面,是为了让Watcher初始化时进行触发
设置订阅者Watcher
收到属性的变化通知并执行相应的函数,从而更新视图
订阅者Watcher在初始化的时候需要将自己添加进订阅器Dep中
设置解析器Compile
可以扫描和解析每个节点的相关指令
解析模板指令,并替换模板数据,初始化视图
将模板指令对应的节点绑定对应的更新函数,初始化相应的订阅器
vue-router 中的导航钩子由那些?
全局钩子函数
router.beforeEach((to, from, next) => {})
to :即将要进入的目标路由对象
from:当前正要离开的路由对象
next:路由控制参数
next():如果一切正常,则调用这个方法进入下一个钩子
next(false):取消导航(即路由不发生改变)
next('/login'):当前导航被中断,然后进行一个新的导航
next(error):如果一个Error实例,则导航会被终止且该错误会被传递给 router.onError ()
常用验证用户权限
router.afterEach((to, from) => {})
常用自动让页面返回最顶端
路由配置中的导航钩子
跟随单个路由的写法
跟随单个路由的写法
beforeEnter: (to, from, next)
beforeEnter: (route)
组件内的钩子函数
1. beforeRouteEnter (to,from,next)
在渲染该组件的对应路由被 confirm 前调用
此时实例还没被创建,所以不能获取实例(this)
2. beforeRouteUpdate (to,from,next)
当前路由改变,但改组件被复用时候调用
该函数内可以访问组件实例(this)
3. beforeRouteLeave (to,from,next)
当导航离开组件的对应路由时调用。
该函数内可以访问获取组件实例(this)
路由监测变化
使用watch 来对$route 监听
什么是递归组件?
组件是可以在它们自己的模板中调用自身的
一定要有一个结束的条件,否则就会使组件循环引用,最终出现的错误,我们可以使用vif="false"作为递归组件的结束条件。当遇到v-if为false时,组件将不会再进行渲染
应用
树结构的导航目录
树结构的table
说一说vue响应式理解?
vue2
Object.defineProperty
data通过Observer转换成了getter/setter的形式来追踪变化
当外界通过Watcher读取数据的,会触发getter从而将watcher添加到依赖中
当数据变化时,会触发setter从而向Dep(依赖收集)中的依赖(watcher)发送通知
watcher接收通知后,会向外界发送通知,变化通知到外界后可能会触发视图更新,也有可能触发用户
的某个回调函数等
的某个回调函数等
总结
数据劫持 / 数据代理
依赖收集
发布订阅模式
依赖收集
发布订阅模式
1. 侦测数据的变化
2. 收集视图依赖了哪些数据
3. 数据变化时,自动“通知”需要更新的视图部分,并进行更新
2. 收集视图依赖了哪些数据
3. 数据变化时,自动“通知”需要更新的视图部分,并进行更新
vue3
proxy
Vue
三要素
响应式
模板引擎
渲染
响应式
什么是响应式
修改data属性后Vue立刻监听到
data属性被代理到VM上
Object.defineProperty
模拟
模板解析
概念
本质:字符串
有逻辑,如v-if v-for等
与html格式很像,但是有区别
有逻辑
动态的
最终要转换为html来显示
模板最终转换为JS代码
只有JS能实现逻辑
只有JS才能渲染页面
render函数
width的用法
自己开发时尽量不使用
可读性比较差
可能是混淆错误和兼容性问题的根源
在 ECMAScript 5 严格模式中该标签已被禁止
扩展一个语句的作用域链
讲解
模板中的所有信息都包含在了render函数中
this即vm
price即this.price即vm.price,即data中的price
_c即this._c即vm._c
render函数与vdom
vm._c其实相当于snabbdom中的h 函数
render函数执行之后,返回的是vnode
updateComponent中实现了vdom的patch
页面首次渲染执行updateComponent
data中每次修改属性,执行updateComponent
整体的实现流程
解析模板成render函数
with的应用
模板中的所有信息都被render函数包含
模板中用到的data中的属性,都变成JS变量
模板中的v-model v-for v-on变成了JS变量
render函数返回VNode
响应式开始监听
Object.defineProperty
将data的属性代理到vm上
首次渲染,显示页面,绑定依赖
初次渲染,执行updateComponent,执行 vm._render()
执行render函数,访问到vm.list和vm.title
会被响应式的get方法监听到
为什么不监听set
data中有属性不被用到
被用到的会走get,不被用到的不走get
为走到get的,set的时候也无需关心
从而避免不必要的渲染
data属性变化,触发reRender
被响应式的set监听到
自适应布局
什么是 rem ?
指相对根元素 html 的 font-size 来做计算的大小单位
html { font-size: 75px }时,1rem = 75px,4rem = 300px
什么是 em ?
相对于当前元素设置的 font-size 大小,如果当前元素没有设置,则按照浏览器默认字体来计算
h1 { font-size:16px; }时,1em = 16px
h2 { font-size:18px; }时,1em = 18px
什么是 vw ?
基于 viewport 视窗的长度单位,1vw 等于 window。innerWidth 的 1%
什么是 dpr?
设备像素比 device pixel ratio 的简称,即物理像素和设备独立像素的比值
浏览器为我们提供了 window.devicePixelRatio 来帮助我们获取 dpr
iPhone 6、7、8的实际物理像素是750 x 1334,在开发者工具中我们可以看到:
它的设备独立像素是375 x 667,设备像素比dpr为2
饿了么移动端方案
postcss-px-to-viewport
网易移动端方案
rem + vw
手淘方案
Flexible
前端性能分析
分析工具
LightHouse
检查的是网址,它将针对此页面运行一连串的测试,然后生成一个有关页面性能的报告
分析结果以可视化的方式展示给开发者,甚至还指出了可以优化的地方
使用方式
Chrome 开发者工具(DevTools)
Chrome 扩展
Node CLI
https://github.com/GoogleChrome/lighthouse#using-the-node-cli
https://github.com/GoogleChrome/lighthouse#using-the-node-cli
Node Module
//安装
npm install -g lighthouse
npm install -g lighthouse
//生成报告 并在浏览器中打开
lighthouse http://kaikeba.com --view
lighthouse http://kaikeba.com --view
//报告格式是json,保存在当前目录
lighthouse http://kaikeba.com --output=json --output-path=./report.json
lighthouse http://kaikeba.com --output=json --output-path=./report.json
//设置浏览器的窗口尺寸
lighthouse http://kaikeba.com --chrome-flags="--window-size=1190,660"
lighthouse http://kaikeba.com --chrome-flags="--window-size=1190,660"
//模拟器设为桌面
lighthouse http://kaikeba.com --emulated-form-factor=desktop
lighthouse http://kaikeba.com --emulated-form-factor=desktop
性能报告分类
Perfermance 性能分析
Accessibility 可访问性
Best Practices 最佳实践
SEO 搜索引擎优化
Progressive Web App 是否集成pwa
分析出发点
根据 Google 在 web.dev 上公布的数据,他们认为以用户为中心的性能指标,应该能回答以下四个问题
1. 是否发生?
浏览器是否成功地把我的请求发送出去,而服务器是否已经知道并开始处理我的请求
TTFB (Time to First Byte)
首字节到达的时间点
FP (First Paint)
首次绘制,标记浏览器渲染任何在视觉上不同于导航前屏幕内容的时间点
FCP(First Contentful Paint)
首次内容绘制,标记浏览器渲染来自 DOM 第一位内容的时间点,内容可能是文本、图像等元素<br>
测试加载速度的体验,页面最主要的内容何时呈现
2. 是否有用?
用户在确定页面有反应了后,就开始关心,什么时候能展现有用的内容
LCP(Largest Contentful Paint)
最大内容绘制时间,计算从页面开始加载到用户与页面发生交互(点击,滚动)这段时间内,最大元素绘制的时间,该时间会随着页面渲染变化而变化
SI(Speed Index)
速度指标,填充页面内容的速度,取开始加载到最后完成渲染,每一时刻页面未完成度的积分
3. 是否可用?
在用户得到了有用的信息后,用户就会基于得到的信息作出反应,这就是页面“是否可用?
TTI(Time to Interactive)
可交互时间,用于标记页面已进行视觉渲染并能可靠响应用户输入的时间点
TBT Total Blocking Time
总共阻塞时间,计算的是从 FCP 到 TTI 之间,主线程阻塞的总时间。阻塞时间是指单次任务占用主线程超过 50 ms 的部分
FID (First Input Delay)
首次输入延迟,指用户首次输入到页面响应的时间
会成为用户对网站很重要的第一印象,决定用户有可能成为忠实用户或者弃之而去
FID 仅关注用户离散的操作,如点击,轻击,按键等,其他交互如滚动和缩放,并不是 FID 关注的,因为通常浏览器会用一个单独的线程来处理它们
4. 是否令人愉快?
使用是否流畅,有没有发生预料外的视觉偏移。
CLS(Cumulative Layout Shift)
累计布局偏移。测量在页面的整个生命周期中发生的每个意外的样式移动所造成的布局偏移分数的总和
测试视觉稳定性上的体验,有多少内容发生了意外的偏移
提取出核心指标
Time to Interactive
名称:可交互时间
用于标记页面已进行视觉渲染并能可靠响应用户输入的时间点
可识别页面初始 JavaScript 已加载且主线程处于空闲状态(没有耗时较长的任务)的时间点
Total Blocking Time
名称:总共阻塞时间
计算的是从 FCP 到 TTI 之间,主线程阻塞的总时间
指单次任务占用主线程超过 50 ms 的部分
Cumulative Layout Shift
名称:累计布局偏移
测量在页面的整个生命周期中发生的每个意外的样式移动所造成的布局偏移分数的总和
测试视觉稳定性上的体验,有多少内容发生了意外的偏移
First Contentful Paint
名称:首次内容绘制
标记浏览器渲染来自 DOM 第一位内容的时间点
内容可能是文本、图像等元素
测试加载速度的体验,页面最主要的内容何时呈现
Speed Index
名称:速度指标
填充页面内容的速度,取开始加载到最后完成渲染,每一时刻页面未完成度的积分
Largest Contentful Paint
名称:最大内容绘制时间
计算从页面开始加载到用户与页面发生交互(点击,滚动)这段时间内,最大元素绘制的时间
间会随着页面渲染变化而变化,因为页面中的最大元素在渲染过程中可能会发生改变
关于(新)SSR的理解
服务端渲染
传统的web开发
基本都是服务端渲染,如JSP,ASP,.NET,PHP等
代码不易维护,前后端职责不清晰
在项目不断壮大、更新和迭代情况下会导致用户体验、性能下降
客户端渲染
使用js发起网络请求来异步获取数据
不刷新整个页面的情况下动态生成HTML
前后端分离可以减轻服务器压力同时部署相对简单
开发效率更高,性能更好,交互性更加,前后端职责清晰
存在一些问题
不利于SEO
首屏渲染慢,会出现较长白屏现象
增加了客户端负担
服务端渲染
在服务端完成html文件的拼接
相对于客户端渲染的好处
有利于SEO
大多数爬虫是不会执行网站的任何脚本的,客户端渲染是JS动态渲染的所以爬取的信息有限
服务端渲染返回给客户端的是已经获取了异步数据并执行JavaScript脚本的最终HTML,网络爬中就可以抓取到完整页面的信息
有利于首屏渲染
根据node发送过来的html字符串完成渲染
不需要等待JS、CSS文件加载完成
也存在一些问题
服务器算力压力增大,相对于客户端渲染会更多的消耗服务器性能
不易维护,开发相对客户端渲染有限制
学习成本较高,需增加node相关方面的学习
使用场景
对SEO要求严格,对首屏的渲染时间要求较高的适合服务端渲染模式
公司官网
webview页面
客户演示页面
不对用户体验或SEO要求到极致的话依旧可以考虑从客户端渲染方面进行优化
白屏问题
增加loading动画
增加骨架屏
SEO
vue-meta-info
预渲染prerender
前端权限管理思路(以Vue为例)
路由权限
路由表配置在前端
constantRoutes
无需权限即可访问
asyncRoutes
需要权限才可访问
登录后获取权限roles
根据roles遍历asyncRoutes路由生成权限路由表
比对路由meta内的roles
perimission.js
权限路由逻辑处理
本质:路由守卫
未登录
去往白名单路由
放行
去往需要权限的路由
重定向到登录页
已登录
去往需要权限的路由
无角色信息
获取角色信息
过滤可访问的路由
添加路由
完成路由切换
有角色信息
放行
去往白名单路由
重定向致首页
路由表配置在后端
登录后请求路由表
相较上一种增加了本地组件映射步骤
按钮权限
封装自定义指令
inserted钩子内判断权限集是否包含用户的权限
包含
渲染
不包含
移除element
v-if
一些情况下还是需要使用v-if来实现
比如elementUI的tab切换组件:el-tan-pane
0 条评论
下一页