“Actual combat” series is Blue with everyone to do things, programs that it’s not a difficult thing, just begin, more things to the knowledge of master, so in this series, I hope you did not ask a lot (there is a problem please leave a message), so as to grasp the knowledge of what they have learned, welcome thumb up, collection, review, forwarding

“Learning war in war.”

Vue is very popular and Vue3 is discussed a lot, so this time Blue will take you to Vue3 to make an “infinite drop-down load” application (I am not good at naming, anyone who has a good name welcome to contribute 😂), let’s have a look at the effect

In a nutshell, when we scroll down to the bottom of the page, we load more data so that the user can scroll down to the bottom of the page — an infinite drop-down effect. So, to do this, we need to consider a few things:

  1. Some basic: project construction, basic layout
  2. How to load data
  3. How to listen to the pull down, how to judge the end
  4. How are components divided and who is responsible for functions

Step 1: Create the project

Since we’re going to build something, we have to have a project and start building it

Create a project

If you don’t already have a CLI for Vue, you can install it first (you can upgrade if you do, the latest VERSION of cli only supports VUe3) :

npm install -g @vue/cli  # If it is too slow, you can configure CNPM taobao source or YARN, which is much faster
Copy the code

We then do the rest of the creation directly with the vue command

vue create endless-scroll  Endless Scroll is the name of the project, you can take whatever you like
Copy the code

Next it lets you choose the type of project, in this case vue3

The following “manual selection” has a lot of features available, we are not here for the time being, interested in you can have a look, a variety of testing what

Then there’s the long wait, which can take about 10 minutes to make a cup of tea or go for a spin

Tea’s done, it’s done, so we can try to start it

cd endless-scroll  Follow your own project name
npm run serve      There are three common startup methods. Serve is used for debugging during development
Copy the code

If all goes well, open http://localhost:8080 and you will see the following default content

So, our development journey has officially begun

The project structure

First, let’s take a look at the small things we’ve just created

Just to give you an idea

.git             Git local library, version control use, we here is a test project can be deleted
node_modules     # All dependency modules and future third party modules will also be placed here, extremely fragile, must be supplied
public           Static public files (e.g. index.html, ICONS etc.)
src              # The heart of the project, the code you wrote is right here
.gitignore       Git filters will be read by your IDE
babel.config.js  # Babel configuration, leave it
package.json     Project configuration files - dependencies on modules, startup commands, project configuration, etc
README.md        # file description, you can see, after reading can delete
yarn.lock        # module version lock, whether to leave or not (deleted will be automatically created), lock the version used
Copy the code

Do a sweep

I’ve deleted it a little bit, and now it looks like this

First, delete the component. The HelloWorld attached to it is useless. Just delete it

Finally, clean up our app. vue root component as well, leaving a template is enough

Clean up before

Clean up after

So far, the preparation is complete. Let’s go. Let’s get started

Step 2: Figure out the layout

So where do we start? Let’s get the big picture

Just start writing

