Skip to content
On This Page

面试题[Vue生态系统]

1. Vue Router

1.1 hash 模式和 history 模式的区别

Vue Router 的 hash 模式和 history 模式主要区别在于 URL 的表现形式和实现方式。

  1. hash 模式:URL 中会出现 # 号,例如:http://example.com/#/home。hash 模式的原理是基于 window.location.hash 的变化,来实现更新视图而不重新加载页面。hash 模式的核心特性是利用了浏览器对哈希的支持,使得页面在哈希值变化时不会重新加载全部内容。
  2. history 模式:URL 是普通路径形式,例如:http://example.com/home。history 模式的原理是基于 HTML5 的 history.pushStatehistory.replaceState,通过这两个 API 来操作浏览器历史记录,改变 URL 同时不重新加载页面。所以使用 history 模式时,需要在服务端进行配置,让所有路由都指向同一个入口页面,否则会出现 404 错误

1.2 route 和 router 的区别

在 Vue Router 中,routerouter 是截然不同的两个概念。简单来说,route 是一个当前路由信息对象,而 routerVue Router 的实例

  1. route

    • route 是针对当前活跃路由的对象, 它包含了当前路由的信息,比如路径(path)、参数(params)、查询参数(query)、路由匹配对象(matched)等。

    • 举例:this.$route,你可以通过它来获取当前路由的信息,如 this.$route.path 获取当前路径。

  2. router

    • router 是 Vue Router 的实例,它包含控制路由的功能,比如跳转(navigations)、导航守卫(guards)等。
    • 举例:this.$router,你可以通过它来执行路由导航,如 this.$router.push('/home') 进行页面跳转。

1.3 params 和 query 的区别

在 Vue Router 中,paramsquery 是两种不同的传参方式:

  1. params:用于动态路径参数,需要在路由配置中定义。例如 /user/:id 中的 id 是一个动态参数,你可以通过 this.$route.params.id 来获取它。params 更加隐蔽,不直接暴露在 URL 中。
  2. query:用于查询参数,使用前不需要在路由配置中定义。它们会以键值对的形式出现在 URL 的问号之后。例如 /user?id=123,你可以通过 this.$route.query.id 来获取 id 的值。query 更适合较为显性的参数传递和处理。

1.4 路由之间是如何跳转的

Vue Router 提供了两种基本的跳转方式:编程式导航和声明式导航。

  1. 编程式导航

    使用 this.$router.pushthis.$router.replace 方法进行路由跳转。其中 push 方法会在浏览器的历史记录中增加一条新记录,而 replace 方法则会替换当前的历史记录。

    javascript
    // 编程式导航实例
    this.$router.push('/home'); // 跳转到/home路径
    this.$router.replace('/login'); // 跳转到/login路径,替换当前的路径
  2. 声明式导航

    通过 <router-link> 组件进行跳转。这种方式在模板中使用,使代码更清晰直观。

    javascript
    <!-- 声明式导航实例 -->
    <router-link to="/home">Go to Home</router-link>

1.5 如何获取页面中的 hash 变化

在 Vue 项目中获取页面 hash 变化,主要取决于你是否使用了 Vue Router。

  1. 使用 Vue Router(最常用)

    • watch 监听 $route 对象

      javascript
      export default {
        watch: {
          '$route' (to, from) {
            // to 和 from 都是路由对象
            console.log('hash 变化了', to.fullPath, from.fullPath)
            // 获取当前 hash(不含 #)
            console.log('当前 hash:', to.hash)
          }
        }
      }
    • 监听具体的 $route.hash

      javascript
      watch: {
        '$route.hash': {
          handler(newHash, oldHash) {
            console.log('hash 从', oldHash, '变为', newHash)
          },
          immediate: true // 立即执行一次
        }
      }
    • 使用路由守卫(全局或组件内)

    • @vueuse/coreuseRouteHash(Composition API,Vue 3)

  2. 原生 JavaScript 监听(无 Vue Router)

    • 如果只是普通 Vue 项目,没有使用路由库,可以直接监听浏览器的 hashchange 事件。

1.6 切换路由时,如何保存草稿

实现草稿保存的核心是使用组件内守卫 beforeRouteLeave,在用户离开页面前触发保存逻辑。

1.7 切换路由后,保持原滚动位置

