vue2.5去哪儿(慕课网)学习笔记

vue2.5去哪儿(慕课网)学习笔记

文章目录

项目预热

安装环境

  1. 安装Node.js
    • 官网下载安装包进行安装
    • 输入 node -v ,如果显示版本号则安装成功
    • 输入 npm -v,安装包管理工具的验证
  2. 注册码云账号并关联本地仓库
    • 码云创建仓库[语言:Javascript;开源许可:MIT ]
    • 本地安装Git
      1. 验证:输入 git --version ,显示版本号即为成功
    • 关联本地与云仓库
      1. 码云->设置-> SSH公钥->本地Git窗口中输入一系列linux指令生成公钥
      2. 复制线上仓库SSH地址,在需要的文件中使用Git窗口输入:git clone ‘复制的内容’
  3. 使用vue-cli脚手架创建工程
    • 全局安装vue-cli

      npm install --global vue-cli

    • 创建项目

      vue init webpack ‘文件夹名称’

    • 运行项目

      npm run dev

使用到的一系列Git指令

  • 查看git状态
    1. git status
  • 提交项目到云端
    1. git add . 将项目添加到缓存
    2. git commit -m ‘注释’ 将项目添加到本地仓库
    3. git push 将项目添加到云端仓库
  • 分支的创建与切换
    1. 直接在码云的界面创建分支或者使用指令
    2. git pull 将线上创建的分支信息拉回本地
    3. git checkout ‘分支名称’ 切换操作的分支
    4. git merge ‘分支名称’ 将分支进行合并
    5. git branch 查看分支

vue-cli创建的项目的基本介绍

项目路径下的各个文件

  • README.md 介绍项目
  • package.json 记录了依赖的包
  • package-lock.json 确定依赖的包的版本信息
  • LICENSE 开源协议的说明
  • index.html 项目默认的首页模板文件
  • .gitignore 记录不希望上传到云仓库的文件与目录
  • .eslintrc.js 语法规范文件
  • .eslintignore 记录不需要检查语法的文件与目录
  • .editorconfig 配置了编辑器的语法
  • .babelrc 语法解析器,将vue转化为浏览器可以识别的代码
  • static目录 存放静态资源
  • node_modules目录 存放依赖的包
  • src目录 存放项目的源代码
    • App.vue 项目最原始的根组件
    • main.js 项目的入口文件
    • router目录 路由组件
    • components目录 项目要用的组件
    • assets 目录 存放图片资源
  • config目录 存放项目配置文件
    • index.js 存放基础的配置信息
    • dev.env.js 开发环境的配置信息
    • prod.env.js 线上环境的配置信息
  • build目录 存放打包的webpack的配置内容
    • webpack.base.conf.js 基础的配置项
    • webpack.dev.conf.js 开发环境配置项
    • webpack.prod.conf.js 线上环境配置项

单文件组件与Vue中的路由

/main.js 项目的入口文件

import Vue from 'vue'
import App from './App'
import router from './router'

new Vue({
el: '#app',
router,
components: { App },
template: '<App/>' 
})

/App.vue 项目默认的渲染文件

<template>
    <div id="app">
        <keep-alive exclude="Detail">
             <router-view/>       //显示当前地址所对应的内容
        </keep-alive>
    </div>
</template>
<script>
    export default {
        name: 'App'
    }
</script>
<style>
</style>

router/main.js 路由配置文件

import Vue from 'vue'
import Router from 'vue-router'
import Home from '@/pages/home/Home'
import City from '@/pages/city/City'
import Detail from '@/pages/detail/Detail'
Vue.use(Router)
export default new Router({
    routes: [
        {
            path: '/',
            name: 'Home',
            component: Home
        }, {
            path: '/city',
            name: 'City',
            component: City
        }, {
            path: '/detail/:id',
            name: 'Detail',
            component: Detail
        }
    ],
})

