当前位置:首页 » 《休闲阅读》 » 正文

2024前端Vue面试题

18 人参与  2024年04月17日 13:35  分类 : 《休闲阅读》  评论

点击全文阅读


1.Vue2.0 双向绑定的原理与缺陷

Vue响应式指的是:组件的data发生变化,立刻触发视图的更新

原理: Vue 采用数据劫持结合发布者-订阅者模式的方式来实现数据的响应式,通过Object.defineProperty来劫持数据的setter,getter,在数据变动时发布消息给订阅者,订阅者收到消息后进行相应的处理。 通过原生js提供的监听数据的API,当数据发生变化的时候,在回调函数中修改dom

核心API:Object.defineProperty

Object.defineProperty API的使用 作用: 用来定义对象属性

特点: 默认情况下定义的数据的属性不能修改 描述属性和存取属性不能同时使用,使用会报错

响应式原理: 获取属性值会触发getter方法 设置属性值会触发setter方法 在setter方法中调用修改dom的方法

Object.defineProperty的缺点

1. 一次性递归到底开销很大,如果数据很大,大量的递归导致调用栈溢出

2. 不能监听对象的新增属性和删除属性

3. 无法正确的监听数组的方法,当监听的下标对应的数据发生改变时

2.Vue3.0 实现数据双向绑定的方法

Vue3.0 是通过Proxy实现的数据双向绑定,Proxy是ES6中新增的一个特性,实现的过程是在目标对象之前设置了一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。

用法: ES6 原生提供 Proxy 构造函数,用来生成 Proxy 实例。 var proxy = new Proxy(target, handler) target: 是用Proxy包装的被代理对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。

handler: 是一个对象,其声明了代理target 的一些操作,其属性是当执行一个操作时定义代理的行为的函数。

`Object.defineProperty` 的问题:在Vue中,`Object.defineProperty`无法监控到数组下标的变化,导致直接通过数组的下标给数组设置值,不能实时响应。目前只针对以上方法做了hack处理,所以数组属性是检测不到的,有局限性Object.defineProperty只能劫持对象的属性,因此我们需要对每个对象的每个属性进行遍历。Vue里,是通过递归以及遍历data对象来实现对数据的监控的,如果属性值也是对象那么需要深度遍历,显然如果能劫持一个完整的对象,不管是对操作性还是性能都会有一个很大的提升。

Proxy的两个优点:可以劫持整个对象,并返回一个新对象,有13种劫持

3.vue 的 keep-alive

``作用:缓存组件,提升性能,避免重复加载一些不需要经常变动且内容较多的组件。

``的使用方法:使用``标签对需要缓存的组件进行包裹,默认情况下被``标签包裹的组件都会进行缓存,区分被包裹的组件是否缓存有两种方法,第一种是给keepalive 添加属性,组件名称指的是具体组件添加的name,不是路由里面的name。include 包含的组件(可以为字符串,数组,以及正则表达式,只有匹配的组件会被缓存)。exclude 排除的组件(以为字符串,数组,以及正则表达式,任何匹配的组件都不会被缓存)。第二种也是最常用的一种是,和路由配合使用:在路由中添加meta属性。 使用keepalive导致组件不重新加载,也就不会重新执行生命周期的函数,如果要解决这个问题,就需要两个属性进入时触发:activated 退出时触发:deactivated

``适用的场景:首页展示固定数据的组件,比如banner九宫格

4.Vue 中 $nextTick 作用与原理

Vue 在更新 DOM 时是异步执行的,在修改数据后,视图不会立刻更新,而是等同一事件循环中的所有数据变化完成之后,再统一进行视图更新。所以修改完数据,立即在方法中获取DOM,获取的仍然是未修改的DOM。

$nextTick的作用是:该方法中的代码会在当前渲染完成后执行,就解决了异步渲染获取不到更新后DOM的问题了。

$nextTick的原理:$nextTick本质是返回一个Promise

应用场景:在钩子函数created()里面想要获取操作Dom,把操作DOM的方法放在$nextTick中

5.v-if 和 v-show区别

作用: 都是控制元素隐藏和显示的指令

区别:

v-show: 控制的元素无论是true还是false,都被渲染出来了,通过display:none控制元素隐藏

v-if: 控制的元素是true,进行渲染,如果是false不渲染,根本在dom树结构中不显示

应用: v-show: 适合使用在切换频繁显示/隐藏的元素上 v-if: 适合使用在切换不频繁,且元素内容很多,渲染一次性能消耗很大的元素上

6.Vue 列表为什么加 key

为了性能优化 因为vue是虚拟DOM,更新DOM时用diff算法对节点进行一一比对,比如有很多li元素,要在某个位置插入一个li元素,但没有给li上加key,那么在进行运算的时候,就会将所有li元素重新渲染一遍,但是如果有key,那么它就会按照key一一比对li元素,只需要创建新的li元素,插入即可,不需要对其他元素进行修改和重新渲染。

key也不能是li元素的index,因为假设我们给数组前插入一个新元素,它的下标是0,那么和原来的第一个元素重复了,整个数组的key都发生了改变,这样就跟没有key的情况一样了

7.vue-router 实现懒加载的方法

vue-router 实现懒加载的方法有两种:

ES6的impot方式: component: () => import(/* webpackChunkName: "about" */ '../views/About.vue'),

