前端路由vue-router介绍

一、前端路由vue-router介绍

  Vue-Router 是 Vue.js 官方的路由管理器。它和 Vue.js 的核心深度集成,让构建单页面应用变得易如反掌。包含的功能有:

  • 嵌套的路由/视图表
  • 模块化的、基于组件的路由配置
  • 路由参数、查询、通配符
  • 基于 Vue.js 过渡系统的视图过渡效果
  • 细粒度的导航控制
  • 带有自动激活的 CSS class 的链接
  • HTML5 历史模式或 hash 模式,在 IE9 中自动降级
  • 自定义的滚动条行为

  vue vue-router 主要是用来做 单页面应用(Single Page Application)

1、为什么要做单页面应用?

(1)传统的开发方式

  url改变后,立马发送请求,响应整个页面,有可能资源过多,传统的开发会让前端的页面出现“白屏”。

  用户体验不好。

(2)SPA单页面应用

  SPA:Single Page Application

  锚点值改变后,不会立刻发送请求,而是在某个合适的时机,发送ajax请求,局部改变页面中的数据。

  页面不立刻跳转,用户体验好。

 2、前端路由的实现原理

  前端路由:

     1.锚点值监视; 2.ajax获取动态的数据; 3.核心点是锚点值的改变;

  前端中的 vue/react/angular 都很适合做单页面应用。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <a href="#/login">登录页面</a>
    <a href="#/register">注册页面</a>
    <div id="app">
        
    </div>
    <script type="text/javascript">
        // 获取div
        var oDiv = document.getElementById('app');

        window.onhashchange = function () {
            console.log(location.hash);
            // 根据不同的锚点值,对页面不同的切换
            switch (location.hash) {
                case '#/login':
                    oDiv.innerHTML = '<h2>登录页面</h2>';
                    break;
                case '#/register':
                    oDiv.innerHTML = '<h2>注册页面</h2>';
                    break;
                default:
                    // statements_def
                    break;
            }
        }
    </script>
</body>
</html>

(1)window.onhashchange介绍

  当一个窗口的 hash (URL中'#'后面的部分)改变时就会触发 onhashchange 事件。

  onhashchange 事件在当前url的锚点部分(以'#'号为开始)发生改变时触发。

  锚部分的实例:指定当前URL为http://www.example.com/test.htm#part2 - 这个 URL 中的锚部分为 #part2。

(2)onhashchange调用事件方法

3、根据不同锚点切换页面效果

(1)初始页面显示如下:

  

(2)点击登录页面

  

(3)点击注册页面

  

二、vue-router使用 

1、用NPM下载安装vue-router

(venv) MacBook-Pro:vue_study hqs$ cd 03-vue-router/
(venv) MacBook-Pro:03-vue-router hqs$ ls
01-前端路由实现原理.html                vue.js
(venv) MacBook-Pro:03-vue-router hqs$ npm init --yes
Wrote to /Users/hqs/PycharmProjects/vue_study/03-vue-router/package.json:

