Vue电商项目实战-2

本文详细介绍了Vue电商项目的开发过程,包括Home模块的结构拆分,使用Pinia管理响应式数据,实现分类和轮播图。同时,文章涵盖了组件封装,如HomePanel和GoodsItem,以及图片懒加载指令的实现。此外,还涉及了路由配置、面包屑导航的构建,以及一级分类配置和轮播图功能的实现。
摘要由CSDN通过智能技术生成

Vue电商项目实战-2

Home模块开发

  • 整体结构拆分和分类实现
    • 将Home模块划分成了左侧分类和轮番图、新鲜好物、人气推荐和产品列表五个部分,在Home文件夹下创建文件夹components用于存放各个部分
    • 在home中的Index.vue文件中实现各个部分的引入
    • 分类实现通过准备模板后的用Pinia中的数据渲染来实现,引入UseCategoryStore,通过三个v-for实现数据左侧分类的渲染
<script setup>
import { useCategoryStore } from '@/stores/category'
import { includeBooleanAttr } from '@vue/shared';
import indexVue from '../index.vue';
const categoryStore = useCategoryStore()

</script>

<template>
  <div class="home-category">
    <ul class="menu">
      <li v-for="item in categoryStore.categoryList" :key="item.id">
        <RouterLink to="/">{{ item.name }}</RouterLink>
        <RouterLink v-for="i in item.children.slice(0,2)" :key="i" to="/">{{i.name}}</RouterLink>
        <!-- 弹层layer位置 -->
        <div class="layer">
          <h4>分类推荐 <small>根据您的购买或浏览记录推荐</small></h4>
          <ul>
            <li v-for="i in item.goods" :key="i.id">
              <RouterLink to="/">
                <img :src="i.picture" alt="" />
                <div class="info">
                  <p class="name ellipsis-2">
                    {{i.name}}
                  </p>
                  <p class="desc ellipsis">{{i.desc}}</p>
                  <p class="price"><i>¥</i>{{i.price}}</p>
                </div>
              </RouterLink>
            </li>
          </ul>
        </div>
      </li>
    </ul>
  </div>
</template>


<style scoped lang='scss'>
.home-category {
  width: 250px;
  height: 500px;
  background: rgba(0, 0, 0, 0.8);
  position: relative;
  z-index: 99;

  .menu {
    li {
      padding-left: 40px;
      height: 55px;
      line-height: 55px;

      &:hover {
        background: $xtxColor;
      }

      a {
        margin-right: 4px;
        color: #fff;

        &:first-child {
          font-size: 16px;
        }
      }

      .layer {
        width: 990px;
        height: 500px;
        background: rgba(255, 255, 255, 0.8);
        position: absolute;
        left: 250px;
        top: 0;
        display: none;
        padding: 0 15px;

        h4 {
          font-size: 20px;
          font-weight: normal;
          line-height: 80px;

          small {
            font-size: 16px;
            color: #666;
          }
        }

        ul {
          display: flex;
          flex-wrap: wrap;

          li {
            width: 310px;
            height: 120px;
            margin-right: 15px;
            margin-bottom: 15px;
            border: 1px solid #eee;
            border-radius: 4px;
            background: #fff;

            &:nth-child(3n) {
              margin-right: 0;
            }

            a {
              display: flex;
              width: 100%;
              height: 100%;
              align-items: center;
              padding: 10px;

              &:hover {
                background: #e3f9f4;
              }

              img {
                width: 95px;
                height: 95px;
              }

              .info {
                padding-left: 10px;
                line-height: 24px;
                overflow: hidden;

                .name {
                  font-size: 16px;
                  color: #666;
                }

                .desc {
                  color: #999;
                }

                .price {
                  font-size: 22px;
                  color: $priceColor;

                  i {
                    font-size: 16px;
                  }
                }
              }
            }
          }
        }
      }

      // 关键样式  hover状态下的layer盒子变成block
      &:hover {
        .layer {
          display: block;
        }
      }
    }
  }
}
</style>
  • banner轮播页设计
    • 从Elementplus上获得模板
    • 在apis中创建Home.js用于封装接口
    • 组件渲染数据,创建响应数据BnanerList,书写getBanner来实现数据获取,在onMounted中调用getBanner
    • 模板中用v-for循环实现遍历