多页应用与单页应用

  1. 多页应用
    • 每次请求一个HTML
    • 首屏时间快,SEO效果好
    • 页面切换慢
  2. 单页应用
    • vue中使用: 列表页面 进行跳转
    • 使用JS感知页面的变化,然后清除当前页面的内容,显示新的内容
    • 特点
      1. 页面切换快
      2. 首屏时间慢,SEO差
      3. 使用JS渲染

项目代码的初始化

1./index.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport"
        content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">  //页面的比例始终是一比一,用户端不可以进行窗口的扩大缩小
        <title>Travel</title>
    </head>
    <body>
        <div id="app"></div>
    </body>
</html>
  1. 引入重置页面的样式表 reset.css和一像素边框border.css和‘300毫秒延迟点击问题’fastclick库
    • 将文件添加进来
    • 在main.js中添加代码

      import ‘./assets/styles/reset.css’
      import ‘./assets/styles/border.css’

    • 关于fastckick库
      1. npm install fastclick --save
      2. 引入

        import fastclick from ‘fastckick’

      3. 使用

        fastclick.attach(document.body)

在项目中使用iconfont

  1. 收集图标
    1. 创建帐号
    2. 新建项目
    3. 浏览图标库,将要使用的图表添加到购物车
    4. 将购物车的内容添加至项目
    5. 在项目中‘下载至本地’
  2. 使用图标
    1. 解压文件包
      1. 将iconfont.eot,iconfont.svg,iconfont.ttf,iconfont.woff 添加到项目的assets/styles/iconfont/中
      2. 将iconfont.css文件放到iconfont文件夹同级,修改下其中涉及到其他文件的路径
    2. 引入文件
      1. 在main.js中引入iconfont

        import ‘./assets/styles/iconfont.css’

    3. 使用
      1. 在需要使用的地方

        图标代码
        图表代码可以在购物车中的图表上查看

项目实战 - 旅游网站首页开发

首页header区域开发

准备工作
  1. 安装stylus

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

  2. 设计布局
    • home.vue 作为展示的容器,内部使用各个组件来搭页面
    • 在同一级创建目录‘components’,内部新建header.vue
    • 在 home.vue中引入并在components这个区域中命名它
Header.vue页面开发
零散的知识点
  1. 限制样式只在当前页面有用,使用scoped对css区域进行修饰
  2. 关于stylus
    1. 在reset.css中的html中的font-size: 50px 指示了 1rem=50px
    2. 想要靠近可以设置负值
    3. 将常使用的值设为变量
      • 在styles文件夹中添加varibles.styl

        设置变量 $bgColor = #00bcd4

      • 在的首行进行引入

        import ‘~styles/assets/styles/varibles.styl’

      • 使用的时候,在值的部分输入

        $bgColor

  3. 将常用的路径设置为常量(符号表示)
    • 在build/webpack.base.conf.js中,resolve: 中的alias:中添加一个键值对

      ‘styles’ : resolve(‘src/assets/styles’),

使用轮播图

  1. 查看插件与语法
    • 在GitHub上搜索vue-awesome-swiper
    • 新版可能存在bug,建议使用老版本
  2. 安装
    • npm install vue-awesome-swiper@2.6.7 --save
  3. 引入
    • 在main.js中引入和使用

      import ‘swiper/dist/css/swiper.css’
      import VueAwesomeSwiper from ‘vue-awesome-swiper’
      Vue.use(VueAwesomeSwiper,‘可以给个参数’)

  4. 使用,建立swiper.vue
<template>
    <div class="wrapper">
        <div class="iconw">
            <div class="iconw-img">
                <swiper :options="swiperOption" v-if="showSwiper" >   //应用轮播插件
                    <swiper-slide v-for="item of list" :key="item.id">
                        <img class="swipper-img" :src="item.imgUrl" />
                    </swiper-slide>
                    <div class="swiper-pagination" slot="pagination"></div>  //应用下面的选择点
                </swiper>
            </div>
        </div>
    </div>
    
