前端框架Vue(五)


Vue路由

路由基本知识

Vue RouterVue.js的官方路由。它与Vue.js核心深度集成,使用Vue.js构建单页面应用变得轻而易举。

功能包括:

  • 嵌套路由映射
  • 动态路由选择
  • 模块化、基于组件的路由配置
  • 路由参数、查询、通配符
  • 展示由Vue.js的过渡系统提供的过渡效果
  • 细致的导航控制
  • 自动激活CSS类的链接
  • HTML5 history 模式或hash模式
  • 可定制的滚动行为
  • url的正确编码

安装Vue Router

npm instal vue-router@4

added 2 packages, and audited 29 packages in 2s

4 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

新建一个项目:

npm create vite@latest
✔ Project name: … vue_test03
✔ Select a framework: › Vue
✔ Select a variant: › JavaScript

Scaffolding project in /Users/laobai/TempProjects/vue_test03...

Done. Now run:

  cd vue_test03
  npm install
  npm run dev

安装包

npm install           

added 26 packages, and audited 27 packages in 10s

3 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

启动服务

npm run dev

> vue_test03@0.0.0 dev
> vite


  VITE v4.4.9  ready in 332 ms

  ➜  Local:   http://localhost:5173/
  ➜  Network: use --host to expose
  ➜  press h to show help

路由基本配置使用

在src目录下创建router目录存放index.js路由配置文件,创建views目录存放子组件文件。

src—VueRouter—router—index.js
src—VueRouter—views—Lists.vue
src—VueRouter—views—My.vue
src—VueRouter—App.vue
src—main.js

index.js

// 配置路由

import { createRouter, createWebHashHistory } from 'vue-router'
import My from '../views/My.vue'
import Lists from '../views/Lists.vue'

const routes = [
  {
    path:"/my",
    component:My
  },
  {
    path:"/lists",
    component:Lists
  },
]

const router = createRouter({
  history: createWebHashHistory(),  // hash模式 #/my  #/list
            // history普通模式  /my   /list

  routes, // routers: routes 的缩写
})

export default router

App.vue

<router-view/>负责显示路径对应的组件内容

组件显示的出口,是根据用户输入的path显示对应的组件 (在index.js中配置)

<template>
  <div>
    App
    <!-- 通过插槽插入子组件路由 -->
    
    <router-view></router-view>
  </div>
</template>

Lists.vue

<template>
  <div>
    lists
  </div>
</template>

My.vue

<template>
  <div>
    my
  </div>
</template>

main.js

main.js中注册/挂载路由,才可以在页面中通过<router-view/>显示出来

import { createApp } from 'vue'
import './style.css'
import App from './VueRouter/App.vue'
import router from './VueRouter/router'

let app = createApp(App)

app.use(router) // 注册路由插件
app.mount("#app")

/根路径配置重定向

// 配置路由

import { createRouter, createWebHashHistory } from 'vue-router'
import My from '../views/My.vue'
import Lists from '../views/Lists.vue'

const routes = [
  {
    path:"/my",
    component:My
  },
  {
    path:"/lists",
    component:Lists
  },
  // 设置访问根节点时重定向到/lists
  {
    path:"/",
    redirect:"/lists"
  }
]

const router = createRouter({
  history: createWebHashHistory(),  // hash模式 #/my  #/list
            // history普通模式  /my   /list

  routes, // routers: routes 的缩写
})

export default router

在上面的基础上,可以为每个路径配置的组件设置一个名字

// 配置路由

import { createRouter, createWebHashHistory } from 'vue-router'
import My from '../views/My.vue'
import Lists from '../views/Lists.vue'

const routes = [
  {
    path:"/my",
    name:"my",
    component:My
  },
  {
    path:"/lists",
    name:"lists",		// 命名路由
    component:Lists
  },
  // 设置访问根节点时重定向到/lists
  {
    path:"/",
    // redirect:"/lists"
    redirect:{
      name:"lists"
    }
  }
]

const router = createRouter({
  history: createWebHashHistory(),  // hash模式 #/my  #/list
            // history普通模式  /my   /list

  routes, // routers: routes 的缩写
})

export default router

针对访问不存在的路径,配置404路由

增加NotFound.vue

<template>
  <div>
    404 not found
  </div>
</template>

修改index.js

// 配置路由

import { createRouter, createWebHashHistory } from 'vue-router'
import My from '../views/My.vue'
import Lists from '../views/Lists.vue'
import NotFound from '../views/NotFound.vue'

