小黑子的——vue从入门到入土过程:第五章

vue.js零基础入门到vue项目入土5.0

  • VUE2.0——VUE3.0系列第五章
    • 1. 数据懒加载
      • 1.1 运用vant库List组件加载
      • 1.2 数据到底结束
    • 2. loading加载&axios拦截器
      • 2.1 Vue Loading 加载动画组件
      • 2.2 vue-axios拦截器
    • 3. city 组件数据
    • 4. city 数据转换
    • 5. city 组件渲染
    • 6. vuex 引入
      • 6.1 什么是Vuex?
    • 7. vuex 同步工作流
    • 8. vuex 异步引入
    • 9. vuex异步应用
    • 10. 影院搜素组件
    • 11. 影院vuex-bug解决
    • 12. vuex 新写法
    • 13. vuex 控制底部选项卡
    • 14. vuex 持久化
    • 15. git 工具引入
    • 16. git 本地仓库
    • 17. git 远程仓库
    • 18. git 两人协作
      • 18.1 非冲突
      • 18.2 冲突
    • 19. git 分支

VUE2.0——VUE3.0系列第五章

1. 数据懒加载

每次检测到到底,触发函数获取数据

在这里插入图片描述
因为上面这个元素会导致第一次就立即触发到底的效果

1.1 运用vant库List组件加载

List 组件通过 loading 和 finished 两个变量控制加载状态,当组件滚动到底部时,会触发 load 事件并将 loading 设置成 true。此时可以发起异步操作并更新数据,数据更新完毕后,将 loading 设置成 false 即可。若数据已全部加载完毕,则直接将 finished 设置成 true 即可。

<template>
    <div>
        <van-list 
        v-model="loading" 
        :finished="finished" 
        finished-text="我是有底线的" 
        @load="onLoad" 
        :immediate-check="false">

            <van-cell v-for="data in datalist" :key="data.filmId" @click="handleChangePage(data.filmId)">
                <img :src="data.poster" />
                <div>
                    <div class="title">{{ data.name }}div>
                    <div class="content">
                        <div :class="data.grade ? '' : 'hidden'">观众评分:<span style="color:red;">{{ data.grade }}span>div>
                        <div class="actors">主演:{{ data.actors | actorsFilter }}div>
                        <div>
                            {{ data.nation }}|{{ data.runtime }}分钟
                        div>
                    div>
                div>
            van-cell>
        van-list>
    div>
template>
<script>
import http from '@/util/http'
import Vue from 'vue'
Vue.filter('actorsFilter', (data) => {
    if (data === undefined) return '暂无主演'// 防止有undefined的数据导致无法过滤,造成报错
    // 把数据的名字给映射出来
    return data.map(item => item.name).join('')
})
export default {
    data() {
        return {
            datalist: [],
            loading:false,
            finished:false,
            current:1
        }
    },
    mounted() {
        http({
            url: '/gateway?cityId=440100&pageNum=1&pageSize=10&type=1&k=5877842',
            headers: {
                'X-Host': 'mall.film-ticket.film.list'
            }
        }).then(res => {
            console.log(res.data.data.films)
            this.datalist = res.data.data.films
        })

    },
    methods: {
        onLoad(){
            console.log("到底了")
            this.current++
            http({
            url: `/gateway?cityId=440100&pageNum=${this.current}&pageSize=10&type=1&k=7018656`,
            headers: {
                'X-Host': 'mall.film-ticket.film.list'
            }
        }).then(res => {
            // console.log(res.data.data.films)
            this.datalist = [...this.datalist, ...res.data.data.films]
        })
        },
        handleChangePage(id) {
            // console.log(id)
            // 编程式导航
            //detail/1111
            // 1-通过路径跳转
            // this.$router.push(`/detail/${id}`)

            // 2-通过命名路由跳转
            this.$router.push({
                name: "vanDetail",
                params: {
                    id
                }
            })
        }
    }
}
script>
<style lang="scss" scoped>
.van-list{
    .van-cell {
        overflow: hidden;
        padding: .9375rem;

        img {
            width: 4.125rem;
            height: 5.625rem;
            float: left;
        }

        .title {
            font-size: 16px;
        }

        .content {
            font-size: 13px;
            color: gray;

            .actors {
                overflow: hidden;
                text-overflow: ellipsis;
                white-space: nowrap;
                width: 12.5rem;
                letter-spacing: 2px;
            }
        }
    }
}
style>