</template>
<script>
export default {
    name: 'HomeSwiper',
    props: {
        list: Array
    },
    data () {
        return {
            swiperOption: {
                pagination: '.swiper-pagination',
                loop: true      //可以从最好一个滚回到第一个
            }
        }
    },
    computed: {
        showSwiper () {
            return this.list.length
        }
    }
}
</script>

<style lang="stylus" scoped>
    .wrapper >>> .swiper-pagination-bullet-active   //样式穿透,将设置应用在非当前页面的class样式中,不受scoped的限制
        background: #fff
    .wrapper
        overflow: hidden
        height: 0
        padding-bottom: 30.9%    //将高度设置为宽度的30.9%
        background: green
        .iconw
            position: relative
            .iconw-img
                position: absolute
                top: 0
                left: 0
                right: 0
        .swipper-img
            width: 100%
</style>

图标区域的开发

零散知识点
  1. 利用computed进行计算:每8个图表放一个页面

data () {
return {
swiperOption: {
autoplay: false     //不允许自动播放
}
}
},
computed: {
    pages () {
        const pages = []
        this.list.forEach((item, index) => {
            const page = Math.floor(index / 8)
            if (!pages[page]) {
                pages[page] = []
             }
            pages[page].push(item)
        })
        return pages
     }
}

  1. 将图标名字长的部分进行隐藏,显示三个点

.icon-desc
    position: absolute
    left: 0
    right: 0
    bottom: 0
    height: .44rem
    line-height: .44rem
    text-align: center
    color: $darkTextColor
    //以下三句话实现隐藏加点
    overflow: hidden
    white-sapce: nowrap
    text-overflow: ellipsis

推荐区域的开发

零散知识点
  1. 1像素边框的使用

    <li class="item **border-bottom** ">

周末游区域的开发

零散知识点
  1. 有些时候希望组件的内容可以被调用的组件进行定制,使用 slot=""

Ajax获取首页数据

零散知识点
  1. json数据的最后一个大括号后面不要加逗号,否则可能出错