VUE中的异步组件进行懒加载方式: component: resolve=>(require(['../views/About'],resolve))

vue-router 实现懒加载的作用:性能优化,不用到该路由,不加载该组件。

8.vue钩子函数

钩子函数用来描述一个组件从引入到退出的全过程中的某个过程,整个过程称为生命周期。

钩子函数按照组件生命周期的过程分为,挂载阶段=>更新阶段=>销毁阶段。

每个阶段对应的钩子函数

挂载阶段:beforeCreate、created、beforeMounted、mounted

更新阶段:beforeUpdate、updated

销毁阶段:beforeDestroy、destroyed

每个阶段特点与适合做什么

created:实例创建完成,可访问data、computed、watch、methods上的方法和数据,未挂载到DOM,不能访问到el属性,el属性,ref属性内容为空数组常用于简单的ajax请求,页面的初始化

beforeMount:在挂载开始之前被调用,beforeMount之前,会找到对应的template,并编译成render函数

mounted:实例挂载到DOM上,此时可以通过DOM API获取到DOM节点,$ref属性可以访问常用于获取VNode信息和操作,ajax请求

beforeupdate:响应式数据更新时调用,发生在虚拟DOM打补丁之前,适合在更新之前访问现有的DOM,比如手动移除已添加的事件监听器

updated:虚拟 DOM 重新渲染和打补丁之后调用,组件DOM已经更新,可执行依赖于DOM的操作避免在这个钩子函数中操作数据,可能陷入死循环

beforeDestroy:实例销毁之前调用。这一步,实例仍然完全可用,this仍能获取到实例,常用于销毁定时器、解绑全局事件、销毁插件对象等操作

父子组件钩子函数在三个阶段的代码执行顺序

挂载:父亲created> 子created > 子mounted> 父亲mounted>

更新:父亲beforeUpdate > 子beforeUpdated > 子updated > 父亲updated

销毁:父亲beforeDestroy> 子beforeDestroy > 子destroyed> 父destroyed

9.Vue的响应式原理

Vue的响应式原理基于Object.defineProperty方法,通过getter和setter来观察和响应数据的变化。当一个普通的JavaScript对象被用作Vue实例的data对象时,Vue会遍历这个对象的所有属性,并使用Object.defineProperty将它们转化为getter和setter。

当属性被访问时,getter函数会被调用,这个函数可以用来实时计算属性的值。而在属性被修改时,setter函数会被调用,这个函数可以用来监听属性的变化,并触发相应的更新操作。

当一个属性被访问时,getter函数会返回属性的当前值。如果这个值与上一次的值不同,那么Vue就会知道属性值已经改变,并触发相应的更新操作。

当一个属性被修改时,setter函数会被调用,并传入一个新的值作为参数。在setter函数中,可以监听到属性的变化,并触发相应的更新操作。

通过这种方式,Vue可以自动追踪依赖关系,并在数据发生变化时自动更新视图。这就是Vue的响应式原理。

10.Vue的组件通信方式

Vue的组件通信方式有多种,包括props、emit/on、vuex、ref/parent/children、attrs/listeners和provide/inject等。

props:父组件通过props的方式向子组件传递数据,子组件通过props接收数据。emit/on:子组件通过emit触发事件,父组件通过on监听事件,实现子组件向父组件传递数据。vuex:用于全局状态管理,可以通过getters、mutations和actions实现组件间的通信。ref/parent/children:通过parent访问父组件,通过children访问子组件,实现父子组件间的通信。attrs/listeners:attrs包含传递给组件的所有属性,listeners包含监听的所有事件。provide/inject:提供者组件通过provide向消费者组件提供数据,消费者组件通过inject注入数据。

此外,还有事件总线、插槽作用域等通信方式。选择合适的通信方式可以有效地实现组件间的通信,提高开发效率和代码质量。

11.Vue中如何处理事件冒泡和捕获

在Vue中,可以使用v-on指令或@符号来监听DOM事件。默认情况下,Vue会遵循事件冒泡机制,即从子组件到父组件的事件传播顺序。但是,也可以使用事件修饰符来控制事件的捕获和冒泡阶段。

要阻止事件冒泡,可以在事件处理函数之前添加.stop修饰符, 要阻止事件默认行为,可以在事件处理函数之前添加.prevent修饰符

12.Vue中的混入(mixins)和继承(extends)的使用场景和区别

在Vue中,混入(mixins)和继承(extends)都是扩展组件功能的方式,但它们在使用场景和实现方式上存在一些区别。

混入(mixins)

混入是一种将可复用的组件逻辑封装到独立的对象中的技术,可以将其视为一种组件的模块化方式。混入对象可以包含任何组件选项,如data、methods、computed、watch、props、template等。在一个组件中,可以引入多个混入对象,并将它们合并在一起。

混入对象的使用场景包括:

定义可复用的逻辑:混入对象可以包含可复用的逻辑,这些逻辑可以在多个组件中共享。共享方法:如果多个组件需要使用相同的方法,可以将这些方法放在混入对象中,并在需要的地方引入混入对象。自定义事件:在混入对象中定义事件,可以在组件中使用这些事件来触发自定义行为。

继承(extends)

继承是一种基于已有组件创建新组件的技术。子组件继承父组件的所有属性和方法,并可以在此基础上添加或覆盖它们。通过继承,我们可以创建一个与现有组件功能相似的新组件,而无需复制和粘贴代码。

