ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【Vue项目】尚品汇(三)Home模块+Floor模块+Swiper轮播图

2022-07-23 14:34:00  阅读:155  来源: 互联网

标签:Vue 轮播 state 模块 import 组件 home swiper


写在前面

今天是7.23,这一篇内容主要完成了Home模块和部分Search模块的开发,主要是使用了swiper轮播图插件获取vuex仓库数据展示组件以及其他信息。

1 Search模块

1.1 Search模块的全部商品分类的显示

需求分析 :在home模块中,需要显示商品分类的三级联动全局组件,而在search模块则不需要。

①在Search组件中引入typeNav全局组件,并在一级分类元素上添加v-show,在data中添加show数据

<div class="sort" v-show="show">
data() {
    return {
      curIndex: -1,
      show: true
    };
  },

②在TypeNav组件的mounted函数中根据当前路由修改show的值

mounted() {
    // 通知vuex向服务器发送数据,并存储于home数据仓库中
    this.$store.dispatch("CategoryList");
    // 当前组件不是home组件则不显示
    if(this.$route.name!='home') {
      this.show = false;
    }
  },

1.2 Search模块三级联动模块鼠标进出效果

在三级目录父元素上添加leaveShow和enterShow函数

<div @mouseleave="leaveShow()" @mouseenter="enterShow()">

并在方法中定义,其中leavaShow函数需要对当前路由进行判断,home组件的三级联动模块在鼠标移开的时候是不需要消失的

enterShow() {
      this.show = true;
    },
    leaveShow() {
      this.curIndex = -1;
      if(this.$route.name != 'home') {
        this.show = false;
      } 
    },

1.3 Search模块中三级联动模块的过渡动画

transition只能和v-show或者v-if一起使用

<transition name="sort">
  <div class="sort" v-show="show">
      <div class="all-sort-list2">

          <div class="item"  
              v-for="(c1, index) in CateGoryList" :key="c1.categoryId" 
              :class="{cur:index==curIndex}"
              @click="goSearch"
          >

              <h3 @mouseenter="changeIndex(index)">
                  <a :data-cateGoryName="c1.categoryName" :data-cateGory1Id="c1.categoryId">{{c1.categoryName}}</a>
              </h3>
              <div class="item-list clearfix" :style="{display:index==curIndex?'block':'none'}">
                  <div class="subitem" v-for="(c2, index) in c1.categoryChild" :key="c2.categoryId">
                      <dl class="fore">
                          <dt>
                              <a :data-cateGoryName="c2.categoryName" :data-cateGory2Id="c2.categoryId">{{c2.categoryName}}</a>
                          </dt>
                          <dd>
                              <em v-for="(c3, index) in c2.categoryChild" :key="c3.categoryId">
                                  <a :data-cateGoryName="c3.categoryName" :data-cateGory3Id="c3.categoryId">{{c3.categoryName}}</a>
                              </em>
                          </dd>
                      </dl>
                  </div>
              </div>
          </div>

      </div>
  </div>
</transition>

定义动画样式:

    // 过渡动画样式
    // 过渡动画开始状态
    .sort-enter{
      height: 0px;
    }
    // 过渡动画结束状态
    .sort-enter-to{
      height: 461px;
    }
    // 定义动画的时间、速率等
    .sort-enter-active{
      transition: .2s linear;
      // 可以解决文字的显示问题
      overflow: hidden;
    }

1.5 TypeNav列表优化

存在的问题:在home和search路由切换的过程里面,换出的路由会被销毁,换入的路由会重新创建,如此一来每次都需要向服务器发送请求请求列表数据,这显然是不合理的,我们需要的是每次使用只请求一次数据,然后放入输入仓库供各模块使用即可。

分析:因为要求只请求一次数据,所以可以在App根组件的mounted中向数据仓库进行派发acion的操作,TypeNav全局组件的mounted就不需要进行重复的操作了。

App.vue

<template>
  <div id="app">
    <Header></Header>
    <router-view></router-view>
    <Footer v-show="$route.meta.showFooter"></Footer>
  </div>
</template>

<script>
import Header from './components/Header'
import Footer from './components/Footer'

export default {
  name: 'App',
  components: {
    Header,
    Footer
  },
  mounted() {
    this.$router.push('/home');
    this.$store.dispatch('CategoryList')
  },
}
</script>

<style>
</style>

TypeNav模块

  mounted() {
    // 通知vuex向服务器发送数据,并存储于home数据仓库中
    // App根组件请求一次,这里注释掉了
    // this.$store.dispatch("CategoryList");

    // 当前组件不是home组件则不显示
    if (this.$route.name != "home") {
      this.show = false;
    }
  },

