1、对MVVM架构的理解?
❝❞
- Model层:表示数据模型。
- View层:表示UI组件,用于视图展示。
- ViewModel:监听数据模型的状态改变和控制视图的展示,VM通过双向数据绑定,将View层和Model层连接起来,所以当数据状态改变后视图会自动更新,无需人为干涉,因此使用MVVM可以令开发者更注重业务逻辑的开发,而不需要去手动操作DOM和关注数据状态的同步问题,复杂的数据状态维护交由MVVM统一管理。
2、对Vue生命周期的理解?
❝什么是生命周期?
一个组件从开始创建,初始化数据,编译模板,挂载DOM,渲染,等待更新,重新渲染,销毁等一系列过程,称之为生命周期。
❞
❝「Vue组件生命周期」
❞
beforeCreate(创建前):组件实例刚刚创建好,此时实例中还没有进行成员提升
created(创建后):组件实例已经提升成员,此时还没有进行页面渲染
beforeMount(挂载前):组件已编译好渲染模板,即将进行渲染
mounted(挂载后):组件已完成渲染,界面已经可见
beforeUpdate(更新前):组件实例中的数据是新的,但是界面还是旧的
updated(更新后):此时数据和界面都是最新的
beforeDestroy(销毁前):组件即将销毁
destroyed(销毁后):组件已经销毁
❝「被keep-alive缓存的组件」
❞
activated(触发前):当被keep-alive缓存的组件激活时调用
deactivated(触发后): 当被keep-alive缓存的组件停用时调用
3、Vue异步请求适合在哪个生命周期中调用?
❝一般来说,Vue的异步请求可以在created和mounted中发送。如果在考虑效率问题的情况下,大部分时候会在created中进行请求。在mounted中发送请求会造成二次渲染。
❞
created生命周期的使用场景:一个页面的首次渲染来自于服务器端(SSR)
created异步请求的优点:created支持SSR,能更快的获取服务端数据,减少页面Loading时间。
4、Vue组件之间如何通信?
❝「父传子通信:」
- props:一层一层传递下去
- $attrs:接收除了props声明外的所有绑定属性(class,style除外)
- $parent / $root:获取父/根作用域上下文
- provide / inject:导出注入方式,主要在开发高阶插件/组件库时使用。
「子传父通信:」
- $children:获取直接子组件的作用域上下文
- $refs:引用ref属性注册过的组件实例
「组件事件通信:」
- $emit:触发某个父作用域传递的事件(包括自定义事件)
- $listeners:获取父作用域传递的全部事件(包括自定义事件)
- event bus(事件总线):通过一个全局bus注册触发事件
「全局共享数据通信」
❞
- Vuex:使用仓库来管理大量,复杂的组件间共享数据
// 代码演示:
// 1. props:一层一层传递数据
/*
<div id="app">
<child_comp :title='title'></child_comp>
</div>
*/
var app = new Vue({
el: '#app',
data: {
title: '传递给子组件的数据'
},
components: {
// 子组件
'child_comp': {
props: ['title'], // 通过props绑定父作用域传递的数据
template: '<li>{{ title }}</li>'
}
}
})
// 2. $attrs:接收除了props声明外的所有绑定属性(class,style除外)
/*
<div id="app">
<child_comp :title='title'></child_comp>
</div>
*/
var app = new Vue({
el: '#app',
data: {
title: '传递给子组件的数据'
},
components: {
// 子组件
'child_comp': {
// 未被props声明的绑定属性会收纳进$attrs对象中
template: '<li>{{ this.$attrs.title }}</li>'
}
}
})
// 3. $parent / $root:获取父/根作用域上下文
/*
<div id="app">
<child_comp :title='title'></child_comp>
</div>
*/
var app = new Vue({
el: '#app',
data: {
title: '传递给子组件的数据'
},
components: {
// 子组件
'child_comp': {
// this.$parent得到父组件的上下文 / this.$root得到根组件的上下文
template: `
<li>{{ this.$parent.title }}</li>
<li>{{ this.$root.title }}</li>
`
}
}
})
// 4. provide / inject:导出注入方式,主要在开发高阶插件/组件库时使用。
/*
<div id="app">
<child_comp :title='title'></child_comp>
</div>
*/
var app = new Vue({
el: '#app',
// 使用provide方式创建数据对象并导出
provide: {
title: '传递给子组件的数据'
},
components: {
// 子组件
'child_comp': {
// 需要用到导出数据的组件使用inject注入
inject: ['title'],
template: '<li>{{ title }}</li>'
}
}
})
// 5. $children:获取直接子组件的作用域上下文
/*
<div id="app">
<child_comp :title='title'></child_comp>
</div>
*/
var app = new Vue({
el: '#app',
data: {
title: ''
},
components: {
// 子组件
'child_comp': {
data(){
return {
title: '传递给父组件的数据'
}
},
template: '<li></li>'
}
},
mounted(){
// 获取直接子组件的上下文
this.title = this.$children[0].title
}
})
// 6. $refs:引用ref属性注册过的组件实例
/*
<div id="app">
// 通过ref注册引用
<child_comp ref=‘dom’></child_comp>
</div>
*/
var app = new Vue({
el: '#app',
data: {
title: ''
},
components: {
// 子组件
'child_comp': {
data(){
return {
title: '传递给父组件的数据'
}
},
template: '<li></li>'
}
},
mounted(){
// 通过$refs获取dom对应的组件实例
this.title = this.$refs.dom.title
}
})
// 7. $emit:触发某个父作用域传递的事件(包括自定义事件)
// $listeners:获取父作用域传递的全部事件(包括自定义事件)
/*
<div id="app">
<child_comp @click='handleClick' @test='fun'></child_comp>
</div>
*/
var app = new Vue({
el: '#app',
data: {
title: ''
},
methods: {
handleClick(param){
this.title = param;
}
},
components: {
// 子组件
'child_comp': {
data(){
return {
title: '传递给父组件的数据'
}
},
template: '<li @click="callback">点击触发事件</li>',
methods: {
callback(){
// 触发click事件
this.$emit('click', this.title);
// 得到所有事件对象
console.log(this.$listeners); // {click: fun(), test: fun()};
}
}
}
}
})
5、computed和watch有什么区别?
❝「computed(计算属性):」 计算属性中的成员会提升到vue实例中,因此,在模板中可以直接当作属性使用,使用时,实际调用的是对于的方法。
- 计算属性在使用时会计算属性的依赖,当依赖没有发生变化时,vue会直接使用之前缓存的结果。
- 计算属性可以配置get和set两个方法,分别用于读取和设置。
「watch(侦听属性):」 侦听属性用来观察和响应vue实例上的数据变动。
由于计算属性会缓存结果,对于一个性能开销较大的计算,如果多个地方同时使用同一个计算属性,只需要计算一次,如果使用侦听属性,每调用一次都会重新计算一次。
❞
6、Vue是如何实现双向数据绑定的?
❝Vue2.x:使用Object.defineProperty实现数据双向绑定。
Vue3.x:使用ES6中的Proxy进行代理。
「Object.defineProperty的缺陷:」
❞
- 无法检测数组下标的变化。
Vue中使用数组方法可以监听到是因为Vue对这些方法进行了重写。- 只可以监听属性,无法监听对象本身。
Vue是通过遍历对象的每个属性,通过添加getter/setter属性修改成访问器属性。而不是监听对象本身,所以对于后来给对象添加的属性难以监听。- 当对象增删的事件,监听不到变化。
原因同上。Vue监听的是对象属性,而不是对象本身。
本文使用 mdnice 排版