用Vue开发仿旅游站webapp项目总结

<article data-v-13f76525="" itemscope="itemscope" itemtype="http://schema.org/Article" class="article" data-v-3f216172="">

用Vue开发仿旅游站webapp项目总结 (上)

该说的话,该表明的上篇已经表明了。谢谢上篇评论区一些同学~ 很鼓励我,不过下下篇估计没了,这篇总结完,下下篇可能就是之后学习路的总结记录啦。

有些话还是要说的

接触vue不久的朋友应该会有收获。此项目也才是萌新做的第二个Vue项目,使用了脚手架工具(vue-cli2.x非3),前辈老手们有时间看的话,有写得不好的地方还请多多指导!~

项目中Vuex的不那么低级的用法

因为这只是总结操作/思路,没一步步讲代码,还是先给个官网的图,这样方便看点:

<figure>[图片上传中...(image-f338bd-1542539098452-67)]

<figcaption></figcaption>

</figure>

前提假设,在脚手架中,我们跟路由引入全局的方法一致去在全局中引入Vuex。创建一个文件夹store,在文件夹下创建个index.js脚本。在store/index.js里面写Vuex的一些用法逻辑,然后在入口函数main.js里引入就行了。如下:

先忽略马赛克...

<figure>[图片上传中...(image-b94ae6-1542539098452-66)]

<figcaption></figcaption>

</figure>

在入口函数main.js中引入:

<figure>[图片上传中...(image-5b4695-1542539098452-65)]

<figcaption></figcaption>

</figure>

此时index.js里面的逻辑

<figure>[图片上传中...(image-82ddc6-1542539098452-64)]

<figcaption></figcaption>

</figure>

拆分

以此项目中为例,随着项目的开发,index.js里的逻辑会越来越复杂,所以选择拆分。

<figure>[图片上传中...(image-8768cd-1542539098451-63)]

<figcaption></figcaption>

</figure>

所以,我们建立两个脚本(state.js、mutations.js)来分别存储这两段代码。

<figure>[图片上传中...(image-d50bc9-1542539098451-62)]

<figcaption></figcaption>

</figure>

<figure>[图片上传中...(image-97cd5c-1542539098451-61)]

<figcaption></figcaption>

</figure>

然后在index.js中引入:

<figure>[图片上传中...(image-6a1307-1542539098451-60)]

<figcaption></figcaption>

</figure>

这样拆分完成,简洁不少。

mapState辅助函数

在项目中,如下图用法去取得state里面city的数据,是不是显稍长了点?

<figure>[图片上传中...(image-3e5839-1542539098451-59)]

<figcaption></figcaption>

</figure>

Vuex为我们提供了一个方便的API -> mapState

这样用:

<figure>[图片上传中...(image-901062-1542539098451-58)]

<figcaption></figcaption>

</figure>

mapState是指,我把State区域里面的公有属性值映射到这个计算属性里。

在这里是:把state里city这个公有属性的值映射到这里的计算属性city里。

这样子的话,就可以把

<figure>[图片上传中...(image-8e8d44-1542539098451-57)]

<figcaption></figcaption>

</figure>

变成可以直接调用这个计算属性:

<figure>[图片上传中...(image-adcb33-1542539098451-56)]

<figcaption></figcaption>

</figure>

...mapState({}/[])这里面可以是数组也可以是对象。是数组的话,那我们就等于直接给计算属性取名city了,和公有属性的名称一样。

是对象的话,我们就可以给计算属性自定义取名。

举个...mapState(对象)的例子:

在当前城市这里也可以改:

<figure>[图片上传中...(image-9031f7-1542539098451-55)]

<figcaption></figcaption>

</figure>

<figure>[图片上传中...(image-d2a880-1542539098451-54)]

<figcaption></figcaption>

</figure>

这里就是传对象给mapState,等于把公有属性city的值映射到计算属性currentCity里。

此时就可以这样用:

<figure>[图片上传中...(image-1474b5-1542539098451-53)]

<figcaption></figcaption>

</figure>

这样子就不用写的那么复杂了。

mapMutations辅助函数

利用Vuex提供的这个API可以简化下列代码:

<figure>[图片上传中...(image-4a9668-1542539098451-52)]

<figcaption></figcaption>

</figure>

这样用:

<figure>[图片上传中...(image-b38839-1542539098451-51)]

