2021Vue.js面试题汇总及答案【全网最全 建议收藏】(二)

简介: 2021Vue.js面试题汇总及答案【全网最全 建议收藏】

1.23.批量异步更新策略


Vue 在修改数据后,视图不会立刻更新,而是等同一事件循环中的所有数据变化完成之后,再统一进行视图更新。


换句话说,只要观察到数据变化,就会自动开启一个队列,并缓冲在同一个事件循环中发生的所以数据改变。在缓冲时会去除重复数据,从而避免不必要的计算和 DOM 操作。


1.24.vue 的 nextTick 方法的实现原理


1.vue 用异步队列的方式来控制 DOM 更新和 nextTick 回调先后执行


2.microtask 因为其高优先级特性,能确保队列中的微任务在一次事件循环前被执行完毕


1.25.Vue 组件 data 为什么必须是函数 ?


因为组件是可以复用的,JS 里对象是引用关系,如果组件 data 是一个对象,那么子组件中的 data 属性值会互相污染。


所以一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝。


1.26.v-if和v-for一起使用的弊端及解决办法


由于v-for的优先级比v-if高,所以导致每循环一次就会去v-if一次,而v-if是通过创建和销毁dom元素来控制元素的显示与隐藏,所以就会不停的去创建和销毁元素,造成页面卡顿,性能下降。


解决办法:


1.在v-for的外层或内层包裹一个元素来使用v-if

2.用computed处理


1.27.vue常用指令


1.v-model 多用于表单元素实现双向数据绑定(同angular中的ng-model)


2.v-bind 动态绑定 作用: 及时对页面的数据进行更改


3.v-on:click 给标签绑定函数,可以缩写为@,例如绑定一个点击函数 函数必须写在methods里面


4.v-for 格式: v-for=“字段名 in(of) 数组json” 循环数组或json(同angular中的ng-repeat)


5.v-show 显示内容 (同angular中的ng-show)


6.v-hide 隐藏内容(同angular中的ng-hide)


7.v-if 显示与隐藏 (dom元素的删除添加 同angular中的ng-if 默认值为false)


8.v-else-if 必须和v-if连用


9.v-else 必须和v-if连用 不能单独使用 否则报错 模板编译错误


10.v-text 解析文本


11.v-html 解析html标签


12.v-bind:class 三种绑定方法 1、对象型 ‘{red:isred}’ 2、三元型 ‘isred?“red”:“blue”’ 3、数组型 ‘[{red:“isred”},{blue:“isblue”}]’


13.v-once 进入页面时 只渲染一次 不在进行渲染


14.v-cloak 防止闪烁


15.v-pre 把标签内部的元素原位输出


1.28. 组件传值方式有哪些


1.父传子:子组件通过props[‘xx’] 来接收父组件传递的属性 xx 的值


2.子传父:子组件通过 this.$emit(‘fnName’,value) 来传递,父组件通过接收 fnName 事件方法来接收回调


3.其他方式:通过创建一个bus,进行传值


4.使用Vuex


1.30.vue中 key 值的作用


当 Vue.js 用 v-for 正在更新已渲染过的元素列表时,它默认用“就地复用”策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序, 而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。key的作用主要是为了高效的更新虚拟DOM


1.31.为什么在 Vue3.0 采用了 Proxy,抛弃了 Object.defineProperty?


Object.defineProperty 本身有一定的监控到数组下标变化的能力,但是在 Vue 中,从性能/体验的性价比考虑,尤大大就弃用了这个特性(Vue 为什么不能检测数组变动 )。为了解决这个问题,经过 vue 内部处理后可以使用以下几种方法来监听数组


push();

pop();

shift();

unshift();

splice();

sort();

reverse();


由于只针对了以上 7 种方法进行了 hack 处理,所以其他数组的属性也是检测不到的,还是具有一定的局限性。


Object.defineProperty 只能劫持对象的属性,因此我们需要对每个对象的每个属性进行遍历。Vue 2.x 里,是通过 递归 + 遍历 data 对象来实现对数据的监控的,如果属性值也是对象那么需要深度遍历,显然如果能劫持一个完整的对象是才是更好的选择。


Proxy 可以劫持整个对象,并返回一个新的对象。Proxy 不仅可以代理对象,还可以代理数组。还可以代理动态增加的属性。


1.32.谈一谈 nextTick 的原理


JS 运行机制


JS 执行是单线程的,它是基于事件循环的。事件循环大致分为以下几个步骤:


所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。


主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。


一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。


主线程不断重复上面的第三步。



83fd1d03489245d3b0a421183bc41193.png


