小兔新--项目实战总结1

news2024/12/23 0:03:28

目录

Layout-吸顶导航交互实现

吸顶交互

vueUse

 Layout-Pinia优化重复请求

为什么要优化

如何优化

Home-懒加载指令优化

场景和指令用法

实现思路和步骤

 回顾核心步骤代码

 Home-懒加载指令优化

问题1:逻辑书写位置不合理

问题2:重复监听问题

一级分类-解决路由缓存问题

什么是路由缓存问题

方案一:给router-view添加key

方案二:使用beforeRouteUpdate导航钩子

一级分类-使用逻辑函数拆分业务 

 具体怎么做

核心思想总结

二级分类-商品列表实现

列表无限加载功能实现

 定制路由行为解决什么问题

详情页-图片预览组件封装

通过小图切换大图实现

放大镜效果实现

 放大镜效果实现 - 滑块跟随鼠标移动

 放大镜效果实现 - 大图效果实现

 放大镜效果实现 - 鼠标移入控制显隐

总结 

详情页-通用组件统一注册全局

为什么要优化


Layout-吸顶导航交互实现

吸顶交互

要求:浏览器在上下滚动的过程中,如果距离顶部的滚动距离大于78px,吸顶导航显示,小于78px隐藏

 

代码实现

获取滚动距离 动态添加类名show 控制组件的显示和隐藏

<template>
  <div class="app-header-sticky" :class="{ show: y > 78 }">
    <div class="container">
      <RouterLink class="logo" to="/" />
      <!-- 导航区域 -->
      <ul class="app-header-nav">
        <li class="home" v-for="item in getCategoryList" :key="item.id">
          <RouterLink to="/">{{ item.name }}</RouterLink>
        </li>
      </ul>

      <div class="right">
        <RouterLink to="/">品牌</RouterLink>
        <RouterLink to="/">专题</RouterLink>
      </div>
    </div>
  </div>
</template>


