Vue生态及实践 - 优化实践

news2024/9/22 21:26:47

目录

目标

keep alive

util/vue.js【vue里面常用的函数】

src/components/UKeepAlive.vue

无限加载列表优化的实现方案

src/util/throttle.js

src/components/UInfiniteList.vue

src/module/topic/views/UTopic.vue

献上一张ai生成图~


目标

  1. Keep Alive实践
  2. 长列表优化

keep alive

keep alive缓存组件

可以缓存router-view

// 整个路由做个keep-alive的缓存,减少dom直接生成的性能损耗
<keep-alive max="2">// max 缓存个数
    <router-view></router-view>
</keep-alive>

自己实现一个keep-alive组件

util/vue.js【vue里面常用的函数】

// src/util/vue.js
// 已经定义过的值,非undefined,非null
function isDef(x) {
  return x !== undefined && x !== null;
}
// 根据item【数组的一项值】删除此项
function remove(arr, item) {
  if (arr.length) {
    const index = arr.indexOf(item);
    if (index > -1) {
      return arr.splice(index, 1);
    }
  }
}
// 获取opts实例上的名字
function getComponentName(opts) {
  return opts && (opts.Ctor.options.name || opts.tag);
}
// children 是vue实例上的子节点,子数据
function getFirstComponentChild(children) {
  if (Array.isArray(children)) { // 是否是数组
    for (let i = 0; i < children.length; i++) {
      const c = children[i];
      if (isDef(c) && (isDef(c.componentOptions) || isAsyncPlaceholder(c))) {
        return c; // 返回第一个定义的子组件数据
      }
    }
  }
}
export { isDef, remove, getComponentName, getFirstComponentChild };

src/components/UKeepAlive.vue

// src/components/UKeepAlive.vue
<script>
import {
  isDef,
  remove,
  getComponentName,
  getFirstComponentChild
} from "../util/vue";
function pruneCacheEntry(cache, key, keys, current) {
    // 当前key的cache
  const cached = cache[key];
  // 当前key的cache存在,如果当前current不存在或者当前tag不等于current.tag
  // 他们的标签不一样
  if (cached && (!current || cached.tag !== current.tag)) {
      // 组件实例销毁方法
    cached.componentInstance.$destroy();
  }
  // 清空当前key的缓存
  cache[key] = null;
  // 删除keys里的key
  remove(keys, key);
}
export default {
  name: "u-keep-alive",
  abstract: true,
  props: {
    max: [Number, String]
  },
  created() {
    // 对应的组件映射成key,放到keys里,通过key索引到cache里
    this.keys = [];
    // 创造一个缓存节点
  	// 对象原型就是null,不会包含object方法,提升性能
    this.cache = Object.create(null);
  },
  // 渲染
  render() {
      // 获取u-keep-alive组件下的信息
    const slot = this.$slots.default;
    // 获取组件第一个子组件的vnode
    const vnode = getFirstComponentChild(slot);
    // 获得这个子组件信息
    const componentOptions = vnode && vnode.componentOptions;
    if (componentOptions) { // 存在,做一些缓存处理操作
      const { cache, keys } = this; // 解构出created时候定义的this
      // 定义一下key
      const key =
        vnode.key == null
        // 如果key不存在,得生成个key,组件【`${id}::${tag}`】表示key
          ? `${componentOptions.Ctor.cid}::${componentOptions.tag || ""}`
          : vnode.key;
    	// 如果刚刚这个key,在缓存里有了,
      if (cache[key]) {
    	// 直接把缓存赋值给【当前组件实例】
        vnode.componentInstance = cache[key].componentInstance;
        // 删除,添加,更新一下,把key放在最后
        remove(keys, key);
        keys.push(key);
      } else { // 不存在缓存,缓存一下
        cache[key] = vnode;
        keys.push(key);
        // 如果设置了最大缓存数量,并且超出了
        if (this.max && keys.length > parseInt(this.max)) {
 			// 把最早使用的清除keys[0]【移除最早的缓存cache】
          pruneCacheEntry(cache, keys[0], keys, this._vnode);
        }
      }
      // 开关标记keepAlive为true,并且vnode.componentInstance存在,就会直接渲染
      vnode.data.keepAlive = true;
    }
    return vnode; // 渲染vnode
  }
};
</script>