<template> <div class="container"> <div class="left"> Content area </div> <div class="right"> list </div> </div> </template> <style> /* */ * {margin: 0; margin: 0; padding: 0; list-style: none; } body { background: #389acc; } </style> <style scoped> /* App own style */. 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>Copy the code

Here we have two divs, the one on the left automatically sized, the one on the right fixed, and the result looks like this

Create a List component

Now that the big frame is set up, let’s build some details, because all we need to do is the list on the right, so we can pull out a separate component, so we can play around with it

Next, let’s see if it comes out, and then we’ll go ahead and import it from app.vue

Here we do a few things:

  • Specify the development language (lang="ts"Vue3’s support for TS is already quite good (compared to V2), so it’s nice to use strong types
  • Define a component (defineComponent) : Help us define components – mainly types in TS, very convenient

It looks like this

Then, we need to introduce the List component we just wrote (although there is nothing there), three things:

  • importIntroduce the List component: This is easy, we need to introduce everything first
  • registeredListComponent: Available in componentscomponentsTo register the local component, and thentemplateThe use of
  • Add to the template

At this point, we can see the List component on the page

Now that the basic layout is in place, let’s start writing the List

Writing a List component

First, regardless of function, the list has to have a look, so let’s add some HTML and styles:

<! -- List.vue layout --> <template> <div> <! <div class="item"> <h3 class="header"> </h3> <p class="content"> this is the content of some text. This is the content of some text, this is the content of some text, this is the content of some text </p> </div> <div class="item"> <h3 class="header"> This is a header, maybe a little long, yeah, </h3> <p class="content"> </p> < div> </div> </template>Copy the code

There’s no styling yet, so our thing looks like this:

Oh, my god, it’s ugly. It’s okay. Let’s decorate it

<style scoped> /* 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; /* Text is too long to truncate */ overflow: hidden; white-space: nowrap; text-overflow: ellipsis; } .content { color: #999; font-size: 14px; line-height: 26px; height: 78px; /* Multiline text truncation */ overflow: hidden; text-overflow: ellipsis; -webkit-line-clamp: 3; } </style>Copy the code

Let’s see if it’s better:

I can’t say it’s amazing, but at least it’s meeting people, right

Step 3: Add data

So far, I’ve got a little bit of stuff, but it’s still dead, and I can’t write it out directly, so we want the data, so let’s start with the simplest thing

First edition, static data

In list. vue, we add data

<script lang="ts"> import { defineComponent } from "vue"; Export default defineComponent({setup() {// Const datas = [{ID: 1, title: Multiple flights unable to land due to suspected unidentified aircraft activity. A flight tracking software used by aviation enthusiasts found that several flights had circled and waited in the air before arriving at Hangzhou Xiaoshan International Airport or diverted to nearby Ningbo Lihe International Airport on The night of April 4. "A bankrupt company in Changxing, Huzhou, with assets of 200 million yuan, was auctioned. Behind it was the figure of" The richest man in China who fell fastest ", Content: "Yes," Hanergy ", "Hanergy Thin Film" was the most obscure "monster stock" in Hong Kong stocks in 2013 and 2014, and its founder Li Hejun was the richest man in China in March 2015 with a wealth of 160 billion yuan. However, his wealth plummeted 100 billion yuan only 3 months later, and he was called the richest man in the world. Return {datas, // to use} in template; }}); </script>Copy the code

And then again, list.vue, we add loops and output

<template> <div> <div class="item"> <h3 class="header"> </h3> <p class="content"> This is the content of some text, </p> </div> </div> </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>Copy the code

Let’s see what happens

That’s good. There’s no shortage of one. So what we’re going to do is we’re going to animate the data

How to use dynamic data

First of all, if you want to read data, you definitely need Ajax, and this time we’re not going to focus on Ajax itself, so just make it simple and use axios

First install, CTRL + C to stop the project, then install (note that mixing package managers can be problematic)

npm i axios -S
# or
cnpm i axios -S
# or
yarn add axios
Copy the code

Complete the

Next, we need a file to hold the data in.

The files in public, by the way, will be copied to the compiled result exactly as they are, so this stuff will still be accessible when it goes live

Read the data

<script lang="ts"> import { defineComponent } from "vue"; // import axios from "axios"; Export default defineComponent({setup() {axios("/datas.json").then((res) => {console.log(res.data); // Type out what you read}); Return {datas: [], // null}; }}); </script>Copy the code

Run and see how it works (don’t forget to start NPM Run Serve)

Good. I got everything I need

Update the data

Then we need to face another problem — how to update the data

In Vue3, we have several ways to build “responsive data”, which means that after you modify the data, the view automatically rerenders in response to your changes to the data. In this case, we chose ref, which is very convenient to use with individual data (such as arrays here)

How to use Ref

So let’s start with a simple example, how do we use ref

{{name}} <button @click="name += 'a'"> {{age}} <button @click="age++"> Modify </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>Copy the code

It looks like this:

Regardless of whether it’s ugly or not, what happens if we click on it?

Here we see two strange things:

  • Modify thename(Ordinary variable) no response
  • Modify theage(Response data) respond and connectnameThe changes have taken effect together

It’s actually quite simple, one by one:

  • Why is there no response to modifying name?

    For performance reasons, vUE does not listen for all changes and only specific data (ref, Reactive, etc.) triggers rendering

  • Why is age valid with name?

    Since vue checks for changes to all data (including non-response data) during re-rendering, changes to the natural name are also detected

Second edition, server data

It’s time to put everything above us together

<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([]); // Array of responses (human version: Then ((res) => {//1-ref =.value, Datas.value = [...datas.value,...res.data]; datas.value = [...datas.value,...res.data]; }); Return {datas, // output to the page to use}; }}); </script>Copy the code

See if you can come out:

Data this basic fix, but there is a problem, pull to the bottom it unexpectedly will not automatically update (nonsense, haven’t done 😂), how to do, continue to do

Step 4, listen to the page scroll

It’s actually pretty easy for us to auto-load, two things:

  • Monitor the scroll
  • When you see it’s rolling to the bottom, you get a new one

Let’s follow this procedure

Add to monitor

First of all, we know that most of the events in vue are added by @xxx, but the scroll events are Windows. We can’t window.@scroll. It’s actually very simple, just add it manually

import { onMounted, onUnmounted } from "vue";

export default defineComponent({
  setup() {
    // Scroll the event handler
    function scrollHandle() {
      console.log('roll');
    }

    onMounted(() = > {
      // Add scroll listener when component is mounted
      window.addEventListener("scroll", scrollHandle, false);
    });
    onUnmounted(() = > {
      // Stop listening when the component is uninstalled
      window.removeEventListener("scroll", scrollHandle, false); }); }});Copy the code

What was the effect?

That seems to be fine, and it does remind us to scroll, which brings us to the second question — how do we know we’re near the end

Is it close to the end?

What trifle, whole follow tongue twister like 😂

In a nutshell, we need to detect “is the user rolling to the bottom?” So how do we do that? Let’s take a look at a picture with Blue

It’s rough (you call that rough??) “, but the meaning is very clear: the page is very high, the viewable area slides up and down on the page, and we can calculate the distance between the viewable area and the bottom of the page, and if it’s below a certain threshold, we’ll say, “This is almost the end.”

Okay, so how do we calculate this distance? Let’s see another picture

There are several values on the page that you can use

  • #1-scrollTop: Roll distance

  • #2-scrollHeight: Total height of the page content

  • #3-clientHeight: The height of the viewing area

// Distance = total height - rolling distance - viewable height
let distance = scrollHeight - scrollTop - clientHeight;
Copy the code

Let’s try it in code

Look at the results

It turns out to be something like -100. Why is that? Actually very simple, our margin caused

Because margin does not count the height of the object itself, so the content is smaller than it actually looks. What should I do? Change the bai

Try it out

This is much better, so we just need to determine that the value is small enough (say 200, depending on your needs)

Let’s try it out

But there is a problem, it appears many times, what to do?

Prevent reloading

In simple terms, we can “lock” it once it has been loaded and not allow it to fire again until the load is complete

// Load logic

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


/ / modified
let readyForLoad = true; // Load once by default

if (readyForLoad) {
  // It needs to be loaded in order to prevent repetition
  readyForLoad = false; // Lock it up

  axios("/datas.json").then((res) = > {
    datas.value = [...datas.value, ...res.data];
    readyForLoad = true; // Open the lock only after loading, allowing it to trigger again
  });
}
Copy the code

Because we need to load it all the time, so we’ll wrap it up as a function, like this

let readyForLoad = true; // Load once by default

function loadMore() {
  if (readyForLoad) {
    // It needs to be loaded in order to prevent repetition
    readyForLoad = false; // Lock it up

    axios("/datas.json").then((res) = > {
      datas.value = [...datas.value, ...res.data];
      readyForLoad = true; // Open the lock only after loading, allowing it to trigger again}); }}Copy the code

Our setup would then look like this:

setup() {
  // Scroll the event handler
  function scrollHandle() {... Omit some code...if (distance <= 200) {
      console.log("Near the end!);
      loadMore(); // It doesn't matter how many times the trigger is triggered} } onMounted(...) ; onUnmounted(...) ;const datas = ref([]);

  let readyForLoad = true; // Load once by default

  loadMore(); // Start loading once
  function loadMore() {
    if (readyForLoad) {
      // It needs to be loaded in order to prevent repetition
      readyForLoad = false; // Lock it up

      axios("/datas.json").then((res) = > {
        datas.value = [...datas.value, ...res.data];
        readyForLoad = true; // Open the lock only after loading, allowing it to trigger again}); }}return {
    datas, // Output to the page for use
  };
},
Copy the code

Try the effect, basically no problem (GIF format is too poor, pressure out of the actually 10M, webM is only a few hundred K, so be careful)

conclusion

It’s time to go over what Blue said, so first of all

  • use@vue/cliQuick construction project, convenient
  • Read the data using Axios
  • inmountandunmountListen for the page scroll event
  • Calculate the distance to the bottom of the page (distance = total height – scrolling distance – viewable area height), less than a certain threshold (such as 200) as the bottom
  • By “throttling” mode, prevent repeated trigger loading action
  • After loading,[...oldData, ...newData]Join all the data together to form a new array to use
  • throughrefUpdate views in real time

Have a bug? Would like to add?

Thank you for watching this tutorial, if you have any questions or want to talk to me, please leave a comment directly. If you find anything inappropriate in this article, please also point out, thanks in advance