Vue全家桶--10 Vue-Router路由

10.1 什么是路由

Vue Router 是 Vue.js 官方的路由管理器。它和 Vue.js 的核心深度集成,让构建单页面应用变得非常简单。

通过根据不同的请求路径,切换显示不同组件进行渲染页面。

10.2 基本路由使用

10.2.1 安装路由

npm install vue-router

10.2.2 引入 vue-router.js

<script src="./node_modules/vue/dist/vue.js"></script>
<script src="./node_modules/vue-router/dist/vue-router.js"></script>

10.2.3 HTML 路由切换

<div >
        <div class="header">
            <h1>header</h1>
        </div>
        <div class="left">
            <ul>
                <!-- 方式1:传统方式! -->
                <li><a href="#/foo">foo</a></li>
                <li><a href="#/bar">bar</a></li>
                <!-- 方式2:官方推荐! -->
                <!-- <router-link> 默认会被渲染成一个 `<a>` 标签, -->
                <!-- 通过传入 `to` 属性指定跳转链接,不用像上面加 `#` 号 -->
                <li><router-link to="/foo">Go to Foo</router-link></li>
                <li><router-link to="/bar">Go to Bar</router-link></li>
            </ul>
        </div>
        <div class="main">
            <!-- 路由出口: 路由匹配到的组件将渲染在这里 -->
            <router-view></router-view>
        </div>
    </div>

10.2.4 JS 配置路由

  <script src="./node_modules/vue/dist/vue.js"></script>
    <script src="./node_modules/vue-router/dist/vue-router.js"></script>
    <script>
        //1.定义组件
        const Foo = {
            template:`<div>hello Foo</div>`
        };
        const Bar ={
            template:`<div>hello Bar</div>`
        }

        // 2. 配置路由表:当点击特定的 url 时,显示对应的那个组件。
        const router = new VueRouter({
            routes:[ //配置每个路由映射一个组件
                {path:'/foo',component:Foo},
                {path:'/bar',component:Bar}
            ]
        });

        // 3. 注入路由到实例中
        new Vue({
            el:'#app',
            router //router:router
        });
    </script>

10.3 路由案例实战

10.3.1 修改模板

安装路由/axios

npm install vue-router

npm install axios

10.3.2 News组件

**js函数自调用 ;(function(){})()

**windows 全局属性 window.News={} ,这样就可以全局调用组件,否则在该作用域内,外面无法调用到

;(function(){
    //
    const template=`<!--右边主页面区域-->
    <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
          <div class="header clearfix">
            <nav>
              <ul class="nav nav-pills">
                <li class="active"><a href="#">体育</a></li>
                <li ><a href="#">科技</a></li>
              </ul>
            </nav>
            <hr>
          </div>
          
          <!--体育栏目-->
        <div>
            <ul>
                <li>
                    <a href="#">世界杯开赛啦</a>
                </li>
                <li>
                    <a href="#">NBA开赛倒计时</a>
                </li>
            </ul>
            <!--详情-->
            <div class="jumbotron">
                <h2>世界杯开赛啦</h2>
                <p>世界杯于明晚8点举行开幕式.....</p>
            </div>
        </div>  
          <!--科技栏目-->
        <div>
            <ul >
                <li>
                    <span>5G时代到来了 </span>
                    <button class="btn  btn-default btn-xs">查看(Push)</button>&nbsp;
                    <button class="btn btn-default btn-xs">查看(replace)</button>
                </li>
                <li>
                    <span>互联网大洗牌</span>
                    <button class="btn  btn-default btn-xs">查看(Push)</button>&nbsp;
                    <button class="btn  btn-default btn-xs">查看(replace)</button>
                </li>
            </ul>
            <!--详情-->
            <div class="jumbotron">
                <h2>世界杯开赛啦</h2>
                <p>世界杯于明晚8点举行开幕式.....</p>
            </div>
        </div>         
    </div>`;

    window.News={
        template
    }

})()

