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

13次阅读

共计 6187 个字符,预计需要花费 16 分钟才能阅读完成。

写着写着发现会写不少内容 … 全部写在一篇文章里感觉太多了不方便看,所以分为上下篇吧 …
温馨提示
此文章,仅是做完项目后的个人觉得可以总结下来的操作 / 思路,接触 vue 不久的朋友应该会有收获。此项目也才是萌新做的第二个 Vue 项目,使用了脚手架工具(vue-cli 2.x 非 3),前辈老手们有时间看的话,有写得不好的地方还请多多指导!~
成果预览
仅实现项目首页、项目详情页、城市列表页组件的页面 / 逻辑。
千万别点我
项目初始化
先用脚手架生成项目框架。
因为做的是一个移动端网页,所以我们可以有一些配置:
第一步 配置 meta 标签
index.html 里加个 meta 标签:
<meta name=”viewport”content=”width=device-width,initial-scale=1.0,
minimum-scale=1.0,maximum-scale=1.0,user-scalable=no”>
在网页的 <head> 标签中增加以上代码,可以让网页的宽度自动适应手机屏幕的宽度。
其中:
width=device-width:表示宽度是设备屏幕的宽度
initial-scale=1.0:表示初始的缩放比例
minimum-scale=1.0:表示最小的缩放比例
maximum-scale=1.0:表示最大的缩放比例
user-scalable=no:表示用户是否可以调整缩放比例
以上我设置的参数的目的是:想要一打开网页,就自动以原始比例显示,并且不允许用户修改,不允许用户缩放。
第二步 初始化默认样式
在不同的手机浏览器上,默认的一些样式是不同统一的。我们需要把这些不同手机的初始化样式做一个统一。
所以可以引入个 reset.css
这是一个初始化的代码,其中一些初始化配置可以自行根据需求修改。

想看 / 拿代码的可以到我 Github 上拿:src/assets/styles/reset.css
第三步 解决 1 像素边框方案
在移动端页面开发,常常有个 1 像素边框 的问题。
也就是有的手机屏幕分辨率比较高,如果我们在页面上写 border-bottom 啥的样式,会导致这些手机屏幕分辨率高的当中,1px 边框显示成 2px 边框或 3px 边框等显示成多像素。
为了解决这个 1 像素边框问题,我们就引入了 border.css(貌似这是哪个团队提出的解决方案?忘记啦,只知道怎么用 … 但仍然要表示感谢。还有其他解决方案,这个就自行搜索了。)

两百多行不贴出来了,想看 / 拿完整代码的可以到我 Github 上:src/assets/styles/border.css
具体这个解决方案的用法,看过 border.css 代码的同志就会发现很简单:在元素上根据想要的需求加以下这些类名。

按照字面意思理解就行。
比如要给一个元素加上一像素的下边框,就直接加个类名:<div class=”border-bottom>” 就行了。
要给一个元素加上一像素的上边框和下边框,就直接加类名:<div class=”border-topbottom”> 就行了。
边框也可以改颜色的,举个项目中的例子,按照这种格式去改颜色(本文例子有 css 代码的话基本都是 stylus 的写法):

对应页面:
第四步 引入 fastclick 库
在移动端开发中,某些机型、某些浏览器上,click 点击事件要延迟 300ms 执行。
要解决这个问题,我们引入个 fastclick 库
npm install fastclick –save
–save 的意思是:不管在开发环境测试,还是线上跑代码,安装了的库都可以使用。并且下载好后,自动存到 package.json 的 dependencies 属性中,比如这里 install 的 fastclick:

然后在入口函数 main.js 中引入和使用:
import fastClick from ‘fastclick’、fastClick.attach(document.body)

第五步 配置使用 iconfont
先在阿里巴巴矢量图标库创建一个项目
然后在 iconfont 上选购,添加到购物车,选好后添加到自己的项目,然后下载到本地。
下载的东西中只要用到这几个:

然后把 iconfont.css 放到一个文件夹中并且在入口函数 main.js 处引入后就可以全局使用了。
举个使用的例子:

注意类名要加 iconfont,然后这里在 span 里输入的代码就是你选中的图标的代码:

第六步 自定义目录
这步其实应该穿插在做项目过程中进行的,这里先列出来。
在 build 文件夹下的 webpack.base.conf.js 中配置,如下图,圈起来的是我在项目中配置的。

这样有配置后就可以使用自定义目录了。比如按照我上面的配置的话
import src/assets/styles/border.css
就可以写成
import styles/border.css 了。
首页开发中想记录下来的
轮播插件
安装与使用
这里使用的是 vue-awesome-swiper。

先安装:npm install vue-awesome-swiper –save 然后用法一些的其实都可以在其文档中查阅到:
鉴于在各个页面都可能会用到轮播功能, 所以直接在全局也就是入口文件 main.js 处引入。

然后使用:

比如要使用按钮区的话,就需要配置参数。根据个人在项目中的需求,可以查阅其 github 文档按需使用。
这里值得一提的是:假如在轮播图的下面有东西

比如这个 test。在网速慢慢加载的时候,可能 test 会先在上面显示,然后等图片撑开区域的时候再跑回下面。
为了防止这种抖动,最好这样子做:
给轮播 swiper 外面套一层类为 wrapper 的 div,然后给 div 固定大小。比如在项目中这里的轮播图片的宽高比是 364:97 约等于 3.75,高度是宽度的百分之 26.6。
所以响应式开发可以这样给 div 框样式:
现在 vw 单位的兼容性其实可以了。
有种兼容性很好的方案:
overflow: hidden
width: 100%
height: 0
padding-bottom: 26.6%
这种方式也是高度是宽度的 26.6%。
其实吧。。这个 vue-aowsome-swiper 组件目前为止已经不存在这种抖动问题了。。
还有个改变插件默认颜色的问题
比如插件按钮区配置后,默认按钮颜色是蓝色小圆点。

审查元素可以看出:

那我们如下这样子加样式去改变行吗?

一般这样子问都是不行滴 …
因为有 scoped 作用域的原因,这个类的属性的设置是在原本的 swiper 组件下,而不是在我们这里设置的 swiper 组件下。
应该这样设置:

意思是在.wrapper 下的所有类中,找.swiper-pagination-bullet-active 类。
>>> 是具有穿透作用域的意思,穿透其他组件的作用域。
如果文字在一行中多了,实现省略号效果

比如,这里是个 p 标签。
P 标签里的数据太多的话 希望显示一个 … 省略号。
可以这样利用 text-overflow 属性:

要实现溢出时产生省略号的效果,应该在定义两个样式:强制文本在一行内显示(white-space:nowrap)和溢出内容为隐藏(overflow:hidden),只有这样才能实现溢出文本显示为省略号效果。

发送 ajax 请求
一般情况下在 mounted 钩子里发送 ajax 请求数据。想详细了解生命周期 => 我有写过一篇文章
请求方式,看自己,这个项目中 axios、fetch 两个方式我都写过。
配置
首先,用 vue 脚手架工具生成的工程里面,只有 static 目录(静态文件目录)下,才能被外界访问到。
我们就把本地的一些模拟数据放在这个 static 目录下,自己建个文件夹存储数据。这个项目中是 static/mock/index.json:

这数据是本地的模拟数据,我们不希望到时候一起把它 push 到线上,可以在.gitignore 里这样配置:

现在这个文件夹下的所有东西都不会被传到线上了。
当然,也不会提交到本地的 git 仓库里面。
这样配置还不够,目前在局部根组件中写的请求的路径是这样的,拿项目中举例:

当我们上线这代码的时候,我们请求的网址,最好前面加上个‘api’,如下面的红框中:/api/index.json 这样子最好了。
很好的是,恰好 Vue 脚手架里面有这样一个转发的代理功能。通过这个功能,就可以实现以上构想。
config 配置文件下,有个 index.js 脚本,官方给我们提供了一个 proxyTable{} 的配置项,我们可以这样配置:

这意思是:当我们请求 api 的时候,依然映射到本地 8080 端口,然后访问任何以 api 为开头的 url 的时候,做一个路径替换,代理访问到 /static/mock 处。
实际这功能 是 webpack-dev-sever 提供的。
改过配置文件,需要重启下服务器,重启后就可以如下请求数据了:

这里实际上访问的就是 static/mock/index.json 里的内容了。
优化
如果可以的话,最好能在局部根组件里请求一次 ajax 数据,然后从局部根组件里把接收到的数据分别传给各个局部的子组件,而不是每个局部的子组件都发送一次 ajax 请求。
城市列表页开发中想记录下来的
记 box-sizing:border-box 的一个应用场景

这里是这样写的 css

现在在输入框里面输入文字字符超过输入框大小时:

这两部会贴着, 不那么好看。想要给这个输入框加个 padding,留点间隙会更好。
但直接在 input 下面加行吗?

页面:

因为包裹 input 框的 div 没设置 width,也就是 width 是 auto。input 框的 width 设置的是 100%。
如果直接给 input 加 padding 左右一点的话是会撑开 input 框的宽度的。所以会溢出。
那怎样解决这个问题呢?
我们只是想设置一个左右 padding 值,没想让 input 框长宽变化。所以,我们在 Input 下面加个:box-sizing: border-box
这样的话,我们直接给 input 设置的宽高就包括了 padding、border 在内的宽高了。
此时我们要修改 padding 的值的话,就只会在这个框内变化 不会撑开框宽高了。

在列表栏用第三方插件 better-scroll
初始开发页面时,到这步,因为加了比较多字母对应的区域,页面出现了滚动条的时候:

为了使用 better-scroll 库,我们仅让列表区域显示到刚进页面时能显示到的区域就行了,不需要出现滚动条。所以可以给最外层的包裹整个列表区域的框 div.list 加个 overflow: hidden 就行了。(整个列表页指下图中的从当前城市开始到最后,城市选择和输入框是其他子组件写的了)

接下来具体 better-scroll 的用法,github 上其文档有说明,各人可根据具体情况查阅使用。
字母表的逻辑实现思路
点击事件中,我们需要查看点击的内容时,可以利用点击事件的事件对象
在 Vue 的一个点击事件里,在 methods 定义点击方法时这个方法可以接收一个参数 e,e 就是我们点击到的那个事件对象。
要拿到我们点击到的事件对象的内容 可以这样来:e.target.innerText
举个项目中的例子验证一下:

比如此时页面上点击了 D F J

需求 1
当点击到相应的字母的时候显示 list 组件的对应城市的区域。
better-scroll 这个第三方插件有个方法可以实现这个需求,思路是:
监听所点击的字母表里字母的值 letter 的变化,一旦 letter 变了,就利用 better-scroll 的提供的一个接口,如下图划线的部分它会让 better-scroll 的滚动区域,自动滚动到某一个元素上。需要给这个方法传递我们滚动到该元素的该元素 DOM。
利用这个思路就可以实现需求 1 了。
需求 2
手指拖动字母表,字母对应的列表跟着联动展示。
思路:
利用 touchstart、touchmove、touchend 事件,并给个限制(touchStatus)只当手指在屏幕移动的时候才执行一些操作。然后用个数组 letters 来存放字母表的所有字母,这里的 letters 大概是 [‘A’,’B’,’C’…],并让页面 v -for 这个 letters 来显示相应的内容。用数组存放这些字母的原因也是为了实现这需求的主要思想:根据下标,找到对应的字母。
接下来,先用 offsetTop 找到字母表中字母 A 距离包裹它的顶部的高度。下图红框。

这里的 74 代表的是红色方框的高度。
然后获取移动时手指所在的高度,此高度时针对于客户端的高度,用 clientY。touchmove 事件有个事件对象,事件对象里有个 touches 数组,touches[0] 里面就有当前手指的信息,包括 clientY 属性。

实时获取我们手指的位置。

我们要获取距离包裹块的高度 => 也就是 clientY 的高度要减去 headers 区和 serach 区的高度,这两个区高度是 79px。

然后算我们手指移到的字母在数组中的下标 逻辑是 <font color=”#dd0000″>(touchY – startY)/ 每个字母的高度,再把结果向下取个整。</font>

最后,把这个下标在 letters 数组中对应的字母传给需求 1 所在的组件利用需求 1 的思路就行了。(这里的传值涉及到了兄弟组件之间的传值,此时该页面比较简单不建议用 vuex,可以用 event bus/ 找同一父组件做媒介传值,具体方法百度。)
在我项目中最终逻辑代码是这样写的:

最后做个最佳实践,用个 if,确保 index 的值。
性能优化
第一处
handleTouchMove 是手指滑动的时候就会执行,而我们框起来的也就是 A 字母距离包裹框的高度是固定的,不用每次滑动的都执行这段代码。
所以此处需要优化,这样子来:
在 data 里初始化 startY 为 0

然后用生命周期钩子 updated 去执行给 startY 赋值的语句

这里为什么用 updated 这个钩子呢?在项目中初次渲染字母表组件 alphabet.vue 的时候,从它父组件 City.vue 传过来的值是一个空对象。当 City.vue 里 ajax 动态获取数据后,从 City 再传到 alphabet 的值让数据从初次的空对象发生改变。在数据更新完毕后,就触发了 updated 钩子,此时给 startY 赋值就是值都有,而且只会赋一次。
第二处:函数节流
通过函数节流 减少 handleTouchMove() 的执行频率(因为我们手指在滑动的时候 该函数执行频率很高的)。
怎样使得函数节流呢?
通过定时器和清除定时器来实现。
先在 data 中初始化 timer 为 null,然后这样来用

这样子用定时器进行函数节流的话:
如果已经正在做这件事情的时候,我呢,让它延迟 16ms 之后再去执行。假设在这 16ms 之间你又去做了手指的滚动,那么它会把上一次你要做的操作给清除掉(clearTimeout),然后重新执行你这次要做的事情(等于以最终的手指滑动的位置为准)。
通过这种函数节流方式,会大大减少该函数的执行次数,从而提高网页性能。
函数节流的方式,当一个函数执行次数很多想要减少而且减少也没影响的时候,是很有必要采用的一种方式。
localStorage
可能用户会有不小的概率关闭了本地自动存储的功能,一般我们使用 localStorage 的时候 都要使用 try catch 代码块,这样就算用户关闭本地自动存储功能,也不会让整个代码都不能运行,只是没了这个 localStorage 的功能而已。
举个项目中的例子:

上篇结束,未完待续 …

正文完
 0