import httpInstance from '@/utils/https'

export function getBannerAPI(){
    return httpInstance({
        url:'home/banner'
    })
}
<script setup>
import { getBannerAPI } from '@/apis/home'
import { ref,onMounted } from 'vue'

const bannerList = ref([])
const getBanner=async()=>{
    const res = await getBannerAPI()
    console.log(res)
    bannerList.value=res.result
}
onMounted(()=>getBanner())
</script>



<template>
  <div class="home-banner">
    <el-carousel height="500px">
      <el-carousel-item v-for="item in bannerList" :key="item.id">
        <img :src="item.imgUrl" alt="">
      </el-carousel-item>
    </el-carousel>
  </div>
</template>



<style scoped lang='scss'>
.home-banner {
  width: 1240px;
  height: 500px;
  position: absolute;
  left: 0;
  top: 0;
  z-index: 98;

  img {
    width: 100%;
    height: 500px;
  }
}
</style>
  • 面板组件封装
    • 封装的核心思想:把可复写的结构只写一次,把可能变化的部分抽象为组件参数(props/插槽)
    • 实现步骤:
      • 准备静态模板
      • 抽象可变部分
        • 纯文本抽象为prop插入
        • 复杂的内容抽象为插槽插入
<script setup>
defineProps({
    Title:{
        type:String
    },
    subTitle:{
        type:String
    }
})
</script>


<template>
  <div class="home-panel">
    <div class="container">
      <div class="head">
         <!-- 主标题和副标题 -->
        <h3>
          {{ Title }}<small>{{ subTitle }}</small>
        </h3>
      </div>
      <!-- 主体内容区域 -->
      <slot  />
    </div>
  </div>
</template>

<style scoped lang='scss'>
.home-panel {
  background-color: #fff;

  .head {
    padding: 40px 0;
    display: flex;
    align-items: flex-end;

    h3 {
      flex: 1;
      font-size: 32px;
      font-weight: normal;
      margin-left: 6px;
      height: 35px;
      line-height: 35px;

      small {
        font-size: 16px;
        color: #999;
        margin-left: 20px;
      }
    }
  }
}
</style>

  • 新鲜好物、热卖单品业务实现
    • 引入模板、运用封装的组件进行从写
    • 在home.js写接口,在HomeNew中创建响应数据NewList,书写getNewList来实现数据获取,在onMounted中调用getNewList
    • 同理:在home.js写接口,在HotNew中创建响应数据HotList,书写getHotList来实现数据获取,在onMounted中调用getHotList

export const getNewAPI=()=>{
   return httpInstance({
       url:'home/new'
   })
}

export const getHotAPI=()=>{
   return httpInstance({
       url:'home/hot'
   })
}
<script setup>
import HomePannel from './HomePannel.vue';
import { getHotAPI } from '@/apis/home.js';
import { onMounted,ref } from 'vue';

const hotList = ref([])
const getHotList = async()=>{
    const res= await getHotAPI()
    hotList.value=res.result
}
onMounted(()=>getHotList())
</script>

<template>
    <HomePannel Title="热品推荐" subTitle="当下爆款 疯狂推荐">
        <ul class="goods-list">
    <li v-for="item in hotList" :key="item.id">
      <RouterLink to="/">
        <img :src="item.picture" alt="" />
        <p class="name">{{ item.name }}</p>
        <p class="price">{{ item.alt }}</p>
      </RouterLink>
    </li>
  </ul>
    </HomePannel>

</template>


