后端路由

在没有出现ajax技术,前后端还没有分离的时候,往往我们通过浏览器访问一个网址时,后端会返回对应网址的完整页面(html+css+js),也就是说我们看到的页面,在后端就已经长这样了。
那么当一个网站的页面多达几十上百个的时候,对这些网址(路径)的请求,就需要组织管理起来,对指定的请求路径返回指定的网页,这就是后端路由

前端路由

而出现ajax技术以后,前后端迅速的分离并兴起了SPA(单页面富应用),后端主要负责提供接口,前端主要负责页面渲染。我们看到的页面是在前端生成的。
主要流程是这样,由前端通过ajax请求接口,后端返回对应的数据,前端拿到数据以后,再结合对应模版跑一些js来生成dom,因此页面的呈现变成了前端的事情,由前端来决定哪个请求路径显示哪个页面。同样当页面逐渐变多的时候我们也需要管理,这就成了前端路由

SPA单页面富应用

在访问SPA时,会直接请求下来全部的前端文件,然后根据路由选择性的渲染呈现。
因此,在访问其他路由时,网页不会刷新,因为所有的前端文件(html+css+js)在第一次时已经请求下来了不需要再请求(前端使用Ajax与后端交互的请求例外),前端会监听路由的变化,并且切换页面的显示

那么怎么做到路由改变而浏览器不刷新呢?

// 方法一:改变location.hash
location.hash = 'foo'

在这里插入图片描述

// 方法二:改变history对象
// 向栈顶推入记录
history.pushState({}, '', 'home')
// 或者
// 替换栈顶记录
history.replaceState({}, '', 'home')

在这里插入图片描述
这两种方法都不会使浏览器刷新(页面没有重新加载)

// 扩充history的其他方法
history.back() 	// 后退一个页面
// 相当于
history.go(-1)

history.forward() // 前进一个页面
// 相当于
history.go(1)

Vue-router

安装
npm install --save vue-router
	// 目前使用的版本
    "vue": "^2.5.2",
    "vue-router": "^3.0.1"
起步
1.创建router对象

按照使用习惯,建立以下文件结构

/src
	/router
		-index.js

在这里插入图片描述
然后在index.js中配置路由相关信息

import VueRouter from 'vue-router'
import Vue from 'vue'

Vue.use(VueRouter)

export default new VueRouter({
    // 配置路由和组件之间的映射关系
    routes: [

    ]
})

接着在main.js中导入使用

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

new Vue({
  el: '#app',
  router,
  render: h => h(App)
})
2. 配置映射关系
  • 为了可以映射关系,我们先创建两个组件,结构如下
/src
	/components
		/about
			-About.vue
		/home
			-Home.vue

在这里插入图片描述
内容如下

<!-- Home.vue -->
<template>
  <div>
      <h1>我是Home</h1>
  </div>
</template>
<script>
export default {
  name: 'Home',
}
</script>
<!-- About.vue -->
<template>
  <div>
      <h1>我是About</h1>
  </div>
</template>
<script>
export default {
  name: 'About',
}
</script>
  • 配置映射规则:
import VueRouter from 'vue-router'
import Vue from 'vue'
import Home from '../components/home/Home.vue'
import About from '../components/about/about.vue'

Vue.use(VueRouter)

export default new VueRouter({
    // 配置路由和组件之间的映射关系
    routes: [
        {
            path: '/home',
            component: Home
        },
        {
            path: '/about',
            component: About
        }
    ]
})

App.vue内容如下

<template>
  <div id="app">
    <router-link to="/home">home</router-link>
    <!--
	<router-link :to="{path: '/home'}">home</router-link>
	-->
    <router-link to="/about">about</router-link>
    <router-view></router-view>
  </div>
</template>

<script>

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

这里看到App.vue中使用了两个新标签,这两个标签是由vue-router全局注册的。因此我们可以在模板内任何地方使用这两个标签
router-link用来跳转路由,切换显示不同的组件
router-view用来占位,表示路由对应组件应该显示在什么位置

<!--
 router-link的tag属性
 router-link标签默认被转换成a标签,当需要转换成其他标签时,可以在tag属性中指定
-->
<router-link to="/home" tag="button">首页</router-link>

<!--
 router-link的replace属性
 router-link标签默认使用的history.pushState(),意味着路由记录可以回退,
 如果不想保存之前的路由记录,可以指定replace属性
-->
<router-link to="/home"  replace>首页</router-link>

<!--
 router-link的class属性
 router-link标签对应的路由匹配成功时,会自动设置一个router-link-active的class
 也可以修改这个class的名称,例如下面,将其修改为active
-->
<router-link to="/home" active-class="active">首页</router-link>
  • 配置重定向
// router/index.js 部分内容
    routes: [
    	{
    		// 当用户访问根路由时,会重定向到'/home',进而展示 Home组件
    		path: '/',
    		redirect: '/home'
    	},
        {
            path: '/home',
            component: Home
        },
        {
            path: '/about',
            component: About
        }
    ]
  • 配置路由模式
    前面提到过,改变路由而不刷新网页有两种方法,一种是hash另一种是history,因此路由的模式也分为这两种,其中hash是默认值
// router/index.js 部分内容
export default new VueRouter({
	// 设置路由模式为history的方式(这种给人的感觉更好)
    mode: 'history',
    routes: [
		...
    ]
})
  • 配置router-link激活样式
