前言

马上要秋招了,搜集整理了一些Vue面试题,包括组件、指令、API等相关内容,巩固基础😎秋招冲冲冲!!!本篇包括:

✅ webpack和vite的对比
✅ v-if和v-show的区别
✅ 绑定class的数组用法
✅ 组件中data为什么是函数
✅ 生命周期

Webpack 和 Vite对比

vite凭什么比webpack快

webpack启动需要打包

webpack dev serve在启动时,会把所有的包都build一遍,从入口文件起索引整个项目的文件,编译成一个或多个js文件,不管模块是否被执行,都会被打包到bundler里。随着项目的复杂程度上升,模块增加,打包后的bundler也会越来越大,打包速度会越来越。即项目越复杂,启动时间越来越长。

Vite在启动时不需要打包,不需要拆分模块的依赖,不需要编译,启动速度非常快。

由于现代浏览器本身就支持ES Module,会自动向依赖的Module发出请求。vite充分利用这一点,将开发环境下的模块文件,就作为浏览器要执行的文件,而不是像webpack那样进行打包合并。

vite 请求哪个模块再对该模块进行实时编译 webpack全部编译

当浏览器请求某个模块时,再根据需要对模块内容进行编译。这种按需动态编译的方式,极大的缩减了编译时间,项目越复杂、模块越多,vite的优势越明显。 在HMR方面,当改动了一个模块后,仅需让浏览器重新请求该模块即可,不像webpack那样需要把该模块的相关依赖模块全部编译一次,效率更高。 当需要打包到生产环境时,vite使用传统的rollup进行打包,因此,vite的主要优势在开发阶段。另外,由于vite利用的是ES Module,因此在代码中不可以使用CommonJS

在底层实现上,vite是基于esbuild预构建的,esbuild使用go语言编写,比js编写的打包器预构建快10-100倍,js的操作一般是毫秒级,但go达到了纳秒级

参考链接:

v-if 和 v-show的区别

首先,v-show 只是 CSS 级别的 display: none;display: block; 之间的切换,而 v-if 决定是否会选择代码块的内容(或组件)。

当需要非常频繁地切换时,使用 v-show;一次性渲染完(运行时条件很少改变),使用 v-if

使用 v-if 在性能优化上有什么经验?

因为当 v-if="false" 时,内部组件是不会渲染的,所以在特定条件才渲染部分组件(或内容)时,可以先将条件设置为 false,需要时(或异步,比如 $nextTick)再设置为 true,这样可以优先渲染重要的其它内容,合理利用,可以进行性能优化。

v-if 是“真正”的条件渲染,因为它会确保在切换过程中,条件块内的事件监听器和子组件适当地被销毁和重建。v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。

相比之下,v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。

一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。

v-ifv-for 一起使用时,v-if 具有比 v-for 更高的优先级。不推荐v-if和v-for一起使用

参考链接:

绑定 class 的数组用法

首先是基本的对象用法,传给 :class (v-bind:class 的简写) 一个对象,以动态地切换 class:

1
2
3
4
5
6
7
8
9
10
11
12
<template>
<div :class="{show: isShow}">内容</div>
</template>
<script>
export default {
data () {
return {
isShow: true
}
}
}
</script>

我们也可以在这里绑定一个返回对象的计算属性。这是一个常用且强大的模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<template>
<div :class="classObject"></div>
</template>
<script>
data() {
return {
isActive: true,
error: null,
activeClass: 'active',
errorClass: 'text-danger'
}
},
computed: {
classObject() {
return {
active: this.isActive && !this.error,
'text-danger': this.error && this.error.type === 'fatal'
}
}
}
</script>

绑定 class 的对象用法能满足大部分业务需求,不过,在复杂的场景下,会用到数组,数组中可以有对象、三元表达式

1
2
3
4
5
<div :class="[activeClass, errorClass]"></div>
// 数组中有对象
<div :class="[{ active: isActive }, errorClass]"></div>
// 数组中可以有三元表达式
<div :class="[isActive ? activeClass : '', errorClass]"></div>