<style scoped lang='scss'>
.goods-list {
  display: flex;
  justify-content: space-between;
  height: 406px;

  li {
    width: 306px;
    height: 406px;

    background: #f0f9f4;
    transition: all .5s;

    &:hover {
      transform: translate3d(0, -3px, 0);
      box-shadow: 0 3px 8px rgb(0 0 0 / 20%);
    }

    img {
      width: 306px;
      height: 306px;
    }

    p {
      font-size: 22px;
      padding-top: 12px;
      text-align: center;
      text-overflow: ellipsis;
      overflow: hidden;
      white-space: nowrap;
    }

    .price {
      color: $priceColor;
    }
  }
}
</style>
<script setup>
import HomePannel from './HomePannel.vue';
import { getNewAPI } from '@/apis/home.js';
import { onMounted,ref } from 'vue';

const newList = ref([])
const getNewList = async()=>{
    const res= await getNewAPI()
    newList.value=res.result
}
onMounted(()=>getNewList())
</script>

<template>
    <HomePannel Title="好物分享" subTitle="完美好物 敬请挑选">
        <ul class="goods-list">
    <li v-for="item in newList" :key="item.id">
      <RouterLink to="/">
        <img :src="item.picture" alt="" />
        <p class="name">{{ item.name }}</p>
        <p class="price">&yen;{{ item.price }}</p>
      </RouterLink>
    </li>
  </ul>
    </HomePannel>

</template>


<style scoped lang='scss'>
.goods-list {
  display: flex;
  justify-content: space-between;
  height: 406px;

  li {
    width: 306px;
    height: 406px;

    background: #f0f9f4;
    transition: all .5s;

    &:hover {
      transform: translate3d(0, -3px, 0);
      box-shadow: 0 3px 8px rgb(0 0 0 / 20%);
    }

    img {
      width: 306px;
      height: 306px;
    }

    p {
      font-size: 22px;
      padding-top: 12px;
      text-align: center;
      text-overflow: ellipsis;
      overflow: hidden;
      white-space: nowrap;
    }

    .price {
      color: $priceColor;
    }
  }
}
</style>
  • 图片懒加载指令实现
    • 通过官方网站提示在main.js文件中实现空指令
      • el:指令绑定的那个元素 img
      • binding:binding.value 指令等于号后面绑定的值
    • 实现指令核心逻辑,判断图片是否进入视口区域,若进入,实现发送
app.directive('m-lazy',{
   mounted(el,binding){
       //el:指令绑定的那个元素 img
       //binding:binding.value 指令等于号后面绑定的值
       console.log(el,binding.value);
       useIntersectionObserver(
           el,
           ([{ isIntersecting }]) => {
               console.log(isIntersecting);
               if(isIntersecting){
                   //进入视口区域
                   el.src=binding.value
               }
           },
         )
   }
})
  • 软加载指令优化
    • 将懒加载插件单独写出来
      • 在derectives中的创建index.js,写入:
      • 在main.js中引入
    • useIntersectionObserver存在重复监听的问题
      • 在监听的图片完成第一次加载之后就停止监听
mport { useIntersectionObserver } from '@vueuse/core'
//定义懒加载插件
export const lazyPlugin = {
    install(app){
        //懒加载逻辑
        app.directive('m-lazy',{
            mounted(el,binding){
                //el:指令绑定的那个元素 img
                //binding:binding.value 指令等于号后面绑定的值
                console.log(el,binding.value);
                useIntersectionObserver(
                    el,
                    ([{ isIntersecting }]) => {
                        console.log(isIntersecting);
                        if(isIntersecting){
                            //进入视口区域
                            el.src=binding.value
                        }
                    },
                  )
            }
        })
        
    }
}

在这里插入图片描述

  • Product产品列表渲染
    • 引入静态模板
    • 封装接口
    • 获取数据渲染模板
    • 图片懒加载
<script setup>
import HomePanel from './HomePannel.vue'
import { getGoodsAPI } from '@/apis/home'
import { onMounted,ref } from 'vue';

//获取数据列表
// const goodsProduct=ref([])
// const getGoods = async=()=>{
//     const res = await getGoodsAPI()
//     goodsProduct.value = res.result 
// }
// onMounted(()=>getGoods)
const goodsProduct = ref([])
const getGoods = async () => {
  const { result } = await getGoodsAPI()
  goodsProduct.value = result
}
onMounted( ()=> getGoods() )
</script>