但是又一个问题,数据拉到底之后就不加载了
小黑子的——vue从入门到入土过程:第五章_第1张图片

1.2 数据到底结束

解决懒加载数据到底结束

this.total不等于0是解决点击一个电影后返回一上来就触发到底的问题

            // 总长度匹配,禁用懒加载功能
            if (this.datalist.length === this.total && this.total !==0) {
                this.finished = true
                return;
            }

2. loading加载&axios拦截器

对于每个页面都起到加载效果,可以封装axios。

http.js

import axios from 'axios'
import { Toast } from 'vant';
const http = axios.create({
    baseURL: 'https://m.maizuo.com',
    timeout: 1000,
    headers: {
        'X-Client-Info': '{ "a": "3000", "ch": "1002", "v": "5.2.1", "e": "167643376563166084022273", "bc": "440100" }'
    }
})
//在发请求之前拦截--showLoading
http.interceptors.request.use(function (config) {
    // Do something before request is sent
    // console.log(config)
    Toast.loading({
        message:'加载中...',
        forbidClick:true,
        duration:0
    })
    return config
}, function (error) {
    // Do something with request error
    return Promise.reject(error);
});

//在成功后拦截--hideLoading
http.interceptors.response.use(function (response) {
    // Any status code that lie within the range of 2xx cause this function to trigger
    // Do something with response data
    // 成功了就清除loading
    Toast.clear()
    return {
        ...response,
        aaa:'van'
    }
}, function (error) {
    // Any status codes that falls outside the range of 2xx cause this function to trigger
    // Do something with response error
     // 失败了也清除loading
    Toast.clear()
    return Promise.reject(error);
});
export default http

小黑子的——vue从入门到入土过程:第五章_第2张图片

2.1 Vue Loading 加载动画组件

  1. Vue Simple Spinner - Loading 加载动画基础款,简单可配置代码优秀
  2. Vue Radial Progress - Loading 加载进度条基础款,根据步长显示进度,可自定义多种变量
  3. nprogress - 网页顶部加载进度条,全新 UI 视觉效果愉悦
  4. TB Skeleton - APP / 网页结构加载动画,全局加载显示王者
  5. Vue Loading Overlay - 加载进度条,内置任务取消按钮,触发事件取消用户执行任务
  6. Vue Progress Path - Google Material 设计风格,可替换你自己设计的 loading 图,高度可定制化
  7. Vue Loading Button - 轻盈的按钮 Loading 加载动画效果组件

加载:

Toast.loading({
message:'加载中.….'forbidclick: true
})

是方法 ,需要单独引入

duration展示时长(ms),值为0时,toast不会消失
默认2000