继承的使用场景包括:

创建可复用的组件:如果有一组组件具有相似的功能和结构,可以使用继承创建一个基类组件,然后从该基类组件派生出其他组件。扩展现有组件:如果需要修改现有组件的功能或添加新功能,但不想修改原始代码,则可以使用继承创建一个新的子组件,并在子组件中覆盖或添加所需的逻辑。实现多态性:通过继承,我们可以创建具有相似功能但表现不同的多个组件。例如,一个“按钮”组件可以派生出一个“确认”按钮和一个“取消”按钮。

混入与继承的区别

混入是一种对象组合技术,而继承是一种基于类的技术。在Vue中,一个组件可以同时使用混入和继承来扩展其功能。混入适用于定义可复用的逻辑和行为,而继承适用于创建具有相似功能和结构的组件。混入对象的逻辑不会直接影响到其他混入对象或父组件,而继承会直接继承父组件的所有属性和方法。在处理冲突时,混入更灵活。如果多个混入对象定义了相同的方法或数据,可以通过合并它们的属性来解决冲突。而在继承中,如果子组件和父组件定义了相同的方法或数据,子组件会覆盖父组件的相应定义。

13.Vue中如何处理异步操作

使用async/await语法:可以使用async/await语法来处理异步操作,它可以使异步代码看起来像同步代码一样。在Vue组件中,可以在methods或computed属性中定义异步函数,并使用async/await语法来执行异步操作。使用Promise:Promise是处理异步操作的另一种方式,它代表了一个最终可能完成或失败的异步操作及其结果值。在Vue组件中,可以在methods或computed属性中定义返回Promise的函数,并在异步函数中返回Promise。使用axios库:axios是一个基于Promise的HTTP客户端,可以方便地发送HTTP请求和处理响应。在Vue项目中,可以使用axios库来处理异步操作,例如从API获取数据。

14.Vue中的插槽(Slots)的概念和用法

在Vue中,插槽(Slots)是一种灵活的组件通信方式,允许将父组件中的内容插入到子组件的特定位置。通过使用插槽,可以将父组件中的内容传递给子组件,并在子组件中渲染这些内容。

插槽主要有两种类型:默认插槽和具名插槽。

默认插槽:默认插槽是最基本的插槽,当子组件没有定义具名插槽时,父组件传递给子组件的内容会被渲染到默认插槽中。

在子组件中定义默认插槽非常简单,只需要在模板中添加一个元素即可

在父组件中,可以将需要传递给子组件的内容放在标签中,并使用v-slot指令指定要插入的插槽

具名插槽:具名插槽允许将父组件中的不同内容插入到子组件的不同位置。通过在子组件中定义具名插槽,可以指定每个插槽的名称,并在父组件中传递相应的内容。

在子组件中定义具名插槽时,需要使用元素并指定一个名称属性

在父组件中,可以使用v-slot指令将不同的内容传递给具名插槽

15.Vue中如何实现路由守卫

在Vue中,可以使用路由守卫来实现权限控制、登录保护等功能。路由守卫是Vue Router提供的一种机制,可以在路由跳转之前或之后执行一些操作。

Vue Router提供了三种类型的路由守卫:全局前置守卫、全局后置守卫和组件内守卫。

1.全局前置守卫:在路由跳转之前执行的操作。可以通过全局前置守卫来控制路由的访问权限,例如检查用户是否登录。

全局前置守卫可以在全局router对象上定义beforeEach方法实现

2.全局后置守卫:在路由跳转之后执行的操作。可以通过全局后置守卫来进行一些统计、日志记录等操作。

全局后置守卫可以在全局router对象上定义afterEach方法实现

3.组件内守卫:在组件内部使用beforeRouteEnter、beforeRouteUpdate和beforeRouteLeave等生命周期钩子函数实现路由守卫。这些钩子函数会在路由进入、更新和离开时被调用。

在组件内部可以使用这些钩子函数来进行权限控制、数据初始化等操作

16.如何处理Vue中的动态路由

在Vue Router中,可以通过定义路由时指定path和component属性来创建路由,同时可以使用:来创建动态路由。例如,假设有一个用户页面,其中每个用户都有唯一的ID,我们希望通过不同的用户ID来访问不同的用户页面。我们可以这样定义动态路由当用户访问/user/123这样的URL时,我们希望在UserComponent中能够获取到这个123。为此,我们可以在组件中通过this.$route.params来获取这些参数当组件被挂载时,我们通过this.$route.params.id获取到了用户ID。注意,由于:id是动态段,因此它会被解析为字符串。如果需要数字或其他类型的数据,需要进行相应的类型转换。

此外,还可以使用Vue Router的导航守卫来处理动态路由。例如,在全局前置守卫中,可以检查动态路由参数是否存在,如果不存在则重定向到错误页面。

17.Vue中的指令(Directives)和属性(Attributes)的区别

在Vue中,指令(Directives)和属性(Attributes)是两个不同的概念,具有以下区别:

指令(Directives): 指令是Vue提供的特殊属性,它们将特殊的反应性行为应用于渲染的DOM。指令带有前缀“v-”,用于指示它们是Vue提供的特殊属性。指令的值预期是单个JavaScript表达式(v-for是例外情况)。当表达式的值改变时,指令将产生的连带影响,响应式地作用于DOM。常见的指令包括v-bind、v-model、v-if、v-for等。 属性(Attributes): 属性是HTML元素的普通属性,用于为元素提供附加信息。属性本身不会对DOM产生影响,它们只提供附加信息,如样式、类名等。在Vue中,可以使用双大括号语法{{}}将数据绑定到属性上,以实现动态更新。例如,使用{{src}}将图片的src属性绑定到Vue实例的src数据属性上。

总结:Vue中的指令和属性是两个不同的概念。指令是Vue提供的特殊属性,用于实现特殊的反应性行为,如条件渲染、事件处理等。而属性是HTML元素的普通属性,用于提供附加信息,可以通过Vue的数据绑定实现动态更新。

18.Vue中的computed属性和methods方法的区别

在Vue中,computed属性和methods方法具有以下区别:

缓存机制:computed属性是带缓存的,只有在其依赖的数据发生改变时才会重新计算,否则直接返回之前的计算结果。而methods方法在每次调用时都会执行,没有缓存机制。调用方式:computed属性更像是一个属性,其调用方式像访问数据属性一样,无需加括号。而methods方法必须以函数的形式调用,需要加括号。可读性:computed属性中的get和set方法可以组合在一起形成可读写的属性,而methods方法中无法实现类似功能。性能:由于computed属性具有缓存机制,因此在多次使用相同计算时更加高效,而methods方法每次调用都会执行,可能存在性能问题。

综上所述,computed属性和methods方法在缓存机制、调用方式、可读性和性能方面存在差异。在需要多次使用相同计算时,推荐使用computed属性;而在需要定义复杂逻辑或方法时,可以使用methods方法。

19.Vue中的虚拟DOM diff算法

Vue.js 使用了一种称为虚拟 DOM (Virtual DOM) 的技术来提高应用程序的性能。虚拟 DOM 是一种编程概念,它通过创建一个与真实 DOM 相映射的虚拟节点来代表页面上的元素,从而优化了实际的 DOM 操作。Vue 的 diff 算法是用来比较新旧虚拟 DOM 树,并找出需要被改变的部分,然后只更新这些部分,而不是重新渲染整个页面。

Vue 的 diff 算法遵循以下几个步骤:

创建新旧节点:当数据发生变化时,Vue 会创建一个新的虚拟节点 (vNode) 来表示视图,同时保留旧的 vNode。比较节点:Vue 使用一种称为 "key" 的特殊属性来区分不同的 vNode。如果一个组件的 "key" 没有发生变化,那么新旧 vNode 将被认为是相同的,并且只进行属性的更新。如果 "key" 发生了变化,那么这两个 vNode 将被认为是不同的,并且 Vue 会销毁旧的节点并创建新的节点。计算差异:如果新旧 vNode 不同,Vue 将计算它们之间的差异。这个过程包括但不限于:文本内容的改变、子节点数量的改变、子节点位置的改变等。更新 DOM:最后,Vue 将根据计算出的差异更新实际的 DOM。这个过程通常包括删除旧节点、创建新节点和移动节点。

Vue 的 diff 算法旨在最小化对实际 DOM 的操作,从而提高应用程序的性能。这种优化对于大型应用程序来说尤其重要,因为它们通常包含大量的 DOM 操作,这可能会导致性能问题。

20.Vue中实现自定义指令

首先,创建一个自定义指令函数。这个函数接收一个绑定元素和一个绑定值作为参数,并返回一个对象,其中包含指令的钩子函数。

function myDirective(el, binding) {

// 指令的钩子函数

// el:绑定元素

// binding:绑定信息对象,包含以下属性:

// - name:指令名(不包括 v- 前缀)

// - value:指令的绑定值,例如 v-my-directive="1 + 1" 中,绑定值为 2

// - oldValue:指令绑定前的值,仅在 update 和 componentUpdated 钩子中可用

// - expression:字符串形式的指令表达式。例如 v-my-directive="1 + 1" 中,表达式为 "1 + 1"

// - arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 "foo"

// - modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }

// 在这里添加自定义指令的行为

// ...

}

接下来,可以通过全局或组件级别注册自定义指令。

Vue.directive('my-directive', {

bind: myDirective, // 绑定阶段钩子函数

update: myDirective, // 更新阶段钩子函数(只调用一次)

componentUpdated: myDirective, // 组件更新完成时钩子函数(只调用一次)

unbind: myDirective // 解绑阶段钩子函数

})

在上面的示例中,

v-my-directive 是自定义指令的名称,

message 是传递给指令的值。当模板渲染时,自定义指令将根据绑定的值执行相应的行为。

21.Vue3 区分ref 与 reactive 的原因

定义数据角度对比:

ref 用来定义:基本类型数据

reactive 用来定义:引用类型,例如对象、或数组类型的数据

备注:ref也可以用来定义对象或数组类型数据,它内部会自动通过 reactive 转为代理对象

ref.value 返回的是一个 proxy 对象,他是通过代理 reactive 实现的

原理角度对比:

ref 通过 Class 的 get 与 set 来实现响应式的(数据劫持)

reactive 通过使用 Proxy 来实现响应式(数据劫持),并通过Reflect 操作源对象内部的数据。

使用角度对比:

ref 定义的数据:操作数据需要 .value,读取数据时模版中直接读取不需要 .value

