🔥«Настоящий бой» vue3 бесконечная прокрутка

Vue.js
🔥«Настоящий бой» vue3 бесконечная прокрутка

Серия "Актуальный бой" - это рубрика, где Блю ведет вас к созданию вещей. В программировании нет ничего сложного. Просто нужно делать больше. Как только вы сделаете вещи, вы овладеете знаниями. Поэтому в этой серии я надеюсь Вы сделаете больше, если не знаете, как это сделать.Лайк, избранное, комментарий, ретвит

«Учимся войне на войне»

Vue очень часто используется, и Vue3 также много обсуждается, поэтому на этот раз Blue предложит вам использовать Vue3 в качестве приложения с «бесконечной выпадающей загрузкой» (я не очень хорошо умею называть имена, любой с хорошим именем можете внести свой вклад 😂), давайте сначала посмотрим на эффект

0

Проще говоря, когда мы прокручиваем страницу вниз и прокручиваем до конца, будет продолжать загружаться больше данных, чтобы пользователь мог продолжать прокручивать вниз — для достижения эффекта бесконечного выпадающего списка, тогда, если мы хотим чтобы сделать это, нам нужно рассмотреть несколько вещей:

  1. Некоторые основы: построение проекта, базовая компоновка
  2. Как загрузить данные
  3. Как следить за выпадающим, как судить об окончании
  4. Как делятся компоненты и кто отвечает за их функции

Первый шаг, создать проект

Так как мы собираемся что-то делать, мы должны сначала иметь проект и начать его создавать

Создать проект

Если у вас еще нет vuecli, вы можете сначала установить его (вы также можете обновить его, последняя версия cli поддерживает vue3):

npm install -g @vue/cli  #如果嫌慢,可以配一下cnpm的淘宝源或者yarn,快很多

1

Затем мы напрямую используемvueКоманда делает остальную часть создания

vue create endless-scroll  #endless-scroll是项目的名称,你可以随便取自己喜欢的

Далее он позволит вам выбрать тип проекта, который мы используем здесьvue3

2

«Ручной выбор» ниже имеет много доступных функций, мы пока не используем его здесь, если вам интересно, вы можете посмотреть его сами, различные тесты и так далее.

Далее долгое ожидание, можно заварить чай или взять кружок, время около 10 минут

3

Чай кончился, кончился, а потом можно попробовать начать

cd endless-scroll  #跟着你自己的项目名走
npm run serve      #它里面有三种常用启动方式,serve是开发期调试用的

4

Таким образом, даже если нет проблем, мы можем получить доступ к нашему проекту через адрес в красном поле (обычно это локальный хост для этой машины), если все пойдет хорошо, откройтеhttp://localhost:8080Вы увидите следующее содержимое по умолчанию

5

Итак, наш путь разработки официально начался

Структура проекта

Прежде всего, сначала сделаем мелочь, познакомиться с проектом, который мы только что создали, удалите то, что следует удалить, и упаковать слегка

6

Просто скажи всем

.git             #git本地库,版本控制用的,我们这儿是个测试项目可以删
node_modules     #所有的依赖模块,以后装的第三方模块也会放在这儿,极其脆弱,得供着
public           #静态公共文件(例如index.html、图标啥的)
src              #项目的核心,你写的代码都在这儿
.gitignore       #上传git的过滤器,一般本地IDE也会来读,留着
babel.config.js  #babel配置,留着
package.json     #项目配置文件——依赖模块、启动命令、项目配置啥的
README.md        #说明文件,可以看看,看完可以删掉
yarn.lock        #模块版本锁,留不留都行(删了还会自动创建),锁定版本用的

сделать зачистку

Я его немного удалил, теперь он выглядит так

7

Не только каталог, но и код.Сначала удалите компонент.Приносимый им HelloWorld бесполезен, просто удалите его напрямую.

8

Наконец поместите нашApp.vueКорневой компонент, но и очистить, оставитьtemplateдостаточно

перед очисткой

9

после очистки