//在成功后拦截--hideloading
http.interceptors.response.use(function (response){
// Any status code that lie within the range of 2xx cause this function to
	//trigger
// Do something with response data

2.2 vue-axios拦截器

vue-axios拦截器 axios还提供了拦截器的功能,他的拦截器是用拦截 请求和响应的,说简单是:请求到服务器前拦截 和 响应到客户端前拦截。
小黑子的——vue从入门到入土过程:第五章_第3张图片

3. city 组件数据

Cinema.vue:
添加

methods:{
        handleLeft() {
            // console.log('left')
            this.$router.push('/city')
        }
    }

City.vue:

<template>
    <div>
        <van-index-bar>
            <van-index-anchor index="A" />
            <van-cell title="A1" />
            <van-cell title="A2" />
            <van-cell title="A3" />

            <van-index-anchor index="B" />
            <van-cell title="B1" />
            <van-cell title="B2" />
            <van-cell title="B3" />
        van-index-bar>
    div>
template>
<script>
import http from '@/util/http'
export default {
    mounted() {
        http({
            url: '/gateway?k=247794',
            headers: {
                'X-Host': ' mall.film-ticket.city.list'
            }
        }).then(res => {
            console.log(res.data.data.cities)
        })
        // 任务:
        // 1,316条 ==>A ,B进行分组工
        // 2.利用转换后的数组,结合组件库进行渲染页面。

    }
}
script>

点击左上角地点后显现
小黑子的——vue从入门到入土过程:第五章_第4张图片

4. city 数据转换

City.vue:
将城市的数据转换

import http from '@/util/http'
export default {
    data() {
        return {
            cityList: [

            ]

        }
    },
    mounted() {
        http({
            url: '/gateway?k=247794',
            headers: {
                'X-Host': ' mall.film-ticket.city.list'
            }
        }).then(res => {
            // console.log(res.data.data.cities)
            this.renderCity(res.data.data.cities)
        })
        // 任务:
        // 1,316条 ==>A ,B进行分组工
        // 2.利用转换后的数组,结合组件库进行渲染页面。
    },
    methods: {
        renderCity(list) {
            console.log(list)
            var cityList = []
            var letterList = []
            for (var i = 65; i < 91; i++) {
                // console.log(String.fromCharCode(i))
                letterList.push(String.fromCharCode(i))
            }
            // console.log( letterList)
            letterList.forEach(letter => {
                var newList = list.filter(item => item.pinyin.substring(0, 1).toUpperCase() === letter)
                // 过滤出包含每个开头的字母
                // console.log(newList)
                newList.length > 0 && cityList.push({ // 26个字母中有的才进行添加
                    type: letterList,
                    list: newList
                })
            })
            // console.log(cityList)
            return cityList
        }

    }
}
</script>

小黑子的——vue从入门到入土过程:第五章_第5张图片

5. city 组件渲染

City.vue:

<template>
    <div class="city">
        <van-index-bar :index-list="computedList" @select="handleChange">
            
            <div v-for="data in cityList" :key="data.type">
                <van-index-anchor :index="data.type" />

                <van-cell :title="item.name" v-for="item in data.list" :key="item.cityId" @click="handleClick(item)" />
            div>
        van-index-bar>
    div>
template>

<script>
import http from '@/util/http'
import { Toast } from 'vant'
export default {
    data() {
        return {
            cityList: []
        }
    },
    computed: {
        // 令右边字母的城市切换选择可以一一对应
        computedList() {
            return this.cityList.map(item => item.type)
        }
    }
    ,
    mounted() {
        http({
            url: '/gateway?k=247794',
            headers: {
                'X-Host': ' mall.film-ticket.city.list'
            }
        }).then(res => {
            // console.log(res.data.data.cities)
            this.cityList = this.renderCity(res.data.data.cities)
        })
        // 任务:
        // 1,316条 ==>A ,B进行分组工
        // 2.利用转换后的数组,结合组件库进行渲染页面。
    },
    methods: {
        handleChange(data) {
            // console.log("change",data)
            Toast(data)
        },
        renderCity(list) {
            console.log(list)
            var cityList = []
            var letterList = []
            for (var i = 65; i < 91; i++) {
                // console.log(String.fromCharCode(i))
                letterList.push(String.fromCharCode(i))
            }
            // console.log( letterList)
            letterList.forEach((letter) => {
                var newList = list.filter(
                    (item) => item.pinyin.substring(0, 1).toUpperCase() === letter)
                // 过滤出包含每个开头的字母
                // console.log(newList)
                newList.length > 0 && cityList.push({
                    type: letter,
                    list: newList
                })
            })
            // console.log(cityList)
            return cityList
        },
        handleClick(item) {
            // console.log(item.name, item.cityId)
            //一、 传统的多页面方案
            // 1.location.href = '#/cinemas?cityname='+item.name
            // 2.cookie,localStorage

            // 单页面方案,
            // 1.中间人模式
            // 2. bus事件总线 $on, $emit
            // vuex 状态管理模式
        }

    }
}
script>
<style lang="scss" scoped>
.van-toast--html .van-toast--text {
    min-width: 30px
}
style>

小黑子的——vue从入门到入土过程:第五章_第6张图片

6. vuex 引入

小黑子的——vue从入门到入土过程:第五章_第7张图片

6.1 什么是Vuex?

vuex是一个专门为vue.js设计的集中式状态管理架构。状态?我把它理解为在data中的属性需要共享给其他vue组件使用的部分,就叫做状态。简单的说就是data中需要共用的属性。

引入Vuex(前提是已经用Vue脚手架工具构建好项目)

1、利用npm包管理工具,进行安装 vuex。在控制命令行中输入下边的命令就可以了。

npm install vuex--save

要注意的是这里一定要加上 –save,因为你这个包我们在生产环境中是要使用的。

2、新建一个store文件夹(这个不是必须的),并在文件夹下新建store.js文件,文件中引入我们的vue和vuex。

3、使用我们vuex,引入之后用Vue.use进行引用。

4、在main.js 中引入新建的vuex文件

5、再然后 , 在实例化 Vue对象时加入 store 对象

7. vuex 同步工作流

vuex的工作流程就是:
(1)通过dispatch去提交一个actions,
(2) actions接收到这个事件之后,在actions中可以执行一些异步|同步操作,根据不同的情况去分发给不同的mutations,
(3)actions通过commit去触发mutations,
(4)mutations去更新state数据,state更新之后,就会通知vue进行渲染

小黑子的——vue从入门到入土过程:第五章_第8张图片
案例:
store.vue index.js:

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  // state公共状态
 state:{
  cityId:'310100',
  cityName:'上海'
 },
 // 统一管理,被devtools 记录状态的修改dd
 mutations:{
  changeCityName(state,cityName) {
    // 通过mutations得到最新传回来的cityName
    state.cityName = cityName
    // console.log(cityName)
  }
 }
})

