2022前端面试题整理

123 阅读22分钟

1、HTML

1.1、html 行内标签和块级元素

  • 行内元素(span,a,img,input,strong)
  • 块级元素(div,p,h1...h6,ol,ul,table)

2、CSS

2.1、如何让盒子实现垂直水平居中?

  • absolute + 负margin
  • absolute + margin auto
  • absolute + calc
  • absolute + transform
  • flex
  • css-table

仅居中元素定宽高适用

  1. absolute + 负margin

    //box1父盒子,box2子盒子
    //绝对定位50%,子元素左上角居中,减去子元素自身一半修正位置
    .box1 {
        position: relative;
    }
    .box2 {
        position: absolute;;
        top: 50%;
        left: 50%;
        margin-left: -50px;
        margin-top: -50px;
    }
    
  2. absolute + margin auto

    //所有方向距离为0margin设置auto
    .box1 {
        position: relative;
    }
    .box2 {
        position: absolute;;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        margin: auto;
    }
    
  3. absolute + calc(calc,css3计算)

    //css3计算属性,基于父元素移动50%,在减去自身宽高的一半
    .box1 {
        position: relative;
    }
    .box2 {
        position: absolute;;
        top: calc(50% - 50px);
        left: calc(50% - 50px);
    }
    

居中元素不定宽高

  1. absolute + transform

    //基于父元素移动50%,通过transform:translate:(50%, 50%)修正位置
    .box1 {
        position: relative;
    }
    .box2 {
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
    }
    
  2. flex

    .box1 {
        display: flex;
        justify-content: center;
        align-items: center;
    }
    
  3. css-table

    //css新增的table属性,可以让我们把普通元素,变为table元素的现实效果
    .box1{
        display: table-cell;
        text-align: center;
        vertical-align: middle;
    }
    .box2 {
        display: inline-block;
    }
    

2.2、圣杯布局、双飞翼?

最终效果类似,left 和 right 的宽度是固定的,center宽度自适应

圣杯布局

  • content 设置一个padding:0 200px,将左右两边各腾出200px宽度。

  • left设置margin:-100%,right设置margin;-200px

  • 最后通过绝对定位,移动到合适位置,left:-200px;left:200px

    #content{
        overflow: hidden;
        padding: 0 200px;//给两边盒子留位置
    }
    #left{
        margin-left:-100% ;//向左移动父元素100%距离,和父元素左边对齐
        position: relative;
        left:-200px;
    } 
    #right{
        margin-left: -200px;//向左移动自身宽度距离,和父元素右边对其
        position: relative;
        left:200px; 
    }
    

2.3、BFC

BFC概念

也叫【块级格式化上下文】,简单来说,BFC 是一个完全独立的空间(布局环境),让空间里的子元素不会影响到外面的布局。

怎样触发BFC

  • float:left / right
  • position:absoulte / fixed
  • display: inline-block / table-cell / flex
  • overflow: hidden / auto / scroll

BFC解决了什么问题

  1. 阻止margin重叠
  2. 可以包含浮动元素 —— 清除内部浮动
  3. 自适应两栏布局
  4. 可以阻止元素被浮动元素覆盖

2.4、优先级

!important > 内联 > ID选择器 > 类选择器 > 标签选择器。

  • 每个选择器都有权值,权值越大越优先
  • 继承的样式优先级低于自身指定样式
  • 权值相同时,靠近元素的样式优先级高 顺序为内联样式表(标签内部)> 内部样式表(当前文件中)> 外部样式表(外部文件中)

2.5、盒子模型

  • IE盒模型:属性 widthheight包含 contentborderpadding,指的是 content + padding + border
  • 标准盒模型:属性 widthheight 只包含内容 content,不包含 borderpadding

切换盒子模型

//W3C盒子模型
box-sizing: content-box 
//IE盒子模型
box-sizing: border-box 

3、HTML5

3.1、语义化标签

标签描述
header页眉
footer页脚
nav导航链接
main主内容
article文章
dialog对话框(弹窗)

