Home组件搭建【Vue3】

Home-整体结构搭建和分类实现

  1. 按照结构新增五个组件,准备最简单的模版,分别在Home模块的入口组件中引入

HomeCategory
HomeBanner
HomeNew
HomeHot
HomeProduct

HomeProduct
<script setup>
</script>

<template>
  <div> HomeCategory </div>
</template>
  1. Home模块入口组件中引入并渲染
<script setup>
import HomeCategory from './components/HomeCategory.vue'
import HomeBanner from './components/HomeBanner.vue'
import HomeNew from './components/HomeNew.vue'
import HomeHot from './components/HomeHot.vue'
import homeProduct from './components/HomeProduct.vue'
</script>

<template>
  <div class="container">
    <HomeCategory />
    <HomeBanner />
  </div>
  <HomeNew />
  <HomeHot />
  <homeProduct />
</template>

Home-banner轮播图实现

在这里插入图片描述

Home-面板组件封装

场景说明
问:组件封装解决了什么问题?
答:1. 复用问题 2. 业务维护问题
组件封装
核心思路:把可复用的结构只写一次,把可能发生变化的部分抽象成组件参数(props / 插槽)
实现步骤

  1. 不做任何抽象,准备静态模版
  2. 抽象可变的部分
  • 主标题和副标题是纯文本,可以抽象成prop传入
  • 主体内容是复杂的模版,抽象成插槽传入

纯展示类组件通用封装思路总结:

  1. 搭建纯静态的部分,不管可变的部分
  2. 抽象可变的部分为组件参数
    非复杂的模版抽象成props,复杂的结构模版抽象为插槽
<script setup>

defineProps({
  title: {
    type: String,
    default: ''
  },
  subTitle: {
    type: String,
    default: ''
  }
})

</script>


<template>
  <div class="home-panel">
    <div class="container">
      <div class="head">
        <!-- 主标题和副标题 -->
        <h3>
          {{ title }}<small>{{ subTitle }}</small>
        </h3>
      </div>
      <!-- 主体内容区域 -->
      <slot name="main" />
    </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-新鲜好物和人气推荐实现

新鲜好物实现
Home组件搭建【Vue3】_第1张图片

Home-图片懒加载指令实现

场景和指令用法
场景:电商网站的首页通常会很长,用户不一定能访问到页面靠下面的图片,这类图片通过懒加载优化手段可以做到只有进入视口区域才发送图片请求
指令用法:

<img v-img-lazy="item.picture" />

在图片img身上绑定指令,该图片只有在正式进入到视口区域时才会发送图片网络请求
实现思路和步骤:
核心原理:图片进入视口才发送资源请求
Home组件搭建【Vue3】_第2张图片

回顾核心步骤代码

  1. 空指令实现
app.directive('img-lazy', {
    mounted(el, binding){
        //el: 指令绑定的那个元素  img
        // binding: binding.value 指令等于号后面绑定的表达式的值 图片url
     }
 }
<img v-img-lazy="item.picture" alt="">
  1. 指令逻辑实现
useIntersectionObserver(el, ([{isIntersecting}]) => {
            console.log(isIntersecting);
            if(isIntersecting){
                // 进入视口区域
                el.src = binding.value
            }
        })

Home-懒加载指令优化

问题1:逻辑书写位置不合理
问:懒加载指令的逻辑直接写到入口文件中,合理吗?
答:不合理,入口文件通常只做一些初始化的事情,不应该包含太多的逻辑嗲吗,可以通过插件的方法把懒加载指令封装为插件,main.js入口文件只需要负责注册插件即可。

Home组件搭建【Vue3】_第3张图片
问题2:重复监听问题
useIntersectionObserver对于元素的监听是一直存在的,除非手动停止监听,存在内存浪费
解决思路:在监听的图片第一次完成加载之后就停止监听

const {stop} = useIntersectionObserver(el, ([{isIntersecting}]) => {
                    console.log(isIntersecting);
                    if(isIntersecting){
                        // 进入视口区域
                        el.src = binding.value
                        stop()
                    }
                })

Home-Product产品列表实现

Product产品列表
Product产品列表是一个常规的列表渲染,实现步骤如下:
Home组件搭建【Vue3】_第4张图片

Home-GoodsItem组件封装

为什么要封装GoodsItem组件
Home组件搭建【Vue3】_第5张图片
在小兔鲜项目的很多个业务模块中都需要用到同样的商品展示模块,没必要重复定义,封装起来,方便复用
如何封装
核心思想:把要显示的数据对象设计为props参数,传入什么数据对象就显示什么数据

GoodsItem属于纯展示类组件,这类组件的封装思路是什么?
__ 抽象Props参数,传入什么就显示什么

一级分类-整体认识和路由配置

Home组件搭建【Vue3】_第6张图片

	{
      path: 'category/:id',
      component: Category
    }
  <RouterLink :to="`/category/${item.id}`">{{item.name}}</RouterLink>

一级分类-面包屑导航渲染

在这里插入图片描述

  1. 认识组件准备模版
<script setup>

</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>
  1. 封装接口
import request from '@/utils/request'

/**
 * @description: 获取分类数据
 * @param {*} id 分类id 
 * @return {*}
 */
export const getTopCategoryAPI = (id) => {
  return request({
    url:'/category',
    params:{
      id
    }
  })
}
  1. 渲染面包屑导航
<script setup>
  import { findTopCategoryAPI } from '@/apis/category'
  const categoryData = ref({})
  const route = useRoute()
  const getCategory = async (id) => {
    // 如何在setup中获取路由参数 useRoute() -> route 等价于this.$route
    const res = await findTopCategoryAPI(id)
    categoryData.value = res.result
  }
  getCategory(route.params.id)
</script>


<template>
  <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>
</template>

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