const routes = [
  {
    path:"/my",
    name:"my",
    component:My
  },
  {
    path:"/lists",
    name:"lists",
    component:Lists
  },
  // 设置访问根节点时重定向到/lists
  {
    path:"/",
    // redirect:"/lists"
    redirect:{
      name:"lists"
    }
  },
  {
    path:"/:pathMatch(.*)*",
    name:"NotFound",
    component: NotFound
  }
]

const router = createRouter({
  history: createWebHashHistory(),  // hash模式 #/my  #/list
            // history普通模式  /my   /list

  routes, // routers: routes 的缩写
})

export default router

声明式导航

在VueRouter目录下创建components目录,创建Tabbar.vue

通过<router-link to="">来实现页面跳转(单页面应用,不会刷新页面)

<template>
  <div class="tabbar">
    <ul>
      <router-link custom to="/lists" v-slot="{isActive,navigate}">
        <li :class="isActive?'plactive':''" @click="navigate">商品列表</li>
      </router-link>
      <router-link custom to="/my" v-slot="{isActive,navigate}">
        <li :class="isActive?'plactive':''" @click="navigate">我的</li>
      </router-link>
    </ul>
  </div>
</template>

<style scoped lang="scss">
.plactive{
  color: red;
}
.tabbar{
  position: fixed;
  bottom: 0;
  width:100%;
  height: 50px;
  line-height: 50px;
  text-align: center;
  ul{
    display: flex;
    li {
      flex: 1;
    }
  }
}
</style>

路由index.js

// 配置路由
import { createRouter, createWebHashHistory } from 'vue-router'
import My from '../views/My.vue'
import Lists from '../views/Lists.vue'
import NotFound from '../views/NotFound.vue'

const routes = [
  {
    path:"/my",
    name:"my",
    component:My
  },
  {
    path:"/lists",
    name:"lists",
    component:Lists
  },
  // 设置访问根节点时重定向到/lists
  {
    path:"/",
    // redirect:"/lists"
    redirect:{
      name:"lists"
    }
  },
  {
    path:"/:pathMatch(.*)*",
    name:"NotFound",
    component: NotFound
  }
]

const router = createRouter({
  history: createWebHashHistory(),  // hash模式 #/my  #/list
            // history普通模式  /my   /list

  routes, // routers: routes 的缩写
})

export default router

App.vue

<template>
  <div>
    <!-- 通过插槽插入子组件路由 -->
    <router-view></router-view>
    <!-- 增加导航 -->
    <Tabbar></Tabbar>
  </div>
</template>

<script>
import Tabbar from './components/Tabbar.vue';
export default {
  components:{
    Tabbar
  }
}
</script>

<style>
*{
  margin: 0;
  padding: 0;
}
ul{
  list-style: none;
}
</style>

嵌套路由

在路由中创建子路由,对应上面的例子中即为商品列表中包多个子列表,比如最新商品、推荐商品等。

Lists.vue同级目录中创建lists目录,在其中创建两个子文件New.vue和Recommend.vue

<template>
  <div>
    new
  </div>
</template>
<template>
  <div>
    recommond
  </div>
</template>

在index.js中导入上面两个文件,并在lists路由中加入子路由

// 配置路由

import { createRouter, createWebHashHistory } from 'vue-router'
import My from '../views/My.vue'
import Lists from '../views/Lists.vue'
import NotFound from '../views/NotFound.vue'
import New from '../views/lists/New.vue'
import Recommend from '../views/lists/Recommend.vue'


const routes = [
  {
    path:"/my",
    component:My
  },
  {
    path:"/lists",
    component:Lists,
    // 增加子路由
    children:[
      {
        path:"/lists/new",
        component:New
      },
      {
        path:"recommend",
        component:Recommend
      },
      // 访问lists页重定向到recommend
      {
        path:"/lists",
        redirect:'/lists/recommend'
      }
    ]
  },
  // 设置访问根节点时重定向到/lists,即访问根目录直接访问到lists页
  {
    path:"/",
    redirect:"/lists"
    // 若要匹配多级路由,只能用上面的写法,下面的写法无法匹配多级路由
    // redirect:{
    //   name:"lists"
    // }
  },
  {
    path:"/:pathMatch(.*)*",
    name:"NotFound",
    component: NotFound
  }
]

