仿去哪儿网项目视频学习总结

4,753 阅读10分钟

     本文就不根据整个项目从头到尾来详写了,掘金社区很多写得很好的大神已经把这整个项目总结的文章写的非常好了,例如:Chrislinlin作者写的Vue.js开发去哪儿网WebApp,还有一些写的好的文章,我就不一一列举了。
     所以本文只记录我个人记录在项目学习笔记本上的一些点。这个仿去哪儿项目的学习,我个人的主要目的是为了了解一个App开发需要如何去做,如何组件化开发,如何利用Git进行代码管理(完整地看过Git操作视频学习,但只看不操作压根就是纸上谈兵,这个项目让我对Git真正有了一个入门的理解),还有巩固学习的vue的知识点。

1.项目框架图

大体框架如图,图源自:作者Chrislinlin

2.项目环境搭建

①nodejs
②代码管理(本项目选用码云)新建项目仓库
③Git for windows下载

添加SSH公钥并将电脑与线上仓库连接
     1.生成公钥
     2.打开Git Bash,输入ssh-keygen -t rsa -C "xxx@xxx.com"根据提示按下3次回车
     3.查看并复制刚才生成的公钥cat /.ssh/id_rsa.pub
     4.将公钥粘贴到git仓库管理处建立连接
     5.添加成功后,在Git Bash输入ssh -T git@gitee.com若提示Hi xxx,but...则为连接成功

⑤将线上生成的README.md文件克隆到本地
     1.在码云刚才新建的项目仓库右边的克隆/下载按钮选择SSH,复制链接
     2.打开Git Bash,用cd进入到自己新建的项目文件目录
     3.输入git clone+ 刚才复制的链接
     4.这样就把线上生成的项目克隆到本地刚才新建的项目文件目录

在刚才的目录里面生成vue项目脚手架(此处以vue2.x版本为例介绍)
     1.在该项目文件目录下运行cmd或者powershell(或直接vs终端控制台)
     2.输入npm install --global vue-cli回车确认(安装的为2.9.6版本)

     3.输入vue init webpack xxx(xxx)为项目名称,此处用Travel命名
         a.提示xxxProject name xxx,不能用含有大写字母的项目名称命名,改用travel
         b.Project description?项目描述,没有描述就直接回车
         c.Author 项目作者填写
         d.Vue.build (Runtime+complier跟Runtime only)选择Runtime+complier具体区别请看 Runtime-Compiler和Runtime-Only的区别
         e.Install vue-router?是否安装vue路由,Yes
         f.Use ESlint to lint your code?Yes 是否使用ESlint对代码进行一个规范化约束
              Pick an ESlint prest选择Standand
         (说实话ESlint这玩意儿,如果以前没用过,一开始用的时候时不时给你提示一个波浪线、红点错误提示,真的有时候逼死强迫症,但是用了两三天,啊,我的代码真整齐,真舒服,现在如果敲代码没了这东西,我都觉得浑身难受)
         g.Set up unit tests? 是否使用单元测试,这个项目是没使用的No
         h.Set up e2e tests with Nightwatch? 是否进行端到端的测试,一样的No
         i.npm yarn选择npm做整个项目的包管理

⑦测试脚手架是否成功安装,能否正确运行,输入cd Travel再输入npm run dev回车进入到端口初始页面
⑧同步线上线下初始代码
     1.在项目文件目录里面右键选择git bash here打开git bash
     2.先输入git status以查看在你上次提交之后是否有修改,以及现在所有分支,能看到增加了许多新文件
     3.输入git add .来添加当前项目的所有文件到本地缓存区
     4.输入git commit -m 'xxxxx'xxx内容为你对本次提交的修改的文件的描述
     5.输入git push将上述操作的文件推到云端项目代码管理仓库
做完上述步骤(有一些简单能看懂的步骤我可能直接跳过了没有写入进来),项目的环境就已经搭建完成了,以上是我个人的理解,中间可能有一些写错的地方,或者写的不好的地方还请大家指出。

3.项目初始文件介绍

