Vue 生态三大核心库概览

Q: 掌握了 Vue3 基础之后,要构建一个完整的中大型应用,还需要哪些生态库?

A: Vue 生态中最核心的三个库分别是:前端路由库(Vue Router)状态管理库(Pinia)前端组件库(如 Element Plus)。这三个库覆盖了页面导航、全局状态共享、UI 快速搭建三大核心诉求,是任何一个 Vue3 项目的标配。

什么是前端路由

Q: 路由不是后端才有的概念吗?前端为什么也要有路由?

A: 这个问题的答案藏在 Web 开发模式的演变中。

多页应用时代的后端路由

在早期的多页应用(MPA)时代,路由完全属于后端。后端根据不同的请求路径返回不同的完整 HTML 页面。

多页应用路由示意

这个时期的两个显著特点:

  • 整个项目的前后端代码是杂糅在一起的。
  • 每次切换页面,都需要重新请求服务器,加载一个全新的 HTML。

单页应用时代的前端路由

随着 Ajax 技术的普及,应用进入了单页应用(SPA)时代。整个应用只有一个 HTML 页面,以前「整个页面切换」变成了「页面上某个模块的切换」。

单页应用路由示意

这个模块在 Vue 中就是页面级组件(通常放在 views 目录下)。页面级组件与 URL 路径之间需要建立映射关系——这就是前端路由的核心职责。

值得注意的是,有了前端路由后,后端路由并未消失,而是从「路由和页面的映射」变成了「路由和数据接口的映射」。

Vue Router 快速入门

Q: Vue Router 具体怎么用?有哪些核心概念?

A: Vue Router 是 Vue 官方推出的前端路由库,与 Vue3 深度集成。首先安装:

npm install vue-router@4

第一步:创建页面级组件

src/views/ 目录下创建页面组件,例如 Home.vueAbout.vue

第二步:配置路由

src/router/index.js 中定义路由映射:

import { createRouter, createWebHistory } from 'vue-router';
import Home from '../views/Home.vue';
import About from '../views/About.vue';

const router = createRouter({
  history: createWebHistory(), // 路由模式:history 或 hash
  routes: [
    { path: '/', name: 'Home', component: Home },
    { path: '/about', name: 'About', component: About },
  ],
});
export default router;

第三步:挂载路由实例

main.js 入口文件中挂载:

import router from '@/router';

const app = createApp(App);
app.use(router).mount('#app');

第四步:在组件中使用

Vue Router 提供了两个核心组件:

组件 作用
<router-link> 导航链接,to 属性指定目标路由路径
<router-view> 路由匹配的组件渲染位置(占位出口)
<template>
  <div id="app">
    <h1>欢迎来到 Vue Router 快速入门示例</h1>
    <nav>
      <router-link to="/">Home</router-link>
      <router-link to="/about">About</router-link>
    </nav>
    <router-view />
  </div>
</template>

理解要点

  • <router-link> 本质是 <a> 标签的封装,但避免了页面的全量刷新。
  • <router-view> 是动态组件渲染的出口,URL 变化时自动切换显示对应组件。

KeepAlive 内置组件

Q: 在页面间切换时,上一个页面的状态(如表单填写、滚动位置)会丢失,如何保留?

A: Vue 提供了 KeepAlive 内置组件来解决这个问题——它会缓存被包裹组件的状态,组件切换后不销毁,状态得以保留。

基础用法

配合 Vue Router 使用时,通过 router-view 的作用域插槽获取当前匹配的组件,再用 KeepAlive 包裹:

<router-view v-slot="{ Component }">
  <keep-alive>
    <component :is="Component" />
  </keep-alive>
</router-view>

其中 component 并非真正的组件,而是模板语法中的特殊元素(和 slottemplate 类似),用于动态渲染组件——具体渲染哪个组件由 is 属性决定。这些特殊元素在模板编译期间会被编译掉。

包含与排除

使用 include / exclude 属性来精细控制哪些组件需要缓存:


<KeepAlive include="a,b">
  <component :is="view" />
</KeepAlive>


<KeepAlive :include="/a|b/">
  <component :is="view" />
</KeepAlive>


<KeepAlive :include="['a', 'b']">
  <component :is="view" />
</KeepAlive>

实际场景中配合路由使用:

<router-view v-slot="{ Component }">
  <keep-alive include="Counter,Timer">
    <component :is="Component" />
  </keep-alive>
</router-view>

注意:使用逗号分隔字符串时,组件名之间不要加空格

最大缓存数

通过 max 属性限制缓存实例的数量:

