虚拟列表【vue】等高虚拟列表/非等高虚拟列表

news2024/11/25 10:38:13

文章目录

  • 1、等高虚拟列表
  • 2、非等高虚拟列表

1、等高虚拟列表

参考文章1
参考文章2
在这里插入图片描述
在这里插入图片描述

<!-- eslint-disable vue/multi-word-component-names -->
<template>
  <div
    class="waterfall-wrapper"
    ref="waterfallWrapperRef"
    @scroll="handleScroll"
  >
    <div :style="scrollStyle">
      <div v-for="item in computedData.items" :key="item.userId">
        {{ item.username }}
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { computed, onMounted, ref } from "vue";
import { sameHighData } from "../data/index";

const itemHeight = 18; //每个item的高度
let itemCount = ref(500); // 设置总共长度为500
const scrollTop = ref(0); // 滚动的高度
const wrapperHeight = ref(0); // 滚动容器的高度
const waterfallWrapperRef = ref();

const list = ref([...sameHighData]);

const handleScroll = (e: any) => {
  // 在content内容中距离content盒子的上部分的滚动距离
  scrollTop.value = e.target.scrollTop;

  // 判断是否接近底部,如果是则加载更多数据
  if (e.target.scrollHeight - e.target.clientHeight - scrollTop.value < 100) {
    getData();
  }
};

const getData = () => {
  list.value = list.value.concat(list.value);
  itemCount.value = list.value.length;
  // scrollBarRef.value.style.height = itemCount.value * itemHeight + "px";
  console.log("发送网络请求", list.value.length);
};

const scrollStyle = computed(() => ({
  height: `${
    itemHeight * (list.value.length - computedData.value.startIndex)
  }px`,
  transform: `translateY(${itemHeight * computedData.value.startIndex}px)`,
}));

// 这里不使用方法的原因是以来了scrollTop 响应式数据来变更 所以使用computed更方便
const computedData = computed(() => {
  // 可视区域的索引
  const startIndex = Math.floor(scrollTop.value / itemHeight);

  // 设置上缓冲区为2 上缓冲区的索引
  const finialStartIndex = Math.max(0, startIndex - 2);

  // 可视区显示的数量 这里设置了item的高度为50
  const numVisible = Math.floor(wrapperHeight.value / itemHeight);

  // 设置下缓冲区的结束索引 上缓冲区也设置为2
  const endIndex = Math.min(itemCount.value, startIndex + numVisible + 2);

  // 将上缓冲区到下缓冲区的数据返回
  const items: any = [];

  // contentWrapRef.value!.style.height = finialStartIndex * itemHeight + "px";
  for (let i = finialStartIndex; i < endIndex; i++) {
    const item = {
      ...list.value[i],
      top: itemHeight * i + "px",
      transform: `translateY(${itemHeight * i + "px"})`,
    };
    items.push(item);
  }
  console.log(startIndex);

  return { items, startIndex };
});

onMounted(() => {
  // 在页面一挂载就获取滚动区域的高度
  if (waterfallWrapperRef.value) {
    wrapperHeight.value = waterfallWrapperRef.value?.clientHeight;
  }
});
</script>

<style scoped>
.waterfall-wrapper {
  height: 500px;
  overflow: hidden;
  overflow-y: scroll;
  position: relative;
  .scroll-bar {
    width: 100%;
    position: absolute;
  }
}
</style>



2、非等高虚拟列表

参考链接

非等高序列列表的实现逻辑并没有通过相对定位的方式,而是通过设置translateY来实现滚动
思路:

  • 难点1:每个元素的高度不一样,没办法直接计算出容器的高度
    • 容器高度的作用是足够大 可以让用户进行滚动,所以我们可以直接假设每一个元素的高度 需要保证预测的 item 高度尽量比真实的每一项 item 的高度要小或者接近所有 item 高度的平均值
  • 难点2: 每个元素高度不一样,top值不能通过count * index算出来
  • 难点3: 每个元素高度不一样,不能通过scrollTop * size计算出已经滚动的元素个数,很难获取可视区的起始索引
    • 难度2和难度3的解决方案是先把数据渲染到页面上 之后再通过 获取正式dom的高度 来计算
      在这里插入图片描述