const router = createRouter({
  history: createWebHashHistory(),  // hash模式 #/my  #/list
            // history普通模式  /my   /list

  routes, // routers: routes 的缩写
})

export default router

上面的代码中,两次使用到了重定向redirect,均是为页面设置默认展示的内容。

在Lists.vue中增加子路由,即最新商品和推荐商品等

<template>
  <div>
    <div style="height:100px;line-height: 100px;text-align: center;">轮播图</div>
    <ul class="lists">
      <router-link custom to="/lists/new" v-slot="{ isActive, navigate }">
        <!-- <li >最新商品</li> -->
        <li @click="navigate">
          <span :class="isActive ? 'plactive' : ''" >最新商品</span>
        </li>
      </router-link>
      <router-link custom to="/lists/recommend" v-slot="{ isActive, navigate }">
        <li @click="navigate">
          <span :class="isActive ? 'plactive' : ''" >推荐商品</span>
        </li>
      </router-link>
    </ul>


    <router-view></router-view>
  </div>
</template>

<style scoped lang="scss">
.lists {
  display: flex;
  height: 50px;
  line-height: 50px;
  text-align: center;

  li {
    flex: 1;
    list-style: none;
    span{
          padding:10px 0;
        }
  }
}
.plactive {
  color: red;
  border-bottom: 3px solid red;
}
</style>

编程式导航

编程式一般用于在js中,使用this.$router.push()方法来进行

<template>
  <div>
    <ul>
      <!-- 声明式 -->
      <!-- <router-link custom :to="'/detail/'+item.filmId" v-slot="{navigate}" v-for="item in datalist" :key="item.filmId">
        <li @click="navigate">
          {{item.name}}
        </li>
      </router-link> -->
      <!-- 编程式 -->
      <li v-for="item in datalist" :key="item.filmId" @click="plClick(item.filmId)">
        {{item.name}}
      </li>
    </ul>
  </div>
</template>

<script>
import axios from 'axios'
export default{
  data(){
    return{
      datalist:[]
    }
  },
  async mounted(){
    const res = await axios({
      url:"https://m.maizuo.com/gateway?cityId=110100&pageNum=1&pageSize=10&type=1&k=3317109",
      headers:{
        'X-Client-Info':'{"a":"3000","ch":"1002","v":"5.2.1","e":"16931924276063063597842433","bc":"110100"}',
        'X-Host':'mall.film-ticket.film.list'
      }
    })

    this.datalist = res.data.data.films
    console.log(this.datalist)
  },
  methods:{
    plClick(id){
      this.$router.push(`/detail/${id}`)
    }
  }
}
</script>

<style scoped lang="scss">
ul{
  li{
    padding:10px;
  }
}
</style>

动态路由匹配

上面增加了detail商品详情,而在商品详情页url中商品id是根据访问的商品动态变化的

新建Detail.vue文件

<template>
  <div>
    <button @click="plBackClick">返回</button>
    detail
  </div>
</template>

<script>
export default {
  mounted(){
    console.log('接收上一个页面传来的参数',this.$route.params.plid)
  },
  methods:{
    plBackClick(){
      this.$router.back()   // 返回  this.$router.forward()
    }
  }
}
</script>

在路由配置文件index.js中导入并配置路由

// 配置路由

import { createRouter, createWebHashHistory } from 'vue-router'
import My from '../views/My.vue'
import Lists from '../views/Lists.vue'
import NotFound from '../views/NotFound.vue'
import New from '../views/lists/New.vue'
import Recommend from '../views/lists/Recommend.vue'
import Detail from '../views/Detail.vue'


const routes = [
  {
    path:"/my",
    component:My
  },
  {
    path:"/lists",
    component:Lists,
    // 增加子路由
    children:[
      {
        path:"/lists/new",
        component:New
      },
      {
        path:"recommend",
        component:Recommend
      },
      // 访问lists页重定向到recommend
      {
        path:"/lists",
        redirect:'/lists/recommend'
      }
    ]
  },
  {
    name:'detail',
    path:'/detail/:plid',
    component:Detail
  },
  // 设置访问根节点时重定向到/lists,即访问根目录直接访问到lists页
  {
    path:"/",
    redirect:"/lists"
    // 若要匹配多级路由,只能用上面的写法,下面的写法无法匹配多级路由
    // redirect:{
    //   name:"lists"
    // }
  },
  {
    path:"/:pathMatch(.*)*",
    name:"NotFound",
    component: NotFound
  }
]