语义化的优点有:

  • 代码结构清晰,易于阅读,利于开发和维护
  • 方便其他设备解析(如屏幕阅读器)根据语义渲染网页。
  • 有利于搜索引擎优化(SEO)

3.2、本地储存

Cookie / sessionStorage / localStorage

共同点:

  • 都是保存在浏览器端,且同源的

区别:

  1. cookie数据始终在同源的http请求中携带,即cookie在浏览器和服务器间来回传递。而sessionStorage和localStorage不会自动把数据发给服务器,仅在本地保存。

  2. cookie数据不能超过4k(适合保存小数据)。 sessionStorage和localStorage容量较大

  3. 数据有效期也不同

    • sessionStorage:仅在当前浏览器窗口关闭前有效
    • localStorage:始终有效,窗口或浏览器关闭也一直保存,需手动清除
    • cookie只在设置的cookie过期时间之前一直有效,即使窗口或浏览器关闭
  4. 作用域也不同

    • sessionStorage 不在不同的浏览器窗口中共享
    • localStorage 在所有同源窗口中都是共享的;
    • cookie 也是在所有同源窗口中都是共享的。

应用场景

  • localStorage:常用于长期登录(+判断用户是否已登录),适合长期保存在本地的数据
  • sessionStorage :敏感账号一次性登录
  • cookies与服务器交互

4、CSS3

4.1、CSS3的新特性

css3新特性描述
box-shadow盒子阴影
border-radius圆角
box-sizing盒子模型
linear-gradient, radial-gradient渐变
transition过渡
animate动画
transform2D 转换/3D 转换
iconfont字体图标
flex弹性布局

4.2、flex常见属性

flex常见属性描述
* flex-direction设置容器主轴的方向
* flex-wrap用于设置容器盛不下,是否换行(不 / 向上 / 向下)
* justify-content用于设置容器中的内容对齐方式
* align-items用于设置容器整体对齐方式
align-content定义了多根轴线的对齐方式。如果项目只有一根轴线,该属性不起作用
flew-flow是flex-deriction和flex-wrap属性的简写,默认值为[row nowrap]

5、JS

5.1、var let const 的区别?

  1. const 定义常量,不可以重复赋值
  2. var 有变量提升,可以重复声明。let 不存在变量提升,不可以重复声明
  3. var 全局作用域或块级作用域,let const 块级作用域

5.2、javascript 有哪些基础数据类型?

  • 基础数据类型Undefined、Null、String、Number、Boolean、Symbol、BigInt

  • Symbol、BigInt是ES6之后新增的

    • Symbol 指的是独一无二的值
    • BigInt 是一种数字类型的数据,它可以表示任意精度格式的整数

5.3、判断数据类型的方法有哪些,有什么区别

判断数据类型的方法描述
typeof判断基础数据类型(数组、对象、null都会被判断为object)
instanceof判断引用数据类型,不能判断基本数据类型
constructor判断数据的类型
Object.prototype.toString.call()使用 Object 对象的原型方法 toString 来判断数据类型

5.4、== 与 === 的区别?

  • 双等号(==)进行相等判断时,如果两边的类型不一致,会强制类型转化再进行比较。
  • 三等号(===)进行相等判断时,如果两边的类型不一致,不强制类型准换,直接返回 false。

5.5、js中那些数据在 if 判断时是 false

  • 0、false、null、undefined、“”、NaN 判断为false
  • 其他皆为true

5.6、call、apply、bind

  • call() 和apply()的第一个参数相同,就是指定的对象。这个对象就是该函数的执行上下文。
  • call()和apply()的区别就在于,两者之间的参数。
  • call()在第一个参数之后的 后续所有参数就是传入该函数的值。
  • apply() 只有两个参数,第一个是对象,第二个是数组,这个数组就是该函数的参数。 bind() 方法和前两者不同在于: bind() 方法会返回执行上下文被改变的函数而不会立即执行,而前两者是 直接执行该函数。他的参数和call()相同。

5.7、原型、原型链?

原型:

所有对象,都有一个隐式引用,它被称之为这个对象的 prototype 原型。当访问对对象的某个属性时,会先在对象本身的属性上寻找,没找到就会去他的原型对象上寻找。

原型链

当访问一个对象的属性时,如果这个对象内部不存在这个属性,那么它就会去它的原型对象里找这个属性,这个原型对象又会有自己的原型,于是就这样一直找下去,也就是原型链的概念。原型链的尽头一般来说都是 Object.prototype 所以这就是新建的对象为什么能够使用 toString() 等方法的原因。

5.8、闭包、闭包的使用场景

闭包的概念:

闭包简单理解就是内嵌函数,也即在函数中嵌套函数

作用:函数内部调用外部变量、构造函数的私有属性、延长变量生命周期

闭包的优缺点

  • 优点:延长变量生命周期、私有化变量
  • 缺点:过多的闭包可能会导致内存泄漏

闭包的应用场景

  • ajax请求的成功回调
  • 事件绑定的回调方法
  • setTimeout的延时回调
  • 函数内部返回另一个匿名函数
  • 函数节流、防抖

5.9、箭头函数和普通函数的区别

  • 箭头函数比普通函数更加简洁
  • 箭头函数没有自己的this
  • 箭头函数继承来的this指向永远不会改变
  • 箭头函数不能作为构造函数使用
  • 箭头函数没有自己的arguments
  • 箭头函数没有prototype
  • call()、apply()、bind()等方法不能改变箭头函数中this的指向

5.10、防抖和节流

  • 防抖:在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时
  • 节流:规定在一个单位时间内,只能触发一次函数

防抖应用场景

  • 搜索框搜索输入。只需用户最后一次输入完,再发送请求
  • 手机号、邮箱验证输入检测 onchange / oninput事件
  • 窗口大小计算。只需窗口调整完成后,计算窗口大小。防止重复渲染。

节流应用场景

  • 懒加载、滚动加载、加载更多或监听滚动条位置;
  • 百度搜索框,搜索联想功能;
  • 防止高频点击提交,防止表单重复提交;

5.11、回流和重绘

回流(重排)

  • 定义:

    • DOM的变化影响了元素的几何信息(DOM对象的位置和尺寸大小),浏览器需要重新计算元素的几何属性,将其安放在界面中的正确位置,这个过程叫做回流(重排)。
  • 触发:

    1. 页面初次渲染
    2. 浏览器窗口大小改变
    3. 元素尺寸、位置、内容发生改变
    4. 元素字体大小变化
    5. 添加或者删除可见的 dom 元素

重绘

  • 定义:

    • 当一个元素的外观发生改变,但没有改变布局,重新把元素外观绘制出来的过程,叫做重绘
  • 触发:

    • 改变元素的color、background、box-shadow等属性

回流(重排)优化建议

  1. 分离读写操作
  2. 样式集中修改
  3. 缓存需要修改的DOM元素
  4. 尽量只修改position:absolutefixed元素,对其他元素影响不大
  5. 动画开始GPU加速,translate使用3D变化

5.12、JS垃圾回收

有两种垃圾回收策略:

  1. 标记清除: 标记阶段即为所有活动对象做上标记,清除阶段则把没有标记(也就是非活动对象)销毁。
  2. 引用计数: 它把对象是否不再需要简化定义为对象有没有其他对象引用到它。如果没有引用指向该对象(引用计数为 0),对象将被垃圾回收机制回收。

5.13、事件循环Eventloop(宏任务、微任务)(笔试题)

Event Loop概念

即事件循环,是指浏览器的一种解决javaScript单线程运行时不会阻塞的一种机制,也就是我们经常使用异步的原理。

在一次事件循环中,会先执行js线程的主任务,然后会去查找是否有微任务,如果有那就优先执行微任务,如果没有,在去查找宏任务进行执行。

微任务

  • process.nextTick (Node)
  • promise
  • Object.observe
  • MutationObserver (浏览器)

宏任务

  • script
  • setTimeout
  • setInterval
  • setImmediate
  • 回调函数(事件、ajax等)

