十、Vue项目使用Vuex实现数据共享

之前做了首页和城市选择页面,现在需要点击城市选择页面,选择城市,首页的城市跟着一起变,这就需要首页和城市选择页面有一些关联,需要用到Vuex。

Vuex是Vue官方推荐我们使用的数据框架,在Vue大型项目开发中Vue只能承担视图层的主要内容,涉及到大量数据之间传递时,需要一个数据框架进行辅助,就是Vuex
在这里插入图片描述
上图的大概意思是:

State是公用数据,组件可直接调用;组件改数据必须先调用Actions做一些异步处理或批量操作,然后Actions再去调用Mutations,只有通过Mutations,最后才可改变State的值,这样当数据发生变化时,组件也会发生变化。
上面这种情况也不是绝对的,有时也可略过Actions,让组件直接去调用Mutations,来修改State的公用数据

mutation 必须同步执行; Action 就不受约束!我们可以在 action 内部执行异步操作

安装Vuex

Vuex官方文档: https://vuex.vuejs.org/zh/installation.html
npm安装

npm install vuex --save

由于Vuex数据比较复杂就不直接在main.js中引入了

在src文件夹下新建 store文件夹 > index.js

在一个模块化的打包系统中,您必须显式地通过 Vue.use() 来安装 Vuex:

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

Vue.use(Vuex)

store文件夹 > index.js

在这里插入图片描述

扫描二维码关注公众号,回复: 12382683 查看本文章

store文件夹 > index.js 引入到main.js

在这里插入图片描述
现在项目里就已经使用了Vuex
在这里插入图片描述

现在已经创建好了Vuex(红框部分),就是main.js引入的store

之前在state公用数据里面存了一个city,下一步需要组件来使用这个数据,根据之前的页面选择首页的header.vue组件来使用这个数据
在这里插入图片描述

Home.vue

现在不需要用axios从后端传数据,已经在前端存储过了

<template>
  <div>
    <home-header></home-header>
    <home-swiper :list="swiperList"></home-swiper>
    <home-icons :list="iconList"></home-icons>
    <home-recommend :list="recommendList"></home-recommend>
    <home-weekend :list="weekendList"></home-weekend>
  </div>
</template>

<script>
// 局部组件需要插入到components中,由于键和值都是一样的,所以写成HomeHeader
import HomeHeader from './components/Header'
import HomeSwiper from './components/Swiper'
import HomeIcons from './components/Icons'
import HomeRecommend from './components/Recommend'
import HomeWeekend from './components/Weekend'
import axios from 'axios'
export default {
     
     
  name: 'home',
  data () {
     
     
    return {
     
     
      swiperList: [],
      iconList: [],
      recommendList: [],
      weekendList: []
    }
  },
  components: {
     
     
    HomeHeader,
    HomeSwiper,
    HomeIcons,
    HomeRecommend,
    HomeWeekend
  },
  methods: {
     
     
    getHomeInfo () {
     
     
      axios({
     
     
        url: '/static/mock/index.json',
        method: 'get'
      }).then(this.getHomeInfoSucc)
    },
    getHomeInfoSucc (res) {
     
     
      console.log(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
      }
    }
  },
  // 钩子函数mounted
  mounted () {
     
     
    this.getHomeInfo()
  }
}
</script>

<style></style>

static > mock > index.json也删除掉城市信息在这里插入图片描述
返回首页,现在城市信息就是空的
在这里插入图片描述

公用数据显示在header.vue和 list.vue组件

在这里插入图片描述
在这里插入图片描述

<template>
  <div class="header">
    <div class="header-left">
      <div class="iconfont back-icon">&#xe685;</div>
    </div>
    <div class="header-input">
      <span class="iconfont">&#xe67d;</span>
      输入城市/景点/游玩主题
    </div>
    <router-link to="/city">
      <div class="header-right">
      {
   
   {this.$store.state.city}}
      <span class="iconfont arrow-icon">&#xe688;</span>
    </div>
    </router-link>
  </div>
</template>