1.6 合并参数

分析:现在Header组件TypeNav组件都可以通过goSearch方法进行路由跳转到search路由,但是Header组件会向路由通过params参数传递搜索关键字,而三级联动TypeNav组件则是通过query参数向路由传递categoryname以及三级的categoryid参数,因此需要对两种参数进行合并,如果先点击三级联动再点击搜索、或者先点击搜索再点击三级联动,出现的效果应该是两种参数的集合。

实现:在进行路由跳转之前判断一下当前路由是否含有另一个组件的params或者query参数,如果存在则添加到路由器中一起进行传递。

Header模块(使用的是params参数)

    goSearch() {
      let location = {
        name: "search"
      }
      let params = {
        keyWord: this.keyWord
      }
      location.params = params
      if(this.$route.query) {
        location.query = this.$route.query
      }

      this.$router.push(location);
    }

TypeNav模块(使用的是query参数)

    goSearch(event) {
      let element = event.target;
      let {categoryname, category1id, category2id, category3id} =
          element.dataset;
      if (categoryname) {
        // 整理参数
        let location = {
          name: "search",
        };
        let query = {
          categoryname: categoryname,
        };
        if (category1id) {
          query.category1id = category1id;
        } else if (category2id) {
          query.category2id = category2id;
        } else {
          query.category3id = category3id;
        }
        location.query = query;
        // 合并参数
        if (this.$route.params) {
          location.params = this.$route.params
        }
        // 转发路由传递参数
        this.$router.push(location);
      }

注意!!!

想要获取到转发路由器的query参数,需要在路由配置中做如下配置:

path: '/search:keyWord'

否则将无法获取到keyWord参数

2 Home模块

2.1 ListContainer轮播图组件

2.1.1 Mock.js 生成随机书籍 拦截Ajax请求

①安装mockjs(没有点)

npm install --save mockjs

②在项目src文件目录下建立mock文件夹

③将需要模拟的数据以json的形式放在mock下

④并将其中的静态资源放到public下面,这是因为webpack打包之后所有的静态资源会被放到public下面

⑤在mock下面调用引入Mock的mock方法

mock/service.js

import Mock from 'mockjs'

import banner from './banner.json'
import floor from './floor.json'

Mock.mock('/mock/banner', {code: 200, data: banner})
Mock.mock('/mock/floor', {code: 200, data: floor})floor)

webpack中json、图片是默认暴露的

⑥在main.js入口文件中引入mock

main.js

import Vue from 'vue'
import App from './App.vue'
import router from '@/router'
import TypeNav from '@/components/TypeNav'
import store from '@/store'
import '@/mock/service'

Vue.config.productionTip = false;
Vue.component(TypeNav.name, TypeNav);

new Vue({
  render: h => h(App),
  router,
  store
}).$mount('#app')
2.1.2 获取Mock中Banner轮播图的数据

①在home数据仓库中添加actionsmutations以及state

store/home/index.js

import {reqCategoryList} from '@/api'
import {reqBannerList} from "@/api";

const actions = {
    // 通过api中的函数调用,向服务器发送请求,获取服务器数据
    async CategoryList(context) {
        let result = await reqCategoryList();
        if(result.code === 200) {
            context.commit('CATEGORYLIST', result.data)
        }
    },
    async BannerList(context) {
        let result = await reqBannerList();
        if(result.code === 200) {
            context.commit('BANNERLIST',result.data)
        }
    }
}

const mutations = {
    CATEGORYLIST(state, CateGoryList) {
        state.CateGoryList = CateGoryList;
    },
    BANNERLIST(state, BannerList) {
        state.BannerList = BannerList
    }
}

const getters = {

}

const state = {
    CateGoryList: [],
    BannerList: []
}

// 将模块仓库内容对外暴露
export default {
    state,
    actions,
    mutations,
    getters
}

②在ListContainer组件中利用mapState生成对应计算属性

  computed: {
    // mapState生成vuex仓库数据的计算属性
    ...mapState({
      BannerList: (state) => {
        return state.home.BannerList
      }
    })
  }

③在轮播图中展示数据

          <div class="swiper-wrapper">
            <div class="swiper-slide" v-for="(banner, index) in BannerList" :key="banner.id">
              <img :src="banner.imgUrl"/>
            </div>
          </div>
2.1.3 swiper 轮播图插件

①swiper插件安装

npm i --save swiper@5

更高版本的swiper需要vue3

②引入swiper样式

main.js

import 'swiper/css/swiper.css'