<router-view v-slot="{ Component }">
  <keep-alive :max="3">
    <component :is="Component" />
  </keep-alive>
</router-view>

当缓存的实例数量即将超过指定最大值时,最久没有被访问的缓存实例将被销毁,为新的实例腾出空间。这实际上是 LRU(最近最少使用)缓存策略。

状态管理库 Pinia

Q: 小项目用 Props + Emit 传递数据就够了,为什么还需要引入状态管理库?

A: 当应用规模逐步变大,不同层级之间组件状态的传递会变得非常麻烦。兄弟组件、跨层级组件之间的数据共享,靠逐层传递 Props 和手动触发事件很快就会失控。

传统组件通信问题

状态管理库的解决思路是:建立一个统一的数据仓库,任何一个组件将状态提交到仓库,另一个组件直接从仓库获取最新状态,组件之间不再直接通信。

状态管理库解决方案

Pinia 简介

Pinia(发音 /piːnjʌ/,西班牙语「菠萝」之意)是 Vue 官方推荐的新一代状态管理库,用于替代 Vuex。由 Vue.js 核心团队成员开发,最初是作为 Vuex 5 的实验性探索,后来因实现了 Vuex 5 提案的大部分内容,直接取代了 Vuex。

Pinia logo

Pinia vs Vuex

特性 Vuex Pinia
mutations 必须通过 mutation 修改 state 不再有 mutations,只有 state、getters、actions
异步操作 只能在 action 中,mutation 必须同步 actions 中支持同步和异步
TypeScript 类型推断较差 可靠的类型推断支持
模块嵌套 支持模块嵌套 不再有模块嵌套,只有 Store,Store 间可互调
体积 较大 更轻量,压缩后仅 2KB 左右
插件扩展 有限 支持插件扩展,可方便实现本地存储等功能

Pinia 快速入门

安装:

npm install pinia

main.js 中挂载:

import { createApp } from 'vue';
import App from './App.vue';
import { createPinia } from 'pinia';

const app = createApp(App);
const pinia = createPinia();

app.use(pinia).mount('#app');

创建数据仓库(src/stores/useCounterStore.js):

import { defineStore } from 'pinia';

export const useCounterStore = defineStore('counter', {
  state: () => ({
    count: 0,
  }),
  actions: {
    increment() { this.count++; },
    decrement() { this.count--; },
  },
});

在组件中使用:

<template>
  <div class="counter">
    <h1>计数器:{{ counterStore.count }}</h1>
    <button @click="counterStore.increment">增加</button>
    <button @click="counterStore.decrement">减少</button>
  </div>
</template>

<script setup>
import { useCounterStore } from './stores/useCounterStore.js';
const counterStore = useCounterStore();
</script>

核心要点:

  • defineStore 接收仓库名称和配置对象两个参数。
  • 命名惯例使用 useXxxStore 的形式。
  • 组件中直接通过 store 实例访问 state 和调用 actions,无需 commit/dispatch。

Element Plus 组件库实战

Q: 在实际项目中,每个 UI 组件都从零手写效率太低。有没有现成的 Vue3 组件库可用?

A: Vue3 生态中有丰富的组件库可选。以下是几个主流选择:

组件库 特点 适用场景
Element Plus Element UI 的 Vue3 版本,企业级 后台管理系统
Vuetify 材料设计规范,组件丰富 快速开发各种应用
Ant Design Vue Ant Design 的 Vue 实现 企业级后台应用
Naive UI TypeScript 编写,轻量灵活,尤雨溪推荐 追求极简和类型安全
Vant 有赞出品的移动端组件库 移动端 H5 应用

安装与挂载

以 Element Plus 为例:

npm install element-plus --save

全量引入——简单直接,适合学习阶段:

import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css';

const app = createApp(App);
app.use(ElementPlus).mount('#app');

注册后即可在任意组件中使用:

<el-button>Default</el-button>
<el-button type="primary">Primary</el-button>
<el-button type="success">Success</el-button>

图标需要额外安装:

npm install @element-plus/icons-vue

全局注册图标:

import * as ElementPlusIconsVue from '@element-plus/icons-vue';

for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
  app.component(key, component);
}

按需引入(推荐生产使用)

全量引入会导致打包体积过大。推荐使用两个插件实现自动按需引入

npm install -D unplugin-vue-components unplugin-auto-import

vite.config.js 中配置:

import AutoImport from 'unplugin-auto-import/vite';
import Components from 'unplugin-vue-components/vite';
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers';

export default defineConfig({
  plugins: [
    AutoImport({
      resolvers: [ElementPlusResolver()],
    }),
    Components({
      resolvers: [ElementPlusResolver()],
    }),
  ],
});