主线程的执行过程就是一个 tick,而所有的异步结果都是通过 “任务队列” 来调度。 消息队列中存放的是一个个的任务(task)。 规范中规定 task 分为两大类,分别是 macro task 和 micro task,并且每个 macro task 结束后,都要清空所有的 micro task。


for (macroTask of macroTaskQueue) {
  // 1. Handle current MACRO-TASK
  handleMacroTask();
  // 2. Handle all MICRO-TASK
  for (microTask of microTaskQueue) {
    handleMicroTask(microTask);
  }
}

在浏览器环境中 :


常见的 macro task 有 setTimeout、MessageChannel、postMessage、setImmediate


常见的 micro task 有 MutationObsever 和 Promise.then


异步更新队列


可能你还没有注意到,Vue 在更新 DOM 时是异步执行的。只要侦听到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。


如果同一个 watcher 被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作是非常重要的。


然后,在下一个的事件循环“tick”中,Vue 刷新队列并执行实际 (已去重的) 工作。

Vue 在内部对异步队列尝试使用原生的 Promise.then、MutationObserver 和 setImmediate,如果执行环境不支持,则会采用 setTimeout(fn, 0) 代替。


在 vue2.5 的源码中,macrotask 降级的方案依次是:setImmediate、MessageChannel、setTimeout


vue 的 nextTick 方法的实现原理:


vue 用异步队列的方式来控制 DOM 更新和 nextTick 回调先后执行


microtask 因为其高优先级特性,能确保队列中的微任务在一次事件循环前被执行完毕

考虑兼容问题,vue 做了 microtask 向 macrotask 的降级方案


1.33.谈谈 Vue 事件机制,手写 o n , on,on,off,e m i t , emit,emit,once


Vue 事件机制 本质上就是 一个 发布-订阅 模式的实现。


class Vue {
  constructor() {
    //  事件通道调度中心
    this._events = Object.create(null);
  }
  $on(event, fn) {
    if (Array.isArray(event)) {
      event.map(item => {
        this.$on(item, fn);
      });
    } else {
      (this._events[event] || (this._events[event] = [])).push(fn);
    }
    return this;
  }
  $once(event, fn) {
    function on() {
      this.$off(event, on);
      fn.apply(this, arguments);
    }
    on.fn = fn;
    this.$on(event, on);
    return this;
  }
  $off(event, fn) {
    if (!arguments.length) {
      this._events = Object.create(null);
      return this;
    }
    if (Array.isArray(event)) {
      event.map(item => {
        this.$off(item, fn);
      });
      return this;
    }
    const cbs = this._events[event];
    if (!cbs) {
     return this;
    }
    if (!fn) {
      this._events[event] = null;
      return this;
    }
    let cb;
    let i = cbs.length;
    while (i--) {
      cb = cbs[i];
      if (cb === fn || cb.fn === fn) {
        cbs.splice(i, 1);
        break;
      }
    }
    return this;
  }
  $emit(event) {
    let cbs = this._events[event];
    if (cbs) {
      const args = [].slice.call(arguments, 1);
      cbs.map(item => {
        args ? item.apply(this, args) : item.call(this);
      });
    }
    return this;
  }
}


1.34.vue-router有哪几种导航钩子?


三种:


一种是全局导航钩子:router.beforeEach(to,from,next),作用:跳转前进行判断拦截。第二种:组件内的钩子;第三种:单独路由独享组件


1.35.vuex是什么?怎么使用?哪种功能场景使用它?


vue框架中状态管理。在main.js引入store,注入。新建了一个目录store,…… export 。

场景有:单页应用中,组件之间的状态。音乐播放、登录状态、加入购物车


cde41d9be7e9428680a8e2a093f37313.png


1.36.MVVM和MVC区别?和其他框架(jquery)区别?那些场景适用?


MVVM和MVC都是一种设计思想,主要就是MVC中的Controller演变成ViewModel,,MVVM主要通过数据来显示视图层而不是操作节点,解决了MVC中大量的DOM操作使页面渲染性能降低,加载速度慢,影响用户体验问题。主要用于数据操作比较多的场景。

场景:数据操作比较多的场景,更加便捷



0dbc5c6b0b0d4bfa998ec70a5929f4d5.png


1.37.聊聊你对Vue.js的模板编译的理解


简而言之,就是先转化成AST树,再得到的渲染函数返回VNODE(Vue公司的虚拟DOM节点)


详情步骤:


首先,通过编译编译器把模板编译成AST语法树(抽象语法树即源代码的抽象语法结构的树状表现形式),编译是createCompiler的返回值,createCompiler是用以创建编译器的。负责合并选项。