使用axios进行ajax
  • 进行安装相应的模块

    npm install axios --save

  • 处理请求的api在开发环境进行重定向(转发)
    • 开发中只有static中的文件可以被访问到,故在static文件夹中新建mock文件夹,然后建立index.json
    • 在config/index.js中的dev中的proxyTable中添加对象
    
        proxyTable: {
        '/api': {
            target: 'http://localhost:8080',
             pathRewrite: {
                 '^/api': '/static/mock'
             }
        }
    
    
  • 在Home.vue进行使用
    • 首先引入

      import axios from ‘axios’

    • 在mounted进行使用
      mounted () {
          this.getHomeInfo()
      },
      methods: {
          getHomeInfo () {
              axios.get('/api/index.json?city=' + this.city).then(this.getHomeInfoSucc)
          },
          getHomeInfoSucc (res) {
              res = res.data
              if (res.ret && res.data) {
                  const data = res.data
                  this.swiperList = data.swiperList
                  this.iconList = data.iconList
                  this.recommendList = data.recommendList
                  this.weekendList = data.weekendList
                  this.cities = data.cities
              }
          }
      }
      
      
    • 调用其他vue页面的时候将参数传递进去

      <city-al :citise="cities"><city-al>

    • 被调用的页面进行接收

      props:{
      cities:Object
      }

    • 被调用的页面在接收后进行使用
      <li class="item" v-for="item of letters" :key="item" :ref="item">{{item}}</li>

旅游网站城市列表页面开发

配置路由

/router/index.js


    import Vue from 'vue'
    import Router from 'vue-router'
    import Home from '@/pages/home/Home'
    import City from '@/pages/city/City'
    import Detail from '@/pages/detail/Detail'
    Vue.use(Router)
    export default new Router({
    routes: [
    {
        path: '/',
        name: 'Home',
        component: Home
    }, {
        path: '/city',
        name: 'City',
        component: City
    }, {
        path: '/detail/:id',
        name: 'Detail',
        component: Detail
}
],
scrollBehavior (to, from, savedPosition) {
    return {x: 0, y: 0}   //页面切换后从顶部开始显示内容
}
})

Header中点击城市名,将页面跳转到城市选择页面


<router-link to='/city'>
    <div class="header-right">
        {{this.city}}
        <span class="iconfont arrow-icon">&#xe62d;</span>
    </div>
</router-link>

搜索框(Search.vue)

搜索框样式

    <style lang="stylus" scoped>
    @import '~styles/varibles.styl'
    .search
        height: .72rem
        padding: 0 .1rem
        background: $bgColor
        .search-input
            box-sizing: border-box
            width: 100%
            height: .62rem
            padding: 0 .1rem
            line-height: .62rem
            text-align: center
            border-radius: .06rem
            color: #666
    .search-content
        z-index: 1
        overflow: hidden
        position: absolute
        top: 1.58rem
        left: 0
        right: 0
        bottom: 0
        background: #eee
        .search-item
            line-height: .62rem
            padding-left: .2rem
            color: #666
            background: #fff
    </style>

城市选择列表

零散的知识点
  1. 修改之前引入的1像素边框的颜色

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

Better-scroll的使用
  1. 安装

npm install better-scroll --save

  1. 使用规范
    • 查看Git官网的项目,来认识他的使用规范
    • 两层div里面放需要的内容,如其他的div,或者li等标签
  2. 使用
    • 利用ref来获取dom
      1. 在需要使用scroll的div中添加

        <div class="list" ref="wrapper">

    • 引入

      import Bscroll from ‘better-scroll’

    • 在mounted中实例化

      mounted () {
      this.scroll = new Bscroll(this.$refs.wrapper)
      },

字母表的设计(alphabet.vue)
零碎的知识点
  1. 通过点击获取li标签里面的文本

    console.log(e.target.innerText)

  2. 样式

    <style lang="stylus" scoped>
    @import '~styles/varibles.styl'
    .list
        display: flex
        list-style:none
        flex-direction: column
        justify-content: center
        position: absolute
        top: 1.58rem
        right: 0
        bottom: 0
        width: .4rem
        .item
            line-height: .44rem
            text-align: center
            color: $bgColor
    </style>


兄弟组件的联动-点击转到对应区域

目的:实现点击一个字母(alphabet.vue),将城市选择(cityList.vue)跳转到字母对应的区域

  1. 首先在字母的li上绑定一个事件

    <li @click="handleLetterClick" >{{key}}</li>

  2. 实现这个方法

    methods: {
    handleLetterClick: function (e) {
    this.$emit(‘change’, e.target.innerText)
    },

  3. 主页面中对这个组件进行监听

    <city-alphabet :cities=“cities” @change=“handleLetterChange”>

  4. 实现handleLetterChange这个方法

    handleLetterChange (letter) {
    this.letter = letter
    }

  5. 将letter传递给cityList.vue组件

    <city-list :cities="cities" :hot="hotCities" :letter="letter"></city-list>

  6. cityList.vue中进行接收

    props: {
    hot: Array,
    cities: Object,
    letter: String
    },

  7. 利用侦听器进行侦听,若letter改变,则显示对应的区域

        watch: {
            letter () {
                if (this.letter) {
                    const element = this.$refs[this.letter][0]
                    this.scroll.scrollToElement(element)
                }
            }
    }
    
    
  8. 在li标签中进行显示

    <div class="area" v-for="(item,key) of cities" :key="key" :ref="key">
    <div class="title border-topbottom">{{key}}</div>  //key中的值为A,B,C,D...
    <div class="item-list">
    <div class="item border-bottom" v-for="innerItem of item" :key="innerItem.id" >
    {{innerItem.name}}  //为具体的城市名称
    </div>
    </div>
    </div>
    

兄弟组件的联动-在字母表上下拖拽转到对应区域

目的:实现在字母表(alphabet.vue)上滑动,将城市选择(cityList.vue)跳转到字母对应的区域

  1. 在li标签中绑定一个事件

    <li class="item" v-for="item of letters" :key="item" :ref="item"
    @touchstart.prevent="handleTouchStart"
    @touchmove="handleTouchMove" @touchend="handleTouchEnd">{{item}}</li>

  2. 实现对应的三个方法
    
    props: {
        cities: Object
        },
    computed: {
        letters () {
            const letters = []
            for (let i in this.cities) {
            letters.push(i)
        }
        return letters
        }
    },
    data () {
        return {
            touchStatus: false,
            startY: 0,
            timer: null
            }
        },
    updated () {          //当页面的数据被更新,页面完成了渲染
        this.startY = this.$refs['A'][0].offsetTop  //A距离顶部的高度
        },
    methods: {
        handleTouchStart () {
            this.touchStatus = true
            },
        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)
        }
        },
        handleTouchEnd () {
            this.touchStatus = false
        }
    }
    
    

