女程序员重返前端之路——VUE面试题

3,201 阅读6分钟

两年没有在发新的文章了,是因为生了个娃娃。
说起生孩子不得不说是每个程序员 每个职业女性都要经历的事吧!很遗憾我没有能平衡好家庭与事业。
在这两年时间里我把我全部的重心都放在孩子身上。技术在不断的更新进步,而我却停了下了。
时间消磨了我,技术也抛弃了我。 经过一番思考,我决定重新来过,希望一切都还来得及。
持续了半年的疫情有了的明显的好转,5月初我把孩子安排在了老家,来了北京。
正式开启了我的前端面试之路!!! 半个月的时间过去了,投递了上百份简历也石沉大海
我开始反思,开始沉淀,开始总结 ....

下面是我近期总结的vue面试题及个人版答案
(ps:不是标准答案,希望大家可以帮忙指出错误和纠正错误,不想再孤军奋战了 )

vue生命周期面试题

vue 生命周期是什么?
Vue 实例从创建到销毁的过程,就是生命周期
beforeCreate阶段:vue实例的挂载元素el和数据对象data都是undefined,还没有初始化。
created阶段:vue实例的数据对象data有了,可以访问里面的数据和方法,未挂载到DOM,el还没有
beforeMount阶段:vue实例的el和data都初始化了,但是挂载之前为虚拟的dom节点
mounted阶段:vue实例挂载到真实DOM上,就可以通过DOM获取DOM节点
beforeUpdate阶段:响应式数据更新时调用,发生在虚拟DOM打补丁之前,适合在更新之前访问现有的DOM,比如手动移除已添加的事件监听器
updated阶段:虚拟DOM重新渲染和打补丁之后调用,组成新的DOM已经更新,避免在这个钩子函数中操作数据,防止死循环
beforeDestroy阶段:实例销毁前调用,实例还可以用,this能获取到实例,常用于销毁定时器,解绑事件
destroyed阶段:实例销毁后调用,调用后所有事件监听器会被移除,所有的子实例都会被销毁

vue生命周期的作用是什么?
它的生命周期中有多个事件钩子,让我们在控制整个Vue实例的过程时更容易形成好的逻辑

每个生命周期适合哪些场景?
生命周期钩子的一些使用方法:
beforecreate : 可以在这加个loading事件,在加载实例时触发
created : 初始化完成时的事件写在这里,如在这结束loading事件,异步请求也适宜在这里调用
mounted : 挂载元素,获取到DOM节点
updated : 如果对数据统一处理,在这里写上相应函数
beforeDestroy : 可以做一个确认停止事件的确认框
nextTick : 更新数据后立即操作dom

第一次页面加载会触发哪几个钩子?
第一次页面加载时会触发 beforeCreate, created, beforeMount, mounted 这几个钩子

在beforeDestroy中我们都可以做那些事情
1.及时销毁自定义事件,否则可能造成内存泄露
2.解除绑定销毁子组件以及事件监听器

何时需要使用beforeDestory?
1.解绑自定义事件event.$off
2.清除定时器
3.解绑自定义的dom事件,如window scroll 等

ajax请求应该放在那个生命周期
mounted(整个渲染完成,dom加载完成)
js是单线程,ajax异步获取数据
放在mounted 之前没有用,只会让逻辑更混乱

父子组件的生命周期触发顺序?
加载渲染过程:
父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted
子组件更新过程:
父beforeUpdate->子beforeUpdate->子updated->父updated
父组件更新过程:
父beforeUpdate->父updated
销毁过程:
父beforeDestroy->子beforeDestroy->子destroyed->父destroyed

vue基本操作及原理面试题

v-show和v-if的区别?
v-show 是通过css 设置display控制显示和隐藏
v-if是组件真正的渲染和销毁,而不是显示和隐藏
频繁切换显示状态v-show,否则用v-if

Vue 中的 key 有什么作用? Vue 中 key 的作用是:key 是为 Vue 中 vnode 的唯一标记,通过这个 key,我们的 diff 操作可以更准确、更快速 更准确:因为带 key 就不是就地复用了,在 sameNode 函数 a.key === b.key 对比中可以避免就地复用的情况。所以会更加准确。 更快速:利用 key 的唯一性生成 map 对象来获取对应节点,比遍历方式更快

ref的作用
1.获取dom元素this.$refs.box
2.获取子组件中的datathis.$refs.box.msg
调用子组件中的方法this.$refs.box.open()

computed 和 watch 的区别和运用的场景?
computed: 是计算属性,依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值;
watch: 更多的是「观察」的作用,类似于某些数据的监听回调 ,每当监听的数据变化时都会执行回调进行后续操作;
运用场景:
1.当我们需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算;
2.当我们需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许我们执行异步操作 ( 访问一个 API ),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。

如何将组件所有props传递给子组件?

<user v-bind = "$props"/>

多个组件有相同的逻辑,如何抽离?
mixin核心方法是 mergeOptions
可以帮助你合并数据,合并方法,合并生命周期

