十、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

在这里插入图片描述

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>

Vue 3是一个流行的JavaScript框架,用于构建用户界面。VuexVue.js的官方状态管理库,用于在应用程序中集中管理和共享状态。下面是使用Vue 3和Vuex存储数据的步骤: 1. 安装Vuex:首先,你需要在项目中安装Vuex。可以使用npm或yarn来安装,命令如下: ``` npm install vuex ``` 2. 创建store:在项目的根目录下创建一个名为store.js的文件,并导入VueVuex: ```javascript import { createApp } from 'vue' import { createStore } from 'vuex' const store = createStore({ state() { return { // 在这里定义你的状态 } }, mutations: { // 在这里定义你的mutations }, actions: { // 在这里定义你的actions }, getters: { // 在这里定义你的getters } }) const app = createApp({}) app.use(store) export default store ``` 3. 定义状态:在store.js文件中的state对象中定义你的状态。例如: ```javascript state() { return { count: 0 } } ``` 4. 定义mutations:在mutations对象中定义用于修改状态的方法。例如: ```javascript mutations: { increment(state) { state.count++ }, decrement(state) { state.count-- } } ``` 5. 定义actions:在actions对象中定义用于处理异步操作的方法。例如: ```javascript actions: { asyncIncrement(context) { setTimeout(() => { context.commit('increment') }, 1000) } } ``` 6. 使用状态:在Vue组件中使用store中的状态。可以使用`$store.state`来访问状态,例如: ```javascript export default { computed: { count() { return this.$store.state.count } }, methods: { increment() { this.$store.commit('increment') }, decrement() { this.$store.commit('decrement') }, asyncIncrement() { this.$store.dispatch('asyncIncrement') } } } ``` 这样,你就可以在Vue组件中使用Vuex来存储和管理数据了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

铁锤妹妹@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值