关于vue的面试考点总结🤯

页面渲染优化

  1. html 不要嵌套过深 (减轻回流的压力)
  2. css 尽量使用精确的选择器 (减少回流的压力)
  3. 代码压缩 (减轻请求的压力)
  4. js 角度(js引擎线程和浏览器的渲染线程不会同时工作) 在script标签里面放 (async 异步加载 + 直接执行) 和 (defer 异步加载 + 延迟执行) 关键字
  5. 图片(懒加载,预加载,骨架屏,压缩,精灵图)
  6. 缓存

谈谈你对 vue 的理解

  1. 渐进式的单页应用框架 (根据项目的需求逐渐引入功能 use install 函数)
  2. MVVM 数据驱动页面更新 (model views views-model)
  3. 组件化 (可复用,方便调试)
  4. 指令
  5. 虚拟dom (跨端开发,减少dom操作,读取代码,生成虚拟dom树,给浏览器渲染)
  6. 生态完善

谈谈你对spa的理解

spa(单页面应用),整个项目只有一个页面,页面中的内容是动态的,以组件的形式展示,靠路由来映射匹配组件。

优点:

  1. 组件化开发,易于维护
  2. 页面切换快,体验好
  3. 前后端分离,提升开发效率

缺点:首屏加载时间长,不利于 SEO

可以通过服务端渲染 ssr 解决首屏加载时间长,不利于SEO的问题:

ssr服务端渲染原理:

  1. 创建 node 服务
  2. 读取 vue 组件
  3. 借助 vue 自带编译器函数编译 vue 组件,得到 AST
  4. 借助 vue 自带渲染器函数渲染 AST 得到 html
  5. 拼接 html 模板
  6. 发送响应

vue项目里面再启动一个node服务,读取需要第一个被展示出来的组件,将该组件由vue自带的编译器和渲染器,处理成代码块,最后拼接到一个html模板当中,响应给浏览器。

SPA 首屏加载优化

首屏加载慢的原因:

  1. 单页应用需要把所有页面的代码都执行完,首屏才加载
  2. 加载js脚本
  3. 网络延时

优化策略:

  1. 路由懒加载
  2. ssr 服务端渲染
  3. 骨架屏
  4. UI 框架按需加载

说说你对 vue 生命周期的理解

生命周期就是一个vue组件从创建到销毁的过程,其中官方打造了一系列的钩子函数。

  1. setup = beforeCreate + created

  2. onBeforeMount = beforeMount

  3. onMounted = mounted

  4. onBeforeUpdated = beforeUpdated

  5. onUpdated = updated

  6. onBeforeUnmount = beforeUnmount

  7. onUnmounted = unmounted

  8. onActived() 命中缓存会调用

  9. onDeactivated()

  10. 捕获子组件错误时触发 onErrorCaptured

  11. 收集依赖时触发 onRenderTracked

  12. 触发依赖时触发 onRenderTriggered 响应式值变更,调试用的

当被keep-alive标签包裹后,里面的内容就被缓存,没有执行卸载操作

说说你对双向绑定的理解

vue中的双向绑定就是v-model指令,修改数据,页面会同步更新,页面内容修改,数据也会同步更新。

原理:双向绑定由三个重要部分构成(MVVM):Model(模板层),View(视图层,浏览器的可视区域),ViewModel(vue框架的核心,将数据与视图关联起来)。

ViewModel:

  1. Observer(监听器):数据的代理
  2. Compiler(解析器):解析模板的指令

当vue组件被读取到时,将数据变成响应式以及解析模板指令,解析完指令会初始化视图,当数据源发生变化的时候就需要更新视图,通过Dep机制通知变更,通知watcher观察者,然后观察者去触发更新视图的函数。Dep充当数据与视图之间的中介,它知道哪些视图(Watcher)依赖于特定的数据,当数据变化时,能够通知到所有依赖该数据的视图。为了避免不必要的试图更新。

双向绑定的原理:

  1. 变量被处理成响应式的过程中会为变量做依赖收集,当变量的值变更时,触发setter,并执行依赖,导致试图更新。
  2. 视图更新相当于用户触发了 input 事件,修改响应式变量,进而又导致setter触发。

vue 组件通讯

list是一个数组。

父向子传值

  1. 父组件用 v-bind : 传递给子组件,子组件用 defineProps 接收数据。
js 复制代码
// parent.vue
<child :data="list"></child>

// child.vue
const props = defineProps({
  data: {
    type: String,
    default: ''
  }
})

console.log(props.data);
  1. 父组件 provide 数据,子组件 inject 注入 (全部子组件都能接收到,数据流向不明确,只能从上往下注入,在最外层的App.vue里面使用provide可以类似仓库效果)
js 复制代码
// parent.vue
import { provide } from 'vue'
provide('list', list.value)  // 向下提供数据 提供的是引用地址(子组件可以改)
provide('list', readonly(list.value))  // readonly接收一个对象,返回只读代理(不能改)

