共计 6835 个字符,预计需要花费 18 分钟才能阅读完成。
用 Vue 开发仿旅游站 webapp 项目总结(上)该说的话,该表明的上篇已经表明了。谢谢上篇评论区一些同学~ 很鼓励我,不过下下篇估计没了,这篇总结完,下下篇可能就是之后学习路的总结记录啦。
有些话还是要说的
接触 vue 不久的朋友应该会有收获。此项目也才是萌新做的第二个 Vue 项目,使用了脚手架工具(vue-cli2.x 非 3),前辈老手们有时间看的话,有写得不好的地方还请多多指导!~
项目中 Vuex 的不那么低级的用法
因为这只是总结操作 / 思路,没一步步讲代码,还是先给个官网的图,这样方便看点:
前提假设,在脚手架中,我们跟路由引入全局的方法一致去在全局中引入 Vuex。创建一个文件夹 store,在文件夹下创建个 index.js 脚本。在 store/index.js 里面写 Vuex 的一些用法逻辑,然后在入口函数 main.js 里引入就行了。如下:
先忽略马赛克 …
在入口函数 main.js 中引入:
此时 index.js 里面的逻辑
拆分
以此项目中为例,随着项目的开发,index.js 里的逻辑会越来越复杂,所以选择拆分。
所以,我们建立两个脚本(state.js、mutations.js)来分别存储这两段代码。
然后在 index.js 中引入:
这样拆分完成,简洁不少。
mapState 辅助函数
在项目中,如下图用法去取得 state 里面 city 的数据,是不是显稍长了点?
Vuex 为我们提供了一个方便的 API -> mapState
这样用:
mapState 是指,我把 State 区域里面的公有属性值映射到这个计算属性里。
在这里是:把 state 里 city 这个公有属性的值映射到这里的计算属性 city 里。
这样子的话,就可以把
变成可以直接调用这个计算属性:
…mapState({}/[])这里面可以是数组也可以是对象。是数组的话,那我们就等于直接给计算属性取名 city 了,和公有属性的名称一样。
是对象的话,我们就可以给计算属性自定义取名。
举个 …mapState(对象)的例子:
在当前城市这里也可以改:
这里就是传对象给 mapState,等于把公有属性 city 的值映射到计算属性 currentCity 里。
此时就可以这样用:
这样子就不用写的那么复杂了。
mapMutations 辅助函数
利用 Vuex 提供的这个 API 可以简化下列代码:
这样用:
mutations 里面是有 changeC2()这个函数的(这个命名就 …. 仅当测试,轻喷)。我们想在组件中调用 mutations 里的这个函数去改变公有数据区域 state 里的值,运用 mapMutations 可以这样简洁地在组件中调用。
这什么意思呢?
mutations 里有个叫做 changeC2 这个方法,这里在该组件的 methods 中是把这个 mutations 里的 changeC2 方法映射到了该组件中 methods 的 changeC2 方法里。
…mapMutations()参数也是可接收 []/{} 的。
还是用 …mapMutations(对象写法)好理解一点,如下,做个测试:
(乱入的小姐姐~)
这样子写也是可以的。也更容易理解这里的映射。
Getter、Module
Getter 和 Module 该项目中都没有用到。
Getter
假如我们想根据 state 的值,通过一些计算得到新的值的话,就可以用 getter 来提供新的数据,避免数据冗余。它的定义也和 computed 一样。
getter 可以认为是 store 的计算属性。就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
Getter 接受 state 作为其第一个参数,也可以接收其他 getter 作为第二个参数。可以属性访问(store.getters),可以通过方法访问,也有 mapGetters 辅助函数(该辅助函数主要也是映射关系,将 store 中的 getter 映射到局部组件的计算属性中)。官方文档给的例子也比较好理解,可以自行看文档。
Module
Module 的话,看得勉强理解,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块。
当应用变得非常复杂时,可以使用 Module 避免 store 对象变得相当臃肿。
我觉得 Module 具体的应用要在复杂项目中亲自练手过才能熟练,目前不敢轻易下手记录,暂且记一笔。
使用 keep-alive 优化性能
我们可以在 开发者工具 network 的 xhr 中看到
每次我们路由切换的时候(从 Home 组件页面跳转到 City 组件页面或反之的时候)都要发送 ajax 请求数据。
这样子就重复请求 ajax 数据了,因为每次路由切换到一个组件的时候,都要重新执行该组件钩子函数,如果该组件有在 mounted 钩子里请求 ajax 的话,就每次路由切换都要执行了。这样性能不好。
此时就可以用 vue 内置的 keep-alive 标签来优化。
在全局根组件 App.vue 中
<router-view> 代表的是显示当前路由的组件,而在这个外面加个 vue 内置的 keep-alive 标签后,就可以实现这样一个功能:
我的路由的内容被加载过一次之后,我的路由中的内容就都放到内存之中,下一次再进这个路由的内容的时候,就只需要从内存里把以前的内容拿出来就可以了。
此时,不管切换多少次路由,都只有两次 ajax 数据请求了。
因为使用了 keep-alive 标签,导致有了新的生命周期函数 activated(keep-alive 组件激活时调用)、deactivated(keep-alive 组件停用时调用)。
之后的路由切换不再请求 ajax 数据是因为组件内容是从内存取了不会再重新创建了,对应的 mounted 钩子函数不会再执行了。
但每次切换、页面重新显示的时候,activated 钩子会执行。
此时可以利用这个钩子实现一个需求:
当在列表页选择点击了哪个城市后,路由切换回到首页时,首页显示的数据是对应着该城市的数据(意味着 ajax 只请求该城市对应的 Home 组件页面的数据), 然后如果在列表页,选择了与原本在 Home 页相同的城市的话,就不发送 ajax 请求新数据。
之前,发 ajax 请求的时候,是直接这样子发:
实际上,在发送 ajax 请求的时候,应该带一个参数的。带的这个参数应该是 Vuex 中公有的这个数据。也就是当点击哪个城市的时候,这个参数就是对应哪个城市的名称。
怎么在请求中带参数呢?
在请求的连接后面 加上 ?= 参数数据
如:
在网页上试一试,发送的参数就在下面
实现需求的思路是:
在每一次页面重新显示的时候(activated 钩子函数触发)我们判断此时页面上的城市是否和上一次显示的城市相同 如果不相同 就发送 ajax 请求
然后我们设置个空字符串 lastCity (当做一个中间缓存值用于判断)
紧接着,当页面挂载完毕的时候,我们给它赋予当前页面的城市数据。
然后在页面更新的时候,判断上一次的 this.lastCity 的值 是否等于 更新后的 this.city 的值。如果不等于的话,就发送新的 ajax 请求,请求相应 city 的数据,如果等于的话就不发送。
此时,通过 keep-alive 新增的生命周期钩子函数以及 lastCity 这个缓存值就实现了我们要的功能了。
详情页想记录下来的东西
实现画廊组件功能的主要思路
先看个 gif 说明此功能啥样:
就这个玩意儿。这个就是画廊。上面有轮播,下面有页码。
因为这不仅仅只有一个页面会用到,可能以后很多页面都会用到,所以 写个全局公用的组件 Gallary.vue。
需要在详情页的一个组件 Banner.vue 里面去使用这个公共组件
gallary 用 fixed 占满全屏。
利用 flex 布局,让这个如下 wrapper 区域垂直居中
然后使用 Vue-awosome-swiper 第三方插件,先放入两张图
然后给那个.wrapper 定义个宽高 100%。按照如下这种写法的话 width 先有个 100% 的宽度了然后 height 也 100% 的意思是针对于这个 width 的宽度来说的,所以这里定义了这个 100% 的 height 的意思是这个 height 与 width 的宽度相等 意思是一个正方形。
此时页面:
我们让图片按比例自适应这个正方形,再把 wrapper 下的背景颜色去掉,此时两张图片可以正常显示轮播了:
然后加页码,其实这个页码就是该插件的按钮区和配置参数一起控制的。先加上按钮区代码:
vue-awesome-swiper 这个插件是基于 swiper 实现的,这里面的配置参数比如 pagination 可以去 swiper 官网找的。
我们去官网找找看能不能找到配置我们页码需求的参数(当然是能找到的,不然我还写个啥 …):
由此可见,这个页面的翻页样式就是对应这个 paginnation 中的 paginationType 中的 fraction。
现在来配置参数,首先在 swiper 上加上 :options=”swiperOption”。
然后在 data 里配置。先把按钮区配置出来:
再把 paginationType‘fraction’分式 给配置出来
现在就有了,但在小小的地方,审查元素才可以看见
原本框架的样式,通过审查元素找出这里是绝对定位。
那么我们这样改 bottom -1rem 就行了?
那肯定是不行的 …
这里有个坑,当感觉代码没写错,页面却没达到预期的时候,就是再次审查元素的时候了 … 审查元素发现这个插件组件有个 swiper-container 里还定义了个 overflow: hidden。
所以我们在画廊组件里穿透作用域来改掉这个样式就行
画廊逻辑部分很简单就跳过不记录了,不过有个坑还是值得提一下。
在画廊自身的组件 gallary.vue 里测试功能的时候都好好的,但是 gallary 这个公有组件是要在详情页的 Banner.vue 组件中引入的。
那么问题就来了,当我们在 Banner 组件对应的页面一下子点进去 gallary 组件对应的页面的时候,轮播插件会出现一个计算宽度高度的问题。如下 gif 图这样:
要解决这个问题,需要在 gallary 的轮播插件配置参数中加上这两个配置参数
加上这两个参数的意思是:
我这个 swiper 插件,只要监听到我这个元素,或者父级元素变化的时候(这个监听的就是 swiper 和 swiper 的父级元素),这个插件会自动地自动刷新一次,重新计算宽高。
通过这次自我刷新,就可以解决轮播插件的这个计算宽度高度的问题。(这些配置参数在 swiper 官网都可以查到怎样用的)
实现 header 区块渐隐渐现的效果
看个 gif。
仅提这段,当手指往下滑的时候,逐渐显示清晰之后一直清晰的 div 景点详情框逻辑。
这个逐渐显示清晰的这块是用个 div 框来 fixed 定位写的。
逻辑
一开始 v -show 不显示这个 div 框并且让该 div 框的 opacity 为 0,在 activated 钩子函数中检测全局 scroll(window.onscroll)事件(即检测滚动条的状态,滚动条一旦动了就触发 scroll 事件),当触发 scroll 事件时,执行一个方法,此方法里面写逻辑。写的逻辑是:当滚动条往下滑动 60px 外时让这个 div 框的 v -show 参数为 true 并且通过公式 let opacity = document.documentElement.scrollTop / 140 来让该 div 框随着越往下滑动清晰度越高,然后在这条语句下面限制 opacity 透明度值为 1:opacity = opacity > 1 ? 1 : opacity。
显而易见:document.documentElement.scrollTop 的意思是获取当前页面的滚动条纵坐标位置。
这样功能实现了,但还有个很重要的坑。对全局事件的解绑。
对全局事件的解绑
在 header 区块逻辑中,我们在 activated 钩子中定义了个全局 scroll 事件。
因为这个是全局事件,所以我们在其他组件中也可以监测到。这样很容易引发一系列严重的隐藏的 bug。
所以我们应该在 detail 下 header.vue 组件中对该组件解绑:
对应 keep-alive 引用而可以使用的钩子还有一个 deactivated 钩子。该钩子在页面即将被替换成新的页面的时候触发。
所以这里我们利用 deactivated 钩子和 removeEventListener 函数来解绑全局事件。
组件中 name 属性的三个作用
1、做递归组件的时候会用到
举个例子,list 组件的 name: ‘DetailList’。在 list 组件模板中想要使用递归组件调用自身时,就要根据 name 的值来用作标签(detail-list)调用。如下:
2、对某个页面取消 keep-alive 的缓存的时候会用到
假设有个 Detail.vue 组件,其 name: ‘ Detail’。当想要 keep-alive 全局组件时,Detail.vue 组件对应的页面,路由重新切换到这个页面不用去内存中取缓存值,可以利用 Detail 组件的 name 的值如下使用:
这样等于是除了 Detail.vue 组件,其他组件都可以拥有设置 keep-alive 后的功能。
3、vue-devtools 调试工具
如上图红框里的组件名称,这里的名称就取决于设置的组件的 name 属性的值。
动态路由中 ajax 动态获取各个路由目录对应的值
举项目中例子说明,注意下图红框中的值
此时我们应该获取的是,动态路由中 id 为 0002 的参数的数据。
这样设置后,其实动态路由中,会把对应的参数存在 这个变量 id 里。
每次请求,希望把这个 id 带给后端,就可以这样写:
写法一
现在可以在 network 的 XHR 里看见我们发送给后端的请求中附带了 id
写法二
前面只写接口名 后面这样子写(我们把参数放到 params 去了):
其实还有问题,这地方我想一步一步来总结
设置的动态路由,只是动态加了个 id 参数并不能自动让页面也跟着动态显示数据,动态显示数据还得靠 ajax 请求数据。
而目前这个组件是在 mounted 钩子中执行 ajax 请求的,并且该组件有 keep-alive 的作用影响着。这样当路由跳转到 id 为 0003/0004… 页面的时候,组件也只会从内存中取出第一次进入该组件某个 id 的页面。并不能根据 id 对应显示页面。
怎样通过 ajax 请求而动态显示数据呢?有两种方法,两种方法上面都提到过。
第一种
在列表页选择某个城市,路由自动跳转回首页后,首页需要显示的是该城市对应的数据这里记录过。(提示一下:利用 activated 钩子,判断参数 id 是否等于之前的 id,如果不等则重新执行 ajax 请求。)
第二种
组件中 name 属性的三个作用中的第二个作用已经说出了解决方案。
这两种方式留给读者自行思考,想不通的可以参考下我 github 该仓库里的代码。src/pages/detail/Detail.vue
路由滚动行为
来看个 gif
这就是路由跳转页面会带来的影响。会把当前页面(Home.vue)的屏幕的显示的宽高 带到 我们跳转到的页面(Detail.vue)上去。造成如上 gif 所示现象。
这样来解决。
这个在官方文档中称为 路由的滚动行为。
我们现在是想让每次路由切换进入到下一个页面的时候滚动在顶部显示。继续看文档。
把这段代码复制到路由配置项中:
这样就实现我们预期的需求。
配置打包
前后端联调
一般等后端数据写好后,我们就不再使用自己前端模拟的数据,而是去使用后端给过来的数据来调试。
如果要访问服务器上的数据的话,要在配置文件 config/index.js 下的 proxyTable 里把 target 改为服务器的地址(可以写内网的 IP 地址 或 外网的域名都行)。然后改 pathRewrite 的话,就见实际情况数据存放在服务器的哪个文件夹下了。
真机测试
这里的前端的项目是通过 webpack-dev-server 启动的,默认不允许通过 ip 来访问内部服务器。所以我们需要把默认的配置项做修改。
想让这个 webpack-dev-sever 能够被 ip 访问的话,需要这样配置下:
(貌似漏点了 … 其实也没关系 ….)
然后这里真机测试的时候,有时候就会遇到一些在 PC 上开发时发现不了的 bug 以及要考虑兼容性。这个就要各人根据实际情况来改了。
打包上线
vue-cli 2.x 中,就可以在项目目录下执行指令 npm run build,此时 Vue 的脚手架工具会帮我们自动地对 src 目录下源代码进行打包编译生成一个能被浏览器运行的代码,同时这个代码也是压缩过后的代码。
打包完成后会生成一个 dist 文件夹,给到后端开发人员,或者直接把 static 文件夹里的内容扔到后端服务器根目录上就 OK 了。这只是基本的操作,想要改变访问路径或怎样的操作,就各位小伙伴自己去找了,有心自会找到~
结语
这篇文章仅是在这个项目中对于我个人而言觉得可以总结记录下来的,更具体更详细的知识和流程,感兴趣不妨去 imooc 支持一下 DellLee 老师的这门课程~
有部分地方代码量太多不方便贴出来,想参考代码学习的可以进我 Github。
希望也能帮到你们~