vue2面试题

发布时间:2026/8/12 18:38:42
vue2面试题 vue2生命周期勾子有哪些每个勾子作用是什么beforeCreatecreatedbeforeMountmountedbeforeUpdateupdatedbeforeDestroydestoryedactiveddeactivatedvue2中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监听没有缓存支持异步有两个参数immediatetrue立即执行deeptrue深度监听没有返回值。computed适用于多个值影响一个值watch适用于一个值影响多个值vue2的响应式原理是什么通过数据劫持结合发布者-订阅者模式的方式通过Object.defineProperty()来劫持各个属性的setter和getter在数据变动的时候发布消息给订阅者触发相应的监听回调。核心三大角色Observer劫持对象所有属性初始化的时候存在的属性给属性添加setter和getterdep依赖收集管理watcher数据变了通知更新watcher订阅者关联视图收到通知更新视图弊端只能劫持对象的已有属性无法感知属性的新增和删除。解决方案通过Vue提供的set和set和set和delete或者通过重写数组原型push、pop、shift、unshift、splice、sort、reversev-for中的key有什么作用为什么不能用index作为keykey是虚拟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、默认插槽一处占位父组件插入任意内容 子组件templatedivclasscardh3标题/h3!-- 定义默认插槽的占位 --slot/slot/div/template父组件templateChild!-- 这里的内容会自动填充到子组件的slot位置 --p这是插入的默认内容/p/Child/template2、具名插槽多个插槽按照名字对应 子组件!-- 子组件 Child.vue --在子组件中给slot标签添加 name 属性。如果不写 name默认名称是 default。template!-- 具名插槽头部 --slotnameheader/slot/template!-- 父组件 Child.vue --父组件必须在template标签上使用 v-slot 指令或简写#加上插槽名称templateChild!-- 填充 header 插槽 --template v-slot:headerh1这是头部标题/h1/template/Child/template3、作用域插槽!-- 子组件 --子组件暴漏数据在子组件中将需要传给父组件的数据以keyvalue的形式写在slot属性上。templateslot :aaaa:bbbb/slot/template!-- 父组件1 接收一个变量名--父组件接收时使用 v-slot简写#接收一个变量名或者直接解构。templateList!-- 用#default 接收slotProps 包含了子组件传过来的所有属性 --template#defaultslotPropsspan{{slotProps.a}}-{{slotProps.b}}/span/template/List/template!-- 父组件2 直接解构 --templateList!-- 解构取出 a 和 b 可以给个默认的值--template#default{ a, b 0 }span{{a}}.{{b}}/span/template/List/templateprovide/inject祖先/后代祖先通过provide提供变量后代通过inject来注入变量解决了跨层级组件间的通信问题主要是子组件/子孙组件获取上层组件状态provide直接传普通值不是响应式用函数包括后代通过computed接收实现响应式原因直接传值传的是常量不是引用地址!--祖先组件--// provide 写成函数返回一个包含响应式对象的引用templatedivh1祖先组件/h1p原始计数{{state.count}}/pbutton clickstate.count计数1/buttonbutton clickstate.user.firstName 张改姓/buttonChild //div/templatescriptimportChild from./Child.vueexportdefault{components:{Child},data(){return{// 将需要共享的数据放在 data 中保证响应式 state:{count:0, user:{firstName:李, lastName:四}}}}, // provide 写成函数返回一个包含响应式对象的引用provide(){return{// 直接传递 state 对象引用后代修改其属性会触发响应 sharedState: this.state}}}/script!--中间组件--templatedivh2中间组件/h2Grandchild //div/templatescriptimportGrandchild from./Grandchild.vueexportdefault{components:{Grandchild}}/script!--后代组件--使用 inject 注入祖先提供的 sharedState并用 computed 衍生出新数据。templatedivstyleborder:1px solid #ccc; padding:10px;h3后代组件/h3p原始计数{{sharedState.count}}/p!-- 使用 computed 衍生双倍计数 --p双倍计数{{doubleCount}}/pp原始姓名{{sharedState.user.firstName}}{{sharedState.user.lastName}}/p!-- 使用 computed 衍生全名 --p全名{{fullName}}/pbutton clicksharedState.count 2后代2/button/div/templatescriptexportdefault{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.countval}}}// 后代只调用方法不直接改属性button clickupdateCount(999)调用方法修改/buttonvuex 允许跨组件或者夸页面共享状态state数据仓库getters计算属性加工statemutations修改stateactions异步操作modules模块化原生调用this.store.state.XXXthis.store.state.XXX this.store.state.XXXthis.store.getters.XXXthis.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 APIpushState和replaceStateurl没有#更灭管但是需要服务器配置支持所有路由都返回index.html否则刷新页面会404server{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:登陆权限验证beforeResolveafterEach更改title页面跳转后执行路由独享守卫beforeEnter组件内守卫beforeRouteEnter进入组件之前触发还没有thisbeforeRouteUpdate路由服用同一个组件触发页面不刷新只变参数用来监听路由的变化beforeRouteLeave清除定时器离开提示保存离开当前组件触发Vue Router中route和route和route和router的区别是什么$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而非直接修改statemodules将store分割成模块mutations必须是同步函数由commit触发actions可以是异步的由dispatch触发内部通过commit调用mutation来修改statevuex中如何实现模块化namespaced有什么作用通过modules选项将store分割成多个模块每个模块拥有自己的state、mutations、actions、getters。设置namespacedtrue之后模块内部的action、mutation和getter会注册在模块的命名空间下调用时需加路径前缀如store.dispatch(user/login))避免命名冲突使模块具有更高的封装性。vuex的严格模式是什么有什么作用通过 stricttrue开启严格模式。在严格模式喜爱任何由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组件的选项如datamethodscreated等完全一样// mixin.jsexportconst myMixin{data(){return{mixinMessage:Hello from Mixin!}}, methods:{mixinSayHi(){console.log(this.mixinMessage);}},created(){console.log(Mixin 的 created 钩子触发了);}};使用混入Mixin在组件中通过mixins选项来引入是一个数组// MyComponent.vuescriptimport{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 的值是goodbye2、生命周期钩子 (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 propinput事件的语法糖3子组件emit事件通知父组件修改Vue2组件中data为什么必须是函数组件是可复用的如果data是对象所有组件实例共享同一个data对象的引用一个实例修改会影响其他实例。data 是一个函数每次实例化组件时Vue 会执行这个函数并返回一个全新的对象。根实例中的data可以是对象因为根实例只会创建一次。在v-for循环的模版中如何处理复杂的逻辑不在模板中写复杂逻辑更好的其他方式1、使用 computed 属性预处理数据返回计算后的结果2、使用 methods 方法封装逻辑适用场景简单的数据格式化如时间戳转日期、数字转货币、状态码转文字且不需要缓存。注意方法在每次重新渲染时都会执行所以禁止在里面做复杂循环或重度运算。3、当需要根据条件过滤和排序时创建过滤后的 computed 列表避免在 v-for 中嵌套 v-if。