vue2面试题
2026/8/12 18:38:30 网站建设 项目流程

vue2生命周期勾子有哪些?每个勾子作用是什么?

beforeCreate
created
beforeMount
mounted
beforeUpdate
updated
beforeDestroy
destoryed
actived
deactivated

vue2中v-if和v-show区别是什么?

都是控制元素的显示和隐藏
v-if在切换时会对元素进行销毁和重建
v-show只是通过css中display属性切换显示/隐藏
v-if适用于切换频率低的场景
v-show适用于频繁切换的场景。
v-if可以配合v-else-if和v-else使用,v-show不支持

vue2中computed和watch的区别及适用场景是什么?

computed计算属性,有缓存,不支持异步,有返回值。
watch监听,没有缓存,支持异步,有两个参数,immediate:true立即执行,deep:true深度监听,没有返回值。
computed适用于多个值影响一个值
watch适用于一个值影响多个值

vue2的响应式原理是什么?

通过数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter和getter,在数据变动的时候发布消息给订阅者,触发相应的监听回调。
核心三大角色:
Observer:劫持对象所有属性(初始化的时候存在的属性),给属性添加setter和getter
dep:依赖收集,管理watcher,数据变了通知更新
watcher:订阅者,关联视图,收到通知更新视图
弊端:只能劫持对象的已有属性,无法感知属性的新增和删除。
解决方案:通过Vue提供的set和set和setdelete,或者通过重写数组原型push、pop、shift、unshift、splice、sort、reverse

v-for中的key有什么作用?为什么不能用index作为key?

key是虚拟DOM中节点的唯一标识,帮助vue的diff算法更高效地更新DOM。使用index作为key时,如果数据顺序发生变化(如插入、删除、排序),index会重新分配,导致key与实际节点不匹配。使用diff算法无法正确复用节点,kennel引发渲染错误(如输入框内容错位)和性能问题。应使用数据的唯一ID作为key。

vue2中组件通信?

  • props/$emit 父子组件通信
父组件通过v-bind(简写:)在子组件上绑定变量 父组件通过v-on(简写@)绑定方法,这方法是子组件中传过来的方法 子组件通过props接收父组件传过来的变量。 子组件中通过$emit像父组件传参。
  • ref访问子组件
父组件通过在子组件上添加ref this.$refs.childRef.xxx // 访问属性 this.$refs.childRef.方法名()//访问方法
  • parent 子组件访问父组件
this.$parent.父组件的属性
  • v-slot 父子组件通信 插槽
