2023前端一面vue面试题合集_2023-02-27

2023-02-27 13:59:29 浏览数 (1)

函数式组件优势和原理

函数组件的特点

  1. 函数式组件需要在声明组件是指定 functional:true
  2. 不需要实例化,所以没有this,this通过render函数的第二个参数context来代替
  3. 没有生命周期钩子函数,不能使用计算属性,watch
  4. 不能通过$emit 对外暴露事件,调用事件只能通过context.listeners.click的方式调用外部传入的事件
  5. 因为函数式组件是没有实例化的,所以在外部通过ref去引用组件时,实际引用的是HTMLElement
  6. 函数式组件的props可以不用显示声明,所以没有在props里面声明的属性都会被自动隐式解析为prop,而普通组件所有未声明的属性都解析到$attrs里面,并自动挂载到组件根元素上面(可以通过inheritAttrs属性禁止)

优点

  1. 由于函数式组件不需要实例化,无状态,没有生命周期,所以渲染性能要好于普通组件
  2. 函数式组件结构比较简单,代码结构更清晰

使用场景:

  • 一个简单的展示组件,作为容器组件使用 比如 router-view 就是一个函数式组件
  • “高阶组件”——用于接收一个组件作为参数,返回一个被包装过的组件

例子

代码语言:javascript复制
Vue.component('functional',{ // 构造函数产生虚拟节点的
    functional:true, // 函数式组件 // data={attrs:{}}
    render(h){
        return h('div','test')
    }
})
const vm = new Vue({
    el: '#app'
})

源码相关

代码语言:javascript复制
// functional component
if (isTrue(Ctor.options.functional)) { // 带有functional的属性的就是函数式组件
  return createFunctionalComponent(Ctor, propsData, data, context, children)
}

// extract listeners, since these needs to be treated as
// child component listeners instead of DOM listeners
const listeners = data.on // 处理事件
// replace with listeners with .native modifier
// so it gets processed during parent component patch.
data.on = data.nativeOn // 处理原生事件

// install component management hooks onto the placeholder node
installComponentHooks(data) // 安装组件相关钩子 (函数式组件没有调用此方法,从而性能高于普通组件)

双向数据绑定的原理

Vue.js 是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。主要分为以下几个步骤:

  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变更的双向绑定效果。

Vue组件如何通信?

Vue组件通信的方法如下:

  • props/$emit v-on: 通过props将数据自上而下传递,而通过$emit和v-on来向上传递信息。
  • EventBus: 通过EventBus进行信息的发布与订阅
  • vuex: 是全局数据管理库,可以通过vuex管理全局的数据流
  • $attrs/$listeners: Vue2.4中加入的$attrs/$listeners可以进行跨级的组件通信
  • provide/inject:以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效,这成为了跨组件通信的基础

还有一些用solt插槽或者ref实例进行通信的,使用场景过于有限就不赘述了。

Vue.extend 作用和原理

官方解释:Vue.extend 使用基础 Vue 构造器,创建一个“子类”。参数是一个包含组件选项的对象。

其实就是一个子类构造器 是 Vue 组件的核心 api 实现思路就是使用原型继承的方法返回了 Vue 的子类 并且利用 mergeOptions 把传入组件的 options 和父类的 options 进行了合并

虚拟DOM实现原理?

  • 虚拟DOM本质上是JavaScript对象,是对真实DOM的抽象
  • 状态变更时,记录新树和旧树的差异
  • 最后把差异更新到真正的dom中

说说Vue的生命周期吧

什么时候被调用?

  • beforeCreate :实例初始化之后,数据观测之前调用
  • created:实例创建万之后调用。实例完成:数据观测、属性和方法的运算、watch/event 事件回调。无$el .
  • beforeMount:在挂载之前调用,相关render 函数首次被调用
  • mounted:了被新创建的vm.$el替换,并挂载到实例上去之后调用改钩子。
  • beforeUpdate:数据更新前调用,发生在虚拟DOM重新渲染和打补丁,在这之后会调用改钩子。
  • updated:由于数据更改导致的虚拟DOM重新渲染和打补丁,在这之后会调用改钩子。
  • beforeDestroy:实例销毁前调用,实例仍然可用。
  • destroyed:实例销毁之后调用,调用后,Vue实例指示的所有东西都会解绑,所有事件监听器和所有子实例都会被移除

每个生命周期内部可以做什么?

  • created:实例已经创建完成,因为他是最早触发的,所以可以进行一些数据、资源的请求。
  • mounted:实例已经挂载完成,可以进行一些DOM操作。
  • beforeUpdate:可以在这个钩子中进一步的更改状态,不会触发重渲染。
  • updated:可以执行依赖于DOM的操作,但是要避免更改状态,可能会导致更新无线循环。
  • destroyed:可以执行一些优化操作,清空计时器,解除绑定事件。

ajax放在哪个生命周期?:一般放在mounted 中,保证逻辑统一性,因为生命周期是同步执行的,ajax 是异步执行的。单数服务端渲染ssr 同一放在created 中,因为服务端渲染不支持mounted 方法。 什么时候使用beforeDestroy?:当前页面使用$on ,需要解绑事件。清楚定时器。解除事件绑定,scroll mousemove

$nextTick 是什么?

Vue 实现响应式并不是在数据发生后立即更新 DOM,使用 vm.$nextTick 是在下次 DOM 更新循环结束之后立即执行延迟回调。在修改数据之后使用,则可以在回调中获取更新后的 DOM

diff算法

代码语言:javascript复制
<details open=""><summary><b>答案</b></summary>
<p>
</p><p><strong>时间复杂度:</strong> 个树的完全<code> diff</code> 算法是一个时间复杂度为<code> O(n*3)</code> ,vue进行优化转化成<code> O(n)</code> 。</p>
<p><strong>理解:</strong></p>
<ul>
<li>
<p>最小量更新,<code> key</code> 很重要。这个可以是这个节点的唯一标识,告诉<code> diff</code> 算法,在更改前后它们是同一个DOM节点</p>
<ul>
<li>扩展<code> v-for</code> 为什么要有<code> key</code> ,没有<code> key</code> 会暴力复用,举例子的话随便说一个比如移动节点或者增加节点(修改DOM),加<code> key</code> 只会移动减少操作DOM。</li>
</ul>
</li>
<li>
<p>只有是同一个虚拟节点才会进行精细化比较,否则就是暴力删除旧的,插入新的。</p>
</li>
<li>
<p>只进行同层比较,不会进行跨层比较。</p>
</li>
</ul>
<p><strong>diff算法的优化策略</strong>:四种命中查找,四个指针</p>
<ol>
<li>
<p>旧前与新前(先比开头,后插入和删除节点的这种情况)</p>
</li>
<li>
<p>旧后与新后(比结尾,前插入或删除的情况)</p>
</li>
<li>
<p>旧前与新后(头与尾比,此种发生了,涉及移动节点,那么新前指向的节点,移动到旧后之后)</p>
</li>
<li>
<p>旧后与新前(尾与头比,此种发生了,涉及移动节点,那么新前指向的节点,移动到旧前之前)</p>
</li>
</ol>
<p></p>
</details>

--- 问完上面这些如果都能很清楚的话,基本O了 ---

以下的这些简单的概念,你肯定也是没有问题的啦

0 人点赞