day 84 Vue学习四之过滤器、钩子函数、路由、全家桶等
本节目录
1 moment.js
在这里我们先介绍一个moment.js的js前端时间处理的控件
点击下载之后,我们把文件内容copy下来,在我们自己的项目本地目录创建一个叫做moment.js的文件,将内容保存到里面,通过script的src属性来引入
这个moment.js提供了很多的方法
日期格式化:
moment().format('MMMM Do YYYY, h:mm:ss a'); // 四月 2日 2019, 8:05:29 晚上 moment().format('dddd'); // 星期二 moment().format("MMM Do YY"); // 4月 2日 19 moment().format('YYYY [escaped] YYYY'); // 2019 escaped 2019 moment().format(); // 2019-04-02T20:05:29+08:00
相对时间
moment("20111031", "YYYYMMDD").fromNow(); // 7 年前 moment("20120620", "YYYYMMDD").fromNow(); // 7 年前 moment().startOf('day').fromNow(); // 20 小时前 moment().endOf('day').fromNow(); // 4 小时内 moment().startOf('hour').fromNow(); // 6 分钟前
日历时间
moment().subtract(10, 'days').calendar(); // 2019年3月23日 moment().subtract(6, 'days').calendar(); // 上周三晚上8点06 moment().subtract(3, 'days').calendar(); // 上周六晚上8点06 moment().subtract(1, 'days').calendar(); // 昨天晚上8点06分 moment().calendar(); // 今天晚上8点06分 moment().add(1, 'days').calendar(); // 明天晚上8点06分 moment().add(3, 'days').calendar(); // 本周五晚上8点06 moment().add(10, 'days').calendar(); // 2019年4月12日
后面我们的代码中会用到这个moment.js的功能。
2 局部过滤器
在当前组件内容使用过滤器,给某些元素添油加醋,看代码
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <div id="app"> <App></App> </div> <script src="vue.js"></script> <script src="moment.js"></script> <script src="../jquery.js"></script> <script> let App={ data(){ return{ msg:'hello world', time:new Date(), } }, template: //模板语法中使用过滤器{{ 数据|filters里面定义的函数名 }} ` <div> 我是一个App组件{{ msg | myReserve }} <h2>{{ time | myTime('YYYY-MM-DD') }}</h2> 还可以传多个参数,调用方法的时候myTime(v1,v2,v3) </div> `, //定义局部过滤器 filters:{ //val参数就是传送过来的数据 myReserve:function (val) { console.log(val); //对数据过滤完之后,需要return return val.split('').reverse().join('') }, //看h2标签里面怎么使用的,看传参方式。 myTime:function (val,formatStr) { // return moment(val).format('YYYY-MM-DD') ;//通过我们引用的moment.js插件来出来日期格式显示,现在这是显示年-月-日的形式,其实这个日期格式是后端传给你的,让你生成什么格式的,你就生成什么格式的,所以我们把格式也做一个动态的 return moment(val).format(formatStr) //年-月-日的形式,其实这个日期格式是后端传给你的,让你生成什么格式的,你就生成什么格式的,所以我们把格式也做一个动态的 } } }; new Vue({ el:'#app', data(){ return{ } }, components:{ App, } }) </script> </body> </html>
3.全局过滤器
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <div id="app"> <App></App> </div> <script src="vue.js"></script> <script src="moment.js"></script> <script src="../jquery.js"></script> <script> //全局过滤器,可以定义多个,看写法 Vue.filter('myTime',function (val,formatStr) { return moment(val).format(formatStr) }); // vue.filter('myTime2',function (val,formatStr) { // return moment(val).format(formatStr) // }); let App={ data(){ return{ msg:'hello world', time:new Date(), } }, template: ` <div> 我是一个App组件{{ msg | myReserve }} <h2>{{ time | myTime('YYYY-MM-DD') }}</h2> </div> `, //定义局部过滤器 filters:{ myReserve:function (val) { console.log(val); return val.split('').reverse().join('') }, // //看h2标签里面怎么使用的,看传参方式。并且你想,现在这个日期格式化写到了一个组件里面,其他组件是不是没法用啊,所以我们想把它搞成一个全局的,看上面全局过滤器的写法 // myTime:function (val,formatStr) { // return moment(val).format(formatStr) // } } }; new Vue({ el:'#app', data(){ return{ } }, components:{ App, } }) </script> </body> </html>
简单总结:
局部过滤器
声明: filters:{ '过滤器的名字':function(val,a,...){ //a 假如是chao,而val是当前的数据 } } 使用: {{ 数据 | 过滤器的名字('chao','xxx') }}
全局过滤器(所有组件都能用)
声明 Vue.filter('过滤器的名字',function(val,a,...){ //a:chao,val:数据 }) 使用 {{ 数据 | 过滤器的名字('chao','xxx') }}
从生到死的过程中有一些能够搞事情的钩子,就是我们说的钩子函数,和django里面的那些钩子理解起来是一样的。
看官网怎么说的,下图展示了实例的生命周期。你不需要立马弄明白所有的东西,不过随着你的不断学习和使用,它的参考价值会越来越高。
然后生命周期的钩子函数在官网的这个地方看:
我们来看一下这些钩子函数:
钩子函数的执行顺序简单验证,看代码:
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <div id="app"> {{ data }} </div> <script src="vue.js"></script> <script> var vm=new Vue({ el:"#app", data:{ data:"chao", info:"Jaden" }, //组件创建完成之前可以做一些事情,有些场景比如说某个页面或者app打开的时候,点击打开了但是还没运行起来的时候你看到有些动画效果,有个小孩划船啊什么的,等一会软件打开之后,这个效果就没有了 beforeCreate:function(){ console.log("创建前========"); console.log(this.data); //undefined console.log(this.$el); //undefined,这是我们要将数据挂载到的上面的id为app的div标签对象 }, //组件创建完成之后,但是dom中的数据还没挂载上的之前,也就是div标签还没有被vue对象获取到的时候,那么div标签中的数据{{ data }}还没有传入数据 created:function(){ ///*****重点 //一般在这个钩子函数里面发送ajax请求,去后台请求数据,并赋值给我们的data里面的数据属性,因为在上一步beforeCreate里面发送ajax是没有什么用的,因为数据属性你还没有拿到,this.data还没数据 console.log("已创建========"); console.log(this.info);//Jaden console.log(this.$el); //undefined,还没有获得DOM,$el属性还不存在 }, //挂载之前,意思是找到了id为app的div标签(dom),但是数据还没有挂载进去的时候触发的函数 beforeMount:function(){ console.log("mount之前========"); console.log(this.info); //Jaden console.log(this.$el); //拿到了标签对象<div id="app">{{ data }}</div>,但是data还没数据,现在这个dom是个虚拟dom,只是个叫法,是react提出来的,用到的是一个diff算法来搞的,每个dom操作的性能高了零点几毫秒,v-for中的key的使用也是diff算法的其中一个用法,了解一下就行了 }, mounted:function(){ //*****重点 console.log("mounted========"); console.log(this.info);//Jaden console.log(this.$el);//拿到了标签对象<div id="app">Jaden</div>,并且{{ data }}已经挂载上数据了 //说明这个方法之后,真实的dom已经生成了,我们就可以在这个方法里面通过js来操作dom搞事情了,但是vue的设计里面说一般都用数据驱动来完成你的需求,很少用dom操作,除了一些特殊情况。 }, //更新数据属性的操作触发了,但是还没有完成数据更改的时候,比如vm.data = 'xxx';操作 beforeUpdate:function(){ //应用,获取原始的dom搞些事情 console.log("更新前========"); }, //数据更改完成之后触发 updated:function(){ //应用:获取更新之后的dom搞事情 console.log("更新完成========"); }, //vue对象或者组件销毁操作触发,但是还没有完成销毁的时候触发的函数,用v-if可以让组件销毁,或者用vue对象(vm).destory()方法来销毁vue对象的方式来演示这个效果:vm.$destroy(); beforeDestroy:function(){ console.log("销毁前========"); console.log(this.info);//Jaden console.log(this.$el);//<div id="app">xxx</div> }, //组件或者vue对象销毁之后触发的事情,用的也挺多的,一般用在定时器的销毁上,因为即便是我们通过上面的v-if来销毁组件,但是你的组件里面如果有定时器,这个定时器是不会销毁的,需要我们手动销毁,如果这个定时器里面是播放动画等内容,会消耗你的页面性能。 destroyed:function(){ console.log("已销毁========"); console.log(this.info);//Jaden console.log(this.$el);//<div id="app">xxx</div> } }) </script> </body> </html>
看效果:
简单总结:
1、beforeCreate 此时$el、data 的值都为undefined
2、创建之后,此时可以拿到data的值,但是$el依旧为undefined
3、mount之前,$el的值为“虚拟”的元素节点
4、mount之后,mounted之前,“虚拟”的dom节点被真实的dom节点替换,并将其插入到dom树中,于是在触发mounted时,可以获取到$el为真实的dom元素()
myVue.$el===document.getElementById("app-8") // true
我们来做一个简单的应用,一个父子组件嵌套的使用:其中还有使用了上面我们没有写的两个钩子函数activated和deactivated,看看怎么玩的:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>Title</title> <meta name="viewport" content="width=device-width, initial-scale=1"> </head> <body> <div id="app"> <App/> </div> <script src="vue.js"></script> <script> let Test = { data() { return { msg: 'chao', count:0, timer:null //用来记录定时器的 } }, template: ` <div id="test"> <div id="box">{{ msg }}</div> <p>{{ count }}</p> <button @click = 'change'>修改</button> </div> `, methods: { change() { this.msg = 'Jaden'; //原生js获取dom的操作,如果document.getElementById('#box');不好用,你就可以用下面这个方法来获取dom document.querySelector('#box').style.color = 'red'; } }, beforeCreate() { console.log('组件创建之前', this.msg); }, created() { //动态的修改count的值,并且注意这个定时器会一直触发下面的beforeUpdate和updated方法 //因为定时器是不会跟着组件的销毁而销毁的,所以我们需要在组件销毁后手动的销毁它 // this.timer = setInterval(()=>{ // this.count++ // //这里面可以做一些复杂的动画效果之类的 // },1000); console.log('组件创建之后', this.msg); // this.msg = '嘿嘿黑'; }, beforeMount() { console.log(document.getElementById('app')); }, mounted() { console.log(document.getElementById('app')); }, beforeUpdate() { console.log('>>>>>',document.getElementById('app').innerHTML); }, updated() { console.log(document.getElementById('app').innerHTML); }, beforeDestroy() { console.log('beforeDestroy'); }, destroyed() { //注意: 定时器的销毁 要在此方法中处理 console.log('destroyed',this.timer); clearInterval(this.timer); }, //如果用户频繁的点击创建和销毁dom,那么有损页面的性能,所以vue又提供了下面的两个方法 //激活,这个方法和下面那个方法需要借助vue提供的一个内置组件来完成搞事情,看下面的template里面写的内容 activated(){ console.log('组件被激活了'); }, //停止激活,也就是停用 deactivated(){ console.log('组件被停用了'); } }; let App = { data() { return { isShow:true } }, template: //通过v-if就能控制组件的销毁和创建,来演示对应的Test组件中定义的beforeDestroy和destroyed的方法 // ` // <div> // <Test v-if="isShow"/> // <button @click = 'clickHandler'>改变test组件的生死</button> // </div> // `; //激活和停用钩子函数,需要借助vue提供的内置组件<keep-alive> 组件 </keep-alive>,那么<keep-alive>里面的组件就会被缓存起来,并且组件的状态也被保存下来了,即便是点击了消失按钮,组件也并没有消失,下次点击显示的时候,直接从缓存里面将这个组件拿过来用,并且还带着之前组件的状态,就减少了dom操作,提高了性能,如果没有通过这个内置组件来包裹,那么下面这个场景,用户点击销毁和创建的时候,还是触发的上面的销毁方法和创建方法,不会执行激活或者停用的方法,这些方法根据业务的需求来用,没有绝对的好坏之分。 ` <div> <keep-alive> <Test v-if="isShow"/> //将它动态的删除或者添加,就是一个销毁组件和创建组件的操作,也会触发Test组件里面对应的创建销毁的钩子函数 </keep-alive> <button @click = 'clickHandler'>改变test组件的生死</button> </div> `, methods:{ clickHandler(){ this.isShow = !this.isShow; } }, components: { Test }, }; new Vue({ el: '#app', data() { return {} }, components: { App } }) </script> </body> </html>
在使用activated和deactivated两个钩子函数的时候,别忘了配合vue的内置组件<keep-alive>组件</keep-alive>。
看一个创建和销毁组件的业务场景:
对了,注意一点:django中也是有模板语法的是对不对,上大括号{{}},这和vue的模板语法是冲突的,如果你想在django的模板中使用vue的模板语法,记得要关掉django的{{}}模板语法。
vue全家桶:vue + vue-router + vuex
vue就是我们前面学习的vue基础,vue + vue-router 主要用来做SPA(Single Page Application),单页面应用
为什么要使用单页面应用呢?因为传统的路由跳转,如果后端资源过多,会导致页面出现'白屏现象',所以我们希望让前端来做路由,在某个生命周期的钩子函数中,发送ajax来请求数据,进行数据驱动,之前比如我们用django的MTV模式,我们是将后端的数据全部渲染给了模板,然后模板再发送给前端进行浏览器页面的渲染,一下将所有的数据都给了页面,而我们现在使用vue,我可以在组件的钩子函数中发送对应的ajax请求去获取对应的数据,而不是裤衩一下子就把数据都放到页面上了,单页面应用给我们提供了很多的便利,说起来大家可能没有什么切实的体会,来,给大家推荐一个稀土掘金网站,这个网站就是一个单页面应用,是一个开发者技术社区网站,里面的资源会有很多,看样子:
这样的网站我们通过django是可以来完成页面的渲染的,模板渲染嘛,但是这个论坛的数据资源有很多,我们通过django的MTV模式是一下子就将数据全部放到页面里面了,那么页面通过浏览器渲染的时候,浏览器可能没有那么快渲染出来,会出现几秒钟的白屏现象,也就是说几秒钟之后用户才看到页面的内容,这样体验起来就不好,为了用户体验好,就用到了我们说的单页面应用,django模板渲染做大型应用的时候,也就是页面很复杂,数据量很大的页面的时候,是不太合适的,当然如果你够nb,你也可以优化,但是一般它比较适合一些页面数据比较小的应用。
那么解释一下什么是单页应用,看下图:(react、angular也都是做单页面应用,很多大型的网站像网易云音乐,豆瓣等都是react写的单页面应用)
vue + vue-router就是完成单页面应用的,vue-router(路由)是vue的核心插件
1 路由的基本用法
这个vue-router插件需要我们自己下载下来使用,看官网:
下面我们来下载一下vue-router,文档地址,下载vue-router的cnd链接地址:https://unpkg.com/vue-router/dist/vue-router.js
打开这链接,将里面的内容copy下来,然后在我们的本地创建一个vue-router.js的文件,将内容放进去保存起来,就可以通过引用本地文件的方式来使用vue-router的功能了,当然也可以直接使用上面这个cdn的地址。
看本地文件目录:
下面我们来使用一下:先看一下我们要做的效果:
然后我们看一下官网说的使用方法
// 0. 如果使用模块化机制编程,导入Vue和VueRouter,要调用 Vue.use(VueRouter) // 1. 定义 (路由) 组件。 // 下面两个组件可以从其他文件 import 进来 const Foo = { template: '<div>foo</div>' } const Bar = { template: '<div>bar</div>' } // 2. 定义路由 // 每个路由应该映射一个组件。 其中"component" 可以是 // 通过 Vue.extend() 创建的组件构造器, // 或者,只是一个组件配置对象。 // 我们晚点再讨论嵌套路由。 const routes = [ { path: '/foo', component: Foo }, { path: '/bar', component: Bar } ] // 3. 创建 router 实例,然后传 `routes` 配置 // 你还可以传别的配置参数, 不过先这么简单着吧。 const router = new VueRouter({ routes // (缩写) 相当于 routes: routes }) // 4. 创建和挂载根实例。 // 记得要通过 router 配置参数注入路由, // 从而让整个应用都有路由功能 const app = new Vue({ router }).$mount('#app') // 现在,应用已经启动了!
图解一下:
好,就按照官网说的方法,我们来写一写,看代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> </div> <!--vue-router是依赖于vue的,所以先引入vue.js--> <script src="vue.js"></script> <script src="vue-router.js"></script> <script> //如果以后是模块化编程(组件都分到不同的文件中了,也就是说组件都是局部生效的),Vue.use(VueRouter);相当于Vue.Proptotype.$VueRouter = VueRouter这句话,当然内部不是这样实现的,只是简单举个例子,通过原型链将VueRouter添加到了vue的原型上,也就是挂到了Vue对象的父类上,通过组件的继承性我们就能知道将来通过this.$VueRouter就能调用到它,下面我们还没有用到模块化开发,所以其实Vue.use(VueRouter)这句话现在可用可不用 //0. Vue.use(VueRouter); // 1.定义路由组件 const Home={ data(){ return{} }, template:`<div>我是首页</div>` }; const Course={ data(){ return{} }, template:`<div>我是免费课程</div>` }; //2.创建路由 const router = new VueRouter({ // key:value,如果键值相同,简写key就行 //3.定义路由规则 routes:[ //定义访问路径 { path:'/', //首页 component:Home, //路由对应的路由组件 }, { path:'/course', //首页 component:Course, //路由对应的路由组件 }, ] }); let App = { data(){ return{} }, //5. router-link和router-view是vue-router提供的两个全局组件,router-link将来会自动渲染成a标签,router-link里面的to属性将来渲染成a标签的href属性。router-view是路由组件的出口,什么意思呢,意思是说,我们下面点击路径为'/'或者为'/course'的a标签的时候,每个路径对应着我们上面的一个组件,那么每个组件都要找到一个出口来渲染自己的组件对应的模板内容,那么就通过<router-view>这个出口进行渲染,也就是<router-view></router-view>会替换成对应组件的内容。注意使用了vue-router后,咱们上面所有配置的路径,它都会默认给加一个#,举个例子我们的home页面的路径会自动变成'#/',下面默认显示我们上面配置的router对象里面的routes这个数组的第一个path对应的路径和内容 template: ` <div> <div class="header"> <router-link to="/">首页</router-link> <router-link to="/course">免费课程</router-link> </div> <router-view></router-view> </div> `, }; new Vue({ el:'#app', router,//4.给vue根实例来挂载路由对象,相当于router:router,如果你上面的路由起的名字是rou,那么这里写router:rou data(){ return{} }, template:`<App />`, components:{ App } }) </script> </body> </html>
看代码图解
路由重定向,大家还记得是什么吗,访问一个网址,自动帮我们重定向到了另外一个网址上,我们通过vue-router也可以做:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> </div> <script src="vue.js"></script> <script src="vue-router.js"></script> <script> const Home={ data(){ return{} }, template:`<div>我是首页</div>` }; const Course={ data(){ return{} }, template:`<div>我是免费课程</div>` }; const router = new VueRouter({ //HTML5 的history模式,去掉那个路由里面自动加的#,你用我代码测试的时候,先不要加下面这个mode mode:'history',//但是这个history模式是必须要有服务端的(我们pycharm中自带服务器,这里服务器的意思就是一个socket服务端,帮你将文件发送给浏览器去渲染),将来公司的项目中都是这个模式的,因为公司有服务器啊,nginx什么的,这个模式下每个路径就没有#号了,直观好看,官网有解释,大家可以去看看,加#号其实是个hash模式的url routes:[ { path:'/', //打开页面,默认访问的就是根路径,你打开这个网页就会发现自动跳转到了/home路径下 // redirect:'/home' //如果没有做什么重定向,其实默认显示 redirect:'/home' //重定向, }, { path:'/home', //首页 component:Home, //路由对应的路由组件 }, { path:'/course', //首页 component:Course, //路由对应的路由组件 }, ] }); let App = { data(){ return{} }, template: ` <div> <div class="header"> <router-link to="/home">首页</router-link> <router-link to="/course">免费课程</router-link> </div> <router-view></router-view> </div> `, }; new Vue({ el:'#app', router, data(){ return{} }, template:`<App />`, components:{ App } }) </script> </body> </html>
上面代码中使用mode:'history'的注意问题,看官网解释
看官网的这个地方:
好,上面是我们路由的一些基本用法,那么我们下面学一些比较常用的高级用法。
2 命名路由
直接看代码吧:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> </div> <script src="vue.js"></script> <script src="vue-router.js"></script> <script> const Home={ data(){ return{} }, template:`<div>我是首页</div>` }; const Course={ data(){ return{} }, template:`<div>我是免费课程</div>` }; const router = new VueRouter({ // mode:'history', //注意,这里下面代码命令路由演示的时候,把这个mode去掉,pycharm内置的这个服务器,没有对根路径进行很好的配置,所以以这种路径模式来访问根路径会出现404错误,先去掉吧,以后再说 routes:[ { path:'/', // redirect:'/home', redirect:{name:'Home'}, //重定向使用下面名字的方法 }, { path:'/home', //首页 name:'Home', //给路由起个名字,叫做命名路由,注意不叫别名昂,vue里面的别名不是这么搞的,下面在<router-link>里面写to属性的时候,我们就使用名字来写了,:to="{ name:'home' }",注意是单大括号,这样可以实现将路由动态起来的效果,不再是写死了<router-link to="/home">首页</router-link> component:Home, //路由对应的路由组件 }, { path:'/course', //首页 name:'Course', component:Course, //路由对应的路由组件 }, ] }); let App = { data(){ return{} }, template: ` <div> <div class="header"> <router-link :to="{ name:'Home' }">首页</router-link> <router-link :to="{ name:'Course' }">免费课程</router-link> </div> <router-view></router-view> </div> `, }; new Vue({ el:'#app', router, data(){ return{} }, template:`<App />`, components:{ App } }) </script> </body> </html>
3 动态路由匹配
假如我想动态的显示用户信息,每个用户有自己的信息,通过用户的id来判断是哪个用户,并显示对应用户的信息,怎么玩呢,例如我们通过下面的路由方式来搞:
通过访问下面不同的路径 /user/1 加载第一个用户的信息 /user/2 第二个用户的信息 /user/3 第三个用户的信息
相当于路由动态起来了,每个用户信息展示部分的组件大家可以用同一个,只是数据不同
简单说几个路由范式:
// 常用路由范式 // 1 // http://127.0.0.1:8000/index/user // 2 我们要学的这里的动态路由指的是这第二种 // http://127.0.0.1:8000/user/1 //路由后面的1\2\3等等的叫做params参数 // http://127.0.0.1:8000/user/2 // 3 // http://127.0.0.1:8000/user?user_id=1 //这种路由后面的?user_id=1叫做query参数 // http://127.0.0.1:8000/user?user_id=2
直接看代码吧:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> </div> <script src="vue.js"></script> <script src="vue-router.js"></script> <script> //Vue.use(VueRouter);
//每个用户信息的展示都用这个组件 const User={ data(){ return{ user_id:null, //5. 定义一个数据属性来接收watch改动的数据 } }, //6. template里面使用一下这个user_id数据属性 template:`<div>我是用户{{ user_id }}</div>`, created(){ // 2. console.log(this.$route);//看打印结果,是个object对象,里面有很多的属性{fullPath:..,params:{id:1},query:{}等等内容},其中这个query是路由中有query参数的时候,/user?user_id=1,会拿到1,这个object对象就是当前调用这个组件的路由对象信息,以后想找url里面的一些信息,就用这个对象找 console.log(this.$route.params.id); //7.别忘了第一次加载组件的时候,我们要给数据属性的user_id添加数据 this.user_id = this.$route.params.id; //3. 但是这里你发现点击了用户1再点击用户2,你会发现打印的id还是1,这是为什么呢,这时候你要细心看官网了,官网是这么说的,两个路径使用相同组件的时候,原来的组件实例会被复用,也就是说原来的组件被缓存起来了,点击用户2的时候,你使用的还是用户1的组件,状态还是用户1的组件状态,并且组件的生命周期钩子函数不会再被重新调用,也就是说这个created方法不会重新执行了,那怎么办呢?需要watch来监听 }, // 4.监听$route对象,因为它里面的id数据会发生变化,我们就监听这个对象的数据变化来做出响应 watch:{ //$route就是上面说的路由信息对象 '$route'(to,from){ //to就是到哪里去,from就是从哪里来,如果我们从用户1点击到用户2,那么你就会看到to表示用户2的路由信息对象,from是用户1的路由信息对象 // console.log('>>>',to); //{name: "User", meta: {…}, path: "/user/2", hash: "", query: {…}, …} //和我们上面在created方法里面打印的this.$route是一样的对象 // console.log(from); //{name: "User", meta: {…}, path: "/user/1", hash: "", query: {…}, …} // 我们要的就是to里面的数据 let user_id = to.params.id; this.user_id = user_id; //更改数据属性 //其实一般这个id也是后端给你的每条数据的id,通过这个id我们可以发送ajax请求,去后端获取对应的数据来进行渲染,这里我们就不模拟发送了昂,简单写写,我们去上面定义一个user_id数据属性来动态保存这个数据 } } }; const router = new VueRouter({ routes:[ { // path:'/user/1', path:'/user/:id', //1. 写法,这个id就是在下面<router-link>里面写params参数的时候{id:1]里面的id,两个名字要对应好 name:'User', component:User, //路由对应的路由组件 }, ] }); let App = { data(){ return{} }, template: ` <div> <div class="header"> <router-link :to="{ name:'User',params:{id:1} }">用户1</router-link> <router-link :to="{ name:'User',params:{id:2} }">用户2</router-link> </div> <router-view></router-view> </div> `, }; new Vue({ el:'#app', router, //别忘了它,挂载路由对象 data(){ return{} }, template:`<App />`, components:{ App //别忘了它 } }) </script> </body> </html>
4 编程式导航
vue-router给我们的vue添加了两个内置的属性:$route和$router,$router指的是VueRouter路由对象,$route指的是路由信息对象,我们在上面使用过了。这两个对象都可以通过组件对象来调用
下面我们说一下编程式导航和声明式导航:
声明式导航就是我们上面的代码中写的,a标签和a标签里面的url直接就渲染好了等你使用:
<router-link :to="{ name:'User',params:{id:1} }">用户1</router-link> <router-link :to="{ name:'User',params:{id:2} }">用户2</router-link>
编程式导航就是以后我们可以通过任意标签来进行页面的跳转等操作,不是单纯的上面的router-link渲染出来的a标签了。
看官网的解释:
在 Vue 实例内部,你可以通过 $router
访问路由实例。因此你可以调用 this.$router.push
。
想要导航到不同的 URL,则使用 router.push
方法。这个方法会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,则回到之前的 URL。
当你点击 <router-link>
时,这个方法会在内部调用,所以说,点击 <router-link :to="...">
等同于调用 router.push(...)
。
使用一下,看代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> </div> <script src="vue.js"></script> <script src="vue-router.js"></script> <script> const Home={ data(){ return{} }, template:`<div>我是首页</div>` }; const User={ data(){ return{ user_id:null, } }, template://点击按钮跳转到首页 `<div> 我是用户{{ user_id }} <button @click='clickHandler'>跳转首页</button> </div> `, created(){ this.user_id = this.$route.params.id; }, methods:{ //编程式导航,不是通过router-link渲染的了,而是通过任意的一个标签(现在用的是button)进行跳转的,当然通过声明式router-link也是可以做这个效果的。 clickHandler(){ this.$router.push({ name:'Home',//通过Home的名字找到Home对应的那个组件进行加载 }) } }, watch:{ '$route'(to,from){ let user_id = to.params.id; this.user_id = user_id; } } }; const router = new VueRouter({ routes:[ { path:'/user/:id', name:'User', component:User, }, { path:'/home', name:'Home', component:Home, }, ] }); let App = { data(){ return{} }, template: ` <div> <div class="header"> <router-link :to="{ name:'User',params:{id:1} }">用户1</router-link> <router-link :to="{ name:'User',params:{id:2} }">用户2</router-link> </div> <router-view></router-view> </div> `, }; new Vue({ el:'#app', router, data(){ return{} }, template:`<App />`, components:{ App } }) </script> </body> </html>
好,留个练习吧:
这些东西效果先做出来,至于课程分类的那些标签页对应的内容,有能力的就写。