5.14、深拷贝、浅拷贝(笔试题)

  • 浅拷贝: 以赋值的形式拷贝引用对象,仍指向同一个地址,修改时原对象也会受到影响

    • Object.assign
    • 展开运算符 ...
  • 深拷贝: 完全拷贝一个新对象,修改时原对象不再受到任何影响

    • JSON.parse(JSON.stringify(obj)): 性能最快

      • 具有循环引用的对象时,报错
      • 当值为函数、undefined、或symbol时,无法拷贝
    • 递归进行逐一赋值

    • lodash工具库:cloneDeep

5.15、数组的方法

数组的方法描述
map遍历数组,返回回调返回值组成的新数组
forEach无法break,可以用try/catchthrow new Error来停止
filter过滤
some有一项返回true,则整体为`true
every有一项返回false,则整体为`false
join通过指定连接符生成字符串
push / pop末尾推入和弹出,改变原数组。push 返回数组长度, pop 返回原数组最后一项;
unshift / shift头部推入和弹出,改变原数组,unshift 返回数组长度,shift 返回原数组第一项 ;
sort(fn) / reverse排序与反转,改变原数组
concat连接数组,不影响原数组, 浅拷贝
slice(start, end)返回截断后的新数组,不改变原数组
splice(start, number, value...)返回删除元素组成的数组,value 为插入项,改变原数组
indexOf / lastIndexOf(value, fromIndex)查找数组项,返回对应的下标
reduce / reduceRight(fn(prev, cur), defaultPrev)两两执行,prev 为上次化简函数的return值,cur 为当前值 当传入 defaultPrev 时,从第一项开始; 当未传入时,则为第二项

数组乱序

var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
arr.sort(function () {
    return Math.random() - 0.5;
});
//输出结果:[4, 2, 10, 6, 9, 5, 1, 3, 8, 7]
//通过sort排序,用随机数和0.5比较大小,实现伪随机排序

5.16、promise、手写promise

function Promise(fn) {
  // Promise resolve时的回调函数集
  this.cbs = [];
​
  // 传递给Promise处理函数的resolve
  // 这里直接往实例上挂个data
  // 然后把onResolvedCallback数组里的函数依次执行一遍就可以
  const resolve = (value) => {
    // 注意promise的then函数需要异步执行
    setTimeout(() => {
      this.data = value;
      this.cbs.forEach((cb) => cb(value));
    });
  }
​
  // 执行用户传入的函数 
  // 并且把resolve方法交给用户执行
  fn(resolve);
}
Promise.prototype.then = function (onResolved) {
  // 这里叫做promise2
  return new Promise((resolve) => {
    this.cbs.push(() => {
      const res = onResolved(this.data);
      if (res instanceof Promise) {
        // resolve的权力被交给了user promise
        res.then(resolve);
      } else {
        // 如果是普通值 就直接resolve
        // 依次执行cbs里的函数 并且把值传递给cbs
        resolve(res);
      }
    });
  });
};

5.17、ES6新特性

  • let const
  • 块级作用域
  • 箭头函数
  • class
  • 模板字符串
  • 展开运算符
  • 结构赋值
  • 新增的数组API和对象API
  • promise
  • proxy
  • Symbol、bigInt
  • Set、Map/WeapMap

5.18、跨域、同源策略

同源策略: 是一种约定,它是浏览器最核心也最基本的安全功能,所谓同源是指:域名、协议、端口相同。

跨域: 是基于同源策略延伸出来的一种概念,所谓跨域是指:域名、协议、端口不一样,跨域只存在浏览器中

解决跨域

  • jsonp(利用script标签没有跨域限制的漏洞实现。缺点:只支持GET请求)
  • CORS(设置Access-Control-Allow-Origin:指定可访问资源的域名)
  • postMessage()(HTML5新增API ),通过onmessage监听 传递过来的数据
  • Websocket是HTML5的一个持久化的协议,也是跨域的一种解决方案。
  • proxy反向代理

日常工作中用的最对的跨域方案是CORS和proxy反向代理。