const router = createRouter({
  history: createWebHashHistory(),  // hash模式 #/my  #/list
            // history普通模式  /my   /list

  routes, // routers: routes 的缩写
})

export default router

修改列表页New.vue

<template>
  <div>
    <ul>
      <!-- 声明式 -->
      <!-- <router-link custom :to="'/detail/'+item.filmId" v-slot="{navigate}" v-for="item in datalist" :key="item.filmId">
        <li @click="navigate">
          {{item.name}}
        </li>
      </router-link> -->
      <!-- 编程式 -->
      <li v-for="item in datalist" :key="item.filmId" @click="plClick(item.filmId)">
        {{item.name}}
      </li>
    </ul>
  </div>
</template>

<script>
import axios from 'axios'
export default{
  data(){
    return{
      datalist:[]
    }
  },
  async mounted(){
    const res = await axios({
      url:"https://m.maizuo.com/gateway?cityId=110100&pageNum=1&pageSize=10&type=1&k=3317109",
      headers:{
        'X-Client-Info':'{"a":"3000","ch":"1002","v":"5.2.1","e":"16931924276063063597842433","bc":"110100"}',
        'X-Host':'mall.film-ticket.film.list'
      }
    })

    this.datalist = res.data.data.films
    console.log(this.datalist)
  },
  methods:{
    plClick(id){
      this.$router.push(`/detail/${id}`)
    }
  }
}
</script>

<style scoped lang="scss">
ul{
  li{
    padding:10px;
  }
}
</style>

路由模式

前面介绍的内容都是基于哈希模式实现,最直观的效果就是url中主机及端口后方有个#,例如:http://localhost:5173/#/lists/new,个人不是很喜欢这种url。

可以在创建路由时使用createWebHistory()进行创建

const router = createRouter({
  // history: createWebHashHistory(),  // hash模式 #/my  #/list
            // history普通模式  /my   /list
  history: createWebHistory(),

  routes, // routers: routes 的缩写
})

全局路由拦截

新建Login.vue

<template>
  <div>
    <h3>login页面</h3>
    <button @click="plClick">登录</button>
  </div>
</template>

<script>
export default{
  methods:{
    plClick(){
      localStorage.setItem("token","plscript")

      this.$router.push("/")
    }
  }
}
</script>

修改路由文件index.js,进行请求拦截,满足条件的才可以继续执行

// 配置路由

import { createRouter, createWebHashHistory,createWebHistory } from 'vue-router'
import My from '../views/My.vue'
import Lists from '../views/Lists.vue'
import NotFound from '../views/NotFound.vue'
import New from '../views/lists/New.vue'
import Recommend from '../views/lists/Recommend.vue'
import Detail from '../views/Detail.vue'
import Login from '../views/Login.vue'


const routes = [
  {
    path:"/my",
    component:My
  },
  {
    path:"/lists",
    component:Lists,
    // 增加子路由
    children:[
      {
        path:"/lists/new",
        component:New
      },
      {
        path:"recommend",
        component:Recommend
      },
      // 访问lists页重定向到recommend
      {
        path:"/lists",
        redirect:'/lists/recommend'
      }
    ]
  },
  {
    name:'detail',
    path:'/detail/:plid',
    component:Detail
  },
  {
    name:'Login',
    path:'/login',
    component:Login
  },
  // 设置访问根节点时重定向到/lists,即访问根目录直接访问到lists页
  {
    path:"/",
    redirect:"/lists"
    // 若要匹配多级路由,只能用上面的写法,下面的写法无法匹配多级路由
    // redirect:{
    //   name:"lists"
    // }
  },
  {
    path:"/:pathMatch(.*)*",
    name:"NotFound",
    component: NotFound
  }
]

const router = createRouter({
  // history: createWebHashHistory(),  // hash模式 #/my  #/list
            // history普通模式  /my   /list
  history: createWebHistory(),

  routes, // routers: routes 的缩写
})

// 全局拦截 beforeEach每次路由都进行拦截,to是要跳转的页面,from是从哪个页面跳过来的,next不执行就不会继续执行
router.beforeEach((to, from, next) =>{
  // console.log(to.fullPath)
  // console.log(from.fullPath)
  let isAuthenticated = localStorage.getItem("token")
  // 判断当前页面不是Login,并且localStorage中不含有token,跳转到登录页
  if(to.name !== 'Login' && !isAuthenticated) next ({name:'Login'})
  // 否则继续执行操作
  else next()
})

