Vue 重点知识复习(三)

1 阅读6分钟

🎯 Vuex 核心概念与数据流

Vuex 是专为 Vue 开发的集中式状态管理库,它提供了一个集中式的状态管理机制,用于管理 Vue 应用中的所有组件的共享状态,适用于中大型 Vue 应用。

核心思想

将组件共享状态抽离为独立状态树,通过定义 getters、mutations、actions 来操作状态。

核心概念

概念作用特点
state应用的数据源唯一的状态树
getters对 state 数据进行计算/过滤类似于计算属性,缓存结果
mutations修改 state 的唯一方式同步操作,通过 commit 调用
actions处理异步或批量同步操作最终通过 mutations 改变 state

单向数据流

在 Vuex 中,数据流的流向是单向的,这种机制使得数据的流动更加清晰,同时也更容易进行调试和维护。

state → 组件 → actions/mutations → state

辅助函数

  • mapState: 映射 state 到组件计算属性
  • mapGetters: 映射 getters 到组件计算属性
  • mapActions: 映射 actions 到组件方法
  • mapMutations: 映射 mutations 到组件方法

高级特性

  • 模块化状态管理: 拆分大型状态树为多个模块
  • 插件机制: 扩展 Vuex 功能(如持久化存储)

这些高级特性能够进一步提高我们的开发效率。


🔄 Vue Router 核心原理

Vue Router 是 Vue 官方路由管理器,通过监听 URL 变化实现单页应用路由控制。

Vue Router 是实现 Vue 单页应用路由控制的核心组件之一,它通过路由匹配、路由模式、路由导航、路由组件等多个方面实现了完整的路由控制逻辑,为开发者提供了强大的路由控制能力。

四大核心原理

1. 路由匹配

  • 定义路由规则匹配 URL 路径
  • 支持路径、参数、查询参数
  • 支持嵌套路由和命名路由

2. 路由模式

模式实现方式特点
hash 模式监听 URL 的 hash 部分来进行路由控制兼容性好,无需后端配置
history 模式借助 HTML5 History API,通过修改浏览器历史记录来进行路由控制URL 更美观,需要后端配置

3. 路由导航

  • 导航钩子监听路由变化,实现路由拦截、身份验证等功能
  • 支持全局和组件内守卫,可以在路由跳转前、跳转后、路由更新等不同阶段执行相应的逻辑

4. 路由组件

  • 动态加载组件提升性能
  • 支持路由懒加载
  • 支持路由元信息

⚓ Vue Router 导航钩子全解析

导航钩子在路由解析流程中按顺序触发,构成完整的路由控制机制。

完整导航流程

  1. 导航被触发

当路由发生变化时,导航开始。

  1. 组件离开守卫 beforeRouteLeave:离开当前组件时调用

可以访问 this,可以用来进行页面数据的保存或弹出提示等操作,通常用于表单未保存提示。

beforeRouteLeave(to, from, next) {
  // 保存数据或提示用户
  next();
}
  1. 全局前置守卫 beforeEach:每次导航都会触发

在每次路由跳转之前执行,可以用来进行用户身份验证、路由拦截等操作。

router.beforeEach((to, from, next) => {
  // 身份验证、路由拦截
  next();
});
  1. 组件更新守卫 beforeRouteUpdate:路由改变但组件复用时调用

可访问 this,用于响应路由参数变化。

beforeRouteUpdate(to, from, next) {
  // 响应路由参数变化
  next();
}
  1. 路由独享守卫 beforeEnter:针对具体路由设置

在进入路由之前执行,与 beforeEach 的区别是它可以针对某个具体路由进行设置。

{
  path: '/user/:id',
  component: User,
  beforeEnter: (to, from, next) => {
    // 路由特定处理
    next();
  }
}
  1. 解析异步路由组件

  2. 组件进入守卫 beforeRouteEnter:进入目标组件前调用

不能访问 this(组件实例还未创建),但可以通过 next 的回调访问。

beforeRouteEnter(to, from, next) {
  next(vm => {
    // 通过回调访问组件实例
  });
}
  1. 全局解析守卫 beforeResolve:所有守卫和异步组件解析后执行

可以用于数据预取或权限的最终检查,或者确保异步组件加载完成。

// 权限最终检查
router.beforeResolve((to, from, next) => {
  if (to.meta.requiresAdmin) {
    return store.dispatch('checkAdminRights')
      .then(hasRights => {
        if (hasRights) next()
        else next('/unauthorized')
      })
  }
  next()
})

// 确保异步组件加载完成
router.beforeResolve((to, from, next) => {
  const matched = to.matched
  const preloadPromises = matched
    .map(record => record.components.default)
    .filter(component => typeof component === 'function')
    .map(asyncComponent => {
      return asyncComponent().catch(() => null)
    })
  
  Promise.all(preloadPromises)
    .then(() => next())
    .catch(error => {
      console.error('组件加载失败:', error)
      next('/error')
    })
})
  1. 导航被确认

  2. 全局后置钩子 afterEach:导航完成后调用

没有 next 函数,可以用来进行路由跳转后的操作,比如页面滚动、统计PV(Page View页面浏览量)等操作。

router.afterEach((to, from) => {
  // 页面滚动、PV 统计
});
  1. 触发 DOM 更新

  2. 执行 beforeRouteEnter 的 next 回调:可访问组件实例

重要规则

  • afterEach 外,每个守卫都必须调用 next() 才能继续往下导航
  • 守卫可以返回 false 取消导航,或重定向到其他路由

总结

这些导航钩子提供了灵活的路由跳转控制机制,可以方便地实现各种复杂的路由跳转需求。 Vue Router 还提供了一些其他的导航钩子和高级特性,比如路由元信息、动态路由、命名路由等,可以进一步提高开发效率和应用的可维护性。


