1.说说你对vue的理解
vue是一个渐进式的JavaScript框架,一套拥有自己规则的语法。旨在更好地组织与简化Web开发。
vue的核心特性:
- 数据驱动视图(MVVM)
MVVM:Model-View-ViewModel
Model:模型层,负责处理业务逻辑以及和服务器端进行交互
View:视图层:负责将数据模型转化为UI展示出来,可以简单的理解为HTML页面
ViewModel:视图模型层,用来连接Model和View,是Model和View之间的通信桥梁
- 组件化
组件化:就是把图形、非图形的各种逻辑均抽象为一个统一的概念(组件)来实现开发的模式,在Vue
中每一个.vue
文件都可以视为一个组件
组件化的优势:
降低整个系统的耦合度,在保持接口不变的情况下,我们可以替换不同的组件快速完成需求,例如输入框,可以替换为日历、时间、范围等组件作具体的实现
调试方便,由于整个系统是通过组件组合起来的,在出现问题的时候,可以用排除法直接移除组件,或者根据报错的组件快速定位问题,之所以能够快速定位,是因为每个组件之间低耦合,职 责单一,所以逻辑会比分析整个系统要简单
提高可维护性,由于每个组件的职责单一,并且组件在系统中是被复用的,所以对代码进行优化可获得系统的整体升级
- 指令系统
指令 (Directives) 是带有 v- 前缀的特殊属性作用:当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM
常用指令:v-if v-for v-bind v-on v-model
2.computed和watch的区别
computed: 是一个计算属性,依赖于其他的属性值,其他依赖的属性值发生变化后,computed的值才会发生改变,最主要的是具有缓存的作用。
- 不支持异步,当computed内有异步操作时无效,无法监听数据的变化
- 如果computed属性值是函数,那么默认会走get方法,函数的返回值就是属性的属性值;computed中的完整写法(get和set两个方法),给计算属性变量赋值时用完整写法,赋值用set(值),取值用get。
watch: 是一个侦听器(监视器),可以侦听data / computed属性值的改变。
- 不支持缓存
- watch支持异步
- 监听的函数接收两个参数,第一个参数是最新的值(newVal)、第二个参数是输入之前的值(oldVal)
- 深度侦听:一般侦听的是复杂数据类型,handler函数一般接收一个参数(指向同一个堆地址);需要深度侦听的话,可以加上deep:true;需要即刻侦听,加上immediate:true
两者的应用场景:
当多个元素发生变化导致一个结果进行变更的时候(多对一或一对一),可使用computed。
当一个元素发生变化导致多个结果进行变更的时候(一对多),可使用watch。
3.谈谈你对vue生命周期的理解
Vue 实例有一个完整的生命周期,也就是创建前后,挂载前后,更新前后,销毁前后,created阶段Vue对象上挂载了methods和data,所以一般用来请求数据,mounted的时候视图就渲染完了,一般可以操作DOM,比如Echarts初始化,这两个平时用的最多,如果有内存泄漏或者要性能优化的话,可以考虑销毁前将事件进行解绑,不过一般不写也没什么影响,有keep-alive的话就多两个周期,激活前后(actived\deactived),这两个后期用不用就要看需求了,还有个捕获组件错误用的(errorCaptured),这个平时不怎么用。
4.v-model的原理
v-model其实就是一个语法糖,原理是用v-bind绑定value,然后用oninput监听把值再更新给绑定value的值,有时候封装组件也可以用这个方法实现即使组件不是输入框也可以进行v-model绑定数据。
v-model 在内部为不同的输入元素使用不同的属性并抛出不同的事件:
- text 和 textarea 元素使用 value 属性和 input 事件;
- checkbox 和 radio 使用 checked 属性和 change 事件;
- select 字段将 value 作为 prop 并将 change 作为事件。
<input v-model='something'> 等价于 <input v-bind:value="something" @input="something = $event.target.value">
如果在自定义组件中,v-model 默认会利用名为 value 的 prop 和名为 input 的事件,如下所示:
父组件: <ModelChild v-model="message"></ModelChild> 子组件: <div>{{value}}</div> props:{ value: String }, methods: { test1(){ this.$emit('input', '小红') }, },
5.v-if和v-show的区别
两者都是控制标签的隐藏或者出现(不含v-else)
两者的区别:
- 控制手段不同
- 编译过程不同
- 编译条件不同
- 控制手段:v-if显示隐藏是从DOM树上创建或移除。v-show隐藏则是为该元素添加css样式display:none。
- 编译过程:v-if切换有一个局部编译/卸载的过程,切换过程中合适地销毁和重建内部的事件监听和子组件;v-show只是简单的基于css切换
- 编译条件:v-if是真正的条件渲染,它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。只有渲染条件为false时,并不做操作,直到为true才渲染
v-show 由false变为true的时候不会触发组件的生命周期
v-if 由false变为true的时候,触发组件的beforeCreate、created、beforeMount、mounted钩子函数,由true变为false的时候触发组件的beforeDestroy、destroyed钩子函数
性能消耗:v-if有更高的切换消耗;v-show有更高的初始渲染消耗。
使用场景:
v-if
与v-show
都能控制dom
元素在页面的显示v-if
相比v-show
开销更大的(直接操作dom
节点增删)- 如果需要非常频繁地切换,则使用 v-show 较好
- 如果在运行时条件很少改变,则使用 v-if 较好
6.说说你对SPA单页面的理解,分别有什么优缺点?
SPA(single-page application)仅在Web页面初始化时加载相应的HTML、JavaScript和css,一旦页面加载完成,SPA不会因为用户的操作而进行页面的重新加载或跳转;取而代之的是利用路由机制实现HTML内容的变换,UI与用户的交互,避免页面的重新加载。
优点:
- 用户体验好,内容的改变不需要重新加载整个页面,避免了不必要的跳转和重复渲染;
- 基于上面一点,SPA对服务器压力小;
- 前后端职责分离,架构清晰,前端进行交互逻辑,后端负责数据处理;
- 初次加载耗时多:为实现单页 Web 应用功能及显示效果,需要在加载页面的时候将 JavaScript、CSS 统一加载,部分页面按需加载;
- 前进后退路由管理:由于单页应用在一个页面中显示所有的内容,所以不能使用浏览器的前进后退功能,所有的页面切换需要自己建立堆栈管理;
- SEO 难度较大:由于所有的内容都在一个页面中动态替换显示,所以在 SEO 上其有着天然的弱势。
7.Vue组件间通信有哪几种方式?
(1)props / $emit
适用 父子组件通信
这种方法是 Vue 组件的基础,相信大部分同学耳闻能详,所以此处就不举例展开介绍。
(2)ref
与 $parent / $children
适用 父子组件通信
ref
:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例$parent
/$children
:访问父 / 子实例
(3)EventBus ($emit / $on)
适用于 父子、隔代、兄弟组件通信
这种方法通过一个空的 Vue 实例作为中央事件总线(事件中心),用它来触发事件和监听事件,从而实现任何组件间的通信,包括父子、隔代、兄弟组件。
(4)$attrs
/$listeners
适用于 隔代组件通信
$attrs
:包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 ( class 和 style 除外 )。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 ( class 和 style 除外 ),并且可以通过v-bind="$attrs"
传入内部组件。通常配合 inheritAttrs 选项一起使用。$listeners
:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过v-on="$listeners"
传入内部组件
(5)provide / inject
适用于 隔代组件通信
祖先组件中通过 provider 来提供变量,然后在子孙组件中通过 inject 来注入变量。 provide / inject API 主要解决了跨级组件间的通信问题,不过它的使用场景,主要是子组件获取上级组件的状态,跨级组件间建立了一种主动提供与依赖注入的关系。
(6)Vuex 适用于 父子、隔代、兄弟组件通信
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。每一个 Vuex 应用的核心就是 store(仓库)。“store” 基本上就是一个容器,它包含着你的应用中大部分的状态 ( state )。
- Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
- 改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化。
8.你使用过Vuex吗?
Vuex是一个专为Vue.js应用程序开发的一个状态管理工具,每个Vuex应用的核心就是store(仓库),“store” 基本上就是一个容器,它包含着你的应用中大部分的状态 ( state )。
(1) Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
(2) 改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化。
主要包括以下几个模块:
- State:定义了应用状态的数据结构,可以在这里设置默认的初始状态。
- Getter:允许组件从 Store 中获取数据,mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性。
- Mutation:是唯一更改 store 中状态的方法,且必须是同步函数。
- Action:用于提交 mutation,而不是直接变更状态,可以包含任意异步操作。
- Module:允许将单一的 Store 拆分为多个 store 且同时保存在单一的状态树中。