搜索功能的实现

Search.vue接收来自主页面的cities这个参数,并设置一个div,在查询内容不为空的条件下(v-show)显示,展示查询结果。

零散的知识点
  1. 运算放在computed中

<template>
    <div>
        <div class="search">
            <input v-model="keyword" class="search-input" type="text" placeholder="输入城市名或拼音"/>
        </div>
        <div class="search-content" ref="search" v-show="keyword">
            <ul>
                <li class="search-item border-bottom" v-for="item of list"
                @click="handleCityClick(item.name)" :key="item.id"> {{item.name}}</li>
                <li class="search-item border-bottom" v-show="hasNoData">没有找到匹配数据</li>
            </ul>
        </div>
    </div>
</template>
<script>
import Bscroll from 'better-scroll'
import { mapMutations } from 'vuex'
export default {
    name: 'CitySearch',
    props: {
        cities: Object
    },
    mounted () {
        this.scroll = new Bscroll(this.$refs.search)
    },
    data () {
        return {
            keyword: '',
            list: [],
            timer: null
        }
    },
    computed: {
        hasNoData () {
            return !this.list.length
        }
    },
    methods: {
        handleCityClick (city) {
            this.changeCitys(city)
            this.$router.push('/')
        },
        ...mapMutations(['changeCitys'])
        },
    watch: {
        keyword () {
            if (this.timer) {
            clearTimeout(this.timer)
            }
            if (!this.keyword) {
            this.list = []
            return
        }
        this.timer = setTimeout(() => {
            const result = []
            for (let i in this.cities) {
            this.cities[i].forEach((value) => {
            if (value.spell.indexOf(this.keyword) > -1 || value.name.indexOf(this.keyword) > -1) {
            result.push(value)
        }
        })
        }
            this.list = result
        }, 100)
        }
    }
    }
</script>

使用Vuex实现数据共享

此时city与home,没有公用的组件,数据传递可以适用bus或者vuex。vuex是传递大量数据的数据框架
在这里插入图片描述

使用Vuex
  1. 安装

    npm install vuex --save

  2. 新建src/store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
import state from './state'
import mutations from './mutations'
Vue.use(Vuex)
export default new Vuex.Store({
    state: state,
    mutations: mutations,
    getters: {
    doubleCity (state) {
        return state.city + ' ' + state.city
        }
    }
})

  1. 在main.js中引入

import store from ‘./store’


new Vue({
    el: '#app',
    store: store,
    router,
    components: { App },
    template: '<App/>'
})

  1. 在Header.vue中进行操作,将显示城市的内容改为