然后,AST会经过生成(将AST语法树转化成渲染功能字符串的过程)得到渲染函数,渲染的返回值是VNode,VNode是Vue的虚拟DOM节点,里面有(标签名,子节点,文本等等)


1.38.< keep-alive>< /keep-alive>的作用是什么,如何使用?


答:包裹动态组件时,会缓存不活动的组件实例,主要用于保留组件状态或避免重新渲染;


使用:简单页面时


缓存: < keep-alive include=”组件名”>< /keep-alive>


不缓存: < keep-alive exclude=”组件名”>< /keep-alive>


1.39.vue和react区别


相同点:都鼓励组件化,都有’props’的概念,都有自己的构建工具,Reat与Vue只有框架的骨架,其他的功能如路由、状态管理等是框架分离的组件。


不同点:React:数据流单向,语法—JSX,在React中你需要使用setState()方法去更新状态。Vue:数据双向绑定,语法–HTML,state对象并不是必须的,数据由data属性在Vue对象中进行管理。适用于小型应用,但对于对于大型应用而言不太适合。


1.40.vue生命周期的理解?


参照大神文章:vue笔记 - 生命周期第二次学习与理解


16bfdc33d3794790bb2637c90408ae7d.png


beforeCreate是new Vue()之后触发的第一个钩子,在当前阶段data、methods、computed以及watch上的数据和方法都不能被访问。


created在实例创建完成后发生,当前阶段已经完成了数据观测,也就是可以使用数据,更改数据,在这里更改数据不会触发updated函数。可以做一些初始数据的获取,在当前阶段无法与Dom进行交互,如果非要想,可以通过vm.$nextTick来访问Dom。


beforeMount发生在挂载之前,在这之前template模板已导入渲染函数编译。而当前阶段虚拟Dom已经创建完成,即将开始渲染。在此时也可以对数据进行更改,不会触发updated。


mounted在挂载完成后发生,在当前阶段,真实的Dom挂载完毕,数据完成双向绑定,可以访问到Dom节点,使用$refs属性对Dom进行操作。


beforeUpdate发生在更新之前,也就是响应式数据发生更新,虚拟dom重新渲染之前被触发,你可以在当前阶段进行更改数据,不会造成重渲染。


updated发生在更新完成之后,当前阶段组件Dom已完成更新。要注意的是避免在此期间更改数据,因为这可能会导致无限循环的更新。


beforeDestroy发生在实例销毁之前,在当前阶段实例完全可以被使用,我们可以在这时进行善后收尾工作,比如清除计时器。


destroyed发生在实例销毁之后,这个时候只剩下了dom空壳。组件已被拆解,数据绑定被卸除,监听被移出,子实例也统统被销毁。


1.41.Vue2.x和Vue3.x渲染器的diff算法分别说一下


简单来说,diff算法有以下过程


1.同级比较,再比较子节点

2.先判断一方有子节点一方没有子节点的情况(如果新的children没有子节点,将旧的子节点移除)

3.比较都有子节点的情况(核心diff)

3.递归比较子节点


正常Diff两个树的时间复杂度是O(n^3),但实际情况下我们很少会进行跨层级的移动DOM,所以Vue将Diff进行了优化,从O(n^3) -> O(n),只有当新旧children都为多个子节点时才需要用核心的Diff算法进行同层级比较。


Vue2的核心Diff算法采用了双端比较的算法,同时从新旧children的两端开始进行比较,借助key值找到可复用的节点,再进行相关操作。相比React的Diff算法,同样情况下可以减少移动节点次数,减少不必要的性能损耗,更加的优雅。


Vue3.x借鉴了

ivi算法和 inferno算法


在创建VNode时就确定其类型,以及在 mount/patch 的过程中采用位运算来判断一个VNode的类型,在这个基础之上再配合核心的Diff算法,使得性能上较Vue2.x有了提升。(实际的实现可以结合Vue3.x源码看。)


该算法中还运用了动态规划的思想求解最长递归子序列。


1.42.你都做过哪些Vue的性能优化?


编码阶段


1.尽量减少data中的数据,data中的数据都会增加getter和setter,会收集对应的2.watcher

3.v-if和v-for不能连用

4.如果需要使用v-for给每项元素绑定事件时使用事件代理

5.SPA 页面采用keep-alive缓存组件

6.在更多的情况下,使用v-if替代v-show

7.key保证唯一

8.使用路由懒加载、异步组件

9.防抖、节流

10.第三方模块按需导入

11.长列表滚动到可视区域动态加载

12.图片懒加载


SEO优化


1.预渲染

2.服务端渲染SSR


打包优化


1.压缩代码

2.Tree Shaking/Scope Hoisting

3.使用cdn加载第三方模块

4.多线程打包happypack