(dispatch)Actions --> (commit)Mutations --> State --> Render Page(渲染页面)
9.你知道DOM diff算法吗?
diff算法是一种通过同层的树节点进行比较的高效算法。
两个特点:
- 比较只会在同级进行,不会跨层级比较
- 在diff比较的过程中,循环从两边向中间比较
while
循环主要处理了以下五种情景:
- 当新老
VNode
节点的start
相同时,直接patchVnode
,同时新老VNode
节点的开始索引都加 1 - 当新老
VNode
节点的end
相同时,同样直接patchVnode
,同时新老VNode
节点的结束索引都减 1 - 当老
VNode
节点的start
和新VNode
节点的end
相同时,这时候在patchVnode
后,还需要将当前真实dom
节点移动到oldEndVnode
的后面,同时老VNode
节点开始索引加 1,新VNode
节点的结束索引减 1 - 当老
VNode
节点的end
和新VNode
节点的start
相同时,这时候在patchVnode
后,还需要将当前真实dom
节点移动到oldStartVnode
的前面,同时老VNode
节点结束索引减 1,新VNode
节点的开始索引加 1 - 如果都不满足以上四种情形,那说明没有相同的节点可以复用,则会分为以下两种情况:
- 从旧的
VNode
为key
值,对应index
序列为value
值的哈希表中找到与newStartVnode
一致key
的旧的VNode
节点,再进行patchVnode
,同时将这个真实dom
移动到oldStartVnode
对应的真实dom
的前面 - 调用
createElm
创建一个新的dom
节点放到当前newStartIdx
的位置
- 从旧的
10.$nextTick是什么?
官方对其的定义:
在下次DOM更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的DOM
可以这样通俗的理解:
在vue中修改数据会进行虚拟DOM的更新,这是个同步操作,而虚拟DOM更新后同步到真实的DOM节点更新的过程是一个异步操作,所以没法再更新完数据同步的获取DOM的最新状态,$nextTick就是一个异步包装,可以将包装起来的操作变成异步的,和setTimeout差不多,比如:一个div的文字是data中的数据,你修改了以后立刻获取div的内容,其实是获取不到的,因为异步更新还没执行,所以要想操作就得将操作包装成异步,可以使用$nextTick,或者在updated周期处理。