this. s t o r e . s t a t e . c i t y 之 所 以 每 个 页 面 都 可 以 使 用 store.state.city 之所以每个页面都可以使用 store.state.city使store,是因为他在主js中引入,之后被派发到每一个页面

  1. 在城市选择页面List.vue,将当前城市改为

this.$store.state.city

  1. 实现点击一个城市名,则将首页的城市显示为该城市名

methods: {
    handleCityClick (city) {
        this.changeCitys(city)
        this.$router.push('/')
    },
    ...mapMutations(['changeCitys'])
},

  1. store/mutations.js

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

  1. store/state.js

    let defaultCity = '上海'
    try {
        if (localStorage.city) {
        defaultCity = localStorage.city
    }
    } catch (e) {
    }
        export default {
        city: defaultCity
    }

使用localstorage记住用户的选择

默认首页的城市为上海,当用户切换后,刷新页面会重新变为上海,
在这里插入图片描述

优化
  1. vuex方法写法优化

先引入
<script>
import { mapState, mapGetters } from 'vuex'

  1. this.$store.state.city 的写法优化

    computed: {
    …mapState([‘city’]),
    }
    使用时
    {{this.city}}

  2. this.$store.state.city 的写法优化2

computed: {
…mapState({
currentCity: ‘city’
})
使用时
{{this.currentCity}}

2.vuex中的getters

类似于computed的作用,可以使用数据计算数据

使用keep-alive优化网页性能

  1. 在App.vue中添加代码keep-alive
意思是;路由的内容加载一次以后,就将内容放到内存之中,下一次再取这个路由的时候直接从内存中取出,不需要重新渲染
  1. 发送ajax数据请求的时候带一个参数city

axios.get(’/api/index.json?city=’ + this.city).then(this.getHomeInfoSucc)

  1. 引入city这个参数的方法是vuex

    1. import { mapState } from ‘vuex’
    2. computed: {
      …mapState([‘city’])
      },
  2. 此时由于包含在keep-alive中并不会重新发送请求,可以使用activated,他在页面每次重新显示的时候被加载


activated () {
    if (this.lastCity !== this.city) {
        this.lastCity = this.city
        this.getHomeInfo()   //这个方法发送ajax请求
    }
},

旅游网站详情页面开发

详情页动态路由及banner布局

  1. 在内容的外部包裹路由跳转

<router-link tag="li" :to=" '/detail/'+ item.id">

  1. 在index.js中加入该路由信息(带有参数)
    {
        path: '/detail/:id',
        name: 'Detail',
        component: Detail
    }
  1. 实现文字区域背景图片的渐变色

background-image: linear-gradient(top, rgba(0, 0, 0, 0),rgba(0, 0, 0, 0.8))

公用图片画廊组件拆分

detail/banner.vue


<template>
<div>
<div class="banner" @click="handleBannerClick">
<img class="banner-img" :src="bannerImg">
<div class="banner-info" >
<div class="banner-title">{{this.sightName}}</div>
<div class="banner-number"><span class="iconfont banner-icon">&#xe600;</span>{{this.bannerImgs.length}}</div>
</div>
</div>
<fade-animation>
<common-gallary :imgs="bannerImgs" v-show="showGallary" @close="handleGallaryClose"></common-gallary>
</fade-animation>
</div>
</template>
<script>
import commonGallary from 'common/gallary/Gallary'
import FadeAnimation from 'common/fade/Fade'
export default {
name: 'DetailBanner',
props: {
sightName: String,
bannerImg: String,
bannerImgs: Array
},
data () {
return {
showGallary: false
}
},
methods: {
handleBannerClick () {
this.showGallary = true
},
handleGallaryClose () {
this.showGallary = false
}
},
components: {
commonGallary,
FadeAnimation
}
}
</script>
<style lang="stylus" scoped>
.banner
position: relative
overflow: hidden
height: 0
padding-bottom: 51%
.banner-img
width: 100%
.banner-info
display: flex
position: absolute
left: 0
right: 0
bottom: 0
line-height: .6rem
color: #fff
background-image: linear-gradient(top, rgba(0, 0, 0, 0),rgba(0, 0, 0, 0.8))
.banner-title
flex:1
font-size: .32rem
padding: 0 .2rem
.banner-number
margin-top: .14rem
padding: 0 .4rem
line-height: .32rem
height: .32rem
border-radius: .2rem
background: rgba(0, 0, 0, .8)
font-size: .24rem
.banner-icon
font-size: .24rem
</style>

common/gallary.vue


<template>
<div class="container" @click='handleGallaryClick'>
<div class="wrapper">
<swiper :options="swiperOptions">
<swiper-slide v-for="(item,index) in imgs" :key="index">
<img class="gallary-img" :src="item" />
</swiper-slide>
<div class="swiper-pagination" slot="pagination"></div>
</swiper>
</div>
</div>
</template>
<script>
export default {
name: 'CommonGallery',
props: {
imgs: {
type: Array,
default () {
return ['https://img1.qunarzz.com/vs_ceph_vs_tts/4d217b8b-a26b-4c50-80ef-c37bcbb8005a.jpg', 'http://img1.qunarzz.com/vs_ceph_vs_tts/b60743c9-5a9c-407a-8e4f-a73344f63ffc.jpg_r_500x333x90_5a5cead1.jpg']
}
}
},
data () {
return {
swiperOptions: {
pagination: '.swiper-pagination',
paginationType: 'fraction',
observeParents: true,
observer: true
}
}
},
methods: {
handleGallaryClick () {
this.$emit('close')
}
}
}
</script>
<style lang="stylus" scoped>
.container >>> .swiper-container
overflow: inherit
.container
display: flex
z-index: 99
position: fixed
flex-direction: column
justify-content: center
left: 0
right: 0
top: 0
bottom: 0
background: #000
.wrapper
height: 0
width: 100%
padding-bottom: 100%
.gallary-img
width: 100%
.swiper-pagination
color: #fff
bottom: -1rem
</style>


实现Header渐隐渐显效果(起初显示一个返回按钮,下滑的时候按钮消失,显示另一个返回的箭头)

detail/header.vue



<template>
<div>
<router-link tag="div" to="/" class="header-abs" v-show='showAbs'>
<div class="iconfont header-abs-back">&#xe624;</div>
</router-link>
<div class="header-fixed" v-show="!showAbs"
:style="opacityStyle"
>
<router-link to="/">
<div class="iconfont header-fixed-back">&#xe624;</div>
</router-link>
景点详情
</div>
</div>
</template>
<script>
export default {
name: 'DetailHeader',
data () {
return {
showAbs: true,
opacityStyle: {
opacity: 0
}
}
},
activated () {
window.addEventListener('scroll', this.handleScroll)
},
deactivated () {
window.removeEventListener('scroll', this.handleScroll)
},
methods: {
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
}
}
}
}
</script>
<style lang="stylus" scoped>
@import '~styles/varibles.styl'
.header-abs
position: absolute
left: .2rem
top: .2rem
width: .8rem
height: .8rem
line-height: .8rem
text-align: center
border-radius: .4rem
background: rgba(0,0,0,.8)
.header-abs-back
color: #fff
font-size: .4rem
.header-fixed
z-index: 2
position: fixed
top: 0
left: 0
right: 0
height: $headerHeight
line-height: $headerHeight
text-align: center
color: #fff
background: $bgColor
font-size: .32rem
.header-fixed-back
position: absolute
top: 0
left: 0
width: .64rem
text-align: center
font-size: .4rem
color: #fff
</style>