10.3.3 About组件

; (function () {
    const template = `<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
        <h1>梦学谷-陪你学习,伴你梦想!</h1>
        <input />
        </div>`;

    window.About={
        template
    }
    
})()

10.3.4 配置路由

新建router.js文件

; (function () {

    window.router = new VueRouter({

        linkActiveClass: 'active',
        routes: [
            { path: '/', component: AppHome },
            { path: '/news', component: News },
            { path: '/about', component: About }
        ]
    })

})()

10.3.5 注入路由到实例中

在 main.js 中的 Vue 实例中引入 router

var vm = new Vue({
    el: '#app',
    // Vue实例中的template选项中引用了组件后,会将这个组件的渲染结果替换掉 #app 标签的元素
    // template: '<app> </app>',
    template:'<app></app>',
    router, // 引用路由配置
    components:{
      App,
    }
  });

10.3.6 配置路由渲染组件出口

在 App.js 中配置

; (function () {
    // 组件模板中,必须包含有且只有一个根元素
    const template = `
    <div>
        <!--头部导航区域-->
        <app-navbar></app-navbar>

        <!--核心区域:分左右两边-->
        <div class="container-fluid">
        <div class="row">

            <!--左边菜单栏区域-->
            <app-left></app-left>

            <!--右边主页面区域: 分上下两个区域
            <app-home>
                <h1 slot="dashboard" class="page-header">{{title}}</h1>
            </app-home>
            -->

            <!-- 配置路由渲染组件出口,也就是右边主页面区域 -->
            <router-view>
                <h1 slot="dashboard" class="page-header">{{title}}</h1>
            </router-view>

        </div>
        </div>
    </div>
    `;

    window.App = {
        template,
        components: {
            AppNavbar,
            AppLeft,
            AppHome
        },
        data() {
            return {
                title: '仪表盘',
            };
        },
    }

})()

10.3.7 修改跳转链接

在 AppLeft.js 中修改跳转链接

; (function () {
window.AppLeaf = {
  template: `<div class="col-sm-3 col-md-2 sidebar">
    <ul class="nav nav-sidebar">
    <li class="active">
      <router-link to="/">首页</router-link>
    </li>
    <li>
      <router-link to="/news">新闻管理</router-link>
    </li>
    <li>
      <router-link to="/about">关于我们</router-link>
    </li>
    </ul>
    </div>`
}
})()

10.3.8 引入js文件

注意先后顺序 

>


<!-- vue-router.js要引入在 vue.js 下方--->


<script src="../node_modules/vue-router/dist/vue-router.js"></script> <script src="../node_modules/axios/dist/axios.js"></script> <script src="./components/AppNavbar.js"></script> <script src="./components/AppLeft.js"></script> <script src="./components/Home/DashBoard.js"></script> <script src="./components/Item.js"></script> <script src="./components/Home/HomeList.js"></script> <script src="./components/Home/AppHome.js"></script> <script src="./components/Home/News.js"></script> <script src="./components/Home/About.js"></script> <script src="./router.js"></script> <script src="./App.js"></script> <script src="./main.js"></script>

10.3.9 启动测试

http://127.0.0.1:5500/vue-08-router/02-bootstrap-ajax-router/index.html#/

10.4 样式匹配--高亮显示导航

10.4.1 tag

<router-link> 默认渲染后生成 <a> 标签。

可在 <router-link> 上使用 tag 属性,指定渲染后生成其他标签。

10.4.2 active-class

<router-link> 渲染后生成标签上默认有 CSS 类名: router-link-active 。

可在 <router-link> 上使用 active-class 属性,指定渲染后生成其他类名。

可以通过路由的构造选项 linkActiveClass 来全局配置,不用在每个<router-link> 使用 active-class 指定生成

的类名

10.4.3 exact