10

Пока что подготовительная работа окончена, давай, приступай к делу

Второй шаг, получить макет

Итак, с чего мы начнем, давайте сначала построим большую рамку

11

Просто начните писать

<template>
  <div class="container">
    <div class="left">内容区</div>
    <div class="right">列表</div>
  </div>
</template>

<style>
/* 把样式重置一下,这里没仔细写,不过重置样式大家都会的,就不浪费时间了 */
* {
  margin: 0;
  padding: 0;
  list-style: none;
}
body {
  background: #389acc;
}
</style>

<style scoped>
/* App自己的样式 */
.container {
  width: 1080px;
  margin: 50px auto;
  display: flex;
}

.left {
  flex: 1;
  background: #fff;
  margin-right: 10px;
  text-align: center;
  line-height: 200px;
  color: #ccc;
  font-size: 26px;
}
.right {
  width: 350px;
  background: #fff;
}
</style>

Здесь мы сделали два div'а, левый имеет автоматический размер, а правый фиксированный, и результат выглядит так:

12

Создать компонент списка

Большая рамка настроена, далее давайте настроим некоторые детали, потому что нам нужно сделать список справа, так что отдельно вытащите компонент, который нам удобно кидать

13

Далее попробуем посмотреть, получится ли сначала выйти, если нет проблем, то продолжаем писать, начинаем сApp.vueввести его в

14

Здесь мы делаем несколько вещей:

  • Укажите язык разработки (lang="ts"): поддержка vue3 для ts относительно полная (по сравнению с v2), поэтому использовать ее естественно, сильные типы такие ароматные
  • определить компонент (defineComponent): помогите нам определить компоненты - в основном различные типы в ts, очень удобно

Результат выглядит так

15

Затем нам нужно представить компонент List, который мы только что написали (хотя это ничего не значит), три вещи:

  • importЗнакомство с компонентом «Список»: это просто, мы должны сначала представить все, что мы используем.
  • регистрListКомпонент: может использоваться в компонентахcomponentsпрописать локальные компоненты, а потом вtemplateиспользуется в
  • добавить в шаблон

16

В это время мы можем видеть на страницеListкомпоненты

17

Базовая компоновка сделана, приступаем к написанию List

Написать список компонентов

Прежде всего, вне зависимости от функции, список должен иметь вид, поэтому давайте добавим немного html и стилей:

<!-- List.vue的布局 -->
<template>
  <div>
    <!-- 我们搞两条数据示意一下 -->
    <div class="item">
      <h3 class="header">这是一个标题,可能有点长,是的,特别特别长的那种</h3>
      <p class="content">
        这是一些文本的内容啊内容,这是一些文本的内容啊内容,这是一些文本的内容啊内容,这是一些文本的内容啊内容
      </p>
    </div>
    <div class="item">
      <h3 class="header">这是一个标题,可能有点长,是的,特别特别长的那种</h3>
      <p class="content">
        这是一些文本的内容啊内容,这是一些文本的内容啊内容,这是一些文本的内容啊内容,这是一些文本的内容啊内容
      </p>
    </div>
  </div>
</template>

На данный момент никакие стили не добавлены, поэтому наши вещи выглядят так:

18

Мама, это действительно некрасиво, неважно, давай украсим его.