③创建轮播图结构

        <div class="swiper-container" id="mySwiper">
          <div class="swiper-wrapper">
            <div class="swiper-slide" v-for="(banner, index) in BannerList" :key="banner.id">
              <img :src="banner.imgUrl"/>
            </div>
          </div>
          <!-- 如果需要分页器 -->
          <div class="swiper-pagination"></div>

          <!-- 如果需要导航按钮 -->
          <div class="swiper-button-prev"></div>
          <div class="swiper-button-next"></div>
        </div>

④new swiper:轮播图添加动态效果

    var mySwiper = new Swiper ('.swiper-container', {
      loop: true, // 循环模式选项

      // 如果需要分页器
      pagination: {
        el: '.swiper-pagination',
      },

      // 如果需要前进后退按钮
      navigation: {
        nextEl: '.swiper-button-next',
        prevEl: '.swiper-button-prev',
      },

      // 如果需要滚动条
      scrollbar: {
        el: '.swiper-scrollbar',
      },
    })

☆在哪里创建swiper实例呢?

1.在mounted生命周期钩子中:结果轮播图无法使用,这是因为mounted是在页面渲染之后运行,但是for循环中的内容牵扯到计算属性需要动态地渲染,这就使得轮播图插件渲染的时候for循环还没有进行,也就是还没有数据,因而轮播图无法呈现。

2.在updated生命周期钩子中:轮播图正常使用,结合updated是在数据动态更新后完成,会导致同一个组件中的其他数据刷新引起轮播图的刷新

3.使用watch监视轮播图无法使用,分析其原因,在于监视属性只能保证BannerList数据发生了变化,但是不能保证for渲染完成,这就使得swiper在for循环之前进行了渲染。

  watch: {
    BannerList: {
      handler(newVal, oldVal) {
        var mySwiper = new Swiper('.swiper-container', {
          loop: true, // 循环模式选项

          // 如果需要分页器
          pagination: {
            el: '.swiper-pagination',
          },

          // 如果需要前进后退按钮
          navigation: {
            nextEl: '.swiper-button-next',
            prevEl: '.swiper-button-prev',
          },

          // 如果需要滚动条
          scrollbar: {
            el: '.swiper-scrollbar',
          },
        })
      }
    }
  }

4.完美解决方案:watch + nextTick:nextTick包裹的函数会在下次DOM更新、循环结束之后执行回调。在修改数据之后立即使用这个方法,获取更新的DOM。

watch: {
    BannerList: {
      handler(newVal, oldVal) {
        this.$nextTick(() => {
          var mySwiper = new Swiper('.swiper-container', {
            loop: true, // 循环模式选项
            // 自动播放
            autoplay:true,
            // 如果需要分页器
            pagination: {
              el: '.swiper-pagination',
              clickable:true,
            },

            // 如果需要前进后退按钮
            navigation: {
              nextEl: '.swiper-button-next',
              prevEl: '.swiper-button-prev',
            },

            // 如果需要滚动条
            scrollbar: {
              el: '.swiper-scrollbar',
            },
          })
        })
      }
    }
  }

2.2 Floor组件

2.2.1 创建访问请求的api

/api/index.js

export const reqFloorList = () => {
    return mockRequests({
        url: '/floor',
        method: 'get'
    })
}
2.2.2 Vuex三大件

actions

    async FloorList(context) {
        let result = await reqFloorList();
        if (result.code === 200) {
            context.commit('FLOORLIST', result.data)
        }
    }

mutations

    FLOORLIST(state, FloorList) {
        state.FloorList = FloorList
    }

state

const state = {
    CateGoryList: [],
    BannerList: [],
    FloorList: []
}
2.2.3 在App.vue中派发floor的actions
this.$store.dispatch('FloorList')

这样做的好处是应用执行期间只需请求一次,每次路由跳转不必再发送请求,只需向vuex仓库中请求数据即可

2.2.4 在home组件中使用mapstate生成仓库数据的计算属性
    computed: {
      ...mapState({
        FloorList: (state) => {
            return state.home.FloorList
        }
      })
    }
2.2.5 生成floor组件,并进行数据的传送
    <div>
        <!-- 包含三级联动加顶部导航栏 -->
        <TypeNav/>
        <!-- 包含轮播图加右侧尚品汇快报 -->
        <ListContainer/>
        <!-- 今日推荐模块 -->
        <TodayRecommend/>
        <!-- 热卖排行模块 -->
        <Rank/>
        <!-- 猜你喜欢模块 -->
        <Like/>
        <!--  -->
        <Floor v-for="(floor, index) in FloorList" :key="floor.id" :floor="floor"/>
    </div>
