Code前端首页关于Code前端联系我们

Vue3+Vue-router4从0到1完整搭建怎么做?新手老手都该避开的坑有哪些?

terry 5小时前 阅读数 80 #Vue

最近好多朋友问我这个问题——要么是刚从原生H5、小程序转过来想入门前端全栈,把Vue3+TS+路由作为第一套技术栈;要么是还在用Vue2+Vue-router3,团队要升级但自己没摸透版本差,怕改崩项目;还有几个做了半年Vue3的,居然还在用keep-alive老写法,或者路由传参总莫名其妙失效,其实这俩核心工具的搭配,核心逻辑没变,但细节调整真不少,从路由实例创建、嵌套路由写法、传参方式、导航守卫,到和Composition API的结合,每一步都有新东西,也每一步都有容易踩的雷,今天我就从0开始,用纯日常的思路聊怎么搭,顺便把收集到的10个高频坑一次性说清楚,最后再补个适合日常开发的进阶小技巧,比如权限控制的简化写法、动态路由的懒加载优化,应该能覆盖大部分人的需求。

先搭Vue3项目框架

别着急装Vue-router,第一步得先有个干净的Vue3项目环境,不管你用Vue CLI还是Vite,现在主流肯定是Vite了,毕竟启动速度快到飞起,热更新延迟几乎为0,写代码爽很多,我用Vite举例子,命令很简单,打开终端(Windows用PowerShell或者CMD管理员模式,Mac/Linux直接Terminal就行):

先全局安装create-vite@latest,不过其实现在npx更方便,不用全局占空间: npm create vite@latest my-vue3-router-app -- --template vue-ts cd my-vue3-router-app npm install 这三条命令敲完,一个带TypeScript的基础Vue3项目就跑起来了,等下看效果可以敲npm run dev,但先别急,先把路由装了。

正确安装并引入Vue-router4

这里第一个小细节新手经常错:Vue-router3只兼容Vue2,Vue-router4只兼容Vue3,千万不要装错版本!而且Vite创建的项目默认package.json里的依赖没有路由,要手动装,终端里接着敲: npm install vue-router@4

装完之后,怎么引入?这和Vue2完全不一样了,不是直接new Vue({ router })就行,而是要先创建路由实例,再在main.ts里用app.use()挂载,具体步骤:

在src目录下创建router文件夹

新建router的文件夹,里面放index.ts——这是约定俗成的路由入口文件,方便项目管理,同事接手一看就懂。

配置路由入口文件index.ts

这是搭建的核心环节,得一步一步来,首先引入Vue-router的核心API,然后定义路由规则数组,最后创建路由实例并导出。 先看最基础的代码结构,里面注释我写得很细,方便理解:

// 引入createRouter和createWebHistory这两个核心API,Hash模式用createWebHashHistory
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
// 引入路由组件,这里可以先随便建两个测试组件,比如Home.vue和About.vue
// 测试组件怎么建?后面讲,先继续写路由
// 引入方式有两种:同步引入(适合常用的首页等组件,减少首屏加载后切换的延迟)和懒加载(适合用户可能不常用的页面,比如设置、个人中心,减少首屏加载体积)
// 同步引入:
// import Home from '../views/Home.vue'
// 懒引入(推荐,用箭头函数,配合Webpack/Vite的代码分割):
const Home = () => import('../views/Home.vue')
const About = () => import('../views/About.vue')
const User = () => import('../views/User.vue') // 后面讲嵌套路由和传参会用到
// 定义路由规则数组,每个对象就是一条路由记录,用RouteRecordRaw类型约束,避免写错属性
const routes: Array<RouteRecordRaw> = [
  {
    path: '/', // 路由路径,必须以/开头
    name: 'home', // 路由名称,可选但强烈建议加,用name跳转更安全,改path不会影响跳转代码
    component: Home, // 对应的组件
    meta: { // meta是路由元信息,超级好用,后面讲权限控制、keep-alive会用到
      title: '首页',
      keepAlive: true, // 标记是否需要缓存组件
      requiresAuth: false // 标记是否需要登录才能访问
    }
  },
  {
    path: '/about',
    name: 'about',
    component: About,
    meta: {
      title: '关于我们',
      keepAlive: false,
      requiresAuth: false
    }
  }
]
// 创建路由实例
const router = createRouter({
  // history模式:URL没有#,更美观,但部署到服务器时需要配置Nginx/Apache,否则刷新页面会404
  // hash模式:URL有#,兼容性好,不用配置服务器,但看起来有点旧,适合不想折腾部署的小项目
  history: createWebHistory(import.meta.env.BASE_URL), // import.meta.env.BASE_URL是Vite的环境变量,指向项目根目录
  routes, // 把刚才定义的路由规则传进去
  scrollBehavior(to, from, savedPosition) { // 路由跳转时的滚动行为,这个也是Vue-router3没有但很实用的配置
    // savedPosition是浏览器记录的上次滚动位置,只有当用户点击浏览器前进/后退按钮时才有值
    if (savedPosition) {
      return savedPosition
    } else {
      // 没有savedPosition时,默认滚动到顶部
      return { top: 0 }
    }
  }
})
// 导出路由实例
export default router