默认情况 下,路由地址 / 、/foo 、/bar 都以 / 开头,它们都会去匹配 / 地址的 CSS 类名。

可在 <router-link> 上使用 exact 属性开启 CSS 类名精确匹配。

<!-- 这个链接只会在地址为 / 的时候被激活, -->
<router-link to="/" exact>

10.4.4 实现高亮显示导航链接

AppLeft.js

;(function(){

    const template=`<div class="col-sm-3 col-md-2 sidebar">
    <ul class="nav nav-sidebar">
    <!--
    router-link 默认渲染出来的是 a 标签,
    如果需要让它渲染出来的 是别的标签,则可以使用 tag 属性指定渲染后的标签

    2. 可以在每个 router-link 上使用 active-class 来激活 CSS 类名 
    或者在 VueRouter 实例中,使用 linkActiveClass 全局配置 CSS 类名 

    3. exact 是精确匹配, 指定在哪个标签上,则这个标签的路径就不会被其他路径模糊匹配
    
    -->
    
      <router-link to="/" tag="li" exact><a>首页</a></router-link>
      <router-link to="/news" tag="li"><a>新闻管理</a></router-link>
      <router-link to="/about" tag="li"><a>关于我们</a></router-link>
     
    </ul>
    
  </div>`;

    window.AppLeft={
        template
    }

})()

router.js

; (function () {

    window.router = new VueRouter({
        
        // 全局配置 router-link 标签生成的 CSS 类名
        linkActiveClass: 'active',
        routes: [
            { path: '/', component: AppHome },
            { path: '/news', component: News },
            { path: '/about', component: About }
        ]
    })

})()

10.5 嵌套路由

10.5.1 子路由组件

10.5.2 配置嵌套路由

{
    path: '/news',
    component: News,
    children: [
        // 当匹配到 /news/sport 请求时,
        // 组件 Sport 会被渲染在 News 组件中的 <router-view> 中
        {
        path: '/news/sport',
        component: Sport
        },
        // 简写方式,等价于 /news/tech 路径,注意前面没有 / ,有 / 就是根目录了
        {
        path: 'tech',
        component: Tech
        },
        //点击新闻管理默认选中 新闻,
        // 就是/news后面没有子路径时, redirect 重定向到 体育
        {
        path: '',
        redirect: '/news/sport'
        }
    ]
},
                

10.5.3 路由跳转链接

<ul class="nav nav-pills">
<router-link to="/news/sport" tag="li">
<a >体育</a>
</router-link>
<router-link to="/news/tech" tag="li">
<a >科技</a>
</router-link>
</ul>
<!--定义路由出口-->
<router-view></router-view>

10.6 嵌套路由案例--新闻管理

10.6.1 封装路由需要的组件 New组件中的Sport和Tech

Sport.js

; (function () {
    //
    const template = `
    <div>
            <ul>
                <li v-for="(sport,index) in sportArr" :key="sport.id">
                    <a href="#" >{{ sport.title }}</a>
                </li>
                
            </ul>
            <!--详情-->
            <div class="jumbotron">
                <h2>世界杯开赛啦</h2>
                <p>世界杯于明晚8点举行开幕式.....</p>
            </div>
    </div> 
    `;

    window.Sport = {
        template,
        data() {
            return {
                sportArr: [],
            };
        },
        created() {
           this.getSportArr();
        },
        methods:{
            getSportArr(){
                axios.get('http://127.0.0.1:5500/vue-08-router/02-bootstrap-ajax-router/db/sport.json').then(response => {
                    console.log(response.data) // 得到返回结果数据
                    this.sportArr = response.data
                }).catch(error => {
                    console.log(error.message)
                })
            }
        }
    }

})()

Tech.js