<template>
  <div class="home-product">
    <HomePanel :title="cate.name" v-for="cate in goodsProduct" :key="cate.id">
      <div class="box">
        <RouterLink class="cover" to="/">
          <img v-m-lazy="cate.picture" />
          <strong class="label">
            <span>{{ cate.name }}</span>
            <span>{{ cate.saleInfo }}</span>
          </strong>
        </RouterLink>
        <ul class="goods-list">
          <li v-for="good in cate.goods" :key="good.id">
            <RouterLink to="/" class="goods-item">
              <img v-m-lazy="good.picture" alt="" />
              <p class="name ellipsis">{{ good.name }}</p>
              <p class="desc ellipsis">{{ good.desc }}</p>
              <p class="price">&yen;{{ good.price }}</p>
            </RouterLink>
          </li>
        </ul>
      </div>
    </HomePanel> 
  </div>
</template>

<style scoped lang='scss'>
.home-product {
  background: #fff;
  margin-top: 20px;
  .sub {
    margin-bottom: 2px;

    a {
      padding: 2px 12px;
      font-size: 16px;
      border-radius: 4px;

      &:hover {
        background: $xtxColor;
        color: #fff;
      }

      &:last-child {
        margin-right: 80px;
      }
    }
  }

  .box {
    display: flex;

    .cover {
      width: 240px;
      height: 610px;
      margin-right: 10px;
      position: relative;

      img {
        width: 100%;
        height: 100%;
      }

      .label {
        width: 188px;
        height: 66px;
        display: flex;
        font-size: 18px;
        color: #fff;
        line-height: 66px;
        font-weight: normal;
        position: absolute;
        left: 0;
        top: 50%;
        transform: translate3d(0, -50%, 0);

        span {
          text-align: center;

          &:first-child {
            width: 76px;
            background: rgba(0, 0, 0, 0.9);
          }

          &:last-child {
            flex: 1;
            background: rgba(0, 0, 0, 0.7);
          }
        }
      }
    }

    .goods-list {
      width: 990px;
      display: flex;
      flex-wrap: wrap;

      li {
        width: 240px;
        height: 300px;
        margin-right: 10px;
        margin-bottom: 10px;

        &:nth-last-child(-n + 4) {
          margin-bottom: 0;
        }

        &:nth-child(4n) {
          margin-right: 0;
        }
      }
    }

    .goods-item {
      display: block;
      width: 220px;
      padding: 20px 30px;
      text-align: center;
      transition: all .5s;

      &:hover {
        transform: translate3d(0, -3px, 0);
        box-shadow: 0 3px 8px rgb(0 0 0 / 20%);
      }

      img {
        width: 160px;
        height: 160px;
      }

      p {
        padding-top: 10px;
      }

      .name {
        font-size: 16px;
      }

      .desc {
        color: #999;
        height: 29px;
      }

      .price {
        color: $priceColor;
        font-size: 20px;
      }
    }
  }
}
</style>
export const getGoodsAPI=()=>{
    return httpInstance({
        url:'home/goods'
    })
}
  • GoodsItem组件封装
    • 在多个业务模块中将使用相同的商品展示模板,为避免重复定义,封装起来
    • 把要显示的数据对象抽象为props参数,传入什么就显示什么
    • 在Home的components中创建goodsItem.vue,写入
    • 并在HomeProduct中引入:
<script setup>
defineProps({
  goods: {
    type: Object,
    default:() => { }
  }
})
</script>

<template>
     <RouterLink to="/" class="goods-item">
              <img v-m-lazy="goods.picture" alt="" />
              <p class="name ellipsis">{{ goods.name }}</p>
              <p class="desc ellipsis">{{ goods.desc }}</p>
              <p class="price">&yen;{{ goods.price }}</p>
            </RouterLink>
</template>