{
  "name": "03-vue-router",
  "version": "1.0.0",
  "description": "",
  "main": "vue.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

(venv) MacBook-Pro:03-vue-router hqs$ npm install vue-router -S
03-vue-router@1.0.0 /Users/hqs/PycharmProjects/vue_study/03-vue-router
└── vue-router@3.0.1 

npm WARN 03-vue-router@1.0.0 No description
npm WARN 03-vue-router@1.0.0 No repository field.

  查看项目文件目录:

  

2、vue-router应用实例

<body>
    <div id="app"></div>
    <script type="text/javascript" src="vue.js"></script>
    <!--1.引入vue-router的对象-->
    <script type="text/javascript" src="./node_modules/vue-router/dist/vue-router.js"></script>
    <!--全局的VueRouter对象 vue-router还提供了两个全局的组件router-link / router-view-->
    <script type="text/javascript">
        // 2.让Vue使用该VueRouter创建
        Vue.use(VueRouter);

        var Login = {      // 组件创建
            template:`
                <div>登录页面</div>
            `
        };

        var Register = {   // 组件创建
            template:`
                <div>注册页面</div>
            `
        };

        // 3.创建一个路由对象
        var router = new VueRouter({
            // 配置路由对象
            routes:[
                {
                    path:'/login',   // login路由
                    component:Login
                },
                {
                    path:'/register',
                    component:Register
                }
            ]
        });

        var App = {   // App:入口组件
            // router-link默认会被渲染为a标签,to属性默认会被渲染为href属性
            // router-view是路由组件的出口
            template:`
                <div>
                    <router-link to="/login">登录页面</router-link>
                    <router-link to="/register">注册页面</router-link>
                    <router-view></router-view>
                </div>
            `
        };

        new Vue({   // Vue实例化对象
            el:'#app',
            components:{
                App
            },
            router,   // router:router, 在key和value相同时可以只写一个
            template:`<App/>`   // 入口组件
        });
    </script>
</body>

注意要点

(1)<router-link>组件和<router-view>组件

  全局的VueRouter对象 vue-router 还提供了两个全局的组件router-link / router-view

  <router-link> 组件支持用户在具有路由功能的应用中 (点击) 导航。 通过 to 属性指定目标地址,默认渲染成带有正确链接的 <a> 标签,可以通过配置 tag 属性生成别的标签.。另外,当目标路由成功激活时,链接元素自动设置一个表示激活的 CSS 类名。

  <router-view> 组件是一个 functional 组件,渲染路径匹配到的视图组件,是所有路由组件的出口<router-view> 渲染的组件还可以内嵌自己的 <router-view>,根据嵌套路径,渲染嵌套组件。

var App = {
    // router-link默认会被渲染为a标签 to属性默认会被渲染为href属性
    // router-view是路由组件的出口
    template:`
        <div>
            <router-link to="/login">登录页面</router-link>
            <router-link to="/register">注册页面</router-link>

            <router-view></router-view>
        </div>
    `
};

(2)报错Cannot read property 'matched' of undefined

  

  这种问题就是因为自己创建的router对象没有被Vue实例化对象所使用:

new Vue({   // Vue实例化对象
    el:'#app',
    components:{
        App
    },
    router,   // router:router, 在key和value相同时可以只写一个
    template:`<App/>`   // 入口组件
});

(3)页面通过router-view渲染

  1)点击前效果:

  

  可以看到router-link默认会被渲染为a标签,to属性默认会被渲染为href属性。

  2)点击后效果:

  

(4)小结  

  当你在页面访问login时,router-link对应着的路径是'/login',加载对应的component组件:Login,component组件找一个出口渲染出来:router-view。

  

3、命名路由

  有时候,通过一个名称来标识一个路由显得更方便一些,特别是在链接一个路由,或者是执行一些跳转的时候。你可以在创建 Router 实例的时候,在 routes 配置中给某个路由设置名称。

  给当前的配置路由信息对象设置name属性。

  对上面代码做如下修改:

var router = new VueRouter({
    // 配置路由对象
    routes:[
        {
            path:'/login',
            name:'login',      // 设置路由名称
            component:Login
        },
        {
            path:'/register',
            name:'register',   // 设置路由名称
            component:Register
        }
    ]
});

var App = {
    // 不使用to属性访问路由,改用动态命名路由绑定
    // 要链接到一个命名路由,可以给 router-link 的 to 属性传一个对象:
    template:`
        <div>
            <router-link :to="{name:'login'}">登录页面</router-link>
            <router-link :to="{name:'register'}">注册页面</router-link>
            <router-view></router-view>
        </div>
    `
};

4、总结Vue Router流程

(1)引入vue-router对象模块

<script type="text/javascript" src="vue.js"></script>
<!--引入vue-router的对象-->
<script type="text/javascript" src="./node_modules/vue-router/dist/vue-router.js"></script>

  该模块默认会抛出一个VueRouter对象,另外还有两个全局的组件router-link和router-view。

(2)让Vue使用该VueRouter创建

// 如果使用模块化机制编程,导入Vue和VueRouter,要调用Vue.use(VueRouter)

<script type="text/javascript">

    // 让Vue使用该VueRouter创建
    Vue.use(VueRouter);
    
    // 代码省略
</script>

(3)创建路由对象

// 3.创建一个路由对象
var router = new VueRouter({
    // 配置路由对象
    routes:[
        {
            path:'/login',
            name:'login',      // 设置路由名称
            component:Login
        },
        {
            path:'/register',
            name:'register',   // 设置路由名称
            component:Register
        }
    ]
});

(4)路由对象挂载到vue实例化对象中

var App = {
    // 不使用to属性访问路由,改用动态命名路由绑定
    // 要链接到一个命名路由,可以给 router-link 的 to 属性传一个对象:
    template:`
        <div>
            <router-link :to="{name:'login'}">登录页面</router-link>
            <router-link :to="{name:'register'}">注册页面</router-link>
            <router-view></router-view>
        </div>
    `
};