main.js:

import Vue from 'vue' // ES6 导入方式
import App from './App.vue' // 导入根组件App
import router from './router'
import store from './store'
Vue.config.productionTip = false

new Vue({
  router, //this.$router === router
  store, //this.$store === store
  render: h => h(App) // vue 支持的新写法
}).$mount('#app')

在谷歌浏览器控制台安装vue插件:
可见记录了城市切换
小黑子的——vue从入门到入土过程:第五章_第9张图片

8. vuex 异步引入

vuex 管理保存公共状态,(分散在各个组件内的状态,统一管理,)

注意:

vuex 默认是管理在内存, 一刷新页面,公共状态就丢失了。
vuex 持久化-todo

vuex 项目应用
1.非父子的通信
2.后端数据的缓存快照,减少重复数据请求,减轻服务器压力,提高用户体验

9. vuex异步应用

面试坑问题:

如何在 mutations 中做异步请求
但是 mutations只能支持同步,无法做异步
Cinemas.vue:

<template>
    <div>
        <van-nav-bar title="影院" ref="navbar" @click-left="handleLeft">
            <template #left>
                {{ $store.state.cityName }}<van-icon name="arrow-down" />
            template>
            <template #right>
                <van-icon name="search" size="20" color="black"/>
            template>
        van-nav-bar>


        <div class="box" :style="{
            height: height
        }">
            <ul>
                <li v-for="data in $store.state.cinemaList" :key="data.cinemaId">
                    <div class="left">
                        <div class="cinema_name">{{ data.name }}div>
                        <div class="cinema_text">{{ data.address }}div>
                    div>
                    <div class="right">
                        <div style="color: red;">¥{{ data.lowPrice / 100 }}起div>
                    div>
                li>

            ul>
        div>
div>
template>

<script>