5.19、输入url地址到页面展示发生了什么?

  1. 浏览器的地址栏输入URL并按下回车。
  2. 浏览器查找当前URL是否存在缓存,并比较缓存是否过期。
  3. DNS解析URL对应的IP。
  4. 根据IP建立TCP连接(三次握手)。
  5. HTTP发起请求。
  6. 服务器处理请求,浏览器接收HTTP响应。
  7. 渲染页面,构建DOM树。
  8. 关闭TCP连接(四次挥手)

5.20、状态码

状态码状态类型描述例子
1xxInformational信息状态码接受请求正在处理
2xxSuccess成功状态码请求正常处理完毕200响应成功
3xxRedirection重定向状态码需要附加操作已完成请求301永久重定向 302临时重定向
4xxClient Error客户端错误状态码服务器无法处理请求403服务器禁止访问 404服务器资源未找到
5xxServer Error服务器错误状态码服务器处理请求出错500 502服务器内部错误 504 服务器繁忙

5.21、ES6新特性

  1. let const
  2. 字符串、数组、对象的方法扩展
  3. symbol、set、map新的数据类型和数据结构
  4. proxy代理拦截
  5. 异步解决方案:promise、generate,async、await
  6. class类
  7. module模块

5.22、异步编程的实现方式?

JavaScript中的异步机制可以分为以下几种:

  • 回调函数 的方式,使用回调函数的方式有一个缺点是,多个回调函数嵌套的时候会造成回调函数地狱,上下两层的回调函数间的代码耦合度太高,不利于代码的可维护。

Promise 的方式,使用 Promise 的方式可以将嵌套的回调函数作为链式调用。但是使用这种方法,有时会造成多个 then 的链式调用,可能会造成代码的语义不够明确。

generator 的方式,它可以在函数的执行过程中,将函数的执行权转移出去,在函数外部还可以将执行权转移回来。当遇到异步函数执行的时候,将函数执行权转移出去,当异步函数执行完毕时再将执行权给转移回来。因此在 generator 内部对于异步操作的方式,可以以同步的顺序来书写。使用这种方式需要考虑的问题是何时将函数的控制权转移回来,因此需要有一个自动执行 generator 的机制,比如说 co 模块等方式来实现 generator 的自动执行。

async 函数 的方式,async 函数是 generator 和 promise 实现的一个自动执行的语法糖,它内部自带执行器,当函数内部执行到一个 await 语句的时候,如果语句返回一个 promise 对象,那么函数将会等待 promise 对象的状态变为 resolve 后再继续向下执行。因此可以将异步逻辑,转化为同步的顺序来书写,并且这个函数可以自动执行。

6、VUE

6.1、双向数据绑定原理?

Vue的双向数据绑定是由数据劫持结合发布者订阅者实现的。 数据劫持是通过Object.defineProperty()来劫持对象数据的setter和getter操作。 在数据变动时作你想做的事

  • 原理 通过Observer来监听自己的model数据变化,通过Compile来解析编译模板指令,最终利用Watcher搭起Observer和Compile之间的通信桥梁,
  • 达到数据变化->视图更新 在初始化vue实例时,遍历data这个对象,给每一个键值对利用Object.definedProperty对data的键值对新增get和set方法,利用了事件监听DOM的机制,让视图去改变数据

6.2、vue生命周期

生命周期描述
beforeCreate初始化之前(数据拿不到)
created初始化之后(数据可以拿到)
beforeMount挂载之前(页面的元素看不到)
mounted挂载之后(页面元素可以看到)
beforeUpdate更新前(数据更新,页面未更新)
updated更新后(数据更新,页面更新)
beforeDestory销毁前
destoryed销毁后

常用的生命钩子

  • mounted(): 发送ajax请求, 启动定时器等异步任务
  • beforeDestory(): 做收尾工作, 如: 清除定时器