<style scoped lang='scss'>
.app-header-sticky {
  width: 100%;
  height: 80px;
  position: fixed;
  left: 0;
  top: 0;
  z-index: 999;
  background-color: #fff;
  border-bottom: 1px solid #e4e4e4;
  // 此处为关键样式!!!
  // 状态一:往上平移自身高度 + 完全透明
  transform: translateY(-100%);
  opacity: 0;

  // 状态二:移除平移 + 完全不透明
  &.show {
    transition: all 0.3s linear;
    transform: none;
    opacity: 1;
  }

  .container {
    display: flex;
    align-items: center;
  }

  .logo {
    width: 200px;
    height: 80px;
    background: url("@/assets/images/logo.png") no-repeat right 2px;
    background-size: 160px auto;
  }

  .right {
    width: 220px;
    display: flex;
    text-align: center;
    padding-left: 40px;
    border-left: 2px solid $xtxColor;

    a {
      width: 38px;
      margin-right: 40px;
      font-size: 16px;
      line-height: 1;

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

.app-header-nav {
  width: 820px;
  display: flex;
  padding-left: 40px;
  position: relative;
  z-index: 998;

  li {
    margin-right: 40px;
    width: 38px;
    text-align: center;

    a {
      font-size: 16px;
      line-height: 32px;
      height: 32px;
      display: inline-block;

      &:hover {
        color: $xtxColor;
        border-bottom: 1px solid $xtxColor;
      }
    }

    .active {
      color: $xtxColor;
      border-bottom: 1px solid $xtxColor;
    }
  }
}
</style>

vueUse

1. 安装Vueuse
VueUse 的官方(https://vueuse.org/)的介绍说这是一个 Composition API 的工具集合,适用于 Vue 2.x 或者 Vue 3.x,用起来和 React Hooks 还挺像的。

VueUse 插件的安装

npm install @vueuse/core

useScroll 

响应式获取滚动位置和状态。

<script setup lang="ts">
import { useScroll } from '@vueuse/core'

const el = ref<HTMLElement | null>(null)
const { x, y, isScrolling, arrivedState, directions } = useScroll(el)
</script>

<template>
  <div ref="el"></div>
</template>

 Layout-Pinia优化重复请求

为什么要优化

 结论:俩个导航中的列表是完全一致的,但是要发送俩次网络请求,存在浪费。通过Pinia集中管理数据,再把数据给 组件使用

如何优化

Home-懒加载指令优化

场景和指令用法

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

指令用法:

在图片img身上绑定指令,该图片只有在正式进入到视口区域时才会发送图片网络请求

实现思路和步骤

核心原理:图片进入视口才发送资源请求

 回顾核心步骤代码

app.directive注册全局指令(指令名称,对象【指令生命周期】)

传递给指令的值。例如在 v-my-directive="1 + 1" 中,值是 2

vueUes (useIntersectionObserver)监听是否进入视口区域

会重复监听 赋值完成后 调用返回值 stop停止监听 

进入到视口区域在给图片的src赋值 src有值后才会发送情求实现懒加载

 Home-懒加载指令优化

问题1:逻辑书写位置不合理

问:懒加载指令的逻辑直接写到入口文件中,合理吗?

答:不合理,入口文件通常只做一些初始化的事情,不应该包含太多的逻辑代码,可以通过插件的方法把懒加载指令 封装为插件,main.js入口文件只需要负责注册插件即可

问题2:重复监听问题

useIntersectionObserver对于元素的监听是一直存在的,除非手动停止监听,存在内存浪费 解决思路:在监听的图片第一次完成加载之后就停止监听

 一个插件可以是一个拥有 install() 方法的对象,也可以直接是一个安装函数本身。安装函数会接收到安装它的应用实例和传递给 app.use() 的额外选项作为参数:

app.use执行 install 会被调用 传入app实例对象

// 定义懒加载插件
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);
                // 会重复监听  复制完成后调用stop 停止监听
                const { stop } = useIntersectionObserver(
                    el,
                    ([{ isIntersecting }]) => {
                        console.log(isIntersecting, '是否进入到视口区域');
                        if (isIntersecting) {
                            // 进入视口区域
                            el.src = binding.value
                            stop()
                        }
                    }
                )
            },
        })
    }
}
import { lazyPlugin } from '@/directives'
app.use(lazyPlugin)

一级分类-解决路由缓存问题

什么是路由缓存问题

题:一级分类的切换正好满足上面的条件,组件实例复用,导致分类数据无法更新 解决问题的思路:1. 让组件实例不复用,强制销毁重建 2. 监听路由变化,变化之后执行数据更新操作 

方案一:给router-view添加key

以当前路由完整路径为key的值,给router-view组件绑定

方案二:使用beforeRouteUpdate导航钩子

beforeRouteUpdate钩子函数可以在每次路由更新之前执行,在回调中执行需要数据更新的业务逻辑即可

import { onBeforeRouteUpdate } from "vue-router";
// 路由参数变化的时候,可以将分类接口重新发送
onBeforeRouteUpdate((to) => {
  console.log("路由变化了");
  //   存在问题:使用最新的路由参数 请求最新的分类数据
  getTopCategory(to.params.id);
});



  <!-- 添加key 破坏副用机制 强制销毁重建 -->
  <!-- <router-view :key="$route.fullPath"></router-view> -->

1. 路由缓存问题产生的原因是什么?

路由只有参数变化时,会复用组件实例

2. 俩种方案都可以解决路由缓存问题,如何选择呢?

在意性能问题,选择onBeforeUpdate, 精细化控制 不在意性能问题,选择key,简单粗暴

一级分类-使用逻辑函数拆分业务 

概念理解

基于逻辑函数拆分业务是指把同一个组件中独立的业务代码通过函数做封装处理,提升代码的可维护性

 具体怎么做

实现步骤:
1. 按照业务声明以 `use` 打头的逻辑函数

2. 把独立的业务逻辑封装到各个函数内部
3. 函数内部把组件中需要用到的数据或者方法return出去