; (function () {

    //
    const template = `
    <div>
        <ul >
            <li v-for="(tech, index) in techArr" :key="tech.id">
                <span> {{tech.title}} </span>
                <button class="btn btn-default btn-xs">查看(Push)</button>&nbsp;
                <button class="btn btn-default btn-xs">查看(replace)</button>
            </li>
        </ul>
    <!--详情-->
        <div class="jumbotron">
        <h2>世界杯开赛啦</h2>
        <p>世界杯于明晚8点举行开幕式.....</p>
        </div>
    </div>
    `;

    window.Tech = {
        template,
        data() {
            return {
                techArr: [],
            };
        },
        created() {
            this.getTechArr();
        },
        methods: {
            getTechArr() {
                axios.get('http://127.0.0.1:5500/vue-08-router/02-bootstrap-ajax-router/db/tech.json').then(response => {
                    console.log(response.data) // 得到返回结果数据
                    this.techArr = response.data
                }).catch(error => {
                    console.log(error.message)
                })
            }
        }
    }

})()

10.6.2 配置嵌套路由

; (function () {

    window.router = new VueRouter({

        // 全局配置 router-link 标签生成的 CSS 类名
        linkActiveClass: 'active',
        routes: [
            { path: '/', component: AppHome },
            {
                path: '/news', component: News,
                children: [
                    {
                        path: '/news/sport', component: Sport
                    },
                    {
                        // 简写方式,等价于 /news/tech 路径,注意前面没有 / ,有 / 就是根目录了
                        path: 'tech', component: Tech
                    },
                    {
                        //点击新闻管理默认选中 新闻,
                        //就是/news后面没有子路径时, redirect 重定向到 体育
                        path: '', redirect: '/news/sport'
                    }
                ]
            },
            { path: '/about', component: About }
        ]
    })

})()

10.6.3 跳转链接和路由渲染出口

;(function(){
    //
    const template=`<!--右边主页面区域-->
    <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
          <div class="header clearfix">
            <nav>
              <ul class="nav nav-pills">
                <router-link to="/news/sport" tag="li" exact><a>体育</a></router-link>
                <router-link to="/news/tech" tag="li" exact><a>科技</a></router-link>
              </ul>
            </nav>
            <hr>
          </div>

          <!--定义路由出口-->
          <router-view></router-view>
    </div>`;

    window.News={
        template
    }

})()

10.6.4 引入JS

  <script src="./components/Home/Sport.js"></script>
  <script src="./components/Home/Tech.js"></script>

10.7 缓存路由组件与案例

10.7.1 场景与作用

1. 默认情况下,当路由组件被切换后组件实例会销毁,当切换回来时实例会重新创建。

2. 如果可以缓存路由组件实例,切换后不用重新加载数据,可以提高用户体验。

10.7.2 实现缓存路由组件

<keep-alive> 可缓存渲染的路由组件实例

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

10.7.3 demo

上诉的demo中在App.js中添加缓存路由组件

<!-- 配置路由渲染组件出口,也就是右边主页面区域 -->
            <keep-alive>
            <router-view>
                <h1 slot="dashboard" class="page-header">{{title}}</h1>
            </router-view>
            </keep-alive>

10.8 路由组件传递数据

10.8.1 路由传递数据步骤

(1)路由配置

{
  path: '/news/sport',
  component: Sport,
  children: [
    {
      path: '/news/sport/detail/:id', // :id 路径变量占位符
      component: SportDetail
    }
  ]
}

(2)路由跳转路径

<!--
要动态拼接值, 则 to 属性值是 JS 表达式,
要写 JS 表达式, 则要使用 v-bind 方式绑定属性
注意 + 前面有单引号 ''
-->
<router-link :to="'/news/sport/detail/' + sport.id">
{{sport.title}}
</router-link>

(3)在路由组件中读取请求参数

this.$route.params.id

10.8.2 DEMO

配置路由