<figcaption></figcaption>

</figure>

mutations里面是有changeC2()这个函数的(这个命名就....仅当测试,轻喷)。我们想在组件中调用mutations里的这个函数去改变公有数据区域state里的值,运用mapMutations可以这样简洁地在组件中调用。

<figure>[图片上传中...(image-1105ca-1542539098451-50)]

<figcaption></figcaption>

</figure>

<figure>[图片上传中...(image-978a7b-1542539098451-49)]

<figcaption></figcaption>

</figure>

这什么意思呢?

mutations里有个叫做changeC2这个方法,这里在该组件的methods中是把这个mutations里的changeC2方法映射到了该组件中methods的changeC2方法里。

...mapMutations()参数也是可接收[]/{}的。

还是用...mapMutations(对象写法)好理解一点,如下,做个测试:

<figure>[图片上传中...(image-11165-1542539098451-48)]

<figcaption></figcaption>

</figure>

(乱入的小姐姐~)

这样子写也是可以的。也更容易理解这里的映射。

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 中看到

<figure>[图片上传中...(image-92a8a6-1542539098451-47)]

<figcaption></figcaption>

</figure>

每次我们路由切换的时候(从Home组件页面跳转到City组件页面或反之的时候)都要发送ajax请求数据。

这样子就重复请求ajax数据了,因为每次路由切换到一个组件的时候,都要重新执行该组件钩子函数,如果该组件有在mounted钩子里请求ajax的话,就每次路由切换都要执行了。这样性能不好。

此时就可以用vue内置的 keep-alive标签来优化。

在全局根组件App.vue中

<figure>[图片上传中...(image-73712a-1542539098451-46)]

<figcaption></figcaption>

</figure>

代表的是显示当前路由的组件,而在这个外面加个 vue 内置的 keep-alive标签后,就可以实现这样一个功能:

我的路由的内容被加载过一次之后,我的路由中的内容就都放到内存之中,下一次再进这个路由的内容的时候,就只需要从内存里把以前的内容拿出来就可以了。

此时,不管切换多少次路由,都只有两次ajax数据请求了。

<figure>[图片上传中...(image-97a38b-1542539098451-45)]

<figcaption></figcaption>

</figure>

因为使用了keep-alive标签,导致有了新的生命周期函数activated(keep-alive组件激活时调用)、deactivated(keep-alive 组件停用时调用)。

之后的路由切换不再请求ajax数据是因为组件内容是从内存取了不会再重新创建了,对应的mounted钩子函数不会再执行了。

但每次切换、页面重新显示的时候,activated钩子会执行。

此时可以利用这个钩子实现一个需求:

当在列表页选择点击了哪个城市后,路由切换回到首页时,首页显示的数据是对应着该城市的数据(意味着ajax只请求该城市对应的Home组件页面的数据),然后如果在列表页,选择了与原本在Home页相同的城市的话,就不发送ajax请求新数据。

之前,发ajax请求的时候,是直接这样子发:

<figure>[图片上传中...(image-daab32-1542539098451-44)]

<figcaption></figcaption>

</figure>

实际上,在发送ajax请求的时候,应该带一个参数的。带的这个参数应该是 Vuex 中公有的这个数据。也就是当点击哪个城市的时候,这个参数就是对应哪个城市的名称。

怎么在请求中带参数呢?

在请求的连接后面 加上 ?=参数数据

如:

<figure>[图片上传中...(image-1b7730-1542539098451-43)]

<figcaption></figcaption>

</figure>

在网页上试一试,发送的参数就在下面

<figure>[图片上传中...(image-2fc318-1542539098451-42)]

<figcaption></figcaption>

</figure>

实现需求的思路是:

在每一次页面重新显示的时候(activated钩子函数触发)我们判断此时页面上的城市是否和上一次显示的城市相同 如果不相同 就发送ajax请求

然后我们设置个空字符串 lastCity (当做一个中间缓存值用于判断)

<figure>[图片上传中...(image-9882fb-1542539098451-41)]

<figcaption></figcaption>

</figure>

紧接着,当页面挂载完毕的时候,我们给它赋予当前页面的城市数据。

<figure>[图片上传中...(image-15994d-1542539098451-40)]

<figcaption></figcaption>

</figure>