🧭 Vue Router hash 模式实现锚点的四种方式

方式一:监听 hash 变化并手动滚动

// router/index.js
const router = new VueRouter({
  mode: 'hash',
  routes: [/* 路由配置 */]
})

// 设置目标 URL 的 hash 部分为锚点的名称
this.$router.push({ path: '/yourpath', hash: '#youranchor' })

// 目标组件
<script setup>
import { useRoute, onMounted } from 'vue-router'

const route = useRoute()

onMounted(() => {
  const anchor = document.getElementById(route.hash)
  if (anchor) {
    anchor.scrollIntoView()
  }
})
</script>

方式二:编程式导航

<template>
  <div>
    <button @click="scrollToSection('section1')">Go to Section 1</button>
    <button @click="scrollToSection('section2')">Go to Section 2</button>
    
    <div id="section1">Section 1 Content</div>
    <div id="section2">Section 2 Content</div>
  </div>
</template>

<script setup>
import { useRouter } from 'vue-router'

const router = useRouter()

const scrollToSection = (id) => {
  // 方法1:使用 router.push
  router.push({ hash: `#${id}` })

  // 方法2:直接修改 URL(不推荐,可能和 Vue Router 冲突)
  window.location.hash = '#' + id
}
</script>

方式三:使用原生锚点

<template>
  <div>
    <!-- 方式一:直接使用 a 标签 -->
    <a href="#section1">Go to Section 1</a>
    
    <!-- 方式二:Vue Router 链接添加 hash -->
    <router-link :to="{ path: $route.path, hash: '#section1' }">
      Go to Section 1
    </router-link>
  </div>
</template>

方式四:使用 Vue Router 的 scrollBehavior(推荐)

// router/index.js
import { createRouter, createWebHashHistory } from 'vue-router'

const router = createRouter({
  history: createWebHashHistory(),
  routes: [/* 路由配置 */],
  scrollBehavior(to, from, savedPosition) {
    // 在 Vue Router 4 中,hash 模式下的 scrollBehavior 默认不工作,需要手动实现
    if (to.hash) {
      return {
        el: to.hash,
        behavior: 'smooth', // 平滑滚动
        top: 20 // 偏移量
      }
    }
    return { top: 0 }
  }
})
<template>
  <div>
    <nav>
      <router-link to="#section1">Section 1</router-link>
      <router-link to="#section2">Section 2</router-link>
      <router-link to="#section3">Section 3</router-link>
    </nav>
    
    <div style="height: 100vh">首页内容</div>
    
    <section id="section1">
      <h2>Section 1</h2>
      <div>内容...</div>
    </section>
    
    <section id="section2">
      <h2>Section 2</h2>
      <div>内容...</div>
    </section>
    
    <section id="section3">
      <h2>Section 3</h2>
      <div>内容...</div>
    </section>
  </div>
</template>

🚀 Vue Router history 模式上线注意事项

Vue Router 的 history 模式相比于默认的 Hash 模式来说,能够更好地模拟传统的多页面应用的 URL 地址,让用户体验更加自然。

1. 后端配置

必须配置后端服务器,将所有路由请求指向入口文件(index.html),避免刷新 404。

2. 安全性

  • 暴露服务器文件路径,需仔细检查配置
  • 防止恶意请求导致安全问题

3. 兼容性

需要浏览器支持 HTML5 History API,旧浏览器可能存在兼容性问题。

4. 打包发布配置

在使用 Webpack 等工具打包发布时,需要配置正确的 publicPath,保证 HTML 中引用的资源路径正确。

Vite 配置(对应 Webpack 的 publicPath)

// vite.config.js
export default defineConfig({
  base: '/my-app/', // 部署基础路径
  plugins: [vue()]
})

多环境配置

// vite.config.js
export default defineConfig(({ mode }) => {
  const base = {
    development: '/',
    staging: '/staging/',
    production: '/my-app/'
  }[mode] || '/'
  
  return {
    base,
    plugins: [vue()],
  }
})

环境变量配置

# .env.production
VITE_APP_BASE_URL=/my-app/
// vite.config.js
export default defineConfig({
  base: process.env.VITE_APP_BASE_URL || '/',
})

与 Vue Router 配合

// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
const base = import.meta.env.BASE_URL || '/' // 动态获取 base 路径
const router = createRouter({
  history: createWebHistory(base),
  routes: [
    { path: '/', component: Home },
    { path: '/about', component: About },
    { path: '/user/:id', component: User }
    // 所有子路由都会自动基于这个 base
  ]
})

❌ history 模式刷新 404 问题解析

原因

浏览器刷新页面时向服务器发送 GET 请求,但服务器没有对应的资源文件,因为在 history 模式下,所有路由都是在前端路由中实现的。

解决方案

配置服务器将所有请求指向 index.html,由前端路由处理。

Nginx 配置示例

server {
    listen 80;
    server_name yourdomain.com;

    location / {
        root /usr/share/nginx/html;
        index index.html;
        try_files $uri $uri/ /index.html;  # 关键配置
    }
}

📝 总结

本文详细复习了 Vue 生态中两个核心工具:

  1. Vuex:集中式状态管理,通过 state、getters、mutations、actions 实现单向数据流
  2. Vue Router:路由管理核心原理,导航钩子全流程,hash/history 模式应用场景

重点掌握:

  • Vuex 的单向数据流和模块化使用
  • Vue Router 导航钩子的执行顺序和应用
  • hash/history 模式的区别和部署注意事项
  • history 模式刷新 404 问题的解决方案

这些知识是中高级 Vue 开发者必须掌握的核心内容,理解并熟练应用它们将极大提升你的 Vue 项目开发能力。