1、什么情况下使用 Vuex?
- 如果应用够简单,最好不要使用
Vuex
,一个简单的store
模式即可 - 需要构建一个中大型单页应用时,使用
Vuex
能更好地在组件外部管理状态
2、Vuex和单纯的全局对象有什么区别?
Vuex
的状态存储是响应式的。当Vue
组件从store
中读取状态的时候,若store
中的状态发生变化,那么相应的组件也会相应地得到高效更新。- 不能直接改变
store
中的状态。改变store
中的状态的唯一途径就是显式地提交(commit) mutation
。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。
3、为什么 Vuex 的 mutation 中不能做异步操作?
Vuex
中所有的状态更新的唯一途径都是mutation
,异步操作通过Action
来提交mutation
实现,这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。- 每个
mutation
执行完成后都会对应到一个新的状态变更,这样devtools
就可以打个快照存下来,然后就可以实现time-travel
了。如果mutation
支持异步操作,就没有办法知道状态是何时更新的,无法很好的进行状态的追踪,给调试带来困难。
4、vuex的action有返回值吗?返回的是什么?
store.dispatch
可以处理被触发的action
的处理函数返回的Promise
,并且store.dispatch
仍旧返回Promise
Action
通常是异步的,要知道action
什么时候结束或者组合多个action
以处理更加复杂的异步流程,可以通过定义action
时返回一个promise
对象,就可以在派发action
的时候就可以通过处理返回的Promise
处理异步流程
一个
store.dispatch
在不同模块中可以触发多个action
函数。在这种情况下,只有当所有触发函数完成后,返回的Promise
才会执行。
5、computed 和 watch 的区别和运用的场景?
-
computed
:是计算属性,依赖其它属性值,并且computed
的值有缓存,只有它依赖的属性值发生改变,下一次获取computed
的值时才会重新计算computed
的值; -
watch
:没有缓存性,更多的是「观察」的作用,类似于某些数据的监听回调 ,每当监听的数据变化时都会执行回调进行后续操作;当我们需要深度监听对象中的属性时,可以打开deep:true
选项,这样便会对对象中的每一项进行监听 -
运用场景:
- 当我们需要进行数值计算,并且依赖于其它数据时,应该使用
computed
,因为可以利用computed
的缓存特性,避免每次获取值时,都要重新计算; - 当我们需要在数据变化时执行异步或开销较大的操作时,应该使用
watch
,使用watch
选项允许我们执行异步操作 ( 访问一个 API ),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。
- 当我们需要进行数值计算,并且依赖于其它数据时,应该使用
6、Vue2.x组件通信有哪些方式?
-
父子组件通信
事件机制(**父->子props,子->父 $on、$emit)
获取父子组件实例 $parent、$children
Ref 获取实例的方式调用组件的属性或者方法
Provide、inject (不推荐使用,组件库时很常用)
-
兄弟组件通信
Vue.prototype.$bus = new Vue
- Vuex
- eventBus 这种方法通过一个空的 Vue实例作为中央事件总线(事件中心),用它来触发事件和监听事件,从而实现任何组件间的通信,包括父子、隔代、兄弟组件
-
跨级组件通信
Vuex
$attrs、$listeners
Provide、inject
7、为什么 v-for 和 v-if 不建议用在一起
- 当
v-for
和v-if
处于同一个节点时,v-for
的优先级比v-if
更高,这意味着v-if
将分别重复运行于每个v-for
循环中。如果要遍历的数组很大,而真正要展示的数据很少时,这将造成很大的性能浪费 - 这种场景建议使用
computed
,先对数据进行过滤
8、组件中的data为什么是一个函数?
- 一个组件被复用多次的话,也就会创建多个实例。本质上,这些实例用的都是同一个构造函数。
- 如果
data
是对象的话,对象属于引用类型,会影响到所有的实例。所以为了保证组件不同的实例之间data
不冲突,data
必须是一个函数。
9、子组件为什么不可以修改父组件传递的Prop?/怎么理解vue的单向数据流?
Vue
提倡单向数据流,即父级props的更新会流向子组件,但是反过来则不行。- 这是为了防止意外的改变父组件状态,使得应用的数据流变得难以理解。
- 如果破坏了单向数据流,当应用复杂时,
debug
的成本会非常高。
10、v-model是如何实现双向绑定的?
v-model
是用来在表单控件或者组件上创建双向绑定的- 他的本质是
v-bind
和v-on
的语法糖 - 在一个组件上使用
v-model
,默认会为组件绑定名为value
的prop
和名为input
的事件
11、nextTick的实现原理是什么?
- 在下次
DOM
更新循环结束之后执行延迟回调,在修改数据之后立即使用 nextTick 来获取更新后的DOM
。 nextTick
主要使用了宏任务和微任务。- 根据执行环境分别尝试采用
Promise、MutationObserver、setImmediate
,如果以上都不行则采用setTimeout
定义了一个异步方法,多次调用nextTick
会将方法存入队列中,通过这个异步方法清空当前队列。
12、Vue事件绑定原理是什么?
- 原生事件绑定是通过
addEventListener
绑定给真实元素的,组件事件绑定是通过Vue自定义的$on
实现的。
13、说一下虚拟Dom以及key属性的作用
-
由于在浏览器中操作
DOM
是很昂贵的。频繁的操作DOM
,会产生一定的性能问题。这就是虚拟Dom的产生原因。 -
Virtual DOM
本质就是用一个原生的JS对象去描述一个DOM节点。是对真实DOM
的一层抽象。(也就是源码中的VNode
类,它定义在src/core/vdom/vnode.js
中。) -
虚拟 DOM 的实现原理主要包括以下 3 部分:
用 JavaScript 对象模拟真实 DOM 树,对真实 DOM 进行抽象;
diff 算法 — 比较两棵虚拟 DOM 树的差异;
pach 算法 — 将两个虚拟 DOM 对象的差异应用到真正的 DOM 树。
-
key 是为
Vue
中vnode
的唯一标记,通过这个key
,我们的diff
操作可以更准确、更快速- 更准确:因为带 key 就不是就地复用了,在
sameNode
函数a.key === b.key
对比中可以避免就地复用的情况。所以会更加准确。 更快速:利用 key 的唯一性生成 map 对象来获取对应节点,比遍历方式更快
- 更准确:因为带 key 就不是就地复用了,在
14、为什么不建议用index作为key?
- 不建议 用
index 作为 key,和没写基本上没区别
,因为不管你数组的顺序怎么颠倒,index 都是 0, 1, 2 这样排列,导致 Vue 会复用错误的旧子节点,做很多额外的工作
15、说一下你对Vue的生命周期的理解
beforeCreate
:实例初始化之后,此时的数据观察和事件配置都没好准备(undefined)created
:读取到数据data的值,但是DOM还没生成beforeMount
:页面还没有被成功地渲染成我们data中的数据mounted
:已经成功渲染beforeUpdate
:视图重新渲染更新之前updated
:视图已经更新了。beforeDestroy
:销毁当前的组件destroyed
。成功销毁之后,会触发destroyed钩子keep-alive
有自己独立的钩子函数 activated 和 deactivated
16、Vue中组件生命周期调用顺序是什么样的?
- 组件的调用顺序都是先父后子,渲染完成的顺序是先子后父。
- 组件的销毁操作是先父后子,销毁完成的顺序是先子后父。
17、在什么阶段才能访问操作DOM?
在钩子函数mounted
被调用前,Vue
已经将编译好的模板挂载到页面上,所以在 mounted
中可以访问操作 DOM。
18、你的接口请求一般放在哪个生命周期中?
-
可以在钩子函数
created、beforeMount、mounted
中进行调用,因为在这三个钩子函数中,data 已经创建,可以将服务端端返回的数据进行赋值。 -
但是推荐在 created 钩子函数中调用异步请求,因为在 created 钩子函数中调用异步请求有以下优点:
- 能更快获取到服务端数据,减少页面loading 时间;
- ssr不支持 beforeMount 、mounted 钩子函数,所以放在 created 中有助于一致性;
19、vue路由hash模式和history模式实现原理分别是什么,他们的区别是什么?
-
hash 模式:
- #后面 hash 值的变化,不会导致浏览器向服务器发出请求,浏览器不发出请求,就不会刷新页面
- 通过监听 hashchange 事件可以知道 hash 发生了哪些变化,然后根据 hash 变化来实现更新页面部分内容的操作。
-
history 模式:
- history 模式的实现,主要是 HTML5 标准发布的两个 API,pushState 和replaceState,这两个 API 可以在改变 url,但是不会发送请求。这样就可以监听 url 变化来实现更新页面部分内容的操作
-
区别
- url 展示上,hash 模式有“#”,history 模式没有
- 刷新页面时,hash 模式可以正常加载到 hash 值对应的页面,而 history 没有处理的话,会返回 404,一般需要后端将所有页面都配置重定向到首页路由
- 兼容性,hash 可以支持低版本浏览器和 IE。
20、Vue-router 导航守卫有哪些?
- 全局前置/钩子:
beforeEach、beforeResolve、afterEach
- 路由独享的守卫:
beforeEnter
- 组件内的守卫:
beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave
21、在 Vue 实例中编写生命周期 hook 或其他 option/properties 时,为什么不使用箭头函数 ?
- 箭头函数自已没有定义
this
上下文中。 - 当你在 Vue 程序中使用箭头函数
( => )
时,this
关键字病不会绑定到Vue
实例,因此会引发错误。所以强烈建议改用标准函数声明。
22、说说你对keep-alive组件的了解
-
keep-alive
是 Vue 内置的一个组件,可以使被包含的组件保留状态,避免重新渲染 ,其有以下特性:- 一般结合路由和动态组件一起使用,用于缓存组件;
- 提供
include
和exclude
属性,两者都支持字符串或正则表达式,include
表示只有名称匹配的组件会被缓存,exclude
表示任何名称匹配的组件都不会被缓存 ,其中exclude
的优先级比include
高; - 对应两个钩子函数
activated 和 deactivated
,当组件被激活时,触发钩子函数activated
,当组件被移除时,触发钩子函数deactivated。
23、说说你对SSR的了解?
-
SSR也就是服务端渲染,也就是将Vue在客户端把标签渲染成HTML的工作放在服务端完成,然后再把html直接返回给客户端
-
SSR的优势
- 更好的SEO
- 首屏加载速度更快
-
SSR的缺点
- 开发条件会受到限制,服务器端渲染只支持beforeCreate和created两个钩子
- 当我们需要一些外部扩展库时需要特殊处理,服务端渲染应用程序也需要处于Node.js的运行环境
- 更多的服务端负载
24、你都做过哪些Vue的性能优化?
-
编码阶段
尽量减少data中的数据,data中的数据都会增加getter和setter,会收集对应的watcher
v-if和v-for不能连用
如果需要使用v-for给每项元素绑定事件时使用事件代理
SPA 页面采用keep-alive缓存组件
在更多的情况下,使用v-if替代v-show
key保证唯一
使用路由懒加载、异步组件
防抖、节流
第三方模块按需导入
长列表滚动到可视区域动态加载
图片懒加载
-
SEO优化
预渲染
服务端渲染SSR
-
打包优化
压缩代码
Tree Shaking/Scope Hoisting
使用cdn加载第三方模块
多线程打包happypack
splitChunks抽离公共文件
- `sourceMap优化
-
用户体验
骨架屏
还可以使用缓存(客户端缓存、服务端缓存)优化、服务端开启gzip压缩等。