1. Vue的优点?Vue的缺点?
优点:渐进式,组件化,轻量级,虚拟dom,响应式,单页面路由,数据与视图分开
缺点:单页面不利于seo,不支持IE8以下,首屏加载时间长
2. Vue跟React的异同点?
相同点:
- 1.都使用了虚拟dom
- 2.组件化开发
- 3.都是单向数据流(父子组件之间,不建议子修改父传下来的数据)
- 4.都支持服务端渲染
不同点:
- 1.React的JSX,Vue的template
- 2.数据变化,React手动(setState),Vue自动(初始化已响应式处理,Object.defineProperty)
- 3.React单向绑定,Vue双向绑定
- 4.React的Redux,Vue的Vuex
3. MVVM是什么?
MVVM是Model-View-ViewModel缩写,也就是把MVC中的Controller演变成ViewModel。Model层代表数据模型,View代表UI组件,ViewModel是View和Model层的桥梁,数据会绑定到viewModel层并自动将数据渲染到页面中,视图变化的时候会通知viewModel层更新数据。
4. Vue和JQuery的区别在哪?为什么放弃JQuery用Vue?
- 1.jQuery是直接操作DOM,Vue不直接操作DOM,Vue的数据与视图是分开的,Vue只需要操作数据即可
- 2.jQuery的操作DOM行为是频繁的,而Vue利用虚拟DOM的技术,大大提高了更新DOM时的性能
- 3.Vue中不倡导直接操作DOM,开发者只需要把大部分精力放在数据层面上
- 4.Vue集成的一些库,大大提高开发效率,比如Vuex,Router等
5. 为什么data是个函数并且返回一个对象呢?
data之所以只一个函数,是因为一个组件可能会多处调用,而每一次调用就会执行data函数并返回新的数据对象,这样,可以避免多处调用之间的数据污染。
6. Vue 是如何实现数据双向绑定的?
1、实现一个监听器 Observer ,用来劫持并监听所有属性,如果属性发生变化,就通知订阅者
2、实现一个解析器 Compile,可以解析每个节点的相关指令,对模板数据和订阅器进行初始化
3、实现一个订阅者 Watcher,可以收到属性的变化通知并执行相应的方法,从而更新视图
4、实现一个订阅器 Dep,用来收集订阅者,对监听器 Observer 和 订阅者 Watcher 进行统一管理
以上四个步骤的流程图参考《0 到 1 掌握:Vue 核心之数据双向绑定》
7. 组件之间的传值方式有哪些?
- 父组件传值给子组件,子组件使用
props进行接收## DOM - 子组件传值给父组件,子组件使用
$emit+事件对父组件进行传值 - 组件中可以使用
$parent和$children获取到父组件实例和子组件实例,进而获取数据 - 使用
$attrs和$listeners,在对一些组件进行二次封装时可以方便传值,例如A->B->C - 使用
$refs获取组件实例,进而获取数据 - 使用
Vuex进行状态管理 - 使用
eventBus进行跨组件触发事件,进而传递数据 - 使用
provide和inject,官方建议我们不要用这个,我在看ElementUI源码时发现大量使用 - 使用浏览器本地缓存,例如
localStorage
8. 路由有哪些模式呢?又有什么不同呢?
- hash模式:通过
#号后面的内容的更改,触发hashchange事件,实现路由切换 - history模式:通过
pushState和replaceState切换url,触发popstate事件,实现路由切换,需要后端配合
9. v-if和v-show有何区别?
- 1.
v-if是通过控制dom元素的删除和生成来实现显隐,每一次显隐都会使组件重新跑一遍生命周期,因为显隐决定了组件的生成和销毁 - 2.
v-show是通过控制dom元素的css样式来实现显隐,不会销毁 - 3.频繁或者大数量显隐使用
v-show,否则使用v-if
14. computed和watch有何区别?
- 1.
computed是依赖已有的变量来计算一个目标变量,大多数情况都是多个变量凑在一起计算出一个变量,并且computed具有缓存机制,依赖值不变的情况下其会直接读取缓存进行复用,computed不能进行异步操作 - 2.
watch是监听某一个变量的变化,并执行相应的回调函数,通常是一个变量的变化决定多个变量的变化,watch可以进行异步操作 - 3.简单记就是:一般情况下
computed是多对一,watch是一对多
10. Vue的生命周期和组件加载顺序讲一讲?
父beforeCreate -> 父created -> 父beforeMount ->
子beforeCreate -> 子created -> 子beforeMount -> 子mounted ->
父mounted
11. 为什么v-if和v-for不建议用在同一标签?
在Vue2中,v-for优先级是高于v-if的,咱们来看例子
<div v-for="item in [1, 2, 3, 4, 5, 6, 7]" v-if="item !== 3">
{{item}}
</div>
上面的写法是v-for和v-if同时存在,会先把7个元素都遍历出来,然后再一个个判断是否为3,并把3给隐藏掉,这样的坏处就是,渲染了无用的3节点,增加无用的dom操作,建议使用computed来解决这个问题:
<div v-for="item in list">
{{item}}
</div>
computed() {
list() {
return [1, 2, 3, 4, 5, 6, 7].filter(item => item !== 3)
}
}
11. vuex的有哪些属性?用处是什么?
- State:定义了应用状态的数据结构,可以在这里设置默认的初始状态。
- Getter:允许组件从 Store 中获取数据,mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性。
- Mutation:是唯一更改 store 中状态的方法,且必须是同步函数。
- Action:用于提交 mutation,而不是直接变更状态,可以包含任意异步操作。
- Module:允许将单一的 Store 拆分为多个 store 且同时保存在单一的状态树中。
12. 不需要响应式的数据应该怎么处理?
方法一:将数据定义在data之外
方法二:Object.freeze()
13. 对象新属性无法更新视图,删除属性无法更新视图,为什么?怎么办?
- 原因:
Object.defineProperty没有对对象的新属性进行属性劫持 - 对象新属性无法更新视图:使用
Vue.$set(obj, key, value),组件中this.$set(obj, key, value) - 删除属性无法更新视图:使用
Vue.$delete(obj, key),组件中this.$delete(obj, key)
14. 直接arr[index] = xxx无法更新视图怎么办?为什么?怎么办?
- 原因:Vue没有对数组进行
Object.defineProperty的属性劫持,所以直接arr[index] = xxx是无法更新视图的 - 使用数组的splice方法,
arr.splice(index, 1, item) - 使用
Vue.$set(arr, index, value)
15. 说说nextTick的用处?
我举个例子,在vue中:
this.name = '林三心'
this.age = 18
this.gender = '男'
我们修改了三个变量,那问题来了,是每修改一次,DOM就更新一次吗?不是的,Vue采用的是异步更新的策略,通俗点说就是,同一事件循环内多次修改,会统一进行一次视图更新,这样才能节省性能嘛
看懂了上面,那你应该也看得懂下面的例子了吧:
<div ref="testDiv">{{name}}</div>
name: '小林'
this.name = '林三心'
console.log(this.$refs.testDiv.innerHTML) // 这里是啥呢
答案是“小林”,前面说了,Vue是异步更新,所以数据一更新,视图却还没更新,所以拿到的还是上一次的旧视图数据,那么想要拿到最新视图数据怎么办呢?
this.name = '林三心'
this.$nextTick(() => {
console.log(this.$refs.testDiv.innerHTML) // 林三心
})
16. Vue的SSR是什么?有什么好处?
SSR就是服务端渲染- 基于
nodejs serve服务环境开发,所有html代码在服务端渲染 - 数据返回给前端,然后前端进行“激活”,即可成为浏览器识别的html代码
SSR首次加载更快,有更好的用户体验,有更好的seo优化,因为爬虫能看到整个页面的内容,如果是vue项目,由于数据还要经过解析,这就造成爬虫并不会等待你的数据加载完成,所以其实Vue项目的seo体验并不是很好
17. 如果子组件改变props里的数据会发生什么?
props: {
arr: {
type: Array,
default: []
},
}
method() {
changeArr () {
this.arr.push('add item') //不报错
this.arr[0] = 'first item' //不报错
this.arr = [] //报错
}
}
在 JavaScript 中对象和数组是引用类型,指向同一个内存空间,如果 prop 是一个对象或数组,在子组件内部改变它会影响父组件的状态。
18. props怎么自定义验证?
props: {
num: {
default: 1,
validator: function (value) {
// 返回值为true则验证不通过,报错
return [1, 2, 3, 4, 5].indexOf(value) !== -1
}
}
}
19. vue的hook的使用
这是我们常用的使用定时器的方式
export default{
data(){
timer:null
},
mounted(){
this.timer = setInterval(()=>{
//具体执行内容
console.log('1');
},1000);
}
beforeDestory(){
clearInterval(this.timer);
this.timer = null;
}
}
上面做法不好的地方在于:得全局多定义一个timer变量,可以使用hook这么做:
export default{
methods:{
fn(){
const timer = setInterval(()=>{
//具体执行代码
console.log('1');
},1000);
this.$once('hook:beforeDestroy',()=>{
clearInterval(timer);
timer = null;
})
}
}
}
- 7.2 父子组件使用
如果子组件需要在mounted时触发父组件的某一个函数,平时都会这么写:
//父组件
<rl-child @childMounted="childMountedHandle"
/>
method () {
childMountedHandle() {
// do something...
}
},
// 子组件
mounted () {
this.$emit('childMounted')
},
使用hook的话可以更方便:
//父组件
<rl-child @hook:mounted="childMountedHandle"
/>
method () {
childMountedHandle() {
// do something...
}
},
20. provide和inject是响应式的吗?
// 祖先组件
provide(){
return {
// keyName: { name: this.name }, // value 是对象才能实现响应式,也就是引用类型
keyName: this.changeValue // 通过函数的方式也可以[注意,这里是把函数作为value,而不是this.changeValue()]
// keyName: 'test' value 如果是基本类型,就无法实现响应式
}
},
data(){
return {
name:'张三'
}
},
methods: {
changeValue(){
this.name = '改变后的名字-李四'
}
}
```````
// 后代组件
inject:['keyName']
create(){
console.log(this.keyName) // 改变后的名字-李四
}
21. 相同的路由组件如何重新渲染?
开发人员经常遇到的情况是,多个路由解析为同一个Vue组件。问题是,Vue出于性能原因,默认情况下共享组件将不会重新渲染,如果你尝试在使用相同组件的路由之间进行切换,则不会发生任何变化。
const routes = [
{
path: "/a",
component: MyComponent
},
{
path: "/b",
component: MyComponent
},
];
如果依然想重新渲染,怎么办呢?可以使用
key
<template>
<router-view :key="$route.path"></router-view>
</template>
22. 虚拟Dom以及key属性的作用
由于在浏览器中操作DOM是很昂贵的。频繁的操作DOM,会产生一定的性能问题。这就是虚拟Dom的产生原因。
Vue2的Virtual DOM借鉴了开源库snabbdom的实现。
Virtual DOM本质就是用一个原生的JS对象去描述一个DOM节点。是对真实DOM的一层抽象。(也就是源码中的VNode类,它定义在src/core/vdom/vnode.js中。)
VirtualDOM映射到真实DOM要经历VNode的create、diff、patch等阶段。
23. 单向数据流
所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。
额外的,每次父级组件发生更新时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。
子组件想修改时,只能通过 $emit 派发一个自定义事件,父组件接收到后,由父组件修改。
有两种常见的试图改变一个 prop 的情形 :
- 这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用。 在这种情况下,最好定义一个本地的 data 属性并将这个 prop 用作其初始值:
props: ['initialCounter'],
data: function () {
return {
counter: this.initialCounter
$}$
}
- 这个 prop 以一种原始的值传入且需要进行转换。 在这种情况下,最好使用这个 prop 的值来定义一个计算属性
props: ['size'],
computed: {
normalizedSize: function () {
return this.size.trim().toLowerCase()
}
}
24. 谈谈你对 keep-alive 的了解?
keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,避免重新渲染 ,其有以下特性:
- 一般结合路由和动态组件一起使用,用于缓存组件;
- 提供 include 和 exclude 属性,两者都支持字符串或正则表达式, include 表示只有名称匹配的组件会被缓存,exclude 表示任何名称匹配的组件都不会被缓存 ,其中 exclude 的优先级比 include 高;
- 对应两个钩子函数 activated 和 deactivated ,当组件被激活时,触发钩子函数 activated,当组件被移除时,触发钩子函数 deactivated。
25. 虚拟 DOM 实现原理?
虚拟 DOM 的实现原理主要包括以下 3 部分:
- 用 JavaScript 对象模拟真实 DOM 树,对真实 DOM 进行抽象;
- diff 算法 — 比较两棵虚拟 DOM 树的差异;
- pach 算法 — 将两个虚拟 DOM 对象的差异应用到真正的 DOM 树。