前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >vue面试题总结

vue面试题总结

作者头像
andyhu
发布2023-06-27 14:30:31
2450
发布2023-06-27 14:30:31
举报
文章被收录于专栏:andyhu-大前端andyhu-大前端

前言

前两篇文章记录了一些我遇到的js的面试题,这篇文章来介绍vue的一些常见面试题。

vue

1. MVVM(Model-View-ViewModel) 模式和MVC 模式分别是什么?有什么区别?

  • MVC: 所有通信都是单向的
    • 视图(View):用户界面。(传送指令到 Controller)
    • 控制器(Controller):业务逻辑(完成业务逻辑后,要求 Model 改变状态)
    • 模型(Model):数据保存(将新的数据发送到 View,用户得到反)
    image
    image
  • MVVM:前后端分离:各部分之间的通信,都是双向的,View 与 Model 不发生联系,都通过ViewModel传递
image
image

ViewModel把view和model关联起来,ViewModel负责把Model的数据同步到view显出来,还负责吧view修改同步到Model。 答案详情

2. 【重点】vue的底层原理的实现(双向数据绑定原理的实现)?

Vue是一个典型的MVVM框架,模型(Model)只是普通的JavaScript对象,修改它则视图(View)会自动更新。这种设计让状态管理变得非常简单而直观。那么Vue是如何把模型和视图建立起关联的呢?

image
image

vue通过三大模块来实现的

  1. ==Observe: 能对数据对象的所有属性进行监听,如有订阅可拿到最新值并通知订阅者==
  2. ==Compile:对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数==
  3. ==Watcher: 操作Observer和Compile的桥梁,能够订阅并收到每个属变得的通知,执行指令绑定的相应回调函数,从而更新视图==

==一句话总结vue底层逻辑:创建vue实例后,遍历data选项的数据,通过object.defineProperty为属性添加getter和setter对数据进行劫持,劫持数据时会为属性创建 dep 用来收集watcher,当数据更新时通过dep.notify()通知watcher,派发更新,并且触发compile中绑定的回调,渲染视图== ==长话短说:劫持数据,创建def通知watcher,触发回调,更新数据,渲染视图==

==一个属性对象多个dep 一个dep对象多个watcher 一个watcher对应多个dep dep和watcher是多对多关系 ==

答案详情1 答案详情2

【重点】v-model和v-text的区别?

  • v-model通常用于表单组件的绑定,表单组件的双向绑定
  • v-text用于操作纯文本,单向绑定,数据变化->插值跟着变化,但插值变化不会影响数据对象的值

3. 【重点】Vue的生命周期方法有哪些?一般在哪一步发送请求及原因?

==答题思路:什么时候执行?实例内部发生了什么变化?能进行什么业务操作?==

  • beforeCreate 在实例初始化之后,数据观测(data observe)和watcher配置之前被调用;在此可加载loading事件
  • create 实例已经创建完成之后被调用。在这一步实例已经完成数据观测(data observe)和watcher事件回调,但实例还未挂载到DOM上;可在此结束beforeCreate中的loading事件
  • beforeMount 在挂载开始之前被调用:找到对应的template模板,并编译成render函数。
  • mounted 实例挂载到DOM上了(可以通过DOM api回去到节点,ref属性可以访问了)
  • beforeUpdate 虚拟DOM重新渲染和打补丁之前调用
  • updated 虚拟 DOM 重新渲染和打补丁之后调用,组件DOM已经更新,可执行依赖于DOM的操作
  • beforeDestroy 实例销毁之前调用,这一步实例仍然完全可用。
  • destroyed 实例销毁之后调用,调用后实例绑定的所有东西都会解绑,所有事件监听会被销毁,所有的子实例也会被销毁
  • keep-alive (activated 和 deactivated)
download.png
download.png

4. 【重点】谈谈对vue组件化的理解

  • 高内聚低耦合,单向流数据
  • 提高开发效率,和复用性
  • 降低更新范畴,只重新渲染变化的组件,可以提高性能 比如说当某个组件的数据改变时,它只会重新渲染数据改变的那个组件的dom,不会重现渲染整个的Dom

5. 【重点】v-for为什么要加key,能用index作为key吗?

  • 可以复用dom节点,提升性能
  • 用index作为key和不加key是一样的,都采用“就地复用”的策略
  • diff算法默认使用 “就地复用”的策略
  • “就地复用”原则只适用于不依赖子组件状态或临时dom状态(例如:表单输入)

6. ==【重点】vue组件之间的通信==?

怎么回答?什么场景用什么方法?项目中有哪些地方用到了?

