深度监听对象

有如下数据,实现对obj的深度监听

data() {
  return {
    obj: {
      name: '张三',
      age: 21,
    }
  }
}

结合computed

将要监听的属性使用computed计算出来,然后监听这个计算属性,如下

computed: {
  oName() {
    return this.obj.name
  }
},
watch: {
  oName(newValue) {
    console.log('name值变为'+newValue)
  }
}

深度监听

利用Vue自身的深度监听,设置深度监听为true,如下

watch: {
  obj: {
    deep: true,
    handler: (newValue) => {
      console.log('name值变为'+newValue)
    }
  }
}

什么是MVVM

传统的MVC(Model-View-Controller)中,当数据Model发生变化时由控制器Controller控制视图View的更新。

随着前端页面越来越复杂以及单页应用的出现,MVC模型已经不再适应前端的开发,MVVM(Model-View-ViewModel)应运而生 ,MVVM中Model和View没有直接联系,通过ViewModel进行交互,而且是双向的,这样开发时就不再需要关注View,只需要对数据进行处理就可以了

响应式数据的原理

Vue在初始化时,会给data中属性通过Object.defineProperty()重新定义所有属性,当页面取到对应的属性是会进行依赖收集,如果属性发生变化会通知相关依赖进行更新

image-20210118133633090

Vue如何检测数组变化

  • 使用函数劫持的方法,重写数组方法
  • Vue将data中的数组进行了原型链重写,指向了自己顶一个数组原型链方法,在调用数组API时,可以通知依赖更新,如果数组中存在引用类型,,会对引用类型再次进行监控

image-20210118133615121

为什么Vue采用异步渲染

如果不采用异步更新,那么每次数据变化都会引起组件的重新渲染,为了性能考虑,Vue会在本轮数据更新后再去异步渲染

image-20210118133602510

nextTick原理

nextTick 方法主要是使用了宏任务和微任务,定义了一个异步方法.多次调用 nextTick 会将方法存入 队列中,通过这个异步方法清空当前队列。 所以这个 nextTick 方法就是异步方法

image-20210118133538831

Computed和Watch的区别

Computed和Watch都是watcher,区别在于Computed具有缓存功能

image-20210118134706804

Watch中的deep: true怎么实现的

当用户指定了 watch 中的deep属性为 true 时,如果当前监控的值是数组类型。会对对象中的每 一项进行求值,此时会将当前 watcher 存入到对应属性的依赖中,这样数组中对象发生变化时也 会通知数据更新

生命周期

image-20210118141813195

ajax请求放在哪个生命周期中

  • 在created的时候,视图中的 dom 并没有渲染出来,所以此时如果直接去操 dom 节点,无法找到相 关的元素

  • 在mounted中,由于此时 dom 已经渲染出来了,所以可以直接操作 dom 节点 ;一般情况下都放到 mounted 中,保证逻辑的统一性,因为生命周期是同步执行的, ajax 是异步执行的

  • 服务端渲染不支持mounted方法,所以在服务端渲染的情况下统一放到created中

何时需要使用beforeDestroy

  1. 可能在当前页面中使用了 $on 方法,那需要在组件销毁前解绑。
  2. 清除自己定义的定时器
  3. 解除事件的绑定 scroll mousemove ….

Vue中模板编译原理

  • 第一步是将 模板字符串 转换成 element ASTs(解析器)
  • 第二步是对 AST 进行静态节点标记,主要用来做虚拟DOM的渲染优化(优化器)
  • 第三步是 使用 element ASTs 生成 render 函数代码字符串(代码生成器)

Vue中v-if和v-show的区别

v-if 如果条件不成立不会渲染当前指令所在节点的 dom 元素

v-show 只是切换当前 dom 的显示或者隐藏(display: none)

为什么V-for和v-if不能连用

v-for 会比 v-if 的优先级高一些,如果连用的话会把 v-if 给每个元素都添加一下,会造成性能问题

如果需要连用,可以使用计算属性,先处理要遍历的数据,筛选之后再遍历

<template>
    <ul>
    <li v-for="computeData"></li>
  </ul>
</template>
<script>
    data() {
    return {
      initData: [
        {flag: true, name: 'aaa'},
        {flag: false, name: 'bbb'},
      ]
    }
  },
  computed: {
    computeData() {
      this.initData.map((item) => {
        return item.flag
      })
    }
  }
</script>

diff算法的时间复杂度

两个树的完全的 diff 算法是一个时间复杂度为 O(n³) , Vue 进行了优化O(n³) 复杂度的问题转换成 O(n) 复杂度的问题(只比较同级不考虑跨级问题) 在前端当中, 你很少会跨越层级地移动Dom元素。 所 以 Virtual Dom只会对同一个层级的元素进行对比

diff原理

  1. 先同级比较,在比较子节点
  2. 先判断一方有儿子一方没儿子的情况
  3. 比较都有儿子的情况
  4. 递归比较子节点

v-for中为什么要有key

image-20210119195353025

组件中的 data为什么是一个函数

同一个组件被复用多次,会创建多个实例。这些实例用的是同一个构造函数,如果 data 是一个对象的 话。那么所有组件都共享了同一个对象。为了保证组件的数据独立性要求每个组件必须通过 data 函数 返回一个对象作为组件的状态

一个组件被使用多次,用的都是同一个构造函数。为了保证组件的不同的实例data不冲突,要求 data必须是一个函数,这样组件间不会相互影响

Vue中事件绑定的原理

Vue 的事件绑定分为两种一种是原生的事件绑定,还有一种是组件的事件绑定

  • 原生 dom 事件的绑定,采用的是 addEventListener 实现
  • 组件绑定事件采用的是 $on 方法 原理

image-20210119195735862

Vue中常见性能优化

1.编码优化:

  1. 不要将所有的数据都放在data中,data中的数据都会增加getter和setter,会收集对应的 watcher

  2. vue 在 v-for 时给每项元素绑定事件需要用事件代理

  3. SPA 页面采用keep-alive缓存组件

  4. 拆分组件( 提高复用性、增加代码的可维护性,减少不必要的渲染 )

  5. v-if 当值为false时内部指令不会执行,具有阻断功能,很多情况下使用v-if替代v-show

  6. key 保证唯一性 ( 默认 vue 会采用就地复用策略 )

  7. Object.freeze 冻结数据

  8. 合理使用路由懒加载、异步组件

  9. 尽量采用runtime运行时版本

  10. 数据持久化的问题 (防抖、节流)

2.Vue加载性能优化:
第三方模块按需导入 ( babel-plugin-component )

滚动到可视区域动态加载

图片懒加载

3.用户体验:
app-skeleton 骨架屏

app-shell app壳

pwa serviceworker

4.SEO优化:
预渲染插件 prerender-spa-plugin

服务端渲染 ssr

5.打包优化:
使用 cdn 的方式加载第三方模块 多线程打包 happypack splitChunks 抽离公共文件 sourceMap 生成

6.缓存,压缩
客户端缓存、服务端缓存 服务端 gzip 压缩

参考资料

Vue面试题汇总一

Vue面试题汇总二


前端小白