<template>
  <div
    class="waterfall-wrapper"
    ref="waterfallWrapperRef"
    @scroll="handleScroll"
  >
    <!-- 内容显示的区域 -->
    <div ref="listRef" :style="scrollStyle">
      <div v-for="(item, index) in computedData" :key="index">
        {{ index }} {{ item.sentence }}
      </div>
    </div>
  </div>
</template>
  • 1、先确定预测的item高度和数据
  • 2、获取滚动区域的高度且计算容器最大容量
onMounted(async () => {
  // 在页面一挂载就获取滚动区域的高度
  if (waterfallWrapperRef.value) {
    wrapperHeight.value = waterfallWrapperRef.value?.clientHeight;
    // 预测容器最多显示多少个元素
    maxCount.value = Math.ceil(wrapperHeight.value / estimatedHeight) + 1;
    await nextTick();

    // 先把数据显示再页面上
    initData();
    setItemHeight();
  }
});
  • 3、引用一个新的变量来存放实际的dom和预测结果和实际的偏差高度
interface IPosInfo {
  // 当前pos对应的元素索引
  index: number;
  // 元素顶部所处位置
  top: number;
  // 元素底部所处位置
  bottom: number;
  // 元素高度
  height: number;
  // 自身对比高度差:判断是否需要更新
  dHeight: number;
}

在这里插入图片描述

  • 4、初始化预测的dom

参考逻辑

function initPosition() {
  const pos: IPosInfo[] = [];
	//  将获取到的数据全部进行初始化
  for(let i = 0; i < props.dataSource.length; i++) {
    pos.push({
      index: item.id,
      height: props.estimatedHeight, // 使用预测高度先填充 positions
      top: item.id * props.estimatedHeight,
      bottom: (item.id + 1) * props.estimatedHeight,
      dHeight: 0,
    })
  }
  positions.value = pos;
}

实际代码

// 初始化数据
const initData = () => {
  const items: IPosInfo[] = [];
  // 获取需要更新位置的dom长度 即新增加的数据
  const len = list.value.length - preLen.value;
  // 已经处理好位置的长度
  const currentLen = initList.value.length;

  // 可以用三元运算符是因为初始化的时候initList的数值是空的
  const preTop = initList.value[currentLen - 1]
    ? initList.value[currentLen - 1].top
    : 0;
  const preBottom = initList.value[currentLen - 1]
    ? initList.value[currentLen - 1].bottom
    : 0;

  for (let i = 0; i < len; i++) {
    const currentIndex = preLen.value + i;
    // 获取当前要初始化的item
    const item: DiffHigh = list.value[currentIndex];
    items.push({
      id: item.id,
      height: estimatedHeight, // 刚开始不知道他高度 所以暂时先设置为预置的高度
      top: preTop
        ? preTop + i * estimatedHeight
        : currentIndex * estimatedHeight,
      // 元素底部所处位置 这里获取的是底部的位置 所以需要+1
      bottom: preBottom
        ? preBottom + (i + 1) * estimatedHeight
        : (currentIndex + 1) * estimatedHeight,
      // 高度差:判断是否需要更新
      dHeight: 0,
    });
  }
  initList.value = [...initList.value, ...items];
  preLen.value = list.value.length;
};
  • 5、更新实际的数据,拿到实际的数据高度
    • 通过 ref 获取到 list DOM,进而获取到它的 children
    • 遍历 children,针对于每一个 DOM item 获取其高度信息,通过其 id 属性找到 positions 中对应的 item,更新该 item 信息
// 数据渲染之后更新item的真实高度
const setItemHeight = () => {
  const nodes = listRef.value.children;

  if (!nodes.length) return;

  //  Array.from(nodes): 使用 Array.from() 方法,其中 nodes 是一个类数组对象。
  // [...nodes]: 使用数组解构语法,其中 nodes 是一个可迭代对象,例如 NodeList。
  // 1、遍历节点并获取位置信息
  // 2、更新节点高度和位置信息:
  // 这里只更新了视图上的bottom 没有更新top的数值 而且只更新视图上面显示的,并没有更新整个列表,因为height发生了改变视图以外的数据也发生了变化 需要同步修改
  [...nodes].forEach((node: Element, index: number) => {
    const rect = node.getBoundingClientRect();
    const item = initList.value[startIndex.value + index];
    const dHeight = item?.height - rect?.height;
    if (dHeight) {
      item.height = rect.height;
      item.bottom = item.bottom - dHeight;
      item.dHeight = dHeight;
    }
  });
  // 3、将当前 item 的 dHeight 进行累计,之后再重置为 0 (更新后就不再存在高度差了)
  const len = initList.value.length;
  let startHeight = initList.value[startIndex.value]?.dHeight;
  if (startHeight) {
    initList.value[startIndex.value].dHeight = 0;
  }
	// 从渲染视图的第二个开始处理
	// 实际上第一项的 top 值为 0,bottom 值在上轮也更新过了,所以遍历的时候我们从第二项开始
  for (let i = startIndex.value + 1; i < len; i++) {
    const item = initList.value[i];
    item.top = initList.value[i - 1].bottom;
    item.bottom = item.bottom - startHeight;
    if (item.dHeight !== 0) {
      startHeight += item.dHeight;
      item.dHeight = 0;
    }
  }
  // 设置 list 高度
  listHeight.value = initList.value[len - 1].bottom;
};
  • 6、设置滚动