readme.md              //项目的说明文件
package.json         //第三方依赖包配置、项目开发依赖包、依赖环境信息
package.lock.json    //帮助我们去确定安装的第三方依赖包的具体的版本,保持团队编程的统一,锁住一些包的版本,保证程序在这些版本的包下运行是能够正常运行的
license             //开源协议的说明
index.html          //项目默认的首页模版文件
.postcssrc.js        //对 postcss 的配置项
.gitignore          //不需要上传到 git 上的文件管理 、忽略上传到云端的文件
.eslintrc.js        //对写的代码检测是否标准做一个检测、配置了对代码的规范
.eslintignore       //配置不需要 eslintrc 检测工具检测的文件
.editorconfig       //配置编辑器总风格统一的自动化格式的语法
.babelrc            //项目写的代码是 Vue 的大文件组件的代码的写法,所以需要通过 babel 这种语法解析器做一些语法上的转换,最终转换成浏览器能够编译执行的代码,babel 需要做额外配置时,就放在文件里面
static                  //static 目录放的是静态资源,要用到的静态图片啊或者后续需要模拟的 json 数据
node_modules                 //项目中需要用到的第三方 node 包
src                         //放的是项目的源代码
src/main.js                  //整个项目的入口文件
src/app.vue                 //整个项目最原始的根组件
src/router/index.js          //项目的路由放置位置
src/components               //项目中要用到的小组件
src/assets                  //项目中需要用到的图片
config                      //放置项目配置文件
config/index.js              //放基础配置
config/dev.ent.js            //开发环境配置信息
config/prod.ent.js           //线上环境配置信息
build                      //放置项目打包的 webpack 配置信息,vue-cli 会自动构建
build/webpack.base.conf.js   //基础的 webpack 配置信息
build/webpack.dev.conf.js    //开发环境的 webpack 配置信息
build/webpack.prod.conf.js   //线上环境的 webpack 配置信息

4.项目笔记记录点

4.1 多页应用与单页应用对比

多页应用:页面跳转→返回HTML
     优点:首屏时间快,SEO效果好(搜索引擎权重效果好)
     缺点:页面切换慢
单页应用:页面跳转→JS渲染
     优点:页面切换快
     缺点:首屏时间稍慢,SEO效果差

4.2 项目自定义快捷路径

①(vue2.x脚手架):在webpack.base.conf.js里面往下拉,找到resolve里面的alias处可以自定义快捷路径

resolve: {
    extensions: ['.js', '.vue', '.json'],
    alias: {
      'vue$': 'vue/dist/vue.esm.js',
      '@': resolve('src'),
      'styles': resolve('src/assets/styles'),
      'common': resolve('src/common')
    }
  },

②(vue3.0脚手架):整合到了'vue.config.js'文件里面,如下这样配置

module.exports = {
    chainWebpack: (config) => {
      config.resolve.alias
        .set('styles', path.join(__dirname, './src/assets/styles'))
        .set('@', path.join(__dirname, './src/'))
        .set('common', path.join(__dirname, './src/common/'))
    }
}

4.3 每开发一个新功能,Git的一系列操作

①先到线上代码管理仓库处创建新功能分支(建议以页面组件名称命名,便于管理)
②进入本地项目目录下git bash here,输入git pull将线上分支拉取下来
③输入git checkout xxxxxx切换到新分支上(xxxxxx为刚才新建的分支名)
④输入git status查看自己现在所处分支,确认无误开始写代码

4.4每完成一个新功能的开发后,Git的一系列操作

①进入本地项目目录下右键选择git bash here打开git bash
②输入git add .将所有修改的文件添加到本地缓存区
③输入git commit -m 'xxxxxx'提交,并输入提交上线的描述(xxxx内容为对此次提交、改动的描述)
④输入git push将上面提交的代码推到云端项目代码管理仓库
(做完上述步骤之后还没好,我们仅仅是将刚才分支的内容推到了云端,是还没有做完的,我们还得将分支合并)
⑤输入git checkout master切换到master分支上
⑥输入git merge origin/xxxxxx合并分支(xxxxxx为刚才新建的分支)一般在实际操作中,是要经过测试后,测试没问题,在上线前合并分支
⑦再次输入git push将合并后的master分支内容推到云端同步

4.5 加一个固定div防止页面因为网速加载问题发生抖动

这个固定div的模板样式为如下,以一个例子css数据来展示

width100%
height0
overflow:hidden
padding-bottom31.25%

这种处理方法在整个项目组件的地方都用到了,做过这个项目的小伙伴应该都有映像^_^

4.6 static下存放静态资源

     项目里面的图片,我一开始都是用的自己下载的一些图片,然后是把它们放到项目组件目录下新建的一个img文件,但我发现引用后页面没效果,我还以为我哪里写错了,反复检查src引用的链接有没有问题,检查几次之后我上网查了一下,发现有些人也是这样,然后就放到了static目录下的文件里面,页面才有效果,所以这些静态资源还是得放在static下。