4. 在组件中调用函数把数据或者方法组合回来使用

核心思想总结

1. 逻辑拆分的过程是一个拆分再组合的过程

 2. 函数use打头,内部封装逻辑,return组件需要用到的数据和方法给组件消费

// 封装banner相关业务代码
import { getBannerAPI } from "@/apis/layout";
import { onMounted, ref } from "vue";
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 { useBanner } from "@/composables/useBanner";
const { bannerList } = useBanner();
</script>

二级分类-商品列表实现

列表无限加载功能实现

 核心实现逻辑:使用elementPlus提供的 v-infinite-scroll 指令监听是否满足触底条件,满足加载条件时让页数参数加 一获取下一页数据,做新老数据拼接渲染

基础思路

  1. 触底条件满足之后 page++,拉取下一页数据
  2. 新老数据做数组拼接
  3. 判断是否已经全部加载完毕,停止监听

 在要实现滚动加载的列表上上添加v-infinite-scroll,并赋值相应的加载方法,可实现滚动到底部时自动执行加载方法。

infinite-scroll-disabled 是否禁用

     <div
        class="body"
        v-infinite-scroll="load"
        :infinite-scroll-disabled="disabled"
      >
        <!-- 商品列表-->
        <GoodsItem v-for="good in goodList" :goods="good" :key="good.id" />
      </div>


// 加载更多
const disabled = ref(false)
const load = async () => {
  console.log('加载更多数据咯')
  // 获取下一页的数据
  reqData.value.page++
  const res = await getSubCategoryAPI(reqData.value)
  goodList.value = [...goodList.value, ...res.result.items]
  // 加载完毕 停止监听
  if (res.result.items.length === 0) {
    disabled.value = true
  }
}

 定制路由行为解决什么问题

在不同路由切换的时候,可以自动滚动到页面的顶部,而不是停留在原先的位置

如何配置:vue-router支持 scrollBehavior 配置项,可以指定路由切换时的滚动位置

详情页-图片预览组件封装

通过小图切换大图实现

思路:维护一个数组图片列表,鼠标划入小图记录当前小图下标值,通过下标值 在数组中取对应图片,显示到大图位置 

放大镜效果实现

功能拆解
1. 左侧滑块跟随鼠标移动
2. 右侧大图放大效果实现
3. 鼠标移入控制滑块和大图显示隐藏

 放大镜效果实现 - 滑块跟随鼠标移动

思路: 获取到当前的鼠标在盒子内的相对位置(useMouseInElement),控

制滑块跟随鼠标移动(left/top)

1. 有效移动范围内的计算逻辑
横向:100 < elementX < 300,left = elementX - 小滑块宽度一半

纵向: 100 < elementY < 300,top = elementY - 小滑块高度一半

2. 边界距离控制

横向:elementX > 300 left = 200 ,elementX < 100 left = 0

纵向:elementY > 300 top = 200, elementY < 100 top = 0

 放大镜效果实现 - 大图效果实现

效果:为实现放大效果,大图的宽高是小图的俩倍

思路:大图的移动方向和滑块移动方向相反,且数值为2倍

 放大镜效果实现 - 鼠标移入控制显隐

总结 

<script setup>
import { ref, watch } from "vue";
import { useMouseInElement } from "@vueuse/core";

defineProps({
  imageList: {
    type: Array,
    default: () => {},
  },
});
// 图片列表
// const imageList = [
//   "https://yanxuan-item.nosdn.127.net/d917c92e663c5ed0bb577c7ded73e4ec.png",
//   "https://yanxuan-item.nosdn.127.net/e801b9572f0b0c02a52952b01adab967.jpg",
//   "https://yanxuan-item.nosdn.127.net/b52c447ad472d51adbdde1a83f550ac2.jpg",
//   "https://yanxuan-item.nosdn.127.net/f93243224dc37674dfca5874fe089c60.jpg",
//   "https://yanxuan-item.nosdn.127.net/f881cfe7de9a576aaeea6ee0d1d24823.jpg",
// ];
// 小图切换 大图显示
const curIndex = ref(0);
// 实现鼠标移入交互
const mouseEnterFn = (i) => (curIndex.value = i);
//  获取鼠标相对位置
const target = ref(null);
const { elementX, elementY, isOutside } = useMouseInElement(target);
//  控制滑块跟随鼠标移动(监听elementX/Y变化,一旦变化 重新设置left/top)
const left = ref(0);
const top = ref(0);

