Vue 面试专题

255 阅读3分钟

vue 常见面试题

  1. v-show和v-if 的区别
  2. 为何v-for中要用到key
  3. 描述Vue组件生命周期(有父子组件的情况)
  4. Vue组件如何通讯
  5. 描述组件渲染和更新的过程
  6. 双向数据绑定v-model的实现原理

基础

Vue基本使用

1、指令、插值

指令:

  • v-text
  • v-html
  • v-show
  • v-if
  • v-else
  • v-else-if
  • v-for
  • v-on
  • v-bind
  • v-model
  • v-slot
  • v-pre
  • v-cloak
  • v-once

a、插值、表达式

b、指令、动态属性

c、v-html:会有xss风险、会覆盖子组件

2、computed和watch

computed有缓存,data不变则不会重新计算

watch 如何深度监听

info:{
    handler(oldVal,val){
        
    },
    deep:true
}

watch监听引用类型,拿不到oldVal

3、class和style

  • 使用动态属性
  • 使用驼峰式写法

4、条件渲染

  • v-if v-else的用法,可使用变量,也可以使用===表达式
  • v-if 和v-show的区别
  • v-if和v-show的使用场景

5、事件

【观察】事件被绑定到哪里

  • event是原生的
  • 事件被挂在到当前元素

组件

Vue组件使用

1. props和$emit

2. 组件间通讯-自定义事件

  • event.$emit('add',this.add)
  • event.$on('add',this.addHandler)

3、组件生命周期

  • 挂载阶段
  • 更新阶段
  • 销毁阶段

高级

Vue高级特性

  • a、自定义v-model
  • b、$nextTick
  • c、slot
  • d、动态、异步组件
  • e、keep-alive
    • 频繁切换,不需要重复渲染
  • f、mixin
    • 变量来源不明确,不利于阅读
    • 多mixin可能会造成命名冲突
    • mixin和组件可能出现多对多的关系,复杂度较高

Vue 是异步渲染

data 改变之后,DOM不会立刻渲染 $nextTick会在DOM渲染之后被触发,以获取最新DOM节点

  1. 异步组件:图表
  2. import() 函数
  3. 第三ui库按需加载

vuex 使用

  • dispatch
  • commit
  • mapstate
  • mapGetters
  • mapActions
  • mapMutations

vue-router

  • 动态路由
  • to和push
  • hash和history
  • 懒加载(配合动态组件)

vue 原理

  • 面试为何会考察原理
  • 面试中如何考察?以何种方式
  • 考察重点,而不是考察细节。掌握好2/8原则
  • 和使用相关联的原理,例如vdom,模板渲染
  • 整体流程是否全面?热门技术是否有深度

Vue原理包括那些?

  • 组件化
  • 响应式
  • vdom和diff
  • 模板编译
  • 组件渲染过程
  • 前端路由

组件化基础

“很久以前”就有组件化
asp jsp php 已经有组件化
node.js 
数据驱动视图(MVVM、setState)
传统组件,只是静态渲染,更新还要依赖于操作DOM
数据驱动视图 - Vue MVVM
数据驱动视图 - React setState
Vue 响应式
组件data的数据一旦变化,立刻触发视图的更新
实现数据驱动视图的第一步

核心API - Object.defineProperty

Vue3.0 Proxy

  • Proxy 有兼容性问题
  • Proxy 兼容性不好,且无法polyfill

Object.defineProperty基本使用

Object.defineProperty 实现响应式

  • 监听对象,监听数组
  • 复杂对象,深度监听
  • 几个缺点
  • 深度监听,需要递归到底,一次性计算量大
  • 无法监听到新增属性/删除属性 (Vue.set Vue.delete)
  • 无法原生监听数组,需要特殊处理

// 触发更新视图 function updateView() { console.log('视图更新') }

// 重新定义数组原型

const oldArrayProperty = Array.prototype
// 创建新对象,原型指向 oldArrayProperty ,再扩展新的方法不会影响原型
const arrProto = Object.create(oldArrayProperty);
['push', 'pop', 'shift', 'unshift', 'splice'].forEach(methodName => {
    arrProto[methodName] = function () {
        updateView() // 触发视图更新
        oldArrayProperty[methodName].call(this, ...arguments)
        // Array.prototype.push.call(this, ...arguments)
    }
})

