Vue

Vue2路由 VueRouter

2023-08-12  本文已影响0人  h2coder

Vue路由,也就是VueRouter,Vue2和Vue3有版本之分,本文以Vue2为例

添加依赖

npm install vue-router@3.6.5

基础使用

创建VueRouter实例,并配置地址和组件的映射关系

import Vue from 'vue'

// 引入VueRouter
import VueRouter from 'vue-router'

// 路由页面组件
import Find from "../views/Find.vue";
import My from "../views/My.vue";
import Friend from "../views/Friend.vue";

// 安装注册
Vue.use(VueRouter);

// 创建路由实例,传入配置对象,配置url和组件的映射关系
const router = new VueRouter({
    routes: [
        // 发现音乐
        {
            // #号后面的url地址
            path: '/find',
            // url对应的组件
            // 注意:属性名是component,不是components,是不带s的
            component: Find
        },
        // 我的音乐
        {
            path: '/my',
            component: My
        },
        // 朋友
        {
            path: '/friend',
            component: Friend
        },
    ]
});

// 默认导出,导出路由实例
export default router;

注册路由给Vue实例

import Vue from 'vue'
import App from './App.vue'

// 导入路由实例
import router from './router/index.js'

Vue.config.productionTip = false

// 注入路由对象给Vue实例
new Vue({
  render: h => h(App),
  router
}).$mount('#app')

首页(App.vue)

<template>
  <div>
    <div class="footer_wrap">
      <a href="#/find">发现音乐</a>
      <a href="#/my">我的音乐</a>
      <a href="#/friend">朋友</a>
    </div>
    <div class="top">
      <!-- 路由出口 → 匹配的组件所展示的位置 -->
      <router-view></router-view>
    </div>
  </div>
</template>

<script>
export default {};
</script>

<style>
body {
  margin: 0;
  padding: 0;
}
.footer_wrap {
  position: relative;
  left: 0;
  top: 0;
  display: flex;
  width: 100%;
  text-align: center;
  background-color: #333;
  color: #ccc;
}
.footer_wrap a {
  flex: 1;
  text-decoration: none;
  padding: 20px 0;
  line-height: 20px;
  background-color: #333;
  color: #ccc;
  border: 1px solid black;
}
.footer_wrap a:hover {
  background-color: #555;
}
</style>

准备3个路由页面

<template>
  <div>
    <p>发现音乐</p>
    <p>发现音乐</p>
    <p>发现音乐</p>
    <p>发现音乐</p>
  </div>
</template>

<script>
export default {
  name: 'FindMusic'
}
</script>

<style>
</style>
<template>
  <div>
    <p>我的朋友</p>
    <p>我的朋友</p>
    <p>我的朋友</p>
    <p>我的朋友</p>
  </div>
</template>

<script>
export default {
  name: 'MyFriend'
}
</script>

<style>
</style>
<template>
  <div>
    <p>我的音乐</p>
    <p>我的音乐</p>
    <p>我的音乐</p>
    <p>我的音乐</p>
  </div>
</template>

<script>
export default {
  name: 'MyMusic'
}
</script>

<style>
</style>

导航链接高亮

<template>
  <div>
    <div class="footer_wrap">
      <!-- 路由链接,是VueRouter封装的组件,本质就是一个a标签,通过to属性,来实现a标签的href属性 -->
      <!-- 注意,to属性,不需要加#号 -->
      <!-- 如果用a标签,href属性,还要加上#/,太麻烦 -->
      <!-- router-link组件,还可以实现跨平台,底层能映射为小程序和原生app对应的跳转功能 -->
      <router-link to="/find">发现音乐</router-link>
      <router-link to="/my">我的音乐</router-link>
      <router-link to="/friend">朋友</router-link>
    </div>
    <div class="top">
      <!-- 路由出口 → 匹配的组件所展示的位置 -->
      <router-view></router-view>
    </div>
  </div>
</template>

<script>
export default {};
</script>

<style>
// ...