// 已经滚动的距离
const offsetDis = computed(() =>
  startIndex.value > 0 ? initList.value[startIndex.value - 1]?.bottom : 0
);

const scrollStyle = computed(() => ({
  height: `${listHeight.value - offsetDis.value}px`,
  transform: `translateY(${offsetDis.value}px)`,
}));

在这里插入图片描述

  • 7、滚动事件和 startIndex 计算
    • 如何判断一个 item 滚出视图?这个问题在最早就提到过了,只需要看它的 bottom <= scrollTop
    • 现在就好办了,我们可以遍历 positions 数组找到第一个 item.bottom >= scrollTop 的 item,它就是 startIndex 所对应的 item,那 startIndex 就拿到了
    • 这里再补充一个细节,在 initList 数组中 item.bottom 一定是递增的,而我们现在想要做的是查找操作,有序递增 + 查找 = 二分查找
// 二分查找 找到可视区域的索引startIndex
const getStartIndex = (list: any, value: number) => {
  let left = 0,
    right = list.length - 1,
    templateIndex = -1;

  while (left < right) {
    const mid = Math.floor((left + right) / 2);
    const midValue = list[mid].bottom;
    // 如果找到了就用找到的索引 + 1 作为 startIndex,因为找到的 item 是它的 bottom 与 scrollTop 相等,即该 item 已经滚出去了
    if (midValue === value) return mid + 1;
    else if (midValue < value) left = mid + 1;
    else if (midValue > value) {
      if (templateIndex == -1 || templateIndex > mid) {
        templateIndex = mid;
      }
      right = mid;
    }
  }
  return templateIndex;
};
  • 8、每次 startIndex 改变,不仅会改变 renderList 的计算,我们还需要重新计算 item 信息
watch(
  () => startIndex.value,
  () => {
    setItemHeight();
  }
);
<!-- eslint-disable vue/multi-word-component-names -->
<template>
  <div
    class="waterfall-wrapper"
    ref="waterfallWrapperRef"
    @scroll="handleScroll"
  >
    <!-- 内容显示的区域 -->
    <div ref="listRef" :style="scrollStyle">
      <div v-for="(item, index) in computedData" :key="index">
        {{ index }} {{ item.sentence }}
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { computed, nextTick, onMounted, ref, watch } from "vue";
import { sameDiffData } from "../data/index";

interface IPosInfo {
  // 当前pos对应的元素索引
  id: number;
  // 元素顶部所处位置
  top: number;
  // 元素底部所处位置
  bottom: number;
  // 元素高度
  height: number;
  // 高度差:判断是否需要更新
  dHeight: number;
}

interface DiffHigh {
  id: number;
  sentence: string;
}

const waterfallWrapperRef = ref();
const listRef = ref();

const estimatedHeight = 60; // 设置预估的高度为100
let itemCount = ref(500); // 设置总共长度为500
const scrollTop = ref(0); // 滚动的高度
const wrapperHeight = ref(0); // 滚动容器的高度
const preLen = ref(0); // 因为每次处理数值的时候都需要处理全部数据 这个用来记录已经处理的数据

const list = ref<DiffHigh[]>([...sameDiffData]);
const initList = ref<IPosInfo[]>([]); // 用来存放已经处理好位置的数据
const listHeight = ref(0); // 列表的高度

const startIndex = ref(0);
const maxCount = ref(0);

const endIndex = computed(() =>
  Math.min(list.value.length, startIndex.value + maxCount.value + 2)
);