1、默认插槽:一处占位,父组件插入任意内容 子组件<template><divclass="card"><h3>标题</h3><!-- 定义默认插槽的占位 --><slot></slot></div></template>父组件<template><Child><!-- 这里的内容会自动填充到子组件的<slot>位置 --><p>这是插入的默认内容</p></Child></template>2、具名插槽:多个插槽,按照名字对应 子组件<!-- 子组件 Child.vue -->在子组件中,给<slot>标签添加 name 属性。如果不写 name,默认名称是 default。<template><!-- 具名插槽:头部 --><slotname="header"></slot></template><!-- 父组件 Child.vue -->父组件必须在<template>标签上使用 v-slot 指令(或简写#)加上插槽名称<template><Child><!-- 填充 header 插槽 --><template v-slot:header><h1>这是头部标题</h1></template></Child></template>3、作用域插槽<!-- 子组件 -->子组件暴漏数据,在子组件中,将需要传给父组件的数据以key="value"的形式写在<slot>属性上。<template><slot :a="aaa":b="bbb"></slot></template><!-- 父组件1 接收一个变量名-->父组件接收时,使用 v-slot(简写#)接收一个变量名,或者直接解构。<template><List><!-- 用#default 接收,slotProps 包含了子组件传过来的所有属性 --><template#default="slotProps"><span>{{slotProps.a}}-{{slotProps.b}}</span></template></List></template><!-- 父组件2 直接解构 --><template><List><!-- 解构取出 a 和 b 可以给个默认的值--><template#default="{ a, b = 0 }"><span>{{a}}.{{b}}</span></template></List></template>
  • provide/inject(祖先/后代)
    祖先通过provide提供变量
    后代通过inject来注入变量
    解决了跨层级组件间的通信问题
    主要是子组件/子孙组件获取上层组件状态
    provide直接传普通值不是响应式,用函数包括+后代通过computed接收实现响应式
    原因:直接传值传的是常量,不是引用地址
<!--祖先组件-->// provide 写成函数,返回一个包含响应式对象的引用<template><div><h1>祖先组件</h1><p>原始计数:{{state.count}}</p><button @click="state.count++">计数+1</button><button @click="state.user.firstName = '张'">改姓</button><Child /></div></template><script>importChild from'./Child.vue'exportdefault{components:{Child},data(){return{// 将需要共享的数据放在 data 中,保证响应式 state:{count:0, user:{firstName:'李', lastName:'四'}}}}, // provide 写成函数,返回一个包含响应式对象的引用provide(){return{// 直接传递 state 对象(引用),后代修改其属性会触发响应 sharedState: this.state}}}</script><!--中间组件--><template><div><h2>中间组件</h2><Grandchild /></div></template><script>importGrandchild from'./Grandchild.vue'exportdefault{components:{Grandchild}}</script><!--后代组件-->使用 inject 注入祖先提供的 sharedState,并用 computed 衍生出新数据。<template><divstyle="border:1px solid #ccc; padding:10px;"><h3>后代组件</h3><p>原始计数:{{sharedState.count}}</p><!-- 使用 computed 衍生:双倍计数 --><p>双倍计数:{{doubleCount}}</p><p>原始姓名:{{sharedState.user.firstName}}{{sharedState.user.lastName}}</p><!-- 使用 computed 衍生:全名 --><p>全名:{{fullName}}</p><button @click="sharedState.count += 2">后代+2</button></div></template><script>exportdefault{inject:['sharedState'], computed:{// 依赖 sharedState.count,当 count 变化时重新计算doubleCount(){returnthis.sharedState.count *2}, // 依赖 sharedState.user 的属性,响应式更新fullName(){const{firstName, lastName}=this.sharedState.userreturnfirstName +' · '+ lastName}}}</script>

后代直接修改祖先提供的数据破坏了“数据单项流动”的清晰性(数据流向变得混乱,难以追踪),所以虽然技术上能改,但是为了代码可维护性,推荐后代只读数据,通过$emit或调用祖先提供的方法来修改

//方法一:$emit比较繁琐,必须逐层中转 //方法二:祖先提供修改方法provide(){return{sharedState: this.state, updateCount:(val)=>{this.state.count=val}}}// 后代只调用方法,不直接改属性<button @click="updateCount(999)">调用方法修改</button>
  • vuex 允许跨组件或者夸页面共享状态
    state:数据仓库
    getters:计算属性,加工state
    mutations:修改state
    actions:异步操作
    modules:模块化
    原生调用
    this.store.state.XXXthis.store.state.XXX this.store.state.XXXthis.store.getters.XXX
    this.store.commit(′add′,1)//同步this.store.commit('add',1) // 同步 this.store.commit(add,1)//同步this.store.dispatch(‘asyncAdd’,‘2’) // 异步
    辅助函数:
    写在computed里面:mapState、mapGetters
    写在methods里面:mapActions、mapMutations、

Vue Router 的hash模式和history模式有什么区别?

hash模式:url中包含#符号,#后面的内容不会被发送给浏览器,前端通过hashChange事件监听路由变化,不需要服务器配置,兼容性好,但是url不够美观。
history模式:使用HTML5 History API(pushState和replaceState),url没有#,更灭管,但是需要服务器配置支持(所有路由都返回index.html),否则刷新页面会404

server{listen80;server_name your-domain.com;root /path/to/your/dist;# 指向你打包后的 dist 文件夹[reference:6]location /{# 核心配置:尝试按顺序匹配文件,都找不到则返回 index.html[reference:7]try_files$uri$uri/ /index.html;}}

Vue Router有哪些导航守卫?它们的执行顺序是怎么样的?

全局守卫
beforeEach:登陆,权限验证
beforeResolve:
afterEach:更改title,页面跳转后执行
路由独享守卫
beforeEnter
组件内守卫
beforeRouteEnter:进入组件之前触发,还没有this
beforeRouteUpdate:路由服用同一个组件触发,页面不刷新,只变参数用来监听路由的变化
beforeRouteLeave:清除定时器,离开提示保存,离开当前组件触发

Vue Router中route和route和routerouter的区别是什么?

$route是当前激活的路由信息对象,包含path、params、query、name、hash、fullpath、matched等属性,只读。
$router是VueRouter的实例,包含路由跳转方法如push、replace、go、back、forword

如何实现路由懒加载?它的原理是什么?

使用动态的import语法:const Foo = () => import('./Foo.vue),在路由配置中将compontent设为返回import()的函数。原理是webpack会将这些动态导入的模块进行代码分割。生成独立的chunk文件,只有在路由被访问时才会加载对应的chunk,减少首屏加载体积,提高性能。

vuex的核心概念有哪些?mutations和actions的区别是什么?

state存储数据
getters类似computd,派生状态
mutations同步修改state的唯一方式
actions处理异步操作,提交mutation而非直接修改state
modules将store分割成模块
mutations必须是同步函数,由commit触发
actions可以是异步的,由dispatch触发,内部通过commit调用mutation来修改state

vuex中如何实现模块化?namespaced有什么作用?

通过modules选项将store分割成多个模块,每个模块拥有自己的state、mutations、actions、getters。设置namespaced:true之后,模块内部的action、mutation和getter会注册在模块的命名空间下,调用时需加路径前缀(如store.dispatch('user/login))),避免命名冲突,使模块具有更高的封装性。

vuex的严格模式是什么?有什么作用?

通过 strict:true开启严格模式。在严格模式喜爱,任何由mutation以外的途径修改state都会抛出错误。这确保了state的所有修改都能被devtools追踪,形成清晰的状态变更记录。仅在开发环境开启,生产环境应该关闭(因为深度监测会带来性能开销)

vue2中keep-alive的作用和原理是什么?

用内存空间换取渲染性能
当keep-alive包裹的动态组件被切换时,它不会销毁当前组件,而是将组件的实例和对应的虚拟DOM节点存储在一个内存的cache对象里。当组件再次被激活时,keep-alive会直接从cache对象中取出之前缓存的实例和虚拟DOM节点,将其重新挂载到DOM上,这个过程跳过了组件正常的created、mounted等初始化生命周期,因此速度极快。
核心属性:
include:白名单
exclude:黑名单
max:最大缓存数量。当缓存的组件实例超过这个数字时,会根据LRU策略自动销毁最久未被访问的实例
被缓存的组件会触发actived和deactived生命周期钩子

vue2的虚拟DOM和diff算法原理是什么?

虚拟DOM是用JS对象描述真实DOM的轻量级副本
当数据发生变化时,vue生成新的虚拟DOM树,与旧的虚拟DOM树进行同层比较。
diff算法采用深度优先、同层对比策略。比较新旧节点是否为同一类型,不同则直接替换,相同则更新属性和子节点。对于子节点列表,采用双端比较(头头、尾尾、头尾、尾头)优化,减少移动操作,时间复杂度。

vue2中如何进行性能优化?

使用v-if代替v-show处理初始化不显示的内容
v-for绑定唯一的key
使用computed缓存计算结果
使用keep-alive缓存组件
路由懒加载
合理使用异步组件
避免v-for和v-if同时使用(v-for优先级更高)
事件销毁时移除自定义事件和定时器
图片懒加载
第三方库按需引入

vue2中如何实现自定义指令?有哪些钩子函数?

通过Vue.directive('name‘,options)全局注册或directives选项局部注册。
钩子函数:
bind:指令绑定到元素时
inserted:元素插入父节点时
update:组件更新时
componentUpdated:组件及子组件更新后
unbind:指令解绑时
每个钩子接收el、binding、vnode、oldVnode参数。
适用场景:DOM操作、集成第三方库、权限控制等

Mixin

定义混入(Mixin):创建一个独立的.js文件,导出(export)一个配置对象,这个对象的写法和普通Vue组件的选项(如data,methods,created等)完全一样

// mixin.jsexportconst myMixin={data(){return{mixinMessage:'Hello from Mixin!'}}, methods:{mixinSayHi(){console.log(this.mixinMessage);}},created(){console.log('Mixin 的 created 钩子触发了');}};

使用混入(Mixin):在组件中,通过mixins选项来引入,是一个数组

// MyComponent.vue<script>import{myMixin}from'./mixin.js';exportdefault{mixins:[myMixin], // 使用 mixins 数组data(){return{componentMessage:'Hello from Component!'}},created(){console.log('组件的 created 钩子触发了');this.mixinSayHi();// 直接调用混入中的方法}};</script>

当mixin和组件自身有同名选项时,Vue有一套明确的合并规则:
1、数据对象 (data):递归合并,如果发生属性名冲突,以组件的属性优先

// mixin 有 message:'hello',组件有 message:'goodbye'// 最终 this.message 的值是'goodbye'

2、生命周期钩子 (created, mounted 等):都会被调用。Mixin 中的钩子会先于组件自身的钩子执行
3、对象类型的选项 (methods, components, directives 等):合并为同一个对象。如果键名冲突,以组件的选项优先

vue2中nextTick的原理和用途是什么?

vue的DOM更新是异步的,数据变化后Vue不会立即更新DOM,而是将更新任务推入队列,在下一次事件循环tick中批量执行。nextTick接收一个回调函数,在DOM更新完成后执行。

vue2直接通过索引修改数组不会直接触发视图更新?如何解决?

vue2使用Object.defineProperty劫持数据。
解决方案:
1、Vue.set(array,index,value)或者this.$set(array,index,value)
2、arr.splice(index,1,value)
3、重新赋值为新数组。Vue重写了数组的7个变异方法(push、pop、shift、unshift、splice、sort、reverse),使用这些方法可以重新触发更新

子组件能否直接修改父组件传递的prop?为什么?如何实现‘双向绑定’?

不能,因为vue遵循单向数据流
方案:
1)使用.sync修饰符,子组件通过$emit(‘update:propName’,value)更新
2)使用v-model(本质是value prop+input事件的语法糖)
3)子组件emit事件通知父组件修改

Vue2组件中data为什么必须是函数?

组件是可复用的,如果data是对象,所有组件实例共享同一个data对象的引用,一个实例修改会影响其他实例。data 是一个函数,每次实例化组件时,Vue 会执行这个函数,并返回一个全新的对象。根实例中的data可以是对象,因为根实例只会创建一次。

在v-for循环的模版中如何处理复杂的逻辑?

不在模板中写复杂逻辑
更好的其他方式:
1、使用 computed 属性预处理数据,返回计算后的结果
2、使用 methods 方法封装逻辑
适用场景:简单的数据格式化(如时间戳转日期、数字转货币、状态码转文字),且不需要缓存。
注意:方法在每次重新渲染时都会执行,所以禁止在里面做复杂循环或重度运算。
3、当需要根据条件过滤和排序时,创建过滤后的 computed 列表,避免在 v-for 中嵌套 v-if。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询