创建对应的测试组件

刚才路由里引用了Home.vue、About.vue、User.vue,得在src目录下新建views文件夹,放这些页面级组件,组件级的放在components文件夹里,这也是前端通用的规范,别搞混。 随便写两个简单的测试组件就行: 比如Home.vue:

<template>
  <div class="home">
    <h1>欢迎来到Vue3+Vue-router4的测试首页</h1>
    <p>这里用的是keep-alive缓存哦,你可以输入一些内容再跳转,回来应该还在</p>
    <input type="text" v-model="inputValue" placeholder="随便输入点什么试试" />
  </div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const inputValue = ref('')
</script>
<style scoped>
.home {
  padding: 2rem;
}
input {
  margin-top: 1rem;
  padding: 0.5rem 1rem;
  font-size: 1rem;
  border-radius: 4px;
  border: 1px solid #ddd;
}
</style>

About.vue更简单:

<template>
  <div class="about">
    <h1>关于我们</h1>
    <p>这是一个测试页面,不需要缓存,所以首页的跳转逻辑不会保留这个页面的状态</p>
  </div>
</template>
<script setup lang="ts">
</script>
<style scoped>
.about {
  padding: 2rem;
}
</style>

在main.ts里挂载路由

现在路由实例和组件都有了,最后一步是在Vue应用入口文件main.ts里挂载路由,这样整个应用才能识别路由规则,进行页面跳转。 打开main.ts,把默认的代码改一下:

import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router' // 引入刚才导出的路由实例
// 创建Vue应用实例
const app = createApp(App)
// 挂载路由
app.use(router)
// 挂载到DOM节点
app.mount('#app')

在App.vue里配置路由出口和导航链接

这是最后一步基础搭建!路由出口是,就是告诉Vue-router“把匹配到的组件渲染在这里”;导航链接是,这是Vue-router自带的组件,比普通标签好,因为它会自动处理active状态(比如当前页面是首页,对应的会自动加router-link-active和router-link-exact-active类名),而且不会刷新页面,是单页应用的核心跳转方式。 打开App.vue,把默认的代码全改了:

<template>
  <!-- 导航栏 -->
  <nav class="nav">
    <router-link to="/" class="nav-item">首页</router-link>
    <router-link to="/about" class="nav-item">关于我们</router-link>
  </nav>
  <!-- 路由出口,要缓存的话,外面包个<keep-alive>,结合meta里的keepAlive属性 -->
  <router-view v-slot="{ Component }">
    <keep-alive>
      <!-- 只有当路由的meta.keepAlive为true时,才会被缓存 -->
      <component :is="Component" v-if="$route.meta.keepAlive" />
    </keep-alive>
    <!-- 不需要缓存的组件直接渲染 -->
    <component :is="Component" v-if="!$route.meta.keepAlive" />
  </router-view>
</template>
<script setup lang="ts">
</script>
<style scoped>
.nav {
  display: flex;
  gap: 2rem;
  padding: 1rem 2rem;
  background-color: #f5f5f5;
  border-bottom: 1px solid #ddd;
}
.nav-item {
  text-decoration: none;
  color: #333;
  font-size: 1.1rem;
}
/* 激活状态的样式,exact-active是完全匹配路径才激活,不会激活/about */
.router-link-exact-active {
  color: #42b983;
  font-weight: bold;
}
</style>