export default router

这里使用的是beforeEach(to,from,next),会拦截全部的路由请求

可以拦截指定的一个或几个地址进行拦截

router.beforeEach((to, from, next) =>{
  // console.log(to.fullPath)
  // console.log(from.fullPath)
  let isAuthenticated = localStorage.getItem("token")
  // 判断当前页面不是Login,并且localStorage中不含有token,跳转到登录页
  if(to.name !== 'Login' && !isAuthenticated && to.fullPath==='/my') next ({name:'Login'})
  // 否则继续执行操作
  else next()
})

还可以对需要进行拦截的添加meta

{
    path:"/my",
    component:My,
    meta:{
      requiredAuth:true
    }
  }




router.beforeEach((to, from, next) =>{
  // console.log(to.fullPath)
  // console.log(from.fullPath)
  let isAuthenticated = localStorage.getItem("token")
  // 判断当前页面不是Login,并localStorage中不含有token,且meta的requiredAuth为True,跳转到登录页
  if(to.name !== 'Login' && !isAuthenticated && to.meta.requiredAuth) next ({name:'Login'})
  // 否则继续执行操作
  else next()
})

组件内的钩子

上面介绍了在路由配置文件中进行拦截,vue也提供了在组件内进行拦截的方法

修改my.vue

进入组件之前

<template>
  <div>
    my
  </div>
</template>
<script>
export default{
  // 进入路由
  beforeRouteEnter(to, from, next){
    let isAuthenticated = localStorage.getItem("token")
    // 如果存在token,直接继续操作
    if(isAuthenticated){
      next()
      // 如果没有token,跳转到登录页
    }else{
      next({name:"Login"})
    }

  }
}
</script>

更新组件之前

beforeRouteUpdate(to,from){
    // console.log("beforeRouteUpdate",to)
    console.log("接受猜你喜欢传来的参数",to.params.myid,"带着id参数请求后端接口")
}

离开组件之前

beforeRouteLeave(){
    const answer = window.confirm("确定要离开吗?")
if(!answer) return false
}

路由懒加载

在打包构建应用时,JavaScript包会变得非常大,影响页面加载。为了提高加载效率,可以把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件。

Vue Router支持开箱即用的动态导入,这意味着可以用动态导入代替静态导入。

将原有导入组件路由的代码注释掉

// import My from '../views/My.vue'

在增加路由的位置导入对应组件路由

{
    path:"/my",
    // 使用箭头函数导入My.vue,即动态导入
    component: ()=> import('../views/My.vue'),
    meta:{
      requiredAuth:true
    }
  }

VCA与路由

上面的例子是使用选项式API模式写的,这里改写成Vue3的组合式API模式(VCA)

src/main.js不用改

import { createApp } from 'vue'
import './style.css'
import App from './VueRouter/App.vue'
import router from './VueRouter/router'

let app = createApp(App)

app.use(router) // 注册路由插件
app.mount("#app")

scr/VueRouter/router/index.js不用改

// 配置路由

import { createRouter, createWebHashHistory,createWebHistory } from 'vue-router'
// import My from '../views/My.vue'
import Lists from '../views/Lists.vue'
import NotFound from '../views/NotFound.vue'
import New from '../views/lists/New.vue'
import Recommend from '../views/lists/Recommend.vue'
import Detail from '../views/Detail.vue'
import Login from '../views/Login.vue'


const routes = [
  {
    path:"/my",
    component: ()=> import('../views/My.vue'),
    meta:{
      requiredAuth:true
    }
  },
  {
    path:"/lists",
    component:Lists,
    // 增加子路由
    children:[
      {
        path:"/lists/new",
        component:New
      },
      {
        path:"recommend",
        component:Recommend
      },
      // 访问lists页重定向到recommend
      {
        path:"/lists",
        redirect:'/lists/recommend'
      }
    ]
  },
  {
    name:'detail',
    path:'/detail/:plid',
    component:Detail
  },
  {
    name:'Login',
    path:'/login',
    component:Login
  },
  // 设置访问根节点时重定向到/lists,即访问根目录直接访问到lists页
  {
    path:"/",
    redirect:"/lists"
    // 若要匹配多级路由,只能用上面的写法,下面的写法无法匹配多级路由
    // redirect:{
    //   name:"lists"
    // }
  },
  {
    path:"/:pathMatch(.*)*",
    name:"NotFound",
    component: NotFound
  }
]

