HomeCategory
HomeBanner
HomeNew
HomeHot
HomeProduct
HomeProduct
<script setup>
</script>
<template>
<div> HomeCategory </div>
</template>
<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. 业务维护问题
组件封装
核心思路:把可复用的结构只写一次,把可能发生变化的部分抽象成组件参数(props / 插槽)
实现步骤
纯文本
,可以抽象成prop
传入复杂的模版
,抽象成插槽
传入纯展示类组件通用封装思路总结:
<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>
场景和指令用法
场景:电商网站的首页通常会很长,用户不一定能访问到页面靠下面的图片
,这类图片通过懒加载优化手段可以做到只有进入视口区域才发送图片请求
指令用法:
<img v-img-lazy="item.picture" />
在图片img身上绑定指令,该图片只有在正式进入到视口区域时才会发送图片网络请求
实现思路和步骤:
核心原理:图片进入视口才发送资源请求
回顾核心步骤代码
app.directive('img-lazy', {
mounted(el, binding){
//el: 指令绑定的那个元素 img
// binding: binding.value 指令等于号后面绑定的表达式的值 图片url
}
})
<img v-img-lazy="item.picture" alt="">
useIntersectionObserver(el, ([{isIntersecting}]) => {
console.log(isIntersecting);
if(isIntersecting){
// 进入视口区域
el.src = binding.value
}
})
问题1:逻辑书写位置不合理
问:懒加载指令的逻辑直接写到入口文件中,合理吗?
答:不合理,入口文件通常只做一些初始化的事情,不应该包含太多的逻辑嗲吗,可以通过插件的方法把懒加载指令封装为插件
,main.js入口文件只需要负责注册插件
即可。
问题2:重复监听问题
useIntersectionObserver对于元素的监听是一直存在的,除非手动停止监听,存在内存浪费
解决思路:在监听的图片第一次完成加载之后就停止监听
const {stop} = useIntersectionObserver(el, ([{isIntersecting}]) => {
console.log(isIntersecting);
if(isIntersecting){
// 进入视口区域
el.src = binding.value
stop()
}
})
Product产品列表
Product产品列表是一个常规的列表渲染,实现步骤如下:
为什么要封装GoodsItem组件
在小兔鲜项目的很多个业务模块中都需要用到同样的商品展示模块,没必要重复定义,封装起来,方便复用
如何封装
核心思想:把要显示的数据对象设计为props参数
,传入什么数据对象就显示什么数据
GoodsItem属于纯展示类组件,这类组件的封装思路是什么?
__ 抽象Props参数,传入什么就显示什么
{
path: 'category/:id',
component: Category
}
<RouterLink :to="`/category/${item.id}`">{{item.name}}</RouterLink>
<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>
import request from '@/utils/request'
/**
* @description: 获取分类数据
* @param {*} id 分类id
* @return {*}
*/
export const getTopCategoryAPI = (id) => {
return request({
url:'/category',
params:{
id
}
})
}
<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>