import BetterScroll from 'better-scroll'
export default {
    data() {
        return {
            cinemaList: [],
            height: '0px'
        }
    },

    mounted() {
        // console.log(this.$refs.navbar.$el.offsetHeight)
        // 动态结算高度,视口高度-底部选项卡的高度-顶部导航栏的高度=中心页面滚动的高度
        this.height = 
        document.documentElement.clientHeight 
        - this.$refs.navbar.$el.offsetHeight
        -document.querySelector("footer").offsetHeight + 'px'
        
        // 一进来就分发getCinema action 并传来cityId
        if(this.$store.state.cinemaList.length === 0){
            this.$store.dispatch('getCinemaData',this.$store.state.cityId).then(res=>{
                // console.log("数据完事了")
                this.$nextTick(() => {// 保证dom上树,保证BetterScroll回调,实现初始化滚动
                new BetterScroll('.box', {
                    scrollbar: {
                        fade: true
                    }
                })
            })
            })
        } else {
            console.log('缓存')
            this.$nextTick(() => {// 保证dom上树,保证BetterScroll回调,实现初始化滚动
                new BetterScroll('.box', {
                    scrollbar: {
                        fade: true
                    }
                })
            })
        }

        // http({
        //     url: `/gateway?cityId=${this.$store.state.cityId}&ticketFlag=1&k=9458654`,
        //     headers: {
        //         'X-Host': 'mall.film-ticket.cinema.list'
        //     }
        // }).then(res => {
        //     // console.log(res.data)
        //     this.cinemaList = res.data.data.cinemas

        //     this.$nextTick(() => {// 保证dom上树,保证BetterScroll回调,实现初始化滚动
        //         new BetterScroll('.box', {
        //             scrollbar: {
        //                 fade: true
        //             }
        //         })
        //     })
        // })
    },
    methods:{
        handleLeft() {
            // console.log('left')
            this.$router.push('/city')
        }
    }
}
script>
<style lang="scss" scoped>
li {
    padding: .9375rem;
    display: flex;
    justify-content: space-between;

    .left {
        width: 13.25rem;
    }

    .right {
        font-size: 15px;
    }

    .cinema_name {
        font-size: 15px;
    }

    .cinema_text {
        color: #797d82;
        font-size: 12px;
        margin-top: 5px;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
}

.box {
    // height: 38.625rem;// 但是rem一直比的是宽度的百分比,所以高度在不同窗口下是会有问题的
    overflow: hidden;
    position: relative;
    // 修正滚动条的的位置

}
style>

store文件 index.js:

import Vue from 'vue'
import Vuex from 'vuex'
import http from '@/util/http'
Vue.use(Vuex)

export default new Vuex.Store({
  // state公共状态
  state: {
    cityId: '310100',
    cityName: '上海',
    cinemaList:[]
  },
  // 支持异步和同步
  // 从cinemas.vue传来cityId,发起ajax请求
  actions: {
    getCinemaData(store, cityId) {
      return http({
        url: `/gateway?cityId=${cityId}&ticketFlag=1&k=9458654`,
        headers: {
          'X-Host': 'mall.film-ticket.cinema.list'
        }
      }).then(res => {
        // console.log(res.data.data.cinemas)
        // 把数据存入vue中
        store.commit("changeCinemaData", res.data.data.cinemas)
      })
    }
  },
  // 统一管理,被devtools 记录状态的修改
  //  mutations只能支持同步
  mutations: {
    changeCityName(state, cityName) {
      // 通过mutations得到最新传回来的cityName
      state.cityName = cityName
      // console.log(cityName)
    },
    changeCityId(state, cityId) {
      state.cityId = cityId
    },
    changeCinemaData(state,data) {
      state.cinemaList = data
    }
  }
})

小黑子的——vue从入门到入土过程:第五章_第10张图片

10. 影院搜素组件

vuex三连招:

store/search/index.js

Search…vue:

<template>
    <div>
        <van-search v-model="value" show-action placeholder="请输入搜索关键词" @search="onSearch" @cancel="onCancel" />
        <ul v-if="value">
            <li v-for="data in computedList" :key="data.cinemaId">
                <div class="left">
                    <div class="cinema_name">{{ data.name }}div>
                    <div class="cinema_text">{{ data.address }}div>
                div>
                <div class="right">
                    <div style="color: red;">¥{{ data.lowPrice / 100 }}起div>
                div>
            li>

        ul>
    div>
template>
<script>
export default {
    data() {
        return {
            value: ''
        }
    },
    computed: {
        computedList() {
            return this.$store.state.cinemaList.filter(item => item.name.toUpperCase().includes(this.value.toUpperCase()) || item.address.toUpperCase().includes(this.value.toUpperCase()))
        }
    },
    methods: {
        onSearch() {

        },
        onCancel() {
            this.$router.back()
        }
    },
    mounted() {
        if (this.$store.state.cinemaList.length === 0) {
            this.$store.dispatch('getCinemaData', this.$store.state.cityId)
        } else {
            console.log('缓存')
        }
    }
}
script>
<style lang="scss" scoped>
li {
    padding: .9375rem;
    display: flex;
    justify-content: space-between;

    .left {
        width: 13.25rem;
    }

    .right {
        font-size: 15px;
    }

    .cinema_name {
        font-size: 15px;
    }

    .cinema_text {
        color: #797d82;
        font-size: 12px;
        margin-top: 5px;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
}
style>

小黑子的——vue从入门到入土过程:第五章_第11张图片

但是点击其他城市出了问题——电影数据不会更新
小黑子的——vue从入门到入土过程:第五章_第12张图片

11. 影院vuex-bug解决

(1)应用层级的状态应该集中到单个store 对象中。
(2)提交mutation是更改状态的唯一方法,并且这个过程是同步的
(3)异步逻辑都应该封装到action里面

12. vuex 新写法

  1. 根目录下新建 vuex 目录,在 vuex 目录下创建 index.js 定义状态值
  2. 然后,在 main.js 挂载 Vuex
  3. 最后,在页面引入
<template>
    <div>
        <van-nav-bar title="影院" ref="navbar" @click-left="handleLeft" 
        @click-right="handleRight">
            <template #left>
                {{ cityName }}<van-icon name="arrow-down" />
            template>
            <template #right>
                <van-icon name="search" size="28" color="black" />
            template>
        van-nav-bar>


        <div class="box" :style="{
            height: height
        }">
            <ul>
                <li v-for="data in $store.state.cinemaList" :key="data.cinemaId">
                    <div class="left">
                        <div class="cinema_name">{{ data.name }}div>
                        <div class="cinema_text">{{ data.address }}div>
                    div>
                    <div class="right">
                        <div style="color: red;">¥{{ data.lowPrice / 100 }}起div>
                    div>
                li>

            ul>
        div>
    div>
template>

<script>

import BetterScroll from 'better-scroll'
import {mapState,mapActions,mapMutations} from 'vuex'
console.log(mapState(['cinemaList']))
export default {
    data() {
        return {
            cinemaList: [],
            height: '0px'
        }
    },
    computed:{
        a(){
            return '111'
        },
        ...mapState(['cinemaList','cityId','cityName'])
    },

    mounted() {
        // console.log(this.$refs.navbar.$el.offsetHeight)
        // 动态结算高度,视口高度-底部选项卡的高度-顶部导航栏的高度=中心页面滚动的高度
        this.height =
            document.documentElement.clientHeight
            - this.$refs.navbar.$el.offsetHeight
            - document.querySelector("footer").offsetHeight + 'px'

        // 一进来就分发getCinema action 并传来cityId
        if (this.$store.state.cinemaList.length === 0) {
            this.$store.dispatch('getCinemaData', this.$store.state.cityId).then(res => {
                // console.log("数据完事了")
                this.$nextTick(() => {// 保证dom上树,保证BetterScroll回调,实现初始化滚动
                    new BetterScroll('.box', {
                        scrollbar: {
                            fade: true
                        }
                    })
                })
            })
        } else {
            console.log('缓存')
            this.$nextTick(() => {// 保证dom上树,保证BetterScroll回调,实现初始化滚动
                new BetterScroll('.box', {
                    scrollbar: {
                        fade: true
                    }
                })
            })
        }

        // http({
        //     url: `/gateway?cityId=${this.$store.state.cityId}&ticketFlag=1&k=9458654`,
        //     headers: {
        //         'X-Host': 'mall.film-ticket.cinema.list'
        //     }
        // }).then(res => {
        //     // console.log(res.data)
        //     this.cinemaList = res.data.data.cinemas

        //     this.$nextTick(() => {// 保证dom上树,保证BetterScroll回调,实现初始化滚动
        //         new BetterScroll('.box', {
        //             scrollbar: {
        //                 fade: true
        //             }
        //         })
        //     })
        // })
    },
    methods: {
        ...mapActions(['getCinemaData']),
        ...mapMutations(['']),
        handleLeft() {
            // console.log('left')
            this.$router.push('/city')

            // 换城市清空cinemaList
            // this.$store.commit('clearCinema')
            this.clearCinema()
        },
        handleRight() {
            this.$router.push('/cinemas/Search')
        }
    }
}
script>
<style lang="scss" scoped>
li {
    padding: .9375rem;
    display: flex;
    justify-content: space-between;

    .left {
        width: 13.25rem;
    }

    .right {
        font-size: 15px;
    }

    .cinema_name {
        font-size: 15px;
    }

    .cinema_text {
        color: #797d82;
        font-size: 12px;
        margin-top: 5px;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
}

.box {
    // height: 38.625rem;// 但是rem一直比的是宽度的百分比,所以高度在不同窗口下是会有问题的
    overflow: hidden;
    position: relative;
    // 修正滚动条的的位置

}
style>

小黑子的——vue从入门到入土过程:第五章_第13张图片

13. vuex 控制底部选项卡

Deatail.vue中导入:

import obj from '@/util/mixinObj'
  • mixinObj,js
const obj = {
    created(){
        this.$store.commit('hide')
        // console.log('创建完成')
    },
    destroyed(){
        this.$store.commit('show')
    },
    methods:{
        a(){
            console.log("aaaaaaaaaaaaaaaa")
        }
    }
    
}

export default obj
  • city.vue:
<template>
    <div class="city">
        <van-index-bar :index-list="computedList" @select="handleChange">
            
            <div v-for="data in cityList" :key="data.type">
                <van-index-anchor :index="data.type" />

                <van-cell :title="item.name" v-for="item in data.list" :key="item.cityId" @click="handleClick(item)" />
            div>
        van-index-bar>
    div>
template>

<script>
import http from '@/util/http'
import { Toast } from 'vant' 
import obj from '@/util/mixinObj'

export default {
    mixins:[obj],//混入
    data() {
        return {
            cityList: []
        }
    },

    computed: {
        // 令右边字母的城市切换选择可以一一对应
        computedList() {
            return this.cityList.map(item => item.type)
        }
    }
    ,

    mounted() {
        // this.$store.commit('hide')
        http({
            url: '/gateway?k=247794',
            headers: {
                'X-Host': ' mall.film-ticket.city.list'
            }
        }).then(res => {
            // console.log(res.data.data.cities)
            this.cityList = this.renderCity(res.data.data.cities)
        })
        // 任务:
        // 1,316条 ==>A ,B进行分组工
        // 2.利用转换后的数组,结合组件库进行渲染页面。
    },
    methods: {
        handleChange(data) {
            // console.log("change",data)
            Toast(data)
        },
        renderCity(list) {
            console.log(list)
            var cityList = []
            var letterList = []
            for (var i = 65; i < 91; i++) {
                // console.log(String.fromCharCode(i))
                letterList.push(String.fromCharCode(i))
            }
            // console.log( letterList)
            letterList.forEach((letter) => {
                var newList = list.filter(
                    (item) => item.pinyin.substring(0, 1).toUpperCase() === letter)
                // 过滤出包含每个开头的字母
                // console.log(newList)
                newList.length > 0 && cityList.push({
                    type: letter,
                    list: newList
                })
            })
            // console.log(cityList)
            return cityList
        },
        handleClick(item) {
            // console.log(item.name, item.cityId)
            //一、 传统的多页面方案
            // 1.location.href = '#/cinemas?cityname='+item.name
            // 2.cookie,localStorage

            // 单页面方案,
            // 1.中间人模式
            // 2. bus事件总线 $on, $emit

            this.$store.commit('changeCityName', item.name) // 改成点击的值
            this.$router.back() //回退切换城市
            // 但是这样做有隐患——谁都能改这个状态
            // 必须经过 mutations 方便管理并且防止恶意修改
            // 相当于门禁卡的效果
        }

    }
}
script>
<style lang="scss" scoped>
.van-toast--html .van-toast--text {
    min-width: 30px
}
style>

14. vuex 持久化

Vuex为什么要持久化?
原因:因为Vuex是基于内存,存在内存里面的,刷新网页之后就没有了,不会持久化储存

vuex可以实现全局状态管理,即实现所有组件间数据共享,但是刷新页面vuex对象会重新加载,状态(数据)会清空

导入:
小黑子的——vue从入门到入土过程:第五章_第14张图片

  • index.js
import Vue from 'vue'
import Vuex from 'vuex'
import http from '@/util/http'
import createPersistedState from 'vuex-persistedstate'

Vue.use(Vuex)

export default new Vuex.Store({
  plugins:[createPersistedState(
    {
      reducer: (state)=>{
        return {
          cityId:state.cityId,
          cityName:state.cityName
        }
      }
    }
  )],

  // state公共状态
  state: {
    cityId: '310100',
    cityName: '上海',
    cinemaList:[],
    isTabbarShow:true
  },
  // 支持异步和同步
  // 从cinemas.vue传来cityId,发起ajax请求
  actions: {
    getCinemaData(store, cityId) {
      return http({
        url: `/gateway?cityId=${cityId}&ticketFlag=1&k=9458654`,
        headers: {
          'X-Host': 'mall.film-ticket.cinema.list'
        }
      }).then(res => {
        // console.log(res.data.data.cinemas)
        // 把数据存入vue中
        store.commit("changeCinemaData", res.data.data.cinemas)
      })
    }
  },
  // 统一管理,被devtools 记录状态的修改
  //  mutations只能支持同步
  mutations: {
    changeCityName(state, cityName) {
      // 通过mutations得到最新传回来的cityName
      state.cityName = cityName
      // console.log(cityName)
    },
    changeCityId(state, cityId) {
      state.cityId = cityId
    },
    changeCinemaData(state,data) {
      state.cinemaList = data
    },
    clearCinema(state){
      // 不要在外面进行清空的操作——会监听不到清空的行为
      state.cinemaList = []
    },
    show(satte){
      state.isTabbarShow = true
    },
    hide(state){
      state.isTabbarShow = false
    }
  }
})

15. git 工具引入

打开git的官网: https://git-scm.com/

小黑子的——vue从入门到入土过程:第五章_第15张图片
小黑子的——vue从入门到入土过程:第五章_第16张图片
小黑子的——vue从入门到入土过程:第五章_第17张图片

16. git 本地仓库

Git创建一个自己的本地仓库 如果我们要把一个项目加入到Git的版本管理中,可以在项目所在的目录用 git init 命令建立一个空的本地仓库,然后再用 git add 命令把它们都加入到Git本地仓库的暂存区(stage or index)中,最后再用 git commit 命令提交到本地仓库里。

17. git 远程仓库

利用Git连接远程仓库步骤及常见问题
1.先创建一个文件夹,名字为远程仓库的名称

2.在该文件目录下打开Git Bash

3.输入git init,进行初始化(初次连接时)

4.连接远程仓库(初次连接是下一次进入该文件夹就不用了)

18. git 两人协作

在你想要协同的工作文件夹中打开Git Bash Here,创建一个SSH
key,这里用到了非对称公钥加密体系,生成的公钥放到github的网站上,二生成的私钥放在自己的电脑上,每当需要将文件上传到github上时,服务器就会用事先的公钥与你给出的私钥进行验证,验证是否是真正的用户在操作(原理类似于数字签名)。

多人协作原理
典型的做法是,首先创建一个git服务器,被多个人所操作。

小黑子的——vue从入门到入土过程:第五章_第18张图片

18.1 非冲突

18.2 冲突

19. git 分支

小黑子的——vue从入门到入土过程:第五章_第19张图片
几乎每一种版本控制系统都以某种形式支持分支,一个分支代表一条独立的开发线。

使用分支意味着你可以从开发主线上分离开来,然后在不影响主线的同时继续工作。

小黑子的——vue从入门到入土过程:第五章_第20张图片

Git 分支实际上是指向更改快照的指针。

有人把 Git 的分支模型称为必杀技特性,而正是因为它,将 Git 从版本控制系统家族里区分出来。

你可能感兴趣的:(vue,vue.js,javascript,前端)