const router = createRouter({
  // history: createWebHashHistory(),  // hash模式 #/my  #/list
            // history普通模式  /my   /list
  history: createWebHistory(),

  routes, // routers: routes 的缩写
})

// 全局拦截 beforeEach每次路由都进行拦截,to是要跳转的页面,from是从哪个页面跳过来的,next不执行就不会继续执行
// router.beforeEach((to, from, next) =>{
//   // console.log(to.fullPath)
//   // console.log(from.fullPath)
//   let isAuthenticated = localStorage.getItem("token")
//   // 判断当前页面不是Login,并且localStorage中不含有token,跳转到登录页
//   if(to.name !== 'Login' && !isAuthenticated) next ({name:'Login'})
//   // 否则继续执行操作
//   else next()
// })

// 全局拦截 beforeEach每次路由都进行拦截,to是要跳转的页面,from是从哪个页面跳过来的,next不执行就不会继续执行
// router.beforeEach((to, from, next) =>{
//   // console.log(to.fullPath)
//   // console.log(from.fullPath)
//   let isAuthenticated = localStorage.getItem("token")
//   // 判断当前页面不是Login,并且localStorage中不含有token,跳转到登录页
//   if(to.name !== 'Login' && !isAuthenticated && to.fullPath==='/my') next ({name:'Login'})
//   // 否则继续执行操作
//   else next()
// })

// router.beforeEach((to, from, next) =>{
//   // console.log(to.fullPath)
//   // console.log(from.fullPath)
//   let isAuthenticated = localStorage.getItem("token")
//   // 判断当前页面不是Login,并且localStorage中不含有token,跳转到登录页
//   if(to.name !== 'Login' && !isAuthenticated && to.meta.requiredAuth) next ({name:'Login'})
//   // 否则继续执行操作
//   else next()
// })

export default router

scr/App.vue修改如下:

<template>
  <div>
    <!-- 通过插槽插入子组件路由 -->
    <router-view></router-view>
    <!-- 增加导航 -->
    <Tabbar></Tabbar>
  </div>
</template>

<script setup>
import Tabbar from './components/Tabbar.vue';
// export default {
//   components:{
//     Tabbar
//   }
// }
</script>

<style>
*{
  margin: 0;
  padding: 0;
}
ul{
  list-style: none;
}
</style>

scr/VueRouter/components/Tabbar.vue不用修改

<template>
  <div class="tabbar">
    <ul>
      <router-link custom to="/lists" v-slot="{isActive,navigate}">
        <li :class="isActive?'plactive':''" @click="navigate">商品列表</li>
      </router-link>
      <router-link custom to="/my" v-slot="{isActive,navigate}">
        <li :class="isActive?'plactive':''" @click="navigate">我的</li>
      </router-link>
      
    </ul>
  </div>
</template>

<style scoped lang="scss">
.plactive{
  color: red;
}
.tabbar{
  position: fixed;
  bottom: 0;
  width:100%;
  height: 50px;
  line-height: 50px;
  background: white;
  z-index: 100;
  text-align: center;
  ul{
    display: flex;
    li {
      flex: 1;
    }
  }
}
</style>

src/VueRouter/views/Login.vue修改如下:

<template>
  <div>
    <h3>login页面</h3>
    <button @click="plClick">登录</button>
  </div>
</template>

<!-- <script>
export default{
  methods:{
    plClick(){
      localStorage.setItem("token","plscript")

      this.$router.push("/")
    }
  }
}
</script> -->
<script setup>
import {useRouter} from 'vue-router'
const router = useRouter()
const plClick = ()=>{
  localStorage.setItem("token","plscript")
  router.push("/")
}
</script>

src/VueRouter/views/List.vue不用修改。List.vue中的内容已经分离到Lists目录下的子文件

<template>
  <div>
    <div style="height:100px;line-height: 100px;text-align: center;">轮播图</div>
    <ul class="lists">
      <router-link custom to="/lists/new" v-slot="{ isActive, navigate }">
        <!-- <li >最新商品</li> -->
        <li @click="navigate">
          <span :class="isActive ? 'plactive' : ''" >最新商品</span>
        </li>
      </router-link>
      <router-link custom to="/lists/recommend" v-slot="{ isActive, navigate }">
        <li @click="navigate">
          <span :class="isActive ? 'plactive' : ''" >推荐商品</span>
        </li>
      </router-link>
    </ul>


    <router-view></router-view>
  </div>
