「锋利的Vue.js」前端路由

239 阅读4分钟

以下内容的代码:前端路由

前端路由的核心:通过一定方式改变URL,但是页面不进行整体的刷新,还在当前页面

实现方式有两种:

  • 通过改变URL的hash
  • 通过HTML5的history接口实现

通过改变URL的hash
URL的hash也就是锚点(#),通过改变location.hash来改变href属性,但是页面不发生刷新,如下图所示:

通过改变URL的hash

通过HTML5的history接口实现
history是HTNL5新增的接口,使用以下几个方法改变URL而不刷新网页

  • history.pushState()、history.replaceState()
  • history.back()、history.go(-1)
  • history.forward()、histroy.go(1)
    通过HTML5的history接口实现

初识vue-router

vue-router是基于路由组件的,将路径(url)和组件映射起来。在vue-router的单页面中路径的改变其实就是组件之间的切换。

安装使用vue-router

安装

npm install vue-router --save

使用vue-router分为以下几步:

第一步:创建router.js文件,导入vuevue-router,调用Vue.use(val)

import Vue from 'vue'
import vueRouter from 'vue-router'

Vue.use(vueRouter);

第二步:创建组件foo.vue和about.vue,创建路由实例,并传入路由映射表

import about from './components/about.vue'
import foo from './components/foo.vue'

const routes = [{
        path: '/about',
        component: about
    },
    {
        path: '/foo',
        component: foo
    }
]

const router = new vueRouter({
    routes,
})

export default router;

特别注意,在这一步中不要将routes写成routers

第三步:将路由传入vue实例

import router from './router.js'

Vue.config.productionTip = false

new Vue({
    render: h => h(App),
    router
}).$mount('#app')

第四步:使用路由

<router-link to="/about" class="routerLink">about</router-link>
<router-link to="/foo" class="routerLink">foo</router-link>

<router-view></router-view>

配置路由

路由重定向

进入页面时显示想要展示组件内容,重定向到某个路由path即可

const routes = [{
        path: '/',
        redirect: '/about'
    },
    {
        path: '/about',
        component: about
    },
    {
        path: '/foo',
        component: foo
    }
]

路由模式

默认情况下,url路径使用的是URL的hash方式

如果使用HTML5的history模式,需要在创建router实例时进行配置即可

const router = new vueRouter({
    routes,
    mode: 'history'
})

router-link几个属性

在上面的例子中,使用了to属性,进行路由的跳转,下面还有几个:

  • tag
    用于指定router-link渲染成什么元素,默认渲染成<a></a>
    用法:<router-link to="/about" class="routerLink" tag='button'>about</router-link>
  • replace
    使用这个属性,内部调用history.replaceState()
    用法:<router-link to="/foo" class="routerLink" replace>foo</router-link>
  • active-class
    当对应的路由匹配成功时,会自动给当前元素设置一个 router-link-active的class,可以使用linkActiveClass属性来修改这个默认的class的名字 ,下面更改为: active
    const router = new vueRouter({
        routes,
        // 修改激活路由的class
        linkActiveClass: 'active',
    })
    

push()

在业务中,可能需要在js代码中控制路由的跳转,就会使用push(val)函数

<!--tempalte-->
<button @click="jumpToFoo">跳转到foo</button>  

<!--script-->
methods: {
    jumpToFoo() {
      this.$router.push('/foo');
    }
 }

路由懒加载

在路由表中的很组件,这些组件最终会被打包到一个js中,这个js文件会相对比较大。如果我们一次性从服务器请求这个文件,可能会花费一定的时间,甚至在用户端出现短暂的空白,为了缓解这一现象,可以使用路由懒加载

路由懒加载主要做了两件事:

  1. 将路由表中对应的组件分别打包成一个个js文件
  2. 只有路由组件被访问时,才会去加载该组件对应的js文件

懒加载实现的方式:

  • 结合Vue的异步组件和webpack的代码分析
    const foo = resolve => {
        require.ensure(['./components/foo.vue'], () => { resolve(require('./components/foo.vue')) })
    };
    
  • AMD写法
    const foo = resolve => require(['./components/foo.vue'], resolve);
    
  • ES6写法
    const foo = () =>
       import ('./components/foo.vue')
    

动态路由

在某些情况下,配置路由表时path可能是不确定的,可能根据后台数据动态渲染的,这时就要用到动态路由

首先,新建组件user.vue,在配置路由时,将不确定的地方使用:展位字符串的方式

<!--加载组件-->
const user = () =>
    import ('./components/user.vue')

<!--路由配置-->
{
    path: '/user:id',
    component: user
}

使用路由

 <!-- 动态路由 -->
 <router-link :to="'/user'+ID" class="routerLink">user</router-link>
 
 data() {
    return {
      ID: '12345'
    }
 }

user.vue中显示路由的动态参数

<div>
    <h2>这是uesr组件</h2>

    <!-- 获取动态路由参数 -->
    <h2>{{$route.params.id}}</h2>
</div>

路由嵌套

因业务需要,在路由中可能还要需要路由,这就形成了路由嵌套,在路由中嵌套一些路由。下面我们创建两个组件:new.vuemessage.vue,将这个组件嵌套在about中,对应的路由是:/about/new/about/message

使用children属性,在路由映射表中配置相应的子路由

<!--加载组件-->
const about_new = () =>
    import ('./components/about/new.vue')
const about_message = () =>
    import ('./components/about/message.vue')

<!--配置路由-->
{
    path: '/about',
    component: about,
    children: [{
            path: '',
            redirect: 'new'

        },
        {
            path: 'new',
            component: about_new
        },
        {
            path: 'message',
            component: about_message
        }
    ]
},

注意,子路由的path不带/

在about组件中,使用路由

<div>
    <h2>我是about组件</h2>  

    <router-link to="/about/message" class="routerLink">about/message</router-link>
    <router-link to="/about/new" class="routerLink">about/new</router-link>

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

路由传参

通过路由传递参数,有两种方式:

  • 动态路由方式(params)
  • query方式

通过动态路由的方式传递方式可以参考:动态路由

query方式
我们在这里通过路由,传递给message.vue一些参数:

<!--about.vue-->
<router-link :to="{path: '/about/message', query: {type:'开会', ID: 189}}"  
class="routerLink">about/message</router-link>  

<!--message.vue获取参数-->
<div> query:{{$route.query }}</div>

也可以在js中使用push(val)方法传递参数:

<!--2.javascript方式-->
methods:{
  toMessage(){
    this.$router.push({
      path:'/about/message',
      query:{type:'开会', ID: 189}
    })
  }
}

$router 和 $route的区别
$router 是VueRouter实例,想要导航到不同的URL,则使用$router.push方法
$route 是当前router跳转的路由 可以获取name 、path 、query、params等属性值

导航守卫

vue-router提供的导航守卫主要用于监听路由的进入和离开,在路由改变前和改变后分别会触发beforeEachafterEach钩子函数。

在一个SPA应用中如何改变网页上的标题
网页标题是通过<title>来显示,但是SPA只有一个固定的HTML页面,切换不同的组件,标题并不会改变。那么比较容易想的办法就是在每个路由对应的vue组件中修改,通过mounted周期函数,通过调用JavaScript的window.document.title = "标题"的方法实现

mounted() {
    window.document.title = "关于";
}

这种方法,在很多组件都要写,很分散,也不利于维护代码

我们可以使用beforeEach()函数来实现
首先,在router.js利用meta定义组件对应的标题

{
    path: '/foo',
    component: foo,
    meta: {
        title: 'foo'
    },
}

然后,在beforeEach()中改变标题

/**
 * 路由改变之前
 * to 即将进入的路由
 * from 即将离开的路由
 * next 调用这个方法,进入下一个钩子函数
 */
router.beforeEach((to, from, next) => {
    window.document.title = to.meta.title;
    next();
})

上边使用的是全局导航守卫,导航守卫总共有三种

  • 全局导航守卫
  • 路由独享守卫
  • 组件守卫

具体用法可以参考官网:导航守卫

keep-alive遇见vue-router

keep-alive是vue的一个组件,目的在于

  • 缓存组件,避免重复创建和销毁
  • 保存组件的浏览状态

用法:

<keep-alive>
  <router-view></router-view>
</keep-alive>
    

exclude和include
include:字符串或正则表达,只有匹配的组件会被缓存; exclude:字符串或正则表达,任何匹配的组件都不会被缓存

保持子路由的状态

如下图所示,当我们点击foo时,再返回about路由时,有个需求是:还展示/about/message

保持子路由的状态

activated(){
  console.log("--activated-");
  this.$router.push(this.path);

},

// 组件守卫的方法
beforeRouteLeave (to, from, next) {
    this.path = this.$route.path;
    next();
}