Vue学习-路由router

Vue学习-路由router

认识路由

什么是前端渲染,什么是后端渲染?

后端渲染阶段

后端渲染: JSP – java server page

后台收到请求之后,html+css+java, java代码作用是从数据库中读取数据,并将它动态的放在页面中,页面在服务器端已经渲染好了,然后直接渲染好的页面返回给前端(html+css)

后端路由:后台处理URL和页面之间的映射关系

后端路由的缺点:

  • 整个页面的模块由后端人员来编写和维护的
  • 前端开发人员如果开发页面,需要通过PHP,Java等语言来编写页面代码
  • 通常情况下HTML代码和数据以及对应的逻辑会混在一起,编写和维护都是非常糟糕的事情

前后端分离阶段

前后端分离.png

输入jd.com,先去静态资源浏览器请求静态资源(html+css+js),当浏览器引擎执行JS代码时,会触发API请求,会去后台的API服务器上请求数据。

前端渲染:浏览器中显示的网页中的大部分内容,都是由前端写的JS代码,在浏览器中执行,最终渲染出来的网页。

  • 随着Ajax的出现,由了前后端分离的开发模式
  • 后端只提供API来返回数据,前端通过Ajax获取数据,并且可以通过JS将数据渲染到页面中
  • 这样做最大的优点就是前后端责任的清晰,后端专注于数据上,前端专注于交互和可视化上
  • 并且当移动端出现后,后端不需要进行任何处理,依然使用之前的一套API即可

前端路由阶段(单页面富应用阶段)

前后端分离的基础之上,加上前端路由

SPA应用:整个网站只有一个HTML页面,一套html+css+js

前端路由:页面与静态资源的映射关系由前端来管理,改变URL,页面不会整体刷新

url的hash和HTML5的history