reactive 定义的数据:操作数据与读取数据,均不需要 .value

可以得出 data.value.name === obj.name,但是对象更推荐用 reactive 定义,语义化更强

22.v-for 和 v-if 可以混合使用吗

不可以,v-for计算优先级比v-if高,首先会把虚拟000000节点渲染出来,然后再进行v-if判断。降低渲染性能

23.v-for 中为什么加 key

如果不使用 key,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法。key 是为 Vue 中 vnode 的唯一标记,通过这个 key,diff 算法可以更准确、更快速

更准确:因为带 key 就不是就地复用了,在 sameNode 函数 a.key === b.key 对比中可以避免就地复用的情况。所以会更加准确。

更快速:利用 key 的唯一性生成 map 对象来获取对应节点,比遍历方式更快

24.事件默认有个 event 参数,它是什么?怎么使用?事件被绑定到哪里

当事件没有参数,则默认有个 event 参数;如果有自定义参数,则需要使用$event 传过去。

event 的构造函数是 MouseEvent,即是原生的 event 对象event 被挂载到当前元素下,即 event.target

25.常用的设计模式

1.工厂模式 - 传入参数即可创建实例

虚拟 DOM 根据参数的不同返回基础标签的 Vnode 和组件 Vnode

2.单例模式 - 整个程序有且仅有一个实例

vuex 和 vue-router 的插件注册方法 install 判断如果系统存在实例就直接返回掉

3.观察者模式 (响应式数据原理)

4.策略模式 策略模式指对象有某个行为,但是在不同的场景中,该行为有不同的实现方案-比如选项的合并策略

5.策略模式:优化 if else 冗余代码

6.代理模式:mini-vue proxy

26.$emit 和 $on 的本质

1.$on、$emit 基于发布订阅模式

2.$on 用来收集所有的事件依赖,他会将传入的参数 event 和 fn 作为 key 和 value 的形式存到 vm._events 这个事件集合里,就像这样 vm._events[event]=[fn];

3.而 $emit 是用来触发事件的,他会根据传入的 event 在 vm_events 中找到对应的事件并执行 invokeWithErrorHandling(cbs[i], vm, args, vm, info)

4.最后我们看 invokeWithErrorHandling 方法可以发现,他是通过 handler.apply(context, args) 和 handler.call(context) 的形式执行对应的方法

27.虚拟 dom 是什么?原理?优缺点

用 js 模拟一颗 dom 树,放在浏览器内存中.当你要变更时,虚拟 dom 使用 diff 算法进行新旧虚拟 dom 的比较,将变更放到变更队列中,反应到实际的 dom 树,减少了 dom 操作。

虚拟 DOM 将 DOM 树转换成一个 JS 对象树,diff 算法逐层比较,删除,添加操作,但是,如果有多个相同的元素,可能会浪费性能,所以,react 和 vue-for 引入 key 值进行区分。

优点:

保证性能下限: 框架的虚拟 DOM 需要适配任何上层 API 可能产生的操作,它的一些 DOM 操作的实现必须是普适的,所以它的性能并不是最优的;但是比起粗暴的 DOM 操作性能要好很多,因此框架的虚拟 DOM 至少可以保证在你不需要手动优化的情况下,依然可以提供还不错的性能,即保证性能的下限;

无需手动操作 DOM: 我们不再需要手动去操作 DOM,只需要写好 View-Model 的代码逻辑,框架会根据虚拟 DOM 和 数据双向绑定,帮我们以可预期的方式更新视图,极大提高我们的开发效率;

跨平台: 虚拟 DOM 本质上是 JavaScript 对象,而 DOM 与平台强相关,相比之下虚拟 DOM 可以进行更方便地跨平台操作,例如服务器渲染、weex 开发等。

缺点:

无法进行极致优化: 虽然虚拟 DOM + 合理的优化,足以应对绝大部分应用的性能需求,但在一些性能要求极高的应用中虚拟 DOM 无法进行针对性的极致优化。

首次渲染大量 DOM 时,由于多了一层虚拟 DOM 的计算,会比 innerHTML 插入慢。

28.mixin 是什么?优缺点?原理?vue3 用什么取代了

组件和组件之间有时候会存在相同的代码逻辑,分为局部混入和全局混入,我们希望对相同的代码逻辑进行抽取

Mixin 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能

一个 Mixin 对象可以包含任何组件选项 — 其本质就是一个对象

当组件使用 Mixin 对象时,所有 Mixin 对象的选项将被 混入该组件本身的选项中

缺点:

变量来源不明确,不利于阅读

多 mixin 可能会造成命名冲突

mixin 和组件可能出现多对多的关系,复杂度较高

Vue3 使用 Composition API 替代了,优点:

代码提取

代码重用

命名冲突解决

29.自定义指令?原理

Vue 自定义指令有全局注册和局部注册两种方式。先来看看注册全局指令的方式,通过 Vue.directive(id, [definition]) 方式注册全局指令。然后在入口文件中进行 Vue.use() 调用。

它的作用价值在于当开发人员在某些场景下需要对普通 DOM 元素进行操作。提高代码复用。

指令本质上是装饰器,是 vue 对 HTML 元素的扩展,给 HTML 元素增加自定义功能。vue 编译 DOM 时,会找到指令对象,执行指令的相关方法。