无限加载列表优化的实现方案

src/util/throttle.js

// 节流
export function throttle(fn, timeout = 10) {
  let timer;
  return function(...args) {
    if (timer) {
      timer = clearTimeout(timer);
    }
    timer = setTimeout(() => fn.apply(this, [...args]), timeout);
  };
}

src/components/UInfiniteList.vue

// renderless方案
<template>
  <div class="x-infinite" ref="container" :style="{padding: padding}"> // 上下滚动条位置填充占位
// 暴漏出去sliceItems
    <slot :sliceItems="sliceItems"></slot>
  </div>
</template>
<script>
import { throttle } from "../util/throttle"; // 节流函数
function getScrollTop() {
  return document.documentElement.scrollTop || document.body.scrollTop;
}
export default {
  props: {
    items: {
      required: true
    },
    itemHeight: { // 每条item高度
      required: true,
      type: Number
    }
  },
  data() {
    return {
      buffer: 5,// 缓冲
      scrollTop: 0,// 页面高度
      viewportHeight: 0// 当前视口高度
    };
  },
  computed: {
    over() { // 滚动过的节点个数
      return Math.max(//保证值大于0
        Math.floor(this.scrollTop / this.itemHeight) - this.buffer,
        0
      );
    },
    down() { // 将要滚动到的节点个数
      return Math.min(
        Math.ceil(
          (this.scrollTop + this.viewportHeight) / this.itemHeight + this.buffer
        ),
        this.items.length
      );
    },
    sliceItems() { // 中间的片段
      return this.items.slice(this.over, this.down);
    },
    padding() { // 计算当前dom结构的padding值,上下滚动条位置填充占位
      return `${this.over * this.itemHeight}px  // padding-top
      0 
      ${Math.max( (this.items.length - this.down) * this.itemHeight, 0 )}px // padding-bottom
      0`;
    }
  },
  created() {
      // 监听滚动事件
      // 初始值设置
    this.scrollTop = getScrollTop();
    this.viewportHeight = window.innerHeight;
    document.addEventListener("scroll", this.onScroll, {
      passive: true // 提升流畅度,不用等待onScroll执行完毕
    });
  },
  // 销毁事件监听
  destroyed() {
    document.removeEventListener("scroll", this.onScroll);
  },
  methods: {
    onScroll: throttle(function() {
      this.scrollTop = getScrollTop();
      this.viewportHeight = window.innerHeight;
    })
  }
};
</script>

src/module/topic/views/UTopic.vue

<template>
  <div>
    <u-infinite-list :items="items" :item-height="80" #default="{ sliceItems }">
      <u-list :items="sliceItems"></u-list>
    </u-infinite-list>
    自定义指令intersect,出现在屏幕中执行handler
    <div class="x-bottom" v-intersect="{ handler: fetchNext }">
      滚动到底部,加载下一页
    </div>
  </div>
</template>
<script>
import { createNamespacedHelpers } from "vuex";
import UList from "../components/UList.vue";
import UInfiniteList from "../../../components/UInfiniteList.vue";
// 便捷的映射topic命名空间的一个mapState方法
const { mapState, mapActions } = createNamespacedHelpers("topic");
export default {
  name: "u-top",
  props: ["type"],
  components: {
    UList,
    UInfiniteList,
  },
  computed: {
    ...mapState({
      items: (state) => state[state.activeType].items,
    }),
  },
  // asyncData({ store, route }) {
  //   return store
  //     .dispatch("topic/FETCH_LIST_DATA", {
  //       type: route.name
  //     })
  //     .catch(e => console.error(e));
  // },
  watch: {
    type(type) {
      this.fetchData({ type });
    },
  },
  mounted() {
    this.fetchNext();
  },
  methods: {
    ...mapActions({
      fetchData: "FETCH_LIST_DATA",
    }),
    fetchNext() {
      const { type } = this;
      this.fetchData({ type });
    },
  },
};
</script>
<style scoped>
.x-bottom {
  width: 100%;
  height: 1px;
}
</style>