4.7 优化轮播图手动滑动后就停止自动轮播的问题(自己加的)

     这个在项目里是没有提到的,是我自己在页面测试的时候发现的问题,只要我手动滑动一次轮播图之后,那个轮播效果就停止了,停在一副图上。然后也上网查了这个问题怎么解决,解决办法其实也很简单,看一下下面的轮播图控制的代码,下面有指出

 swiperOption: {
        pagination: '.swiper-pagination',
        loop: true, //是否循环轮播
        speed: 5000, //滑动速度
        autoplay: {
          delay: 2200, //自动切换时间间隔
          stopOnLastSlide: false //是否切换到最后一幅图时停止自动切换
        },
        autoplayDisableOnInteraction: false// 避免手动滑了之后就停止自动轮播,去掉autoplay可以放入autoplay对象中
      }

4.8 文本超出盒子区域后自动显示省略号css样式模板

     要做到文本超出盒子就显示...省略号的css代码

overflow:hidden
white-space:nowrap
text-overflow:ellipsis

     如果不行就在上一级容器中加 min-width:0

4.9 手机移动端li-1像素边框问题

因为手机移动端写1像素的时候,实际上能够明显看到不是1像素的边框,那么如何解决这个问题呢?本项目引入border.css来解决这个问题,已经是写好的文件了,直接引入即可 引入之后,在需要1像素边框的地方看是上下左右哪个地方需要,如果顶部需要就写一个class=“border-top”,同样底部需要就写一个class=“border-bottom”,具体原理可以参考这篇文章 移动端1像素边框问题

4.10 router点击后变色问题

     因为router-link也有超链接样式,点击之后会变色,所以要给这一层的盒子加一个固定的颜色color。否则点击过后颜色就改变了

4.11 绑定方法时有无()与获取事件源对象

     项目中点击城市列表的时候,要获取事件源对象,绑定方法时@click="handlexxx",不添加括号,自动传入事件源对象,写入methods时写方法名handlexxx(e){}如此即可调用事件源对象,例如获取点击区域下的文本,就是e.target.innerText即可获取到。
     如果@click="handlexxx()"绑定时加上这个括号直接这样用是获取不了的,还得加上$event,即@click="handlexxx($event)",如此就能调用事件源对象了

4.12 组件间传值问题:父子组件间传值(子传父、父传子),兄弟组件间传值

     本来呢,在我看来这里可以化为两个问题,兄弟组件间传值的过程其实也就是子A先传父,父再传到子B那,那么最终无非也就是子传父、父传子这两个问题,因为项目里面用的也是这种方法,但是我后来上网查了其实还有一种方法(Vuex在这里面我也就不单独算一种方法了,学过的vue应该都知道,大家那么聪明),还有一种方法就是兄弟之间借助于事件车,通过事件车的方式传递数据。

①父传子:子组件写在props下即可,写上类型(初始值)即可用:调用
②子传父:子组件通过$emit触发父组件的自定义事件,this.$emit('xxx自定义事件名',附加参数),附加的参数会传给监听器回调,示例代码如下图,具体可参考 父子组件传值
子组件: 父组件: ③兄弟组件间通过事件车传值
     1.兄弟之间传递数据需要借助于事件车,通过事件车的方式传递数据
     2.创建一个Vue的实例,让各个兄弟共用同一个事件机制
     3.传递数据方,通过一个事件触发bus.$emit(方法名,传递的数据)

     4.接收数据方,通过mounted(){}生命周期触发bus.$on(方法名,function(接收数据的参数){用该组件的数据接收传递过来的数据}),此时函数中的this已经发生了改变,可以使用箭头函数
     实例如下,我们可以创建一个单独的js文件eventVue.js,内容如下

import Vue from 'vue'

export default new Vue

父组件:

<template>
     <components-a></components-a>
     <components-b></components-b>
</template>

子组件a:

<template>
      <div class="components-a">
           <button @click="abtn">A按钮</button>
      </div>
</template>
<script>
import eventVue from '../../js/event.js'
export default {
      name: 'app',
      data () {
        return {
                'msg':"我是组件A"
        }
      },
      methods:{
           abtn:function(){
                   eventVue .$emit("myFun",this.msg)   //$emit这个方法会触发一个事件
           }
      }
}
</script>

子组件b:

<template>
     <div class="components-a">
         <div>{{btext}}</div>
     </div>
</template>
<script>
import eventVue from '../../js/event.js'
export default {
   name: 'app',
   data () {
        return {
           'btext':"我是B组件内容"
        }
   },
   created:function(){
       this.bbtn();
   },
   methods:{
       bbtn:function(){
            eventVue .$on("myFun",(message)=>{   //这里最好用箭头函数,不然this指向有问题
                 this.btext = message      
            })
       }
    }
}
</script>