/* 
  exact:是确切的意思

  精确匹配:会有2个类名,.router-link-exact-active、.router-link-active
  模糊匹配:只有一个类名,.router-link-active
  没匹配上,没有上面2个类名

  疑问:为什么要有区别?
    - 方便后面做路由嵌套,#/my/cart => 第一层是my,第二层是cart
  
  模糊匹配
    - #/my/a #/my/b #/my/c  =>  有效
    - #/my123 #/my456  =>  无效
*/
.footer_wrap .router-link-exact-active {
  background-color: blue;
  color: white;
}
</style>

自定义导航高亮类名

在路由实例上配置类名

import Find from '../views/Find'
import My from '../views/My'
import Friend from '../views/Friend'

import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter) // VueRouter插件初始化

// 创建了一个路由对象
const router = new VueRouter({
  // routes 路由规则们
  // route  一条路由规则 { path: 路径, component: 组件 }
  routes: [
    { path: '/find', component: Find },
    { path: '/my', component: My },
    { path: '/friend', component: Friend },
  ],
  // 自定义精确匹配的类名
  linkExactActiveClass: "exact-active-link",
  // 自定义模糊匹配的类名
  linkActiveClass: "active-link",
})

export default router

设置类名对应的样式

//...

<style>

/* 精确匹配 */
.exact-active-link {
  background-color: red;
  color: white;
}
</style>

导航链接传参-查询参数传参

路由配置

import Home from '../views/Home'
import Search from '../views/Search'
import Vue from 'vue'
import VueRouter from 'vue-router'

// VueRouter插件初始化
Vue.use(VueRouter)

// 创建了一个路由对象
const router = new VueRouter({
  routes: [
    { path: '/home', component: Home },
    { path: '/search', component: Search }
  ]
})

export default router

首页

<template>
  <div class="home">
    <div class="logo-box"></div>
    <div class="search-box">
      <input type="text" />
      <button>搜索一下</button>
    </div>
    <div class="hot-link">
      热门搜索:
      <!-- 查询参数传参 (比较适合传多个参数) -->
      <!-- 路由(导航)链接,参数放在to属性中,通过URL传参的方式,也就是?号后面,参数名=参数值&参数名2=参数值2 -->
      <router-link to="/search?words=程序员">程序员</router-link>
    </div>
  </div>
</template>

<script>
export default {
  name: "HomePage",
};
</script>

<style>
// ...
</style>

搜索结果页面

<template>
  <div class="search">
    <p>搜索关键字: {{ $route.query.words }}</p>
    <p>搜索结果:</p>
    <ul>
      <li>.............</li>
      <li>.............</li>
      <li>.............</li>
      <li>.............</li>
    </ul>
  </div>
</template>

<script>
export default {
  name: "MyFriend",
  created() {
    console.log(this.$route);
    // 获取首页传过来的参数,格式:$route.query.参数名
    console.log(this.$route.query.words);
  },
};
</script>

<style>
// ...
</style>

导航链接传参-动态路由传参

配置路由

import Home from '../views/Home'
import Search from '../views/Search'
import Vue from 'vue'
import VueRouter from 'vue-router'

// VueRouter插件初始化
Vue.use(VueRouter)

// 创建了一个路由对象
const router = new VueRouter({
  routes: [
    {
      path: '/home',
      component: Home
    },
    {
      // 动态路由传参,格式:/路径/:参数名
      // 注意:如果没有配置,或配置出错,那就页面跳转的路由匹配就不正确,会白屏
      path: '/search/:words',
      component: Search
    }
  ]
})

export default router

首页

<template>
  <div class="home">
    <div class="logo-box"></div>
    <div class="search-box">
      <input type="text" />
      <button>搜索一下</button>
    </div>
    <div class="hot-link">
      热门搜索:
      <!-- 动态路由传参 (优雅简洁,传单个参数比较方便) -->
      <!-- 导航链接,动态路由传参,格式:/路径/参数 -->
      <router-link to="/search/程序员">程序员</router-link>
    </div>
  </div>