<style lang="scss">
.goods-item {
      display: block;
      width: 220px;
      padding: 20px 30px;
      text-align: center;
      transition: all .5s;

      &:hover {
        transform: translate3d(0, -3px, 0);
        box-shadow: 0 3px 8px rgb(0 0 0 / 20%);
      }

      img {
        width: 160px;
        height: 160px;
      }

      p {
        padding-top: 10px;
      }

      .name {
        font-size: 16px;
      }

      .desc {
        color: #999;
        height: 29px;
      }

      .price {
        color: $priceColor;
        font-size: 20px;
      }
    }
</style>
 <ul class="goods-list">
          <li v-for="goods in cate.goods" :key="goods.id">
            <GoodsItem :goods="goods" />
          </li>
        </ul>

一级分类配置

  • 路由配置
    • 为分类的不同页面设置不同的参数路由
    • 在导航栏上配置导航区域链接
{
          path:'category/:id',
          component:category,
        }
<li class="home" v-for="item in categoryStore.categoryList" :key="item.id">
          <RouterLink active-class="active" :to="`/category/${item.id}`">{{ item.name }}</RouterLink>
        </li>
  • 面包屑导航渲染
    • 准备组件模板
    • 封装接口函数:需要通过id参数来实现传参
    • 使用useRoute得到route实例,得到路由参数,调用接口获得数据
    • 渲染模板
<script setup>
import { getCategoryAPI } from '@/apis/category';
import { onMounted, ref } from 'vue';
import { useRoute } from 'vue-router'; 

const categaryData = ref([])
const route=useRoute()
const getCategory= async () =>{
    const res = await getCategoryAPI(route.params.id)
    categaryData.value=res.result
}
onMounted(()=>getCategory)
</script>

<template>
  <div class="top-category">
    <div class="container m-top-20">
      <!-- 面包屑 -->
      <div class="bread-container">
        <el-breadcrumb separator=">">
          <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
          <el-breadcrumb-item>居家</el-breadcrumb-item>
        </el-breadcrumb>
      </div>
    </div>
  </div>
</template>


<style scoped lang="scss">
.top-category {
  h3 {
    font-size: 28px;
    color: #666;
    font-weight: normal;
    text-align: center;
    line-height: 100px;
  }

  .sub-list {
    margin-top: 20px;
    background-color: #fff;

    ul {
      display: flex;
      padding: 0 32px;
      flex-wrap: wrap;

      li {
        width: 168px;
        height: 160px;


        a {
          text-align: center;
          display: block;
          font-size: 16px;

          img {
            width: 100px;
            height: 100px;
          }

          p {
            line-height: 40px;
          }

          &:hover {
            color: $xtxColor;
          }
        }
      }
    }
  }

  .ref-goods {
    background-color: #fff;
    margin-top: 20px;
    position: relative;

    .head {
      .xtx-more {
        position: absolute;
        top: 20px;
        right: 20px;
      }

      .tag {
        text-align: center;
        color: #999;
        font-size: 20px;
        position: relative;
        top: -20px;
      }
    }

    .body {
      display: flex;
      justify-content: space-around;
      padding: 0 40px 30px;
    }
  }

  .bread-container {
    padding: 25px 0;
  }
}
</style>
  • 轮播图功能实现
    • 改造之前banner的接口,默认为1,商品为2
    • 在category的index.js文件里实现获取banner的功能
<script setup>
import { getCategoryAPI } from '@/apis/category';
import { onMounted, ref } from 'vue';
import { useRoute } from 'vue-router'; 
import { getBannerAPI } from '@/apis/home'

const categaryData = ref([])
const route=useRoute()
const getCategory= async () =>{
    const res = await getCategoryAPI(route.params.id)
    categaryData.value=res.result
}
onMounted(()=>getCategory)

//获取banner
const bannerList = ref([])
const getBanner=async()=>{
    const res = await getBannerAPI({
        distributionSite:'2'
    })
    console.log(res)
    bannerList.value=res.result
}
onMounted(()=>getBanner())
</script>