; (function () {

    window.router = new VueRouter({

        // 全局配置 router-link 标签生成的 CSS 类名
        linkActiveClass: 'active',
        routes: [
            { path: '/', component: AppHome },
            {
                path: '/news', component: News,
                children: [
                    {
                        path: '/news/sport', component: Sport,
                        children: [
                            // :id 路径变量占位符
                            { path: '/news/sport/detail/:id', component: SportDetail }
                        ]
                    },
                    {
                        // 简写方式,等价于 /news/tech 路径,注意前面没有 / ,有 / 就是根目录了
                        path: 'tech', component: Tech,
                        children:[
                            {path: '/news/tech/detail/:id', component: TechDetail }
                        ]
                    },
                    {
                        //点击新闻管理默认选中 新闻,
                        //就是/news后面没有子路径时, redirect 重定向到 体育
                        path: '', redirect: '/news/sport'
                    }
                ]
            },
            { path: '/about', component: About }
        ]
    })

})()

组件指定路径和渲染出口

; (function () {
    //
    const template = `
    <div>
            <ul>
                <li v-for="(sport,index) in sportArr" :key="sport.id">
                    <router-link :to="'/news/sport/detail/'+ sport.id" >
                       {{sport.title}}
                    </router-link>
                </li>
                
            </ul>

            <!--详情-->
            <!--定义路由出口-->
            <router-view></router-view>
    </div> 
    `;

    window.Sport = {
        template,
        data() {
            return {
                sportArr: [],
            };
        },
        created() {
           this.getSportArr();
        },
        methods:{
            getSportArr(){
                axios.get('http://127.0.0.1:5500/vue-08-router/02-bootstrap-ajax-router/db/sport.json').then(response => {
                    console.log(response.data) // 得到返回结果数据
                    this.sportArr = response.data
                }).catch(error => {
                    console.log(error.message)
                })
            }
        }
    }

})()

详情组件

; (function () {

    const template = `
    <div class="jumbotron">
        <h2>{{ sportDetail.title }}</h2>
        <p>{{ sportDetail.content }}</p>
    </div>
    `;

    window.SportDetail = {
        template,
        data() {
            return {
                id: null,
                sportDetail: {}
            };
        },
        created() {
            // 注意:
            // 1. 是 $route , 最后没有 r 字母
            // 2. created 钩子只会调用1次,当切换标题列表的路由时,此钩子不会再次调用,
            // 所以对应 ID 不会被更新, 可以使用 watch 监听 $route 路由的变化。
            this.getItemById();
        },
        methods: {
            getItemById() {
                //将路由路径的变量赋值给本地变量
                this.id = this.$route.params.id - 0;
                var sportItem = [];
                axios.get('http://127.0.0.1:5500/vue-08-router/02-bootstrap-ajax-router/db/sport.json').then(response => {
                    //console.log(response.data) // 得到返回结果数据
                    sportItem = response.data;
                    this.sportDetail = sportItem.find(arr => {
                        return arr.id == this.id;
                    })
                    console.log(this.sportDetail);

                }).catch(error => {
                    console.log(error.message);
                })
            }
        },
        watch: { // watch 是对象,用于监听属性使用
            // 使用 watch 监听 $route 路由的变化,获取 ID 值
            '$route': function () {
                //console.log('$route');
                if ((this.$route.params.id - 0) > 0) { //判断是否是详情页路由
                    this.getItemById()
                }

            }
        }
    }
})()

10.9 编程式路由导航

10.9.1 声明式与编程式路由

**声明式( 直接通过 <a> 标签href指定链接跳转)

<router-link :to="...">

**编程式(采用 js 代码链接跳转,如 localhost.href)

router.push(...)

10.9.2 编程式路由导航 API

this.$router.push(path) 相当于点击路由链接(后退1步,会返回当前路由界面)
this.$router.replace(path) 用新路由替换当前路由(后退1步,不可返回到当前路由界面)
this.$router.back() 后退回上一个记录路由
this.$router.go(n) 参数 n 指定步数
this.$router.go(-1) 后退回上一个记录路由
this.$router.go(1) 向前进下一个记录路由