new Vue({
    el:'#app',
    components:{
        App
    },
    router,   // router:router, 在key和value相同时可以只写一个
    template:`<App/>`
});

三、路由参数(范式)

  在vue-router路由中,传参方式一般分两种。如下所示:

(1)xxx.html#/user/1            params 动态路由参数
(2)xxx.html#/user?userId=2     query 查询

  示例代码如下所示:

<body>
    <div id="app"></div>
    <script type="text/javascript" src="vue.js"></script>
    <!--1.引入vue-router的对象-->
    <script type="text/javascript" src="./node_modules/vue-router/dist/vue-router.js"></script>
    <!--全局的VueRouter对象 vue-router还提供了两个全局的组件router-link / router-view-->
    <script type="text/javascript">
        // 路由范式
        //(1)xxx.html#/user/1   params 动态路由参数
        //(2)xxx.html#/user?userId=2   query 查询

        // 2.让Vue使用该VueRouter创建
        Vue.use(VueRouter);

        var UserParams = {
            template:`
                <div>我是用户1</div>
            `,
            created(){
                // 接收参数
                console.log(this.$route);   // 其中包含params属性
                console.log(this.$route.params.userId);  // 输出:1
                // 传参,发送ajax请求
                console.log(this.$router);  // VueRouter对象
            }
        };

        var UserQuery = {
            template:`
                <div>我是用户2</div>
            `,
            created(){
                // 接收参数
                console.log(this.$route);  // 包含query属性,query: {userId: "2"}
                console.log(this.$route.query.userId);  // 输出:2

                // 传参,发送ajax请求
                console.log(this.$router);  // VueRouter对象
            }
        };

        // 3.创建一个路由对象
        var router = new VueRouter({
            // 配置路由对象
            routes:[
                {
                    path:'/user/:userId',  // params形式,动态路由参数,以冒号开头
                    name:'userp',      // 设置路由名称
                    component:UserParams
                },
                {
                    path:'/user',
                    name:'userq',   // 设置路由名称
                    component:UserQuery
                }
            ]
        });

        var App = {
            // 不使用to属性访问路由,改用动态命名路由绑定.要链接到一个命名路由,可以给 router-link 的 to 属性传一个对象
            // 路由匹配,query是查询操作
            template: `
                <div>
                    <router-link :to="{name:'userp', params:{userId:1}}">用户1</router-link>
                    <router-link :to="{name:'userq', query:{userId:2}}">用户2</router-link>
                    <router-view></router-view>
                </div>
            `
        };

        new Vue({
            el:'#app',
            components:{
                App
            },
            router,   // router:router, 在key和value相同时可以只写一个
            template:`<App/>`
        });
    </script>
</body>

1、query方式传参和接收参数

  利用$route.query对象的Get方式传参,与http的get方式一样,会将参数暴露到地址栏。

var UserQuery = {
    template:`
        <div>我是用户2</div>
    `,
    created(){
        // 接收参数
        console.log(this.$route);  // 包含query属性,query: {userId: "2"}
        console.log(this.$route.query.userId);  // 输出:2

        // 传参,发送ajax请求
        console.log(this.$router);  // VueRouter对象
    }
};

  显示效果如下:

  

2、params方式传参和接收参数

  利用$route.params对象的Post方式传参,该方式具有一定限制,必须通过路径传参方式。

var UserParams = {
    template:`
        <div>我是用户1</div>
    `,
    created(){
        // 接收参数
        console.log(this.$route);   // 其中包含params属性
        console.log(this.$route.params.userId);  // 输出:1
        // 传参,发送ajax请求
        console.log(this.$router);  // VueRouter对象
    }
};

  显示效果如下所示:

  

3、源码分析

// vue-router.js文件558行到564行
  Object.defineProperty(Vue.prototype, '$router', {
    get: function get () { return this._routerRoot._router }
  });

  Object.defineProperty(Vue.prototype, '$route', {
    get: function get () { return this._routerRoot._route }
  });

  相当于给vue实例化对象添加了两个属性$router(VueRouter)$route(路由配置信息)

4、配置路由对象方式