对全局时间的解绑

  1. 在header.vue中的方法

window.removeEventListener(‘scroll’, this.handleScroll)
该方法是对全局的影响,使用deactivated()钩子进行消除影响
在这里插入图片描述

list.vue的设计编写

  1. 关于递归组件

组件的name值,常用于递归组件的调用


<template>
<div>
<div class='item' v-for="(item,index) of list" :key="index">
<div class="item-title border-bottom">
<span class='item-title-icon'></span>
{{item.title}}
</div>
<div v-if="item.children" class="item-children">
<detail-list :list="item.children"></detail-list>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'DetailList',
props: {
list: Array
}
}
</script>
<style lang="stylus" scoped>
.item-title-icon
position: relative
left: .06rem
top: .06rem
display: inline-block
width: .36rem
height: .36rem
background: url(http://s.qunarzz.com/piao/image/touch/sight/detail.png) 0 -.45rem no-repeat
margin-right: .1rem
background-size: .4rem 3rem
.item-title
line-height: .8rem
font-size: .32rem
padding: 0 .2rem
.item-children
padding: 0 .2rem
</style>

detail.vue中写了一些模拟数据
在这里插入图片描述

使用Ajax获取动态数据

将detail/list的数据通过Ajax获取
detail.vue


<template>
<div>
<detail-banner :sightName="sightName"
:bannerImg="bannerImg"
:bannerImgs='gallaryImg'
></detail-banner>
<detail-header></detail-header>
<div class="content" >
<detail-list :list="list"></detail-list>
</div>
</div>
</template>
<script>
import detailBanner from './components/Banner'
import DetailHeader from './components/Header'
import DetailList from './components/List'
import axios from 'axios'
export default {
name: 'Detail',
components: {
detailBanner,
DetailHeader,
DetailList
},
data () {
return {
sightName: '',
bannerImg: '',
gallaryImg: [],
list: []
}
},
methods: {
getDetailInfo () {
// axios.get('/api/detail.json?id=' + this.$route.params.id)
axios.get('/api/detail.json', {
params: {
id: this.$route.params.id
}
}).then(this.handleGetDataSucc)
},
handleGetDataSucc (res) {
res = res.data
if (res.ret && res.data) {
const data = res.data
this.sightName = data.sightName
this.bannerImg = data.bannerImg
this.gallaryImg = data.gallaryImgs
this.list = data.categoryList
}
}
},
mounted () {
this.getDetailInfo()
}
}
</script>
<style lang="stylus" scoped>
.content
height: 50rem
</style>


组件中的name值有什么用

  1. 递归组件使用
  2. 取消对个别页面的缓存
    在这里插入图片描述
  3. 浏览器中vue调试工具看到的也是名字
页面拖拽在多个页面之中相互影响

在router/index.js中添加scrollBahavior


Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'Home',
component: Home
}, {
path: '/city',
name: 'City',
component: City
}, {
path: '/detail/:id',
name: 'Detail',
component: Detail
}
],
scrollBehavior (to, from, savedPosition) {
return {x: 0, y: 0}
}
})

在项目中加入基础动画

  1. 在详情的点击进入图片轮播这里加入基础动画
  • common/Fade.vue

<template>
<transition>
<slot>
</slot>
</transition>
</template>
<script>
export default {
name: 'Fade'
}
</script>
<style lang="stylus" scoped>
.v-enter, .v-leave-to
opacity: 0
.v-enter-active, .v-leave-active
transition: opacity .5s
</style>

  • 引入之后,注册,然后包裹在轮播组件之外
    在这里插入图片描述

优化收尾

Vue项目的接口联调

在这里插入图片描述

Vue项目的真机测试

  • 修改package.json文件
    在这里插入图片描述
    在这里插入图片描述
    这样你就可以通过ip访问这个开启的项目了
  • 手机与电脑在同一个局域网,然后访问项目开启的端口即可

Vue项目打包上线

  1. 运行命令 npm run build
  2. 项目目录中出现dist这个目录,这个目录中的文件就是上线的代码
  3. 如果打包后在后端服务器中放到某个文件夹中
    • /config/index.js
      在这里插入图片描述
    • 重新进行打包,然后将打包结果放到服务器中
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值