下面给出一个更为复杂的例子,iView的Button组件,数组里,可以是固定的值,还有动态值(对象)的混合。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<template>
<div :class="classes"></div>
</template>
<script>
export default {
computed: {
classes () {
return [
`${prefixCls}`,
`${prefixCls}-${this.type}`,
{
[`${prefixCls}-long`]: this.long,
[`${prefixCls}-${this.shape}`]: !!this.shape,
[`${prefixCls}-${this.size}`]: this.size !== 'default',
[`${prefixCls}-loading`]: this.loading != null && this.loading,
[`${prefixCls}-icon-only`]: !this.showSlot && (!!this.icon || !!this.customIcon || this.loading),
[`${prefixCls}-ghost`]: this.ghost
}
];
}
}
}
</script>

参考链接:

组件中data为什么是函数

组件是用来复用的。 在Vue中,组件就相当于积木,我们需要用这些积木来搭建我们所预期的结构。在搭建过程中,会重复使用某一类别的积木以完成功能。

我们希望在使用组件时,data数据是相互隔离,互不影响的。 组件每复用一次,data数据就应该被复制一次,之后,当某一处复用的地方组件内data数据被改变时,其他复用地方组件的data数据不受影响,就需要通过data函数返回一个对象作为组件的状态。

如果我们把组件的data写成对象形式,这些实例用的是同一个构造函数,由于JavaScript的特性,js里对象是引用关系,作用域没有隔离 ,所有的组件实例共用了一个data,就会造成一个变了全都会变(牵一发而动全身)的结果。不能因为将一块圆锥体积木放置在屋顶位置,就将其他用到圆锥体的地方都重置在屋顶。

当我们将组件中的data写成一个函数,数据以函数返回值形式定义 ,这样每复用一次组件,就会返回一份新的data,拥有自己的作用域,类似于给每个组件实例创建一个私有的数据空间,让各个组件实例维护各自的数据


vue每次会通过组件创建出一个构造函数,每个实例都是通过这个构造函数new出来的。

如果data是一个对象,将这个对象放到这个放到原型上去

1
2
3
4
5
6
7
8
9
function VueComponent(){}
VueComponent.prototype.$options = {
data:{name:'three'}
}
let vc1 = new VueComponent();
vc1.$options.data.name = 'six'; // 将vc1实例上的data修改为six
let vc2 = new VueComponent(); // 在new一个新的实例vc2
console.log(vc2.$options.data.name); six
// 输出vc2的data的值是six,这时候发现vc2中的data也被修改了,他们data相互影响

将data改为一个函数

1
2
3
4
5
6
7
8
9
10
11
// 这样就可以保证每个组件调用data返回一个全新的对象,和外部没有关系
function VueComponent(){}
VueComponent.prototype.$options = {
data: () => ({name:'three'})
}
let vc1 = new VueComponent();
let objData = vc1.$options.data()
objData.name = 'six'; // 调用data方法会返回一个对象,用这个对象作为它的属性
console.log(objData)
let vc2 = new VueComponent();
console.log(vc2.$options.data());

参考链接:

生命周期

Vue3 生命周期 主要有 8 个阶段:

  • 创建前 / 后(beforeCreate / created):在 beforeCreate 阶段,Vue 实例的挂载元素 el 和数据对象 data 都为 undefined,还未初始化。在 created 阶段,Vue 实例的数据对象 data 有了,el 还没有。

  • 载入前 / 后(beforeMount / mounted):在 beforeMount 阶段,Vue 实例的 $el 和 data 都初始化了,但还是挂载之前为虚拟的 DOM 节点,data 尚未替换。在 mounted 阶段,Vue 实例挂载完成,data 成功渲染。

  • 更新前 / 后(beforeUpdate / updated):当 data 变化时,会触发 beforeUpdate 和 updated 方法。这两个不常用,且不推荐使用。

  • 销毁前 / 后(beforeDestroy / destroyed):beforeDestroy 是在 Vue 实例销毁前触发,一般在这里要通过 removeEventListener 解除手动绑定的事件。实例销毁后,触发 destroyed。

所有生命周期钩子的 this 上下文将自动绑定至实例中,因此你可以访问 data、computed 和 methods。这意味着你不应该使用箭头函数来定义一个生命周期方法 (例如 created: () => this.fetchTodos())。因为箭头函数绑定了父级上下文,所以 this 不会指向预期的组件实例,并且this.fetchTodos 将会是 undefined。

相关文章

我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=30t04w2kn86ck