1. Home-整体结构搭建和分类实现
结构的搭建
步骤:
1) 按照结构新增五个组件,准备最简单的模版,分别在Home模块的入口组件中引入
在组件里准备简单的模块,如下
//src/views/Home/components/HomeCategory.vue
<script setup>
</script>
<template>
<div> HomeCategory </div>
</template>
2)Home模块入口组件中引入并渲染
//src/views/Home/index.vue
<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>
分类的实现
步骤:
1)准备静态页面
2)使用pinia中的数据渲染(因为数据和吸顶导航的数据一样,所以直接拿取就可以)
//src/views/Home/components/HomeCategory.vue
<script setup>
import {useCategoryStore} from '@/stores/category'
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" />
<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>
2. Home-banner轮播图功能实现
步骤:
1)搭建静态页面
2)使用elementPlus搭建相关组件
3)获取数据
//src/apis/home.js
import httpInstance from "@/utils/http";
// 获取banner
export function getBannerAPI(){
return httpInstance({
url:'/home/banner'
})
}
4)渲染数据
//src/views/Home/components/HomeBanner.vue
<script setup>
import {getBannerAPI} from '@/apis/home'
import {onMounted, ref} from 'vue'
const bannerList=ref([])
const getBanner=async()=>{
const res=await getBannerAPI()
console.log(res);
bannerList.value=res.result
}
onMounted(()=>getBanner())
</script>
3. Home-面板组件封装
由于,新鲜好物和人气推荐模块,在
结构上非常相似
,只是内容不同。
通过组件封装可以
实现复用结构
的效果。
核心思路:把可复用的结构只写一次,把
可能发生变化的部分抽象成组件参数(props / 插槽)。
步骤:
1)创建三个组件,HomeHot,HomeNew,HomePanel,并给HomeHot,HomeNew准备静态模版
2)HomePanel里书写抽象可变的部分
- 主标题和副标题是
纯文本
,可以抽象成
prop
传入
- 主体内容是
复杂的模版
,抽象成
插槽
传入
4. Home-新鲜好物和人气推荐实现
步骤:
1)准备静态页面
2)先实现标题区域
//src/views/Home/components/HomeNew.vue
<script setup>
// 导入封装的组件
import HomePanel from "./HomePanel.vue";
</script>
<template>
//使用封装组件修改静态组件
<HomePanel title="新鲜好物" sub-title="新鲜出炉 品质靠谱">
<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">¥{{ item.price }}</p>
</RouterLink>
</li>
</ul>
</HomePanel>
<div></div>
<!-- 下面是插槽主体内容模版
<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">¥{{ item.price }}</p>
</RouterLink>
</li>
</ul>
-->
</template>
3)实现主体内容区域
调用接口获取数据,渲染数据
//src/apis/home.js
// 获取新鲜好物
export const findNewAPI=()=>{
return httpInstance({
url:'/home/new'
})
}
//src/views/Home/components/HomeNew.vue
<script setup>
// 导入封装的组件
import HomePanel from "./HomePanel.vue";
import { findNewAPI } from "@/apis/home";
import { onMounted, ref } from "vue";
// 获取数据
const newList = ref([]);
const getNewList = async () => {
const res = await findNewAPI();
newList.value = res.result;
};
onMounted(() => getNewList());
</script>
人气推荐的实现和新鲜好物一样。
5.Home-图片懒加载指令实现
场景:电商网站的首页通常会很长,用户不一定能访问到
页面靠下面的图片
,这类图片通过懒加载优化手段可以做到
只有进入视口区域才发送图片请求。
核心原理:图片进入视口才发送资源请求。
步骤:
1)
可以通过插件的方法把
懒加载指令
封装为插件。
懒加载指令可以使用vueUse完成。
当图片进入视口区域,发送图片资源请求
//src/directive/index.js
// 定义懒加载插件
import { useIntersectionObserver } from '@vueuse/core'
export const lazyPlugin = {
install(app) {
// 懒加载指令逻辑
app.directive('img-lazy', {
mounted(el, binding) {
// el:指令绑定的那个元素 img
// binding:binding.value 指令等于号后面绑定的表达式的值 图片url
console.log(el, binding.value);
// 利用vueUse判断图片是否进入视口区域
// isIntersecting是一个布尔值
useIntersectionObserver(el, ([{ isIntersecting }]) => {
console.log(isIntersecting);
if (isIntersecting) {
// 进入了视口区域,发送图片资源请求
el.src = binding.value
}
})
}
})
}
}
2)在main.js中进行插件的注册
//src/main.js
// 引入懒加载插件
import {lazyPlugin} from '@/directives'
// 注册懒加载插件
app.use(lazyPlugin)
3)解决重复监听问题
useIntersectionObserver会一直监听是否进入视口区域,每进入和离开一次,都会监听。
所以在监听的图片第一次完成加载之后就停止监听。
7.Home-Product产品列表实现
步骤:
1)准备静态页面
所以在Product页面上也要使用HomePanel组件。
所以记得引入。
2)封装接口
//src/apis/home.js
// 获取所有商品模块
export const getGoodsAPI=()=>{
return httpInstance({
url: '/home/goods'
})
}
3)获取数据,渲染数据
//src/views/Home/components/HomeProduct.vue
<script setup>
// 引入组件
import HomePanel from './HomePanel.vue'
import {getGoodsAPI} from '@/apis/home'
import { ref,onMounted } from 'vue'
const goodsProduct = ref([])
const getGoods = async () => {
const { result } = await getGoodsAPI()
goodsProduct.value = result
}
onMounted( ()=> getGoods() )
</script>
4)图片懒加载
8.Home-GoodsItem组件封装
注意产品列表的主体区域里,有很多这个组成,我们可以封装为一个组件,方便复用。
步骤:
1)创建GoodsItem组件
//src/views/Home/components/GoodsItem.vue
<script setup>
defineProps({
goods:{
type:Object,
default:()=>{}
}
})
</script>
<template>
<RouterLink to="/" class="goods-item">
<img v-img-lazy="goods.picture" alt="" />
<p class="name ellipsis">{{ goods.name }}</p>
<p class="desc ellipsis">{{ goods.desc }}</p>
<p class="price">¥{{ goods.price }}</p>
</RouterLink>
</template>
2)在HomeProduct组件中使用
//src/views/Home/components/HomeProduct.vue
<script setup>
import GoodsItem from './GoodsItem.vue'
</script>
总结:
1.纯展示类组件通用封装思路总结:
home页中的HomePanel和GoodsItem这两个组件就是纯展示类的组件。
1)搭建纯静态的部分,不管可变的部分
2)抽象可变的部分为组件参数
非复杂的模版抽象成props,复杂的结构模版抽象为插槽
2.home页面使用了图片懒加载。
把懒加载封装为了一个插件,可以全局使用。
3.使用elementPlus制作轮播图。