Vue闪念

261 阅读5分钟

面试题:

谈一谈 nextTick 的原理:

在下次 DOM 更新循环结束之后执行延迟回调。nextTick主要使用了宏任务和微任务。根据执行环境分别尝试采用 ● Promise ● MutationObserver ● setImmediate ● 如果以上都不行则采用setTimeout 定义了一个异步方法,多次调用nextTick会将方法存入队列中,通过这个异步方法清空当前队列

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

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

vm.$set()实现原理是什么?

通过set可以对指定的属性通过Object.defineProperty增加getset从而让其具备响应式。防追问,为什么这么做?受现代JavaScript的限制(而且Object.observe也已经被废弃)Vue无法检测到对象属性的添加或删除。由于Vue会在初始化实例时对属性执行getter/setter转化,所以属性必须在data对象上存在才能让Vue将它转换为响应式的。对于已经创建的实例,Vue不允许动态添加根级别的响应式属性。但是,所以我们就可以使用vm.set可以对指定的属性 通过 Object.defineProperty 增加get 和set 从而让其具备响应式。 防追问,为什么这么做? 受现代 JavaScript 的限制 (而且 Object.observe 也已经被废弃),Vue 无法检测到对象属性的添加或删除。 由于 Vue 会在初始化实例时对属性执行 getter/setter 转化,所以属性必须在 data 对象上存在才能让 Vue 将它转换为响应式的。 对于已经创建的实例,Vue 不允许动态添加根级别的响应式属性。但是,所以我们就可以使用 vm.set(object, propertyName, value) 方法向嵌套对象添加响应式属性。

什么是MVVM框架

MVVM是Model-View-ViewModel缩写,也就是把MVC中的Controller演变成ViewModel。Model层代表数据模型,View代表UI组件,ViewModel是View和Model层的桥梁,数据会绑定到viewModel层并自动将数据渲染到页面中,视图变化的时候会通知viewModel层更新数据。

响应式数据原理(Vue2.x & Vue3.0)

Vue2.x在初始化数据时,会使用Object.defineProperty重新定义data中的所有属性,当页面使用对应属性时,首先会进行依赖收集(收集当前组件的watcher),如果属性发生变化会通知相关依赖进行派发更细(发布订阅模式)。 vue3.0采用es6中的proxy代替Object.defineProperty做数据监听。

Proxy与Object.defineProperty的优劣对比?

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

vue中组件通信的方式

  1. 父传子:props
  2. 子传父:emitrefemit、ref parent
  3. 跨组件: provide inject
  4. 中央状态管理:vuex
  5. 兄弟:EventBus

虚拟dom

● 由于dom操作耗时十分长,且dom对象的体积很大,单个div的dom属性就有294个之多; ● Virtual DOM 就是用一个原生的 JS 对象去描述一个 DOM 节点,所以它比创建一个 DOM 的代价要小很多。 ● VNode 是对真实 DOM 的一种抽象描述,它的核心定义无非就几个关键属性,标签名、数据、子节点、键值等,其它属性都是用来扩展 VNode 的灵活性以及实现一些特殊 feature 的。由于 VNode 只是用来映射到真实 DOM 的渲染,不需要包含操作 DOM 的方法,因此它是非常轻量和简单的。 ● Virtual DOM到真实的dom需要经过以下过程:VNode 的 create、diff、patch

v-model双向数据绑定原理

v-model本质上是语法糖,v-model在内部为不同的输入元素使用不同的属性并抛出不同的事件 ● text 和 textarea 元素使用 value 属性和 input 事件 ● checkbox 和 radio 使用 checked 属性和 change 事件 ● select 字段将 value 作为 prop 并将 change 作为事件

为什么v-for的key尽量不要使用index

当数据进行更改的时候,会通过key来判断虚拟dom树是否进行了更改。如果发现了相同的dom-key就可以直接复用。减少了渲染的性能损耗。数据只做展示,用index是没问题的。如果涉及到删除,添加,就不行了,因为index不再唯一了。