双向数据绑定的实现原理
1.vue 双向数据绑定是通过 数据劫持 结合 发布订阅模式的方式来实现的, 组件data的数据一旦变化,立刻触发视图的更新
2.核心 API - Object.defineProperty

Object.defineProperty缺点:
1.深度监听,需要递归到底,一次性计算量大
2.无法监听新增属性/删除属性(vue2.0可以使用vue.set vue.delete)
3.无法原生监听数组,需要特殊处理

Proxy 的优势
Proxy 可以直接监听对象而非属性
Proxy 可以直接监听数组的变化
Proxy 有多达 13 种拦截方法,不限于apply、ownKeys、deleteProperty、has 等等是 Object.defineProperty 不具备的
Proxy 返回的是一个新对象,我们可以只操作新的对象达到目的,而 Object.defineProperty 只能遍历对象属性直接修改
Proxy 作为新标准将受到浏览器厂商重点持续的性能优化,也就是传说中的新标准的性能红利

为何组件data 必须是函数?
因为组件是可以复用的,JS 里对象是引用关系,如果组件 data 是一个对象,那么子组件中的 data 属性值会互相污染,产生副作用。
所以一个组件的 data必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝。
new Vue 的实例是不会被复用的,因此不存在以上问题。

如何监听数组变化
1.重新定义原型

//重新定义数组的原型prototype
const oldArrayProperty=Array.prototype
//创建新对象,原型指向oldArrayProperty,再扩展新的方法不会影响原型
const arrProto=Object.create(oldArrayProperty)

2.重写push pop等方法
3.proxy可以原生支持监听数组变化

何时使用异步组件?
加载大组件 路由异步加载

vue 为何是异步渲染,$nextTick何用?
vue 是组件级更新 为了防止每次修改数据都更新视图,所以使用异步渲染

异步渲染(以及合并data修改)提高渲染性能
$nextTick在DOM更新完之后,触发回调

何时需要使用keep-alive?
缓存组件,不需要重复渲染
如多个静态tab页面切换,可以优化性能
常用的2个属性 include exclude
两个生命周期 activated deactivated
LRU算法

vue 常见的性能优化方式
合理使用v-show和v-if
合理使用computed 有缓存
v-for加key,以及避免和v-if同时使用
自定义事件,DOM 事件即时销毁 (会导致内存泄露)
合理使用异步组件
合理使用keep-alive
data层级不要太深,扁平化(深度监听时的一次性监听完成)
使用vue-loader 在开发环境做模板编译(预编译)
webpack 层级的优化------
前端通用的性能优化,如 图片懒加载....
使用SSR

Vue 组件间通讯有哪几种方式?
1.props/ $emit适用 父子组件通信 这种方法是 Vue 组件的基础,相信大部分同学耳闻能详,所以此处就不举例展开介绍。
2.ref$parent/ $children 适用 父子组件通信
ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例
$parent / $children:访问父 / 子实例
3. EventBus ($emit / $on) 适用于 父子、隔代、兄弟组件通信
这种方法通过一个空的 Vue 实例作为中央事件总线(事件中心),用它来触发事件和监听事件,从而实现任何组件间的通信,包括父子、隔代、兄弟组件 4.$attrs/$listeners 适用于 隔代组件通信
$attrs:包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 ( class 和 style 除外 )。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 ( class 和 style 除外 ),并且可以通过 v-bind="$attrs" 传入内部组件。通常配合 inheritAttrs 选项一起使用。
$listeners:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件
5.provide / inject 适用于 隔代组件通信
祖先组件中通过 provider 来提供变量,然后在子孙组件中通过 inject 来注入变量。
provide / inject API 主要解决了跨级组件间的通信问题,不过它的使用场景,主要是子组件获取上级组件的状态,跨级组件间建立了一种主动提供与依赖注入的关系。
6.Vuex 适用于 父子、隔代、兄弟组件通信
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。每一个 Vuex 应用的核心就是 store(仓库)。“store” 基本上就是一个容器,它包含着你的应用中大部分的状态 ( state )。

Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化。

vue3.0 有那些改进?
vue3采用TS来编写
支持 composition API 解决了mixin的问题,让代码更有条理性
vue3 中的响应式数据原理改成proxy
vdom的对比算法更新,只更新vdom的绑定来动态数组部分

vuex 中action 和mutation 有何区别
mutaction 是同步更新数据(内部会进行是否为异步方式更新数据检测) $watch 在严格模式下会报错
action 异步操作,可以获取数据后调用mutaction 提交最终数据

vue-router 常见的路由模式?
hash 默认 h5 history (需要服务端支持)
实现的方式: hash onhashchange h5 history history.pushState

如何配置vue-router 异步加载?
在component 中配置 import

请用Vnode 描述一个DOM结构

<div id="div1" calss="container">
    <p>vdom</p>
    <ul style="font-size:20px">
       <li>a</li>
    </ul>
</div>
//vnode
{
    tag:'div',
    props:{
        className:'container',
        id:'div1'
    }
    children:[
    {
        tag:'p',
        children:'vdom'
    },
    {
        tag:'ul',
        props:{ style:'font-size :20px'},
        children:[
        {
            tag:'li',
            children:'a'
        }
        ]
    }
    ]
}