解释 Vue Router 中的编程式导航(Programmatic Navigation)和声明式导航(Declarative Navigation)的区别。

各位未来的前端大神们,早上好!我是老码农,今天咱们聊聊 Vue Router 里的两种导航方式,编程式导航和声明式导航。这俩哥们儿,一个动如脱兔,一个静如处子,各有千秋,掌握好了能让你的 Vue 应用流畅丝滑。

开场白:导航的重要性,以及为什么要有两种方式?

想象一下,你正在逛淘宝,如果每次点击链接都要刷新整个页面,那体验简直就是噩梦。Vue Router 的出现就是为了解决这个问题,它让你的 Vue 应用也能拥有单页应用 (SPA) 的丝滑体验,无需刷新页面就能切换不同的视图。

那为什么要有编程式和声明式两种导航方式呢?原因很简单:

  • 声明式导航: 简单直接,适合静态链接,比如导航栏里的链接。
  • 编程式导航: 灵活强大,适合处理复杂的逻辑,比如表单提交后跳转,或者根据用户权限跳转到不同的页面。

第一部分:声明式导航:简单粗暴,一目了然

声明式导航,顾名思义,就是通过声明的方式来告诉 Vue Router 你想去哪里。最常用的就是 <router-link> 组件。

1. router-link 组件:导航界的扛把子

router-link 组件就像一个披着 <a> 标签外衣的超级英雄,它能帮你轻松实现页面跳转,而且还不会刷新页面。

  • 基本用法:

    <router-link to="/home">去首页</router-link>

    这段代码会在页面上渲染出一个链接,点击后会跳转到 /home 路由。

  • to 属性:

    to 属性是 router-link 最重要的属性,它用来指定你要跳转的路由。to 属性的值可以是:

    • 字符串: 直接指定路由路径,例如 to="/home"
    • 对象: 更加灵活,可以传递参数,例如:

      <router-link :to="{ path: '/user', query: { id: 123 }}">查看用户详情</router-link>

      这段代码会跳转到 /user?id=123 路由。

      <router-link :to="{ name: 'user', params: { id: 123 }}">查看用户详情</router-link>

      这段代码会跳转到 name 为 user 的路由,并传递 id 参数。

  • replace 属性:

    默认情况下,router-link 会使用 history.pushState() 方法来跳转页面,这会在浏览器的历史记录中添加一条记录。如果你想使用 history.replaceState() 方法来替换当前的记录,可以使用 replace 属性。

    <router-link to="/home" replace>去首页</router-link>
  • active-classexact-active-class 属性:

    这两个属性用来指定链接激活时的 CSS 类名。active-class 会在链接对应的路由是当前路由的子路由时激活,而 exact-active-class 只会在链接对应的路由是当前路由时激活。

    <router-link to="/home" active-class="active">首页</router-link>

    这段代码会在 /home 路由激活时,给链接添加 active 类。

2. 声明式导航的优点和缺点:

优点 缺点
简单易用,代码可读性高 灵活性较差,无法处理复杂的逻辑
适合静态链接,比如导航栏、侧边栏等 无法在跳转前进行验证,比如判断用户是否登录
性能较好,router-link 组件内部做了优化,避免了不必要的渲染 如果需要动态生成链接,或者需要根据条件显示不同的链接,代码会变得比较复杂

第二部分:编程式导航:灵活强大,掌控全局

编程式导航,顾名思义,就是通过编写代码的方式来控制 Vue Router 的跳转。它允许你更加灵活地处理各种复杂的场景。

1. $router 对象:导航界的指挥官