自定义指令有五个生命周期(也叫钩子函数),分别是 bind、inserted、update、componentUpdated、unbind

1.bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。

2.inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。

3.update:被绑定于元素所在的模板更新时调用,而无论绑定值是否变化。通过比较更新前后的绑定值,可以忽略不必要的模板更新。

4.componentUpdated:被绑定元素所在模板完成一次更新周期时调用。

5.unbind:只调用一次,指令与元素解绑时调用。

原理

1.在生成 ast 语法树时,遇到指令会给当前元素添加 directives 属性

2.通过 genDirectives 生成指令代码

3.在 patch 前将指令的钩子提取到 cbs 中,在 patch 过程中调用对应的钩子

4.当执行指令对应钩子函数时,调用对应指令定义的方法

30.事件绑定原理

$on、$emit 是基于发布订阅模式的,维护一个事件中心,on 的时候将事件按名称存在事件中心里,称之为订阅者,然后 emit 将对应的事件进行发布,去执行事件中心里的对应的监听器

31.$set 的原理

因为响应式数据 我们给对象和数组本身都增加了 ob 属性,代表的是 Observer 实例。当给对象新增不存在的属性 首先会把新的属性进行响应式跟踪 然后会触发对象 ob 的 dep 收集到的 watcher 去更新,当修改数组索引时我们调用数组本身的 splice 方法去更新数组

32.Vue3 比 Vue2 有什么优势

1.性能更好2.体积更小3.更好的 TS 支持4.更好的代码组织5.更好的逻辑抽离6.更多新功能

33.Vue3 声明周期

Options API

1.beforeDestroy 改为 beforeUnmount

2.destroyed 改为 unmounted

3.其他沿用 Vue2 的生命周期

Composition API

setup 相当于整合了 beforeCreate 和 created

其余生命周期分别是写在 setup 中的函数:

1.onBeforeMount()

2.onMounted()

3.onBeforeUpdate()

4.onUpdated()

5.onBeforeUnmount()

6.onUnmounted()

34.如何理解 ref、toRef 和 toRefs

ref:

1.生成值类型的响应式数据

2.可用于模版和 reactive

3.通过.value 修改值

toRef:

1.针对一个响应式对象(reactive 封装)的 prop

2.创建一个 ref,具有响应式

3.两者保持引用关系

toRefs:

1.将响应式对象(reactive 封装)转换为普通对象

2.对象的每个 prop 都是对应的 ref(不然 reactive 响应式直接解构会失去响应式)

3.两者保持引用关系

35.为何 ref 需要 value 属性

1.ref 是一个对象(不丢失响应式,值类型不能用 proxy 代理),value 储存值2.通过 .value 属性的 get 和 set 实现响应式3.用于模版、reactive 时,不需要 .value,其他情况都需要

36.Vue3 升级了哪些重要的功能

1.createApp

2.emits 属性:在子组件中声明 emits options 父组件的绑定事件

3.生命周期:使用 setup 整合 beforeCreate 和 created``,beforeDestroy 改为 beforeUnmount,destroyed 改为 unmounted,其他与 vue2 一致

4.多事件处理:在点击事件中写入多个处理函数,用逗号分割

5.Fragment:可以存放多个根节点

6.移除 .sync

7.异步组件的写法:需要从 Vue 引入 defineAsyncComponent,使用这个函数包裹 import() 引入异步组件

8.移除 filter:双括号中用 | 分割转换含义

9.Teleport:主要场景就是把组件的嵌套层级提高

10.Suspense:用来加载异步组件未成功时的一些 loading,主要实现原理就是具名插槽

11.Composition API

37.MVVM 和MVC的原理以及区别

MVVM视图模型双向绑定,是Model-View-ViewModel的缩写

1、MVVM的优点:

低耦合。视图(View)可以独立于Model变化和修改,一个Model可以绑定到不同的View上,当View变化的时候Model可以不变化,当Model变化的时候View也可以不变;

可重用性。你可以把一些视图逻辑放在一个Model里面,让很多View重用这段视图逻辑。

独立开发。开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计。

可测试。

2、什么是MVC?

MVC是应用最广泛的软件架构之一,一般MVC分为:Model(模型),View(视图),Controller(控制器)。 这主要是基于分层的目的,让彼此的职责分开.View一般用过Controller来和Model进行联系。Controller是Model和View的协调者,View和Model不直接联系。基本都是单向联系。M和V指的意思和MVVM中的M和V意思一样。C即Controller指的是页面业务逻辑。MVC是单向通信。也就是View跟Model,必须通过Controller来承上启下。

MVC与MVVM的区别:

MVC和MVVM的区别并不是VM完全取代了C,ViewModel存在目的在于抽离Controller中展示的业务逻辑,而不是替代Controller,其它视图操作业务等还是应该放在Controller中实现。也就是说MVVM实现的是业务逻辑组件的重用。MVC中Controller演变成MVVM中的ViewModel

MVVM通过数据来显示视图层而不是节点操作

MVVM主要解决了MVC中大量的dom操作使页面渲染性能降低,加载速度变慢,影响用户体验

38.vue常见指令

v-model 多用于表单元素实现双向数据绑定

v-bind:简写为冒号:“:”,动态绑定一些元素的属性,类型可以是:字符串、对象或数组。