// 重新定义属性,监听起来

function defineReactive(target, key, value) {
    // 深度监听
    observer(value)

    // 核心 API
    Object.defineProperty(target, key, {
        get() {
            return value
        },
        set(newValue) {
            if (newValue !== value) {
                // 深度监听
                observer(newValue)

                // 设置新值
                // 注意,value 一直在闭包中,此处设置完之后,再 get 时也是会获取最新的值
                value = newValue

                // 触发更新视图
                updateView()
            }
        }
    })
}

// 监听对象属性

function observer(target) {
    if (typeof target !== 'object' || target === null) {
        // 不是对象或数组
        return target
    }

    // 污染全局的 Array 原型
    // Array.prototype.push = function () {
    //     updateView()
    //     ...
    // }

    if (Array.isArray(target)) {
        target.__proto__ = arrProto
    }

    // 重新定义各个属性(for in 也可以遍历数组)
    for (let key in target) {
        defineReactive(target, key, target[key])
    }
}

// 准备数据

const data = {
    name: 'zhangsan',
    age: 20,
    info: {
        address: '北京' // 需要深度监听
    },
    nums: [10, 20, 30]
}

// 监听数据 observer(data)

// 测试

// data.name = 'lisi'
// data.age = 21
// // console.log('age', data.age)
// data.x = '100' // 新增属性,监听不到 —— 所以有 Vue.set
// delete data.name // 删除属性,监听不到 —— 所有已 Vue.delete
// data.info.address = '上海' // 深度监听
data.nums.push(4) // 监听数组

虚拟DOM(Virtual DOM) 和diff

  • vdom实现vue和React的重要基石
  • diff 算法是vdom 中最核心、最关键的部分
  • vdom 是一个热门话题,也是面试中的热门问题

DOM操作非常消耗性能

  • 以前用jq,可以自行控制DOM 操作的时机,手动调整
  • Vue和React是数据驱动视图,如何有效控制DOM 操作?

解决方案 - vdom

  • 有了一定复杂度,想减少计算次数比较难
  • 能不能把计算,更多的转移为js计算?因为js执行速度很快
  • vdom-用js模拟dom结构,计算出最小的变更,操作dom

通过snabbdom学习vdom

  • 简洁强大的vdom库,易学易用
  • Vue参考它实现的vdom和diff
  1. diff 算法
  2. diff算法是vdom中最核心,最关键的部分
  3. diff算法能在日常使用vue React 中体现出来(如key)
  4. diff算法是前端热门话题
  5. diff 算法总结
  6. patchVnode
  7. addVnodes removeVnodes
  8. updateChildren(key 的重要性)
  9. vdom 和diff 总结

细节不重要,updateChildren vdom核心概念很主要:h、vnode、patch、diff、key等 vdom 存在的价值更加重要:数据驱动视图,控制DOM操作

模板编译

模板是vue开发最常见的部分,即与使用相关联的原理 它不是html,有指令,插值,js表达式,到底是什么? 面试不会直接问题,但会通过“组件渲染和更新过程”考察

vue template complier 将模板编译为render 函数 执行render 函数生成vnode

编译模板

模板编译为render函数,执行render函数返回vnode 基于vnode再执行patch和diff 使用webpack vue-loader,会在开发环境下编译模板(重要)

组件 渲染/更新 过程

一个组件渲染到页面,修改data触发更新 (数据驱动视图) 其背后原理是什么,需要更新掌握哪些要点 考察对流程了解的全面程度

过程:

初次渲染过程
解析模板为render函数(或在开发环境已完成,vue-loader)
触发响应式,监听data属性getter setter
执行render函数,生成vnode,patch(elem,vnode)
更新过程
修改data,触发setter (此前在getter中已被监听)
重新执行render函数,生成newVnode
patch(vnode,newVnode)
渲染异步