// 3.创建一个路由对象
var router = new VueRouter({
    // 配置路由对象
    routes:[
        {
            path:'/user/:userId',  // params形式,动态路由参数,以冒号开头
            name:'userp',      // 设置路由名称
            component:UserParams
        },
        {
            path:'/user',
            name:'userq',   // 设置路由名称
            component:UserQuery
        }
    ]
});

  注意params形式,是配置动态路由参数,要以冒号开头。

5、匹配路由

var App = {
    // 不使用to属性访问路由,改用动态命名路由绑定.要链接到一个命名路由,可以给 router-link 的 to 属性传一个对象
    template:`
        <div>
            <router-link :to="{name:'userp', params:{userId:1}}">用户1</router-link>
            <router-link :to="{name:'userq', query:{userId:2}}">用户2</router-link>
            <router-view></router-view>
        </div>
    `
};

  访问效果如下所示:

  

四、编程式导航 

  除了使用 <router-link> 创建 a 标签来定义导航链接,我们还可以借助 router 的实例方法,通过编写代码来实现。

1、编程式导航示例

  对上例做如下修改,不再使用声明式的<router-link :to='...'>,改为使用编程式:router.push(...)

var App = {
    // 不使用to属性访问路由,改用动态命名路由绑定.要链接到一个命名路由,可以给 router-link 的 to 属性传一个对象
    template:`
        <div>
            <button @click="paramsHandler">用户1</button>
            <button @click="queryHandler">用户2</button>

            <router-view></router-view>
        </div>
    `,
    methods:{
        paramsHandler(){
            // 编程式导航
            this.$router.push({ name: 'userp', params: { userId: 123 }})
        },
        queryHandler(){
            this.$router.push({ name: 'userq', query: {userId: 3221} })
        }
    }
};

  params访问显示效果:

  

  query访问显示效果:

  

2、router.push(location, onComplete?, onAbort?)

  注意:在 Vue 实例内部,你可以通过 $router 访问路由实例。因此你可以调用 this.$router.push

  想要导航到不同的 URL,则使用 router.push 方法。这个方法会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,则回到之前的 URL。

  当你点击 <router-link> 时,这个方法会在内部调用,所以说,点击 <router-link :to="..."> 等同于调用 router.push(...)

   

  该方法的参数可以是一个字符串路径,或者一个描述地址的对象。例如:

// 字符串
router.push('home')

// 对象
router.push({ path: 'home' })

// 命名的路由
router.push({ name: 'user', params: { userId: 123 }})

// 带查询参数,变成 /register?plan=private
router.push({ path: 'register', query: { plan: 'private' }})

  注意:如果提供了 pathparams 会被忽略,上述例子中的 query 并不属于这种情况。取而代之的是下面例子的做法,你需要提供路由的 name 或手写完整的带有参数的 path

const userId = 123
router.push({ name: 'user', params: { userId }}) // -> /user/123
router.push({ path: `/user/${userId}` }) // -> /user/123
// 这里的 params 不生效
router.push({ path: '/user', params: { userId }}) // -> /user

  同样的规则也适用于 router-link 组件的 to 属性。

  在 2.2.0+,可选的在 router.push 或 router.replace 中提供 onComplete 和 onAbort 回调作为第二个和第三个参数。这些回调将会在导航成功完成 (在所有的异步钩子被解析之后) 或终止 (导航到相同的路由、或在当前导航完成之前导航到另一个不同的路由) 的时候进行相应的调用。

  注意:如果目的地和当前路由相同,只有参数发生了改变 (比如从一个用户资料到另一个 /users/1 -> /users/2),你需要使用 beforeRouteUpdate 来响应这个变化 (比如抓取用户信息)

五、嵌套路由 

  实际生活中的应用界面,通常由多层嵌套的组件组合而成。同样地,URL 中各段动态路径按某种结构对应嵌套的各层组件,例如: 

/user/foo/profile                     /user/foo/posts
+------------------+                  +-----------------+
| User             |                  | User            |
| +--------------+ |                  | +-------------+ |
| | Profile      | |  +------------>  | | Posts       | |
| |              | |                  | |             | |
| +--------------+ |                  | +-------------+ |
+------------------+                  +-----------------+

  借助 vue-router,使用嵌套路由配置,就可以很简单地表达这种关系。

1、嵌套路由示例