const positionX = ref(0);
const positionY = ref(0);
watch([elementX, elementY, isOutside], () => {
  console.log("xy变化了");
  // 如果鼠标没有移入到盒子里面 直接不执行后面的逻辑
  if (isOutside.value) return;
  // 有效范围内控制滑块距离
  // 横向
  if (elementX.value > 100 && elementX.value < 300) {
    left.value = elementX.value - 100;
  }
  // 纵向
  if (elementY.value > 100 && elementY.value < 300) {
    top.value = elementY.value - 100;
  }
  // 处理边界
  if (elementX.value > 300) {
    left.value = 200;
  }
  if (elementX.value < 100) {
    left.value = 0;
  }

  if (elementY.value > 300) {
    top.value = 200;
  }
  if (elementY.value < 100) {
    top.value = 0;
  }
  // 控制大图的显示
  positionX.value = -left.value * 2;
  positionY.value = -top.value * 2;
});
</script>


<template>
  <div class="goods-image">
    <!-- 左侧大图-->
    <div class="middle" ref="target">
      <img :src="imageList[curIndex]" alt="" />
      <!-- 蒙层小滑块 -->
      <div
        class="layer"
        v-show="!isOutside"
        :style="{ left: `${left}px`, top: `${top}px` }"
      ></div>
    </div>
    <!-- 小图列表 -->
    <ul class="small">
      <li
        v-for="(img, i) in imageList"
        :key="i"
        @mouseenter="mouseEnterFn(i)"
        :class="{ active: i === curIndex }"
      >
        <img :src="img" alt="" />
      </li>
    </ul>
    <!-- 放大镜大图 -->
    <div
      class="large"
      :style="[
        {
          backgroundImage: `url(${imageList[curIndex]})`,
          backgroundPositionX: `${positionX}px`,
          backgroundPositionY: `${positionY}px`,
        },
      ]"
      v-show="!isOutside"
    ></div>
  </div>
</template>

<style scoped lang="scss">
.goods-image {
  width: 480px;
  height: 400px;
  position: relative;
  display: flex;

  .middle {
    width: 400px;
    height: 400px;
    background: #f5f5f5;
  }

  .large {
    position: absolute;
    top: 0;
    left: 412px;
    width: 400px;
    height: 400px;
    z-index: 500;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
    background-repeat: no-repeat;
    // 背景图:盒子的大小 = 2:1  将来控制背景图的移动来实现放大的效果查看 background-position
    background-size: 800px 800px;
    background-color: #f8f8f8;
  }

  .layer {
    width: 200px;
    height: 200px;
    background: rgba(0, 0, 0, 0.2);
    // 绝对定位 然后跟随咱们鼠标控制left和top属性就可以让滑块移动起来
    left: 0;
    top: 0;
    position: absolute;
  }

  .small {
    width: 80px;

    li {
      width: 68px;
      height: 68px;
      margin-left: 12px;
      margin-bottom: 15px;
      cursor: pointer;

      &:hover,
      &.active {
        border: 2px solid $xtxColor;
      }
    }
  }
}
</style>

详情页-通用组件统一注册全局

为什么要优化

背景:components目录下有可能还会有很多其他通用型组件,有可能在多个业务模块中共享,所有统一进行全局组件 注册比较好

//  把commponents中的组件进行全局注册
// 通过插件的方式
import imageView from "@/components/imageView/index.vue";
import sku from "@/components/XtxSku/index.vue";
export const commponentsPlugin = {
    install(Vue) {
        // Vue.component(组件名字, 组件对象)
        Vue.component('imageView', imageView)
        Vue.component('sku', sku)
    }
}