4.13 优化城市拖动列表

     这个拖动城市右侧列表的功能是拖动字母表,然后执行一个方法,使城市选项跳转到该字母开头的城市处,原方法代码如下:

handleTouchMove(e) {
    if (this.touchStartus) {
        const starY = this.$refs['A'][0].offsetTop
        const touchY = e.touches[0].clientY - 79
        const index = Math.floor((touchY - startY) / 20)
        if (index == 0 && index < this.letters.length){
            this.$emit('change', this.letters[index])
        }
    }
}

(1)startY会被重复计算,浪费性能,所以我们要通过1.减少计算次数 来提高性能,做法是将const starY = this.$refs['A'][0].offsetTop这行代码放到updated(){}生命周期钩子函数里面以减少计算次数,因为只有当另一个组件传过来的数据使页面发生改变时,页面重新渲染才需要重新计算这个值。
(2)拖动城市字母表时,方法函数执行频率过高,我们可以不用那么高频地执行这个方法,所以我们可以通过防抖来提高这部分的性能。先在data处新建一个值timer:null,然后代码修改如下:

 handleTouchMove (e) {
      if (this.touchStatus) {
        if (this.timer) {
          clearTimeout(this.timer)
        }
        this.timer = setTimeout(() => {
          const touchY = e.touches[0].clientY - 79
          const index = Math.floor((touchY - this.startY) / 20)
          if (index >= 0 && index < this.letters.length) {
            this.$emit('change', this.letters[index])
          }
        }, 16)
      }
    },

     修改之后,此处的意思就是,假设正在执行这个方法,我让这个方法延迟16ms再执行,假设16ms内又进行了手指滑动,导致这个方法再次被调用,那么就把上一次执行的方法清除掉,重新执行这一次的方法,那么就能减少很多次不必要的方法执行了,以此来达到提高性能的目的。

4.14 localStorage使用注意问题

     使用localStorage一定要记得捕捉异常,因为有的用户关闭了浏览器本地存储或者是使用了浏览器的隐身模式(匿名模式),那么就会直接导致抛出异常,所以要使用捕捉异常以免发生不必要的bug,在store下的mutations.js文件下如下如此写

export default {
  changeCity (state, city) {
    state.city = city
    try {
      localStorage.city = city
    } catch (e) {}
  }
}

state.js下如此写

let defaultCity = '北京'
try {
  if (localStorage.city) {
    defaultCity = localStorage.city
  }
} catch (e) {}

export default { // 公用数据
  city: defaultCity
}

4.15 Vuex Getters特性

     Vuex的Getters类似computed,可以根据vuex里的静态数据计算新数据,避免数据的冗余问题。

4.16 Html5解决300ms点击延迟问题

     之前解决这个问题呢,是引入使用fastClick这个包来解决的,但是Html5呢,直接在html新增一行{ touch-action:manipulation }即可解决,那么项目中是哪里用了这行代码呢?我一开始以为是在index.html这个文件下的html写入的,但是找了之后没找到,后来发现原来是写在引入的reset.css

4.17 组件中name值的用途

①用于递归使用组件(例如城市详情页的票价一栏)
②对某个页面取消缓存选取(exclude="xxxxxxx组件"
③Vue开发视图

4.18 解决页面切换时滚动行为的相互影响

     当我们页面滚动到某个中间位置时,再点击进行页面切换,会发现切换的新页面也直接是到了刚才的中间位置,这样看起来很不协调,我们可以再router文件下的index.js添加以下这段代码来解决

// 每次做路由切换的时候,始终回到最顶部
  scrollBehavior (to, form, savedPosition) {
    return {x: 0, y: 0}
  }

4.19 城市列表页拖动页面拖拽问题

     原来的代码是touchstart.prevent这样会阻止页面默认拖拽效果,导致出现bug,所以我们这里要把prevent删除掉即可解决

4.20 手机测试白屏bug

     有的手机不支持Promise,会导致手机进局域网测试时出现白屏bug,所以为了解决这个问题,我们可以安装以下这个包来解决 npm install babel-polyfill --save安装后引入即可


     写到这,就算是写完了,这个项目花了一周左右的时间做完的,对于一个初学者来说还是很友好的,能了解一个简单项目的开发流程。以上也就是我的笔记本中的总结了,还有一些别的细节点可以参考一下我开头提到的那些文章,如果觉得我写的还可以的话,麻烦点个赞,写的不好的地方还请大家指出,以便我来改正,谢谢大家。

                                                            主页                                                             城市列表页                                                             城市详情页