<template>
  <div class="top-category">
    <div class="container m-top-20">
      <!-- 面包屑 -->
      <div class="bread-container">
        <el-breadcrumb separator=">">
          <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
          <el-breadcrumb-item>居家</el-breadcrumb-item>
        </el-breadcrumb>
      </div>
      <!-- 轮播图 -->
      <div class="home-banner">
    <el-carousel height="500px">
      <el-carousel-item v-for="item in bannerList" :key="item.id">
        <img :src="item.imgUrl" alt="">
      </el-carousel-item>
    </el-carousel>
  </div>
    </div>
  </div>
</template>


<style scoped lang="scss">
.top-category {
  h3 {
    font-size: 28px;
    color: #666;
    font-weight: normal;
    text-align: center;
    line-height: 100px;
  }

  .sub-list {
    margin-top: 20px;
    background-color: #fff;

    ul {
      display: flex;
      padding: 0 32px;
      flex-wrap: wrap;

      li {
        width: 168px;
        height: 160px;


        a {
          text-align: center;
          display: block;
          font-size: 16px;

          img {
            width: 100px;
            height: 100px;
          }

          p {
            line-height: 40px;
          }

          &:hover {
            color: $xtxColor;
          }
        }
      }
    }
  }

  .ref-goods {
    background-color: #fff;
    margin-top: 20px;
    position: relative;

    .head {
      .xtx-more {
        position: absolute;
        top: 20px;
        right: 20px;
      }

      .tag {
        text-align: center;
        color: #999;
        font-size: 20px;
        position: relative;
        top: -20px;
      }
    }

    .body {
      display: flex;
      justify-content: space-around;
      padding: 0 40px 30px;
    }
  }

  .bread-container {
    padding: 25px 0;
  }
}
.home-banner {
  width: 1240px;
  height: 500px;
  margin:0 auto;
 

  img {
    width: 100%;
    height: 500px;
  }
}
</style>
  • 激活状态控制和列表状态管理
    • RoutetLink支持激活样式属性显示的类名,只需要给active-class设置对应的类名即可
    • 分类的数据在面包屑导航时已经获取,只需要通过v-for遍历即可
<script setup>
import { getCategoryAPI } from '@/apis/category';
import { onMounted, ref } from 'vue';
import { useRoute } from 'vue-router'; 
import { getBannerAPI } from '@/apis/home';
import GoodsItem from '../Home/components/GoodsItem.vue';

const categoryData = ref([])
const route=useRoute()
const getCategory= async (id) =>{
   const res = await getCategoryAPI(id)
   categoryData.value=res.result
}
onMounted(()=>getCategory(route.params.id))

//获取banner
const bannerList = ref([])
const getBanner=async()=>{
   const res = await getBannerAPI({
       distributionSite:'2'
   })
   console.log(res)
   bannerList.value=res.result
}
onMounted(()=>getBanner())
</script>

<template>
 <div class="top-category">
   <div class="container m-top-20">
     <!-- 面包屑 -->
     <div class="bread-container">
       <el-breadcrumb separator=">">
         <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
         <el-breadcrumb-item>{{ categoryData.name }}</el-breadcrumb-item>
       </el-breadcrumb>
     </div>
     <!-- 轮播图 -->
     <div class="home-banner">
   <el-carousel height="500px">
     <el-carousel-item v-for="item in bannerList" :key="item.id">
       <img :src="item.imgUrl" alt="">
     </el-carousel-item>
   </el-carousel>
 </div>
 <div class="sub-list">
 <h3>全部分类</h3>
 <ul>
   <li v-for="i in categoryData.children" :key="i.id">
     <RouterLink to="/">
       <img :src="i.picture" />
       <p>{{ i.name }}</p>
     </RouterLink>
   </li>
 </ul>
</div>
<div class="ref-goods" v-for="item in categoryData.children" :key="item.id">
 <div class="head">
   <h3>- {{ item.name }}-</h3>
 </div>
 <div class="body">
   <GoodsItem v-for="goods in item.goods" :goods="goods" :key="goods.id" />
 </div>