在 Vue Router 中控制路由切换时的滚动行为,主要通过 scrollBehavior 函数实现。它可以让你在切换路由后,指定目标页面的滚动位置(如顶部、保存的位置、或某个元素)。

javascript
import { createRouter, createWebHistory } from 'vue-router'

const router = createRouter({
  history: createWebHistory(),
  routes: [...],
  scrollBehavior(to, from, savedPosition) {
    // 如果存在 savedPosition(使用浏览器的前进/后退按钮时)
    if (savedPosition) {
      return savedPosition
    }
    // 否则滚动到顶部
    return { top: 0 }
  }
})

1.8 导航守卫

Vue Router 提供了三大类导航守卫,用于控制导航流程:

  1. 全局守卫
    • beforeEach:在任何路由跳转前触发,常用于全局权限验证、登录状态检查等
    • beforeResolve:在导航被确认前、所有组件内守卫和异步路由组件被解析后触发
    • afterEach:在导航完成后触发,不能改变导航,常用于分析、日志记录等
  2. 路由独享守卫
    • beforeEnter:直接在路由配置对象中定义,只在进入路由时触发,不会在 params、query 或 hash 改变时触发
  3. 组件内守卫
    • beforeRouteEnter:在渲染组件对应的路由被确认前调用,不能访问组件实例 this
    • beforeRouteUpdate:在当前路由改变但组件被复用时调用,可以访问组件实例 this
    • beforeRouteLeave:在导航离开组件对应路由时调用,可以访问组件实例 this

5. Vuex

5.1 Vuex 的属性

5.2 Vuex 的模块化

5.3 mutation 和 action 的区别

mutation 是用来改变状态的,而 action 是用来触发 mutation 的,可以包含异步操作。

5.4 mapstate 引起的问题

  • vuex 是单向数据流。
  • 双向绑定的是 data 里面的属性,更改 state,不触发 data 属性的 set,也就不触发依赖更新;computed 里面每次更改值,会重新走一遍依赖收集更新机制,所以没问题。
  • computed 就是一个 lazy 的 watch。

5. Pinia

5.1 Pinia 和 Vuex 的区别

Pinia 和 Vuex 是 Vue 中常用的状态管理工具,但它们有一些明显的区别:

  1. API设计和使用方式
    • Pinia 提供了更加简洁和现代化的 API,使用起来更加符合开发者的直觉,而 Vuex 的 API 相对复杂,需要定义多种概念,比如 state、mutations、actions 等。
    • Vuex::Vuex 的 API 设计是基于 Flux 模式,需要定义 state(状态)、mutations(同步修改状态的方法)、actions(异步操作)和 getters(类似于计算属性);两者之间的通信需要通过 commit 和 dispatch。
    • Pinia:Pinia 则更加灵活,可以直接修改 state,并且 actions 既可以是同步的,也可以是异步的,更符合现代 JavaScript 开发习惯。Pinia 还利用了 Vue3 的组合式 API,使得代码结构更加简洁。
  2. 模块化和状态可拆分性
    • Vuex 需要手动分模块并合并,而 Pinia 支持更自然的模块化,通过定义和使用不同的 store 实现状态的拆分和复用。
    • Vuex:在 Vuex 中,模块划分需要手动编写,store 对象会变得庞大,需要手动引入各个模块并合并。
    • Pinia:Pinia 的模块化更加自然,每个 store 相当于是一个模块,可以单独定义并导入到组件中使用,这样的设计符合现代模块化开发的思想。
  3. 开发体验
    • Pinia 具有更好的 TypeScript 支持,能提供更好的开发体验,尤其是在类型推导和 IDE 提示方面,而 Vuex 虽然也支持 TypeScript,但需要更多的手动配置。
    • TypeScript支持:Pinia 在设计之初就考虑了 TypeScript 的使用,类型推导非常自然;而 Vuex 支持 TypeScript 却需要更多的额外配置。
    • 调试工具:两者都支持 Vue DevTools,但由于 Pinia 遵循了 Vue 3 的新设计理念,所以在性能和调试效率上带来了优化。
  4. 插件生态:目前 Pinia 的插件生态还不如 Vuex 丰富。不过 Pinia 是 Vue 官方团队推荐的新一代状态管理工具,社区支持度和生态逐渐提升。