现在敲npm run dev,打开浏览器访问给的地址(通常是http://localhost:5173),点击导航栏,页面会切换,首页输入的内容跳转到关于我们再回来还在,完美!基础搭建搞定了。

新手老手都该避开的10个高频坑

基础搭建看起来简单,但实际开发中很多人会在这里栽跟头,我整理了10个踩过或者见过别人踩最多的坑,按频率排序:

坑1:history模式部署后刷新页面404

这绝对是Top1的坑!刚才也提过,history模式的URL没有#,服务器不知道这是前端路由,会直接去服务器找对应的文件,找不到就404,解决办法很简单,分两种情况:

本地开发没问题,部署到Nginx

在Nginx的配置文件里,找到对应的server块,加一条try_files指令:

location / {
  try_files $uri $uri/ /index.html;
}

意思是:先找URL对应的文件,找不到找对应的文件夹,再找不到就返回根目录的index.html,这样前端路由就能接管了。

部署到GitHub Pages/Gitee Pages/Vercel这些静态托管平台

GitHub Pages比较麻烦,要么用hash模式,要么加一个404.html文件,内容和index.html一模一样;Gitee Pages同理;Vercel就简单多了,不用任何配置,自动处理。

坑2:路由传参用params刷新后数据丢失

这个坑不管新老都踩!Vue-router3里用params传参时,就算刷新页面数据也不会丢(只要路由配置里带了动态参数),但Vue-router4里不一样了! 等下,这里得先分清楚两种params传参:带动态路由参数的params不带动态路由参数的state params(哦不对,Vue-router4里不带动态路由参数的params已经被废弃了!对,废弃了!很多人还在用,这就是坑的根源)。

正确的params传参(带动态路由参数)

路由规则里必须定义动态参数,比如刚才的User.vue,修改路由规则:

{
  path: '/user/:id', // 这里的:id就是动态参数,必须以:开头
  name: 'user',
  component: User,
  meta: { '用户详情',
    keepAlive: false,
    requiresAuth: false
  }
}

然后跳转的时候,有两种方式:

  1. 的to属性:
    <router-link :to="{ name: 'user', params: { id: 123 } }">用户123</router-link>
    <!-- 注意:如果用path跳转的话,params会被忽略!必须用name!Vue-router4更严格了! -->
  2. 用Composition API里的useRouter跳转:
    <script setup lang="ts">
    import { useRouter } from 'vue-router'
    const router = useRouter()

const goToUser = () => { router.push({ name: 'user', params: { id: 123 } }) }

``` 获取参数的时候,用useRoute: ```vue ```

错误的params传参(不带动态路由参数,已废弃)

比如路由规则里是path: '/user',然后跳转时用router.push({ name: 'user', params: { id: 123 } }),Vue-router4会直接给你警告,而且刷新页面后params.id会变成undefined,完全没用。 如果要传不在URL里的隐私数据,应该用query传参吗?不对,query也在URL里,也不安全。正确的替代方案是用state属性: 跳转时:

router.push({ name: 'user', state: { id: 123, secret: 'abc123' } })

获取时:

console.log(route.state)

state属性的数据不在URL里,刷新页面后(只要浏览器没关,sessionStorage还在)数据不会丢,但浏览器关闭后就没了,适合临时的隐私数据。

坑3:keep-alive缓存失效

刚才基础搭建里已经写了keep-alive的正确用法,但很多人还是会失效,常见原因有三个:

原因1:没有用v-slot结合component

Vue-router3里直接在外面包就行,但Vue-router4里默认是一个函数式组件,不能直接包,必须用v-slot="{ Component }"获取匹配到的组件,然后用渲染,再根据条件判断是否缓存。

原因2:meta.keepAlive属性写错了

比如写成了keepalive(小写a),或者属性值是字符串'true'而不是布尔值true,这些都会导致失效。

原因3:组件有多个根节点

Vue3支持组件有多个根节点,但如果被keep-alive缓存的组件有多个根节点,缓存就会失效!解决办法是给组件加一个根节点,比如或者(但有些场景下