<body>
    <div id="app"></div>
    <script type="text/javascript" src="vue.js"></script>
    <!--1.引入vue-router的对象-->
    <script type="text/javascript" src="./node_modules/vue-router/dist/vue-router.js"></script>
    <!--全局的VueRouter对象 vue-router还提供了两个全局的组件router-link / router-view-->
    <script type="text/javascript">

        // 嵌套路由:
        // 需求:进入首页后,点击音乐(/home/music) 电影(/home/movie)

        // 2.让Vue使用该VueRouter创建
        Vue.use(VueRouter);

        var Home = {
            // 子路由出口
            template:`
                <div>
                    <br/>
                    <router-link to="/home/music">音乐</router-link>
                    <router-link to="/home/movie">电影</router-link>

                    <router-view></router-view>
                </div>
            `
        };

        var Music = {
            template:`
                <div>我是音乐</div>
            `
        };

        var Movie = {
            template:`
                <div>我是电影</div>
            `
        };

        // 3.创建一个路由对象
        var router = new VueRouter({
            // 配置路由对象
            routes:[
                {
                    path:'/',
                    redirect:'home'
                    // redirect:{name:'home'}   // 命令路由的方式
                },
                {
                    path:'/home',  // params形式,动态路由参数,以冒号开头
                    name:'home',      // 设置路由名称
                    component:Home,
                    children:[
                        // 动态路由匹配表示你的子组件中的结构是不同的

                        // 当访问/home组件时,Home组件的出口是不会渲染任何内容的,
                        // 这是因为没有匹配到合适的子路由
                        {
                            path:'',  // 访问空字符串就表示访问/home了
                            component:Music   // 默认加载孩子组件Music
                        },
                        {
                            path:'music',  // 自己会默认去拼接斜杠
                            component:Music  // 对应加载的组件
                            // children:  // 可以继续配置三层路由
                        },
                        {
                            path:'movie',  // 自己会默认去拼接斜杠
                            component:Movie  // 对应加载的组件
                        }
                    ]
                },
            ]
        });

        var App = {
            // 路由出口
            template:`
                <div>
                    <router-link :to="{name:'home'}">首页</router-link>

                    <router-view></router-view>
                </div>
            `,
        };

        new Vue({
            el:'#app',
            components:{
                App
            },
            router,   // router:router, 在key和value相同时可以只写一个
            template:`<App/>`
        });
    </script>
</body>

2、注意要点

(1)以 / 开头的嵌套路径会被当作根路径。 这让你充分的使用嵌套组件而无须设置嵌套的路径。

(2)children 配置就是像 routes 配置一样的路由配置数组,所以呢,你可以嵌套多层路由。

3、显示效果

(1)页面首页默认显示音乐 

  

(2)点击切换到电影页面

  

4、嵌套路由警告处理

  

  这个警告的意思是:当父路由有子路由时,不允许子路由中有命名路由。因此对代码做如下调整

var router = new VueRouter({
    // 配置路由对象
    routes:[
        {
            path:'/',
            redirect:'home'
            // redirect:{name:'home'}   // 命令路由的方式
        },
        {
            path:'/home',  // params形式,动态路由参数,以冒号开头
            // name:'home',      // 设置路由名称
            component:Home,
            children:[
                // 动态路由匹配表示你的子组件中的结构是不同的

                // 当访问/home组件时,Home组件的出口是不会渲染任何内容的,
                // 这是因为没有匹配到合适的子路由
                {
                    path:'',  // 访问空字符串就表示访问/home了
                    component:Music   // 默认加载孩子组件Music
                },
                {
                    path:'music',  // 自己会默认去拼接斜杠
                    component:Music  // 对应加载的组件
                    // children:  // 可以继续配置三层路由
                },
                {
                    path:'movie',  // 自己会默认去拼接斜杠
                    component:Movie  // 对应加载的组件
                }
            ]
        },
    ]
});

var App = {
    // 路由出口
    template:`
        <div>
            <!--<router-link :to="{name:'home'}">首页</router-link>-->
            <router-link to="/home">首页</router-link>
            <router-view></router-view>
        </div>
    `,
};

  再次查看控制台,警报解除:

  

六、动态路由匹配  

  我们经常需要把某种模式匹配到的所有路由,全都映射到同个组件。例如,我们有一个 User 组件,对于所有 ID 各不相同的用户,都要使用这个组件来渲染。那么,我们可以在 vue-router 的路由路径中使用“动态路径参数”(dynamic segment) 来达到这个效果。

const User = {
    template: '<div>User</div>'
}

const router = new VueRouter({
    routes: [
        // 动态路径参数 以冒号开头
        { path: '/user/:id', component: User }
    ]
})

