美文网首页
Vue-travel学习笔记

Vue-travel学习笔记

作者: noobakong | 来源:发表于2018-08-25 11:03 被阅读0次

    vue去哪网跟学笔记

    记录学习点滴

    1. 初始化项目

    1.1 手机显示配适

    minimum-scale=1.0,maximum-scale=1.0,user-scalable=no" 阻止用户手滑发大缩小页面

    1.2 初始化样式 -->引入reset.css

    1.3 移动端多倍屏边框不准的问题 --> 引入 border.css

    1.4 解决click延迟300ms的问题 --> 引入 fastclick 库

    • npm install fastclick --save
    • 在main.js中引入fastclick import fastClick from 'fastclick'
    • fastClick.attach(document.body)

    1.5 注册阿里的iconfont并创建travel项目

    2. 首页的开发

    2.1 准备工作&注意事项

    (1).项目中使用stylus来编写css样式

    npm install stylus --save npm install stylus-loader --save

    • 要学习stylus语法
    • 学习flex布局 rem布局的用法

    (2). iconfont的使用和代码的优化

    • 如何使用iconfont
    1. 进入iconfont官网,选择图标,加入购物车放入项目中
    2. 下载zip包并解压,把字体文件放入src/assets/styles/iconfont文件夹中
      把iconfont.css放在src/assets/styles中,把css中的文件路径改一下(因为此时>css和字体文件不在同一目录下了,默认的css和字体文件在一个文件夹内)
    3. 在main.js中引入字体文件 import './assets/styles/iconfont.css'
    4. 上述完成后,在想要使用图标的标签上加入 iconfont 类名,就可以在页面中使用 >图标了,可以用每一个图标类名来引用,也可以使用编码的形式来使用,每一个图标的编码
      都在 iconfont官网我的项目图标里面,点击复制图标就能得到图标编码;
    • 优化代码

    有些代码的样式是多变的,我们可以将可变的css放入assets styles文件夹的varibles.styl文件中,方便以后的更爱--》改一处全部就改的效果

    例如:
    我们的背景色就是一个可改变的css参数,我们可以在varibles.styl中定义 $bgcolor = #00bcd4 背景色
    而后在样式里引入这个styl文件即可
    @import '../../../assets/styles/varibles.styl';
    background-color $bgcolor

    但是@import文件引入的前缀非常长,在可以使用@符号可以优化此问题
    因为我们在webpack配置js文件中制定'@': resolve('src'), 制定了@就是src目录
    但是我们在css中引入css文件是 需要使用src的时候 要在@前面再多加一个~符号

    相同的 我们的sytles文件夹多次使用 我们可以在webapck.config.js文件中定义
    'styles': resolve('src/assets/styles'),
    这样我们使用styles目录的时候就可以简化了

    (3). 在github上创建新分支

    在企业级的开发里,每一个新功能或新模块的开发都是在一个新的分支上进行
    开发完成后再合并到master主分支上

    在github上创建仓库:
    Create a new repository on the command line
    
    touch README.md
    git init
    git add README.md
    git commit -m "first commit"
    git remote add <name> <url> 
    git push -u origin master
    
    在本地新建一个分支: git branch Branch1
    切换到你的新分支: git checkout Branch1
    将新分支发布在github上: git push origin Branch1
    在本地删除一个分支: git branch -d Branch1
    在github远程端删除一个分支: git push origin :Branch1   (分支名前的冒号代表删除)
    

    (4).管理分支的一些语法

    1. 查看分支
    • 查看本地分支
    $ git branch
    * master
    
    • 查看远程分支
      git branch -r
    • 查看所有分支
      git branch -a
    1. 本地创建新的分支
      git branch [branch name]

    2. 切换到新的分支
      git checkout [branch name]

    3. 创建+切换分支
      git checkout -b [branch name]
      git checkout -b [branch name] 的效果相当于以下两步操作:
      git branch [branch name] + git checkout [branch name]

    4. 将新分支推送到github
      git push origin [branch name] orgin是你远程仓库的名字

    5. 删除本地分支
      git branch -d [branch name]

    6. 删除github远程分支
      git push origin :[branch name] 分支名前的冒号代表删除。

    7. 合并分支

    git checkout master
    git merge <name>/<new-branch-name>
    git push
    

    2.2 首页轮播

    首页轮播图,采用vue-awesome-swiper插件

    vue-awesome-swiper github

    npm装包

    npm install vue-awesome-swiper@2.6.7 --save

    使用方法和使用步骤参考官网

    在swiper-slide标签里填入img属性并引入src 加入类swiper-img 在style里定义width的宽度为100% 即可适应轮播

    此时的页面在网速不好的情况下会发生页面抖动 如何解决
    在轮播元素的最外层加一个class为wrapper的div 然后定义.wrapper的样式

      .wrapper
        overflow hidden
        width 100%
        height 0
        padding-bottom: 26.67%
        background #eee
        .swiper-img
          width 100%
    

    这样就能把轮播图的位置保持撑起,就不会发生页面抖动了

    此时,又有一个问题,我们需要导航点,怎么实现

    swiperOption: {
            pagination: '.swiper-pagination'
          }
    

    在swiperOption里添加pagination配置项就可以了

    此时的导航激活状态是蓝色的 怎么更改为白色?
    我们可以在页面查看小原点的类名为swiper-pagination-bullet-active,我们如果直接在样式中修改这个样式的background,是达不到更改效果的,为什么,因为此时的样式是当前组件的样式,而这个小圆点属于swiper组件的样式
    这时,我们可以使用穿透样式来实现
    在样式的最前面编写如下代码

    .wrapper >>> .swiper-pagination-bullet-active
        background: #eee
    

    这样,就能达到从一个组件穿刺到另一个组件的样式更改

    最后 使用v-for 对图标进行列表渲染循环,把数据保存到data的swiperList对象中

    2.3 图标区域页面布局

    初始化

    • git创建分支
    • 新建icons.vue
    • Home.vue中引入组件

    图标区域逻辑实现

    当页面图标大于八个 可以左右拖动

    1. 在图标元素外加入swiper-slide标签和swiper标签

    2.4 首页推荐组件开发

    2.5 周末游组件开发

    3 使用ajax传递数据

    3.1 准备工作

    vue官方推荐使用axios来完成ajax数据的请求

    • 装包: npm install axios --save
    • home组件中引入axios
    • 结合vue的mouted生命周期钩子来完成请求

    如果每个子组件都发送一个ajax请求来获取数据的话,一个首页就要请求多个ajax请求,会使我们的程序效率下降,我们可以在home组件请求一个ajax请求,把数据传给子组件,这样就能提高效率

    怎么模拟后台的数据呢?

    因为我们页面整直接访问static文件夹,所以我们可以在static下创建一个mock文件夹,里面定影json文件来模拟后台数据

    但是我们并不想提交我们的数据到github,所以我们可以在gitnore文件中排除文件

    我们上线后的ajax请求地址都是基本都是相对路径'/api/下的json文件,但是此时我们的文件在static/mock文件夹中,我们可以把axios的请求地址改成我们本地的static/mock,但是这样做的话以后上线前要更改代码,这是不可取的

    即使用api文件目录,又能获取到static中的文件,怎么办?

    我们可以使用vue基于webpack-dev-serve的一个配置选项来解决这个问题,在vuecli生成的config文件夹中index.js文件有一个proxyTable配置选项
    我们可以这样来替换我们的请求地址:

    proxyTable: {
          '/api': {
            target: 'http://localhost:8080',
            pathRewrite: {
              '^/api': '/static/mock'
            }
          }
        }
    

    这样,就能完美解决我们的问题了

    注意,json格式的每一项的最后一项不要加带分号,这样可能会导致json数据解析失败

    3.2 首页父子组件数据传递

    由于home组件获取json数据后,应该向子组件传递数据,这就涉及到父组件向子组件传值的问题
    父组件通过属性向子组件传值,子组件props接受数据

    methods: {
      getHomeInfo () {
        axios.get('/api/index.json') // 返回的是一个promise对象,后面使用then
          .then(this.getHomeInfoSucc) // 获取成功执行getHomeInfoSucc函数
      },
      getHomeInfoSucc (res) {
        res = res.data
        if (res.ret && res.data) {
          const data = res.data
          this.city = data.city
          this.swiperList = data.swiperList
          this.iconList = data.iconList
          this.recommendList = data.recommendList
          this.weekendList = data.weekendList
        }
        console.log(res)
      }
    },
    mounted () {
      this.getHomeInfo() // 页面挂载好执行这个方法ajax获取数据
    }
    

    3. 首页的开发

    3.1 初始化准备

    • 配置路由
    • 创建组件

    3.2 header开发

    • 创建组件
    • city.vue导入

    3.3 搜索框

    • 创建组件
    • city.vue导入

    3.4 城市列表

    • 创建组件
    • city.vue导入

    title聊表的边框不太明显,可以给其添加样式

      .border-topbottom
        &:before
          border-color #ccc
        &:after
          border-color #ccc
    

    因为我们将要使用一个滚动插件--Better-scroll来完成此页面,所以我们应该禁止页面的超出滚动

    .list
      overflow hidden
      position absolute
      top 1.58rem
      left 0
      right 0
      bottom 0
    

    使用Better-scroll

    • 装包 npm install better-scroll --save
    • import Bscroll from 'better-scroll'
    • 在vue的mounted时挂载一个better-scroll实例

    因为这个组件需要最外城的wrapper dom元素 我们给最外层标签添加ref="wrapper"属性

    3.5 字母滑动选择器

    • 创建组件
    • city.vue导入

    使用flex布局使其居中

    3.6 ajax获取城市数据

    • 在city.vue中引入city.json
    • 父子间向子组件传递消息

    3.7 兄弟组件联动

    Todo1. 点击右侧字母表 list也跳到对应的城市也部分

    循环字母列表时为每一个字母绑定点击事件
    alphabet组件传递消息给父组件city,city在传递消息给list组件,实现Alphabet和list的兄弟传值

    • Alphabet.vue
      @click="handleLetterClick"
    handleLetterClick (e) {
          this.$emit('change', e.target.innerText)
        },
    
    • City.vue
      @change="handleLetterChange"
    handleLetterChange (letter) {
          this.letter = letter
        }
    

    :letter="letter" 传递给list.vue组件

    • List.vue
      props 接收 letter
      通过watch来监听letter的变化
      watch: {
        letter () { // 监听letter改变
          if (this.letter) {
            const element = this.$refs[this.letter][0]
            // refs-->通过为每个循环绑定ref ref的值对应的是每个key 也就是每个字母
            // [0]-->取到的是一个数组,具体的元素dom节点为数组的第一项
            this.scroll.scrollToElement(element)
            // scroll插件的而一个方法帮我们调到制定元素
          }
        }
      }
    

    Todo2. 滑动右侧字母表,list跟着滑动到对应的位置

    • Alphabet.vue
      绑定star move end 三个触摸方法
      @touchstart="handleTouchStart"
      @touchmove="handleTouchMove"
      @touchEnd="handleTouchEnd"
    

    把字母表从cities获取放到计算属性letters中

    computed: {
        letters () {
          const letters = []
          for (let i in this.cities) {
            letters.push(i)
          }
          return letters
          // ['A','B','C'...]
        }
      },
    

    将计算出的滑到哪个字母$emit传递给父元素

    handleTouchMove (e) {
          if (this.touchStatus) {
            const startY = this.$refs['A'][0].offsetTop // A元素距离顶部的高度
            const touchY = e.touches[0].clientY - 79 // 手指距离header下边缘的的距离
            const index = Math.floor((touchY - startY) / 22) // 滑动了第几个字母
            if (index >= 0 && index < this.letters.length) {
              this.$emit('change', this.letters[index])
            }
            // console.log(index)
          }
        },
    
    • City.vue
      @change="handleLetterChange"接收来自Alphabet的letter
      :letter="letter" 传递给list
      同样的和1一样使用watch来监视

    Todo3. 列表组件优化

    1. const startY = this.$refs['A'][0].offsetTop
      startY的值是固定的,可以提取出来

    放在updated生命周期函数钩子中,因为刚开始加载citise是通过json获取的,刚开始获取不到的时候是空,之后有获取到了ajax的内容,页面更新,就会执行updated钩子函数

    1. 函数节流
      手指在屏幕上滑动的时候,函数执行的次数是非常高的,我们可以采用函数节流
      通过定义一个定时器,来大大提高我们代码性能
        handleTouchMove (e) {
          if (this.touchStatus) {
            if (this.timer) {
              clearTimeout(this.timer)
            }
            this.timer = setTimeout(() => {
              const touchY = e.touches[0].clientY - 79 // 手指距离header下边缘的的距离
              const index = Math.floor((touchY - this.startY) / 22) // 滑动了第几个字母
              if (index >= 0 && index < this.letters.length) {
                this.$emit('change', this.letters[index])
              }
              // console.log(index)
            }, 10)
          }
        }
    

    3.7 完善搜索框--逻辑

    • 在search.vue增加search-content类 用于展示搜索内容
        <div class="search-content" ref="search" v-show="keyword">
          <ul>
            <li
              class="search-item border-bottom"
              v-for="item of list"
              :key="item.id"
            >
              {{item.name}}
            </li>
            <li class="search-item border-bottom" v-show="hasNoData">
              没有找到匹配数据
            </li>
          </ul>
        </div>
    

    ref = search 用于在mounted挂载滚动插件 v-show="keyword" 没有输入内容不显示

    • 在watch中监听keyword的变化,使用循环遍历,通过筛选把符合的city追加到list数组

    • 使用v-for循环输出list

    • 中间使用了定时器来实现函数节流来提高性能

    4.使用Vuex来实现数据共享

    4.1 实现city和home组件的数据联动

    我们想要城市页面和首页实现数据共享
    City.vue和Home.vue是没有一个父组件可供中转,那么想进行两者的通信,该怎么办呢?

    Vuex

    • npm install vuex --save
    • 在src目录下创建store文件夹并新建index.js文件
    import Vue from 'vue'
    import Vuex from 'vuex'
    
    Vue.use(Vuex)
    
    export default new Vuex.Store({ // 向外暴露一个由Actions State Mutations 三个组成的系统对象
      state: {
        city: 'aaa'
      },
      // actions: {
      //   changeCity (ctx, city) { // 借助这个ctx上下文来使用commit方法来调用Mutations完成数据的更改
      //     console.log(city)
      //     console.log(ctx)
      //     ctx.commit('MchangeCity', city)
      //   }
      // },
      mutations: {
        MchangeCity (state, city) {
          state.city = city
        }
      }
    })
    
    
    • 在main.js中引入
      import store from './store' // vuex 这样我们全局都能访问到store了
      并在vue实例中申明store,这样,我们以后就能使用vuex的数据了
      {{this.$store.state.city}}

    具体使用

    1. 把要公用的数据(例如city)定义到store/index.js的state中,在页面中把有city的地方换成 {{this.$store.state.city}}
    2. 我们为循环的每一个城市按钮绑定一个方法 @click="handleCityClick(item.name)
      并在methods中定义方法:
      methods: {
        handleCityClick (city) {
          this.$store.commit('MchangeCity', city) // 想要通过actions调用方法必须使用dispatch 或者 跳过actions直接通过commit来调用Mutations
          this.$router.push('/') // 选择后跳转到主页 实现联动 用了vue-router的编程跳转链接
        }
      }
    
    1. 在store中定义Actions函数和Mutations函数 来实现数据的修改

    总结:

    想要通过vuex来管理公用数据,想要更改数据 要经过一下步骤

    1. 组件 ---dispatch---> Actions
      methods: {
        handleCityClick (city) {
          this.$store.dispatch('changeCity', city) // 想要通过actions调用方法必须使用dispatch
      }
    
    1. Actions ---commit---> Mutations
    actions: {
        changeCity (ctx, city) { // 借助这个ctx上下文来使用commit方法来调用Mutations完成数据的更改
          ctx.commit('MchangeCity', city)
        }
      },
    
    1. Mutations ------> State
      mutations: {
        MchangeCity (state, city) {
          state.city = city
        }
      }
    

    在不复杂的环境下,有的时候我们更爱数据并不一定需要经过Actions,组件可以直接通过commit来使Mutations改变State

    1. 组件 ---commit---> Mutations
      methods: {
        handleCityClick (city) {
          this.$store.commit('MchangeCity', city) // 想要通过actions调用方法必须使用dispatch
      }
    
    1. Mutations ------> State
      mutations: {
        MchangeCity (state, city) {
          state.city = city
        }
      }
    

    4.2 vuex的高级使用和localStorage

    上述我们已经完成了vuex 实现两个不先练的组件的数据共享,但是我们一旦刷新我们的页面,我们的页面数据还是默认的我们在store中定义的数据,如何让程序记录我们的操作

    使用localStorage来完成
    在Mutations定义的方法里 加入:
    localStorage.city = city 来记录我们选择的城市
    在state中
    city: localStorage.city || '南阳'
    这样 浏览器就能记忆我们选择的城市了

    但是此时存在一个问题,浏览器如果使用了隐身模式或者关闭了浏览器存储,我们的程序就会直接报错无法执行
    我们可以使用try catch来优化一下我们的代码


    vuex高级

    1. 慢慢的 我们的store- index中的代码越来越多,我们可以把状态分开到不同的文件中管理

    2. 使用map辅助函数来进行优化

    4.3 使用keep-alive优化网页性能

    路由发生切换的时候 ajax都会被重新发送,为什么?

    因为我们的页面每一次渲染都会执行mounted钩子 而我们的ajax请求就是放在mounted中进行的

    怎么优化?

    将我们的router-view坑用keep-alive标签包裹起来

    <template>
      <div id="app">
        <keep-alive>
          <router-view/>
        </keep-alive>
      </div>
    </template>
    

    页面被keep-alive包裹起来,就会是页面的资源加载到内存当中,不需要重新渲染,也不需要从新执行钩子,来回返回页面也只会获取一次json
    此时,我们的vue中多出来一个生命周期函数钩子:activated

    4.4 选择城市后返回页面 页面需要被修改

    我们之前写的代码是固定的,虽然选择的城市发生变化,但是我们的我们的home页面中的内容并没有变化,怎么办?

    我们home首页的内容是有index.json ajax来获取的 我们只需要在home组件获得ajax的时候 使用?传参的方式,使得每一个城市对应自己的json文件,就可以了
    axios.get('/api/index.json?city=' + this.city

    但是此时的json文件被缓存到了内存当中,存的还是第一次的值,我们怎么改变缓存的数据呢

    由于此时的页面被keep-alive标签包裹,我们的ajax请求只会在第一次刷新的时候被获取,但是此时我们需要由城市列表选择的城市来同步我们首页的json文件以达到统一刷新的目的

    keep-detail 可以加入exclude="不被缓存的组件名字" 这样就可以指定排除某个组件不被缓存

    此时我们可以使用activated生命周期钩子
    因为在被包裹keep-alive标签之后,mounted钩子不会执行,但是activated钩子只要页面重新出现,就会执行,所以我们可以在activated钩子函数中 判断页面选择的城市和之前的城市是否为一个城市,如果不是一个城市,则重新发送ajax请求
    我们在data数据中新增一个 lastcity 数据 配合activated钩子使用

    activated () {
      if (this.lastcity !== this.city) {
        this.lastcity = this.city
        this.getHomeInfo()
      }
    }
    

    5.详情页面的制作

    创建detail.vue Banner.vue 导入detail路由

    5.1 banner的制作

    • 字体图标更新后 记得替换字体文件和iconfont.css的一段 base64的代码、

    • 使用 background-image linear-gradient 达到渐变效果

    5.2 banner画廊组件

    这个画廊组件不仅仅这个组件中要使用,以后可能在别的地方也会使用

    所以我们新建 src/common/gallary/Gallary.vue 编写画廊组件为以后复用

    • 使用swiper插件实现图片轮播滚动

    • 当我们点击banner的时候调到画廊页面,会发现渲染有问题,怎么办?
      点击跳转dom节点,会使得我们的css属性计算出错,从而造成错误,swiper为我们提供了一组配置,我们在配置项里添加 observeParents: trueobserver: true
      observeParents: 将observe应用于Swiper的父元素。当Swiper的父元素变化时,例如window.resize,Swiper更新。
      observer: 启动动态检查器(OB/观众/观看者),当改变swiper的样式(例如隐藏/显示)或者修改swiper的子元素时,自动初始化swiper。

    5.3 渐隐逐显的header

    • 页面有两个头部,一个是刚进去的的定位为abs的返回按钮,另外一个是定位是fixed头部导航

    • 刚开始我们使用v-show = showAbs 和 v-show = !showAbs 来分别控制两个头部,使其只显示一个

    • 使用 window.addEventListener('scroll', this.handleScroll) 来监听滚动的距离以切换哪个头部的展示 这个方法放在activated钩子里

    • handleScroll方法使用 document.documentElement.scrollTop 监听滚动离顶部的距离

    展示效果做好,剩下渐隐渐显的效果

    在 fixed 的头部标签绑定样式对象 :style="opacityStyle"

    handleScroll () {
          const top = document.documentElement.scrollTop
          if (top > 60) { // 过渡阶段
            let opacity = top / 140 // 过渡效果
            opacity = opacity > 1 ? 1 : opacity
            this.opacityStyle = { opacity }
            this.showAbs = false
          } else {
            this.showAbs = true
          }
        }
      }
    

    5.4 事件绑定的相关问题

    我们在5.3中,使用 window.addEventListener('scroll', this.handleScroll) 来监听滚动的距离,但是这个监听方法被绑定在了全局window中,所以我们的其他页面滚动时也会执行这段代码

    那么 我们怎么样才能使其只绑定在详情页呢?

    当我们使用keep-alive标签的时候,activated钩子函数产生的同时,也产生了一个deactivated的函数钩子,在activated绑定,在deactivated解绑即可

      activated () {
       window.addEventListener('scroll', this.handleScroll) // 页面展示绑定
      },
      deactivated() {
        window.removeEventListener('scroll', this.handleScroll)
      }
    

    5.5 递归列表组件

    组件自生调用自己

    5.6 ajax数据替换

    我们发现,首页滑动到底部,在点击详情页面,详情页面初始状态也是在底部,怎么办?

    页面滑动 各个组件会相互影响,我们可以在路由的配置选项中添加如下配置:

    scrollBehavior: function (to, from, savedPosition) {
      return savedPosition || { x: 0, y: 0 }
    }
    

    router进阶

    每个组件的export defalut的name是干什么用的?

    我们目前接触到的:

    1. 递归组件中可以用到它
    2. 对某个页面取消缓存的时候
    3. vue tools中组件的显示名字

    6 Vue项目上线前准备

    6.1 Vue项目的接口联调

    我们之前都是自己模拟后端的数据,实际项目中,我们是要和后端的数据,实现项目联调,如何进行?

    把mock中的数据替换成真正的后端服务器数据

    把config index.js 中的api制定的路径改为后端服务器的地址 一般都是本地80端口,一般是一下的形式

    proxyTable: {
      '/api': {
        target: 'http://localhost:80' 
      }
    }
    

    6.2 Vue项目的真机测试

    我们的项目是 通过 Webpack dev server 来进行的 它默认不支持ip地址的访问方式,要把它的默认配置项修改

    在package.json下 修改dev配置项 webpack-dev-server --host 0,0,0,0
    这样,就可以直接通过手机用ip地址来访问我们的项目

    在真机上,我们拖动字母表,会发现整个页面都跟着滚动,出现了bug,怎么半?

    @touchstart.prevent="handleTouchStart" 组织拖动的默认行为

    在低版本的安卓浏览器,可能出现白屏现象,怎么办?

    出现白屏现象的原因大部分是因为手机浏览器不支持promise特性,我们在项目中安装一个第三方的包

    • npm install babel-polyfill --save
    • 在main.js引入包 import 'babel-polyfill'

    6.3 Vue项目的打包上线

    1. 运行命令 npm run build 生成一个能被浏览器运行的代码,打包完成后,项目目录中多出来一个dist文件夹,里面就是是上线代码
    2. dist文件夹里的文件放到服务器跟目录中,就能上线了
    3. 如果想把上线文件放到根目录以外的地方,我们可以更改我们的config/index.js --> build--> assetsPublicPath路径

    End To Do

    vue基础官方文档熟连掌握

    VueRouter 细节

    Vuex 细节

    Vue 服务器端渲染 (难)

    Webpack Bable Es6

    相关文章

      网友评论

          本文标题:Vue-travel学习笔记

          本文链接:https://www.haomeiwen.com/subject/zggwiftx.html