在 Vue 组件中,你可以通过 $router 对象来访问 Vue Router 的实例。$router 对象提供了以下几个常用的方法:

  • push(location, onComplete?, onAbort?)

    push 方法用来跳转到指定的路由,它会在浏览器的历史记录中添加一条记录。

    • location 参数:

      location 参数可以是:

      • 字符串: 直接指定路由路径,例如 this.$router.push('/home')
      • 对象: 更加灵活,可以传递参数,例如:

        this.$router.push({ path: '/user', query: { id: 123 } });
        this.$router.push({ name: 'user', params: { id: 123 } });
    • onCompleteonAbort 参数:

      onCompleteonAbort 是可选的回调函数,分别在跳转成功和跳转失败时执行。

      this.$router.push(
        '/home',
        () => {
          console.log('跳转成功');
        },
        () => {
          console.log('跳转失败');
        }
      );
  • replace(location, onComplete?, onAbort?)

    replace 方法和 push 方法类似,但是它会替换当前的浏览器历史记录,而不是添加一条新的记录。

    this.$router.replace('/home');
  • go(n)

    go 方法用来在浏览器的历史记录中前进或后退 n 步。

    // 后退一步
    this.$router.go(-1);
    
    // 前进一步
    this.$router.go(1);
  • back()

    back 方法相当于 this.$router.go(-1),用来后退一步。

    this.$router.back();
  • forward()

    forward 方法相当于 this.$router.go(1),用来前进一步。

    this.$router.forward();

2. 编程式导航的应用场景:

  • 表单提交后跳转:

    <template>
      <form @submit.prevent="handleSubmit">
        <input type="text" v-model="username" />
        <button type="submit">提交</button>
      </form>
    </template>
    
    <script>
    export default {
      data() {
        return {
          username: '',
        };
      },
      methods: {
        handleSubmit() {
          // 验证表单数据
          if (this.username) {
            // 跳转到用户详情页
            this.$router.push({ name: 'user', params: { username: this.username } });
          } else {
            alert('请输入用户名');
          }
        },
      },
    };
    </script>
  • 根据用户权限跳转:

    <template>
      <button @click="handleLogin">登录</button>
    </template>
    
    <script>
    export default {
      methods: {
        handleLogin() {
          // 模拟登录
          const isLoggedIn = true;
    
          if (isLoggedIn) {
            // 跳转到首页
            this.$router.push('/home');
          } else {
            // 跳转到登录页
            this.$router.push('/login');
          }
        },
      },
    };
    </script>
  • 处理复杂的路由参数:

    <template>
      <button @click="handleSearch">搜索</button>
    </template>
    
    <script>
    export default {
      data() {
        return {
          keyword: 'vue',
          category: 'framework',
        };
      },
      methods: {
        handleSearch() {
          // 构建复杂的路由参数
          const query = {
            keyword: this.keyword,
            category: this.category,
            page: 1,
          };
    
          // 跳转到搜索结果页
          this.$router.push({ path: '/search', query: query });
        },
      },
    };
    </script>

3. 编程式导航的优点和缺点:

优点 缺点
灵活性强,可以处理各种复杂的逻辑 代码可读性相对较差,需要编写更多的代码
可以在跳转前进行验证,比如判断用户是否登录 性能相对较差,需要手动控制跳转逻辑
可以动态生成链接,或者根据条件显示不同的链接 容易出错,需要仔细测试

第三部分:声明式导航 vs 编程式导航:如何选择?

既然有两种导航方式,那我们该如何选择呢?

选择依据 声明式导航 编程式导航
使用场景 静态链接,简单的页面跳转 复杂的逻辑,需要在跳转前进行验证或处理参数
代码量 较少 较多
灵活性 较低 较高
可读性 较高 较低
性能 较好 相对较差
适用性 导航栏,侧边栏,简单的列表链接 表单提交,用户权限控制,复杂的搜索功能

总结:

声明式导航就像是自动挡汽车,简单易用,适合日常驾驶。编程式导航就像是手动挡汽车,需要一定的驾驶技巧,但是可以让你更好地掌控车辆。

在实际开发中,我们通常会将两种导航方式结合起来使用。对于简单的静态链接,可以使用声明式导航;对于复杂的逻辑,可以使用编程式导航。

彩蛋:router.resolve 方法

Vue Router 还提供了一个 router.resolve 方法,它可以将一个 location 对象解析成一个完整的 URL。这个方法在某些场景下非常有用,比如你需要获取一个链接的 URL,但是又不想立即跳转。

const route = this.$router.resolve({ name: 'user', params: { id: 123 } });
console.log(route.href); // 输出:/user/123

结束语:

掌握了声明式导航和编程式导航,你就掌握了 Vue Router 的核心技能。希望今天的讲解对你有所帮助。记住,多写代码,多思考,才能成为真正的前端大神!下次有机会再和大家分享更多前端技术。

祝大家编码愉快!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注