<style scoped>  /* scoped-局部样式 */
.item {
  box-sizing: border-box;
  width: 350px;
  height: 180px;
  padding: 20px 20px;
  background: #fff;
  border: 1px solid #ccc;
  margin-bottom: -1px; /* 上下边框合并一下 */
}
.header {
  font-size: 22px;
  margin-bottom: 20px;

  /* 文本过长自动截断 */
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.content {
  color: #999;
  font-size: 14px;
  line-height: 26px;
  height: 78px;

  /* 多行文本截断 */
  overflow: hidden;
  text-overflow: ellipsis;
  -webkit-line-clamp: 3;
}
</style>

Посмотрим, стало ли лучше:

19

Мы не смеем сказать, насколько это удивительно, по крайней мере, мы можем встретить людей, верно?

Третий шаг, добавьте данные

На данный момент что-то есть, но оно все еще мертво, и записать его напрямую точно нельзя.Нам нужны данные, начнем с самого простого

Первое издание, статические данные

List.vue, добавляем данные

<script lang="ts">
import { defineComponent } from "vue";

export default defineComponent({
  setup() {
    //在这里我们先做一个静态数据,调好了再读服务器的
    const datas = [
      {
        ID: 1,
        title: "深夜突发!多个航班无法降落,疑因有不明飞行器活动",
        content:
          "记者从民航爱好者常使用的一款飞机轨迹查询软件看到,4日晚的确有多趟航班在抵达杭州萧山国际机场前在空中进行了盘旋等待,或备降到了周边的宁波栎社国际机场。",
      },
      {
        ID: 2,
        title:
          "湖州长兴有家公司破产资产2亿元起拍,背后有一个“最快跌落的中国首富”的身影",
        content:
          "是的,“汉能”,“汉能薄膜”在2013年和2014年间曾经是港股里最看不懂的“妖股”,而它的创始人李河君在2015年3月以1600亿的身家荣登中国首富,不过仅仅3个月,其身家就暴跌1000亿,被称为最快跌落的首富。",
      },
    ];

    return {
      datas, //返回出去,以便在template里使用
    };
  },
});
</script>

а потом ещеList.vue, мы добавляем цикл и выход

<template>
  <div>
    <div class="item">
      <h3 class="header">这是一个标题,可能有点长,是的,特别特别长的那种</h3>
      <p class="content">
        这是一些文本的内容啊内容,这是一些文本的内容啊内容,这是一些文本的内容啊内容,这是一些文本的内容啊内容
      </p>
    </div>
  </div>
</template>

<!-- 改成 -->

<template>
  <div>
    <div class="item" v-for="item in datas" :key="item.ID">
      <h3 class="header">{{ item.title }}</h3>
      <p class="content">{{ item.content }}</p>
    </div>
  </div>
</template>

посмотри, как это работает

20

Очень хорошо, не должно быть недостатка в одном, а затем мы должны заставить данные двигаться

Как использовать динамические данные

Прежде всего, если вы хотите прочитать данные, вы должныajax, на этот раз наше внимание неajaxсамо собой, так просто, просто сделай этоaxiosиспользовать

Установите сначала,ctrl+cОстановите проект и установите его (обратите внимание, что смешивание менеджеров пакетов чревато проблемами)

npm i axios -S
#或
cnpm i axios -S
#或
yarn add axios

законченный

21

Далее нам нужен файл для загрузки данных (для простоты этот урок не предполагает разработку сервера, если вам интересно, то это будет рассмотрено отдельно)

22

Кстати,publicФайлы в файле будут скопированы в результат компиляции в том виде, в котором они есть, поэтому этот набор вещей по-прежнему будет доступен после подключения к сети.

читать данные

<script lang="ts">
import { defineComponent } from "vue";
//引进来准备读数据
import axios from "axios";

export default defineComponent({
  setup() {
    //读的就是我们刚才的那个文件
    axios("/datas.json").then((res) => {
      console.log(res.data); //读过来的东西打出来瞅瞅
    });

    return {
      datas: [], //暂时搞个空的
    };
  },
});
</script>

Запустите его, чтобы увидеть эффект (не забудьте запустить службуnpm run serve)

23

Хорошо, есть все

обновить данные

Тогда нам нужно столкнуться с другой проблемой - как обновить данные

В Vue3 у нас есть несколько способов создания «отзывчивых данных».Так называемая отзывчивость означает, что после изменения данных представление автоматически перерисовывается,чтобы реагировать на ваши изменения в данных, здесь мы выбираем ref, что удобно для одиночных данных (таких как здесь массив)

Как использовать реф.

Давайте используем простой пример, чтобы увидеть, как использовать ref

<template>
  <div>
    <div>
      姓名:{{ name }}
      <button @click="name += 'a'">修改</button>
    </div>
    <div>
      年龄:{{ age }}
      <button @click="age++">修改</button>
    </div>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref } from "vue";

