您好,欢迎访问一九零五行业门户网

【吐血整理】Vue.js面试题汇总及答案解析(快来收藏)

本篇文章给大家总结分享一些vue面试题(附答案解析),带你梳理基础知识,增强vue知识储备,值得收藏,快来看看吧!
(学习视频分享:vue视频教程)
一、vue.js基本问题1.1.vue 响应式原理
核心实现类:
observer : 它的作用是给对象的属性添加 getter 和 setter,用于依赖收集和派发更新
dep : 用于收集当前响应式对象的依赖关系,每个响应式对象包括子对象都拥有一个 dep 实例(里面 subs 是 watcher 实例数组),当数据有变更时,会通过 dep.notify()通知各个 watcher。
watcher : 观察者对象 , 实例分为渲染 watcher (render watcher),计算属性 watcher (computed watcher),侦听器 watcher(user watcher)三种
watcher 和 dep 的关系:
watcher 中实例化了 dep 并向 dep.subs 中添加了订阅者,dep 通过 notify 遍历了 dep.subs 通知每个 watcher 更新。
依赖收集:
initstate 时,对 computed 属性初始化时,触发 computed watcher 依赖收集
initstate 时,对侦听属性初始化时,触发 user watcher 依赖收集
render()的过程,触发 render watcher 依赖收集
re-render 时,vm.render()再次执行,会移除所有 subs 中的 watcer 的订阅,重新赋值。
派发更新:
组件中对响应的数据进行了修改,触发 setter 的逻辑
调用 dep.notify()
遍历所有的 subs(watcher 实例),调用每一个 watcher 的 update 方法。
原理:
当创建 vue 实例时,vue 会遍历 data 选项的属性,利用 object.defineproperty 为属性添加 getter 和 setter 对数据的读取进行劫持(getter 用来依赖收集,setter 用来派发更新),并且在内部追踪依赖,在属性被访问和修改时通知变化。
每个组件实例会有相应的 watcher 实例,会在组件渲染的过程中记录依赖的所有数据属性(进行依赖收集,还有 computed watcher,user watcher 实例),之后依赖项被改动时,setter 方法会通知依赖与此 data 的 watcher 实例重新计算(派发更新),从而使它关联的组件重新渲染。
一句话总结:
vue.js 采用数据劫持结合发布-订阅模式,通过 object.defineproperty 来劫持各个属性的 setter,getter,在数据变动时发布消息给订阅者,触发响应的监听回调
1.2.vue.js 的特点易用: 简单,易学,上手快
灵活: (渐进式)不断繁荣的生态系统,可以在一个库和一套完整框架之间自如伸缩。
高效: 20kb min+gzip 运行大小;超快虚拟 dom;最省心的优化
双向绑定:开发效率高
基于组件的代码共享
web项目工程化,增加可读性、可维护性
1.3. vue.js 双向绑定的原理vue.js 2.0 采用数据劫持(proxy 模式)结合发布者-订阅者模式(pubsub 模式)的方式,通过 object.defineproperty()来劫持各个属性的 setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
每个组件实例都有相应的watcher程序实例,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的setter被调用时,会通知watcher重新计算,从而致使它关联的组件得以更新。
vue.js 3.0, 放弃了object.defineproperty ,使用更快的es6原生 proxy (访问对象拦截器, 也称代理器)
步骤:
1.需要observe的数据对象进行递归遍历,包括子属性对象的属性,都加上setter和getter这样的话,给这个对象的某个值赋值,就会触发setter,那么就能监听到了数据变化
2.compile解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图
3.watcher订阅者是observer和compile之间通信的桥梁,主要做的事情是: ①在自身实例化时往属性订阅器(dep)里面添加自己 ②自身必须有一个update()方法 ③待属性变动dep.notice()通知时,能调用自身的update()方法,并触发compile中绑定的回调,则功成身退。
4.mvvm作为数据绑定的入口,整合observer、compile和watcher三者,通过observer来监听自己的model数据变化,通过compile来解析编译模板指令,最终利用watcher搭起observer和compile之间的通信桥梁,达到数据变化 -> 视图更新;视图交互变化(input) -> 数据model变更的双向绑定效果。
1.4.vue中如何监控某个属性值的变化?比如现在需要监控data中, obj.a 的变化。vue中监控对象属性的变化你可以这样:
watch: {      obj: {       handler (newvalue, oldvalue) {        console.log('obj changed')      },      deep: true    }
deep属性表示深层遍历,但是这么写会监控obj的所有属性变化,并不是我们想要的效果,所以做点修改:
watch: {   'obj.a': {       handler (newname, oldname) {        console.log('obj.a changed')      }   }  }
还有一种方法,可以通过computed 来实现,只需要:
computed: {    a1 () {      return this.obj.a          }}
利用计算属性的特性来实现,当依赖改变时,便会重新计算一个新值。
1.5.vue.js 3.0 放弃defineproperty, 使用proxy的原因object.defineproperty缺陷
1.监控到数组下标的变化时,开销很大。所以vue.js放弃了下标变化的检测;
2.object.defineproperty只能劫持对象的属性,而proxy是直接代理对象。3.object.defineproperty需要遍历对象的每个属性,如果属性值也是对象,则需要深度遍历。而 proxy 直接代理对象,不需要遍历操作。
4.object.defineproperty对新增属性需要手动进行observe。vue2时需要使用 vm.$set 才能保证新增的属性也是响应式
5.proxy支持13种拦截操作,这是defineproperty所不具有的
6.proxy 作为新标准,长远来看,js引擎会继续优化 proxy,但 getter 和 setter 基本不会再有针对性优化
1.6.vue 2 中给 data 中的对象属性添加一个新的属性时会发生什么?如何解决?视图并未刷新。这是因为在vue实例创建时,新属性并未声明,因此就没有被vue转换为响应式的属性,自然就不会触发视图的更新,这时就需要使用vue的全局 api $set():
this.$set(this.obj, 'new_property', 'new_value')
1.7.computed和watch的区别及运用场景computed 计算属性 : 依赖其它属性值,并且 computed 的值有缓存,只有它依赖的 属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值。
watch 侦听器 : 更多的是观察的作用,无缓存性,类似于某些数据的监听回调,每 当监听的数据变化时都会执行回调进行后续操作。
运用场景:
1.当我们需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算。
2.当我们需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许我们执行异步操作 ( 访问一个 api ),限制我们执行该操作的频率, 并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。
3.多个因素影响一个显示,用computed;一个因素的变化影响多个其他因素、显示,用watch;
1.8. computed 和 methods 的区别1.computed: 计算属性是基于它们的依赖进行缓存的,只有在它的相关依赖发生改变时才会重新求值对于 method ,只要发生重新渲染,
2.method 调用总会执行该函数
1.9.虚拟dom,diff算法1.让我们不用直接操作dom元素,只操作数据便可以重新渲染页面
2.虚拟dom是为了解决浏览器性能问题而被设计出来的
当操作数据时,将改变的dom元素缓存起来,都计算完后再通过比较映射到真实的dom树上
3.diff算法比较新旧虚拟dom。如果节点类型相同,则比较数据,修改数据;如果节点不同,直接干掉节点及所有子节点,插入新的节点;如果给每个节点都设置了唯一的key,就可以准确的找到需要改变的内容,否则就会出现修改一个地方导致其他地方都改变的情况。比如a-b-c-d, 我要插入新节点a-b-m-c-d,实际上改变的了c和d。但是设置了key,就可以准确的找到b c并插入
1.10.为何需要virtual dom?1.具备跨平台的优势
2.操作 dom 慢,js运行效率高。我们可以将dom对比操作放在js层,提高效率。
3.提升渲染性能
1.11.过滤器 (filter)在vue中使用filters来过滤(格式化)数据,filters不会修改数据,而是过滤(格式化)数据,改变用户看到的输出(计算属性 computed ,方法 methods 都是通过修改数据来处理数据格式的输出显示。
使用场景: 比如需要处理时间、数字等的的显示格式;
1.12.常见的事件修饰符及其作用1).stop:等同于 javascript 中的 event.stoppropagation() ,防止事件冒泡;
2).prevent :等同于 javascript 中的 event.preventdefault() ,防止执行预设的行为(如果事件可取消,则取消该事件,而不停止事件的进一步传播);
3).capture :当元素发生冒泡时,先触发带有该修饰符的元素。若有多个该修饰符,则由外而内触发。如 p1中嵌套p2中嵌套p3.capture中嵌套p4,那么执行顺序为:p3=》p4=》p2=》p1
4).self :只会触发自己范围内的事件,不包含子元素;
5).once :只会触发一次。
1.13.v-show指令和v-if指令的区别是什么?v-show 仅仅控制元素的显示方式,将 display 属性在 block 和 none 来回切换;而v-if会控制这个 dom 节点的存在与否。当我们需要经常切换某个元素的显示/隐藏时,使用v-show会更加节省性能上的开销;当只需要一次显示或隐藏时,使用v-if更加合理。
1.14.v-model 是如何实现的,语法糖实际是什么作用在表单元素上v-model=message等同于v-bind:value=“message” v-on:input=message=e v e n t . t a r g e t . v a l u e 作 用 在 组 件 上 , 本 质 是 一 个 父 子 组 件 通 信 的 语 法 糖 ,通过prop和.emit实现, 等同于:value=message @input= $emit('input', $event.target.value)
1.15.data为什么是一个函数而不是对象javascript中的对象是引用类型的数据,当多个实例引用同一个对象时,只要一个实例对这个对象进行操作,其他实例中的数据也会发生变化。
而在vue中,我们更多的是想要复用组件,那就需要每个组件都有自己的数据,这样组件之间才不会相互干扰。
所以组件的数据不能写成对象的形式,而是要写成函数的形式。数据以函数返回值的形式定义,这样当我们每次复用组件的时候,就会返回一个新的data,也就是说每个组件都有自己的私有数据空间,它们各自维护自己的数据,不会干扰其他组件的正常运行。
1.16.vue template 到 render 的过程1.调用parse方法将template转化为ast(抽象语法树, abstract syntax tree)
2.对静态节点做优化。如果为静态节点,他们生成的dom永远不会改变,这对运行时模板更新起到了极大的优化作用。
3.生成渲染函数. 渲染的返回值是vnode,vnode是vue的虚拟dom节点,里面有(标签名,子节点,文本等等)
1.17.vue template 到 render 的过程调用parse方法将template转化为ast(抽象语法树, abstract syntax tree)
对静态节点做优化。如果为静态节点,他们生成的dom永远不会改变,这对运行时模板更新起到了极大的优化作用。
生成渲染函数. 渲染的返回值是vnode,vnode是vue的虚拟dom节点,里面有(标签名,子节点,文本等等)
1.18.axios是什么易用、简洁且高效的http库, 支持node端和浏览器端,支持promise,支持拦截器等高级配置。
1.19.sass是什么?如何在vue中安装和使用?sass是一种css预编译语言安装和使用步骤如下。
1.用npm安装加载程序( sass-loader、 css-loader等加载程序)。
2.在 webpack.config.js中配置sass加载程序。
1.20.vue.js页面闪烁vue. js提供了一个v-cloak指令,该指令一直保持在元素上,直到关联实例结束编译。当和css一起使用时,这个指令可以隐藏未编译的标签,直到实例编译结束。用法如下
1.21.如何解决数据层级结构太深的问题在开发业务时,经常会岀现异步获取数据的情况,有时数据层次比较深,如以下代码: <span 'v-text=“a.b.c.d”>, 可以使用vm.$set手动定义一层数据:
vm.$set(demo,a.b.c.d)
1.22.在 vue. js开发环境下调用api接口,如何避免跨域config/ index.js内对 proxytable项配置代理。
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)。只要异步任务有了运行结果,就在任务队列之中放置一个事件。
一旦执行栈中的所有同步任务执行完毕,系统就会读取任务队列,看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
主线程不断重复上面的第三步。
主线程的执行过程就是一个 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 事件机制,手写on,off,emit,oncevue 事件机制 本质上就是 一个 发布-订阅 模式的实现。
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 。
场景有:单页应用中,组件之间的状态。音乐播放、登录状态、加入购物车
1.36.mvvm和mvc区别?和其他框架(jquery)区别?那些场景适用?mvvm和mvc都是一种设计思想,主要就是mvc中的controller演变成viewmodel,,mvvm主要通过数据来显示视图层而不是操作节点,解决了mvc中大量的dom操作使页面渲染性能降低,加载速度慢,影响用户体验问题。主要用于数据操作比较多的场景。
场景:数据操作比较多的场景,更加便捷
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笔记 - 生命周期第二次学习与理解
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')))
二、组件 component2.1.vue中如何编写可复用的组件 (编写组件的原则)以组件功能命名
只负责ui的展示和交互动画,不要在组件里与服务器打交道(获取异步数据等)
可复用组件不会因组件使用的位置、场景而变化。尽量减少对外部条件的依赖。
2.2.如何让css只在当前组件中起作用?在每一个vue.js组件中都可以定义各自的css、 javascript代码。如果希望组件内写的css只对当前组件起作用,只需要在style标签添加scoped属性即可
2.3.keep-alive是什么?如果需要在组件切换的时候,保存一些组件的状态防止多次渲染,就可以使用 keep-alive 组件包裹需要保存的组件。
两个重要属性,include 缓存组件名称,exclude 不需要缓存的组件名称。
2.4.如何在 vue. js动态插入图片对“src”属性插值将导致404请求错误。应使用 v-bind:src (简写:src)格式代替。
2.5.父子组件的生命周期顺序(可参照上方图解)加载渲染过程:
父beforecreate->父created->父beforemount->子beforecreate->子created->子beforemount->子mounted->父mounted
子组件更新过程:父beforeupdate->子beforeupdate->子updated->父updated
父组件更新过程:父beforeupdate->父updated
销毁过程:父beforedestroy->子beforedestroy->子destroyed->父destroyed
三、vuex3.1.vuex的核心概念1.state => 基本数据
2.getters => 从基本数据派生的数据
3.mutations => 修改数据,同步
4.actions => 修改数据,异步 (action 提交的是 mutation,而不是直接变更状态)
5.modules => 模块化vuex
3.2.vuex是什么?怎么使用?哪种功能场景使用它?vuex 是一个专为 vue.js 应用程序开发的状态管理器,采用集中式存储管理应用的所有组件的状态,主要是为了多页面、多组件之间的通信。
vuex有5个重要的属性,分别是 state、getter、mutation、action、module,由 view 层发起一个 action 给 mutation,在 mutation 中修改状态,返回新的状态,通过 getter暴露给 view层的组件或者页面,页面监测到状态改变于是更新页面。如果你的项目很简单,最好不要使用 vuex,对于大型项目,vuex 能够更好的帮助我们管理组件外部的状态,一般可以运用在购物车、登录状态、播放等场景中。
3.3.多个组件之间如何拆分各自的state,每块小的组件有自己的状态,它们之间还有一些公共的状态需要维护,如何思考这块1.公共的数据部分可以提升至和他们最近的父组件,由父组件派发
2.公共数据可以放到vuex中统一管理,各组件分别获取
3.4.vue.js中ajax请求代码应该写在组件的methods中还是vuex的actions中?1.如果请求来的数据是不是要被其他组件公用,仅仅在请求的组件内使用,就不需要放入vuex 的state里。
2.如果被其他地方复用,这个很大几率上是需要的,如果需要,请将请求放入action里,方便复用,并包装成promise返回,在调用处用async await处理返回的数据。如果不要复用这个请求,那么直接写在vue文件里很方便
3.5.vuex中如何异步修改状态actions与mutations作用类似,都是可以对状态进行修改。不同的是actions是异步操作的。
actions是可以调用mutations里的方法的。
const actions={ addactions(context){ context.commit('add',10);//调用mutations中的方法 settimeout(()=>{context.commit('reduce')},5000) // settimeout(()=>{context.commit('reduce')},3000); console.log('我比reduce提前执行'); }, reduceactions({commit}){ commit('reduce'); }}
3.6.vuex中actions和mutations的区别mutation 更改 vuex 的 store 中的状态的唯一方法是提交 mutation。vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数
const store = new vuex.store({  state: {    count: 1  },  mutations: {    increment (state) {      // 变更状态      state.count++    }  }})
action action 类似于 mutation,不同在于:
action 提交的是 mutation,而不是直接变更状态。
action 可以包含任意异步操作
const store = new vuex.store({  state: {    count: 0  },  mutations: {    increment (state) {      state.count++    }  },  actions: {    increment (context) {      context.commit('increment')    }  }})
3.7.怎么在组件中批量使用vuex的state状态?使用mapstate辅助函数, 利用对象展开运算符将state混入computed对象中
import {mapstate} from 'vuex' export default{ computed:{ ...mapstate(['price','number']) } }
3.8.vuex中状态是对象时,使用时要注意什么?对象是引用类型,复制后改变属性还是会影响原始数据,这样会改变state里面的状态,是不允许,所以先用深度克隆复制对象,再修改。
四、router4.1.vue-router 路由模式有几种1.hash: 使用 url 的 hash 值来作为路由。支持所有浏览器。 带#。如:http://localhost:8080/#/pagea。改变hash,浏览器本身不会有任何请求服务器动作的,但是页面状态和url已经关联起来了。
2.history: 以来 html5 history api 和服务器配置。参考官网中 html5 history 模式,不带#, 如:http://localhost:8080/ 正常的而路径,并没有#。基于html5的 pushstate、replacestate实现
3.abstract: 支持所有 javascript 运行模式。如果发现没有浏览器的 api,路由会自动强制进入这个模式。
4.2.vue-router如何定义嵌套路由通过children 数组:
const router = new vuerouter({  routes: [    {      path: /parentpage,      component: testpage,      children: [        {          path: /childrena,          component: childrencomponenta,        },        {          path: /childrenb,          component: childrencomponentb,        },      ],    },    {      // 其他和parentpage平级的路由    },  ],});
4.3.vue-router有哪几种导航钩子?1.全局导航钩子:router.beforeeach(to,from,next)
2.组件内的钩子beforerouteenter (to, from, next) beforerouteupdate (to, from, next) beforerouteleave (to, from, next)
3.单独路由独享组件 beforeenter: (to, from, next)
参数:有to(去的那个路由)、from(离开的路由)、next(一定要用这个函数才能去到下一个路由,如果不用就拦截)最常用就这几种
4.4. $ route和$ router的区别1、$route是“路由信息对象”,包括path,params,hash,query,fullpath,matched,name等路由信息参数。
1.$route.path 字符串,对应当前路由的路径,总是解析为绝对路径如/foo/bar。
2. $route.params 一个 key/value 对象,包含了 动态片段 和 全匹配片段, 如果没有路由参数,就是一个空对象。
3. $route.query 一个 key/value 对象,表示 url 查询参数。 例如,对于路径 /foo?user=1,则有$route.query.user == 1, 如果没有查询参数,则是个空对象
4. $route.hash 当前路由的hash值 (不带#) ,如果没有 hash 值,则为空字符串
5. $route.fullpath 完成解析后的 url,包含查询参数和hash的完整路径。
6. $route.matched 数组,包含当前匹配的路径中所包含的所有片段所对应的配置参数对象。
7. $route.name 当前路径名字
8. $ route.meta 路由元信息
2、$router是“路由实例”对象包括了路由的跳转方法,钩子函数等
实例方法:
1)、push
1.字符串this.$router.push('home')
2. 对象this.$router.push({path:'home'})
3. 命名的路由this.$router.push({name:'user',params:{userid:123}})
4.带查询参数,变成 /register?plan=123this.$router.push({path:'register',query:{plan:'123'}})
push方法其实和< router-link :to=“…”>是等同的。
注意:push方法的跳转会向 history 栈添加一个新的记录,当我们点击浏览器的返回按钮时可以看到之前的页面。
2)、go
页面路由跳转
前进或者后退this.$router.go(-1) // 后退
3)、replace
push方法会向 history 栈添加一个新的记录,而replace方法是替换当前的页面,
不会向 history 栈添加一个新的记录
4.5.路由之间跳转的方式1.声明式(标签跳转)
2.编程式( js跳转)
4.6.active-class是哪个组件的属性vue-router 模块 的router-link组件
4.7.vue-router实现路由懒加载(动态加载路由)把不同路由对应的组件分割成不同的代码块,然后当路由被访问时才加载对应的组件即为路由的懒加载,可以加快项目的加载速度,提高效率
const router = new vuerouter({  routes: [    {      path: '/home',      name: 'home',            component:() = import('../views/home') }  ]})
4.8.怎么定义vue-router的动态路由以及如何获取传过来的动态参数?在router目录下的index.js文件中,对path属性加上/:id
使用router对象的params id
面试官:我难道问不倒这小子了?(面试官持续懵逼中) 对大家有帮助的话三连呀~ 持续更新
【相关视频教程推荐:vuejs入门教程、web前端入门】
以上就是【吐血整理】vue.js面试题汇总及答案解析(快来收藏)的详细内容。
其它类似信息

推荐信息