// child.vue
import { inject } from 'vue'
const list = inject('list')  // 注入数据

子向父传值

  1. 子组件通过 defineEmits 定义发布一个事件,父组件通过 v-on 订阅这个事件。
js 复制代码
// parent.vue
<Child @add="handle" />

const handle = (val) => {
  console.log(val)    // hello
}

// child.vue
const emit = defineEmits(['add'])   // 定义一个事件
const handle = () => {
  emit('add', 'hello world')   // 发布事件
}
  1. 子组件通过 defineExpose 暴露数据,父组件通过 ref 引用子组件中的数据
js 复制代码
// parent.vue
<Child ref="childRef" />

const childRef = ref(null)
console.log(childRef.value);  // 输出list的值

// childRef?.list 如果 childRef 有值执行 list,否则不执行
// :key 作用是Vue会根据该值来判断哪些元素是新的,哪些元素需要被重用,从而提高渲染效率
<li v-for="(item, index) in childRef?.list" :key="index">{{ item }}</li>


// child.vue
defineExpose({
  list
})
  1. 父组件通过 v-model 绑定数据给子组件,子组件通过 defineProps接收,然后定义 'update:xxx' 事件,并直接修改父组件给过来的数据,但是一定要发布 'update:xxx' 事件
js 复制代码
// parent.vue
<Child v-model:list="arr" />

const arr = ref(['html', 'css', 'js'])

// child.vue
const props = defineProps(['list'])   // 接收父组件传过来的值
const emits = defineEmits(['update:abc'])   // 定义 update: 事件

const add = () => {
  const arr = props.list
  arr.push('vue')
  emits('update:abc', arr)   // 发布 update: 事件
}

兄弟组件通讯

  1. 在外部的 js 文件中定义响应式变量,同时引入到两个组件中,因为是响应式的,所以两个组件都可以修改这个变量,从而实现通讯。
js 复制代码
// bus.js
export const list = ['html', 'css']

// child.vue
import { list } from './bus.js'

const add = () => {
  list.push('child')
}

// parent.vue
import { list } from './bus.js'

const add = () => {
  list.push('parent')
}
  1. pinia 状态管理库

v-if 和 v-show 的区别

  1. v-if是动态地向 DOM 树内添加或删除 DOM 元素,v-show 是通过 css 控制元素的 display属性
  2. v-if 控制的组件会触发生命周期,v-show 不会
  3. v-if 有更高的切换开销, v-show 会导致两次回流

v-if 和 v-for 可以一起使用吗?

  1. 在vue3中,v-if的优先级比v-for高
  2. 在vue2中,v-for的优先级比v-if高

data 为什么是一个函数,不能是一个对象?

如果 data 是一个对象,那么当该组件被多处使用时,会导致数据共享,会出现数据污染的问题,为了确保每个组件实例都有其独立的数据副本,避免数据污染,data 必须是一个函数,返回一个对象,每个组件实例都有自己的 data 对象

说说你对 vue 中 nextTick 的理解

nextTick 是一个异步函数,它的作用是在 DOM 更新后执行回调函数,延迟回调。

实现原理如下:

js 复制代码
<script>
  // 1. 什么时候执行 cb 回调函数
  // 2. 执行resolve函数
  function nextTick(cb) {
    return new Promise(resolve => {
      function fn() {
        return () => {
          cb()
          resolve()
        }
      }
      // 当 DOM 结构渲染完成后,再执行回调
      if (typeof MutationObserver !== 'undefined') {
        const observer = new MutationObserver(fn())  // 浏览器监听DOM的api
        observer.observe(document.body, {   // 监听dom结构更新
          childList: true,
          subtree: true
        })
      } else {//如果浏览器不支持`MutationObserver`,则使用`setTimeout`将回调函数延迟到下一个事件循环执行
        setTimeout(fn(), 0)
      }
    })
  }

  const app = document.getElementById('app')

  nextTick(() => {
    console.log(app.innerHTML);
  }).then(() => {
    console.log(app.innerHTML, 'then');
  })

  app.addEventListener('click', () => {
    app.innerHTML = 'hello world'
  })
</script>

所以:

  • 在拥有 MutationObserver 的浏览器中,通过 MutationObserver 来监听 DOM 的变化,触发会回调
  • 在不支持 MutationObserver 的环境中,使用 setTimeout 来模拟 MutationObserver,当 DOM 变化时,触发回调

说说你对 slot 的理解

slot vue中的插槽功能,是组件中的一个占位符,用于接收该组件标签中的内容

  1. 匿名 slot
  2. 具名 slot
  3. 作用域 slot
  4. 条件 slot
js 复制代码
// 匿名 slot
// layout.vue
<template>
  <div class="head">
    <slot>
       xxxxxx
    </slot>
  </div>
  <div class="body">
    <div class="left"></div>
    <div class="right"></div>
  </div>
</template>