vue能监听到数组变化的方法有哪些?为什么这些方法能监听到呢?

push() pop() shift() unshift() splice() sort() reverse() 这七个操作数组的方法,都会对原数组产生影响,所以Vue在框架内重写过的,并不是原生的方法了,新的方法 里面增加了监听。

v-if和v-for嵌套使用的问题

v-if和v-for的优先级: 在vue2中v-for优先级高 在vue3中v-if优先级高 但是无论如何两个组合在一起使用官方都不推荐 因此同时出现,会导致先for循环子元素,在判断每个子元素的if情况。 想要先执行if,在满足的条件下再循环子元素,可以在v-for的元素外层加一个有if指令的template标签 面试题:

Vue 的⽗组件和⼦组件⽣命周期钩⼦执⾏顺序是什么?

原则:父组件搭台,子组件唱戏 创建实例与挂载阶段:父组件创建实例完毕并预备挂载,子组件一一创建实例完毕并挂载完毕,父组件宣布整体挂载完毕。 数据更新阶段:父组件预备更新,子组件一一更新完毕,父组件宣布整体更新完毕。 卸载阶段:父组件预备卸载,子组件一一卸载完毕,父组件宣布整体卸载完毕。

v-show 和 v-if 有哪些区别?

渲染条件不成立时:v-if压根就不存在(也就是不渲染),v-show不显示(display:none) 适合使用v-show的场景:频繁的切换显隐 适合使用v-if的场景:一锤子买卖(例如某某电影专区,需要VIP才可以观看)

vue-router hash 模式和 history 模式是如何实现的以及区别?

● hash 模式: 后⾯ hash 值的变化,不会导致浏览器向服务器发出请求,浏览器不发出请求,就不会刷新⻚⾯。同时通过监听 hashchange 事件可以知道 hash 发⽣了哪些变化,然后根据 hash 变化来实现更新⻚⾯部分内容的操作。 ● history 模式: history 模式的实现,主要是 HTML5 标准发布的两个 API, pushState 和 replaceState ,这两个 API 可以改变 url,但是不会发送请求。这样就可以监听 url 变化来实现更新⻚⾯部分内容的操作。

区别:

  1. url 展示上,hash 模式有 "#",history 模式没有
  2. 刷新⻚⾯时,hash 模式可以正常加载到 hash 值对应的⻚⾯,⽽ history 没有处理的话,会返回404,⼀般需要后端将所有⻚⾯都配置重定向到⾸⻚路由 防追问 优缺点 哈希模式优点:浏览器直接识别为前端页面内跳转,没有额外的前后端通信过程,不对服务端产生额外压力; 哈希模式缺点:丑陋;不利于搜索引擎优化(SEO=SearchEnginOptimization);(搜索引擎爬虫认为各path与首页就是同一个页面,在爬完首页信息后就扬长而去) H5/历史记录模式优点:漂亮;利于SEO优化; H5/历史记录模式缺点:形成额外的前后端通信过程,一定程度上降低了性能,给服务端造成一定的额外负担;

Object.defineProperty有哪些缺点?

  1. 由于 Object.defineProperty 只能对属性进⾏劫持,需要遍历对象的每个属性。⽽ Proxy 可以直接代理对象。
  2. Object.defineProperty 对新增属性需要⼿动进⾏ Observe , 由于 Object.defineProperty 劫持的是对象的属性,所以新增属性时,需要重新遍历对象,对其新 增属性再使⽤ Object.defineProperty 进⾏劫持。 也正是因为这个原因,使⽤ Vue 给 data中的数组或对象新增属性时,需要使⽤ vm.set才能保证新增的属性也是响应式的。防追问vm.set才能保证新增的属性也是响应式的。 防追问 vm.set()实现原理是什么?

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