这两个插件的作用:

  • unplugin-vue-components:自动扫描项目中使用的 Vue 组件,打包时自动按需引入,无需手动 import。
  • unplugin-auto-import:自动引入 Vue 相关的工具函数(如 ref、computed),同样无需手动 import。

配置后 main.js 变得非常干净:

import { createApp } from 'vue';
import App from './App.vue';

const app = createApp(App);
app.mount('#app');

Element Plus 表单实战

以下是使用 Element Plus 构建用户信息表单的完整示例:

<template>
  <div class="form-container">
    <el-form :model="form" :rules="rules" ref="formRef" label-width="100px">
      <h2 class="form-title">用户信息表单</h2>
      <el-form-item label="姓名:" prop="name">
        <el-input v-model="form.name" />
      </el-form-item>
      <el-form-item label="邮箱:" prop="email">
        <el-input v-model="form.email" />
      </el-form-item>
      <el-form-item label="出生日期:" prop="birthday">
        <el-date-picker
          v-model="form.birthday"
          type="date"
          placeholder="选择日期"
          style="width: 100%"
        />
      </el-form-item>
      <el-form-item label="性别:" prop="gender">
        <el-radio-group v-model="form.gender">
          <el-radio value="male"></el-radio>
          <el-radio value="female"></el-radio>
        </el-radio-group>
      </el-form-item>
      <el-form-item label="兴趣爱好:" prop="hobbies">
        <el-checkbox-group v-model="form.hobbies">
          <el-checkbox value="reading">阅读</el-checkbox>
          <el-checkbox value="music">音乐</el-checkbox>
          <el-checkbox value="sports">运动</el-checkbox>
        </el-checkbox-group>
      </el-form-item>
      <el-form-item label="接收通知:" prop="notifications">
        <el-switch v-model="form.notifications" />
      </el-form-item>
      <el-form-item label="用户评级:" prop="rating">
        <el-rate v-model="form.rating" />
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="submitForm">提交</el-button>
        <el-button @click="resetForm">重置</el-button>
      </el-form-item>
    </el-form>
  </div>
</template>

<script setup>
import { ref } from 'vue';

const form = ref({
  name: '', email: '', birthday: '',
  gender: 'male', hobbies: [],
  notifications: false, rating: 0,
});

const formRef = ref(null);

const rules = {
  name: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
  email: [
    { required: true, message: '请输入您的邮箱', trigger: 'blur' },
    { type: 'email', message: '请输入有效的邮箱地址', trigger: 'blur' },
  ],
  birthday: [{ required: true, message: '请选择您的出生日期', trigger: 'change' }],
  gender: [{ required: true, message: '请选择您的性别', trigger: 'change' }],
  hobbies: [{ required: true, message: '至少选择一个兴趣爱好', trigger: 'change' }],
  rating: [{ required: true, message: '请评价用户等级', trigger: 'change' }],
};

function submitForm() {
  formRef.value.validate((valid) => {
    if (valid) {
      console.log('表单验证通过');
    } else {
      console.log('表单验证不通过');
    }
  });
}
</script>

国际化配置

Element Plus 默认使用英语文本。切换中文:

全量引入时:

import zhCn from 'element-plus/es/locale/lang/zh-cn';

app.use(ElementPlus, { locale: zhCn }).mount('#app');

按需引入时,使用 ElConfigProvider 组件:

<template>
  <ElConfigProvider :locale="locale">
    
  </ElConfigProvider>
</template>

<script setup>
import { ElConfigProvider } from 'element-plus';
import zhCn from 'element-plus/es/locale/lang/zh-cn';
const locale = ref(zhCn);
</script>

总结

本文覆盖了 Vue3 生态的三大核心支柱:

生态库 解决的问题 核心概念
Vue Router 页面导航与 URL 映射 router-link、router-view、history/hash 模式
Pinia 跨组件全局状态共享 Store、state、getters、actions(无 mutations)
Element Plus 快速搭建 UI 界面 组件库、全量/按需引入、国际化

最佳实践建议:

  1. 路由组织:将页面级组件统一放入 views/ 目录,路由配置放在 router/ 下,结构清晰。
  2. 按需使用 KeepAlive:只对需要保留状态的页面(如表单页、列表页)开启缓存,不要对全部页面缓存。
  3. Pinia Store 拆分:按业务领域拆分多个 Store(如 userStore、cartStore),避免单一 Store 过于臃肿。
  4. 生产环境按需引入:项目上线时,使用 unplugin-vue-components + unplugin-auto-import 实现按需引入,大幅减小打包体积。