创建两个组件
home-categroy.vue 分类菜单 home-banner.vue 轮播图
<template><div class="home-category"><ul class="menu"><li v-for="item in list" :key="item.id"><RouterLink to="/">{{item.name}}</RouterLink><!--v-for 和 v-if 不能一起用--><template v-if="item.children.length > 0"><RouterLink v-for="i in item.children" :key="i.id" to="/">{{i.name}}</RouterLink></template><!-- 弹层layer位置 --></li></ul></div></template><script>// 数据:使用的vuex category中的数据 只不过需要把chilren中的前俩项数据筛选出来// 基于某些现有的响应式数据经过一定的计算得到新的数据 -> 计算属性import { computed } from 'vue'import { useStore } from 'vuex'export default {name: 'HomeCategory',setup () {const store = useStore()const list = computed(() => {// 基于vuex中的数据 截取children中的前俩项// 基于一个数组 针对于数据的每一项都做处理 然后返回一个全新的数组 mapreturn store.state.category.list.map((item) => {return {id: item.id,name: item.name,children: item.children.slice(0, 2),goods: item.goods}})})return {list}}}</script><style scoped lang='less'>.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, 1);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 {display: block;}}}}}</style>
<template><div class="home-banner">banner</div></template><script>export default {name: 'HomeBanner'}</script><style scoped lang='less'>.home-banner {width: 1240px;height: 500px;position: absolute;left: 0;top: 0;z-index: 98;}</style>
导入首页
<template><div class="page-home"><div class="home-entry"><div class="container"><!-- 左侧分类 --><HomeCategory /><!-- banner轮播图 --><HomeBanner/></div></div></div></template><script>import HomeBanner from './components/home-banner.vue'import HomeCategory from './components/home-categroy.vue'export default {name: 'PageHome',components: {HomeBanner,HomeCategory}}</script><style></style>
分类
分类弹层组件
<<!-- 弹层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>
loading效果
骨架屏组件
<template><!-- 决定组件的宽高 --><div class="xtx-skeleton shan" :style="{ width: width + 'px', height: height + 'px' }"><!-- 决定组件的背景色 --><div class="block" :style="{ backgroundColor: bg}"></div></div></template><script>export default {name: 'XtxSkeleton',props: {// 宽度定制width: {type: Number,default: 100},// 高度定制height: {type: Number,default: 60},// 背景颜色定制bg: {type: String,default: '#ccc'}}}</script><style scoped lang="less">.xtx-skeleton {display: inline-block;position: relative;overflow: hidden;vertical-align: middle;.block {width: 100%;height: 100%;border-radius: 2px;}}.shan {&::after {content: "";position: absolute;animation: shan 1.5s ease 0s infinite;top: 0;width: 50%;height: 100%;background: linear-gradient(to left,rgba(255, 255, 255, 0) 0,rgba(255, 255, 255, 0.3) 50%,rgba(255, 255, 255, 0) 100%);transform: skewX(-45deg);}}@keyframes shan {0% {left: -100%;}100% {left: 120%;}}</style>
封装插件
import Skeleton from './Skeleton/index.vue'export default {install (app) {app.component('Skeleton', Skeleton)}}
// 插件import ComponentsPlugin from '@/components/index'const app = createApp(App)app.use(ComponentsPlugin).use(store).use(router).mount('#app')
轮播图
<template><div class="box" style="height: 500px"><div class="xtx-slider" @mouseenter="clearTimer" @mouseleave="startTimer"><!-- 图片列表 --><ul class="slider-body" ><!--fade: 当fade类名存在 当前图片就显示 不存在就不显示--><liclass="slider-item"v-for="(item, i) in sliders":key="i":class="{ fade: curIndex === i }"><img :src="item.imgUrl" alt="" /></li></ul><!-- 圆圈切换按钮 --><div class="slider-indicator"><spanv-for="(item, index) in sliders":key="index"@click="curIndex = index":class="{ active: curIndex === index }"></span></div></div></div></template><script>/**目标:点击圆圈按钮 实现对应图片的切换思路:1. 图片和圆圈按钮数量是一样的 下标值是对应的2. 记录一下当前点击的是哪一项3. 需要根据记录下来的下标值 去配合:class 控制fade这个类名是否应该显示*//**目标:图片的自动轮播功能思路:哪个数据变化决定了图片切换? 从之前手动修改curIndex的值 变成一个自动修改 每隔几秒修改一下计时器 setInterval*//**目标:鼠标移入暂停播放 鼠标移除再次开启思路:暂停 - 清除定时器 定时器id 开启 - 再执行一次定时器*/import { onMounted, onUnmounted, ref } from 'vue'export default {name: 'XtxSlider',props: {// 数据列表sliders: {type: Array,default: () => {return []}},autoPlay: {type: Boolean,default: true}},setup (props) {const curIndex = ref(0)// 声明一个存放定时器的数据const timer = ref(null)function clearTimer () {clearInterval(timer.value)}function startTimer () {// 开启定时器 每隔2s中修改一下curIndex的值initLoop()}function initLoop () {if (!props.autoPlay) {return false}// 开启定时器 每隔2s中修改一下curIndex的值timer.value = window.setInterval(() => {// 最大能到多大// 图片总数为4 length - 1为3 只要我发现你大于3了// 我就会重新赋值为 0 ,永远不能到达4 最大只能等于3curIndex.value++if (curIndex.value > props.sliders.length - 1) {curIndex.value = 0}}, 2000)}onMounted(() => {initLoop()})onUnmounted(() => {// 清理工作clearInterval(timer.value)})return {curIndex,clearTimer,startTimer}}}</script><style scoped lang='less'>.xtx-slider {width: 100%;height: 100%;min-width: 300px;min-height: 150px;position: relative;.slider {&-body {width: 100%;height: 100%;}&-item {width: 100%;height: 100%;position: absolute;left: 0;top: 0;opacity: 0;transition: opacity 0.5s linear;&.fade {opacity: 1;z-index: 1;}img {width: 100%;height: 100%;}}&-indicator {position: absolute;left: 0;bottom: 20px;z-index: 2;width: 100%;text-align: center;span {display: inline-block;width: 12px;height: 12px;background: rgba(0, 0, 0, 0.2);border-radius: 50%;cursor: pointer;~ span {margin-left: 12px;}&.active {background: #fff;}}}&-btn {width: 44px;height: 44px;background: rgba(0, 0, 0, 0.2);color: #fff;border-radius: 50%;position: absolute;top: 228px;z-index: 2;text-align: center;line-height: 44px;opacity: 0;transition: all 0.5s;&.prev {left: 20px;}&.next {right: 20px;}}}&:hover {.slider-btn {opacity: 1;}}}</style>
注册后使用
数据懒加载
computed
当依赖项不变的时候会有缓存
不支持异步的操作
不能直接改变值
改值get set
watch
不支持缓存
支持异步操作
属性 deep 深度监听
使用场景