const computedData = computed(() =>
  list.value.slice(Math.max(0, startIndex.value - 2), endIndex.value)
);

// 已经滚动的距离
const offsetDis = computed(() =>
  startIndex.value > 0 ? initList.value[startIndex.value - 1]?.bottom : 0
);

const scrollStyle = computed(() => ({
  height: `${listHeight.value - offsetDis.value}px`,
  transform: `translateY(${offsetDis.value}px)`,
}));

const handleScroll = (e: any) => {
  scrollTop.value = e.target.scrollTop;

  // 在content内容中距离content盒子的上部分的滚动距离
  // 在开始滚动之后获取startIndex
  startIndex.value = getStartIndex(initList.value, scrollTop.value);
  console.log(startIndex.value, "-0-");
  // 判断是否接近底部,如果是则加载更多数据
  if (e.target.scrollHeight - e.target.clientHeight - scrollTop.value < 100) {
    getMoreData();
  }
};

watch(
  () => startIndex.value,
  () => {
    setItemHeight();
  }
);

const getMoreData = async () => {
  list.value = list.value.concat(list.value);
  await nextTick();
  initData();
  setItemHeight();
};

// 二分查找 找到可视区域的索引startIndex
const getStartIndex = (list: any, value: number) => {
  let left = 0,
    right = list.length - 1,
    templateIndex = -1;

  while (left < right) {
    const mid = Math.floor((left + right) / 2);
    const midValue = list[mid].bottom;
    if (midValue === value) return mid + 1;
    else if (midValue < value) left = mid + 1;
    else if (midValue > value) {
      if (templateIndex == -1 || templateIndex > mid) {
        templateIndex = mid;
      }
      right = mid;
    }
  }
  return templateIndex;
};

// 初始化数据
const initData = () => {
  const items: IPosInfo[] = [];
  // 获取需要更新位置的dom长度
  const len = list.value.length - preLen.value;
  // 已经处理好位置的长度
  const currentLen = initList.value.length;

  // 可以用三元运算符是因为初始化的时候initList的数值是空的
  const preTop = initList.value[currentLen - 1]
    ? initList.value[currentLen - 1].top
    : 0;
  const preBottom = initList.value[currentLen - 1]
    ? initList.value[currentLen - 1].bottom
    : 0;

  for (let i = 0; i < len; i++) {
    const currentIndex = preLen.value + i;
    // 获取当前要初始化的item
    const item: DiffHigh = list.value[currentIndex];
    items.push({
      id: item.id,
      height: estimatedHeight, // 刚开始不知道他高度 所以暂时先设置为预置的高度
      top: preTop
        ? preTop + i * estimatedHeight
        : currentIndex * estimatedHeight,
      // 元素底部所处位置 这里获取的是底部的位置 所以需要+1
      bottom: preBottom
        ? preBottom + (i + 1) * estimatedHeight
        : (currentIndex + 1) * estimatedHeight,
      // 高度差:判断是否需要更新
      dHeight: 0,
    });
  }
  initList.value = [...initList.value, ...items];
  preLen.value = list.value.length;
};

// 数据渲染之后更新item的真实高度
const setItemHeight = () => {
  const nodes = listRef.value.children;

  if (!nodes.length) return;

  //  Array.from(nodes): 使用 Array.from() 方法,其中 nodes 是一个类数组对象。
  // [...nodes]: 使用数组解构语法,其中 nodes 是一个可迭代对象,例如 NodeList。
  // 1、遍历节点并获取位置信息
  // 2、更新节点高度和位置信息:
  [...nodes].forEach((node: Element, index: number) => {
    const rect = node.getBoundingClientRect();
    const item = initList.value[startIndex.value + index];
    const dHeight = item?.height - rect?.height;
    if (dHeight) {
      item.height = rect.height;
      item.bottom = item.bottom - dHeight;
      item.dHeight = dHeight;
    }
  });
  // 3、将当前 item 的 dHeight 进行累计,之后再重置为 0 (更新后就不再存在高度差了)
  const len = initList.value.length;
  let startHeight = initList.value[startIndex.value]?.dHeight;
  if (startHeight) {
    initList.value[startIndex.value].dHeight = 0;
  }

  for (let i = startIndex.value + 1; i < len; i++) {
    const item = initList.value[i];
    item.top = initList.value[i - 1].bottom;
    item.bottom = item.bottom - startHeight;
    if (item.dHeight !== 0) {
      startHeight += item.dHeight;
      item.dHeight = 0;
    }
  }
  // 设置 list 高度
  listHeight.value = initList.value[len - 1].bottom;
};

