文章目录
- 2018年12月28日 上午
- 第107个视频 - vue-router动态路由的使用
- 第108个视频 - vue-router - 路由懒加载的使用
- 第109个视频 - vue-router 路由懒加载的使用
- 第110个视频 - 路由的嵌套使用
- 第111个视频 - vue-router参数传递(一)
- 第112个视频 - vue-router参数传递二
- 第113个视频 - r o u t e r 和 router和 router和route的由来
- 第114个视频 - 全局导航守卫
- 第115个视频 - 导航守卫的补充
- 第116个视频 - vue-router-keep-alive及其他问题
- 第117个视频 - keep-alive属性介绍
- 第118个视频 - tabbar基本结构的搭建。
- 第119个视频 - TabBar和TabBarItem组件封装
- 第1个问题 - 现在没有复用性
- 第2个问题 - tabbar当中显示的是文字和图片,不仅仅是文字
- 第3个问题 - 图片怎么用?
- 问题锚点
- 第4个问题 - 图片展示效果太大了。
- 第5个问题 - 图片和文字在一行显示
- 第6个问题 - 感觉现在tabbar.vue当中的逻辑很乱
- 第7个问题 - App.vue当中代码太乱
- 第8个问题 - 我们应该怎么写TabBarItem的template呢?
- 第9个问题 - 现在我们没有图片
- 第10个问题 - TabBarItem当中的文字和图片不能写死
- 第11个问题 - 我们感觉TabBarItem上面的字体有点大
- 第12个问题 - 图片距离TabBar上方要留一点空间
- 第13个问题 - 图片和文字之间的空间有点大
- 第14个问题 - 图片和文字之间的距离想自定义一下
- 第15个问题 - 重新讨论第10个问题
- 第120个视频 - 给TabBarItem传入active图片
- 第121个视频 - TabBarItem和路由结合效果
- 第122个视频 - TabBar的颜色动态控制
- 第123个视频 - 知识回顾
- 第124到140个视频与前面的视频重复
2018年12月28日 上午
第107个视频 - vue-router动态路由的使用
需求
然后我们去App.vue当中去设置router-link。
跑动显示的效果:
但是现在并没有在我们的路径后面拼接动态的内容。
动态路由配置
我们需要在vue-router的配置文件当中,修改变成如下的配置:
如果我们只是修改了这个配置,我们再去跑程序的时候,我们发现现在是这样的效果:
组件渲染不出来了,路径也没有拼接上什么玩意?
我们是需要在App.vue当中拼接上一点东西的。
这个时候的效果:
第三点
我们在真实开发当中/user/zhangsan当中的zhangsan肯定是动态获取的。
这个时候,我们怎么在template当中进行路径的拼接呢?
我们是想要把上面的userId动态绑定到我们的router-link标签当中的to属性的。
这个就是需要用到动态属性绑定,就是v-bind。
稍微回顾一下动态属性绑定
怎么动态绑定呢?
这样的效果就是如下的:
我们就可以根据不同的用户,动态决定路径的信息了。
如果商品的id,也是一样的道理。
再提个需求
如下图:
用户界面希望拿到路径当中的userid的信息,就是如图的lisi,在页面上进行展示。
这个情况怎么做呢?
这个时候,需要在我们的User.vue这个组件当中,使用$route这个属性。
一定,一定要注意区分和$router这个属性。
回顾一下$router属性
昨天,我们在使用代码来进行组件路由跳转的时候,我们使用了button,使用了事件监听,在methods方法当中,我们进行代码的路由跳转的时候,我们使用$router属性,使用了它的push方法和replace方法。
这个$router是谁的呢?
这个就是我们的router/index.js当中创建的大的路由对象了。
我们现在要用$route属性
我们现在是为了在我们的组件当中,获取我们url路径当中的动态拼接的部分信息,在我们的组件页面当中进行展示的。
$route是什么意思呢?
r o u t e 就 是 表 示 , 当 前 哪 个 路 由 处 于 活 跃 状 态 , 这 个 route就是表示,当前哪个路由处于活跃状态,这个 route就是表示,当前哪个路由处于活跃状态,这个route表示的就是哪个路由组件。
r o u t e r 是 我 们 在 r o u t e r / i n d e x . j s 当 中 创 建 的 r o u t e r 对 象 , 就 是 路 由 对 象 。 这 个 路 由 对 象 当 中 有 好 几 个 路 由 。 哪 个 路 由 处 于 活 跃 状 态 , router是我们在router/index.js当中创建的router对象,就是路由对象。这个路由对象当中有好几个路由。哪个路由处于活跃状态, router是我们在router/index.js当中创建的router对象,就是路由对象。这个路由对象当中有好几个路由。哪个路由处于活跃状态,route就是表示哪个路由组件。
我们就是通过上面的过程,在路由组件当中,通过$route.param方法来拿到了动态拼接的路径参数。
直接获取展示
我们也可以直接在User.vue当中,直接获取展示:
总结
第一,我们在router/index.js,路由配置文件当中,配置了动态拼接的路径参数。
第二,我们在App.vue当中,data属性当中表示获取的动态数据userid,然后通过动态绑定,给router-link的to属性动态绑定了userid路径。
到这里,我们就能够实现在路径后面动态拼接路径了。
第三,我们在User.vue当中,通过$route这个参数,获取到params,获取到路由配置文件当中拼接的路径参数,在页面当中进行展示。
所以,关键就是index.js当中的动态路由配置。
ppt
第108个视频 - vue-router - 路由懒加载的使用
认识路由的懒加载
之前我们自己配置过webpack。
我们打包的时候,除了img,index.html,其他全部都放到了bundle.js当中了。
这个bundle.js当中会变得非常大。
如果我们从静态资源服务器请求静态资源的时候,可能花的时间非常长,这样用户的浏览器会出现短暂的空白。
这种体验是非常不好的。
脚手架的webpack配置
脚手架当中自动的webpack配置,就是注意到了这个问题,它会对我们的js文件进行分包,而且对我们的css文件也进行了分包。
我们可以验证一下。
我们可以进行一个打包。
我们通过npm run build
来进行打包。
我们可以看到css文件是被单独分离出来了,我们之前都是在bundle.js当中的。
抽离打包之后,在index.html当中是对css进行了引用的。
我们可以看到,js文件,也不是打包到一个文件当中的,我们这个项目当中,就是有3个文件的。
第一个文件,叫做app,是application,是当前应用程序开发的所有的代码,只要是你写的业务代码,都在这个js文件当中的。
第二个文件,叫做vendor,这个单词是提供商,第三方的意思,在项目当中你引入了第三方的东西,比如说vue,比如说vue-router,比如axios,比如说betterscroll,这些都是协助我们开发的。这些第三方的东西,webpack打包的时候,全部会打包到vendor.js当中。
第三个文件,叫做manifrest,这个文件是为我们打包的代码,做底层支撑的。我们在项目开发当中,用了很多模块化,用了很多的import,export,在项目当中你既可以用es6的模块化,也可以用commonjs当中的模块化,但是最终打包的js当中可能是不支持commonjs或者es6都不支持。打包出来的js当中,需要通过某些方式,对之前的导入导出,让它有效,让浏览器支持。需要写一些代码。就是manifrest。
我们现在打开manifrest是看不懂里面的代码的,因为已经丑化过了。
我们需要看一下里面的代码,就是不要让它打包的时候丑化。
丑化肯定是在webpack.prod.conf.js当中使用了丑化的插件,就是下面的这个插件:
我们把丑化的插件关掉,然后继续打包,然后打开manifrest.js文件。
我们在业务代码当中使用了导入导出,但是我们在打包的文件当中,是没有原来方式的导入导出的。
看看之前的代码
我们可以看看之前的,我们自己写的webpack的配置打包后的文件。
打包出来的bunlde.js里面的结构是什么呢?
(function (modules){})([])
,整个打包出来的bunlde.js当中,就是一个匿名闭包。
这个[]
当中,就是我们业务代码当中的一个一个的模块,传入进去了。
然后在(function (modules) {})([])当中定义了一个最最重要的函数,就是下面的函数__webpack_require__
:
在定义这个__webpack_require__
这个函数的同时,还赋予了很多的参数,如上图。
这个__webpack_require__
函数有个最最关键的操作:
上面这行代码的意思就是:把__webpack_require__.s
赋值为0,然后传入到__webpack_require__
函数当中。
在我们打包后的bundle.js当中,我们的模块之间还可以继续引用的原因,就是因为,我们的有
__webpack_require__
这种代码在支撑。
回到脚手架2打包的代码当中
我们的脚手架2打包出来的第三个manifrest.js当中也是跟上面一样的道理。
在这个js文件当中,也有__webpack_require__
这个关键的函数:
这个函数最大的作用,就是模块化的代码进行底层支撑。
之前我们自己打包的时候,我们的业务代码贺支撑代码都是放到了bundle.js当中。
现在脚手架当中的webpack打包的时候进行了代码分离,都放到了manifrest.js当中。
总结
- app.js:我们的业务代码,application
- manifrest:支撑代码,运行时支撑代码
- vendor:第三方代码,例如vue,vue-router。
第109个视频 - vue-router 路由懒加载的使用
解释什么是懒加载
当我们在公司开发一个大型项目的时候,
项目里面的代码,打包之后,app.js当中的代码,会远远大于vendor或者manifrest当中的代码。
当我们的项目部署之后,用户第一次从静态服务器当中进行请求的时候。
vendor和manifrest都会先请求过去的。但是app.js太大了,咋办呢?
我们需要像个办法来对app.js来进行分离。
现在默认情况下,我们所有的业务都是打包到app.js当中的。
在开发当中,我们常规的做法,就是一个路由,打包一个js文件。
而且在用户第一次请求的时候,这个路由的js文件是不会跟着vendor或者manifrest一起给用户的。
之后根据用户的点击,用到了哪个组件,再加载对应的路由组件的js。
懒加载是什么意思:用到的时候再加载。
路由懒加载的效果
怎么写懒加载的代码
第一种方式,就是异步组件的写法,能够认识就可以了,不推荐专门记忆这种写法。
第二种方式,是AMD的写法,代码量很少。
第三种方式,就是ES6的写法, 上课会使用的写法。
直接写一个箭头函数,里面跟上一个import,到时候就会动态加载了。
通过这种方式,这样写,就可以了。
当然,也可以这样直接写:
我们推荐大家使用第一种写法。
重新打包程序进行验证
打包完成了时候,三个组件用到了懒加载,打包出来的文件当中就多个三个文件。
第110个视频 - 路由的嵌套使用
场景
我们希望在首页/home当中,配置一些其他的东西。比如/home/news,比如/home/message。
这个场景,就是路由的嵌套。
认识路由的嵌套
路由嵌套第一步 - 创建子组件
注意:ul>li{消息$}*4
记住这种webstorm的小技巧的写法。
如上面的图,我们创建了连个子组件:HomeMessage,HomeNews表示这个就是首页的子组件。
路由嵌套第二步 - 路由映射中配置子路由
将我们的子组件懒加载进来:
然后配置子路由:
路由嵌套 - 设置子组件页面占位
设置router-view
设置router-link
注意,这里的router-link当中的to属性当中的路径,是需要写一个完整的路径的,就是/home/news
,不能够直接写/news,不能够写home/news
,这个home/news
是王红元在讲课的时候,疏忽的犯错的一个地方。
效果
然后我们点击新闻,下面的东西就显示出来了。
小问题1
如下图所示:
第一次,用户进入了首页,点击了新闻。
第二次,用户点击了用户标签,进入了用户界面。
第三次,用户点击了首页,回到了首页界面,但是新闻那个内容,就不显示了。
这个从交互逻辑上来说,肯定是不好的。
小问题2
用户第一次进来,首页里面新闻和消息的位置什么都没有显示。
默认情况下,应该显示新闻或者消息的。
所以应该有一个默认的路径的。
问题1和问题2 - 配置子组件的默认路径
效果就是用户访问:localhost:8080的时候,默认的路径就是如下图所示:
问题3
第一,我在首页当中,点击了消息,显示了首页当中消息的内容。
第二,我跑了,我点击了关于页面。
第三,我又回来了,我们添加了子组件的默认路径的时候,回到首页看到的是新闻的内容,但是我还是想要看到第一步当中的消息的内容。
这个怎么做呢?
这个我们放到后面讲解。
这个用一个pipeline就可以了。
第111个视频 - vue-router参数传递(一)
场景
当我们从一个路由组件,跳转到另外的一个路由组件的时候,我们希望能够传递一些消息的。
一开始我们显示的肯定是App.vue这个页面的。
因为我们配置了默认路径,所以在App.vue当中还显示了首页了。
我们从App.vue当中,点击了【用户】按钮的时候,页面就会跳转到【用户】页面了,就是User.vue组件了。
我们希望给【用户】这个界面传入一些东西。
前面我们传递参数的方式
之前我们点击了用户按钮,通过动态路由的方式,动态绑定了router-link的to属性。
然后我们在用户组件当中通过$route.param获取到了路径当中的动态参数。
这就是一种方式。
看看ppt
在很多的项目当中,我的用户的界面,一般都叫做Profile,不要起名叫做me或者my。
- Profile可以翻译成为档案。
传递参数的方式
验证query类型
第一步:创建Profile组件
第二步,在router/index.js当中懒加载导入组件
第三步,在index.js当中的,路由对象的,route属性对象当中配置路径和组件的映射关系:
第四步:配置页面中的router-link标签
效果:
需求:跳到档案的时候,想要传递一些参数,名字,身高,年龄,等等。
怎么使用query传递参数呢?
第一,如果想要传递参数的时候,router-link的to属性的值,不能是个字符串了,必须是个对象。
第二,这个对象动态绑定到我们的to属性上。
第三,在对象当中,我们可以配置很多的属性,比如说path属性。
我们写的效果是这样的:
我们这种to属性的写法,跟以前是一样的,也是可以实现跳转的,如下图所示:
我们使用对象写法的时候,就可以加上其他的属性了。
我们如果是上面的写法的话,验证的效果,就是下面的效果:
拓展:url的完整写法
这个userinfo,在git当中会用到,在网站的url当中很少用到。
如果你做过gpu开发的话,gpu当中就有这个fragment。
之前我们的params属于path后面的参数。
注意从url的结构,区分params和query和path。
在组件当中获取query参数
得到的效果如下所示:
第112个视频 - vue-router参数传递二
场景
我们可以看看上面的代码。
- 用户那里,我们是通过动态绑定了router-link的to属性,然后直接在路径后面拼接了params参数userId传过去了,然后路由组件可以通过$route.params来获取。
- 档案那里,我们是通过动态绑定了router-link的to属性,然后绑定了一个对象,对象当中设置了path和query,然后路由组件可以通过$route.query来获取。
如果我们不使用router-link,我们使用的是button呢?
我们点击button的时候,我们通过代码实现的方式,跳转到对应的组件,然后我们传递过去一些参数。
回忆之前
我们之前,通过代码实现路由组件跳转的时候,是通过button绑定了点击事件,然后在事件方法当中是通过$router对象的push或者replace方法来实现了路由组件的跳转的。
代码实现路由跳转和query传参
效果如下:
第113个视频 - r o u t e r 和 router和 router和route的由来
胡天伟的预习
$router是我们在通过代码来实现组件跳转的时候,没有使用router-link的时候,用到的对象。
$route代表的是在激活的组件当中获取params或者query参数的时候,使用的。
ppt
解释的准备工作
第一,我们在一个组件当中,就是User.vue当中,新增一个按钮,希望点击这个按钮的时候,就打印一下$router这个对象。
第二,我们在项目的入口文件,也就是main.js当中打印一下路由对象:
这个router就是我们导入的,是我们从router/index.js当中导出,就是如下图所示的:
这个导出的router,就是我们使用vue-router插件,创建的路由对象。
我们的目的是:对比一下组件当中打印的$router对象,是不是,和我们在router/index.js当中根据vue-router插件创建的路由对象是一回事呢?
我们可以看到router对象完全一样。
也就是说$router就是我们在router/index.js当中使用vue-router插件创建的路由对象。
看看源码
我们可以看到VueRouter这个类当中有各种方法,有push,replace,go,back,forward这些方法。
任何一个组件当中,通过this.$router拿到的对象都是一样。
this.$route
先打印看一下:
这个打印的$route对象是什么呢?
就是我们在router/index.js当中配置的动态路由:
因为这个路由当前处于活跃的,所以$route就是这个路由。
$route拿到的就是处于活跃的路由。
另外的角度解释 r o u t e r 和 router和 router和route
必须看源码
看一下项目当中使用的vue-router的真实的版本是:
然后从github上面下载这个vue-router的源码。
发散:Vue当中使用插件
之前我们讲过,当我们使用vue-router的时候,第一步,就是使用Vue.use(VueRouter)
任何插件的使用都是这样的。比如说我们后来使用vuex的时候,也是这样的。
我们第一步都是要使用use方法来进行安装插件的。
如果我们执行了上面的代码的时候,在插件内部会去vue-router的install方法的。
看vue-router的源码
- 看源码,推荐从package.json开始看。
- 先看package.json当中的scripts。
- 直接读src。
我们看这个源码,就是要看看VueRouter对象的install方法。
源码当中index.js当中VueRouter对象当中,一开始install当中是void的。
到index.js最后的时候,又给VueRouter.install复制了新的值:
我们点击去install,来到了install.js当中。
在install.js当中的第一个东西
为什么我们的代码当中能够使用router-link标签和router-view标签呢?
因为看到上面的代码当中,使用了Vue.component注册了两个全局组件。
为什么在源码当中注册的组件是RouterView,我们使用的时候,使用的是router-view标签呢?
这是一个约定俗称的玩意。
举个例子:
我们自定义组件的时候,名字都是这种驼峰的方式的。但是我们在页面当中使用组件的时候,标签我们都是采用小写的方式的。
为什么组件当中有 r o u t e r 和 router和 router和route属性呢?
我们先看看下面的图:
为什么我们的自定义组件当中,都会有 r o u t e r 和 router和 router和route属性呢?
我们先说一个结论:所有的组件都继承自Vue类的原型prototype。
上面的图中,User.vue这个组件,是一个对象的,都是继承自Vue类的原型的。
想要真正理解这句话的话,就必须去看Vue的源码。
知道这句话有什么用呢?
理解Vue原型的测试
第一,我们给vue的原型增加了一个test方法。
第二,我们在User组件当中,使用test方法。
举例:
效果:
所以,VueRouter就是给Vue的原型上面,增加了 r o u t e r 属 性 和 router属性和 router属性和route属性。
在install.js当中看的第二个东西
这里使用了一个Object.defineProperty的方法,给Vue.prototype上面增加了 r o u t e r 和 router和 router和route属性。
解释一下Object.defineProperty
我们可以通过Object.defineProperty(对象,属性的key,属性的value)
这个方法,给一个对象添加属性。
这个方法,是Vue响应式实现的核心。
第114个视频 - 全局导航守卫
ppt
场景说明
我们希望对来回跳转的这个过程,做一点监听,可以在对应的监听函数当中做你想做的事情。
导航守卫最主要就是监听,你从哪里跳到了哪里。这种作用的。
需求
在我们当前的项目当中,我们的浏览器页签上面的标题是固定的。
这是因为在我们的项目当中,index.html当中title是固定的。
我的需求是,我点击首页的时候,标题就显示首页。
我点击关于的时候,标题就显示关于。
就是这个需求。
你会不会做这个需求呢?
之前的方案
之前我们讲过生命周期的。
任何的vue实例和我们的组件都是有生命周期的。
有个生命周期函数叫做created(),这是我们的组件被创建的时候,会回调的生命周期函数。
还有一个生命周期函数叫做mounted()。当我们将我们的template挂载到整个dom上面的时候,会回调的函数。
并不是说你的组件一创建出来,就会挂载到dom上面的。
顺序是先创建组件,然后再讲.vue文件当中template挂载到dom上面的。
还有一个生命周期函数叫做updated()。这个是在我们的界面发生更新的时候,会回调的函数。
比如说,我们当前界面当中有一个mustache语法绑定的数据,数据改变了,响应式的,界面会发生更新的,这时候,就会回调update()函数。
这三个生命周期函数是最常用的。
我们现在导航上有四个标签:首页,关于,用户,档案。
第一步:如果我们的home、about、user、profile全部都实现一下created()生命周期函数。
第二步:验证,我们跑起来项目,然后点击哪个导航标签,就打印cteated()函数当中的内容。
所以为了实现上面的需求,我们可以直接在每个组件的created()函数当中来改变title。如图所示的代码:
效果是如下图:
这个实现方案有个致命确定:一个功能的实现,涉及所有的组件,真是要命。
现在的方案 - 全局导航守卫
我们可以在router/index.js当中设置全局导航守卫。
第一步:使用vue-router插件创建的VueRouter路由对象,调用它的beaforEach方法。
第二:这个beforeEach方法,看源码是要求传入一个NavigationGuard。
这个NavigationGuard是个什么玩意呢?
我们点进去看一看,看到下面的内容:
我们可以看到,这里的代码就是定义了一个NavigationGuard的类型,这个定义类型就是相当于起别名,就是相当于给后面的箭头函数起了别名。
也就是说这个NavigationGuard就是一个函数。
也就是说,我们调用路由对象的beforeEach方法的时候,要传入一个函数。
vuerouter.beforEach(NavigationGuard)这个意思就是,传入一个函数。这个函数有三个参数,就是to,from,next。
也就是说:vuerouter.beforEach((to, from, next) => {} )
。
一个结论
我们在beforEach方法当中必须调用next(),也就是如下图:
如果我们这里不调用next()的话,路由跳转就失效了。
现在的方案 - 全局导航守卫
第一,我们可以在index.js当中的routes对象中,给每一个route对象,都加上一个meta属性,在meta当中设置title。
第二,我们利用全局导航守卫来实现上面的需求。
需要注意的是router这个东西,是我们创建的路由对象。
router对象当中,有一个beforeEach方法。
这个方法的参数,是需要传入一个全局导航守卫,也就是NavigationGuard。
这个NavigationGuard就是全局导航守卫。它的本质是一个函数。
这个函数有三个参数,to,from,next。其中to和from都是route对象,也就是routes当中配置的组件路径关系对象。
next也是一个函数,必须调用next()才能够跳转正常。
效果
路由嵌套的小问题
上面的图当中,我们打印了一下导航守卫当中的to对象,因为有路由嵌套,所以,我们可以看到to对象当中的meta当中没有title属性,
所以,上面的title显示的是undefined。
我们 matched属性当中去寻找:
完善需求实现的代码
- 有路由嵌套的时候,从to对象的matched当中拿到第一个数组元素的meta,获取meta当中的title。
- 没有路由嵌套的时候,通过上面的方式,也能够拿到meta当中的title。
第115个视频 - 导航守卫的补充
ppt
元数据
元数据,就是描述数据的数据。很多语言当中都有一个metaclass,就是元类,元类创造类对象,类对象创造实例对象。
前置钩子和后置钩子
导航守卫,就是当发生路由跳转的时候,执行的一些函数。
我们可以看到vue-router的源码当中,有这些函数beforeEach和afterEach这些函数
beforeEach就是叫做前置守卫,afterEach就是叫做后置钩子。
beforeEach就是路由跳转开始之前,会回调的函数,必须执行next()。
afterEach就是路由跳转完成之后,会回调的函数,不需要执行next()。
验证
我们可以看到跳转的内容:
全局守卫
我们之前写的beforeEach和afterEach,这些都是全局守卫。
我们还有路由独享守卫。只有我们进入到某个路由里面的话,才会回调的函数。
我们就想要观察一下,什么时候进入到我们的about路由当中,调用一个回调函数。
我们可以在官方网站来进行一个学习。
我们简单读一下就可以了。
next()的多种用法
用户已经登录过了,我们可能就是直接用next(),正常跳转到下面的就可以了。
用户如果没有登录的情况下,我们就可以使用next('/login')
的其他跳转方式。
路由独享守卫
组件内的守卫
- beforeRouteEnter
- beforeRouteUpdate
- beforeRouteLeave
根据不同的业务需求,来到开发文档当中,查询:全局导航守卫,路由独享守卫,组件内的守卫。
第116个视频 - vue-router-keep-alive及其他问题
ppt
场景
之前我们说过一个业务场景,会放在后面解决,我们回顾一下。
第一,用户第一次进来,是这个页面。
第二,用户在首页当中点击了消息。
第三,用户点击了用户这个页面
第四,用户又回到了首页
在这个时候,用户回来到首页的时候,看到的是新闻,而不是之前看到的消息,如果想要看到消息,还必须自己手动点击一下消息才可以。
这个不是非常人性化的。
场景解释
这个场景的意思就是说,我们组件内部的所有的状态,是没有被保留下来的。
每次用户在路由组件之间进行切换的时候,比如说从用户组件到首页组件的时候。
会重新创建一个新的首页组件的。
如果我们希望把组件状态给保留下来,不想要重新创建新的组件的时候。
这个时候,我们就可以使用keep-alive了。
keep-alive理解
router-view是一个组件,如果我们现在把router-view放在keep-alive组件当中,所有路径匹配到的视图组件到时候就会被缓存。
也就是说,keep-alive可以简单理解成为一个缓存标识。
keep-alive是vue内置的组件。
router-view是vue-router内置的组件。
演示
new Vue或者创建一个组件的时候,都会有很多生命周期函数。
- beforeCreate
- created
- beforeMount
- mounted
- beforeUpdate
- updated
- beforeDestory
- destoryed
演示
我们给我们的home组件,设置了两个生命周期函数,然后我们通过在路由切换的时候,看看这个组件是否是,会被频繁创建和频繁销毁的。
我们希望他们每次都创建一个新的吗?
肯定是不希望的。
我们可以这样做:
这样组件就不会频繁创建了。性能就提升了。
场景需求实现的第一种方案 - 这种方案不行
第一步:取消我们原来在路由映射关系配置当中的,首页子组件重定向
第二步:结合keep-alive,我们在created生命周期函数当中,使用$router对象,就可以改变路径
但是这种方案还是不行。
场景需求实现的第二种方案 - 这种方案不行
当页面是活跃状态的时候,执行activated()函数。如果是页面不活跃的情况下,执行deactivated()函数。
上面这张图当中的分析,是错误的。在页面已经不活跃的时候,this.$route.path,记录的是活跃的组件的path。
所以,这个方案也不会达到效果的。
场景需求实现的第三种方案 - 组件内守卫
activated是为了在页面活跃的时候,给一个默认的路径。
在beforeRouteLeave,就是用户跳转离开之前,我们记录一下用户最后的路径。保存下来。
效果:
activated函数和deactivated函数
activated函数是当页面处于活跃状态时候,回调的函数。
deactivated函数是当页面处于不活跃状态的时候,回调的函数。
之所以,activated和deactivated函数能够执行,这是因为在App.vue当中router-view放在了keep-alive当中。
如果像是下面图片所示的那样,没有keep-avlie了,那么这两个函数也就不会执行了。
本节课讲的内容
第117个视频 - keep-alive属性介绍
需求场景说明
现在我们通过在App.vue当中,给router-view标签包裹了keep-alive,所以上面图中的首页,关于,用户,档案四个组件都不会频繁创建和销毁了。但是我们的需求是:
我不管首页,关于,用户,我要档案这个组件每一次点击进来的时候,都要重新创建。
怎么实现。
keep-alive的属性
include属性和exclude属性
上面的需求,我们通过下面的代码,就可以实现了。
如果是有两个组件想要exclude的时候,我们使用下面的写法,就看可以了:
这里要注意:这里不要随便加空格。
正则表达式当中也不要随便加空格。
第118个视频 - tabbar基本结构的搭建。
ppt
需求场景描述
给我封装一个tabbar组件,让我可以自定义传入图片和文字,然后就能够直接用。
分析思路
第一,我们需要把tabbar封装成为独立的组件,希望不仅仅在这个项目当中能够用,下个项目当中也能够用的。下个项目当中,传入对应的文字和图片就可以根据项目需求生成对应的tabbar。
第二,tabbar的item的图标的个数也是不一定。文字,图片都不是定义死的。
首先,我们的大横框框,是一个组件,我们叫做TabBar,这个组件当中定义插槽,slot。让外界决定给我们这里传入几个插槽。
然后,我再封装TabBarItem这样的小组件,如果你的插槽当中准备放置四个东西的话,你可以创建四个TabBarItem给我的插槽替换掉的。
我通过flex布局,自动把这几个item布局好。
我分成了两类组件,大的叫做TabBar,小的组件叫做TabBarItem,在Item组件当中,图片和文字都是slot。
创建项目
第1个问题 - 显示的字体没有挨着屏幕边缘
这是因为body默认是有margin和padding这些东西的。
在开发当中,针对这种东西,我们一般会怎么做呢?
- assets当中一般都是放置资源,我们创建两个文件夹,img文件夹,放图片,css文件夹,放置css文件。
- css文件夹当中创建base.css文件,进行如下的设置。
设置完了之后,我们在main.js文件当中引用一下这个css文件。
这样这个css文件才会生效的。
但是我们的main.js当中尽量不要写上面的require的代码。
这个时候得到的效果是:
第2个问题:我们尽量不要再main.js当中写require css这种代码
我们应该在App.vue的style当中写。
效果是:
第3个问题:要初始化的样式非常多,所有初始化样式都写到App.vue不合适
我们在App.vue的style当中动态引用文件的。
就是如图这样写:
效果还是一样的:
第4个问题: 四个item没有水平分布
我们应该使用flex进行布局
<template>
<div id="app">
<div id="tab-bar">
<div class="tab-bar-item">首页</div>
<div class="tab-bar-item">分类</div>
<div class="tab-bar-item">购物车</div>
<div class="tab-bar-item">我的</div>
</div>
</div>
</template>
<script>
export default {
name: 'App',
components: {}
};
</script>
<style>
@import "./assets/css/base.css";
#tab-bar {
display: flex;
}
.tab-bar-item {
flex: 1; /* 会对多个item进行均等分*/
}
</style>
得到的效果,就是如下图所示:
第5个问题:所有的文字都是靠左边的
增加text-align,设置为center。
<template>
<div id="app">
<div id="tab-bar">
<div class="tab-bar-item">首页</div>
<div class="tab-bar-item">分类</div>
<div class="tab-bar-item">购物车</div>
<div class="tab-bar-item">我的</div>
</div>
</div>
</template>
<script>
export default {
name: 'App',
components: {}
};
</script>
<style>
@import "./assets/css/base.css";
#tab-bar {
display: flex;
}
.tab-bar-item {
flex: 1; /* 会对多个item进行均等分*/
text-align: center;
}
</style>
效果是:
第6个问题 - 当前item的高度很矮
如图所示:
一般我们的tabbar的高度是49px,在移动端。
像是ios或者android当中,都是49px的。
<template>
<div id="app">
<div id="tab-bar">
<div class="tab-bar-item">首页</div>
<div class="tab-bar-item">分类</div>
<div class="tab-bar-item">购物车</div>
<div class="tab-bar-item">我的</div>
</div>
</div>
</template>
<script>
export default {
name: 'App',
components: {}
};
</script>
<style>
@import "./assets/css/base.css";
#tab-bar {
display: flex;
}
.tab-bar-item {
flex: 1; /* 会对多个item进行均等分*/
text-align: center;
height: 49px;
}
</style>
效果:
第7个问题 - 现在tabbar的背景颜色和页面的背景重合
为了好看,要给tabbar一个颜色。
<template>
<div id="app">
<div id="tab-bar">
<div class="tab-bar-item">首页</div>
<div class="tab-bar-item">分类</div>
<div class="tab-bar-item">购物车</div>
<div class="tab-bar-item">我的</div>
</div>
</div>
</template>
<script>
export default {
name: 'App',
components: {}
};
</script>
<style>
@import "./assets/css/base.css";
#tab-bar {
display: flex;
background-color: #f5f4f5;
}
.tab-bar-item {
flex: 1; /* 会对多个item进行均等分*/
text-align: center;
height: 49px;
}
</style>
效果:
第8个问题 - tabbar在页面上边希望在下面
为了让tabbar跑到页面的下面,我们使用下面的样式代码:
效果:
第9个问题 - tabbar和页面的结合处有阴影
我们可以看看效果图:
就是图中的位置,在tabbar和页面主体之间有一点点阴影,这样看起来会比较平滑,不会很突兀。
所以在这里添加一点阴影。
我们要使用box-shadow属性了。
box-shadow有四个参数。
第一个参数是x轴。
第二个参数是y轴。
第三个参数是模糊程度。
第四个参数是颜色,颜色可以使用rgba,这样我们可以设置透明度。
阴影移动的原理
如图所示:
如上图所示,如果x为正的话,阴影是往右边走的。如果y为正的话,阴影是往下边走的。
我们现在是希望阴影往正上方走的,所以x应该为0,y应该是个负数。
代码:
<template>
<div id="app">
<div id="tab-bar">
<div class="tab-bar-item">首页</div>
<div class="tab-bar-item">分类</div>
<div class="tab-bar-item">购物车</div>
<div class="tab-bar-item">我的</div>
</div>
</div>
</template>
<script>
export default {
name: 'App',
components: {}
};
</script>
<style>
@import "./assets/css/base.css";
#tab-bar {
display: flex;
background-color: #f6f6f6;
position: fixed;
left: 0;
right: 0;
bottom: 0;
box-shadow: 0 -1px 1px rgba(100, 100, 100, .15);
}
.tab-bar-item {
flex: 1; /* 会对多个item进行均等分*/
text-align: center;
height: 49px;
}
</style>
效果:
第119个视频 - TabBar和TabBarItem组件封装
第1个问题 - 现在没有复用性
我们在components文件夹下,新建一个tabbar文件夹。
这是因为,后面我们要在components下面放上很多的封装的组件。
所以说,为了方便管理,我们新建一个tabbar的文件夹。
然后在tabbar的文件夹下面我们创建一个TabBar.vue这个组件。
然后我们将原来在App.vue当中的代码,抽离到TabBar.vue当中。
App.vue当中的代码:
<template>
<div id="app">
<tab-bar>
</tab-bar>
</div>
</template>
<script>
import TabBar from "./components/tabbar/TabBar";
export default {
name: 'App',
components: {
TabBar
}
};
</script>
<style>
@import "./assets/css/base.css";
</style>
TabBar.vue
<template>
<div id="tab-bar">
<div class="tab-bar-item">首页</div>
<div class="tab-bar-item">分类</div>
<div class="tab-bar-item">购物车</div>
<div class="tab-bar-item">我的</div>
</div>
</template>
<script>
export default {
name: "TabBar"
};
</script>
<style scoped>
#tab-bar {
display: flex;
background-color: #f6f6f6;
position: fixed;
left: 0;
right: 0;
bottom: 0;
box-shadow: 0 -1px 1px rgba(100, 100, 100, .15);
}
.tab-bar-item {
flex: 1; /* 会对多个item进行均等分*/
text-align: center;
height: 49px;
}
</style>
现在的效果不变。
第2个问题 - tabbar当中显示的是文字和图片,不仅仅是文字
我们准备一些图片文件:
第3个问题 - 图片怎么用?
我们需要在TabBar.vue当中引用图片。
但是我们引入图片的时候,我们发现这种路径,真的是太恶心了,这种路径太长了。
这个问题后面再说。
写一个锚点记录一下。
问题锚点
第4个问题 - 图片展示效果太大了。
现在我们在tabbar.vue当中引入了四张图片。
代码如下:
<template>
<div id="tab-bar">
<div class="tab-bar-item">
<img src="../../assets/img/tabbar/home.svg" alt="">
首页
</div>
<div class="tab-bar-item">
<img src="../../assets/img/tabbar/category.svg" alt="">
分类
</div>
<div class="tab-bar-item">
<img src="../../assets/img/tabbar/cart.svg" alt="">
购物车
</div>
<div class="tab-bar-item">
<img src="../../assets/img/tabbar/profile.svg" alt="">
我的
</div>
</div>
</template>
<script>
export default {
name: "TabBar"
};
</script>
<style scoped>
#tab-bar {
display: flex;
background-color: #f6f6f6;
position: fixed;
left: 0;
right: 0;
bottom: 0;
box-shadow: 0 -1px 1px rgba(100, 100, 100, .15);
}
.tab-bar-item {
flex: 1; /* 会对多个item进行均等分*/
text-align: center;
height: 49px;
}
</style>
得到的效果是:
这种图片的效果太大了。
所以这个时候,我要设置一下图片的大小。
通过下面的代码进行设置:
这样得到的效果如下所示:
第5个问题 - 图片和文字在一行显示
这个问题,主要是因为我们的img标签是一个行内元素,我们的文字也是一个行内元素。
我们应该把文字变成块级元素。我们使用div标签来包括一下文字。
这样得到一个效果,如下所示:
第6个问题 - 感觉现在tabbar.vue当中的逻辑很乱
tabbar.vue当中,不仅仅有关于tabbar的东西,还有item东西,不仅仅有item标签相关的东西,还有布局的css相关的东西。
全部都放在了tabbar.vue当中。
我发现这种逻辑是很乱的。
这个tabbar.vue这个组件,应该只关心大横框框的内容。不应该关心item当中文字和图片的这些东西。
所以,我们做下面的两个操作:
第一,我们将tabbar.vue当中template中关于tab-bar-item的部分,全部都放到App.vue当中。暂时的。
第二,我们将TabBar.vue当中关于tab-bar-item的样式也放到App.vue当中。暂时的。
这个时候,我们的TabBar.vue当中的代码逻辑就比较清晰了。
第7个问题 - App.vue当中代码太乱
我们最好是抽离一个TabBarItem这样的组件。
第一步,先将App.vue当中,TabBarItem相关的样式,抽离到我们的TabBarItem.vue当中。
第二步,在我们的App.vue当中,导入TabBarItem组件,然后在template当中使用。
第8个问题 - 我们应该怎么写TabBarItem的template呢?
我们现在先写成这样:
这个时候的效果是:
第9个问题 - 现在我们没有图片
我们在TabBarItem里面,添加图片:
效果如下:
第10个问题 - TabBarItem当中的文字和图片不能写死
既然,我们的TabBarItem当中的文字和图片不能够写死,那我们怎么办呢?
搞插槽就可以了。
吴雁月这样说。
所以,我们修改TabBarItem当中的内容,变成下面的样子:
然后我们在App.vue当中,给插槽,传入对应的值。
第11个问题 - 我们感觉TabBarItem上面的字体有点大
效果:
第12个问题 - 图片距离TabBar上方要留一点空间
我们想在这里留一点空间:
代码:
效果:
第13个问题 - 图片和文字之间的空间有点大
现在,我感觉图片和文字之间的空间有点大。
现在文字和图片两个,是紧挨着的。
为什么会有空间的呢?
因为图片下方会多3px。
我们通过下面的代码去掉:
效果:
现在为啥图片和文字还有一点空间?
这个是图片自己本身就有的空间。
我们可以看看图片本身:
第14个问题 - 图片和文字之间的距离想自定义一下
我感觉上面的图片和文字的空间又稍微有点小了。
我们可以自定义一下子,想要变成2px。
效果如下:
看起来就是好看很多了。
第15个问题 - 重新讨论第10个问题
现在所有的item都是展示相同的文字和图片。
我们想要动态展示文字和图片,让调用方决定。
所以,我们在TabBarItem当中搞两个具名插槽,第一个插槽表示图片,第二个插槽表示文字:
然后我们在App.vue当中的tab-bar-item中,传入我们自己定义的图片和文字,传入具名插槽当中。
我们得到的效果是:
如果我们想要传入五个tab-bar-item,也是可以的。
效果上自动就给我们进行了均等分了:
到这里,我们的基本封装,就已经好了。
到了这一步,以后再其他项目当中,我们要使用tabbar就非常方便。我们什么都不用考虑。
我们只需要在App.vue当中,用tabbar的标签,用tabbar-item的标签,然后传入具体的文字和具体的图片就可以了。
不需要考虑均等分的问题,不需要考虑文字和图片的距离的问题等等。
这就是我们封装的,完全独立的组件。
第120个视频 - 给TabBarItem传入active图片
第1个问题 - 现在只有普通图片,没有活跃状态图片
现在别人给我们传图片的时候,应该传进来两张图片,一张是非活跃的图片,一张是活跃的图片。
这个时候,我们必须在我们的TabBarItem当中,再新增一个插槽的。
我们封装组件的时候,应该这样封装。
让别人把两张图片都传好。
然后我内部动态决定,显示哪一张图片。
这种点击活跃的效果,应该组件自己具备这个能力,不要让开发者决定。
第一步,我们在TabBarItem当中新增一个插槽
第二步,我们在App.vue当中调用tab-bar-item的时候,传入新的插槽。
但是这个时候的效果是这个样子的:
第2个问题 - 怎么能自动展示对应图片?
我们使用v-if和v-else来控制一下。
这样默认情况下,我们的效果是这样的:
如果我们的isActive值变成了true,我们的效果就是这样的:
第3个问题 - 图片活跃的时候,文字应该变成红色
这个时候,我们应该给slot-text来动态绑定属性。
我们应该这样修改TabBarItem当中的代码:
但是我们现在的代码中,isActive是为true的,但是我们得到的效果是如下:
我们看到文字,并没有变红。这是为什么呢?
第4个问题 - 为什么插槽中的文字没有变红?
这是因为插槽slot的本质是,在用户进行slot替换的时候,是会直接复制代码替换slot的代码的。
如下图所示的:
插槽的目的就是要被替换的。我们替换过之后,就不会有isActive这种控制的属性了。
所以,在插槽上动态绑定属性没有什么用的。
第5个问题 - 那怎么动态显示文字呢?
为了解决上面的问题,当需要给插槽动态绑定属性的时候,我们不给slot绑定,我们用div包裹slot,在div上面动态绑定属性。
如下图所示:
这样得到的效果是:
第6个问题 - slot上面的v-if会生效吗?
我们刚才测试,slot上面的v-if是有效的。
但是,slot不是就应该被替换的吗?
这个问题,王红元没有解释,为什么v-if在slot上面是生效的。
但是,为了保险,我们给slot都包裹上一个div,写成下面的样子:
效果跟上面是一样的:
第121个视频 - TabBarItem和路由结合效果
第1个问题 - 当我点击对应item的时候,显示对应的页面
也就是说,我们点击item的时候,和路由跳转结合起来,效果是这样的:
让我们每个item的点击,和大的路由组件,一一对应起来。
所以,我们要手动装一个vue-router。
然后我们在src文件夹下面新建一个router文件夹,然后在router文件夹下面新建一个index.js的配置文件。
import Vue from 'vue'
import VueRouter from "vue-router";
// 1. 安装插件
Vue.use(VueRouter)
// 2. 创建路由对象
const routes = [
]
const router = new VueRouter({
routes
})
// 3. 导出路由对象
export default router
然后在main.js当中挂载路由:
然后就可以配置映射关系了。
第一,可以先创建四个路由组件了。
针对大的页面,我们一般不会再components目录当中创建组件。
我们会在src当中创建一个views文件夹。有的人也喜欢起名字叫做pages。
在views当中,我们再创建4个文件夹:home,cart,category,profile。
然后在对应的目录下,分别创建Home,Cart,Category,Profile组件。
components文件夹当中,放置的是公共的组件。
页面放置在views文件夹当中,每个页面的子组件都放在对应的各自的文件夹中。
越往上层越抽象,越往下层越具体。
第2个问题 - 配置组件映射关系
import Vue from 'vue';
import VueRouter from "vue-router";
const Home = () => import('../views/home/Home');
const Category = () => import('../views/category/Category');
const Cart = () => import('../views/cart/Cart');
const Profile = () => import('../views/profile/Profile');
// 1. 安装插件
Vue.use(VueRouter);
// 2. 创建路由对象
const routes = [
{
path: '',
redirect: '/home'
},
{
path: '/home',
component: Home
},
{
path: '/category',
component: Category
},
{
path: '/cart',
component: Cart
},
{
path: '/profile',
component: Profile
}
];
const router = new VueRouter({
routes
});
// 3. 导出路由对象
export default router;
第3个问题 - 点击对应item,页面显示对应路由组件
所以要监听整个item的点击。
我们监听item的点击,不是在App.vue当中监听,也就是说,不是在调用组件的地方监听。
我们是要在组件内部监听。
监听item的itemClick点击,然后在methods当中写一个itemClick方法,在方法内通过this.$router.replace()
方法来实现路由跳转。
跳转的路径是需要从父组件当中传过来的。不能够是写死的。
所以,我们通过TabBarItem的props接收父组件的link属性,然后通过this.$router.replace(this.link)
实现路由跳转。
之后,我们在父组件,也就是App.vue上面每个TabBar中的tab-bar-item标签上,添加link属性。
并且根据vue-router的做法,我们需要在App.vue当中添加router-view来进行占位。
所以,TabBarItem当中的代码如下:
<template>
<div class="tab-bar-item" @click="itemClick">
<div v-if="!isActive"><slot name="item-icon"></slot></div>
<dii v-else><slot name="item-icon-active"></slot></dii>
<div :class="{active: isActive}"><slot name="item-text"></slot></div>
</div>
</template>
<script>
export default {
name: "TabBarItem",
props: {
link: String
},
data() {
return {
isActive: true
};
},
methods: {
itemClick(){
this.$router.replace(this.link)
}
}
};
</script>
<style scoped>
.tab-bar-item {
flex: 1; /* 会对多个item进行均等分*/
text-align: center;
height: 49px;
font-size: 14px;
}
.tab-bar-item img {
width: 24px;
height: 24px;
margin-top: 3px;
vertical-align: middle;
margin-bottom: 2px;
}
.active {
color: #ff5777;
}
</style>
在App.vue当中的代码,如下:
<template>
<div id="app">
<router-view></router-view>
<tab-bar>
<tab-bar-item link="/home">
<img slot="item-icon" src="./assets/img/tabbar/home.svg" alt="">
<img slot="item-icon-active" src="./assets/img/tabbar/home_active.svg" alt="">
<div slot="item-text">首页</div>
</tab-bar-item>
<tab-bar-item link="/category">
<img slot="item-icon" src="./assets/img/tabbar/category.svg" alt="">
<img slot="item-icon-active" src="./assets/img/tabbar/cart_active.svg" alt="">
<div slot="item-text">分类</div>
</tab-bar-item>
<tab-bar-item link="/cart">
<img slot="item-icon" src="./assets/img/tabbar/cart.svg" alt="">
<img slot="item-icon-active" src="./assets/img/tabbar/cart_active.svg" alt="">
<div slot="item-text">购物车</div>
</tab-bar-item>
<tab-bar-item link="/profile">
<img slot="item-icon" src="./assets/img/tabbar/profile.svg" alt="">
<img slot="item-icon-active" src="./assets/img/tabbar/profile_active.svg" alt="">
<div slot="item-text">我的</div>
</tab-bar-item>
</tab-bar>
</div>
</template>
<script>
import TabBar from "./components/tabbar/TabBar";
import TabBarItem from "./components/tabbar/TabBarItem";
export default {
name: 'App',
components: {
TabBar,
TabBarItem
}
};
</script>
<style>
@import "./assets/css/base.css";
</style>
得到的效果如下:
第122个视频 - TabBar的颜色动态控制
第1个问题 - 现在item永远都是活跃的,这个太糟糕
应该是点击了谁,谁处于活跃,谁就是红色的。
现在都是红色,是因为,在TabBarItem组件当中,我们的活跃属性是写死的。
我们上面的isActive不能够写死。
要根据某个条件来判断,当前item是不是活跃的。
某个条件是什么条件?
在路由当中$route就表示当前活跃的路径组件映射对象。
我们可以拿到处于活跃的路由的path。
我们可以将item本身的path(这个是父组件传过来的link)。
和当前活跃路由的path是不是一回事。
这样就能够确定,当前的item是不是活跃的。
锚点:切记切记。这种结合路由判断是否活跃的小技巧。
得到的效果如下:
第2个问题 - 活跃的文字颜色现在是写死的哦
看看下面的图:
我们活跃的文字颜色,直接是写死的。
这个是不好的。
如果人家用户传进来的active的图片,是蓝色,那肯定是需要活跃的文字颜色也是蓝色的。
不能够是红色。
他又不能够进入到你的封装好的组件当中,去修改颜色。
我们封装组件,就是希望所有的东西是动态的。
我希望别人用我们的TabBarItem的时候,动态传入颜色。
这个时候,我们需要从父组件当中动态传入样色,然后给我们的文字动态绑定样式。
代码如下:
这样做过之后,我们可以动态传入活跃文字颜色为蓝色:
第123个视频 - 知识回顾
第1个问题: 我感觉App.vue当中代码有点多
想要把下面的内容抽离出去:
我们在components文件夹下面创建MainTabBar.vue文件,然后把上面的红框当中的代码,都拷贝到MainTabBar.vue的template当中。
然后将script当中当中的导入代码,也拷贝到MainTabBar.vue当中,将TabBar和TabBarItem两个组件注册在MainTabBar组件当中。
最终得到的MainTabBar当中的代码是:
然后我们的App.vue当中就变得非常简单:
第2个问题 :知识回顾
第124到140个视频与前面的视频重复
第124到第140个视频
与
第107到123个视频
重复。