1、动态路由匹配示例

<body>
    <div id="app"></div>
    <script type="text/javascript" src="vue.js"></script>
    <!--1.引入vue-router的对象-->
    <script type="text/javascript" src="./node_modules/vue-router/dist/vue-router.js"></script>
    <!--全局的VueRouter对象 vue-router还提供了两个全局的组件router-link / router-view-->
    <script type="text/javascript">
        // 2.让Vue使用该VueRouter创建
        Vue.use(VueRouter);

        var Timeline = {
            template:`
                <div>
                    <router-link :to="{name:'comDesc', params:{id:'android'}}">Android</router-link>
                    <router-link :to="{name:'comDesc', params:{id:'frontend'}}">前端</router-link>
                    <router-view></router-view>
                </div>
            `
        };

        var Pins = {
            template:`
                <div>
                    我是沸点
                </div>
            `
        };

        // 共同的子组件
        var ComDesc = {
            data(){
                return{
                    msg:''
                }
            },
            template:`
                <div>
                    我是{{msg}}
                </div>
            `,
            created(){   // 生命周期方法
                alert(1);
                this.msg = '安卓';
            }
        };

        // 3.创建一个路由对象
        var router = new VueRouter({
            // 配置路由对象
            routes:[
                // 动态路由参数以冒号开头
                {
                    path:'/timeline',
                    component:Timeline,
                    children:[
                        {
                            path:"",
                            component:ComDesc  // 访问Timeline时访问共同组件ComDesc
                        },
                        {
                            path:'/timeline/:id',
                            name:'comDesc',
                            component:ComDesc  // 访问子动态路由时,也加载共同组件ComDesc
                        }

                    ]
                },
                {
                    path:'/pins',
                    component:Pins
                }
            ]
        });

        var App = {
            // 路由出口
            template:`
                <div>
                    <router-link to="/timeline">首页</router-link>
                    <router-link to="/pins">沸点</router-link>
                    <router-view></router-view>
                </div>
            `,
        };

        new Vue({
            el:'#app',
            components:{
                App
            },
            router,   // router:router, 在key和value相同时可以只写一个
            template:`<App/>`
        });
    </script>
</body>

(1)路由映射到公共路由

var router = new VueRouter({
    // 配置路由对象
    routes:[
        // 动态路由参数以冒号开头
        {
            path:'/timeline',
            component:Timeline,
            children:[
                {
                    path:"",
                    component:ComDesc  // 访问Timeline时访问共同组件ComDesc
                },
                {
                    path:'/timeline/:id',
                    name:'comDesc',
                    component:ComDesc  // 访问子动态路由时,也加载共同组件ComDesc
                }

            ]
        },
        {
            path:'/pins',
            component:Pins
        }
    ]
});

  /timeline/android和/timeline/frontend都将映射相同的路由。

  一个“路径参数”使用冒号 : 标记。当匹配到一个路由时,参数值会被设置到 this.$route.params,可以在每个组件内使用。

(2)组件实例复用

  当使用路由参数时,例如从 /user/foo 导航到 /user/bar原来的组件实例会被复用。因为两个路由都渲染同个组件,比起销毁再创建,复用则显得更加高效。不过,这也意味着组件的生命周期钩子不会再被调用

// 共同的子组件
var ComDesc = {
    data(){
        return{
            msg:''
        }
    },
    template:`
        <div>
            我是{{msg}}
        </div>
    `,
    created(){   // 生命周期方法
        alert(1);
        this.msg = '安卓';
    }
};

(3)显示效果及生命周期钩子不重复调用

  初始状态:

  

  点击首页,显示alert:

  

  点击确认后显示:

  

  点击前端:

   

  再点回安卓或首页,页面显示会调整,但都不再显示alert。说明生命周期钩子不再运行。

2、响应路由参数的变化  

  前面已经说到当使用路由参数时,例如从 /user/foo 导航到 /user/bar,原来的组件实例会被复用。因为两个路由都渲染同个组件,比起销毁再创建,复用则显得更加高效。不过,这也意味着组件的生命周期钩子不会再被调用。

  复用组件时,想对路由参数的变化作出响应的话,你可以简单地 watch (监测变化) $route对象:

// 共同子组件
var ComDesc = {
    data(){
        return{
            msg:''
        }
    },
    template:`
        <div>
            我是{{msg}}
        </div>
    `,
    created(){   // 生命周期方法
        // alert(1);
        // this.msg = '安卓';
    },
    watch:{
        '$route' (to, from) {
            // 对路由变化作出响应...
            console.log(to);
            console.log(from);
        }
    }
};

(1)点击安卓页面,打印出的to和from信息

  

(2)然后再点击前端页面,打印的to和from信息

  

(3)直接渲染

var ComDesc = {
    data(){
        return{
            msg:''
        }
    },
    template:`
        <div>
            我是{{msg}}
        </div>
    `,
    created(){   // 生命周期方法
        // alert(1);
        // this.msg = 'android';
    },
    watch:{
        '$route' (to, from) {
            // 对路由变化作出响应...
            console.log(to);
            console.log(from);

            // 直接渲染
            this.msg = to.params.id;
        }
    }
}; 

  点击显示效果如下所示:

  

  也可以看到组件没有再次被销毁或创建。

七、keep-alive在路由中的使用

  <keep-alive>是Vue的内置组件,能在组件切换过程中将状态保留在内存中(缓存),防止重复渲染DOM。

(1)Props:

  • include:字符串或正则表达式。只有名称匹配的组件会被缓存。
  • exclude:字符串或正则表达式。任何名称匹配的组件都不会被缓存。
  • max:数字。最多可以缓存多少组件实例。

(2)用法:

  <keep-alive> 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。和 <transition> 相似,<keep-alive> 是一个抽象组件:它自身不会渲染一个 DOM 元素,也不会出现在父组件链中

  当组件在 <keep-alive> 内被切换,它的 activated 和 deactivated 这两个生命周期钩子函数将会被对应执行。

  主要用于保留组件状态或皮面重新渲染。

1、不使用keep-alive切换页面

<script type="text/javascript">
    // 2.让Vue使用该VueRouter创建
    Vue.use(VueRouter);

    var Timeline = {
        template:`
            <div>
                我是首页
            </div>
        `,
        created(){
            console.log('首页组件创建了');
        },
        mounted(){
            console.log('首页组件DOM加载了');
        },
        destroyed(){
            console.log('首页销毁了');
        }
    };

    var Pins = {
        template:`
            <div>
                <h3 @click="clickHandler">我是沸点</h3>
            </div>
        `,
        methods:{
            clickHandler(e){
                e.target.style.color = 'red';
            }
        },
        created(){
            console.log('沸点组件创建了');
        },
        mounted(){
            console.log('沸点组件DOM加载了');
        },
        destroyed(){
            console.log('沸点销毁了');
    };

    // 3.创建一个路由对象
    var router = new VueRouter({
        // 配置路由对象
        routes:[
            // 动态路由参数以冒号开头
            {
                path:'/timeline',
                component:Timeline,
            },
            {
                path:'/pins',
                name:'pins',
                component:Pins
            }
        ]
    });

    var App = {
        // keep-alive组件保持缓存,把router-view渲染的出口缓存起来
        template:`
            <div>
                <router-link to="/timeline">首页</router-link>
                <router-link to="/pins">沸点</router-link>

                <router-view></router-view>
            </div>
        `,
    };

    new Vue({
        el:'#app',
        components:{
            App
        },
        router,   // router:router, 在key和value相同时可以只写一个
        template:`<App/>`
    });
</script>

  此时还没有使用keep-alive,切换页面时都会执行生命周期操作:

  

  而且切换到沸点页面,点击“我是沸点”,可以将字体变红,但是切换到首页再切换沸点时,又变黑色了。也说明了重新进行了创建销毁

2、用keep-alive组件把router-view渲染的出口缓存起来

  注意:<keep-alive> 是用在其一个直属的子组件被开关的情形。如果你在其中有 v-for 则不会工作。如果有上述的多个条件性的子元素,<keep-alive> 要求同时只有一个子元素被渲染。

var App = {
    // keep-alive组件保持缓存,把router-view渲染的出口缓存起来
    template:`
        <div>
            <router-link to="/timeline">首页</router-link>
            <router-link to="/pins">沸点</router-link>

            <keep-alive>
                <router-view></router-view>
            </keep-alive>
        </div>
    `,
};

  显示效果如下:

  

  可以看到第一次点击会创建和加载,但并没有销毁DOM,从首页切换到沸点,仍保持为红色。

 

posted @ 2018-10-12 17:03  休耕  阅读(1886)  评论(2编辑  收藏  举报