</template>

<script>
export default {
  name: "HomePage",
};
</script>

<style>
// ...
</style>

搜索结果页

<template>
  <div class="search">
    <!-- 获取动态路由传参 -->
    <p>搜索关键字: {{ $route.params.words }}</p>
    <p>搜索结果:</p>
    <ul>
      <li>.............</li>
      <li>.............</li>
      <li>.............</li>
      <li>.............</li>
    </ul>
  </div>
</template>

<script>
export default {
  name: "SearchResult",
  created() {
    console.log(this.$route);
    console.log(this.$route.params.words);
  },
};
</script>

<style>
// ...
</style>

动态路由-可选符

路由配置

import Home from '../views/Home'
import Search from '../views/Search'
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter) // VueRouter插件初始化

// 创建了一个路由对象
const router = new VueRouter({
  routes: [
    { path: '/home', component: Home },

    // /search/:words ,表示必须要传参数
    // 如果不传参,就会匹配不上,就不会显示 Search 组件
    // { path: '/search/:words', component: Search },

    // 如果不传参数,也希望匹配,那就加个可选符?号
    { path: '/search/:words?', component: Search }
  ]
})

export default router

首页

<template>
  <div class="home">
    <div class="logo-box"></div>
    <div class="search-box">
      <input type="text" />
      <button>搜索一下</button>
    </div>
    <div class="hot-link">
      热门搜索:
      <router-link to="/search">不传参</router-link>
      <router-link to="/search/程序员">程序员</router-link>
    </div>
  </div>
</template>

<script>
export default {
  name: "HomePage",
};
</script>

<style>
// ...
</style>

路由重定向

import Home from '../views/Home'
import Search from '../views/Search'
import Vue from 'vue'
import VueRouter from 'vue-router'

// VueRouter插件初始化
Vue.use(VueRouter)

// 创建了一个路由对象
const router = new VueRouter({
  routes: [
    // 配置路由重定向,如果访问的是根路径,则切换到 /home
    { path: '/', redirect: '/home' },
    { path: '/home', component: Home },
    { path: '/search/:words?', component: Search }
  ]
})

export default router

路由404页面配置

import Home from '../views/Home'
import Search from '../views/Search'
import NotFound from '../views/NotFound.vue'
import Vue from 'vue'
import VueRouter from 'vue-router'

// VueRouter插件初始化
Vue.use(VueRouter)

// 创建了一个路由对象
const router = new VueRouter({
  routes: [
    { path: '/', redirect: '/home' },
    { path: '/home', component: Home },
    { path: '/search/:words?', component: Search },
    // 路由404,*表示通配符,匹配任意路径,当路径不匹配时,就命中这个规则,跳转到一个404提示页面
    { path: '*', component: NotFound },
  ]
})

export default router

路由模式

单页面、多页面应用对比

开发分类 实现方式 页面性能 开发效率 用户体验 学习成本 首屏加载 SEO
单页 一个HTML页面 按需更新,性能高 非常好
多页 多个HTML页面 整页更新,性能差 中等 一般 中等

实现细节

路由配置

import Home from '../views/Home'
import Search from '../views/Search'
import NotFound from '../views/NotFound'
import Vue from 'vue'
import VueRouter from 'vue-router'

// VueRouter插件初始化
Vue.use(VueRouter)

// 创建了一个路由对象
const router = new VueRouter({
  /* 
    路由的路径有#,看起来不自然,,能否切成真正路径形式?
      hash路由(默认) 例如: http://localhost:8080/#/home
      history路由(常用) 例如: http://localhost:8080/home (以后上线需要服务器端支持)
  */
  // hash路由,默认值
  // mode: "hash",
  // 好看
  mode: "history",
  routes: [
    { path: '/', redirect: '/home' },
    { path: '/home', component: Home },
    { name: 'search', path: '/search/:words?', component: Search },
    { path: '*', component: NotFound }
  ]
})

export default router