onMounted(async () => {
  // 在页面一挂载就获取滚动区域的高度
  if (waterfallWrapperRef.value) {
    wrapperHeight.value = waterfallWrapperRef.value?.clientHeight;
    // 预测容器最多显示多少个元素
    maxCount.value = Math.ceil(wrapperHeight.value / estimatedHeight) + 1;
    await nextTick();

    // 先把数据显示再页面上
    initData();
    setItemHeight();
  }
});
</script>

<style scoped>
.waterfall-wrapper {
  height: 300px;
  /* overflow: hidden; */
  overflow-y: scroll;
  /* position: relative; */
  .scroll-bar {
    width: 100%;
  }
}
</style>

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

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

相关文章

Kubernetes部署CNI网络组件

目录 1.概述 K8S的三种网络 VLAN和VXLAN的区别 K8S中Pod网络通信 flannel的三种模式 flannel的UDP模式工作原理 flannel的VXLAN模式工作原理 2.部署flannel 在node01节点上操作 在master01节点上操作 3.部署Calico Calico主要由三个部分组成 calico的IPIP模式工作…

Spring6学习技术|Junit

学习材料 尚硅谷Spring零基础入门到进阶&#xff0c;一套搞定spring6全套视频教程&#xff08;源码级讲解&#xff09; Junit 背景 背景就是每次Test都要重复创建容器&#xff0c;获取对象。就是ApplicationContext和getBean两个语句。通过Spring整合Junit&#xff0c;可以…

集合框架之List集合

目录 ​编辑 一、什么是UML 二、集合框架 三、List集合 1.特点 2.遍历方式 3.删除 4.优化 四、迭代器原理 五、泛型 六、装拆箱 七、ArrayList、LinkedList和Vector的区别 ArrayList和Vector的区别 LinkedList和Vector的区别 一、什么是UML UML&#xff08;Unif…

【《高性能 MySQL》摘录】第 3 章 服务器性能剖析

文章目录 3.1 性能优化简介3.1.1 通过性能剖析进行优化3.1.2 理解性能剖析 3.2 对应用程序进行性能剖析3.3 剖析 MySQL 查询3.3.1 剖析服务器负载捕获 MySQL 的查询到日志文件中分析查询日志 3.3.2 剖析单挑查询使用 SHOW PROFILE &#xff08;现已过时&#xff09;使用SHOW ST…

猫头虎分享已解决Bug || RuntimeError: size mismatch, m1: [32 x 100], m2: [500 x 10]

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

跑步也要飙起来:南卡、韶音、墨觉骨传导耳机大比拼

作为一个热衷于运动同时又不能离开音乐的人&#xff0c;我总是在寻找一款既能让我自由奔跑&#xff0c;又能享受到美妙音乐的耳机。记得买耳机前&#xff0c;朋友都说骨传导耳机就像个小喇叭&#xff0c;漏音厉害&#xff0c;我却不这么认为。对我来说&#xff0c;骨传导耳机不…

游戏平台如何定制开发?

随着科技的飞速发展和互联网的普及&#xff0c;游戏平台已成为人们休闲娱乐的重要选择。为了满足用户多样化的需求&#xff0c;游戏平台的定制开发显得尤为重要。本文将探讨游戏平台定制开发的过程、关键要素以及注意事项&#xff0c;为有志于涉足此领域的开发者提供参考。 一、…

MLflow【部署 01】MLflow官网Quick Start实操(一篇学会部署使用MLflow)

一篇学会部署使用MLflow 1.版本及环境2.官方步骤Step-1 Get MLflowStep-2 Start a Tracking ServerStep 3 - Train a model and prepare metadata for loggingStep 4 - Log the model and its metadata to MLflowStep 5 - Load the model as a Python Function (pyfunc) and us…

【笔试强训错题选择题】Day2.习题(错题)解析

文章目录 前言 错题题目 错题解析 总结 前言 错题题目 1. 错题解析 1. 总结

C#,二叉搜索树(Binary Search Tree)的迭代方法与源代码