<script>
export default {
     
     
  name: 'HomeHeader',
}
</script>

<style lang="scss" scoped>
// styles指的是webpack.base.conf.js中的 src/assets/styles路径的简写
@import '~styles/varibles.scss';
.header {
     
     
  display: flex;
  line-height: $HeaderHeight;
  background-color: $bgColor;
  color: #fff;
  .header-left {
     
     
    width: 0.64rem;
    float: left;
    .back-icon {
     
     
      font-size: 0.4rem;
      text-align: center;
    }
  }
  .header-input {
     
     
    flex: 1;
    background: #fff;
    border-radius: 0.1rem;
    padding-left: 0.2rem;
    margin-top: 0.12rem;
    margin-left: 0.2rem;
    height: 0.64rem;
    line-height: 0.64rem;
    color: #666;
  }
  .header-right {
     
     
    width: 1.24rem;
    float: right;
    text-align: center;
    color: #fff;
    .arrow-icon {
     
     
      margin-left: -0.04rem;
      font-size: 0.24rem;
    }
  }
}
</style>

city文件夹下得list.vue也得改一下
在这里插入图片描述

实现点击逻辑功能

点击切换城市就是改变state,在组件里改变state需要按下图顺序
在这里插入图片描述
现在由于没有异步操作或批量操作,逻辑也没有很复杂,可略过Actions,直接去调用Mutations

给每个内容绑定一个click事件,调用方法时把item.name传进来,把方法写在methods中

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
list.vue

<template>
  <div class="list" ref="wrapper">
    <div>
      <div class="area">
        <div class="title border-topbottom">您的位置</div>
        <div class="button-list">
          <div class="button-wrapper">
            <div class="button">{
   
   { this.$store.state.city }}</div>
          </div>
        </div>
      </div>
      <div class="area">
        <div class="title border-topbottom">热门城市</div>
        <div class="button-list">
          <div
            class="button-wrapper"
            v-for="item in hot"
            :key="item.id"
            @click="handleCityClick(item.name)"
          >
            <div class="button">{
   
   { item.name }}</div>
          </div>
        </div>
      </div>
      <div class="area" v-for="(item, key) in cities" :key="key" :ref="key">
        <div class="title border-topbottom">{
   
   { key }}</div>
        <div class="item-list">
          <div
            v-for="innerItem in item"
            :key="innerItem.id"
            class="item border-bottom"
            @click="handleCityClick(innerItem.name)"
          >
            {
   
   { innerItem.name }}
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import BScroll from 'better-scroll'
export default {
     
     
  name: 'CityList',
  props: {
     
     
    hot: Array,
    cities: Object,
    letter: String
  },
  methods: {
     
     
    handleCityClick(city) {
     
     
      this.$store.commit('changeCity', city)
      this.$router.push('/')
    }
  },
  // 监听letter
  watch: {
     
     
    letter() {
     
     
      // console.log(this.letter)
      // better-scroll提供给我们这样一个接口,如果letter不为空时,调用this.scroll.scrollToElement()方法;让better-scroll的滚动区域自动滚到某个元素上
      if (this.letter) {
     
     
        const element = this.$refs[this.letter][0]
        this.scroll.scrollToElement(element)
      }
    }
  },
  updated() {
     
     
    this.scroll = new BScroll(this.$refs.wrapper)
  }
}
</script>

<style lang="scss" scoped>
@import '~styles/varibles.scss';
// 1像素边框问题
.border-topbottom {
     
     
  &::before {
     
     
    border-color: #ccc;
  }
  &::after {
     
     
    border-color: #ccc;
  }
}
.border-bottom {
     
     
  &::before {
     
     
    border-color: #ccc;
  }
}
.list {
     
     
  overflow: hidden;
  position: absolute;
  top: 1.58rem;
  left: 0;
  right: 0;
  bottom: 0;
  .title {
     
     
    padding: 0.1rem 0.2rem;
    line-height: 0.44rem;
    background: #eee;
  }
  .button-list {
     
     
    overflow: hidden;
    padding: 0.1rem 0.6rem 0.1rem 0.1rem;
    .button-wrapper {
     
     
      float: left;
      width: 33.33%;
      .button {
     
     
        margin: 0.1rem;
        padding: 0.14rem 0;
        text-align: center;
        border: 0.02rem solid #ccc;
        border-radius: 0.1rem;
      }
    }
  }
  .item-list {
     
     
    .item {
     
     
      line-height: 0.76rem;
      background: #fff;
      padding-left: 0.2rem;
    }
  }
}
</style>