编程式导航-两种跳转方式

路由配置

import Home from '../views/Home'
import Search from '../views/Search'
import NotFound from '../views/NotFound'
import Vue from 'vue'
import VueRouter from 'vue-router'

// VueRouter插件初始化
Vue.use(VueRouter)

// 创建了一个路由对象
const router = new VueRouter({
  // 注意:一旦采用了 history 模式,地址栏就没有 #,需要后台配置访问规则
  mode: 'history',
  routes: [
    { path: '/', redirect: '/home' },
    { path: '/home', component: Home },
    
    // 命名路由,通过name属性指定路由的名称,后续则通过指定这个名称去跳转
    { name: 'search', path: '/search/:words?', component: Search },
    
    { path: '*', component: NotFound }
  ]
})

export default router

跳转方式

<template>
  <div class="home">
    <div class="logo-box"></div>
    <div class="search-box">
      <input @keyup.enter="goSearch" type="text" />
      <button @click="goSearch">搜索一下</button>
    </div>
    <div class="hot-link">
      热门搜索:
      <router-link to="/search/程序员">程序员</router-link>
    </div>
  </div>
</template>

<script>
export default {
  name: "HomePage",
  methods: {
    goSearch() {
      // ------- 编程式导航,路径跳转 -------
      // 适合路径比较短,直接在这里写上路径

      // 写法一,直接传入路径
      this.$router.push("/search");

      // 写法二,通过配置对象的path属性指定路径
      this.$router.push({
        path: "/search",
      });

      // ------- 编程式导航,命名路由跳转 -------
      // 适合路径比较长的,将路径放到路由配置文件中,在这里使用名称去找到对应的路径

      // 通过配置对象的name属性,配置命名路由的名称
      this.$router.push({
        name: "search",
      });
    },
  },
};
</script>

<style>
// ...
</style>

编程式导航-跳转传参

上面的编程式导航,没有传参,接下来就来看看怎么传参

<template>
  <div class="home">
    <div class="logo-box"></div>
    <div class="search-box">
      <input @keyup.enter="goSearch" v-model="inpValue" type="text" />
      <button @click="goSearch">搜索一下</button>
    </div>
    <div class="hot-link">
      热门搜索:
      <router-link to="/search/程序员">程序员</router-link>
    </div>
  </div>
</template>

<script>
export default {
  name: "HomePage",
  data() {
    return {
      inpValue: "",
    };
  },
  methods: {
    goSearch() {
      // 方式一:路径路由,使用查询参数,传递参数
      this.$router.push(`/search?words=${this.inpValue}`);

      // 方式二,路径路由,使用配置对象
      this.$router.push({
      path: "/search",
        query: {
          words: this.inpValue,
        },
      });

      // 写法三,动态路由-直接拼路径的方式
      this.$router.push(`/search/${this.inpValue}`);

      // 写法四,动态路由-配置对象的方式
      this.$router.push({
        path: `/search/${this.inpValue}`,
      });

      // 写法五,命名路由的方式
      this.$router.push({
        name: "search",
        params: {
          words: this.inpValue,
        },
      });
    },
  },
};
</script>

<style>
// ...
</style>

keep-alive

一级路由(App.vue)