import { commponentsPlugin } from '@/components/index'
app.use(commponentsPlugin)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/564172.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Web基本与HTTP协议

目录 一&#xff1a;域名 1.域名概述 2.域名空间结构 3. 域名注册 二&#xff1a; 网页访问&#xff08;HTTP/HTTPS&#xff09; 1.网页的基本概念 2.网站 3.主页 4.域名 5.HTTP 6.URL 7.网页基本标签 &#xff08;1&#xff09;网页摘要信息的作用 &#xff08;…

Oracle SQLTUNING优化SQL

通过AWR的TOPSQL观察到sqlid为6pk9xfmjd0s8j的sql语句耗时1分钟。 通过ash观察到&#xff0c;该SQL由于全表扫描占用了大量的IO资源。 解析执行计划&#xff0c;看到是MED_SALE_ORDER表做了全表扫描。 该表有678W条记录&#xff0c;全表扫描必定IO指标会冲高&#xff0c;影…

SFM过程(一)

以图像为基础的三维重建过程SFM基本如下图所示&#xff1a; 以SfM-Toy-Library代码为例&#xff0c;如下&#xff1a; ErrorCode SfM::runSfM() {if (mImages.size() < 0) {cerr << "No images to work on." << endl;return ErrorCode::ERROR;}//in…

Java-网络原理

目录 一、网络互连 局域网LAN 广域网WAN 二、网络通信基础 IP地址 端口号 认识协议 三、五元组 四、协议分层 五、OSI七层模型 六、TCP/IP五层&#xff08;或四层&#xff09;模型 网络分层对应 七、封装和分用 一、网络互连 随着时代的发展&#xff0c;越来越需要计…

Smartbi电子表格故事之用数据进行销售问题分析

天津小麦商贸有限公司&#xff08;X&M&#xff09;成立于2012年11月&#xff0c;主营业务是商贸流通业&#xff0c;主要是日用商品的批发销售。 2012年前&#xff0c;公司创始人&#xff08;总经理和销售总监&#xff09;一直从事外贸的生意&#xff0c;自从2008年金融危机…

java 课程信息管理系统Myeclipse开发mysql数据库struts2结构java编程计算机网页项目

一、源码特点 java 课程信息管理系统 是一套完善的web设计系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为 TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysql&#xff0c;使…

Scrum的执行过程及产品Backlog梳理的目的、时间、内容

Scrum的迭代运行过程 产品Backlog梳理 目的&#xff1a; •对下个Sprint的需求进行需求细节梳理和精化&#xff0c;识别技术风险和依赖&#xff0c;完成估算和优先级排序。 时间&#xff1a; •本Sprint开始后第6天&#xff0c;2小时以内 。 内容&#xff1a; •理解需求…

2020下半年

2020下半年 d a b 小阶向大阶对齐 b b 平均cpi: MIPS: d c 公加验&#xff0c;私解签 加密防止被动攻击&#xff0c;认证防止主动攻击 a 访问控制包括&#xff1a;授权&#xff0c;确定存取权限&#xff0c;实施存取权限 c a c a 先申请先得 b b 著作权包括&…

论文分享 | WSBERT:Weighted Sampling for Masked Language Modeling

本次分享阿里巴巴达摩院语音实验室、新南威尔士大学与香港科技大学&#xff08;广州&#xff09;等在ICASSP2023会议发表的论文《Weighted Sampling for Masked Language Modeling》。该论文主要提出了两种简单有效的加权采样策略&#xff0c;来缓解掩码语言模型&#xff08;ML…

七、数据仓库详细介绍(调度)

0x00 前言 在之前的文章&#xff0c;我们规划了数仓架构&#xff0c;制定了数仓规范&#xff0c;然后在架构和规范的指导下设计了存储模型、构建了 ETL 系统。 数仓模型解决了数据存储问题&#xff0c;ETL 解决了数据同步集成计算问题&#xff0c;而调度解决的是自动化问题。 …