</div>

   </div>
 </div>
</template>


<style scoped lang="scss">
.top-category {
 h3 {
   font-size: 28px;
   color: #666;
   font-weight: normal;
   text-align: center;
   line-height: 100px;
 }

 .sub-list {
   margin-top: 20px;
   background-color: #fff;

   ul {
     display: flex;
     padding: 0 32px;
     flex-wrap: wrap;

     li {
       width: 168px;
       height: 160px;


       a {
         text-align: center;
         display: block;
         font-size: 16px;

         img {
           width: 100px;
           height: 100px;
         }

         p {
           line-height: 40px;
         }

         &:hover {
           color: $xtxColor;
         }
       }
     }
   }
 }

 .ref-goods {
   background-color: #fff;
   margin-top: 20px;
   position: relative;

   .head {
     .xtx-more {
       position: absolute;
       top: 20px;
       right: 20px;
     }

     .tag {
       text-align: center;
       color: #999;
       font-size: 20px;
       position: relative;
       top: -20px;
     }
   }

   .body {
     display: flex;
     justify-content: space-around;
     padding: 0 40px 30px;
   }
 }

 .bread-container {
   padding: 25px 0;
 }
}
.home-banner {
 width: 1240px;
 height: 500px;
 margin:0 auto;


 img {
   width: 100%;
   height: 500px;
 }
}
</style>

  • 解决路由缓存问题
    • 路由缓存问题:一级路由切换时,会遇到相同的组件实例将被重复使用,组件的生命周期钩子不会被调用,导致分类数据无法更新
    • 解决思路:
      • 让组件实例不复用,强行销毁重建:添加key 破坏复用机制 强制销毁重建
      • 监听路由变化,变化之后执行路由更新操作:使用beforeRouteUpdata生命函数勾子即可 在每次路由更新之后执行 回调中执行需要的业务逻辑即可
const categoryData = ref([])
const route=useRoute()
const getCategory= async (id=route.params.id) =>{
   const res = await getCategoryAPI(id)
   categoryData.value=res.result
}
onMounted(()=>getCategory())

//路由参数发生变化时,需要categoryData重新发送
onBeforeRouteUpdate((to)=>{
   console.log("路由改变")
   console.log(to)
   getCategory(to.params.id)
})
  • 使用逻辑函数拆分业务
    • 实现步骤:
      • 按照业务声明以use打头的业务逻辑
      • 把独立的业务逻辑封装到各个函数内部
      • 函数内部把组件需要用到的数据和方法return回来
      • 在组件中调用函数把数据或方法组合起来使用
    • 将轮播图数据获取和分类数据获取的业务逻辑分离出来到useBanner.js和useCategory.js中,在index.js中引入
import { ref,onMounted } from "vue";
import { useRoute } from "vue-router";
import { onBeforeRouteUpdate } from "vue-router";
import { getCategoryAPI } from '@/apis/category';

export function useCategory(){
    //获取分类数据
const categoryData = ref([])
const route=useRoute()
const getCategory= async (id=route.params.id) =>{
    const res = await getCategoryAPI(id)
    categoryData.value=res.result
}
onMounted(()=>getCategory())

//路由参数发生变化时,需要categoryData重新发送
onBeforeRouteUpdate((to)=>{
    console.log("路由改变")
    console.log(to)
    getCategory(to.params.id)
})

return{
    categoryData
}
}
import { ref,onMounted } from 'vue';
import { getBannerAPI } from '@/apis/home';

export function useBanner(){
    const bannerList = ref([])
const getBanner=async()=>{
    const res = await getBannerAPI({
        distributionSite:'2'
    })
    console.log(res)
    bannerList.value=res.result
}
onMounted(()=>getBanner())

return{
    bannerList
}
}
<script setup>
import GoodsItem from '../Home/components/GoodsItem.vue';
import { useBanner } from './composables/useBanner';
import { useCategory } from './composables/useCategory';

const { bannerList } = useBanner()
const { categoryData } = useCategory()

</script>

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

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值