</template>

<style scoped lang="scss">
.lists {
  display: flex;
  height: 50px;
  line-height: 50px;
  text-align: center;

  li {
    flex: 1;
    list-style: none;
    span{
          padding:10px 0;
        }
  }
}
.plactive {
  color: red;
  border-bottom: 3px solid red;
}
</style>

src/VueRouter/views/List/Recommend.vue不用修改

<template>
  <div>
    recommond
  </div>
</template>

src/VueRouter/views/List/New.vue修改如下:

<template>
  <div>
    <ul>
      <!-- 声明式 -->
      <!-- <router-link custom :to="'/detail/'+item.filmId" v-slot="{navigate}" v-for="item in datalist" :key="item.filmId">
        <li @click="navigate">
          {{item.name}}
        </li>
      </router-link> -->
      <!-- 编程式 -->
      <li v-for="item in datalist" :key="item.filmId" @click="plClick(item.filmId)">
        {{ item.name }}
      </li>
    </ul>
  </div>
</template>
<script setup>
import axios from 'axios'
import { onMounted, ref } from 'vue'
import { useRouter } from 'vue-router'
const datalist = ref([])
const router = useRouter()
onMounted(async() => {
  const res = await axios({
    url: "https://m.maizuo.com/gateway?cityId=110100&pageNum=1&pageSize=10&type=1&k=3317109",
    headers: {
      'X-Client-Info': '{"a":"3000","ch":"1002","v":"5.2.1","e":"16931924276063063597842433","bc":"110100"}',
      'X-Host': 'mall.film-ticket.film.list'
    }
  })

  datalist.value = res.data.data.films
  console.log(datalist.value)
})

const plClick = (id)=>{
  router.push(`/detail/${id}`)
}
</script>

<style scoped lang="scss">
ul {
  li {
    padding: 10px;
  }
}
</style>

src/VueRouter/views/Detail.vue修改如下:

<template>
  <div>
    <button @click="plBackClick">返回</button>
    detail
  </div>
</template>
<script setup>
import {onMounted} from 'vue'
import { useRoute,useRouter } from 'vue-router'
const route = useRoute()
const router = useRouter()

onMounted(()=>{
  console.log('接收上一个页面传来的参数',route.params.plid)
})
const plBackClick = ()=>{
  router.back() // 返回  router.forward()
}
</script>

src/VueRouter/views/My.vue修改如下:

<template>
  <div>
    my
  </div>
</template>
<script>
export default{
  // 进入路由之前
  beforeRouteEnter(to, from, next){
    let isAuthenticated = localStorage.getItem("token")
    // 如果存在token,直接继续操作
    if(isAuthenticated){
      next()
      // 如果没有token,跳转到登录页
    }else{
      next({name:"Login"})
    }

  }
}
</script>
<script setup>
// VCA不支持onBeforeRouteEnter (这里使用了VCA和VOA结合的方式,但更推荐使用全局路由拦截的方式实现)
import { onBeforeRouteUpdate,onBeforeRouteLeave } from 'vue-router'

onBeforeRouteUpdate(()=>{
  console.log("beforeRouteUpdate")
})

onBeforeRouteLeave(()=>{
  const answer = window.confirm("你确定要离开吗?")
  if(!answer) return false
})
</script>

VCA不支持onBeforeRouteEnter (这里使用了VCA和VOA结合的方式,但更推荐使用全局路由拦截的方式实现)

附:路由分类

  • 后端路由:
    • 理解:value是function,用于处理客户端提交的请求;
    • 工作过程:服务器接收到一个请求时,根据请求路径找到匹配的函数来处理请求,返回响应数据;
  • 前端路由:
    • 理解:value是component,用于展示页面内容;
    • 工作过程:当浏览器的路径改变时,对应的组件就会显示;

文章作者: 老百
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 老百 !
 上一篇
前端框架Vue(六) 前端框架Vue(六)
Vue 是一款用于构建用户界面的 JavaScript 框架。本小节介绍Vuex,是一个专为Vue.js应用程序开发的状态管理模式+库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
2023-08-29
下一篇 
前端框架Vue(四) 前端框架Vue(四)
Vue 是一款用于构建用户界面的 JavaScript 框架。本小节介绍组合式API(Vue-Composition-API)。
2023-08-14
  目录