6.3、data为什么是一个函数?

  • 组件中的 data 写成一个函数,数据以函数返回值形式定义,这样每复用一次组件,就会返回一份新的 data,类似于给每个组件实例创建一个私有的数据空间,让各个组件实例维护各自的数据。
  • 而单纯的写成对象形式,就使得所有组件实例共用了一份 data,就会造成一个变了全都会变的结果

6.4、computed、watch的区别

  • computed 是计算属性,依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值;
  • watch 更多的是「观察」的作用,类似于某些数据的监听回调 ,每当监听的数据变化时都会执行回调进行后续操作;

应用场景

  • 当我们需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算;
  • 当我们需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许我们执行异步操作 ( 访问一个 API ),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。

6.5、组件通信的几种方式

  1. props 和 emit父向子传递数据通过props,子给父传递数据通过emit ---- 父向子传递数据通过 props,子给父传递数据通过emit 触发事件来做到
  2. eventBus ---- 兄弟组件数据传递 这种情况下可以使用事件总线的方式
  3. v-model 和 sync修饰符 ---- 实现父子组件双向数据同步(本质上还是自定义事件和props组合)
  4. parentparent 和 children ---- 获取当前组件的父组件和当前组件的子组件
  5. attrsattrs 和listeners ---- 就是父组件中给子组件传递的,所有属性组成的对象(排除props声明接收的属性 以及class style),及自定义事件方法组成的对象
  6. $refs ---- 可以直接操作子组件内部的数据及方法
  7. 作用域插槽 ---- 父向子传数据,子组件展示数据,数据的结构和样式由父组件决定
  8. vuex 状态管理
  9. PubSubJS ---- 消息的订阅与发布

6.6、v-if、v-show的区别

  • v-if 在编译过程中会被转化成三元表达式,条件不满足时不渲染此节点。
  • v-show 会被编译成指令,条件不满足时控制样式将对应节点隐藏 (display:none)

使用场景

  • v-if 适用于在运行时很少改变条件,不需要频繁切换条件的场景
  • v-show 适用于需要非常频繁切换条件的场景

6.7、v-model的实现原理

我们在 vue 项目中主要使用 v-model 指令在表单 input、textarea、select 等元素上创建双向数据绑定,我们知道 v-model 本质上不过是语法糖,v-model 在内部为不同的输入元素使用不同的属性并抛出不同的事件:

  • text 和 textarea 元素使用 value property 和 input 事件;
  • checkbox 和 radio 使用 checked property 和 change 事件;
  • select 字段将 value 作为 prop 并将 change 作为事件。

6.8、nextTick(了解)

在下次dom更新循环结束之后执行延迟回调,可用于获取更新后的dom状态

6.9、为什么v-for和v-if不建议用在一起

  1. 当 v-for 和 v-if 处于同一个节点时,v-for 的优先级比 v-if 更高,这意味着 v-if 将分别重复运行于每个 v-for 循环中。如果要遍历的数组很大,而真正要展示的数据很少时,这将造成很大的性能浪费(Vue2.x)
  2. 这种场景建议使用 computed,先对数据进行过滤

注意:3.x 版本中 v-if 总是优先于 v-for 生效。

6.10、v-for中key的作用

key 是为 Vue 中 vnode 的唯一标记,通过这个 key,我们的 diff 操作可以更准确、更快速。

  • 更准确:因为带 key 就不是就地复用了,在 sameNode 函数 a.key === b.key 对比中可以避免就地复用的情况。所以会更加准确。
  • 更快速:利用 key 的唯一性生成 map 对象来获取对应节点,比遍历方式更快

6.11、diff算法

  1. Diff 算法是同级比较,内部是深度优先遍历节点

  2. 判断节点是否同一个元素,

    1. 如果是对比属性对比孩子节点,
    2. 如果不是直接删除老的替换新的。
  3. Vue2采用了双指针对一些场景做了优化策略(静态节点可以直接掉过diff)对头头,尾尾,头尾,尾头进行了优化

  4. 最后根据老节点创造了一个映射表,用新的去里面找能复用就复用