export default defineComponent({
  setup() {
    //准备两个数据作为对比
    let name = "blue"; //普通数据(非响应)
    let age = ref(18); //响应数据

    return { name, age };
  },
});
</script>

Это работает следующим образом:

24

Неважно, уродливо это или нет, что происходит, когда мы нажимаем?

25

Выше мы можем видеть две странные вещи:

  • Исправлятьname(обычная переменная) нет ответа
  • Исправлятьage(данные ответа) отвечает, и дажеnameИзменения вступают в силу вместе

На самом деле, это очень просто, по одному объяснению:

  • Зачем исправлятьnameнет ответа?

    По соображениям производительности vue не отслеживает все изменения, только определенные данные (ref, reactive и т. д.) запускают рендеринг.

  • ЗачемageчетноеnameВступили в силу вместе?

    Потому что при повторном рендеринге vue проверит все изменения данных (включая данные об отсутствии ответов), а также будут найдены естественные изменения имени.

Второе издание, серверные данные

Пришло время собрать то, что у нас есть выше вместе

<template>
  <div>
    <div class="item" v-for="item in datas" :key="item.ID">
      <h3 class="header">{{ item.title }}</h3>
      <p class="content">{{ item.content }}</p>
    </div>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref } from "vue";
import axios from "axios";

export default defineComponent({
  setup() {
    const datas = ref([]); //能响应的数组(人话版:改了会自动更新HTML的数组)

    axios("/datas.json").then((res) => {
      //1-ref需要.value,它本质上是一个reactive({value})
      //2-把老数据+新数据合并为一个新的数组
      datas.value = [...datas.value, ...res.data];
    });

    return {
      datas, //输出到页面中使用
    };
  },
});
</script>

Посмотрите, сможете ли вы его вытащить:

26

Данные в принципе сделаны, но есть проблема, не обновится автоматом при вытягивании на дно (ерунда, еще не делал), что делать, дальше работать

Шаг 4. Следите за прокруткой страницы

На самом деле мы хотим сделать автозагрузку довольно простой, две вещи:

  • прокрутка монитора
  • Я обнаружил, что он катится до конца, поэтому я добавлю несколько новых

Мы следуем этому шагу

добавить слушателя

Во-первых, мы знаем, что большинство событий в vue@xxxдобавить, но событие прокруткиwindowДа, мы не можемwindow.@scrollТакой ублюдок? На самом деле это очень просто, просто добавьте его вручную

import { onMounted, onUnmounted } from "vue";

export default defineComponent({
  setup() {
    //滚动事件处理函数
    function scrollHandle() {
      console.log('滚了');
    }

    onMounted(() => {
      //组件挂载时,添加scroll监听
      window.addEventListener("scroll", scrollHandle, false);
    });
    onUnmounted(() => {
      //组件卸载时,停止监听
      window.removeEventListener("scroll", scrollHandle, false);
    });
  }
});

Как эффект?

27

Вроде ничего страшного, напоминает, что страница прокручивается, поэтому возникает второй вопрос — откуда мы знаем, что почти конец

Это почти конец?

Какого черта, это как скороговорка 😂

Проще говоря, нам нужно определить, «собирается ли пользователь прокрутить страницу вниз», так как же нам это сделать? Давайте сначала посмотрим на картинку с синим

28

Рисунок грубый (вы называете это грубым??), но смысл очень ясен: страница очень высокая, видимая область скользит вверх и вниз (при прокрутке) по странице, мы можемРассчитать расстояние между видимой областью и дном, если оно меньше определенного порога, то можно считать "скоро"

Нет проблем, как рассчитать это "расстояние"? Давайте посмотрим на картинку.

29