按照使用场景可以分为三类:

  • 父子组件通信: props; parent / children; provide / inject ; ref ; attrs / listeners
  • 兄弟组件通信: eventBus ; vuex
  • 跨级通信: eventBus;Vuex;provide / inject 、attrs / listeners

方法:

  • 方法一:props/$emit
    • 父传子:子组件通过props访问父组件的值
    • 子传父:子组件通过$emit自定义事件向父组件发送数据
  • 方法二、三:parent/children与ref

parent/children与ref这两种都是直接得到组件实例,使用后可以直接调用组件的方法或访问数据

但这两种方法无法在跨级或兄弟间通信,不过可以借助vuex来实现

  • 方法三:attrs 和 listeners A->B->C。Vue 2.4 开始提供了 attrs 和 listeners 来解 决这个问题
  • 方法四:通过 emit和on实现eventBus进行数据传递
  • 方法五:父组件中通过 provide 来提供变量,然后在子组件中通过 inject 来注入变量。
  • 方法六:通过vuex状态管理实现组件之间的通信
  • 方法七:甚至可通过 localStorage/sessionStorage

7. 【重点】对template模板编译的理解

问题核心:如何将template转换成render函数 ?

  1. 将template模板转换成 ast 语法树 - parserHTML
  2. 对ast语法树做标记静态节点,优化ast - markUp
  3. 将ast语法树转化为可执行的代码render - codeGen

简而言之,就是先将template转化成AST树,再将ast树转化成render函数

代码语言:javascript
复制
export const createCompiler = createCompilerCreator(function
  baseCompile(
    template: string,
    options: CompilerOptions
  ): CompiledResult {
  const ast = parse(template.trim(), options) // 1.解析ast语法树
  if (options.optimize !== false) {
    optimize(ast, options)           // 2.对ast树进行标记,标记
    静态节点
  }
  const code = generate(ast, options)     // 3.生成代码
  return {
    ast,
    render: code.render,
    staticRenderFns: code.staticRenderFns
  }
})

8. 【重点】computed的实现原理

每个computed计算属性都会对应一个 Watcher实例,通过watcher来实现的

9. 【重点】computed和watch的区别?

  • computed和watch都是基于watcher来实现的
  • 如果一个数据依赖与其他数据或者说受其他数据影响,就用computed,且computed有缓存,依赖不变computed不会进行重新计算操作,可减少开销,提高性能 可以举例服务号项目里面循环展示开门记录时,在计算属性中对时间进行格式化处理
  • 如果需要在某个数据变化时做一些事情,就使用watch,执行异步操作 比如服务号申请预约的页面,通过watch监听房间号的切换来显示对应房间的预约记录

10. 【理解即可】Vue.set()方法是如何实现的?

  • 给对象和数组本身都==添加的def属性==
  • 当给==对象新增属性==的时候,会==触发==依赖==watcher==去更新对象
  • 当==改变数组==的==索引==时,会==重写==数组==splice()方法==更新数组
代码语言:javascript
复制
    export function set(target: Array<any> | Object, key: any, val:
    any): any {
    // 1.是开发环境 target 没定义或者是基础类型则报错
    if (process.env.NODE\_ENV !== 'production' &amp;&amp;
    (isUndef(target) || isPrimitive(target))
    ) {
    warn(`Cannot set reactive property on undefined, null, or
      primitive value: ${(target: any)}`)
    }
    // 2.如果是数组 Vue.set(array,1,100); 调用我们重写的splice方法 (这样可以
    更新视图)
    if (Array.isArray(target) &amp;&amp; isValidArrayIndex(key)) {
    target.length = Math.max(target.length, key)
    target.splice(key, 1, val)
    return val
    }

11. 【重点】Vue组件data为什么必须是个函数?

每次使用组件时都会为组件进行实例化操作,并且调用data函数返回一个对象作为组件的数据源这样可以保证多个组件间数据互不影响

12. 【重点】nextTick在哪里使用?原理是?

  • 可用用获取更新后的Dom
  • Vue中数据更新是异步的,可以保证nextTick里面的回调函数在Dom重新渲染之后执行

使用场景例子

13. ==【重点】$nextTick的原理是什么==?

每次event loop的最后,会有一个UI render步骤,也就是更新DOM