URL中的hash

  • URL中的hash也就是锚点(#),本质上是改变window.location的href属性
  • 可以通过直接赋值location.hash来改变href,但是页面不会发生刷新
  • location.hash = 'aaa'

HTML5中的history

  • history.pushState({}, '', 'home') 压入
  • history.replaceState({}, '', 'home') 替换
  • replace不能返回之前的页面(替换了), push可以返回到之前的页面
  • history.go() 参数: -1: history.back() 1: history.forward()

Vue-router基本使用

安装vue-router

npm install vue-router --save
复制代码

在模块化工程中使用vue-router(插件,可以用过Vue.use()来使用路由功能)

  • 导入路由对象,并且调用Vue.use(VueRouter)
  • 创建路由实例,并且传入路由映射配置
  • Vue实例中挂载创建的路由实例

框架搭建

import Vue from 'vue'
// 导入路由对象,并且调用Vue.use(VueRouter)
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'

Vue.use(VueRouter)

// 创建路由实例,并且传入路由映射配置
const routes = [
    {
        path: '/',
        name: 'Home',
        component: Home
    },
    {
        path: '/about',
        name: 'About',
        // route level code-splitting
        // this generates a separate chunk (about.[hash].js) for this route
        // which is lazy-loaded when the route is visited.
        component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
    }
]

const router = new VueRouter({
    mode: 'history',
    base: process.env.BASE_URL,
    routes
})

// 先导出,然后在main.js中,Vue实例中挂载创建的路由实例
export default router

复制代码

配置路由

  • 创建路由组件
  • 配置路由映射:组件和路径映射关系
  • 使用路由:通过和
{
    path: '/',
        name: 'Home',
            component: Home
},
    {
        path: '/about',
            name: 'About',
                // route level code-splitting
                // this generates a separate chunk (about.[hash].js) for this route
                // which is lazy-loaded when the route is visited.
                // 路由懒加载
                component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
    }
复制代码
<router-link to="/home">首页</router-link>
<router-link to="/about">关于</router-link>
<router-view></router-view>
复制代码

路由的默认值和修改为history模式

路由的默认值

{ path: '/', redirect: 'home' }
复制代码

history模式

mode: history

const router = new VueRouter({
    mode: 'history',
    base: process.env.BASE_URL,
    routes
})
复制代码

router-link的其他属性

tag属性

默认渲染为a标签,想要渲染为其他元素,使用tag属性

<router-link to="/home" tag="button">首页</router-link>
<router-link to="/about" tag="button">关于</router-link>
复制代码

replace属性

默认使用pushState,如果想使用replaceState,使用replace属性

<router-link to="/home" tag="button" replace>首页</router-link>
<router-link to="/about" tag="button" replace>关于</router-link>
复制代码

.router-link-active

选中的router-link会自动具有.router-link-active类,可以直接通过该类设置样式,比如选中的字体变为红色

.router-link-active {
	color: red;
}
复制代码

.router-link-active简写模式

<router-link to="/home" tag="button" replace active-class="acive">首页</router-link>
<router-link to="/about" tag="button" replace active-class="acive">关于</router-link>
复制代码
.active {
	color: red;
}
复制代码

统一修改active-class

const router = new VueRouter({
    mode: 'history',
    base: process.env.BASE_URL,
    routes,
    linkActiveClass: 'active'
})
复制代码

通过代码跳转路由

this.$router.push('/home');
复制代码
this.$router.replace('/home')
复制代码

动态路由

<router-link to="/home">首页</router-link>
<router-link to="/about">关于</router-link>
<router-link to="/user/zhangsan">用户</router-link>
<router-link :to="'/user/' + userId">用户</router-link>
复制代码
{ path: '/user/:userId', component: User }
复制代码

或者

this.$router.push('/user/' + this.userId)
复制代码

User.vue中获取到userId

{{ $route.params.userId }}
或者
this.$route.params.userId
// $route: 处于活跃中的路由
// $router: 整个路由对象
复制代码

路由的懒加载

  • 当打包构建应用时,Javascript包会变得非常大,影响页面加载

  • 如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样跟高效(一个路由打包一个JS文件)

  • 懒加载:用到的时候再加载

// 推荐的方式
const routes = [
	{ path: '/home', component: () => import('../components/Home') },
    { path: '/about', component: () => import('../components/About') }
]
复制代码

或者

const About = resolve => require(['../components/About.vue'], resolve);
复制代码

Vue-router嵌套路由

认识路由嵌套

  • 比如在home页面中,我们希望通过/home/news和/home/message访问一些内容
  • 一个路径映射一个组件,访问这两个路径也会分别渲染两个组件

创建嵌套路由的步骤

  • 创建对应的子组件,并且在路由映射中配置对应的子路由
  • 在组件内部使用标签
{ path:  '/home', component: Home, children: [
    { path: 'news', component: HomeNews },
    { path: 'message', component: HomeMessage },
    { path: '/', redirect: 'news' }
] }
复制代码
<router-link to="/home/news">新闻</router-link>
<router-link to="/home/message">消息</router-link>
复制代码

Vue-router参数传递

从一个页面跳转到另一个页面,希望传递一些消息

方法1: 动态路由中讲到的方法可以传递参数

  • 配置路由格式: /router/:id
  • 传递的方式: 在path后面跟上对应的值
  • 传递后形成的路径: /route/123, /route/abc

方法2: query方式

  • 配置路由格式:/router,也就是普通配置
  • 传递的方式:对象中使用query的key作为传递方式
  • 传递后形成的路径: /router?id=123 , /router?id=abc
<router-link :to="{ path: '/profile', query: { name: 'xx', age: 18, height: 1.88 } }"></router-link>
复制代码

或者

this.$router.push({
	path: '/profile',
    query: {
        name: 'xx',
        age: 18,
        height: 1.88
    }
})
复制代码
{{ $route.query.name }}
或者
this.$route.query.name
复制代码

Vue-router: router和route的由来

所有组件(.vue)都继承着vue的原型,vue原型上又挂载了routergrouterg和route

Vue.prototype.$router = {
	...
}
Vue.prototype.$route = {
    ...
}
或者
Object.defineProperty(Vue.prototype, '$router', ...);
Object.defineProperty(Vue.prototype, '$route', ...);
复制代码

this.$router就是vueRouter类的一个实例

this.$route是处于活跃的路由

Vue-router导航守卫

需求:

在一个SPA应用中,如何改变网页的标题?

  • 网页标题是通过<title>来显示的,但是SPA只有一个固定的HTML,切换不同的页面时,标题并不会改变
  • 可以通过Javascript来修改<title>的内容,window.document.title="新的标题"
  • 那么在VUE项目中,如何实现?

方案1: 利用生命周期函数created实现

方案2:利用全局导航守卫 (前置钩子,前置守卫)

router.beforeEach((to, from, next) => {
	// 从from跳转到to
	document.title = to.matched[0].meta.title;
    next();
})
复制代码
{ path: '/home', component: Home, meta: { title: '首页' } }
复制代码

后置钩子:

afterEach, 已经跳转完了

router.afterEach((to, from) => {
	
})
复制代码

路由独享守卫

const router = new VueRouter({
	routes: [{
        path: '/foo',
        component: Foo,
        beforeEnter: (to, from, next) => {
            
        }
    }]
})
复制代码

keep-alive

  • keep-alive是Vue内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染
  • router-view也是一个组件,如果直接被包在keep-alive中,所有路径匹配的到的视图组件都会被缓存
<keep-alive>
    <router-view></router-view>
</keep-alive>
复制代码
  • activated()deactivated()钩子函数只有在被keep-alive包裹的时候才会生效

  • 如果希望组件中的某一个被频繁的创建和销毁

    这里 Profile,User逗号后面不要加空格

    <keep-alive exclude="Profile,User">
        <router-view></router-view>
    </keep-alive>
    复制代码

路径别名

vue-cli3中可以使用@代替src

案例

App.vue

<template>
  <div id="app">
    <router-view></router-view>
    <tab-bar>
      <tab-bar-item path="/home" activeColor="red">
        <img src="https://juejin.cn/post/assets/image/tabbar/首页.svg"  slot="item-icon">
        <img src="https://juejin.cn/post/assets/image/tabbar/首页_active.svg"  slot="item-icon-active">
        <div slot="item-text">首页</div>
      </tab-bar-item>
      <tab-bar-item path="/category" activeColor="skyblue">
        <img src="https://juejin.cn/post/assets/image/tabbar/分类.svg"  slot="item-icon">
        <img src="https://juejin.cn/post/assets/image/tabbar/分类_active.svg"  slot="item-icon-active">
        <div slot="item-text">分类</div>
      </tab-bar-item>
      <tab-bar-item path="/cart" activeColor="green">
        <img src="https://juejin.cn/post/assets/image/tabbar/购物车.svg"  slot="item-icon">
        <img src="https://juejin.cn/post/assets/image/tabbar/购物车_active.svg"  slot="item-icon-active">
        <div slot="item-text">购物车</div>
      </tab-bar-item>
      <tab-bar-item path="/profile">
        <img src="https://juejin.cn/post/assets/image/tabbar/我的.svg"  slot="item-icon">
        <img src="https://juejin.cn/post/assets/image/tabbar/我的_active.svg"  slot="item-icon-active">
        <div slot="item-text">我的</div>
      </tab-bar-item>
    </tab-bar>
  </div>
</template>

<script>
import TabBar from "./components/tabbar/TabBar";
import TabBarItem from "./components/tabbar/TabBarItem";

export default {
  name: 'App',
  components: {
    TabBar,
    TabBarItem
  }
}
</script>

<style lang="css">
@import './assets/css/base.css';
</style>
复制代码

TabBar.vue

<!--  -->
<template>
   <div id="tab-bar">
       <slot></slot>
   </div>
</template>

<script>
    export default {
        name: 'TabBar',
        data () {
            return {
            };
        }
    }
</script>
<style lang='css' scoped>
    #tab-bar {
        display: flex;
        background-color: #f6f6f6;
        position: fixed;
        left: 0;
        right: 0;
        bottom: 0;
        /* 阴影 */
        box-shadow:  0px -1px 1px rgba(100, 100, 100, 0.2);
    }
</style>
复制代码

TabBarItem.vue

<!--  -->
<template>
<div class="tab-bar-item" @click="itemClick">
    <div v-if="!isActive"><slot name="item-icon"></slot></div>
    <div v-else><slot name="item-icon-active"></slot></div>
    <div :style="activeStyle">
        <slot name="item-text"></slot>
    </div>
</div>
</template>

<script>
    export default {
        name: 'TabBarItem',
        props: {
            path: String,
            activeColor: {
                type: String,
                default: 'red'
            }
        },
        data() {
            return {
            };
        },
        methods: {
            itemClick() {
                this.$router.push(this.path).catch(err => {});
            }
        },
        computed: {
            isActive() {
                return this.$route.path.indexOf(this.path) !== -1;
            },
            activeStyle() {
                return this.isActive ? { color: this.activeColor } : {};
            }
        }
    };
</script>
<style lang='css' scoped>
    .tab-bar-item {
        flex: 1;
        text-align: center;
        height: 49px;
        font-size: 14px;
    }

    .tab-bar-item img {
        width: 24px;
        height: 24px;
        margin-top: 3px;
        vertical-align: middle;
    }

</style>
复制代码

效果图

案例.png

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享