然后在页面更新的时候,判断上一次的 this.lastCity 的值 是否等于 更新后的 this.city 的值。如果不等于的话,就发送新的ajax请求,请求相应city的数据,如果等于的话就不发送。

<figure>[图片上传中...(image-e3e25b-1542539098451-39)]

<figcaption></figcaption>

</figure>

此时,通过keep-alive新增的生命周期钩子函数以及lastCity这个缓存值就实现了我们要的功能了。

详情页想记录下来的东西

实现画廊组件功能的主要思路

先看个gif说明此功能啥样:

<figure>[图片上传中...(image-cbf370-1542539098451-38)]

<figcaption></figcaption>

</figure>

就这个玩意儿。这个就是画廊。上面有轮播,下面有页码。

因为这不仅仅只有一个页面会用到,可能以后很多页面都会用到,所以 写个全局公用的组件Gallary.vue。

<figure>[图片上传中...(image-e20bbe-1542539098451-37)]

<figcaption></figcaption>

</figure>

需要在详情页的一个组件 Banner.vue 里面去使用这个公共组件

<figure>[图片上传中...(image-3e9548-1542539098451-36)]

<figcaption></figcaption>

</figure>

gallary用fixed占满全屏。

<figure>[图片上传中...(image-e7af3f-1542539098451-35)]

<figcaption></figcaption>

</figure>

利用flex布局,让这个如下wrapper区域垂直居中

<figure>[图片上传中...(image-53866f-1542539098451-34)]

<figcaption></figcaption>

</figure>

<figure>[图片上传中...(image-cc9bf-1542539098451-33)]

<figcaption></figcaption>

</figure>

然后使用 Vue-awosome-swiper第三方插件,先放入两张图

<figure>[图片上传中...(image-efe49c-1542539098451-32)]

<figcaption></figcaption>

</figure>

然后给那个.wrapper定义个宽高100%。按照如下这种写法的话width先有个100%的宽度了然后height也100%的意思是针对于这个width的宽度来说的,所以这里定义了这个100%的height的意思是这个height与width的宽度相等 意思是一个正方形。

<figure>[图片上传中...(image-db58d4-1542539098451-31)]

<figcaption></figcaption>

</figure>

此时页面:

<figure>[图片上传中...(image-8ef395-1542539098451-30)]

<figcaption></figcaption>

</figure>

我们让图片按比例自适应这个正方形,再把wrapper下的背景颜色去掉,此时两张图片可以正常显示轮播了:

<figure>[图片上传中...(image-77109-1542539098451-29)]

<figcaption></figcaption>

</figure>

然后加页码,其实这个页码就是该插件的按钮区和配置参数一起控制的。先加上按钮区代码:

<figure>[图片上传中...(image-1b0006-1542539098451-28)]

<figcaption></figcaption>

</figure>

vue-awesome-swiper这个插件是基于swiper实现的,这里面的配置参数比如 pagination 可以去 swiper 官网找的。

我们去官网找找看能不能找到配置我们页码需求的参数(当然是能找到的,不然我还写个啥...):

<figure>[图片上传中...(image-d244ba-1542539098451-27)]

<figcaption></figcaption>

</figure>

<figure>[图片上传中...(image-db4f56-1542539098451-26)]

<figcaption></figcaption>

</figure>

由此可见,这个页面的翻页样式就是对应这个paginnation中的paginationType 中的 fraction。

现在来配置参数,首先在swiper上加上 :options="swiperOption"。

<figure>[图片上传中...(image-1af3ab-1542539098451-25)]

<figcaption></figcaption>

</figure>

然后在data里配置。先把按钮区配置出来:

<figure>[图片上传中...(image-35705f-1542539098451-24)]

<figcaption></figcaption>

</figure>

再把paginationType ‘fraction’ 分式 给配置出来

<figure>[图片上传中...(image-57169f-1542539098451-23)]

<figcaption></figcaption>

</figure>

现在就有了,但在小小的地方,审查元素才可以看见

<figure>[图片上传中...(image-d31cb0-1542539098450-22)]

<figcaption></figcaption>

</figure>

原本框架的样式,通过审查元素找出这里是绝对定位。

<figure>[图片上传中...(image-52d1ee-1542539098450-21)]

<figcaption></figcaption>

</figure>

那么我们这样改bottom -1rem就行了?

<figure>[图片上传中...(image-278426-1542539098450-20)]