store > index.js

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

Vue.use(Vuex)

export default new Vuex.Store({
    
    
  state: {
    
    
    city: '上海'
  },
  mutations: {
    
    
    changeCity (state, city) {
    
    
      state.city = city
    }
  }
})

Search.vue中也要实现点击逻辑

<template>
  <div>
    <div class="search">
      <input
        class="search-input"
        type="text"
        placeholder="输入城市名或拼音"
        v-model="keyWord"
      />
    </div>
    <!-- 有值时才显示 -->
    <div class="search-content" ref="search" v-show="keyWord">
      <ul>
        <li
          class="search-item border-bottom"
          v-for="item in list"
          :key="item.id"
          @click="handleCityClick(item.name)"
        >
          {
   
   { item.name }}
        </li>
        <li class="search-item border-bottom" v-show="hasNoData">
          没有找到匹配数据
        </li>
      </ul>
    </div>
  </div>
</template>

<script>
import BScroll from 'better-scroll'
export default {
     
     
  name: 'CitySearch',
  props: {
     
     
    cities: Object
  },
  data () {
     
     
    return {
     
     
      keyWord: '',
      list: [],
      timer: null
    }
  },
  computed: {
     
     
    hasNoData () {
     
     
      return !this.list.length
    }
  },
  // 监听keyword
  // 这里用到函数节流,因为这里用的频率比较高,节约性能
  watch: {
     
     
    keyWord () {
     
     
      if (this.timer) {
     
     
        clearTimeout(this.timer)
      }
      // 如果input框没有输入内容,那就设置this.list为空数组
      if (!this.keyWord) {
     
     
        this.list = []
        return
      }
      this.timer = setTimeout(() => {
     
     
        const result = []
        // 循环从父组件接收cities里面的内容
        for (let i in this.cities) {
     
     
          // 把cities里面的A、B、C等等键值对里面的值再给遍历一遍
          this.cities[i].forEach(value => {
     
     
            // 如果从spell,name中能搜索到这个关键词,就把这一项添加到result中
            if (
              value.spell.indexOf(this.keyWord) > -1 ||
              value.name.indexOf(this.keyWord) > -1
            ) {
     
     
              result.push(value)
            }
          })
        }
        // 把result结果赋值给list数组,在上面的li中循环list数组
        this.list = result
      }, 100)
    }
  },
  methods: {
     
     
    handleCityClick (city) {
     
     
      this.$store.commit('changeCity', city)
      this.$router.push('/')
    }
  },
  // 列表解决不滑动
  updated() {
     
     
    this.scroll = new BScroll(this.$refs.search)
  }
}
</script>

<style lang="scss" scoped>
@import '~styles/varibles.scss';
.search {
     
     
  background: $bgColor;
  height: 0.72rem;
  padding: 0.1rem;
  padding: 0 0.2rem;
  .search-input {
     
     
    width: 100%;
    height: 0.62rem;
    line-height: 0.62rem;
    text-align: center;
    background: #fff;
    border-radius: 0.06rem;
    color: #666;
  }
}
.search-content {
     
     
  overflow: hidden;
  position: absolute;
  top: 1.58rem;
  left: 0;
  right: 0;
  bottom: 0;
  background: #eee;
  z-index: 1;
  .search-item {
     
     
    line-height: 0.62rem;
    padding-left: 0.2rem;
    color: #666;
    background: #fff;
  }
}
</style>

猜你喜欢

转载自blog.csdn.net/weixin_45811256/article/details/109386463