## **HTML5**
### 语义化标签
> header、nav、footer、section、article、aside 优点:更好的SEO,更清晰的HTML结构,增强了可读性,便于维护开发
- header:定义页面或文章头部,通常包含logo、页面标题等
- nav:定义只包含导航链接的章节
- section:定义文档中的一个章节/部分
- aside:定义和页面内容关联度较低的内容(即使删除,剩下的内容仍然合理)
- footer:定义页面或章节的尾部,通常包含版权信息、法律信息和反馈建议等有效链接地址
### 媒体标签:
audio(音频)、video(视频)
不需要第三方插件就能播放音频和视频了,可以直接插入,并且用同一的API接口控制。
### SVG和Canvas
> SVG:可缩放矢量图形,svg并不是html5专有的标签
- 不依赖分辨率
- 支持事件处理器
- 最适合带有大型渲染区域的应用程序(比如谷歌地图)
- 复杂度高会减慢渲染速度(任何过度使用 DOM 的应用都不快)
- 不适合游戏应用
> Canvas:画布
- 依赖分辨率
- 不支持事件处理器
- 弱的文本渲染能力
- 能够以 .png 或 .jpg 格式保存结果图像
- 最适合图像密集型的游戏,其中的许多对象会被频繁重绘
**SVG和Canvas区别:**
- Canvas是使用javaScript程序绘制动态生成的,SVG是使用xml文档描述来绘图。从这点来看:SVG更适合用来做动态交互,而且SVG绘图更容易编辑
- SVG是基于矢量的,所以它能够很好的处理图形大小的变大。canvas是基于位图的图像,他不能改变大小,只能缩放显示,最适合带有大型渲染区域的应用程序(比如谷歌地图)
- canvas支持的颜色比SVG多,更适合图像密集型的游戏之类使用
### Geolocation定位:
Geolocation:是window.navigator下面的一个对象,该对象提供了实现地理位置定位的接口。要用该功能首先判断浏览器是否支持navigator.geolocation该对象。
### localStorage和sessionStorage和Cookie
- 用法:
- 存储:
- 普通类型:window.localStorage.setItem('key',value);
- OBject类型:window.localStorage.setItem('param',JSON.stringify(Object))
- 读取:
- 普通类型:window.localStorage.getItem('key');
- OBject类型:JSON.parse(window.localStorage.getItem('key'))
- 删除:
- window.localStorage.removeItem('key')
- 清除缓存:
- localStorage.clear()
- > localStorage和sessionStorage一样都是用来存储客户端临时信息的对象。
- 他们均只能存储字符串类型的对象(虽然规范中可以存储其他原生类型的对象,但是目前为止没有浏览器对其进行实现)。
- localStorage生命周期是永久,这意味着除非用户显示在浏览器提供的UI上清除localStorage信息,否则这些信息将永远存在。
- sessionStorage生命周期为当前窗口或标签页,一旦窗口或标签页被永久关闭了,那么所有通过sessionStorage存储的数据也就被清空了。
- 不同浏览器无法共享localStorage或sessionStorage中的信息。相同浏览器的不同页面间可以共享相同的 localStorage(页面属于相同域名和端口),但是不同页面或标签页间无法共享sessionStorage的信息。这里需要注意的是,页面及标 签页仅指顶级窗口,如果一个标签页包含多个iframe标签且他们属于同源页面,那么他们之间是可以共享sessionStorage的。
- > cookie和它们的区别:
- cookie数据始终每次http请求都会携带cookie,浏览器和服务器间来回传递;而sessionStorage和localStorage不会自动把数据发送给服务器;
- 存储大小限制也不同,cookie单个cookie数据不能超过4K,sessionStorage和localStorage可以达到5M或更大;
- 数据有效期不同,cookie只在设置的cookie过期时间之前有效,即使窗口关闭或浏览器关闭;
- 作用域不同,sessionStorage不在不同的浏览器窗口中共享,即使是同一个页面; localstorage在所有同源窗口 中都是共享的; cookie也是在所有同源窗口中都是共享的。
- cookie使用:
- 写入:document.cookie='username=xxxx';
- 读取:document.cookie;
### fetch请求方式:
- Fetch是浏览器内置API,Ajax的替代者,在浏览器环境中,封装了Promise机制,可以通过顶层对象window获取,优化了异步问题——可以直接用.then()方法。
```javascript
const url = "https://gitee.com/api/v5/users/liyangyf";
fetch(url).then( response=>response.json() ) // 将response数据解析成json
.then( json=>console.log(json) )
```
### input新类型:
- email : 提交表单时检测值是否是一个电子邮件格式
- url: 提交表单时检测值是否是一个url格式
- date: 年-月-日格式的控件
- time: 时:分格式的控件
- datetime-local : 年-月-日 时:分 格式的控件(本地时间)
- month : 年-月格式的控件
- week: 年-周数格式的控件
- number: 数字输入框
- Range : 选择区域
- tel: 电话输入框
- search: 用于搜索
- color: 调用系统选色器
### input新属性:
- placeholder: 占位符,输入框提示信息
- required: 该input为必填项
- autofocus: 在页面加载时,域自动地获得焦点
- Pattern : 正则验证 如: pattern="[0-9]{7,12}"
- min/max: input能输入的最小/最大数字的大小
- maxlength:input能输入的最大字节的长度(最多可以输入的文字或者字母的个数)
- Step;针对number和range类型,每次递增step的值
- list: 指定一个datalist,作为下拉提示单
## **CSS**
### CSS3 新特性有哪些?
1. 颜色:新增 rgba(),HSLA 模式
2. 文字阴影(text-shadow、)
3. 边框: 圆角(border-radius)边框阴影: box-shadow ; 边框图片:border-image
4. 盒子模型:box-sizing
5. 背景:background-size 设置背景图片的尺寸 background-origin 设置背景图片的原
点,background-clip 设置背景图片的裁切区域,以”,”分隔可以设置多背景,用于自
适应布局
6. 渐变:linear-gradient、radial-gradient
7. 过渡:transition,可实现动画
8. 自定义动画
9. 在 CSS3 中唯一引入的伪元素是 ::selection.
10. 媒体查询,多栏布局
11. 2D 转换:transform:translate(x,y) rotate(x,y) skew(x,y) scale(x,y)
12. 3D 转换:transform:translate3d(x,y,z)
### Scss与Less区别
- 变量不同:less-@ scss-$
- 运算: scss 加减法中 计算单位必须相同;less 加减法中可以不同单位进行运算
- 作用域不同, less 存在变量提升 scss 没有变量提升 $number:1px @number:1px
### 什么是BFC
BFC直译为‘块级格式化上下文’。它是一个独立的渲染区域,只有Block-level box参与, 它规定了内部的Block-level Box如何布局,并且与这个区域外部毫不相干。 block-level box,display属性为block, list-item, table的元素,会生成block-level box。并且参与block fomatting context。
### BFC特性
- Box垂直方向的距离由margin决定,属于同一个BFC的两个相邻box的margin会发生重叠
- 计算BFC的高度时,浮动元素也参与计算
- BFC的区域不会与float box发生重叠
- BFC内部的Box会在垂直方向,一个接一个的放置
- 每个元素的margin box的左边会与包含块border box的左边相接触(对于从左到右的格式化,否则相反),即使存在浮动也会如此
- BFC就是页面上的一个独立容器,容器里面的元素不会影响到外面的元素
### CSS问题
> 清除图片间隙
- 清除图片左右间距
1. 两张图片挨着写(img标签挨在一起)
2. 父元素的font-size设置为0
3. 给图片设置浮动
- 清除图片上下间距
1. 图片设置为display:block
2. 给图片设置vertical-align:top/bottom/middle
> 元素水平垂直居中
- 弹性盒
\- 父元素:display:flex;子元素:margin:auto;
\- 父元素:display:flex;justify-content:center;align-item:center;
- 定位
\- 父元素:position:relative;子元素:position:absolute;left:0;right:0;bottom:0;top:0;margin:auto;
- 定位+2D变形
\- 父元素:position:relative;子元素:position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);
### css长度单位
- px:常用的绝对长度单位,相对与显示器分辨率而定位的单位,兼容性较好
- 百分比:常用相对长度单位,相对于父元素的尺寸取值
- pt:绝对长度单位 1px = 0.75pt
- em:相对长度单位,相对于当前元素内部的文本尺寸
- rem:css3新增的相对长度单位,相对的是根元素HTML的文本尺寸
- vw:相对视窗宽度的百分比(1vw代表视窗宽度1%)
- vh:相对视窗高度的百分比
- vmax:当前vw和vh中较大的值
- vmin:当前vw和vh中较小的值
### 高度塌陷
高度塌陷是因为所有的子元素浮动,且父元素没有设置高度,就会触发父元素高度塌陷;
> 给父元素添加固定高度; 缺点:不适合高度自适应的布局。
> 给父元素添加overflow:hidden;
### 行内元素、块状元素、行内块状元素
> 行内元素
1. 设置宽高无效
2. margin设置仅左右有效,padding设置上下左右都有效
3. 不会自动换行
> 块状元素
1. 可以设置宽高
2. margin,padding上下左右均有效
3. 会自动换行
4. 块状元素排列从上至下(未脱离文档流时)
> 行内块状元素
1. 不会自动换行
2. 可以设置宽高
3. 默认排列从左至右
### css选择器
!importent > 行内样式 > id选择器 > 类选择器 > 标签选择器 > 通配符选择器
### 动画和过渡区别
> animation(动画)、transition-property(过度)
- 动画不需要事件触发,过渡需要,
- 过渡只有开始和结束两个关键帧,动画可以设置多个关键帧
### 元素水平垂直居中
- 弹性盒
- 父元素:display:flex;子元素:margin:auto;
- 父元素:display:flex;justify-content:center;align-item:center;
- 定位
- 父元素:position:relative;子元素:position:absolute;left:0;right:0;bottom:0;top:0;margin:auto;
- 定位+2D变形
- 父元素:position:relative;子元素:position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);
### 重绘和回流
> 回流:当render tree中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建时,这就称为回流(reflow)。每个页面至少需要一次回流,就是在页面第一次加载的时候,这时候是一定会发生回流的,因为要构建render tree
> 重绘:浏览器会重新绘制受影响的部分到屏幕中,该过程称之为重绘
> 当render tree中的一些元素需要更新属性时,而这些属性只是影响元素的外观、风格而不会影响布局的时候,比如background-color,文字颜色、边框颜色等。则就称为重绘。
**回流一定会触发重绘,而重绘不一定会回流**
## **JavaScript**
### js运行机制
首先我们需要了解JavaScript的特性就是单线程,单线程简单的说就是同一时间只能做一件事。那么为什么是单线程呢?作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程再某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?
单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。多线程可以将任务放到不同的线程中去处理。CPU的调度单位是线程,它会在不同的线程之间切换,任务是隶属于线程的
于是,JavaScript中所有任务可以分成两种,一种是同步任务,另一种是异步任务。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入“任务队列”的任务,只有“任务队列”通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行
主线程从“任务队列”中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件轮询)
这个任务队列都是已经完成的异步操作
对于异步任务队列来说,又分为宏任务和微任务
宏任务:包括整体代码 script,setTimeout,setIntervcal
微任务:Promise,process.nextTick
执行顺序:进入整体代码后,开始第一次循环。接着执行所有的微任务,然后再次从宏任务开始,找到其中一个任务队列执行完毕,再执行所有的微任务
### typescript(ts)
1. 不是一门新语言,是用来规范js的语言,是js的超集,常用在大型项目中,使得代码更规范,维护更加方便
2. 不需要去浏览器中浏览效果,就能知道编译错误
3. 类型就是最好的注释,看类型我们就知道这个是什么
4. 即使ts中有编译报错,tsc依旧可以将其编译成js
5. 特点:
- 增加静态检查,增加代码健壮性
- 多人协作,提高生产力,降低维护成本
6. 新增类型:
- **tuple(元组)**
- 可以明确知道包含了多少元素(这里的元素是类型)
- 可以明确知道每个类型所在的位置
- 长度固定,元组类型的变量需要为每一个位置定义对应类型的值
```javascipt
type StringNumberPair = [string, number];
let testTupleTypeMatched: StringNumberPair = ['1', 2]; // ok
```
- **enum(枚举)**
- 列举类型中包含的各个值,一般用它来管理多个相同系列的常量(即不能被修改的变量),用于状态的判断。这是一种无序的数据结构,把键映射到值上,可以理解为编译时键固定的对象,访问键时,ts将检查指定的键是否存在
```javascpit
//默认从0开始
enum Direction{
Up,
Down,
Left,
Right,
}
console.log(Direction.Up)//0
console.log(Direction[0])//up
//赋值后往后以此类推
enum Direction2{
Up=1,
Down,
Left,
Right,
}
console.log(Direction2.Down)//2
console.log(Direction2[2])//Down
```
- **any(任意类型)**
- **never(null或undefind)**
- **void**
7. interface和type区别
- 是ts中定义类型的两种方式
- 都可以描述一个对象或者函数
- interface只能定义对象类型
- interface能够声明合并,type不行
- type 可以声明基本类型别名,联合类型,元组等类型
8. ts中面向对象成员修饰符:public , private , protexted
- public:是表示是公开的,在任何地方,都可以调用,不管在类中,还是子类中,还是对象中均是可以直接调用
- private:是表示私有的,只能在类中访问,不能在子类或者对象中访问,要调用私有的属性或者方法,可以通过在类中设置公有的方法访问私有的成员,对外暴露公有的方法进行访问,子类继承的时候,也可以继承父类私有的属性和方法,但是也要通过在子类继承过来的公有的方法进行访问私有的属性
- protected: 只能在类中或者子类中进行访问,但是不能在自身类或者子类对象中访问
**总的老说,权限范围:public>protected>private**
### 闭包和递归
> 闭包:通俗来讲我们说函数里面嵌套函数的一种形式,或者说是定义在一个函数内部的函数
> **一句话来概括闭包是指有权访问另一个函数作用域内部变量的函数**
- 闭包的好处/用途
1. 希望一个变量可以常驻内存中(延迟变量作用域)
2. 避免全局变量的污染
3. 私有成员的存在
- 闭包的缺陷
1. 变量常驻内存,增加了内存的开销
2. 使用不当会造成内存泄漏
> 递归:就是在运行的过程中不断地调用自己。递归有两个过程,简单地说一个是递的过程,一个是归的过程
> **一句话来概括递归是指自己不停调用自己的函数**
- 递归必须具备两个条件:
1. 是有边界,即终止条件。
2. 是需要调用自己。
- 这两个条件缺一不可,并且其中终止条件语句必须在递归调用语句之前。如果顺序颠倒则递归函数会进入死循环,永远退不出来,会出现堆栈溢出异常
### 防抖和节流
> 防抖:短时间内多次触发同一事件,只执行最后一次,或者只执行最开始的一次,中间的不执行
在事件触发时,开始计时,在规定的时间(delay)内,若再次触发事件,将上一次计时(timer)清空,然后重新开始计时。保证只有在规定时间内没有再次触发事件之后,再去执行这个事件
- 场景:面resize事件,页面适配时,根据最终呈现的页面情况进行dom渲染,一般使用防抖,只需判断最后一次的变化情况;search搜索联想,用户在不断输入值时,用防抖来节约请求资源
> 节流:指定时间间隔内,若事件被多次触发,只会执行一次
在事件触发之后,开始计时,在规定的时间(delay)内,若再次触发事件,不对此事件做任何处理。保证在规定时间内只执行一次事件
- 场景:一般是onrize,onscroll等这些频繁触发的函数,如获取滚动条的位置,然后执行下一步动作;鼠标不断点击触发,mousedown(单位时间内只触发一次);搜索框input事件,例如要支持输入实时搜索可以使用节流方案
**总的来说防抖控制的是次数,节流控制的是频率**
### 原型和原型链
**原型:**
每一个函数类型的数据,都有一个叫做prototype的属性,这个属性指向的是一个对象,就是所谓的原型对象。
对于原型对象来说,它有个constructor属性,指向它的构造函数。
**原型链:**
> 构造函数:能够使用了new关键字创建对象的函数,被叫做了构造函数;
有一个构造函数new出来一个实例对象,这个对象有一个属性__proto__指向这个构造函数的原型对象
> 原型对象是用来存放实例对象的公有属性和公有方法的一个公共对象,所有原型对象都是Object()的实例
也就是说,如果你要访问一个方法或者属性时,如果在这个实例对象中没有找到,就会去沿着__proto__查找,直到找到Object的实例没有这个属性或者方法,则会返回null;
### 继承
> 原型链继承:子类想要继承父类的属性和方法,可以将其原型对象指向父类的实例,根据原型链就可以使用到父类的方法和属性
> 构造函数继承 : 在子类的构造函数中,执行父类的构造函数,并且为其绑定子类的this;
**缺点:** 构造函数继承可以继承到父类上的属性和方法,但是继承不到父类原型上的属性和方法
> 组合式继承
>寄生式继承
**ES6新继承方法:**
>寄生组合式继承
### 深浅拷贝
**浅拷贝:只是拷贝的指向对象的指针,本质上还是指向同一个对象。** 拷贝的对象改变原对象也随之改变;
- assign
- for in
**深拷贝:就是完全复制一个新的对象出来,它们在堆内存中完全占据两个不同的内存地址。**
- 如果obj里面有时间对象,则`JSON.stringify`后再`JSON.parse`的结果,时间将只是字符串的形式。而不是时间对象 ;
- 如果obj里有函数,`undefined`,则序列化的结果会把函数或`undefined`丢失
- 运用递归来进行深拷贝
- ```JavaScript
//定义深拷贝函数
function deepClone(obj){
//obj是我们要拷贝的数据
//递归要先判断否定
if(typeof obj !== 'object' || obj==null){
//判断要拷贝的值是不是复杂数据类型,普通数据类型直接return
return obj
}
let res
if(obj instanceof Array){
res=[]
}else{
res={}
}
for(key in obj){
//判断值是否为obj本身的数值
if(obj.hasOwnProperty(key)){
res[key] = deepClone(obj[key])
}
}
return res
}
```
### 暂时性死区
let、const声明的变量不会进行变量的提升,如果在声明前访问就会报错
```javascrpit
console.log(userName ) //报错ReferenceError: userName is not defined
let userName = 'Sofia';
```
### 事件冒泡
- 事件冒泡即事件开始时由最具体的元素接收,然后逐级向上传播到较为不具体的节点。
- 事件冒泡是默认开启的,但可以通过js代码来控制事件冒泡。我们可以执行stopPropagation()来阻止事件冒泡
### 事件委托
它其实就是利用DOM元素的事件冒泡原理;将原来需要绑定在子元素的响应事件委托给父元素或者跟外层的元素上,让其担当事件监听的作用;
> 优点:
- 节约内存,减少事件的绑定
- 可以动态绑定事件,新增的子对象事件可以被已绑定的事件处理
## **ES6**
### 暂时性死区—let,const
只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”
> let 、const、var的区别:
- let和var用来声明变量,const用来声明常量,let和const的区别就在于变量和常量,其他规则一样
- var声明变量具有变量提升,即变量可以在声明之前调用,值为undefined;let声明变量不在具有变量提升,在声明之前调用会报错
- var允许重复声明同一变量,在同一作用域内,let和const不允许重复声明变量;
### 箭头函数
- 箭头函数不能作为构造函数使用,也就是不能使用new关键字
- 箭头函数没有原型属性
- 箭头函数的this会捕获其上下文的this,在定义的时候就决定了;是不能改变的(call、bind、apply都不行);
所以解决了一些this执行环境造成的一些问题:
- 解决了匿名函数this指向的问题
- setTimeout和setInterval中使用this所造成的问题
### call()、apply()、bind()
> call()
> 第一个参数表示this要指向的对象,其余参数表示调用函数需要传入的参数,返回调用函数的返回结果,属于立即执行函数;
> apply()
> 第一个参数表示this要指向的对象,第二参数表示调用函数需要传入的参数所组成的数组,返回调用函数的返回结果,属于立即执行函数;
> bind()
> 她的参数和call()的参数是一样的,但是其返回是一个函数,而不是调用函数的返回结果;
### promise
- 什么是promise
- Promise 异步编程的一种解决方案,比传统的解决方案(回调函数)更合理和更强大。
- 可以将异步操作队列化,按照期望的顺序执行,返回符合预期的结果
- 可以在对象之间传递和操作promise,帮助我们处理队列
- 代码风格更加容易理解和维护
- Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(resolve已成功)和rejected(已失败).
- 一旦状态设定,就不会再变
- promise原型下面三个常用的方法:
- Promise.prototype.then
- Promise.prototype.catch
- Promise.prototype.finally
- Promise.all():用于将多个 Promise 实例,包装成一个新的 Promise 实例,接受一个数组作为参数,只有数组里面的每个状态都变成resolve,则新的 Promise 实例状态才会变成resolve.
### async函数
async...await 返回的是一个 Promise 对象,也是用来异步编程的一个解决方法;
### module和commonjs的区别
> module其实就是import;comminJs其实就是require
- module是编译时导出接口,CommonJS是运行时导出对象。
- module输出的值的引用,CommonJS输出的是一个值的拷贝。
- module语法是静态的,CommonJS语法是动态的。
- module导入模块的是只读的引用,CommonJS导入的是可变的,是一个普通的变量。
- module支持异步,CommonJS不支持异步
### class类
> 可以运用Class关键字来定义类。其实它可以看作是一个语法糖,它的绝大部分功能,ES5其实也可以实现,但是它可以让对象原型的写法更加的清晰、更像面向对象编程的语法。
**知识点:**
- Class声明类
- constructor定义构造函数初始化
- extends继承父类
- super调用父级构造方法
- static定义静态方法和属性
- 父类方法可以重写
### proxy
> 原生提供 Proxy 构造函数,用来生成 Proxy 实例,所以可以按照构造函数创建对象的形式去实例化一个Proxy对象
```javascript
var proxy = new Proxy({},{})
console.log(proxy) // Proxy{}
```
**注意点:**
- 实例化一个Proxy对象时,必须要传两个参数对象,否则会报错
- 传两个空对象时,默认的是简单声明了一个Proxy实例
- 第一个参数:target,目标对象,是你要代理的对象.它可以是JavaScript中的任何合法对象
- 第二个参数:handler,配置对象,用来定制拦截行为,对于每一个被代理的操作,需要提供一个对应的处理函数,该函数将拦截对应的操作。
> 当目标对象(target)为空时:这样直接代表着,拦截的对象是空的,所以直接对proxy对象进行操控。target是个空对象,但是操作了proxy,也影响不了target
> 当拦截对象(handler)为空时;handler没有设置任何拦截,那就等同于直接通向原对象。handler是一个空对象,没有任何拦截效果,访问proxy就等同于访问target
Proxy实例化的对象默认带有get和set方法。也可以在这些基础上进行拦截操作,get()是用来进行读取操作,set()是用来进行赋值操作
### set和map数据结构
> map():是一组键值对的结构,具有极快的查找速度。解决了JavaScript的对象键必须是字符串的问题。在map中,Number或者其他数据类型也可以作为键。
> Set()
> Set和Map类似,也是一组key的集合,但不存储value。由于key不能重复,所以,在Set中,没有重复的key。
- Set()去重
```javascript
var mySet = new Set([1, 2, 3, 4, 4]);
```
- Set()合并数组
```javascript
var a = new Set([1, 2, 3]);
var b = new Set([4, 3, 2]);
var union = new Set([...a, ...b]); // {1, 2, 3, 4}
```
- Set()求两个数组的共有值
```javascript
var a = new Set([1, 2, 3]);
var b = new Set([4, 3, 2]);
var intersect = new Set([...a].filter(x => b.has(x))); // {2, 3}
```
- Set()差集
```javascript
var a = new Set([1, 2, 3]);
var b = new Set([4, 3, 2]);
var difference = new Set([...a].filter(x => !b.has(x))); // {1}
```
**set和map的区别:**
> Set():是一种关联式容器
- 所得元素的只有key没有value,value就是key
- 不允许出现键值重复
- 所有的元素都会被自动排序
- 不能通过迭代器来改变set的值,因为set的值就是键
> map和set一样是关联式容器,区别就在于map的值不作为键,键和值是分开的;
- 所有元素都是键+值存在
- 不允许键重复
- 所有元素是通过键进行自动排序的
- map的键是不能修改的,但是其键对应的值是可以修改的
### 循环
> forEach() map() filter() some() every() reduce() reduceRight()
> 3个参数:匿名函数中可传3个参数item(当前项),index(当前项的索引),arr(原数组)
- forEach():不能创建新数组,没有返回值;可以用bind来绑定this的指向,但如果用的是箭头函数的写法,那么this就无法改变;
- map:正常情况下需要配合return 返回一个新数组 若是没有return,相当于forEach
- filter 主要是用来过滤 过滤一些不合格的数据
- every 数组里的所有元素都要符合条件才返回true
- reduce()从左到右 reduceRight()从右往左 他们两个方法是来求数组的和
### 手写new之后的过程
- 创建一个新对象。
- 让这个新的对象的原型指向该构造函数的原型对象。
- 执行构造函数,并且将构造函数指向新的对象。
- 拿到构造函数最后返回的结果,判断是否是对象或者函数,如果是的话,则直接
返回。如果不是则返回新创建的对象。
### 数组的新方法
- Array.from() - 用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map)
- Array.of() - 用于将一组值,转换为数组
- includes()
- flat() - 扁平化数组
### 对象的新方法
- object.is() - 比较两个值是否相等 其运算逻辑与 “===” 基本相似 但是有两个不同 object.is(+0,-0)//false 原生显 示true object.is(NaN,NaN) //true 原生显示false
- Object.assign() - 对象的合并,可以实现浅拷贝
- Object.keys() - 对象的键名
- Object.values() - 对象的键值
- Object.entries() - 回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值对数组
## **前端扩展知识**
### SPA单页面开发
- **优点:**
- 良好的交互体验
- 减轻服务器压力
- 用户体验好了很多
- **缺点:**
- 首屏加载慢
- 按需加载组件
- 尽量缩小包的大小
- SEO(搜索引擎优化)不友好,暴露给搜索引擎只有最基本的html结构;
- **解决方案:**
- SSR(服务端渲染):在后台将vue实例渲染为HTML字符串直接返回,在前端激活为交互程序;
- 客户端发送请求给服务器;
- 服务器读取模板,解析成dom节点,返回一个完整的首屏html结构;
- 客户端进行首屏激活;
- 之后的客户端的页面跳转不再向服务端发请求,只是使用前端路由跳转,只会发送一些ajax请求数据;
### 前端如何调试
> 普通浏览器调试方法:浏览器的检查界面进行调试。
- 最常见的就是console来调试代码中的打印内容;
- Network进行网络请求的调试,js、css文件,图片,http/https都可以查看到;
- 在一行代码的前面或者后面写上debugger,代码运行到这的时候就会停止,然后在sources目录下点击左边的行数即可;
> Postman进行接口请求的调试
> 用浏览器的vue-devtools拓展工具进行VUEX的调试
### 前端的优化
- 尽量用语义化标签,浏览器好显示;
- 可以将共有的样式属性封装成为文件,然后导入各个模块组件内进行使用;
- 使用事件委托来取代大量事件的绑定;
- 使用节流、防抖;
- 在VUE中,如果这个组件只需要请求一次,那么可以用`<keep-alive>`来包裹起来;
- 尽量少点进行HTTP的请求;
### webpack优化
- 将体积比较小的图片转换成base64格式,因为base64格式的字符串体积会比图片大,所以尽量只转换体积小的图片;
- 用webpack进行css压缩;
- 在webpack生产模式下,css文件可以压缩,用css-minimizer-webpack-plugin插件。webpack生产模式默认压缩HTML和JS,所以不需要配置;
```javascript
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
{
plugins: [new CssMinimizerPlugin()],
}
```
## **浏览器知识**
### 同源策略
> 同源策略是浏览器一个重要的安全策略,一个网址是由协议、域名、端口组成,如果都相同说明同源,有一个不相等就不同源。
### 跨域
> 解决跨域就是为了解决同源策略不同源的问题
- JSONP
- 利用 `<script>` 标签没有跨域限制的漏洞,网页可以得到从其他来源动态产生的 JSON 数据。JSONP请求一定需要对方的服务器做支持才可以。
**缺点:** 仅支持get方法具有局限性,不安全可能会遭受XSS攻击。
- Cors
- 实现 CORS 通信的关键是后端。只要服务端设置 Access-Control-Allow-Origin ,就实现了跨域。
- websocket
- Websocket是HTML5的一个持久化的协议
### 浏览器输入URL到显示页面都发生了什么
#### DNS域名解析
> 你输入的网址并不是百度真实的地址,互联网中每一台机器都有唯一标识的ip地址。网址和ip地址的转换,就是DNS解析
- DNS服务器:
- 根DNS服务器:返回顶级DNS服务器ip地址,比如(.com、.cn、.net....)
- 顶级DNS服务器:返回权威DNS服务器ip地址
- 权威DNS服务器:返回相应主机ip地址
- DNS服务器查找过程:客户端->浏览器缓存->本地hosts文件->本地DNS解析器缓存->本地DNS服务器->客户端->.....
- 本地DNS服务器找到:在客户端浏览器输入url网址后,在递归查找找到ip地址,任何一个步骤找到都会结束查找过程
- 本地DNS服务器找不到:根据本地DNS服务器设置的转发器进行查询,迭代查找。
#### 建立TCP连接
- 首先判断是否是https,若协议是https则作加密处理:HTTPS=HTTP+SSL/TLS 加密+认证+完整性保护
- > 三次握手建立TCP连接:ACK(此标志表示应答域有效)、SYN(在连接建立时用来同步序号)、FIN(用来释放一个连接)
- 第一次握手:就相当于客户端拿着SYN=1,seq=x去找服务端;
- 第二次握手:服务器收到后,就会拿着SYN=1,ACK=1,seq=y,ack=x+1
- 第三次握手:客户端收到服务器传过来的暗号后,就会拿着ACK=1,seq=x+1,ack=y+1
#### 发送HTTP请求
- 请求的方式:get或者post
- get和post区别:
- 语义:get获取,post传输。
- 安全:get不安全,post安全。
- 长度:get限制长度(4k),post理论上不限制的(2M)。
- 传输数据:get通过地址栏,post通过send和设置请求头。
- 缓存:get有缓存,post没有缓存
- 请求头:报文头包含若干个属性,格式为“属性名:属性值”,服务端据此获取客户端的信息
- 请求体:传递的请求参数
#### 服务器处理并返回HTTP报文
- HTTP状态码
- 1xx:指示信息–表示请求已接收,继续处理。
- 2xx:成功–表示请求已被成功接收、理解、接受。
- 200:成功
- 3xx:重定向–要完成请求必须进行更进一步的操作。
- 301 被请求的资源已永久移动到新位置
- 304:没有修改,缓冲中取
- 4xx:客户端错误–请求有语法错误或请求无法实现。
- 403 服务器已经理解请求,但是拒绝执行它。
- 404 没有找到
- 5xx:服务器端错误–服务器未能实现合法的请求
- 响应头:多个属性组成
- 响应体:这个请求服务器返回回来的数据
#### 浏览器解析渲染页面
- 浏览器解析HTML,构建DOM树
- 浏览器解析css,构建CSS Rule Tree规则树
- 解析完成后,浏览器引擎根据DOM树和CSS规则构造Render Tree(不包括Header、display:none)
- 根据Render Tree布局layout:计算每个节点在屏幕上的位置
- 绘制页面
#### 关闭TCP连接
- > 四次挥手的整个过程
- 第一次挥手:客户端携带FIN=1,seq=u来找服务器;
- 第二次挥手:服务器带着ACK=1,seq=v,ack=u+1返回给客户端,
- 第三次挥手:服务器带着FIN=1,seq=w,ack=u+1返回给客户端,
- 第四次挥手:客户端携带ACK=1,seq=u+1,ack=w+1给服务器;
- **TCP连接和TCP断开次数不同的原因是:ACK、FIN 不会同时发送**
### HTTP缓存
> HTTP缓存分为强缓存 和 协商缓存 ,这两种缓存策略都是服务端设置HTTP Header来实现的
- 强制缓存:请求的资源本地缓存中有,资源从本地缓存获取,不需要发起请求;
- Expires:服务端在响应头中设置一个 GMT 格式的到期时间。客户端的本地时间小于响应头的 Expires 时间,那么会从本地进行读取,不会去请求服务器。如果超过了,那么就去请求服务器去获取最新资源。
**缺点:**
- 因为根据本地时间进行判断,而电脑本地时间是随意可以改变的,会和服务器时间有偏差,所以有漏洞。
- Cache-control:max-age属性可以设置强缓存时间周期,在这个周期内。将直接从客户端缓存获取资源,而不会去向服务器发送请求;
- 协商缓存:协商缓存表示在使用本地的缓存之前,会先向服务器发一个请求,与服务器协商当前浏览器的缓存是否已经过期了;
- etag配合If-None-Match:通过请求头If-None-Match和响应头etag对比来判断是否过期;
- last-modified配合If-Modified-Since:通过请求头If-Modified-Since和响应头last-modified对比来判断是否过期;
## **WebPack**
> npm i webpack webpack-cli webpack-dev-server -D
> webpack文件命名必须为webpack.config.js,并且必须和package.json同级。配置完成后可以用npx webpack来进行打包
### webpack核心概念:
![img](https://img-blog.csdnimg.cn/7368945709904e3399b1fb9686e9a9b9.png)
- entry(入口):指示webpack从哪个文件开始打包
- output(输出):指示webpack打包完成之后的文件输出到哪里去,如何命名;
- 里面有个path属性
```javascript
output:{
// __dirname 是 node.js的变量,代表当前文件的文件夹目录
path: path.resolove(__dirname,"dist"),
// 入口文件打包输出文件名位置
filename: "static/js/main.js",
// true表示每次打包前会删除上一次打包后的文件
clean: true
}
```
- module(加载器): webpack本身其实只能处理js、json资源,想要处理其他资源就要在module中配置loader;
> css配置:
> 为了将css单独封装成为一个文件可以运用mini-css-extract-plugin这个插件,然后在webpack.config.js中写入下面配置;(https://webpack.docschina.org/plugins/mini-css-extract-plugin)
```javascript
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
plugins: [new MiniCssExtractPlugin()],
module: {
rules: [
{
test: /\.css$/i,
use: [MiniCssExtractPlugin.loader, "css-loader"],
},
],
},
plugins:[
new MiniCssExtractPlugin({
filename:"" // 决定了输出的每个 CSS 文件的名称
})
]
};
```
> 图片配置:
```javascript
{
test: /\.(png|jpg?e|gif|webp|svg)$/,
type: 'asset', // 需要转换为base64格式的用asset,不需要转换的用asset/resource
parser: {
dataUrlCondition: {
maxSize: 10 * 1024 // 小于10kb时会转换成base64
}
},
// 输出图片的路径及名称
generator: {
// hash:10:代表base64值只取前十位
filename: 'static/images/[hash:10][ext][query]'
}
}
```
- devServer:来写服务器的一些属性
```javascript
devServer:{
host:'localhost', // 启动服务器域名
port:'3000', // 启动服务器端口号
open: true, // 是否自动打开浏览器
hot:false // 关闭HMR
}
```
- plugins(插件):扩展Webpack的功能;
- mode(模式):
- 开发模式:development
- 生产模式:production
### 兼容性问题
> babel:用来解决JS的兼容性问题,其实就是JavaScript编译器,主要是用来将ES6语法编写代码转化为JavaScript语法,以便运行在当前或者旧的版本的浏览器中;
- 在webpack.comfig.js 中的module中配置babel-loader
```javascript
{
test: /\.js$/,
exclude:"/node_modules", // 排除node_modules中的js文件
loader:"babel-loader",
}
```
- 然后在与webpack.config.js同级下新建babel.config.js来添加下面智能预设
```javascript
module.exports = {
// 智能预设,能够编译ES6,
presets:['@bable/preset-env']
}
```
>core.js来解决Prmise、async、等一些更高级的语法
我们只需要直接在main.js中引入core-js,我们也可以按需引入
> 下载依赖包:postcss-loader、postcss、postcss-preset-env
- 然后在module的css配置中加上属性(https://webpack.docschina.org/loaders/postcss-loader/#root)
```javascript
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
[
'postcss-preset-env',
{
// 其他选项
},
],
],
},
},
}
```
- 为了解决CSS的兼容性问题,可以在package.json中加上属性'browserslist':[],在里面添加浏览器的兼容;
- 在webpack生产模式下,css文件可以压缩,用css-minimizer-webpack-plugin插件。webpack生产模式默认压缩HTML和JS,所以不需要配置;
```javascript
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
{
plugins: [new CssMinimizerPlugin()],
}
```
### SourceMap
> 因为浏览器提示的错误是构建后代码的错误位置,所以要用到SourceMap;
> SourceMap(源代码映射):它会生成一个.map文件,里面包含源代码和构建后代码的每一行、每一列的映射关系,当构建后的代码出错了,它就会通过.map文件来找出源代码对应的出错位置,
> **作用是为了让报错提示显示的是打包前的代码文件和行数;**
> 开发环境:cheap-module-source-map
- 优点:打包编译速度快,只包含行映射
- 缺点:没有列映射
```javascript
module.exports={
mode:"development",
devtool:"cheap-module-source-map"
}
```
> 生产环境:source-map
- 优点:包含行/列映射
- 打包编译速度慢
```javascript
module.exports={
mode:"production",
devtool:"source-map"
}
```
### 提升打包构建速度
- oneOf:每个文件只能被其中一个loader配置处理
```javascript
module:{
rules:[
{
oneOf:[
// 所有的loader配置
]
}
]
}
```
- 可以运用babel缓存和EsLint缓存来提升打包速度
```javascript
{
test: /\.js$/,
// exclude:"/node_modules", // 排除node_modules中的js文件
include:path.resolve(__dirname,"../src") // 只处理src下的文件,其他文件不做处理
loader:"babel-loader",
options:{
cacheDirectory:true, // 开启bable缓存
cacheCompression:false // 关闭缓存文件压缩
}
}
```
```javascript
plugins:[
new ESLintPlugin({
eontext:path.resolve(__dirname,"../src"),
exclude:"node_modules", // 默认值
cachLooation:path.resolve(
__dirname,
"../node_modules/.cache/eslintcache" // EsLint缓存地址
)
})
]
```
## **Vue基础知识**
### Vue3和Vue2的区别
> vue2使用的是webpack形式去构建项目;vue3使用vite构建项目
> Vue3新增加的特性:
- 组合式API---setup
- setup比beforeCreate、created生命周期更早;setup语法中可接收2个参数setup(props,context),其中的context中的attrs,slots,emit等同于Vue2中的`this.$attrs,this.$slots,this.$emit`
- ref和reactive创建响应式数据
- 在VUE2中所有的响应式数据都要写在data函数中;而在VUE3中,所有的响应式数据必须用ref或reactive来定义;但也必须return出去,reactive定义的对象要用...Refs;
- Teleport标签
- 在html文件中定义一个容器,然后在子组件中写入Teleport标签,这时候就说明Teleport标签里面的东西直接渲染到html文件中;
- style中使用变量
```javascript
<script setup>
const theme = {
color: 'red'
}
</script>
<template>
<p>hello</p>
</template>
<style scoped>
p {
color: v-bind('theme.color');
}
</style>
```
> 与Vue2的差异:
- 双向数据绑定原理
- v-if和v-for的优先级
- 生命周期
- this使用
- typescript支持
- > **双向数据绑定原理发生了改变**
- Vue2中运用的是Object.definepropert() 对各个属性的getter和setter进行劫持;
**缺点 :**
- 当对象中属性过多时Object.defineProperty()需针对每个属性进行遍历实现响应式,效率不高;
- 新增属性、删除属性, 界面不会更新;
- 只有configurable为true时候,该属性才能从对应的对象上被删除,但源数据不会响应删除;
- Vue3是通过Proxy(代理)来拦截对象中任意属性的变化;通过Reflect(反射): 对源对象的属性进行操作。
- 属性值的读写、属性的添加、属性的删除都可以实现。
- Proxy(target, handler)
-- target:要使用 Proxy 包装的目标对象,此处为person源数据;
-- handler:一个对象。可以只传一个空对象,也能实现增删改查操作
- 区别:
- **Object.defineProperty 只能劫持对象的属性,因此我们需要对每个对象的每个属性进行遍历。Vue 2里,是通过 递归 + 遍历 data 对象来实现对数据的监控的**
- **Proxy 可以劫持整个对象,并返回一个新的对象。Proxy 不仅可以代理对象,还可以代理数组。还可以代理动态增加的属性。**
```javascript
<script>
// 源数据
let person = {
name: '张三',
age: 18
}
Proxy和Reflect是window上内置的函数
let p = new Proxy(person, {
get(target, propName) {
console.log(`有人读取了p身上的${propName}属性`)
return Reflect.get(target, propName)
},
set(target, propName, value) {
console.log(`有人新增/修改了p身上的${propName}属性,我要去更新界面了!`)
Reflect.set(target, propName, value)
},
deleteProperty(target, propName) {
console.log(`有人删除了p身上的${propName}属性,我要去更新界面了!`)
return Reflect.deleteProperty(target, prop)
}
})
</script>
```
- Reflect函数身上的一些方法与Object相同,都有get()、set()、deleteProperty(),当然Reflect函数身上还有Reflect.defineProperty()方法,但是和Object.defineProperty()不同的是:
-- Object.defineProperty对同一个对象同一个属性重复操作时,系统会报错代码运行不下去,但Reflect.defineProperty不会报错只会运行第一条结果并继续执行后边代码
-- Reflect.defineProperty拥有返回值,且是一个布尔值;不用再用try...catch捕获结果;
- > **v-if和v-for的优先级**
- 在vue2中:当v-if和v-for同时使用时,v-for的优先级高于v-if(因此我们通常需要计算属性先对数据进行加工处理,以达到性能优化的目的)
- 在vue3中:当v-if和v-for同时使用时,v-if的优先级高于v-for
- > **生命周期:**
- VUE2             VUE3
- beforeCreate         setup()
- created             setup()
- beforeMount         onBeforeMount
- mounted             onMounted
- beforeUpdate         onBeforeUpdate
- updated             onUpdated
- beforeDestroy        onBeforeUnmount
- destroyed          onUnmounted
- activated          onActivated
- deactivated          onDeactivated
- > **this使用**
- vue2中:无时无刻都要使用this
- vue3中:因为setup函数的存在,所有的props、data等都不需要用this进行访问(vue3对vue2绝大多数是兼容的,如果你用了vue2相关的东西,那你还是需要像vue2一样书写)
- > **typescript支持**
- vue2中:默认是不支持typescript的
- vue3中:支持使用typescript
### MVVM和MVC区别
> MVVM包括view视图层、model数据层、viewmodel层;view视图层和viewmodel层可以相互传递,model数据层和viewmodel层可以相互传递;这样就相当于view视图层和model数据层可以相互传递;
> MVC是包括view视图层、controller控制层、model数据层。各部分之间的通信都是单向的。
### 计算属性、方法、监听器
> 计算属性:可以根据data中的数据成员,动态计算出一个新的数据成员(这个数据成员在data中并不存在),计算属性的函数必须**有返回值,有缓存;**
> 方法:计算属性与方法最大的区别是计算属性有缓存,方法**没有缓存**。
> 监视器:可以监视data中某一个数据成员的改变或路由中的某些属性的改变,可以根据这个改变,做一些其他操作(不仅仅局限于更新其他相关数据),**没有返回值**
- 深度监听
- vue中提供了在watch监听时设置deep:true 就可以实现对对象的深度监听
- 也可以利用计算属性配合watch实现单个属性的深度监听
### V-if、V-show的区别
- v-show一定会把Dom渲染出来,而V-if不一定会把DOM渲染出来,V-show是用CSS:display:none来实现显示和隐藏;
- v-if是用创建和销毁来实现显示和隐藏
### 组件通信方法
> $emit/props
父组件通过props方式向子组件传递
子组件通过$emit触发父组件中v-on绑定的自定义事件
> $parent/$children和ref
ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例
$parent / $children:访问父 / 所有子实例
> eventBus
新建一个Vue事件bus对象,然后通过 bus.$emit 触发事件, bus.$on 监听触发的事件
在需要用到的两个不同组件分别引入即可
需要注意的是接受数据的组件要清除事件总线eventBus
> 依赖注入provide和inject
provide:是一个对象,或者是一个返回对象的函数。里面呢就包含要给子孙后代的东西,也就是属性和属性值。
inject:一个字符串数组,或者是一个对象。属性值可以是一个对象,包含from和default默认值。
> VueX和pinia
### VUEX
- state:存储公共共享数据
- moutation:修改公共共享数据
- actions:处理异步任务
- getters:对state中的数据进行封装并返回,类似于计算属性
- modules:类似于组件
> 获取state中数据的方法:
- 在其他组件中通过this.$store.state.'属性名'
- 在组件中通过import{mapState} from VUEX 导入,将它挂载到当前组件的计算属性上...mapState(['属性名'])修改state中数据的方法:
> 同步修改state中数据的方法:
- 通过this.$store.commit('函数名')
- 在组件中通过import{mapMutation} from VUEX 导入,将它挂载到当前组件的计算属性上...mapMutation(['函数名'])
> 异步修改公共共享数据:
- this.$store.dispatch('moutation中函数名')
- 在组件中通过import{mapActions} from VUEX 导入,将它挂载到当前组件的计算属性上...mapActions(['函数名'])
> getters:在其他组件中通过this.$store.getters.'属性名'
### pinia(菠萝)
> Pinia 是Vue的轻量级状态管理库,它其实和VueX差不多,但是比VueX更加轻量;
- **它与VueX的区别:**
- 与在 Vuex 中添加 TypeScript 相比,添加 TypeScript 更容易;
- 极其轻巧
- 取消了VueX中的mutations;可以直接用actions的$patch来修改数据;
### Vue-Router
#### router和route的区别
> router为VueRouter的实例,相当于一个全局的路由器对象,里面含有很多属性和子对象,例如history对象。。。经常用的跳转链接就可以用`this.$router.push`,和router-link跳转一样。`this.$router.push`会往history栈中添加一个新的记录
> route相当于当前正在跳转的路由对象。。可以从里面获取name,path,params,query等。
#### router传参
- 可以手写完整的path:
```javascript
this.$router.push({path:`/user/${userId}`})
```
用this.$route.params.userId来获取
- 也可以用params传递:
```javascript
this.$router.push({name:'user',params:{id:1}})
```
用this.$route.params来获取
- 也可以用query传递:
```javascript
this.$router.push({path:'user',query:{id:1}})
```
用this.$route.query来获取
#### 动态路由
- 在某些情况下,一个页面的path路径可能是不确定的,我们希望在进入用户界面的时候路径后面能带上我们的参数;这时候就要用到动态路由;
#### 路由懒加载
- 在ES6中,我们可以有更加简单的写法来组织Vue异步组件和Webpack的代码分割
```javascript
const Home = () => import(' . ./ components/Home.vue ' )
```
#### 嵌套路由实现
就是在router创建对象的配置中加上children数组
#### 导航守卫的使用
> 全局守卫(全局前置守卫、全局解析守卫、全局后置钩子)
- **router.beforeEach**:全局前置守卫;在跳转路由前执行
- to:去哪个页面;
- from:从哪个页面来;
- next:是否放行;
- **router.beforeResolve**:全局解析守卫,目前看来和beforeEach()没有区别,都可以做同样的操作;
- **router.afterEach**:全局后置钩子,这个router方法是在路由进入之后触发,并且少了一个参数next(),不需要调用next进入
- to:去哪个页面;
- from:从哪个页面来;
> 路由守卫:路由独享守卫只在进入路由时触发,参数改变时不会触发,它们只有在从一个不同的路由导航时,才会被触发
- **beforeEnter**:写在router配置中
```javascript
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
})
```
> 组件内守卫:和生命周期冲突,所以平时不用;
## **Vue原理整理**
### Vue的响应式原理
> 当创建 Vue 实例时,vue 会遍历 data 选项的属性,利用 Object.defineProperty 为属性添加 getter 和 setter 对数据的读取进行劫持(getter 用来依赖收集,setter 用来派发更新),并且在内部追踪依赖,在属性被访问和修改时通知变化。
- Object.defineProperty 的缺点:
- 深度监听需要递归到底,一次性计算量大
- 无法监听新增属性、删除属性(要使用 Vue.set Vue.delete)
- Proxy 可以劫持整个对象,并返回一个新的对象。Proxy 不仅可以代理对象,还可以代理数组。还可以代理动态增加的属性。
### computed(计算属性) 的实现原理
> computed 本质是一个惰性求值的观察者,它的内部实现了一个惰性的 watcher,也就是computed.watcher,他不会立刻去求值,它里面会有一个dep实例,dep有个属性叫做this.dirty,用它来标记computed是否要重新求值;
> 当computed发生改变时,就回去通知这个watcher,然后watcher会去用dep判断有没有订阅者;
- 有订阅者,就会重新计算,然后替代旧值重新渲染;
- 没有订阅者,将this.dirty=true
### Vue 组件 data 为什么必须是函数?
- new Vue()实例中,data 可以直接是一个对象,为什么在 vue 组件中,data 必须是一个函数呢?
> 因为组件是可以复用的,JS 里对象是引用关系,如果组件 data 是一个对象,那么子组件中的 data 属性值会互相污染,产生副作用。所以一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝。
> new Vue 的实例是不会被复用的,因此不存在以上问题。
### Vue单向数据流怎么理解
> 父组件的数据流向子组件中,但子组件不能将这个数据修改掉;
- 如果要早子组件中修改父组件里面的数据,需要用到$emit方法来触发父组件里面的方法来修改这个数据,然后重新传递给子组件;
### 为什么可以用this来调用data中的属性?
- Vue方法方法创建时,执行initMixin 给原型上绑定了_init方法
- new Vue实例时,执行_init方法
- 执行了_init方法,执行initState
- 如果实例化Vue时,data存在,执行initData方法
- proxy代理,给vm.key 做一层代理,返回实际vm['_data'][key]
**所以在vue中通过this. 方式访问data中的数据时,实际上是返回了this._data中的数据**
### 虚拟DOM和diff算法
**虚拟dom本质上就是js和dom之间做了一个缓存,js只操作虚拟dom,最终把变更写入到真实的dom中,如果有大量,频繁的数据更新,能够对视图进行合理,高效的更新。**
> 虚拟DOM是对DOM的js抽象表示,用js对象来描述DOM的层次,是以js对象作为基础的树
> VNode在Vue的整个虚拟DOM过程起着在视图渲染之前,把写好的template模板先编译成VNode并缓存下来
- 减少了DOM的操作,优化了性能(主要是运用了diff算法)
- diff算法就是虚拟DOM和真是DOM有不同的时候,不用全部渲染,只需要渲染修改的节点就可以了;
## **Axios**
### 什么是Axios
> axios是一个基于Promise的http请求库,可用于浏览器和 Node。可以说是目前最为常用的http库;
### Axios的内部原理
- Axios同时支持浏览器端和服务端的请求;
- 通过 XMLHttpRequest 和 process 来判断是浏览器环境还是node环境,从而在不同的环境提供不同的http请求模块,实现客户端和服务端程序的兼容。
- XMLHttpRequest 是浏览器内置的一个对象,它为客户端提供了在客户端和服务器之间传输数据的功能。
- process 对象是node内置的一个全局变量,提供有关信息,控制当前 Node.js 进程。
### Axios拦截器
- axios库对外暴露了一个axios实例,axios实例其中挂载了一个Axios方法,Axios方法有一个interceptors对象(拦截器),interceptors对象有request对象(请求)和response对象(响应)(其实是一个用来管理拦截器的数组(handlers)),并且request对象和response对象都有use方法,所以,我们可以调用axios.interceptors.request.use()和axios.interceptors.response.use()。
- 当我们调用请求拦截器和响应拦截器的时候,就会在拦截器数组(handlers)里面push一个成功回调和一个失败回调