收集一些VUE常出现的面试题,方便自己查阅
这是一个老生常谈的问题了,这次我们深入的看下两者的区别
created
钩子函数在 Vue 实例创建完毕后调用,此时 Vue 组件的数据和方法已经初始化,但 DOM 元素还没有被渲染出来。在 created
生命周期中可以访问组件的数据,进行一些初始化操作,但不能访问到组件中的 DOM 元素。
mounted
钩子函数在组件挂载到 DOM 上后调用,此时组件已经渲染到页面中,在 mounted
生命周期中可以访问和操作组件中的 DOM 元素。通常我们在 mounted 生命周期中绑定网络请求、定时器和事件等。
从事件循环角度来看,Vue 组件的 created
和 mounted
钩子函数都是异步任务,在 Vue 的生命周期流程中处于不同的阶段。
在 JavaScript 中,事件循环机制用于管理执行异步任务和非异步任务的顺序。具体来说,事件循环机制执行以下步骤:
当 Vue 组件被创建时,created
钩子函数被添加到宏任务队列中,该队列可以理解为是 Vue 生命周期的一部分。当主线程空闲时,事件循环机制会从宏任务队列中取出 created
钩子函数,并执行该函数。
当组件被挂载到 DOM 上时,mounted
钩子函数被添加到微任务队列中。微任务是一类优先级比较高的异步任务,会排在宏任务之前执行。返回到上一步,当主线程完成当前宏任务的执行后,事件循环机制从微任务队列中取出 mounted
钩子函数并执行。
因此,从事件循环的角度来看,created
和 mounted
钩子函数是以不同的任务类型被添加到事件队列中,并在不同的事件循环阶段被执行的。在 Vue 的生命周期中,异步任务的区别很重要,因为在不同的任务类型和执行阶段中,组件的状态和数据也会不同。
Vue是一款流行的JavaScript前端框架,它是一个渐进式框架,可以用于构建单页应用程序(SPA)和复杂的用户界面。Vue的核心库只关注视图层(View),它通过数据绑定、组件化和虚拟DOM等技术,使构建Web应用程序更加简单、灵活、高效和可维护。
Vue和其他JS框架的区别主要体现在以下几个方面:
声明式渲染:Vue使用声明式渲染代替常见的命令式渲染,使得编写模板更加高效、简单和直观。
组件化:Vue的组件化架构让应用程序更加模块化和可复用,通过组合不同的组件,可以构建出丰富和复杂的用户界面。
虚拟DOM:Vue使用虚拟DOM技术,优化了绑定和更新的性能,减少了DOM操作的消耗,并提高了应用程序渲染的效率。
数据绑定:Vue提供了多种数据绑定选项,包括单项绑定、双向绑定和属性绑定等,使组件视图和数据模型之间的交互更加流畅和一致。
生态系统:Vue拥有丰富的生态系统和社区支持,提供了大量可扩展和可用的插件、组件库和工具,加速了Vue应用程序的开发和部署。
总之,Vue和其他JS框架的不同之处在于其简单、高效、可维护的特点,以及其强大的组件化架构和优秀的生态系统。这些特点让Vue成为构建现代Web应用程序的优秀选择。
MVVM是一种软件设计模式,它的全称为Model-View-ViewModel(模型-视图-视图模型),它通过将用户界面(UI)的表示层分离出来,让开发人员更好地组织和管理业务逻辑。
MVVM的三个部分分别是:
模型(Model):保存应用程序的数据模型和业务逻辑,与界面无关。
视图(View):用户界面,解释和显示Model的数据。
视图模型(ViewModel):连接视图和模型,维护视图的状态和行为,并将数据从Model中转换或适配到View中。ViewModel也可以向Model发送命令,执行适当的操作。
Vue使用MVVM模式来组织和管理应用程序的用户接口(UI),模型(Model)由应用程序自身的数据和逻辑组成,而HTML, CSS和Vue组件则构成了用户界面(View)。Vue的ViewModel被称为Vue组件,一个组件包含了视图和状态,它将用户界面连接到应用程序的数据,并对组件逻辑和行为进行管理。当数据发生改变时,Vue将自动更新视图,使应用程序的用户界面保持同步。
要实现MVVM模式,我们需要在Vue组件中编写以下代码:
模型(Model):在组件的data属性中定义应用程序的数据模型和业务逻辑。
视图(View):在组件的template属性中定义用户界面的HTML和CSS。
视图模型(ViewModel):在组件的methods属性中定义组件的逻辑和行为,对Model的数据进行转换或适配,并向Model发送命令执行适当的操作。
通过在Vue组件中实现这三个部分,我们可以实现MVVM模式,将应用程序的用户界面和业务逻辑组织和管理起来,从而提高Web应用程序的可维护性和复用性。
Vue组件通信常用的方式有以下几种:
父子组件通信(Props/emit):Props是一种父子组件传递数据的方式,父组件通过Props向子组件传递数据,子组件通过$emit触发父组件的函数来实现和父组件的通信。
兄弟组件通信(EventBus):EventBus是一种利用Vue实例作为事件总线来实现兄弟组件通信的方式,兄弟组件通过Vue实例的事件触发器来进行消息的传递和接收。
跨级组件通信(Provide/Inject):Provide/Inject是一种高级的组件通信方式,父组件通过Provide向子孙组件提供数据,子孙组件通过Inject注入Provider提供的数据,并进行操作。
任意组件通信(Vuex):Vuex是一种全局状态管理器,允许应用程序中任何组件访问全局数据,并使所有组件之间的数据同步更加灵活和方便。
这些组件通信方式各有优缺点,如下:
父子组件通信方式简单直接,父组件向子组件传递数据,子组件向父组件发送命令或者事件,但是仅适用于父子组件之间的通信。
兄弟组件通信方式可以让任意两个拥有Vue实例的组件进行通信,但是需要写大量的事件代码,不如其他方式良好的封装。
跨级组件通信方式可以跨越多层次的组件进行数据传递,提供了更好的数据组织和封装,但是需要开发人员有一定的抽象思维能力和组件化编程技巧。
Vuex数据状态管理能够全局共享数据状态,多个组件之间共享数据更容易,但是Store API 的学习曲线比较陡峭,需要开发人员掌握Vuex的使用。
根据实际的组件通信情况,开发人员需要根据自己的业务需求和技术能力来选择适合自己的组件通信方式。
Vue的响应式原理是指Vue能够自动地追踪所有依赖于数据的内容,并在数据发生变化时自动更新相关内容。这种自动响应机制使得Vue的数据绑定更加强大和灵活,能够更好地处理复杂的动态数据。
Vue的响应式原理基于Object.defineProperty()方法来实现。每个Vue实例存储着它的data属性对象,当属性被访问时,Vue会检测一个依赖性是否存在,如果存在,则把这个依赖性收集起来。一旦这个属性被更改,Vue会通知依赖于它的组件重新渲染。
监听数组的变化可以使用Vue的变异方法,即push,pop,shift,unshift,splice,sort和reverse方法,这些方法被改写为会触发数据更新的形式。
监听对象数据的变化可以使用Vue提供的$watch方法来监视某个对象属性的变化,也可以使用Vue.set方法来添加新属性,Vue.delete方法来删除属性。当对象属性变化时,Vue会自动尽可能地通知依赖于该对象的组件进行更新。
除此之外,在Vue 3中,提供了Proxy代理的方式实现数据的响应式,允许我们监听数组和对象类型的数据变化,而不需要重写Vue的原生的方法。
在使用Vue时,我们需要注意的是,由于Vue使用Object.defineProperty和Proy来追踪数据的变化,因此它只能追踪直接属性访问方式的数据变化,不能追踪到通过数组下标或者对象属性删除方式所产生的变化。如果要追踪这些变化,需要使用Vue提供的特定方法进行更改和监听。
生命周期钩子(Lifecycle Hooks)是Vue提供的一系列回调函数,用于在组件创建、更新或销毁不同阶段自动执行相关的业务逻辑,如初始化数据、获取数据、绑定事件、销毁资源等。组件生命周期钩子可以让开发人员在不同的生命周期阶段对组件进行针对性的处理,从而更加灵活和高效地控制组件的行为。
Vue主要的生命周期钩子包括以下几个:
beforeCreate:组件实例化之前调用,此时组件的data和methods等属性还没有初始化,主要用于组件和插件的初始化。
created:组件初始化完成时调用,此时组件data和methods等属性已经初始化,Vue实例还未挂载到DOM元素上,此时可以进行一些异步数据的获取等操作。
beforeMount:组件挂载到DOM元素之前被调用,此时组件的模板已经编译成DOM节点,但还未挂载到页面上。
mounted:组件挂载到DOM元素后被调用,此时组件已经渲染到页面上,可以进行一些初始化的DOM操作、开启定时器和绑定事件等操作。
beforeUpdate:组件数据发生变化之前被调用,此时组件的数据更新之前已经完成计算,但DOM还没有更新。
updated:组件数据更新完成后调用,此时DOM已经重新渲染完成。
activated:组件被激活时调用,如组件被包含在Vue keep-alive标签中并被激活时调用。
deactivated:组件被停用时调用,如组件被包含在Vue keep-alive标签中并被停用时调用。
beforeDestroy:组件销毁前被调用,此时组件实例仍存在,可以进行一些清理操作。
destroyed: 组件销毁后被调用,此时组件实例已经销毁,所有的事件监听和Vue实例已经解除,不需要进行组件操作了。
以上生命周期钩子函数,能够让我们在组件的不同阶段控制组件行为,并在适当的时候进行资源清理和垃圾回收,从而加强组件的可维护性和性能优化。
计算属性和侦听器是Vue.js框架中的两个重要概念。
计算属性是一个通过其他属性计算得出的属性,在 Vue 实例中通过 getter 函数定义,可以在模板中使用。当计算属性所依赖的属性发生变化时,计算属性会重新计算得到新的值。
侦听器是一个监视对象属性变化并执行相应操作的函数,在 Vue 实例中通过 watch 选项定义。当监视的属性发生变化时,侦听器会执行相应的函数。
区别在于:
Vue中常用的指令有很多,下面列举一些常用的指令:
v-bind和v-model指令的区别是:
因为v-model是双向数据绑定,所以它只能应用于表单元素(如input、textarea、select等),而v-bind可以应用于任何DOM元素的属性。
在Vue中,我们可以通过绑定样式来实现动态控制样式效果。Vue提供了多种方式来绑定样式和类名,下面介绍一些常用的方式。
我们可以使用 v-bind:style
指令进行内联样式的绑定。我们只需要给 v-bind:style
指令传递一个对象,对象中的属性就是样式名,属性值就是样式值:
<div v-bind:style="{ color: textColor, fontSize: textSize + 'px' }">这是一个内联样式div>
上面代码中,textColor
和 textSize
都是Vue实例中的数据属性。
我们可以使用 v-bind:class
或者 :class
进行类名的绑定。和绑定内联样式一样,我们可以给 v-bind:class
或者 :class
指令传递一个对象,对象中的属性就是类名,属性值为 true/false
,表示是否应用该类名:
<div v-bind:class="{ active: isActive, 'text-danger': isDanger }">这是一个样式绑定示例div>
上面代码中,isActive
和 isDanger
都是Vue实例中的数据属性。
Vue中支持在样式或者类名中使用JavaScript表达式,方法和绑定内联样式或者绑定类名一样。在绑定内联样式时使用的是 v-bind:style
或者 :style
,在绑定类名时使用的是 :class
或者 v-bind:class
。 例如:
<div :style="{backgroundColor: isDisabled ? 'gray' : 'white'}">按钮div>
<div :class="{disabled: isDisabled}">按钮div>
上面代码中,当 isDisabled
为真时,按钮会被禁用并且背景颜色变成灰色,同时,按钮会应用 disabled
类名,以便我们对它进行样式控制。
通过这些方法,我们可以很方便地实现在Vue中绑定样式和类名,这也体现了Vue框架的数据驱动思想,使我们能够更方便地控制页面效果。
Vue中的组件懒加载通常使用Webpack提供的代码分割功能来实现,这种方法可以将页面中的组件代码分割成多个文件,从而实现按需加载。
假设我们有一个名为 MyComponent
的组件,我们可以将它的代码拆分成一个独立的模块,在需要的时候再按需加载这个模块。下面是实现组件懒加载的具体步骤:
我们可以使用 import()
函数异步地加载组件代码。使用 import()
函数定义的组件,返回值是一个 Promise
对象,可以使用 resolve
关键字来将组件定义为异步组件。
下面的例子是一个异步定义的组件:
const MyComponent = () => import('./MyComponent.vue')
在需要使用 MyComponent
组件时,只需要把它作为路由的组件即可。同样地,我们可以使用 import()
函数异步地加载路由组件代码,并将其定义为异步路由组件。例如:
const router = new VueRouter({
routes: [
{
path: '/my-component',
component: () => import('./MyComponent.vue')
}
]
})
上面代码中,我们异步地加载了 MyComponent
组件并将它作为 /my-component
路由的组件。
使用组件懒加载可以减小初始 JS 文件的体积,提高了页面的加载速度。不过需要注意,组件懒加载会增加一定的网络请求次数,特别是当组件较多时。
总结一下,组件懒加载是一种优化网页性能的方式,Vue通过webpack的import()
函数来实现组件的异步加载,懒加载的组件仅在使用时被加载,从而减少初始下载量和加快应用程序的启动时间。
Vue中的异步组件使我们可以实现按需加载组件,提高网页性能和用户体验。在Vue中实现异步组件的方法有两种,分别是 Vue.component()
和 import()
。
首先,我们来看看如何使用 Vue.component()
异步加载组件。具体步骤如下:
我们可以定义一个使用 Vue.component()
方法创建的异步组件,将异步组件定义为一个函数,该函数返回一个 Promise
对象。
Vue.component('async-component', () => import('./AsyncComponent.vue'))
上面代码中, AsyncComponent.vue
是需要异步加载的组件。
要在模板中使用异步组件,我们可以使用 v-bind:is
或 :is
属性动态地绑定组件名称。例如:
<component v-bind:is="currentComponent">component>
上面代码中, currentComponent
是组件的名称,可以是任何合法的组件名称。
接下来,我们再来看看如何使用 import()
实现异步组件的加载。具体步骤如下:
import()
加载组件我们可以使用 import()
函数异步地加载组件,并将其定义为异步组件。例如:
export default {
components: {
'async-component': () => import('./AsyncComponent.vue')
}
}
上面代码中,我们异步地加载了 ./AsyncComponent.vue
文件,将其定义为异步组件,并将异步组件注册到了当前Vue组件中。
和使用 Vue.component()
异步加载组件的方法一样,在模板中使用异步组件时,我们也可以使用 v-bind:is
或 :is
属性动态地绑定组件名称。
以上就是使用Vue实现异步组件的方法,需要注意的是,在使用异步组件时,我们需要确保组件名称是唯一的,否则会出现组件渲染错误的问题。同时,在异步加载组件时,我们需要注意组件的引入路径和组件的命名方式,以保证异步组件的正确加载。
Vue 的单向数据流和双向数据绑定是 Vue 中常用的两种数据交互方式。
单向数据流指的是,数据只能从父级组件传递到子级组件,而子级组件无法直接修改父级组件的数据。在 Vue 中,单向数据流是通过 props 标签来实现的。通过在父级组件中定义 props,子级组件可以接收父级组件的数据,并通过 props 来使用这些数据。
例如,在父级组件中定义一个 message 属性,然后将其传递给子级组件:
<template>
<child-component :message="message" />
template>
<script>
export default {
data() {
return {
message: 'hello',
}
},
components: {
ChildComponent,
},
}
script>
在子级组件中,可以通过 props 对象来接收父级组件传递过来的数据:
<template>
<div>{{ message }}div>
template>
<script>
export default {
props: {
message: {
type: String,
required: true,
},
},
}
script>
在这个例子中,子级组件无法直接修改 message 属性的值。如果要修改这个值,必须通过调用父级组件传递过来的方法来实现。
双向数据绑定是指在父级组件和子级组件之间建立一个双向的数据连接,当父级组件的数据发生变化时,子级组件的数据会自动更新。在 Vue 中,可以使用 v-model 指令来实现双向数据绑定。
例如,在父级组件中可以使用 v-model 指令将 message 属性和子级组件中的一个表单元素进行双向绑定:
<template>
<child-component v-model="message" />
template>
<script>
export default {
data() {
return {
message: 'hello',
}
},
components: {
ChildComponent,
},
}
script>
在子级组件中可以使用 $emit 方法来更新父级组件中的 message 属性:
<template>
<div>
<input type="text" :value="message" @input="updateMessage" />
div>
template>
<script>
export default {
props: {
value: {
type: String,
required: true,
},
},
methods: {
updateMessage(event) {
this.$emit('input', event.target.value)
},
},
}
script>
双向数据绑定的优点是方便,可以简化代码的编写。但是在复杂的应用程序中,使用双向数据绑定可能会导致数据流不易跟踪,难以调试。因此,在实际应用中,需要谨慎考虑是否使用双向数据绑定。
Vue和React都是现代流行的前端框架,它们都具有许多优点和适用场景。以下是Vue和React的区别:
Vue使用模板语法,在HTML模板中定义数据绑定和指令,这使得模板更直观、易于理解。而React使用JSX语法,将HTML和JavaScript打包在一起,可以在组件化方面提供更好的支持。模板语法和JSX语法在视觉外表和开发体验方面有明显的差异,开发者可以根据个人喜好和操作习惯来选择使用。
Vue使用指令和组件之间的模板,采用更传统的双向数据流的方式,有效地提高了开发效率。React使用单向数据流和组件之间的传递,拥有更清晰和强大的状态管理机制,支持异步渲染,能够更好地保持应用程序的性能。
Vue和React的生命周期有所不同,Vue具有更多的生命周期钩子,比如beforeCreate
、mounted
、beforeUpdate
、updated
和destroyed
等。React则使用componentDidMount
、componentWillUnmount
、componentDidUpdate
等生命周期函数来处理组件的渲染状态和数据状态管理。
Vue和React都有许多优秀的插件和组件库,比如Vue的Vue Router、Vuex和Element UI,以及React的React Router、Redux和Ant Design等。这些插件和组件库可以快速有效地提高应用程序的开发效率和质量。
总之,Vue和React都是优秀的前端框架,选择哪个框架取决于开发者的具体需求和操作习惯。Vue适合构建小型项目和简单的用户界面,而React更适合定制化、大型或较复杂的应用程序开发。
防抖(debounce)和节流(throttle)是两种前端性能优化方法,用于优化处理频繁触发的事件。在Vue中,我们可以使用防抖和节流来优化输入框输入、滚动加载等常见场景。
防抖的主要思路是:当事件触发后,不立即执行函数,而是等待一段时间,如果在等待期间没有再次触发事件,才执行函数,否则重新开始等待。这个等待期间就称为防抖时间。
在Vue中,我们可以使用 lodash
库提供的 debounce
函数来实现防抖。该函数需要传入 wait
(等待时间)和 options
两个参数,其中 options
可以设置防抖的开始时刻(leading)和结束时刻(trailing)等选项。以下是使用 lodash
库实现防抖的代码示例:
import _ from 'lodash';
export default {
data() {
return {
inputValue: ''
};
},
methods: {
debounceHandler: _.debounce(function() {
// 处理函数代码
}, 500)
}
};
上面的代码中,我们使用 import
导入了 lodash
库,并在 methods
中定义了 debounceHandler
函数。该函数使用 _.debounce
函数对处理函数进行防抖处理,其中 500
表示防抖等待时间为 500 毫秒。当事件触发后,debounceHandler
不会立即执行,而是等待 500 毫秒。如果 500 毫秒内又触发了事件,那么防抖计时器会重置,重新等待 500 毫秒。
节流的主要思路是:当事件触发后,不立即执行函数,而是等待一段时间,如果在等待期间没有再次触发事件,才执行函数,否则在等待时间内只执行一次函数。这个等待期间就称为节流时间。
在Vue中,我们可以使用 lodash
库提供的 throttle
函数来实现节流。该函数需要传入 wait
(等待时间)和 options
两个参数,其中 options
可以设置节流的开始时刻(leading)和结束时刻(trailing)等选项。以下是使用 lodash
库实现节流的代码示例:
import _ from 'lodash';
export default {
data() {
return {
inputValue: ''
};
},
methods: {
throttleHandler: _.throttle(function() {
// 处理函数代码
}, 500)
}
};
上面的代码中,我们使用 import
导入了 lodash
库,并在 methods
中定义了 throttleHandler
函数。该函数使用 _.throttle
函数对处理函数进行节流处理,其中 500
表示节流等待时间为 500 毫秒。当事件触发后,throttleHandler
不会立即执行,而是等待 500 毫秒。如果 500 毫秒内又触发了事件,那么 throttleHandler
只会在 500 毫秒内执行一次。
总之,防抖和节流是前端性能优化的两种常用方法,可以在处理频繁触发的事件时提高用户体验。Vue中可以使用 lodash
库提供的 debounce
和 throttle
函数来实现防抖和节流。使用这些技术可以有效地避免过度的资源消耗和性能浪费。
Vue 服务端渲染(SSR)的原理基本上可以归纳为以下几个步骤:
接收客户端请求,请求发到服务端。
服务端获取请求,获取需要渲染的相关组件,并通过 Vue.js 的实例化函数来创建 Vue.js 实例,然后将当前的路由信息传递给实例。
服务端调用 Vue.js 实例的 asyncData
方法获取数据,并将数据填充到组件内部的 data
中。
服务端使用 Vue.js 实例渲染组件,并将渲染结果返回给客户端。
客户端接收到首屏 html 内容,根据 Vue.js 实例和服务端渲染生成的 HTML 执行客户端 JS。
Vue SSR 的优点如下:
更快的首屏加载速度:由于服务端渲染会提前进行页面渲染,使得用户可以更快地看到页面内容,而不必等待浏览器下载并执行 JavaScript 脚本。
网络爬虫友好:服务端渲染可以更好地支持搜索引擎的爬虫,对于 SEO 优化非常有帮助。
更好的用户体验:与传统 SPA 单页应用相比,服务端渲染应用可以更快响应用户交互操作,并减少页面加载时间。
Vue SSR 的缺点如下:
更高的开发门槛:与创建传统单页应用相比,Vue SSR 应用需要更多地了解服务端相关技术,例如 Node.js 和 Express 等。
服务端负载增加:由于服务端需要更多的工作来处理首屏渲染,因此相对于传统单页应用而言会增加服务端的负载。
开发复杂:对于需要频繁更改页面结构或 UI 的应用,由于服务端渲染需要同时更新前端和后端代码,因此开发难度会更高。