回顾:

  • 响应式:监听data 属性 getter setter (包括数组)
  • 模板编译:模板到render函数,再到vnode
  • vdom:patch(elem,vnode) 和 patch(vnode,newVnode)

总结:

  • 渲染和响应式的关系

  • 渲染和模板编译的关系

  • 渲染和vdom的关系

  • 如何理解MVVM模型

  • 数据驱动视图

  • Model view viewModel

监听data变化的核心

前端路由原理

稍微复杂一点SPA,都需要路由
vue-router 也是vue全家桶的标配之一
属于“和日常使用相关联的原理”,面试参考


回顾vue-router 的路由模式
hash
H5 history

hash

hash 特点:
hash 变化会触发网页跳转,即浏览器的前进、后退
hash 变化不会刷新页面,SPA 必需的特点
hash永远不会提交到server端(前端自生自灭)

H5 history
用url规范的路由,但跳转时不刷新新页面
history.pushState
window.onpopstate

两者选择
to B 的系统推荐用hash,简单易用,对url规范不敏感
to c的系统,可以考虑选择H5 history,但需要服务端支持

Vue面试真题演练

v-show 和 v-if的区别
v-show 通过CSS display 控制显示和隐藏
v-if组件真正的渲染和销毁,而不是显示和隐藏
频繁切换显示状态用v-show,否则用v-if
为何在v-for中用key
必须用key,且不能是index 和random
diff 算法中通过tag和key 来判断,是否是sameNode
减少渲染次数,提升渲染性能
描述Vue组件生命周期(父子组件)
单组件生命周期图
父子组件生命周期关系
Vue组件如何通讯
父子组件props和this.$emit
自定义事件 event.$on event.$off event.$emit
vuex
双向数据绑定v-model的实现原理
input 元素的value = this.name
绑定input 事件 this.name = $event.target.value
data 更新触发re-render
对MVVM的理解
model view viewModel
computed 有何特点
缓存,data 不变不会重新计算
提高性能
为何组件data必须是一个函数
每个vue文件是class 实例, 当data是一个函数的时候,每一个实例的data属性都是独立的,不会相互影响了
ajax 请求应该放在那个生命周期
mounted
JS 是单线程,ajax异步获取数据
放在mounted之前没有用,只会让逻辑更加混乱
如何将组件所有props 传递给子组件
$props
<User v-bind="$props" />
细节知识点优先级不高
如何自己实现v-model
多个组件有相同的逻辑,如何抽离
mixin
以及mixin的一些缺点
变量来源不明确,不利于阅读
多mixin可能会造成命名冲突
mixin和组件可能出现多对多的关系,复杂度较高
何时要使用异步组件
加载大组件
路由异步加载
何时需要使用keep-alive
缓存组件,不需要重复渲染
如多个静态tab页的切换
优化性能
何时需要使用beforeDestory
解绑自定义事件 event.$off
清除定时器
解绑自定义的DOM事件,如window scroll等
Vuex中acttions 和 mutation 有何区别
action 中处理异步,mutation 不可以
mutation 做原子操作
action 可以整合多个mutation
Vue-router 常见的路由模式
hash 默认
H5 history
两者比较
如何配置Vue-router 异步加载
请用vnode 描述一个DOM 结构

监听data 变化的核心API 是什么
object.defineProperty
以及深度监听,监听数组
有何缺点
Vue 如何监听数组变化
Object.defineProperty 不能监听数组变化
重新定义原型,重写push pop等方法,实现监听
Proxy 可以原生支持监听数组变化
请描述响应式原理
监听data 变化
组件渲染和更新的流程
Vue为何是异步渲染,$nextTick 何用
异步渲染(以及合并data修改),以提高渲染性能
$nextTick 在DOM 更新完之后,触发回调
Vue 常见性能优化方式
合理使用 v-show 和 v-if
合理使用computed
v-for 时加key,以及避免和v-if 同时使用
自定义事件、DOM事件时销毁
合理使用异步组件
合理使用keep-alive
data 层级不要太深
使用v-loader 在开发环境做模板编译(预编译)
前端通用的性能优化,如图片懒加载
使用SSR

vue3.0

vue3.0 重写了vdom的代码,优化了了性能