// router/index.js 部分内容
export default new VueRouter({
	// 全局修改,不同于在router-link上添加active-class属性的局部修改
	// 通常不会去修改这个类名
	linkActiveClass: 'acitve',
    routes: [
		...
    ]
})
  • 配置动态路由
    routes: [
        {
        	// userId是动态的,并且可以通过route.params.userId取得
            path: '/user/:userId',
            component: User
        }
    ]
  • 配置嵌套路由
        {
            path: '/home',
            component: Home,
            children: [
            	// 两种写法效果一致
                {
                	// 加 / 表示基于根路由开始
                    path: '/home/news',
                    component: HomeNews
                },
                {
                	// 不加 / 表示基于父路由开始
                    path: 'message',
                    component: HomeMessage
                }
            ]
        },
使用
  • 通过代码更改路由

methods: {
	homeClick() {
		// 保留路由记录
		this.$router.push('/home')
		// 或 不保留路由记录
		this.$router.replace('/home')
	}
}
  • 获取params参数
// 当配置了动态路由,就可以在当前的route对象获取params参数
// 注意是route不是router
// router是/router/index.js中暴露出来的对象,在所有路由页面都可以访问
// route是路由页面自有的对象,因此获取特定页面的params应该是在ruote上取
<template>
  <div>
      <h1>User {{user}}</h1>
  </div>
</template>

<script>

export default {
  name: 'About',
  computed: {
      user() {
          return this.$route.params.userId
      }
  }
}
</script>

  • 获取query参数
<!-- App.vue -->
<template>
  <div id="app">
  	<!-- 传递query参数 -->
    <router-link to="/home?name=dd&age=18">home</router-link>
    <!--
    <router-link :to="{path: '/home', query: {name:'dd', age: 18}}">home</router-link>
	-->
    <router-link to="/about">about</router-link>
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: 'App',
}
</script>
<!-- Home.vue -->
<template>
  <div>
    <h1>我是Home</h1>
    <router-link to="/home/news">新闻</router-link>
    <router-link to="/home/message">消息</router-link>
    <!-- 通过route使用 -->
    <div>{{$route.query.name}}</div>
    <div>{{$route.query.age}}</div>
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: 'Home',
}
</script>
路由懒加载
  • 需求:如果将所有的资源全部打包在一个js里面,那么首次访问网页的请求将非常慢,因此希望将资源分块,请求对应路由时,再去获取对应块的资源

  • 打包好的目录结构
    在这里插入图片描述
    能够看到vue-cli的webpack配置,是将打包的文件分块的(默认情况下,就一个入口一个出口,将所有资源全打包在bundle.js里)

    // js文件前缀解释
    app: 指程序员自己写的代码
    manifest: 底层支撑代码,比如模块化时用到的 __webpack_require__函数
    vendor: 指第三方的代码,比如vue
    

    从上面的分析可以得到,manifest和vendor代码量基本是固定的,并且也不可能部分请求过来,而app,则会随着业务逻辑的增加动态增长,同时可以按需请求,所以要对app进行分块。

  • 使用路由懒加载

// router/index.js
import VueRouter from 'vue-router'
import Vue from 'vue'

// 重要的是按需导入的写法
const Home = () => import('../components/home/Home.vue')
// AMD风格的导入
// const Home = resolve => require(['../components/home/Home.vue'], resolve)
const About = () => import('../components/about/About.vue')
const User = () => import('../components/user/User.vue')

Vue.use(VueRouter)

export default new VueRouter({
    mode: 'history',
    routes: [
        {
            path: '/',
            redirect: '/home'
        },
        {
            path: '/home',
            component: Home
        },
        {
            path: '/about',
            component: About
        },
        {
            path: '/user/:userId',
            component: User
        }

    ]
})

再看打包后的目录结构
在这里插入图片描述
三个按需导入的路由,分别对应新增的三个js文件(.map是对应的辅助文件)
大伙可能会有疑问,app不是被分块了么,为什么还有app存在,因为被分块的是路由,有一些组件比如App.vue是一开始就要加载的,并不是按需加载,因此app并没有被完全分块

全局导航守卫

需求:需要在路由跳转时,做一些中间操作

// router/index.js 部分代码
const router =  new VueRouter({
    mode: 'history',
    routes: [
    	{
			path: '/home',
			component: Home,
			// 可以在meta中存入一些数据,以便在route中使用
			meta: {
				name: 'dd',
				age: 18
			}
		}
		...
    ]
})
// 前置钩子(hook)
router.beforeEach((to, from, next)=>{
    // to: 目的route
    // from: 源route
    // next: 推进路由的函数,必须调用,否则路由跳转会失败

    // 在跳转之间做一些逻辑

    next()

})

// 后置钩子
// 因为是后置(路由切换完成才被调用),因此不需要再调用 next()
router.afterEach((to, from)=>{
    // ...
})

export default router

另外呢还有路由独享守卫

// router/index.js 部分代码
const router =  new VueRouter({
    mode: 'history',
    routes: [
    	{
			path: '/home',
			component: Home,
			meta: {
				name: 'dd',
				age: 18
			},
			beforeEnter: (to, from, next) => {
				// ...
				next()
			}
		}
		...
    ]
})
保持路由组件存活

需求:当路由组件切换时会被销毁,切换回来后再重新创建,这样导致之前在这个路由组件上做的操作丢失,这时希望保存这个路由组件的状态,不要在切换的时候被销毁

<!-- 在router-view标签外嵌套keep-alive标签,这时显示在其中的路由组件就不会因切换而销毁了 -->
<keep-alive>
	<router-view/>
</keep-alive>

有时,我们想某个组件在切换时销毁,但他又被嵌套在了keep-alive标签内,可以使用exclude属性排除

<keep-alive>
	<!-- Home, User 为组件的名字,在.vue文件中,由name属性指定的 -->
	<router-view exclude="Home,User"/>
</keep-alive>

类似的,还有include属性,效果相反

Logo

技术共进,成长同行——讯飞AI开发者社区

更多推荐