==原理:在dom更新之后的下一次的event loop 事件循环中执行nexttick里面的回调函数的异步任务,vue会采用Promise.then()、MutationObserve、setTimeout来执行nexttickhandler,依次测试浏览器支持的方法,如果前两个都不支持就用最后一个setTimeout来执行。==

  • Vue.nextTick([callback, context])是全局的,
  • 使用vm.$nextTick([callback])时的回调会自动绑定到调用它的实例上。 return function queueNextTick (cb, ctx) { var _resolve; callbacks.push(function () { //看这里,其实是可以给cb指定一个对象环境,来改变cb中this的指向 if (cb) { cb.call(ctx); } if (_resolve) { _resolve(ctx); } }); if (!pending) { pending = true; timerFunc(); } if (!cb && typeof Promise !== 'undefined') { return new Promise(function (resolve) { _resolve = resolve; }) } }

源码中使用的是if (cb) { cb.call(ctx) },用.call()改变this指向, 所以不能使用箭头函数,箭头函数的this是固定的,是不可用apply,call,bind来改变的。

参考链接1 参考链接2 参考链接3


Vuex

13. vuex的工作流程

组件应用state 组件调用dispatch发布到action里面,在action里面可以进行异步网络请求,然后commit提交到mutation里面,在mutation里面修改state

需要注意的是:网络请求一般在action里面进行,不要在action里面修改state,因为devtool只能跟踪mutations里面的修改记录

答案详情可以参考我写的另一篇文字 思维导图

14. 【重点】vuex的5大核心

分别是state、getters、mutations、actions、modules

state是状态、

getters:相当于vue中的计算属性computed

代码语言:javascript
复制
getters: {
    total: state => {
        return state.price * state.number
    },
    discountTotal: (state, getters) => {
        return state.discount * getters.total
    }
},

mutations:改变Vuex中的状态的唯一途径就是显式地提交 (commit) mutation。

actions:进行异步操作,比如网络请求

modules:store对象变得臃肿就需要分模块

15. 【重点】为什么要在computed获取vuex中的状态state,而不在data中?

因为data中的内容只会在create钩子触发前初始化一次。具体来说就是data中设置count: this.store.state.count 则count的值是created钩子执行前this.store.state.count的值,赋值之后属性的值就是纯粹的字面量,之后this.

代码语言:javascript
复制
var b = 1;
var a = b;
b = 2;

答案地址


Vue-Router

Vue-Route思维导图 问题地址 参考问题地址

16.【重点】切换路由时,需要保存草稿的功能,怎么实现呢?

可以根据使用方法结合实际项目说

代码语言:javascript
复制
<keep-alive :include="include">
    <router-view></router-view>
</keep-alive>

其中include可以是个数组,数组内容为路由的name选项的值。

17.【重点】怎么配置404页面?

给 path 配置通配符,然后用 redirect 重定向。示例代码:

代码语言:javascript
复制
const router = new VueRouter({
    routes: [
        {
            path: '*', redirect: {path: '/'}
        }
    ]
})

18.【重要】在什么场景下会用到嵌套路由?(结合项目)

例如我做的这个后台管理系统,顶部栏和左侧菜单栏是全局通用的,把它当作父路由,右下侧的页面的内容部分放在子路由里

19.【重点】什么是命名视图<router-view name=""></router-view,举个例子说明一下?(结合项目说)

例如在我这个后台管理系统对的项目中,我们想同级展示多个视图,而不是嵌套展示。例如项目首页,有头部导航,侧边栏导航、主内容区域。头部导航、侧边栏导航==我们不想用组件方式引入==,==想用<router-view name=""></router-view>视图方式展示==。那么这个首页上,就有三个视图,头部导航视图,侧边栏导航视图、主内容区域视图同级展示。

20.【重点】Vue-Router有哪些组件?

  • 导航组件<router-link to="" tag="" active-class=""></router-link>
  • 渲染路径匹配到的视图组件<router-view></router-view>
  • vue的内置组件<keep-alive include="" exclude=""></keep-alive>

21.【重点】active-class是哪个组件的属性?

<router-link/>组件的属性,设置链接激活时使用的 CSS 类名。默认值可以通过路由的构造选项 linkActiveClass 来全局配置。

22.【重点】怎么定义Vue-Router的动态路由?怎么获取传过来的值?

以冒号加参数的形式 path: 'user/:id'

在组件内通过路由对象route访问,例如 this.$route.params.id

23.【重点】Vue-Router有哪几种导航钩子(Vue-Router有哪些导航守卫方法)?

  • 全局的 beforeEach
  • 全局的 beforeResolve 导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后被调用
  • 全局的 afterEach
  • 组件内的 beforeRouterEnter
  • 组件内的 beforeRouterUpdate
  • 组件内的 beforeRouterLeave

24.【了解即可】Vue-Router完整导航解析流程是什么?

离开的组件的相关方法->全局beforeEach守卫->重用的组件的相关方法->路由配置里面的beforeEnter->(解析路由)调用被激活的组件的相关方法(beforeRouteEnter)->全局的beforeResolve->导航被确认->afterEach钩子->触发dom更新->调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。

  • beforeRouterLeave
  • beforeEach
  • beforeRouterUpdate
  • beforeEnter
  • beforeRouterEnter
  • beforeResole 在导航被确认之前,同时所有组件内守卫和异步路由组件被解析之后
  • 导航被确认
  • afterEach
  • 触发 DOM 更新
  • beforeRouterEnter中传给next的回调函数,创建好的组件实例会作为回调函数的参数传入。

25.【重点】$route和$router的区别是什么?

  • router是VueRouter实例,是一个全局路由对象,通过它可以调用路由跳转方法来跳转页面、钩子函数等等;
  • route是当前路由信息的对象,它是局部对象,每个路由都会有一个route对象,它里面包是当前路由对象信息,有path、params、query、name等信息。

26.【重点】Vue-Router相应路由参数的变化

  • 通过watch检测
  • 通过组件内导航钩子

27.【重点】Vue-Router传参方式

需要知道:分别由哪几种方式?有什么区别?

  • 通过 Params
    • 只能使用 name 不能使用 path 路径
    • 参数不会显示在路径上
    • 浏览器强制刷新,参数会被清楚
代码语言:javascript
复制
this.$router.push{
    name: Home,
    params: {
        number: 1,
        code: 99
    }
}
  • 通过 query
    • 可以使用 name 或者 path 路径
    • 参数会显示在路径上,浏览器刷新不会被清空

28.【重点】Vue-Router的两种模式

  • hash模式:
    • 原理是onhashchange事件,hashchange 只能改变 # 后面的代码片段。可以在window对象上监听这个事件
代码语言:javascript
复制
window.onhashchange = function(event){
  console.log(event.oldURL, event.newURL)
  let hash = location.hash.slice(1)
}
  • history模式
    • 利用了HTML5 中新增的pushState()replaceState()方法
    • 需要后台配置支持。如果刷新时,服务器没有响应的资源,会刷出404,
  • abstract
    • 支持所有 JavaScript 运行环境

如果 vue-router 使用 history 模式,部署时需要注意什么?

由于Vue项目是SPA应用(即单页面应用),nginx在跳转时会优先根据你请求的路径去寻找该路径下的index.html页面,而vue应用只有一个index.html文件放在项目根目录,所以要在 Nginx中将所有请求都转发到index.html上就可以了。

代码语言:javascript
复制
location / {
    ...
    try_files $uri $uri/ /test/dist/index.html;
    ...
}

这句话的意思是,当路径变化后,nginx会优先去寻找地址栏上最后一个uri的index.html文件,如果找不到,就会返回指定的html文件(这里就是返回/test/dist/index.html)。

29.【重点】Vue-Router实现路由懒加载(动态加载)

应用场景参考

把导入路由写成方法的形式,然后在配置路由映射的时候把component对应导入路由的方法,当路由被访问时才执行导入路由的方法

例子:

代码语言:javascript
复制
const router = new VueRouter({
  routes: [
    {
      path: '/home',
      name: 'Home',
      component:() => import('../views/home')
        }
  ]
})

30.【重点】路由之间是怎么跳转的?有哪些方式?

  • 通过使用内置组件:<router-link :to="/home">来跳转
  • 通过调用router实例的push方法或者replace方法

31.【重点】路由跳转方法 push 和 repalce 的区别?

  • 使用replace不会向history添加新记录,而是替换当天页面的history记录

32.【重点】后台管理系统项目中怎么获取菜单栏的?菜单栏的路由地址怎么实现的?

1.后台同学返回一个json格式的路由表,我用easymock造了一段:动态路由表,大家可参考; 2.因为后端同学传回来的都是字符串格式的,但是前端这里需要的是一个组件对象啊,写个方法遍历一下,将字符串转换为组件对象; 3.利用vue-router的beforeEach、addRoutes、localStorage来配合上边两步实现效果; 4.左侧菜单栏根据拿到转换好的路由列表进行展示; 大体步骤:**==拦截路由->后台取到路由->保存路由到localStorage(用户登录进来只会从后台取一次,其余都从本地取,所以用户,只有退出在登录路由才会更新)==**

参考答案

33. 【重要】能在vue-router钩子beforeRouteEnter函数里面获取组件实例this吗?怎么解决?

不能,因为当钩子执行前,组件实例还没被创建; 通过 next(vm => { // 通过 vm 访问组件实例 })来访问 vue 组件实例

34. 【重要】谈谈vue2和vue3的实现原理上的区别(vue3有哪些有点?)

  • vue3中采用了composition Api
  • vue2 用 es6 的是 Object.defineProperty 监听对象 ;vue3采用 proxy 代理 监听对象,
  • vue3 TypeScript 有了很好的支持

Object.defineProperty 的缺点:

  • 监听不到数组的变化,vue2是通过重写数组的push、pop、shift、unshift、splice、sort、reverse方法来实现数组的监听
  • 必须遍历对象的每个属性(Object.defineProperty多数要配合Object.keys使用)
  • 必须深层遍历嵌套的对象

proxy 的优点:

  • 针对对象而不是某个属性,省略了遍历每个属性的过程,提高了性能
  • 支持对象嵌套:get里面递归调用proxy并返回

35. vue2 和 vue3 在使用上有哪些区别?

  • vue2 中的 v-for 优先级高于 v-if
  • vue3 中的 v-if 优先级高于 v-for

详细区别参考地址:https://worktile.com/kb/ask/19553.html

【重要】单页面应用程序(SPA)的优缺点

定义:只在一个Web页面初始化时加载相应的HTML、js、css。避免了页面的重新加载。

  • 优点:
    • 单页面内容的改变不需要重新加载整个页面,可以通过ajax异步获取数据
    • 减轻服务器压力,后端不需要管模板渲染
  • 缺点:
    • 不利于SEO,SEO 本质是一个服务器向另一个服务器发起请求,解析请求内容

写在最后

我是 AndyHu,目前暂时是一枚前端搬砖工程师。

文中如有错误,欢迎在评论区指正,如果这篇文章帮到了你,欢迎点赞和关注呀😊

未经许可禁止转载💌

speak less,do more.

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2023-06-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • vue
    • 1. MVVM(Model-View-ViewModel) 模式和MVC 模式分别是什么?有什么区别?
      • 2. 【重点】vue的底层原理的实现(双向数据绑定原理的实现)?
      • 【重点】v-model和v-text的区别?
      • 3. 【重点】Vue的生命周期方法有哪些?一般在哪一步发送请求及原因?
      • 4. 【重点】谈谈对vue组件化的理解
      • 5. 【重点】v-for为什么要加key,能用index作为key吗?
      • 6. ==【重点】vue组件之间的通信==?
      • 7. 【重点】对template模板编译的理解
      • 8. 【重点】computed的实现原理
      • 9. 【重点】computed和watch的区别?
      • 10. 【理解即可】Vue.set()方法是如何实现的?
      • 11. 【重点】Vue组件data为什么必须是个函数?
      • 12. 【重点】nextTick在哪里使用?原理是?
      • 13. ==【重点】$nextTick的原理是什么==?
      • 13. vuex的工作流程
      • 14. 【重点】vuex的5大核心
      • 15. 【重点】为什么要在computed获取vuex中的状态state,而不在data中?
      • 16.【重点】切换路由时,需要保存草稿的功能,怎么实现呢?
      • 17.【重点】怎么配置404页面?
      • 18.【重要】在什么场景下会用到嵌套路由?(结合项目)
      • 19.【重点】什么是命名视图<router-view name=""></router-view,举个例子说明一下?(结合项目说)
      • 20.【重点】Vue-Router有哪些组件?
      • 21.【重点】active-class是哪个组件的属性?
      • 22.【重点】怎么定义Vue-Router的动态路由?怎么获取传过来的值?
      • 23.【重点】Vue-Router有哪几种导航钩子(Vue-Router有哪些导航守卫方法)?
      • 24.【了解即可】Vue-Router完整导航解析流程是什么?
      • 25.【重点】$route和$router的区别是什么?
      • 26.【重点】Vue-Router相应路由参数的变化
      • 27.【重点】Vue-Router传参方式
      • 28.【重点】Vue-Router的两种模式
      • 如果 vue-router 使用 history 模式,部署时需要注意什么?
      • 29.【重点】Vue-Router实现路由懒加载(动态加载)
      • 30.【重点】路由之间是怎么跳转的?有哪些方式?
      • 31.【重点】路由跳转方法 push 和 repalce 的区别?
      • 32.【重点】后台管理系统项目中怎么获取菜单栏的?菜单栏的路由地址怎么实现的?
      • 33. 【重要】能在vue-router钩子beforeRouteEnter函数里面获取组件实例this吗?怎么解决?
      • 34. 【重要】谈谈vue2和vue3的实现原理上的区别(vue3有哪些有点?)
      • 35. vue2 和 vue3 在使用上有哪些区别?
      • 【重要】单页面应用程序(SPA)的优缺点
  • Vuex
  • Vue-Router
    • 写在最后
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档