На странице есть несколько значений, которые можно использовать

  • #1-scrollTop: расстояние прокрутки

  • #2-scrollHeight: общая высота содержимого страницы.

  • #3-clientHeight: Высота видимой области

//距离=总高-滚动距离-可视区高
let distance = scrollHeight - scrollTop - clientHeight;

Попробуйте в коде

30

увидеть результаты

31

В результате-100вокруг значения, почему а? На самом деле все очень просто, наша маржа вызвана

32

Поскольку поле не считается высотой самого объекта, поэтому содержимое меньше, чем кажется на самом деле, что мне делать? изменять

33

попробуй

34

На этот раз намного лучше, поэтому нам просто нужно судить, что это значение в определенной степени мало (например, 200, в зависимости от ваших потребностей).

35

давай попробуем

36

Но есть проблема, появлялась много раз, что делать?

предотвратить перезагрузку

Проще говоря, мы можем «заблокировать» его после запуска загрузки и не позволять ему запускаться снова, пока загрузка не будет завершена.

//加载逻辑

//修改前
axios("/datas.json").then((res) => {
  datas.value = [...datas.value, ...res.data];
});


//修改后
let readyForLoad = true; //默认允许加载一次

if (readyForLoad) {
  //需要加载才进来,防止重复
  readyForLoad = false; //进来了就"锁上"

  axios("/datas.json").then((res) => {
    datas.value = [...datas.value, ...res.data];
    readyForLoad = true; //加载完了才"开锁",允许再次触发
  });
}

Поскольку нам нужно часто его загружать, мы инкапсулируем его как функцию, которая становится такой

let readyForLoad = true; //默认允许加载一次

function loadMore() {
  if (readyForLoad) {
    //需要加载才进来,防止重复
    readyForLoad = false; //进来了就"锁上"

    axios("/datas.json").then((res) => {
      datas.value = [...datas.value, ...res.data];
      readyForLoad = true; //加载完了才"开锁",允许再次触发
    });
  }
}

Тогда наша установка будет выглядеть так:

setup() {
  //滚动事件处理函数
  function scrollHandle() {
    ...省略部分代码...

    if (distance <= 200) {
      console.log("快到底了!");
      loadMore(); //多次触发没关系,加载完之前只能有一个实际执行
    }
  }

  onMounted(...);
  onUnmounted(...);

  const datas = ref([]);

  let readyForLoad = true; //默认允许加载一次

  loadMore(); //初始加载一次
  function loadMore() {
    if (readyForLoad) {
      //需要加载才进来,防止重复
      readyForLoad = false; //进来了就"锁上"

      axios("/datas.json").then((res) => {
        datas.value = [...datas.value, ...res.data];
        readyForLoad = true; //加载完了才"开锁",允许再次触发
      });
    }
  }

  return {
    datas, //输出到页面中使用
  };
},

Попробуйте эффект, это в принципе не проблема (формат GIF слишком жалкий, у него на самом деле 10М при нажатии, а вебм всего несколько сотен К, так что будьте осторожны)

37

Суммировать

Пришло время разобраться в том, что сказала Блю, так что сначала

15-三连

  • использовать@vue/cliСоздавайте проекты быстро и легко
  • Полное чтение данных с помощью axios
  • существуетmountа такжеunmountВ, слушайте событие прокрутки страницы прокрутки
  • Вычислить расстояние от низа страницы (расстояние = общая высота - расстояние прокрутки - высота видимой области), если оно меньше определенного порога (например, 200), считается нижним
  • Предотвращение повторного запуска действий загрузки с помощью «дросселирования»
  • После загрузки[...oldData, ...newData]Объедините все данные, чтобы сформировать новый массив, используя
  • пройти черезrefПросмотр обновлений в реальном времени

Есть ошибка? Хотите добавить?

Спасибо за просмотр этого урока. Если у вас есть какие-либо вопросы или вы хотите связаться со мной, пожалуйста, оставьте сообщение напрямую. Если вы обнаружите какую-либо неуместность в статье, пожалуйста, укажите на нее, заранее спасибо