5.splitChunks抽离公共文件

6.sourceMap优化


用户体验


1.骨架屏

2.PWA


还可以使用缓存(客户端缓存、服务端缓存)优化、服务端开启gzip压缩等。


1.43.hash路由和history路由实现原理说一下

location.hash的值实际就是URL中#后面的东西。


history实际采用了HTML5中提供的API来实现,主要有history.pushState()和history.replaceState()。


1.44.SPA 单页面的理解,它的优缺点分别是什么

SPA( single-page application )仅在 Web 页面初始化时加载相应的 HTML、JavaScript 和 CSS

一旦页面加载完成,SPA 不会因为用户的操作而进行页面的重新加载或跳转

取而代之的是利用路由机制实现 HTML 内容的变换, UI 与用户的交互,避免页面的重新加载


优点:


1、用户体验好、快,内容的改变不需要重新加载整个页面,避免了不必要的跳转和重复渲染

2、基于上面一点,SPA 相对对服务器压力小

3、前后端职责分离,架构清晰,前端进行交互逻辑,后端负责数据处理


缺点:


1、初次加载耗时多:为实现单页 Web 应用功能及显示效果,

需要在加载页面的时候将 JavaScript、CSS 统一加载,部分页面按需加载

2、前进后退路由管理:由于单页应用在一个页面中显示所有的内容,

所以不能使用浏览器的前进后退功能,所有的页面切换需要自己建立堆栈管理

3、SEO 难度较大:由于所有的内容都在一个页面中动态替换显示,所以在 SEO 上其有着天然的弱势


1.45.vue.cli中怎样使用自定义的组件?有遇到过哪些问题吗?


第一步:在components目录新建你的组件文件(indexPage.vue),script一定要export default {}


第二步:在需要用的页面(组件)中导入:import indexPage from

‘@/components/indexPage.vue’


第三步:注入到vue的子组件的components属性上面,components:{indexPage}

第四步:在template视图view中使用,


例如有indexPage命名,使用的时候则index-page


1.46.vue如何实现按需加载配合webpack设置


webpack中提供了require.ensure()来实现按需加载。以前引入路由是通过import 这样的方式引入,改为const定义的方式进行引入。


不进行页面按需加载引入方式:import home from '../../common/home.vue'


进行页面按需加载的引入方式:const home = r => require.ensure( [], () => r (require('../../common/home.vue')))




目录
相关文章
|
19天前
|
前端开发 JavaScript 网络协议
前端最常见的JS面试题大全
【4月更文挑战第3天】前端最常见的JS面试题大全
41 5
|
8天前
|
JavaScript 前端开发 测试技术
「一劳永逸」送你21道高频JavaScript手写面试题(上)
「一劳永逸」送你21道高频JavaScript手写面试题
36 0
|
1月前
|
JavaScript 前端开发 API
vue面试题目汇总
vue面试题目汇总
34 4
|
1月前
|
设计模式 JavaScript 前端开发
最常见的26个JavaScript面试题和答案
最常见的26个JavaScript面试题和答案
44 1
|
1月前
|
缓存 JavaScript 前端开发
Vue常见面试题 标准答案汇总一
Vue常见面试题 标准答案汇总一
40 1
|
1月前
|
存储 JavaScript 前端开发
【JavaScript】面试手撕浅拷贝
引入 浅拷贝和深拷贝应该是面试时非常常见的问题了,为了能将这两者说清楚,于是打算用两篇文章分别解释下深浅拷贝。 PS: 我第一次听到拷贝这个词,有种莫名的熟悉感,感觉跟某个英文很相似,后来发现确实Copy的音译,感觉这翻译还是蛮有意思的
45 6
|
1月前
|
JavaScript 前端开发
【JavaScript】面试手撕节流
上篇我们讲了防抖,这篇我们就谈谈防抖的好兄弟 -- 节流。这里在老生常谈般的提一下他们两者之间的区别,顺带给读者巩固下。
53 3
|
2月前
|
前端开发 JavaScript UED
【JavaScript】面试手撕防抖
防抖: 首先它是常见的性能优化技术,主要用于处理频繁触发的浏览器事件,如窗口大小变化、滚动事件、输入框内容改变等。在用户连续快速地触发同一事件时,防抖机制会确保相关回调函数在一个时间间隔内只会被执行一次。
36 0
|
2月前
|
前端开发 JavaScript 算法
【JavaScript】面试手撕数组排序
这章主要讲的是数组的排序篇,我们知道面试的时候,数组的排序是经常出现的题目。所以这块还是有必要进行一下讲解的。笔者观察了下前端这块的常用算法排序题,大概可以分为如下
24 2