【音视频】音视频自研对比

WVP(自研视频调度平台&#xff09;CM8播放器&#xff08;前端&#xff09;传输协议&#xff1a;websocket 编码协议&#xff1a;ts 语言&#xff1a;TS传输协议&#xff1a;websocket 编码协议&#xff1a;fmp4、mse 语言&#xff1a;JS框架&#xff1a;jessibuca 性质&#xf…

TCP和UDP

文章目录 1、常见误区2、TCP和UDP的区别和应用场景3、使用了它们的协议有哪些&#xff1f;4、总结 我之前的文章中讲解即时通讯系统中&#xff0c;就用到了可靠传输协议TCP&#xff0c;在那里面我们解决了TCP传输中的半包和黏包&#xff0c;而且还实际的讨论了用了TCP&#xff…

免费开源 | 基于SpringBoot的学生信息管理系统

一、项目简介 学生信息管理后台&#xff0c;包括学生管理、课程管理、成绩管理&#xff0c;适用于课设等&#xff0c;仅具有管理功能&#xff0c;感兴趣的伙伴可以再次完善。 二、软件架构 springboot mybatis thymeleaf 三、系统运行 注&#xff1a;mysql数据库要8.0以上…

【老王读SpringMVC-6】Controller method 的正常返回值处理

handler method 的返回值处理包含两个方面&#xff1a; 一是&#xff1a;返回值对象的处理&#xff1b; 二是&#xff1a;视图的渲染。 返回值对象的处理 通过前面的分析&#xff0c;我们知道&#xff0c; 被 RequestMapping 标记 handler method 的执行是通过调用 RequestMa…

Java的反射

1.反射的定义 Java反射是Java被视为动态&#xff08;或准动态&#xff09;语言的一个关键性质。JAVA反射机制是在运行状态中&#xff0c;对于任意一个类&#xff0c;都能够知道这个类的所有属性和方法&#xff1b;对于任意一个对象&#xff0c;都能够调用它的任意方法和属性&a…

react学习3 生命周期

componentDidMount()与render()一个级别的&#xff0c;在组件挂载完成之后调用 卸载组件&#xff1a;REACTDOM.unmountComponentAtNode() componentWillUnmount() 组件马上被卸载的时候 老生命周期&#xff1a; 新的生命周期&#xff1a; 废弃了&#xff08;加上Unsate_还是…

Python3安装pyhanlp最佳解决方法

1、Hanlp介绍 Hanlp是一款中文自然语言处理工具。Hanlp支持多种自然语言处理任务&#xff0c;包括分词、词性标注、命名实体识别、依存句法分析、情感分析、文本分类等。其主要优点包括&#xff1a; 高准确率&#xff1a;Hanlp采用了基于神经网络的分词方法&#xff0c;有效提…

关于字符集

字符集 编码与解码 计算机中储存的信息都是用二进制数表示的&#xff0c;而我们在屏幕上看到的数字、英文、标点符号、汉字等字符是二进制数转换之后的结果。按照某种规则&#xff0c;将字符存储到计算机中&#xff0c;称为编码 。反之&#xff0c;将存储在计算机中的二进制数…

【老王读SpringMVC-7】Controller method 的异常返回值处理

通过前面分析 Controller method的执行过程&#xff0c;我们知道&#xff0c; handler method 的执行是通过调用 ServletInvocableHandlerMethod#invokeAndHandle()。 执行过程中的异常全部会往上抛出&#xff0c;然后由 DispatcherServlet 来处理。 DispatcherServlet 会调用 …

MATLAB第十章_图像处理算法

目录 图像处理算法 图像处理基础 图像处理函数 默认显示方式 添加颜色条 显示多帧图像 显示动画 三维材质图像 图像的直方图 灰度变换 均衡直方图 图像处理应用 图像增强 图像重建 图像变换 图像压缩 图像分割 图像边缘检测 图像识别 图像处理算法 图像处理…