编码阶段 ● 尽量减少data中的数据,data中的数据都会增加getter和setter,会收集对应的watcher ● v-if和v-for不能连⽤ ● 如果需要使⽤v-for给每项元素绑定事件时使⽤事件代理 ● SPA ⻚⾯采⽤keep-alive缓存组件 ● 在更多的情况下,使⽤v-if替代v-show ● key保证唯⼀ ● 使⽤路由懒加载、异步组件 ● 防抖、节流 ● 第三⽅模块按需导⼊ ● ⻓列表滚动到可视区域动态加载 ● 图⽚懒加载 SEO优化 ● 预渲染 ● 服务端渲染SSR 打包优化 ● 压缩代码 ● Tree Shaking/Scope Hoisting ● 使⽤cdn加载第三⽅模块 ● 多线程打包happypack ● splitChunks抽离公共⽂件 ● sourceMap优化 ⽤户体验 ● 骨架屏 ● PWA 还可以使⽤缓存(客户端缓存、服务端缓存)优化、服务端开启gzip压缩等。

Vue 修饰符有哪些 (太多 每个挑出两三个出来回答即可)

事件修饰符 ● .stop 阻止事件继续传播 ● .prevent 阻止标签默认行为 ● .capture 使用事件捕获模式,即元素自身触发的事件先在此处处理,然后才交由内部元素进行处理 ● .self 只当在 event.target 是当前元素自身时触发处理函数 ● .once 事件将只会触发一次 ● .passive 告诉浏览器你不想阻止事件的默认行为 v-model 的修饰符 ● .lazy 通过这个修饰符,转变为在 change 事件再同步 ● .number 自动将用户的输入值转化为数值类型 ● .trim 自动过滤用户输入的首尾空格 键盘事件的修饰符 ● .enter ● .tab ● .delete (捕获“删除”和“退格”键) ● .esc ● .space ● .up ● .down ● .left ● .right 系统修饰键 ● .ctrl ● .alt ● .shift ● .meta 鼠标按钮修饰符 ● .left ● .right ● .middle

你有写过自定义指令吗?自定义指令的生命周期(钩子函数)有哪些?

  1. bind:只调用一次,指令第一次绑定到元素时调用,用这个钩子函数可以定义一个绑定时执行一次的初始化动作。
  2. inserted:被绑定元素插入父节点时调用(父节点存在即可调用,不必存在于document中)。
  3. update:被绑定于元素所在的模板更新时调用,而无论绑定值是否变化。通过比较更新前后的绑定值,可以忽略不必要的模板更新。
  4. componentUpdated:被绑定的元素所在模板完成一次更新周期时调用。
  5. unbind:只调用一次,指令与元素解绑时调用

钩子函数的参数 (即 el、binding、vnode 和 oldVnode)。 el:指令所绑定的元素,可以用来直接操作 DOM。 binding:一个对象,包含以下 property: name:指令名,不包括 v- 前缀。 value:指令的绑定值,例如:v-my-directive="1 + 1" 中,绑定值为 2。 oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。 expression:字符串形式的指令表达式。例如 v-my-directive="1 + 1" 中,表达式为 "1 + 1"。 arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 "foo"。 modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }。 vnode:Vue 编译生成的虚拟节点。 oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。 Vue指令格式:v-指令名:指令参数.指令修饰符="指令值";

如果让样式当前组件有效? 如何修改第三方组件的样式

style标签 设置一个 scoped属性就表示当前组件内有效 。 第三方组件(类似elementui)设置样式 超逸使用 ::v-deep 新建的话用 :deep 这两个内置选择器来穿透到组件内部去覆盖它的样式。 如果使用ui组件提供有一些 变量的话(类似elementui就提供有),那么直接给这些变量重新赋值也可以进行修改。 面试题:

剖析Vue.set与vm.$set的区别?

两个API的区别是Vue.set是定义在构造函数上(static)的,而vm.$set是定义在原型对象上( const vm = new Vue({}) 继承而来)的

Vue.set(obj, key, value) 方法的实现原理?