献上一张ai生成图~

 

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

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

相关文章

基于pytorch的神经网络与对比学习CL的训练示例实战和代码解析

目录 对比学习原理解析构建一个对比学习模型&#xff08;代码详解&#xff09;导入库构建简单的神经网络构建对比学习的损失函数开始训练 完整代码 对比学习原理解析 对比学习&#xff08;Contrastive Learning&#xff09;是一种无监督学习方法&#xff0c;用于从未标记的数据…

3 STM32标准库函数 之 窗口看门狗(WWDG)所有函数的介绍及使用

3 STM32标准库函数 之 窗口看门狗&#xff08;WWDG&#xff09;所有函数的介绍及使用 1. 图片有格式2 文字无格式三 库函数之窗口看门狗&#xff08;WWDG&#xff09;所有函数的介绍及使用前言一、IWDG库函数固件库函数预览1.1 函 数 IWDG_WriteAccessCmd1.1.1 IWDG_WriteAcces…

string模拟实现

文章目录 1.回顾库函数strcpymemcpystrcmpstrstr 2.回顾类和对象哪些函数里会有this指针&#xff1f;this指针调用方法结论&#xff1a;只要是不修改this指针指向的对象内容的成员函数&#xff0c;都可以加上const自己写了构造函数&#xff0c;编译器不会自动生成默认构造2.1构…

代码随想录第21天 | 回溯理论基础 77. 组合

回溯理论基础 回溯法解决的问题都可以抽象为树形结构&#xff0c;是的&#xff0c;我指的是所有回溯法的问题都可以抽象为树形结构&#xff01; 因为回溯法解决的都是在集合中递归查找子集&#xff0c;集合的大小就构成了树的宽度&#xff0c;递归的深度&#xff0c;都构成的…

MySQL面试题总结(部分)

一.介绍MySQL为什么在面试中会提及 1.为什么要在面试时MySQL会被提及&#xff1f; 在面试中问MySQL问题有几个主要原因&#xff1a; 1. 数据库管理系统的重要性&#xff1a;MySQL作为一种常用的关系型数据库管理系统(RDBMS)&#xff0c;在互联网和企业应用中得到广泛使用。对数…

Conda安装及使用方法(常用命令)

系列文章目录 文章目录 系列文章目录前言一、Conda下载安装1.下载2.安装3.配置国内源 二、Conda安装Python环境1.创建虚拟环境2.激活虚拟环境3.虚拟环境安装Python库 三、Conda环境环境执行脚本四、PyCharm配置Conda环境五、Conda迁移环境1.方式一&#xff1a;拷贝环境2.方式二…

Modbus通信从入门到精通_1_Modbus通信基础

关于Modbus通信的相关知识比较零碎&#xff0c;此处对查找到的知识点从理论&#xff0c;通信协议、使用方法方面进行整理。 值得学习的博文&#xff1a;Modbus及调试用软件介绍&#xff1b;Modbus协议和上位机应用开发介绍 文章目录 1. Modbus通信理论1.1 Modbus通信特点1.2 存…

多线程(1): 线程的创建、回收、分离

1. 多线程概述 多线程在项目开发过程中使用频率非常高&#xff0c;因为使用多线程可以提高程序的并发性。提高程序并发性有两种方式:(1)多线程 (2)多进程。但是多线程对系统资源的消耗会更加少一些&#xff0c;并且线程和进程执行效率差不多。 在执行系统应用程序时&#xff…

2023/7/8总结

Tomcat 启动&#xff1a;双击bin目录下的startup.bat文件停止&#xff1a;双击bin目录下的shutdown.bat 文件访问 &#xff1a;http://localhost:8080&#xff08;默认是8080&#xff0c;可以修改&#xff09; git的使用 打开git bash git config --global user.name "名…

Vue3---什么是路由缓存问题