2.2.6 Floor props接收数据,并进行一系列数据显示
<script>
import Swiper from "swiper";
export default {
  props: ['floor'],
  mounted() {
    var mySwiper = new Swiper ('.swiper-container', {
      loop: true, // 循环模式选项
      autoplay: true,
      // 如果需要分页器
      pagination: {
        el: '.swiper-pagination',
      },

      // 如果需要前进后退按钮
      navigation: {
        nextEl: '.swiper-button-next',
        prevEl: '.swiper-button-prev',
      },

      // 如果需要滚动条
      scrollbar: {
        el: '.swiper-scrollbar',
      },
    })
  }
}
</script>

注意这里的轮播图创建实例可以直接写在mounted上,这是因为数据是home组件传来的不需要动态生成,即创建实例的时候for循环不需要等待异步请求获取数据,而提前完成了渲染

展示

<template>
  <!--楼层-->
  <div class="floor">
    <div class="py-container">
      <div class="title clearfix">
        <h3 class="fl">{{ floor.name }}</h3>
        <div class="fr">
          <ul class="nav-tabs clearfix">
            <li :class="index == 0 ? 'active':'' " v-for="(nav, index) in floor.navList" :key="index">
              <a :href="nav.url" data-toggle="tab">{{nav.text}}</a>
            </li>
          </ul>
        </div>
      </div>
      <div class="tab-content">
        <div class="tab-pane">
          <div class="floor-1">
            <div class="blockgary">
              <ul class="jd-list">
                <li v-for="(keyword, index) in floor.keywords" :key="index">{{ keyword }}</li>
              </ul>
              <img :src="floor.bigImg"/>
            </div>
            <div class="floorBanner">
              <div class="swiper-container" id="floor1Swiper">
                <div class="swiper-wrapper">
                  <div class="swiper-slide" v-for="(carousel, index) in floor.carouselList" :key="index">
                    <img :src="carousel.imgUrl">
                  </div>
                </div>
                <!-- 如果需要分页器 -->
                <div class="swiper-pagination"></div>

                <!-- 如果需要导航按钮 -->
                <div class="swiper-button-prev"></div>
                <div class="swiper-button-next"></div>
              </div>
            </div>
            <div class="split">
              <span class="floor-x-line"></span>
              <div class="floor-conver-pit">
                <img :src="floor.recommendList[0]"/>
              </div>
              <div class="floor-conver-pit">
                <img :src="floor.recommendList[1]"/>
              </div>
            </div>
            <div class="split center">
              <img :src="floor.bigImg"/>
            </div>
            <div class="split">
              <span class="floor-x-line"></span>
              <div class="floor-conver-pit">
                <img :src="floor.recommendList[2]"/>
              </div>
              <div class="floor-conver-pit">
                <img :src="floor.recommendList[3]"/>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

2.3 共用组件提取Carsousel

可以观察到,Floor中和Home组件中都使用了轮播图,代码重复率较高,因此可以将代码拿出来成为一个公共的组件:Carsousel

components/Carsousel/index.vue

<template>
  <div class="swiper-container" ref="mySwiper">
    <div class="swiper-wrapper">
      <div class="swiper-slide" v-for="(carousel, index) in list" :key="carousel.id">
        <img :src="carousel.imgUrl"/>
      </div>
    </div>
    <!-- 如果需要分页器 -->
    <div class="swiper-pagination"></div>

    <!-- 如果需要导航按钮 -->
    <div class="swiper-button-prev"></div>
    <div class="swiper-button-next"></div>
  </div>
</template>

<script>
import Swiper from "swiper";

export default {
  name: 'Carouse',
  props: ['list'],
  watch: {
    list: {
      immediate: true,
      handler() {
        this.$nextTick(() => {
          var mySwiper = new Swiper('.swiper-container', {
            loop: true, // 循环模式选项
            autoplay: true,
            // 如果需要分页器
            pagination: {
              el: '.swiper-pagination',
              clickable :true,
            },

            // 如果需要前进后退按钮
            navigation: {
              nextEl: '.swiper-button-next',
              prevEl: '.swiper-button-prev',
            },

            // 如果需要滚动条
            scrollbar: {
              el: '.swiper-scrollbar',
            },
          })
        })
      }
    }
  }
}
</script>

<style scoped>

</style>

Home组件中的轮播图数据需要动态地从vuex中获取,因此采用watch+nextTick的方式。而Floor组件是从父组件中获取的数据,因此不需要采用这种方式,但是需要开启immediate模式,因为不设置的话计算属性检测不到开始的时候list的变化。综合两种写法便成了上面的代码。

标签:Vue,轮播,state,模块,import,组件,home,swiper
来源: https://www.cnblogs.com/tod4/p/16511959.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有