1 二叉搜索树 二叉搜索树&#xff08;BST&#xff0c;Binary Search Tree&#xff09;又称二叉查找树或二叉排序树。 一棵二叉搜索树是以二叉树来组织的&#xff0c;可以使用一个链表数据结构来表示&#xff0c;其中每一个结点就是一个对象。 一般地&#xff0c;除了key和位置…

prometheus安装

https://cloud.tencent.com/developer/article/1449258 https://www.cnblogs.com/jason2018524/p/16995927.html https://developer.aliyun.com/article/1141712 prometheus docker安装 https://prometheus.io/docs/prometheus/latest/installation/ docker run --name prometh…

二.西瓜书——线性模型、决策树

第三章 线性模型 1.线性回归 “线性回归”(linear regression)试图学得一个线性模型以尽可能准确地预测实值输出标记. 2.对数几率回归 假设我们认为示例所对应的输出标记是在指数尺度上变化&#xff0c;那就可将输出标记的对数作为线性模型逼近的目标&#xff0c;即 由此&…

unity-firebase-Analytics分析库对接后数据不显示原因,及最终解决方法

自己记录一下unity对接了 FirebaseAnalytics.unitypackage&#xff08;基于 firebase_unity_sdk_10.3.0 版本&#xff09; 库后&#xff0c;数据不显示的原因及最终显示解决方法&#xff1a; 1. 代码问题&#xff08;有可能是代码写的问题&#xff0c;正确的代码如下&#xff…

分布式系统一致性与共识算法

分布式系统的一致性是指从系统外部读取系统内部的数据时&#xff0c;在一定约束条件下相同&#xff0c;即数据&#xff08;元数据&#xff0c;日志数据等等&#xff09;变动在系统内部各节点应该是一致的。 一致性模型分为如下几种&#xff1a; ① 强一致性 所有用户在任意时…

vue源码分析之nextTick源码分析-逐行逐析-错误分析

nextTick的使用背景 在vue项目中&#xff0c;经常会使用到nextTick这个api&#xff0c;一直在猜想其是怎么实现的&#xff0c;今天有幸研读了下&#xff0c;虽然源码又些许问题&#xff0c;但仍值得借鉴 核心源码解析 判断当前环境使用最合适的API并保存函数 promise 判断…

【RL】Actor-Critic Methods

Lecture 10: Actor-Critic Methods The simplest actor-critic (QAC) 回顾 policy 梯度的概念&#xff1a; 1、标量指标 J ( θ ) J(\theta) J(θ)&#xff0c;可以是 v ˉ π \bar{v}_{\pi} vˉπ​ 或 r ˉ π \bar{r}_{\pi} rˉπ​。 2、最大化 J ( θ ) J(\theta)…

计算机服务器中了DevicData勒索病毒怎么办?DevicData勒索病毒解密数据恢复

网络技术的发展与更新为企业提供了极大便利&#xff0c;让越来越多的企业走向了正规化、数字化&#xff0c;因此&#xff0c;企业的数据安全也成为了大家关心的主要话题&#xff0c;但网络是一把双刃剑&#xff0c;即便企业做好了安全防护&#xff0c;依旧会给企业的数据安全带…

Prometheus+Grafana 监控

第1章Prometheus 入门 Prometheus 受启发于 Google 的 Brogmon 监控系统&#xff08;相似的 Kubernetes 是从 Google的 Brog 系统演变而来&#xff09;&#xff0c;从 2012 年开始由前 Google 工程师在 Soundcloud 以开源软件的 形式进行研发&#xff0c;并且于 2015 年早期对…

如何在Linux搭建Inis网站,并发布至公网实现远程访问【内网穿透】

如何在Linux搭建Inis网站&#xff0c;并发布至公网实现远程访问【内网穿透】 前言1. Inis博客网站搭建1.1. Inis博客网站下载和安装1.2 Inis博客网站测试1.3 cpolar的安装和注册 2. 本地网页发布2.1 Cpolar临时数据隧道2.2 Cpolar稳定隧道&#xff08;云端设置&#xff09;2.3.…

论文阅读:How Do Neural Networks See Depth in Single Images?

是由Technische Universiteit Delft(代尔夫特理工大学)发表于ICCV,2019。这篇文章的研究内容很有趣,没有关注如何提升深度网络的性能&#xff0c;而是关注单目深度估计的工作机理。 What they find&#xff1f; 所有的网络都忽略了物体的实际大小&#xff0c;而关注他们的垂直…