The main functions to be implemented are as follows:

Information list, TAB page switch, article report, channel management, article details, reading memory, focus on function, like function, comment function, reply to comment, search function, login function, personal center, edit information, xiaozhi students…

To recap: yesterday we implemented login, login interception, and layout components

Note :(data is stored in data.data by default, this article uses deconstruction by default)

Today we want to achieve the main function is: information list

1 Implement level-2 routing

1.1 insrc/viewsCreate four folders: home, question, setting and video

Create home.vue, question. Vue, setting.vue and video.vue in four folders respectively. Set up the initial code in each file and mark it with H1 tags for later testing

1.2 Nested routines by

insrc/router/index.js, add the code as follows

{ path: '/', name: 'layout', component: () => import('.. / views/layout/layout. Vue '), / / secondary routing children: [{path: "', / / secondary routing path is empty said the default to load component name: 'home', component: () => import('../views/home/home.vue') }, { path: '/question', name: 'question', component: () => import('../views/question/question.vue') }, { path: '/video', name: 'video', component: () => import('../views/video/video.vue') }, { path: '/setting', name: 'setting', component: () => import('../views/setting/setting.vue') } ] },Copy the code

We added the route attribute to the van-tabbar tag, which automatically enabled the jump

1.3 Testing hops between routes

The test works fine, but a warning line appears

This warning can be eliminated by deleting the name of the layout configured on the router/index.js

Channel list on home page

2.1 insrc/views/home/home.vueUse the [Tabs component] to place a list of channels in.

-- > < template > < div > < van - tabs v - model = "active" > < van - TAB title = "TAB 1" > 1 < / van - TAB > content < / van - tabs > <! --1. tab --> <! --2. list --> </div> </template> <script> export default { name: 'Index', data () { return { active: 1 } } } </script>Copy the code

The van-tab tag only needs to leave a target that can be achieved using v-for traversal as shown below

2.2 Encapsulating API requests

Channel.js, which we encapsulated when testing Axios, was reused. The content code is as follows:

Import request from '@/utils/request' /** * getChannels */ export const getChannels = () => {return request({ url: 'v1_0/user/channels', method: 'GET' }) }Copy the code

2.3 Invoking the API to Obtain Data

** In views/home/home.vue **

Import {getChannels} from '@/ API /channel.js'Copy the code
Export default {data () {return {active: 2, // tabs Create () {this.loadchannels ()}, methods: { async loadChannels () { try { const {data:{data}} = await getChannels() this.channels = data.channels } catch (err) { console.log(err) } } } }Copy the code

2.4 Render View

<van-tab v-for="channel in channels" :title="channel.name" :key="channel.id"> {{channel.name}} </van-tab>Copy the code

View the effect to achieve the final goal in 2.1

Style of channel 3 content

We add content in the corresponding channel:

< div > channel content {{channel. The name}} < p v - for = "independence idx in 20:" key = "independence idx" > the first {{independence idx}} article < / p > < / div >Copy the code

You can see that the scroll bar appears at the top of the screen, and the navigation at the top is hidden by sliding. In order to solve these problems, we need to add some styles to the common style to solve these problems

Add the following style code to styles/index.less (note: attach a fixed class name to the target before using it)

  • The root element id of app.vue is app

  • Add the Container class to the layout.vue root element

  • Add the index class to the root element of home.vue

  • The scroll wrapper is added to the channel content container

/* / #app{position: absolute; left: 0; top: 0; overflow: hidden; width: 100%; height: 100%; } /**. Container is the class name of the root element of the layout. vue component. Container {width: 100%; height: 100%; } /**.index is the class name of the root element of the home/home.vue component **/. Padding-top: 46px; padding-top: 46px; padding-top: 46px; .van-tabs { padding-top:50px; display: flex; flex-direction: column; height: 100%; .van-tabs__wrap { position:fixed; top:46px; /** left:0px; right:30px; /** highlight the underline of the currently selected channel **/. Van -tabs__line {width: 30px! important; background-color: #3296fa; bottom: 20px; } } .van-tabs__content { flex:1; overflow: hidden; padding-bottom: 4rem; /** You can see the loading effect in the article list **/. Van-tab__pane {height: 100%; /**.scroll wrapper is the class name of the root element of the home/ articlelist. vue component **/. Scroll wrapper{overflow:auto; height: 100%; } } } } }Copy the code

4. Implement the pull-up loading of article list

4.1 We separately extract a component articlelist.vue for processing

Create a new articlelist. vue component in the home folder to handle the list of functions for the articleList

< the template > < div class = "scroll - wrapper" > article list component {{Math. The random ()}} < p v - for = "I in 50" : the key = "I" > {{I}} < / p > < / div > </template> <script> export default { name: 'ArticleList' } </script> <style scoped lang='less'></style>Copy the code

4.2 Establish a relationship with the home.vue file

In the home. In vue

Import ArticleList from './ ArticleList ': {ArticleList},Copy the code
<template> <div class="index"> <! -- Channel list https://vant-contrib.gitee.io/vant/#/zh-CN/tab#biao-qian-lan-gun-dong --> <van-tabs> <van-tab v-for="channel in channels" :title="channel.name" :key="channel.id"> <! Channels correspond to the list of articles. Each channel needs to have an article list component. Article - List is written in v-for, so each loop generates an article list component. Van-tab has the effect of lazy loading: only if the current TAB is activated will the article list component be created --> + <article-list></article-list> </van-tab> </van-tabs> <! </div> </template>Copy the code

Van-tabs has an effect similar to lazy loading: “ArticleList will only be loaded if a label is activated.”

4.3 Article List – Channel Information Transfer (Parent to child)

A channel is passed in home.vue

<article-list :channel="channel"></article-list>
Copy the code

Receives a prop in the child component articleList

props: ['channel']
Copy the code
< div class = "scroll - wrapper" > {{channel}} article information channel list component < p v - for = "I in 50" : the key = "I" > {{I}} < / p > < / div >Copy the code

4.4 the use ofVue debugging tool, test results

5 Article list -van-list

5.1In articleList. Vue

<div class="scroll-wrapper"> {{channel}} <! Van-list has more effect of pull-up loading: 2) When displaying data, if the current data is less than one screen, it will automatically trigger the load event and execute the corresponding callback onLoad to load data in onLoad. Use Ajax to fetch new data - append data to list (list will get more and more) - Manually set loading to false - Determine whether all data has been loaded and set finished to True if so And finished is not true, OnLoad --> <van-list v-model="loading" :finished="finished" finished-text=" no more "@load="onLoad" > <van-cell v-for="item in list" :key="item" :title="item" /> </van-list> </div> <script> data () { return { list: [], // Data loading: false, // Whether loading... Finished: false}}, methods: SetTimeout (() => {for (let I = 0; i < 10; I++) {this.list.push(this.list.length + 1)} this.loading = false if (this.list.length >= 40) { this.finished = true } }, 1000) } } </script>Copy the code

5.2 [Data Loading Mechanism] of Van-List

  • If the current content is not enough for a screen, it will automatically call the callback function of the onLoad event to load the data and fill it in.

  • List has the following three states:

  • If loading is not in process, the loading is false. In this case, the loading event will be triggered according to the scrolling position of the list (if the list content is less than one screen, the load event will be triggered directly).

    • Loading,loadingfortrue, indicating that an asynchronous request is being sent and will not be triggered againloadThe event
    • When the load is complete,finishedfortrue, will not trigger againloadThe event
  • After each request is complete, manually set loading to false to indicate that loading is complete

5.3 Obtaining and displaying data

Create it in the SRC/API directoryarticle.jsFile that encapsulates the request function used to process the article.

import request from '@/utils/request.js' // eslint-disable-next-line camelcase export const getArticles = (channel_id, Timestamp) => {// If the function does not write return, // it has a default return value, which is undefined // return request({return request({url: Method: 'GET', params: {channel_id, params: {channel_id, params: {channel_id, params: {channel_id, params: {channel_id, params: {channel_id, params: {channel_id, params: {channel_id, params: {Copy the code

5.4 Import this interface in articlelist. vue

import { getArticles } from '@/api/article.js'
Copy the code
Const {data:{data}} = await getArticles(this.channel.id, date.now ()) const arr = data.results // It is an array // 1. // Expand the array this.list.push(... This. Loading = false // 3. This. Finished === 0. {this.finished = true} // 4. $toast.success(' data loaded successfully ')}Copy the code

Since each item in the list is an object representing an article, adjust the key and title in the V-for directive.

<van-list v-model="loading" :finished="finished" loading-text=" Finished" "@load="onLoad"> <van-cell v-for="article in list" :key="article. Art_id" :title="article. Title "/> </van-list>Copy the code

5.5 Test Effect

6 Obtain the data and fill in the timestamp

The data obtained is the same every time 10 data are the same for each request. According to the tips given by the back-end document, we found that accurate time stamps need to be transmitted in each request, and the data returned every time just includes the timestamp, which is exactly the timestamp data we need to send to the back-end

Therefore:

  1. Add a data item timestamp with an initial value of the current timestamp
  2. Bring it with you when you send requests
  3. When the data comes back, update it
data(){
    return{
+       tiemstamp:Date.now()
    }
}

Copy the code
async onLoad () { // Date.now(): + const {data:{data}} = await getArticles(this.channel.id, {data}} = await getArticles(this.channel.id,) This.tiemstamp) // Const arr = data.results // it is an array // 1. // Expand the array this.list.push(... Arr) // 2. Update timestamp + this.tiemstamp = data.pre_timestamp // 3. Loading = false this.loading = false // 4. This. Finished === 0. {this.finished = true} // 5. $toast.success(' data loaded successfully ')}Copy the code

7. Article layout

  • Slot in van-cell: the function is to wrap the whole, displayed below the title, used to display the picture – author – comment – time, etc. under the title.
  • Van – grid:. It can be used to divide a row into several columns
  • Van-image: Enhanced image, used to display images

7.1 Structure Code

<van-cell v-for="item in list" :key="item.art_id" :title="item.title"> <div slot="label"> <van-grid v-if="item.cover.images" :column-num="item.cover.images.length"> <van-grid-item v-for="(imgSrc,idx) in item.cover.images" :key="idx"> <! -- {{imgSrc}} --> <van-image :src="imgSrc"/> </van-grid-item> </van-grid> <! - text area - - > < div class = "meta" > < span > {{item. Aut_name}} < / span > < span > {{item.com m_count}} comments < / span > <span>{{item.pubdate}}</span> </div> </div> </van-cell>Copy the code

7.2 Adding Styles

  • There are up to three figures per article pulled from the back end. It can be divided into three cases as follows: three pictures, one picture and no picture.
  • The image is saved incover.imagesIn the.

Add styles at the bottom

<style scoped lang='less'>
.meta {
  span{
    margin-right: 10px;
  }
}
</style>
Copy the code

8 pull down refresh function (there is still a bug to write back end so refresh will report errors) error content because the data returned is repeated

When the user pulls down the page, the current timestamp is used to request the interface to get the latest data, and then the data is filled to the top of the page to achieve the drop-down refresh function

8.1Structure can be achieved by wrapping your content with the van-pull-refresh tag, so just put a Van-pull-refresh on the outside of the van-list.

<template> <div class="scroll-wrapper"> + <van-pull-refresh v-model="refreshing" @refresh="onRefresh"> <van-list> . </van-list> + </van-pull-refresh> </div> </template>Copy the code

8.2 Add an isLoadingNew to data

Data () {return {list: [], timestamp: null, + refreshing: false, False, // If finished: false // If all data is finished}}Copy the code

8.3When you pull down, you also need to send a request for the back end to return the latest data to us

Async onRefresh () {// date.now (): Const res = await getArticles(this.channel.id, Date.now()) // Const arr = res.data.data.results // It is an array // 1. // Expand the array this.list.unshift(... Refreshing = false this. biosphere = false this. toast. Success (' successfully loading data ')},Copy the code
  • The second parameter to set getArticles (timestamp) is the latest timestamp.
  • Unshift: Puts data at the head of the array

8.4 Have a look at the effect drawing

9 Time Filter

Encapsulate a time filter defined with vue.use () to globally display days or hours ago or just published

9.1 Understand the basic filter format

Filters: {Filter name (the value to be processed) {//.... Return Result}}Copy the code

Use:

{{filter name (original data)}}Copy the code

9.2 Encapsulate your own filter function

Create your own toolkit file in the utils folder, named dateFormate.js, and write your wrapped functions and export them

export const relativeTime = (value) => { const t = new Date(value) const diff = Date.now() - t.getTime() const year = Math.floor(diff/(1000 * 3600 * 24 * 365)) if (year) {return '${year} before'} const month = math.floor (diff/(1000 *)) 3600 * 24 * 30)) if (month) {return '${month} before'} const day = math.floor (diff/(1000 * 3600 * 24)) if (day) {return Const hour = math. floor(diff/(1000 * 3600)) if (hour) {return '${hour} before'} const minute = Math.floor(diff/(1000 * 60)) if (minute) {return '${minute} before'} else {return 'just'}}Copy the code

An object is then exported by default and Vue is passed by calling the Install method

export default {
  install: function (Vue) {
    Vue.filter('dateFormate', dateFormate)
  }
}
Copy the code

9.3 Introduce this tool file in main.js and implement it with the vue.use () call

import dateFormat from './utils/dateFormate.js'
Vue.use(dateFormat)
Copy the code

Note: You cannot use the custom name dataFormate when using filters, but must use the name of your own wrapped function: relativeTime

9.4 Use custom filters in articlelist. vue

<! - the text area - - > < div class = "meta" > < span > {{item. Aut_name}} < / span > < span > {{item.com m_count}} comments < / span > + < span > {{ item.pubdate | dateFormate }}</span> </div>Copy the code

Lazy loading of images

Using the Vant component Lazyload to implement lazy image loading was introduced in main.js

 import Vant, { Lazyload } from 'vant'
 Vue.use(Lazyload)
Copy the code

Used in the articlelist.vue component

   <van-grid-item v-for="(img,idx) in item.cover.images" :key="idx">
+    <van-image lazy-load :src="img" />
   </van-grid-item>
Copy the code

Note: Lazy loading here will cause an error if the Lazyload directive is not introduced in advance.