// App.vue
<template>
  <layout>
    <template v-slot>
      <div>hello world</div>
      <p>vue</p>
    </template>
  </layout>
</template>
js 复制代码
// 具名 slot
// layout.vue
<template>
  <div class="head">
    <slot name="head">

    </slot>
  </div>
  <div class="body">
    <div class="left"></div>
    <div class="right">
      <slot name="right">

      </slot>
    </div>
  </div>
</template>

// App.vue
<template>
  <layout>
    <template v-slot:head>
      <div>hello world</div>
    </template>
    <template v-slot:right>
      <p>vue</p>
    </template>
  </layout>
</template>
js 复制代码
// 作用域 slot
// layout.vue
<template>
  <div class="head">
    <slot name="head" :user="{ name: '张三' }">

    </slot>
  </div>
</template>

// App.vue
<template>
  <layout>
    <template v-slot:head="props">
      <div>{{ props.user.name }}</div> 
    </template>
  </layout>
</template>
js 复制代码
// 条件 slot
// layout.vue
<template>
  // 如果有head就显示
  <div class="head" v-if="$slots.head">  
    <slot name="head"></slot>
    <slot name="left"></slot>
  </div>
</template>

// App.vue
// 不显示
<template>
  <layout>
    <template v-slot:left>你好</template>   
  </layout>
</template>

应用场景:layout布局组件。

为什么要使用 key? index 做 key 有什么问题?

  • key 大大提高了 diff 的效率,减少了不必要的渲染
  • index 做 key 等同于没有 key,会导致性能问题

什么是虚拟 DOM?

用 js 对象作为树,使用对象的属性来描述节点的状态,用这个对象来描述真实的 DOM 树,这个对象就是虚拟 DOM。最少包含 tag,props,children 三个属性。

  • 减少了真实 DOM 的操作带来页面渲染的性能开销 传给了 v8 引擎
  • 抽象了原本的渲染过程,实现了跨平台开发的能力

diff 算法

diff 算法是一种对象的比较算法,效率很高,在拥有虚拟 DOM 的框架中被使用。

在vue中,拥有虚拟dom这个过程,所以,vue要先将模板代码编译成虚拟dom对象后,再拿虚拟dom对象生成真实的dom交给浏览器渲染,当响应式变量值发生改变时,会重新生成新的虚拟dom对象,为了考虑到降低渲染开销,需要用diff算法来比较,找出新的虚拟dom对象和老的dom对象的区别,把区别单独拎出来后去更新真实的dom结构。

原理:

  1. 比较旧 DOM 虚拟对象的根节点,如果根节点不同,直接替换整个 DOM 树
  2. 如果根节点相同,比较根节点的属性,如果不同,修改根节点的属性
  3. 如果根节点的属性相同,比较根节点的子节点,同层级的子节点进行比较,如果不同,替换该子节点以下的 DOM 树。
  4. 判断是不是文本节点,如果是文本节点,直接修改文本节点的内容
  5. 采用深度优先遍历对比子节点(对比的过程中,有旧没新,删除旧节点,反之增加新节点)

同层级的子节点比较:(双端队列)

  1. 设置新旧 Vnode 的头尾指针、
  2. 新旧头尾指针进行比较,向中间靠拢,这个过程包含 4 种情况,分别是:头头比,尾尾比,头尾比,尾头比

了解过 vue2 吗?vue3 和 vue2 的区别是什么?

  1. 速度更快
  • 重写了虚拟 DOM 的实现,使用 proxy 代替了 Object.defineProperty
  • 编译模板的优化,采用了静态提升策略,将静态节点缓存起来,减少了编译的时间
  1. 体积更小 (组合式api,实现按需分配的能力)
  2. 更易维护 (完全兼容 vue2 的语法,还可以搭配 vue3 模板使用)
  3. 更接近原生
  4. 更友好的 ts 支持
相关推荐
^小桃冰茶3 小时前
CSS知识总结
前端·css
运维@小兵3 小时前
vue注册用户使用v-model实现数据双向绑定
javascript·vue.js·ecmascript
巴巴_羊4 小时前
yarn npm pnpm
前端·npm·node.js
chéng ௹6 小时前
vue2 上传pdf,拖拽盖章,下载图片
前端·css·pdf
嗯.~6 小时前
【无标题】如何在sheel中运行Spark
前端·javascript·c#
A_aspectJ8 小时前
【Bootstrap V4系列】学习入门教程之 组件-输入组(Input group)
前端·css·学习·bootstrap·html
兆。8 小时前
电子商城后台管理平台-Flask Vue项目开发
前端·vue.js·后端·python·flask
互联网搬砖老肖9 小时前
Web 架构之负载均衡全解析
前端·架构·负载均衡
sunbyte9 小时前
Tailwind CSS v4 主题化实践入门(自定义 Theme + 主题模式切换)✨
前端·javascript·css·tailwindcss
大学生小郑10 小时前
Go语言八股之channel详解
面试·golang