<template>
  <div class="h5-wrapper">
    <!--
      Vue组件一般什么时候会被销毁掉?
        - vue实例.$destroy(),手动销毁组件
        - v-if,是将DOM元素直接从DOM树中移除,所以组件也被销毁了
        - 路由切换,例如Tab切换,离开了组件,就会被销毁
     -->

    <!-- keep-alive,缓存组件,保证页面跳转到详情页后返回,一级路由的所有组件都不会被销毁 -->
    <!-- 一级路由,包括 LayoutPage 和 ArticleDetailPage,但我们指向Layout被会缓存,详情页不要被缓存 -->

    <!-- 注意:默认Vue组件的名称就是文件名,但如果可以在组件身上指定 name 属性,就是我们手动指定的 name 属性,而不是文件名 -->

    <!-- (常用)include属性,则指定要被缓存的组件,就是要缓存谁,属性值是要被缓存的组件名数组,是组件名,而不是文件名!!! -->
    <!-- exclude属性,配置不被缓存的组件,就是不要缓存谁,和include属性是相反的,这2个属性一般不会同时用,只会选其一 -->
    <!-- max属性,缓存组件的实例数量 -->
    <!-- 
      注意:
        一般我们使用 exclude 属性,它是默认所有都不缓存,只缓存我们指定的组件,节省内存
        而 include 属性,是所有组件都缓存,然后不缓存我们指定的组件,会导致缓存的组件太多,导致性能问题
     -->
    <keep-alive :include="keepAliveArr">
      <!-- 一级路由-出口 -->
      <router-view></router-view>
    </keep-alive>
  </div>
</template>

<script>
export default {
  name: "h5-wrapper",
  data() {
    return {
      // 缓存的组件的组件名数组
      keepAliveArr: ["LayoutPage"],
    };
  },
};
</script>

<style>
body {
  margin: 0;
  padding: 0;
}
</style>
<style lang="less" scoped>
.h5-wrapper {
  .content {
    margin-bottom: 51px;
  }
  .tabbar {
    position: fixed;
    left: 0;
    bottom: 0;
    width: 100%;
    height: 50px;
    line-height: 50px;
    text-align: center;
    display: flex;
    background: #fff;
    border-top: 1px solid #e4e4e4;
    a {
      flex: 1;
      text-decoration: none;
      font-size: 14px;
      color: #333;
      -webkit-tap-highlight-color: transparent;
      &.router-link-active {
        color: #fa0;
      }
    }
  }
}
</style>

二级路由(Layout.vue)

<template>
  <!-- 一级路由页面-首页 -->
  <div class="h5-wrapper">
    <div class="content">
      <!-- 组件缓存,缓存所有Tab页面 -->
      <keep-alive>
        <!-- 二级路由-出口, -->
        <router-view></router-view>
      </keep-alive>
    </div>
    <!-- 底部TabBar -->
    <nav class="tabbar">
      <router-link to="/article">面经</router-link>
      <router-link to="/collect">收藏</router-link>
      <router-link to="/like">喜欢</router-link>
      <router-link to="/user">我的</router-link>
    </nav>
  </div>
</template>

<script>
export default {
  // 组件名(如果没有配置 name,才会找文件名作为组件名)
  name: "LayoutPage",
  // 注意:一般组件被缓存了,反复进入该组件,组件的created、mounted、destroyed,就只有第一次的时候被执行
  // 默认组件是不会被缓存的,每次进出页面,都会重新加载组件,声明周期回调函数就会被执行,但缓存后就不会了
  // 被缓存后,会多2个生命周期函数回调,分别是 activated 和 deactivated
  created() {
    console.log("created 组件被加载了...");
  },
  mounted() {
    console.log("mounted 组件的DOM渲染完毕了...");
  },
  destroyed() {
    console.log("destroyed 组件被销毁了...");
  },
  activated() {
    console.log("activated 组件被激活了,也就是页面可见了...");
  },
  deactivated() {
    console.log("deactivated 组件失活,也就是页面不可见了...");
  },
};
</script>

<style>
body {
  margin: 0;
  padding: 0;
}
</style>
<style lang="less" scoped>
.h5-wrapper {
  .content {
    margin-bottom: 51px;
  }
  .tabbar {
    position: fixed;
    left: 0;
    bottom: 0;
    width: 100%;
    height: 50px;
    line-height: 50px;
    text-align: center;
    display: flex;
    background: #fff;
    border-top: 1px solid #e4e4e4;
    a {
      flex: 1;
      text-decoration: none;
      font-size: 14px;
      color: #333;
      -webkit-tap-highlight-color: transparent;
    }
    a.router-link-active {
      color: orange;
    }
  }
}
</style>
上一篇 下一篇

猜你喜欢

热点阅读