<figcaption></figcaption>

</figure>

那肯定是不行的...

这里有个坑,当感觉代码没写错,页面却没达到预期的时候,就是再次审查元素的时候了...审查元素发现这个插件组件有个swiper-container里还定义了个overflow: hidden。

所以我们在画廊组件里穿透作用域来改掉这个样式就行

<figure>[图片上传中...(image-d9a966-1542539098450-19)]

<figcaption></figcaption>

</figure>

<figure>[图片上传中...(image-587241-1542539098450-18)]

<figcaption></figcaption>

</figure>

画廊逻辑部分很简单就跳过不记录了,不过有个坑还是值得提一下。

在画廊自身的组件gallary.vue里测试功能的时候都好好的,但是gallary这个公有组件是要在详情页的Banner.vue组件中引入的。

那么问题就来了,当我们在Banner组件对应的页面一下子点进去gallary组件对应的页面的时候,轮播插件会出现一个计算宽度高度的问题。如下gif图这样:

<figure>[图片上传中...(image-2b834d-1542539098450-17)]

<figcaption></figcaption>

</figure>

要解决这个问题,需要在gallary的轮播插件配置参数中加上这两个配置参数

<figure>[图片上传中...(image-5bbbcd-1542539098450-16)]

<figcaption></figcaption>

</figure>

加上这两个参数的意思是:

我这个swiper插件,只要监听到我这个元素,或者父级元素变化的时候(这个监听的就是swiper和swiper的父级元素),这个插件会自动地自动刷新一次,重新计算宽高。

通过这次自我刷新,就可以解决轮播插件的这个计算宽度高度的问题。(这些配置参数在swiper官网都可以查到怎样用的)

实现header区块渐隐渐现的效果

看个gif。

<figure>[图片上传中...(image-5ef8cb-1542539098450-15)]

<figcaption></figcaption>

</figure>

仅提这段,当手指往下滑的时候,逐渐显示清晰之后一直清晰的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事件。

<figure>[图片上传中...(image-61675-1542539098450-14)]

<figcaption></figcaption>

</figure>

因为这个是全局事件,所以我们在其他组件中也可以监测到。这样很容易引发一系列严重的隐藏的bug。

所以我们应该在detail下header.vue组件中对该组件解绑:

对应keep-alive引用而可以使用的钩子还有一个deactivated钩子。

该钩子在页面即将被替换成新的页面的时候触发。

所以这里我们利用deactivated钩子removeEventListener函数来解绑全局事件。

<figure>[图片上传中...(image-aafc69-1542539098450-13)]

<figcaption></figcaption>

</figure>

组件中name属性的三个作用

1、做递归组件的时候会用到

举个例子,list组件的name: 'DetailList'。在list组件模板中想要使用递归组件调用自身时,就要根据name的值来用作标签(detail-list)调用。如下:

<figure>[图片上传中...(image-2e7534-1542539098450-12)]

<figcaption></figcaption>

</figure>

2、对某个页面取消keep-alive的缓存的时候会用到

假设有个Detail.vue组件,其name: ' Detail'。当想要keep-alive全局组件时,Detail.vue组件对应的页面,路由重新切换到这个页面不用去内存中取缓存值,可以利用Detail组件的name的值如下使用:

<figure>[图片上传中...(image-b8d368-1542539098450-11)]

<figcaption></figcaption>

</figure>

这样等于是除了Detail.vue组件,其他组件都可以拥有设置keep-alive后的功能。

3、vue-devtools调试工具

<figure>[图片上传中...(image-d09c48-1542539098450-10)]

<figcaption></figcaption>

</figure>

如上图红框里的组件名称,这里的名称就取决于设置的组件的name属性的值。

动态路由中ajax动态获取各个路由目录对应的值

举项目中例子说明,注意下图红框中的值

<figure>[图片上传中...(image-de7c13-1542539098450-9)]

<figcaption></figcaption>

</figure>

此时我们应该获取的是,动态路由中 id为0002的参数的数据。

<figure>[图片上传中...(image-154ace-1542539098450-8)]

<figcaption></figcaption>

</figure>

这样设置后,其实动态路由中,会把对应的参数存在 这个变量id里。

每次请求,希望把这个id带给后端,就可以这样写:

写法一

<figure>[图片上传中...(image-68131f-1542539098450-7)]

<figcaption></figcaption>