6.12、vm.$set()

  • 由于 Vue 会在初始化实例时对属性执行 getter/setter 转化,所以属性必须在 data 对象上存在才能让 Vue 将它转换为响应式的。
  • 但是 Vue 提供了 Vue.set / vm.$set 来实现为对象添加响应式属性

如何实现的

  • 如果目标是数组,直接使用数组的 splice 方法触发相应式;
  • 如果目标是对象,会先判读属性是否存在、对象是否是响应式,最终如果要对属性进行响应式处理,则是通过调用 defineReactive 方法进行响应式处理

6.13、父子级生命周期的调用顺序

Vue 的父组件和子组件生命周期钩子函数执行顺序可以归类为以下 4 部分:

加载渲染过程

  • 父 beforeCreate -> 父 created -> 父 beforeMount -> 子 beforeCreate -> 子 created -> 子 beforeMount -> 子 mounted -> 父 mounted

子组件更新过程

  • 父 beforeUpdate -> 子 beforeUpdate -> 子 updated -> 父 updated

父组件更新过程

  • 父 beforeUpdate -> 父 updated

销毁过程

  • 父beforeDestroy -> 子 beforeDestroy -> 子 destroyed -> 父 destroyed

小技巧:父组件先开始,子组件先完成

6.14、路由hash、history区别和原理

hash模式: 灵活运用了html的瞄点功能、改变#后的路径本质上是更换了当前页面的瞄点,所以不会刷新页面。

history模式: 是使用了 H5 提供的pushState() 和 replaceState(),允许开发者直接更改前端路由,即更新浏览器 URL 地址而不重新发起请求(将url替换并且不刷新页面)。

