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/views
Create 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.vue
Use 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,
loading
fortrue
, indicating that an asynchronous request is being sent and will not be triggered againload
The event- When the load is complete,
finished
fortrue
, will not trigger againload
The eventAfter 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.js
File 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:
- Add a data item timestamp with an initial value of the current timestamp
- Bring it with you when you send requests
- 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 in
cover.images
In 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.