</figure>

现在可以在network的XHR里看见我们发送给后端的请求中附带了 id

<figure>[图片上传中...(image-9d85cc-1542539098450-6)]

<figcaption></figcaption>

</figure>

写法二

前面只写接口名 后面这样子写(我们把参数放到params去了):

<figure>[图片上传中...(image-ede1ee-1542539098450-5)]

<figcaption></figcaption>

</figure>

其实还有问题,这地方我想一步一步来总结

设置的动态路由,只是动态加了个id参数并不能自动让页面也跟着动态显示数据,动态显示数据还得靠ajax请求数据。

而目前这个组件是在mounted钩子中执行ajax请求的,并且该组件有keep-alive的作用影响着。这样当路由跳转到id为0003/0004...页面的时候,组件也只会从内存中取出第一次进入该组件某个id的页面。并不能根据id对应显示页面。

怎样通过ajax请求而动态显示数据呢?有两种方法,两种方法上面都提到过。

  • 第一种 在列表页选择某个城市,路由自动跳转回首页后,首页需要显示的是该城市对应的数据这里记录过。(提示一下:利用activated钩子,判断参数id是否等于之前的id,如果不等则重新执行ajax请求。)

  • 第二种 组件中name属性的三个作用中的第二个作用已经说出了解决方案。

这两种方式留给读者自行思考,想不通的可以参考下我github该仓库里的代码。src/pages/detail/Detail.vue

路由滚动行为

来看个gif

<figure>[图片上传中...(image-7e31be-1542539098449-4)]

<figcaption></figcaption>

</figure>

这就是路由跳转页面会带来的影响。会把当前页面(Home.vue)的屏幕的显示的宽高 带到 我们跳转到的页面(Detail.vue)上去。造成如上gif所示现象。

这样来解决。

<figure>[图片上传中...(image-775637-1542539098449-3)]

<figcaption></figcaption>

</figure>

这个在官方文档中称为 路由的滚动行为。

我们现在是想让每次路由切换进入到下一个页面的时候滚动在顶部显示。 继续看文档。

<figure>[图片上传中...(image-b23009-1542539098449-2)]

<figcaption></figcaption>

</figure>

把这段代码复制到路由配置项中:

<figure>[图片上传中...(image-122275-1542539098449-1)]

<figcaption></figcaption>

</figure>

这样就实现我们预期的需求。

配置打包

前后端联调

一般等后端数据写好后,我们就不再使用自己前端模拟的数据,而是去使用后端给过来的数据来调试。

如果要访问服务器上的数据的话,要在配置文件config/index.js下的proxyTable里把target改为服务器的地址(可以写内网的IP地址 或 外网的域名都行)。然后改pathRewrite的话,就见实际情况数据存放在服务器的哪个文件夹下了。

真机测试

这里的前端的项目是通过 webpack-dev-server 启动的,默认不允许通过ip来访问内部服务器。所以我们需要把默认的配置项做修改。

想让这个 webpack-dev-sever 能够被ip访问的话,需要这样配置下:

<figure>[图片上传中...(image-97e922-1542539098449-0)]

<figcaption></figcaption>

</figure>

(貌似漏点了... 其实也没关系....)

然后这里真机测试的时候,有时候就会遇到一些在PC上开发时发现不了的bug以及要考虑兼容性。这个就要各人根据实际情况来改了。

打包上线

vue-cli 2.x中,就可以在项目目录下执行指令 npm run build,此时Vue的脚手架工具会帮我们自动地对src目录下源代码进行打包编译生成一个能被浏览器运行的代码,同时这个代码也是压缩过后的代码。

打包完成后会生成一个dist文件夹,给到后端开发人员,或者直接把static文件夹里的内容扔到后端服务器根目录上就OK了。这只是基本的操作,想要改变访问路径或怎样的操作,就各位小伙伴自己去找了,有心自会找到~

结语

这篇文章仅是在这个项目中对于我个人而言觉得可以总结记录下来的,更具体更详细的知识和流程,感兴趣不妨去imooc支持一下DellLee老师的这门课程~

有部分地方代码量太多不方便贴出来,想参考代码学习的可以进我Github

希望也能帮到你们~

</article>

作者:Sadhu
链接:https://juejin.im/post/5bec0eeef265da61193b65cd
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

没有更多推荐了,返回首页