v-on:click 给标签绑定函数,可以缩写为:“@”,例如绑定一个点击函数 函数必须写在methods里面

v-for 格式: v-for="字段名 in(of) 数组json" 循环数组或json,记得加上key

v-show 显示内容

v-if 指令:取值为true/false,控制元素是否需要被渲染

v-else 指令:和v-if指令搭配使用,没有对应的值。当v-if的值false,v-else才会被渲染出来

v-else-if 必须和v-if连用

v-text 解析文本

v-html 解析html标签 (一般常见的解决后台的富文本内容)

v-bind:class 三种绑定方法

对象型 "{red:isred}"

三元型 " isred?"red":"blue"

数组型 " [{red:"isred" },{blue:"isblue"}] "

v-once 进入页面时 只渲染一次 不在进行渲染

v-cloak 防止闪烁

v-pre 把标签内部的元素原位输出

39.vue中的data为什么是一个函数?起到什么作用

在Vue组件中,data选项必须是一个函数,而不能直接是一个对象。这是因为Vue组件可以同时存在多个实例,如果直接使用对象形式的data选项,那么所有的实例将会共享同一个data对象,这样就会造成数据互相干扰的问题。

因此,将data选项设置为函数可以让每个实例都拥有自己独立的data对象。当组件被创建多次时,每个实例都会调用该函数并返回一个新的data对象,从而保证了数据的隔离性。

另外,data选项作为一个函数还具有一个重要的特性,就是它可以接收一个参数,这个参数是组件实例本身。这个特性在一些场景下非常有用,例如在定义组件时需要使用组件实例的一些属性或方法来计算初始数据。

因此,为了避免数据共享和保证数据隔离性,以及方便使用组件实例的属性和方法,Vue组件中的data选项必须是一个函数。

40.动态给vue的data添加一个新的属性时会发生什么

当您在Vue实例的data对象中动态添加一个新的属性时,Vue不会立即响应这个变化,因为它不能检测到对象属性的添加或删除。这是Vue的一个限制,它依赖于Object.defineProperty来监听data对象的变化,而Object.defineProperty只能监听已经存在的属性的变化。

但是,有几种方法可以在Vue中动态添加新的响应式属性:

使用Vue.set方法

:Vue提供了一个全局方法Vue.set,可以用来向响应式对象添加一个属性并确保新属性同样是响应式的。

javascript复制代码Vue.set(this.someObject, 'newProp', 'newValue')

或者使用this.$set,这是Vue实例的一个方法,效果与Vue.set相同:

javascript复制代码this.$set(this.someObject, 'newProp', 'newValue')

使用计算属性

:如果新属性的值依赖于其他响应式属性,那么可以使用计算属性来动态计算新属性的值。

重新创建data对象

:这是一种比较粗暴的方法,通过重新创建data对象来包含新的属性。但是这种方法会导致组件重新渲染,并且可能引入其他的问题。

javascript复制代码this.data = { ...this.data, newProp: 'newValue' }

请注意,在动态添加新属性时,您应该考虑这个操作是否会影响组件的状态和渲染。在大多数情况下,更好的做法是在组件的data函数中预先定义所有可能的属性,并在需要时更新它们的值。

41.Vue常用的修饰符

Vue中常用的修饰符主要包括两类:v-model修饰符和事件修饰符。

v-model修饰符:

.lazy:这个修饰符会让v-model在输入框失去焦点时才更新数据,而不是每次输入变化时都更新。.trim:这个修饰符会自动过滤输入框首尾的空白字符。.number:这个修饰符会自动将输入值转为数值类型。如果输入的不是数字,则会返回原始值。

事件修饰符:

.stop:阻止事件冒泡,相当于调用了event.stopPropagation()方法。.prevent:阻止默认行为,相当于调用了event.preventDefault()方法。.self:只有元素自身触发时才触发方法,即只有点击元素本身才会触发。.once:事件只触发一次,无论点击多少次,执行一次之后就不会再执行。.capture:使用事件的捕获模式,即事件触发的顺序是先捕获到目标元素,然后再冒泡。.sync:这个修饰符是Vue 2.3.0+ 新增的,用于对prop进行双向绑定。

此外,还有一些按键修饰符,如.enter、.tab、.delete、.esc、.space等,用于监听特定按键的触发。

这些修饰符可以单独使用,也可以组合使用,以满足不同的需求。

42.Vue实例挂载的过程

Vue实例挂载的过程大致可以分为以下几个步骤:

调用new Vue()时,会触发initMixin(Vue)函数,该函数会在Vue的原型上定义_init方法。_init方法被调用,在这个方法中,会进行一系列初始化操作,包括初始化生命周期、事件、渲染等。在_init方法的最后,会判断el选项是否存在。如果el选项存在,则会调用$mount方法将实例挂载到指定的DOM元素上。$mount方法首先会编译模板,生成render函数,然后再次调用$mount并返回,调用mountComponent方法。mountComponent方法中的updateComponent方法会调用Vue原型上的render和update方法,最后进行页面渲染。

在这个过程中,Vue会完成数据的初始化、属性的访问、DOM的挂载和渲染等一系列操作,从而生成一个可交互的Vue应用。

43.Vue中组件和插件有什么区别

Vue中的组件和插件存在以下区别:

编写形式:组件可以有多种编写方式,但最常见的是使用Vue单文件组件格式。每个.vue文件都可以被视为一个组件。而插件的实现应暴露一个install方法,该方法的第一个参数是Vue构造器,第二个参数是一个可选的选项对象。注册形式:组件的注册通常是在使用它的地方进行局部注册,通过components属性注册后可以直接使用。而插件的注册则通过Vue.use()的方式进行,第一个参数为插件的名字,第二个参数是可选择的配置项。需要注意的是,注册插件的时候,需要在调用new Vue()启动应用之前完成。使用场景:组件是用来构成App的业务模块,它的目标是App.vue。而插件则是用来增强Vue的技术栈功能模块,它的目标是Vue本身。

总的来说,Vue中的组件和插件在编写形式、注册形式和使用场景上都存在明显的区别。组件更注重业务逻辑的实现,而插件则更多地关注于对Vue技术栈的功能增强。

44.Vue中的过滤器

在 Vue.js 中,过滤器(Filters)是一种特殊的功能,允许你在插值或绑定表达式中应用函数,以格式化或处理数据。过滤器可以用在两个地方:mustache 插值和 v-bind 表达式。

过滤器可以用 pipe 符号 (|) 来定义。例如:

html复制代码<!-- 在 mustache 插值中使用过滤器 --> {{ message | capitalize }} <!-- 在 v-bind 表达式中使用过滤器 --> <div v-bind:id="rawId | formatId"></div>

在上面的例子中,capitalize 和 formatId 是过滤器的名称。这些过滤器函数应该在 Vue 实例的 filters 选项中定义:

javascript复制代码new Vue({ el: '#app', data: { message: 'hello world', rawId: '1' }, filters: { capitalize: function (value) { if (!value) return '' value = value.toString() return value.charAt(0).toUpperCase() + value.slice(1) }, formatId: function (value) { return 'id-' + value } } })

在这个例子中,capitalize 过滤器将字符串的首字母转换为大写,而 formatId 过滤器则在原始 ID 前添加了一个 "id-" 前缀。

需要注意的是,过滤器应该被看作是对纯文本的简单转换。它不应该用于执行复杂的逻辑,或者处理大量数据。对于复杂逻辑,应该使用计算属性或方法。

另外,Vue 3.x 版本已经移除了过滤器功能,因为在 Vue 3.x 中,更推荐使用计算属性或方法来处理复杂的逻辑和数据转换。

45.SPA首屏加载速度慢的怎么解决

SPA(单页应用)首屏加载速度慢的问题可以通过以下几种方式解决:

代码压缩和优化:压缩 JavaScript 和 CSS 文件可以减小它们的大小,从而加快下载速度。还可以使用工具对代码进行优化,例如删除未使用的代码,将重要代码放在前面等。使用 CDN:将静态资源文件上传到 CDN 并使用它来分发这些资源,可以减少服务器请求负载和网络延迟。延迟加载:使用按需加载技术,只有当需要访问某个特定部分时才加载相关的 JavaScript 或 CSS 文件。这样可以减少首屏加载时间,提高整体性能。或者使用 webpack 的 prefetch 功能,提前加载将要用到的资源,从而加速页面的展示速度。服务器端渲染 (SSR):将 SPA 改造为 SSR,可以将首屏所需的内容直接渲染到 HTML 中,然后再将 JavaScript 文件加载完毕后再交由客户端接管。减小入口文件体积:常用手段是路由懒加载,把不同路由对应的组件分割成不同的代码块,待路由被请求的时候会单独打包路由,使得入口文件变小,加载速度大大增加。静态资源本地缓存:利用浏览器缓存机制,对静态资源进行本地缓存,避免重复请求和加载。

46.vue项目本地开发完成后部署到服务器后报404是什么原因

在将Vue项目从本地开发环境部署到服务器后遇到404错误,可能有几个原因:

服务器配置问题: 服务器可能没有正确配置以提供静态文件(如HTML、JS、CSS文件)。如果你的Vue项目是单页应用(SPA),服务器需要配置为对所有路由返回index.html,这样Vue路由器可以接管并处理路由。 路由模式问题: Vue Router有两种模式:hash和history。如果你在开发时使用的是hash模式,但在生产环境中改为history模式,而服务器没有正确配置,可能会出现404错误。确保服务器配置与Vue Router的模式相匹配。 构建问题: 确保你正确运行了npm run build或yarn build来构建你的项目。这将生成一个用于生产的dist目录。 静态资源路径问题: 检查构建后的静态资源路径是否正确。有时候,如果publicPath在vue.config.js中配置错误,可能会导致资源加载失败。 服务器软件问题: 使用的服务器软件(如Nginx、Apache等)可能有特定的配置要求。确保你按照Vue官方文档或其他相关资源中的说明正确配置了服务器。 资源引用问题: 检查HTML中引用的CSS和JS文件路径是否正确。确保所有必要的文件都被正确地包含在了构建后的项目中。 文件权限问题: 确保服务器上的文件和目录具有正确的读取权限。 网络问题: 确保服务器和客户端之间的网络连接是正常的,没有任何网络故障或防火墙限制。

为了更准确地诊断问题,你可以检查服务器的错误日志,这通常可以提供更多关于为什么请求失败的信息。此外,使用浏览器的开发者工具检查网络请求和响应也是一个很好的方法。


点击全文阅读


本文链接:http://zhangshiyu.com/post/96518.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1