● set() 之所以给数组和对象添加新属性可以触发响应式的数据,是因为最后调用 defineReactive() 给新增的属性添加 get/set ● 调用set() 给对象新增属性时可以触发对象依赖收集的 Watcher 去更新。 ● 调用set() 修改数组索引时,内部会通过调用 重写的 splice() 方法更新数组。 ● 调用set() 不能给根实例 vm 或 _data 添加属性,因为性能消耗大。

防追问 vm.$set()实现原理是什么? vue能监听到数组变化的方法有哪些?为什么这些方法能监听到呢?

Vue-Router 的导航守卫和执行流程

● 触发进入其他路由。 ● 调用要离开路由的组件守卫 beforeRouteLeave //先离开 ● 调用全局前置守卫:beforeEach //进来的守卫 ● 在重用的组件里调用 beforeRouteUpdate ● 调用路由独享守卫 beforeEnter。//全局进去 ● 解析异步路由组件。 //解析 ● 在将要进入的路由组件中调用 beforeRouteEnter 组件守卫 //局部进去 ● 调用全局解析守卫 beforeResolve
● 导航被确认。 //确认路径 ● 调用全局后置钩子的 afterEach 钩子。 ● 触发DOM更新 (mounted)。 ● 执行 beforeRouteEnter 守卫中传给 next 的回调函数 //跳转

Vuex 的理解

目的,作用: Vuex 是为了大型项目开发的,实现不同组件之间的状态管理(数据共享)。适用于多个组件之间的数据交互 ● Vuex 应用核心就是 store(仓库)。store 就是一个容器,包含了应用中大部分的 state(状态) ● Vuex 是单向的数据流,组件不能直接修改容器中 state 的状态(数据) ● 改变 store 中的状态的唯一途径就是显示 commit(提交) mutation,组件状态 state 发生变化。 ● 在非严格模式 strict:false 允许直接来修改状态 state,但是一般都是在严格模式中通过mutations 来修改数据。 ● 缺点:Vuex 的数据不能持久化。 vuex 在页面刷新后数据不会再存在 主要有以下几个模块: ● State: 定义了应用状态的数据结构,可以在这里设置默认的初始状态 ● Getter: 允许组件从 store 中获取数据, mapGetters 辅助函数仅仅是将 store 中的 getter映射到计算属性。 ● Mutation: 唯一更改 store 中状态的方法,且必须是同步函数。 ● Action: 用于提交 mutation, 而不是直接变更状态,可以包含任意异步操作。 ● Module: 允许将单一的 store 拆分为多个 store且同时保存在单一的状态树中

● 扩展:action 和 mutation 之间的区别

  1. mutation对象 是 Vuex中唯一一个可以修改 state 状态的方式。mutation对象 中方法必须是同步的,因为提交的 mutation 触发时回调函数还没有被调用,那么数据的状态就无法准确知道是不是最新的。
  2. action对象 用于提交的是一个 mutation 事件,不可以修改数据的状态。action 中的方法支持异步。

你有对 Vue 项目进行哪些优化?(80%的提问率)

(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 查找性能瓶颈

虚拟DOM的优劣如何?

优点: ● 保证性能下限: 虚拟DOM可以经过diff找出最小差异,然后批量进行patch,这种操作虽然比不上手动优化,但是比起粗暴的DOM操作性能要好很多,因此虚拟DOM可以保证性能下限 ● 无需手动操作DOM: 虚拟DOM的diff和patch都是在一次更新中自动进行的,我们无需手动操作DOM,极大提高开发效率 ● 跨平台: 虚拟DOM本质上是JavaScript对象,而DOM与平台强相关,相比之下虚拟DOM可以进行更方便地跨平台操作,例如服务器渲染、移动端开发等等 缺点: ● 无法进行极致优化: 在一些性能要求极高的应用中虚拟DOM无法进行针对性的极致优化,比如VScode采用直接手动操作DOM的方式进行极端的性能优化