使用带有参数的路由时需要注意的是&#xff0c;当用户从 /users/johnny 导航到 /users/jolyne 时&#xff0c;相同的组件实例将被重复使用。因为两个路由都渲染同个组件&#xff0c;比起销毁再创建&#xff0c;复用则显得更加高效。不过&#xff0c;这也意味着组件的生命周期钩…

500万PV的网站需要多少台服务器?

1. 衡量业务量的指标 衡量业务量的指标项有很多&#xff0c;比如&#xff0c;常见Web类应用中的PV、UV、IP。而比较贴近业务的指标项就是大家通常所说的业务用户数。但这个用户数比较笼统&#xff0c;其实和真实访问量有比较大的差距&#xff0c;所以为了更贴近实际业务量及压力…

什么是提示工程?

原文链接&#xff1a;芝士AI吃鱼 理解大规模人工智能模型为何如此行事是一门艺术。即使是最有成就的技术专家也会对大型语言模型 (LLM) 的意想不到的能力感到困惑&#xff0c;大型语言模型是ChatGPT等人工智能聊天机器人的基本构建模块。 因此&#xff0c;提示工程成为生成式 …

特征选择算法 | Matlab 基于最大互信息系数特征选择算法(MIC)的分类数据特征选择

文章目录 效果一览文章概述部分源码参考资料效果一览 文章概述 特征选择算法 | Matlab 基于最大互信息系数特征选择算法(MIC)的分类数据特征选择 部分源码 %--------------------

python 常用数据结构-列表

list 列表 列表定义与使用列表常用方法列表嵌套列表推导式 列表定义 列表是有序的可变元素的集合&#xff0c;使用中括号[]包围&#xff0c;元素之间用逗号分隔 列表是动态的&#xff0c;可以随时扩展和收缩 列表是异构的&#xff0c;可以同时存放不同类型的对象 列表中允…

阶乘后的零(力扣)数学 JAVA

给定一个整数 n &#xff0c;返回 n! 结果中尾随零的数量。 提示 n! n * (n - 1) * (n - 2) * … * 3 * 2 * 1 示例 1&#xff1a; 输入&#xff1a;n 3 输出&#xff1a;0 解释&#xff1a;3! 6 &#xff0c;不含尾随 0 示例 2&#xff1a; 输入&#xff1a;n 5 输出&…

WSL2 及 docker开发环境搭建

WSL2 及 docker开发环境搭建 1.使能WSL 控制面板->程序->程序和功能->启动或关闭Windows功能->勾选红框中选项->确认后重启电脑 &#xfeff; 2.下载Linux Kernel Update安装包 下载地址如下&#xff0c; 附件已将下载的安装包作为附件形式上传&#xff0c;…

ITIL 4服务连续性管理实践

一、目的和描述 关键信息 服务连续性管理实践的目的是确保灾难发生时&#xff0c;服务的可用性和性能能够保持在足够的水平。本实践提供了一个框架机制&#xff0c;利用产生有效响应的能力来构建组织的弹性&#xff0c;以保障关键利益相关者的利益&#xff0c;还有组织的声誉…

element 封装dialog弹窗组件鼠标移动到弹窗出现title

问题&#xff1a; element 封装dialog弹窗组件鼠标移动到弹窗出现title 封装的组件 <template><el-dialog title"111"v-bind"$attrs" v-on"$listeners" :visible.sync"show" ></el-dialog> </template><s…

02-webpack的热更新是如何做的,以及原理

一、是什么 HMR 可以理解为模块热替换&#xff0c;指在应用程序运行过程中&#xff0c;替换、添加、删除模块&#xff0c;而无需重新刷新整个应用. 如&#xff0c;我们在应用运行过程中修改了某个模块&#xff0c;通过自动刷新会导致整个应用的整体刷新&#xff0c;那页面中的…

pygame伪3d 实现地面效果

教程来自What is Mode 7? Let’s code it! 油管镜像 import cv2 import pygame import sys from pygame import gfxdraw import numpy as np(width, height) (800, 600) pygame.init() screen pygame.display.set_mode((width, height)) image pygame.image.load("11…