两者区别

  1. 外观:hash的url有个#符号,history没有,history外观更好看。
  2. 刷新:hash刷新会加载到地址栏对应的页面,history刷新浏览器会重新发起请求,如果服务端没有匹配当前的url,就会出现404页面。
  3. 兼容性:hash能兼容到IE8,history只能兼容到IE10。
  4. 服务端支持:hash(#及后面的内容)的变化不会导致浏览器向服务器发起请求;history刷新会重新向服务器发起请求,需要在服务端做处理:如果没有匹配到资源,就返回同一个html页面。
  5. 原理:hash通过监听浏览器的onhashchange()事件,查找对应的路由规则;history利用H5新增的pushState()和replaceState()方法改变url。
  6. 记录:hash模式只有#后面的内容被修改才会添加新的记录栈;history通过pushState()设置的url于当前url一模一样也会被记录到历史记录栈。

6.15、路由钩子函数

路由钩子函数也叫导航守卫,分为【全局守卫】、【路由独享守卫】、【组件内的守卫】

全局守卫

  1. beforeEach 全局前置守卫 进入路由之前
  2. beforeResolve 全局解析守卫
  3. afterEach 全局后置钩子 进入路由之后
参数描述
to将要进入的路由对象
from将要离开的路由对象
next进入 / 取消 / 跳转新路由

路由独享守卫

可以直接在某个路由配置上定义 beforeEnter 守卫,只在进入路由时触发

组件内的守卫

  1. beforeRouteEnter 进入路由前
  2. beforeRouteUpdate (2.2) 路由复用同一个组件时
  3. beforeRouteLeave 离开当前路由时

6.16、keep-alive、钩子、includes

keep-alive 是 Vue 内置的一个组件,可以实现组件缓存,当组件切换时不会对当前组件进行卸载。

  • 配置属性

    • include 只有名称匹配的组件会被缓存
    • exclude 任何名称匹配的组件都不会被缓存
  • 钩子函数

    • activated组件渲染后调用
    • deactivated组件销毁后调用

6.17、vue单向数据流

  • 所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。
  • 子组件想修改时,只能通过 $emit 派发一个自定义事件,父组件接收到后,由父组件修改。

6.18、虚拟DOM优缺点

虚拟 DOM概念

由于在浏览器中操作 DOM 是很昂贵的。频繁的操作 DOM,会产⽣⼀定的性能问题,所以在vue中将真实的DOM节点抽离成⼀个虚拟的DOM树,这个虚拟的DOM树就是虚拟DOM。

优点:

  1. 保证性能下限: 需要适配任何上层 API 可能产生的操作,所以它的性能并不是最优的,但是比直接操作 DOM 性能要好很多。
  2. 无需手动操作 DOM: 我们不再需要手动去操作 DOM,只需要写好 View-Model 的代码逻辑,框架会根据虚拟 DOM 和 数据双向绑定,帮我们以可预期的方式更新视图,极大提高我们的开发效率
  3. 跨平台: 虚拟 DOM 本质上是 JavaScript 对象,而 DOM 与平台强相关,相比之下虚拟 DOM 可以进行更方便地跨平台操作,例如服务器渲染、weex 开发等等。

缺点:

  1. 无法进行极致优化: 虽然虚拟 DOM + 合理的优化,足以应对绝大部分应用的性能需求,但在一些性能要求极高的应用中虚拟 DOM 无法进行针对性的极致优化。
  2. 首次渲染大量 DOM 时,由于多了一层虚拟 DOM 的计算,会比 innerHTML 插入慢。

6.19、路由传参

  • Params

    1. 只能使用name,不能使用path
    2. 参数不会显示在路径上
    3. 浏览器强制刷新参数会被清空,
  • Query

    • 参数会显示在路径上,刷新不会被清空
    • name 可以使用path路径

6.20、什么是vuex

vuex 是专门为 vue 提供的全局状态管理系统,用于多个组件中数据共享、数据缓存等。(无法持久化、内部核心原理是通过创造一个全局实例 new Vue)

6.21、vuex的几大核心

  • State:定义了应用状态的数据结构,可以在这里设置默认的初始状态。
  • Getter:允许组件从 Store 中获取数据,mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性。
  • Mutation:是唯一更改 store 中状态的方法,且必须是同步函数。
  • Action:用于提交 mutation,而不是直接变更状态,可以包含任意异步操作。
  • Module:允许将单一的 Store 拆分为多个 store 且同时保存在单一的状态树中。

6.22、vuex同步与异步提交

  • Mutation:同步提交
  • Action:异步提交

6.23、vue项目优化

(1)代码层面的优化

  • v-if 和 v-show 区分使用场景
  • computed 和 watch 区分使用场景
  • v-for 遍历必须为 item 添加 key,且避免同时使用 v-if
  • 长列表性能优化
  • 事件的销毁
  • 图片资源懒加载
  • 路由懒加载
  • 第三方插件的按需引入
  • 优化无限列表性能
  • 服务端渲染 SSR or 预渲染

(2)Webpack 层面的优化

  • Webpack 对图片进行压缩
  • 减少 ES6 转为 ES5 的冗余代码
  • 提取公共代码
  • 模板预编译
  • 提取组件的 CSS
  • 优化 SourceMap
  • 构建结果输出分析
  • Vue 项目的编译优化

(3)基础的 Web 技术的优化

  • 开启 gzip 压缩
  • 浏览器缓存
  • CDN 的使用
  • 使用 Chrome Performance 查找性能瓶颈

7、项目相关问题

7.1、说一下前端登录的流程?

  1. 初次登录的时候,前端调后调的登录接口,发送用户名和密码,后端收到请求,验证用户名和密码,验证成功,就给前端返回一个token,和一个用户信息的值,
  2. 前端拿到token,将token储存到Vuex中,然后从Vuex中把token的值存入浏览器Cookies中。
  3. 把用户信息存到Vuex然后再存储到LocalStroage中,然后跳转到下一个页面,
  4. 根据后端接口的要求,只要不登录就不能访问的页面需要在前端每次跳转页面师判断Cookies中是否有token,没有就跳转到登录页,有就跳转到相应的页面,
  5. 我们应该再每次发送post/get请求的时候应该加入token,常用方法再项目utils/service.js中添加全局拦截器,将token的值放入请求头中 后端判断请求头中有无token,有token,就拿到token并验证token是否过期,在这里过期会返回无效的token然后有个跳回登录页面重新登录并且清除本地用户的信息