亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

Vue.js實現無限加載與分頁功能開發

2019-11-20 08:36:18
字體:
來源:轉載
供稿:網友

本篇文章是一篇Vue.js的教程,目標在于用一種常見的業務場景――分頁/無限加載,幫助讀者更好的理解Vue.js中的一些設計思想。與許多Todo List類的入門教程相比,更全面的展示使用Vue.js完成一個需求的思考過程;與一些構建大型應用的高階教程相比,又更專注于一些零碎細節的實現,方便讀者快速掌握、致用。

需求分析

當一個頁面中信息量過大時(例如一個新聞列表中有200條新聞需要展示),就會產生問題,例如:

》數據量過大,影響加載速度

》用戶體驗差,很難定位到之前自己看過的某篇文章

》擴展性差,如果200條變為2000條或者更多

所以常見的解決思路就是至底時加載數據或者分頁展示。無限加載的實現過程類似于:

1.ajax類方法獲取數據

2.數據存入本地數組

3.數組中的每條數據對應插入一個HTML模板片段中

4.將HTML片段append到節點中

前端分頁的實現過程類似于:

1.ajax類方法獲取數據

2.數據替換本地數組

3.數組中的每條數據對應插入一個HTML模板片段中

4.清空節點后將HTML片段append到節點中

往往修改或者維護代碼時,我們會發現渲染HTML和插入部分是比較煩人的。因為我們需要將HTML拼接成字符串,在對應的位置插入數據,往往就是一段非常長的字符串,之后想要加個class都費勁。es6的模板字符串讓這個情況有所好轉,但是依然有瑕疵(例如實際編寫時無法HTML代碼高亮)。同時我們還需要寫不少for或者forEach去循環數組,再命令式的append,如果這段代碼片段有一些復雜的交互,可能還需要通過事件代理綁定一堆方法。

如果在完成這類業務時,你也遇到過上述的問題,那么你就會發現Vue真是太coooooool了,let's vue!

新建一個Vue.js項目

強烈推薦使用vue-cli來新建一個項目。

一開始你可能會認為用node.js和npm安裝一大堆庫,生成了一些你不太了解的目錄和配置文件,一寫代碼還會跳出一堆eslint的提示。但是這絕對物有所值,因為這樣的一個模板可以幫你更好的理解Vue.js組織文件的思路,并且當你適應之后,你會發現這些條條框框極大地加快了你的開發效率。

在這次的教程中,我們新建了一個名叫loadmore的項目,具體的新建項目流程可以參照官網教程的安裝一節。

布局頁面結構

為了配合教程的逐步深入,我先從完成 加載更多 功能入手。為了和之后的分頁保持一致,我的頁面準備由兩部分組成,一是信息列表,二是底部的一個加載更多的按鈕,我將他們都放在App.vue這個根組件中。

<template> <div id="app"> <list></list> <a class="button" @click="next" >GO NEXT</a> </div></template><script>import List from './components/List'export default { components: { List }, data () { return { ... } }, methods: { next () { ... } }}</script><style scoped> .button { display: block; width: 100%; background: #212121; color: #fff; font-weight: bold; text-align: center; padding: 1em; cursor: pointer; text-decoration: none; } .button span { margin-left: 2em; font-size: .5rem; color: #d6d6d6; }</style>

在這個過程中,我們根據Vue的設計思想有了如下思路:

1.在信息列表中,我們會完成我們上文中提到的幾個步驟,而這些步驟都只和信息列表本身有關,與Next按鈕間唯一的聯系就是Next點擊后需要觸發信息列表去獲取,而這可以通過props傳遞。所以我們把列表及其自身業務邏輯、樣式都放在List.vue這個組件中。

2.我們為按鈕定義了一些基本的樣式,但是我們用的css選擇器就是一個.button類名,可能會和別的組件中的.button樣式沖突,所以我們加入了一個scoped屬性,讓App.vue中的style樣式只作用于這個組件內部。

注意:scoped并不會影響css的作用優先級,使用scoped不代表不會被外部樣式表覆蓋。

3.我們想引入一些基礎樣式,比如reset.css。如果在項目中使用了sass之類的語言,那么可以將對應的外部sass文件放在assets文件夾中,通過import引入。普通的css可以直接寫在一個不加scoped屬性的組件中,但是如果你確定這個樣式表不會被頻繁改動,那么也可以作為第三方靜態資源引入index.html中。例如這個例子中,我在index.html中加入了:

<link rel="stylesheet" href="./static/reset.css">

效果:

完成List.vue

目前我們主要的業務邏輯都是圍繞信息列表展開的,也就是我們創建的List.vue。首先,我們需要獲取目標數據,我選用了cnodejs.org社區的API作為例子進行編寫。如果你也想用一個封裝好的ajax庫的話,應該這么做:

引入第三方JS庫
將目標JS庫文件放在static文件夾中,例如我選擇的是reqwest.js,然后在index.html先引入。

<script src="./static/reqwest.min.js"></script>

然后在build配置文件夾中,修改webpack.base.conf.js,export externals屬性:

externals: { 'reqwest': 'reqwest'}

這樣我們在我們的項目中,就可以隨時加載第三方庫了。

import reqwest from 'reqwest'

寫個API接口
在這個例子中,我們只需要調用文章列表這一個接口,但是實際項目中,可能你需要調用很多接口,而這些接口又會在多個組件中被用到。那么調用接口的邏輯四散在各個組件中肯定是不好的,想象一下對方的url發生了變化,你就得在無數個組件中一個個檢查是否要修改。

所以我在src文件夾中新建了一個api文件夾,用于存放各類API接口。當前例子中,要獲取的是新聞列表,所以新建一個news.js文件:

import reqwest from 'reqwest'const domain = 'https://cnodejs.org/api/v1/topics'export default { getList (data, callback) { reqwest({ url: domain, data: data }) .then(val => callback(null, val)) .catch(e => callback(e)) }}

這樣我們就擁有了一個獲取新聞列表的API:getList。

編寫組件

我們用一個<ol>作為新聞列表,內部的每一個<li>就是一條新聞,其中包括標題、時間和作者3個信息。

在data中,我們用一個名為list的數組來儲存新聞列表的數據,一開始當然是空的。我們再在data中設置一個名為limit的值,用來控制每頁加載多少條數據,作為參數傳給getList這個API。

因此我們的template部分是這樣的(加入了一些style美化樣式):

<template> <ol> <li v-for="news of list"> <p class="title">{{ news.title }}</p> <p class="date">{{ news.create_at }}</p> <p class="author">By: {{ news.author.loginname }}</p> </li> </ol></template><style scoped> ol { margin-left: 2rem; list-style: outside decimal; } li { line-height: 1.5; padding: 1rem; border-bottom: 1px solid #b6b6b6; } .title { font-weight: bold; font-size: 1.3rem; } .date { font-size: .8rem; color: #d6d6d6; }</style>

之后我們顯然需要使用getList來獲取數據,不過先想想我們會在哪幾個地方使用呢?首先,我們需要在組件開始渲染時自動獲取一次列表,填充基礎內容。其次,我們在每次點擊APP.vue中的Next按鈕時也需要獲取新的列表。

所以我們在methods中定義一個get方法,成功獲取到數據后,就把獲取的數組拼接到當前list數組后,從而實現了加載更多。

沿著這個思路,再想想get方法需要的參數,一個是包含了page和limit兩個屬性的對象,另一個是回調函數?;卣{函數我們已經說過,只需要拼接數組即可,因此只剩下最后一個page參數還沒設置。

在初始化的時候,page的值應該為1,默認是第一頁內容。之后page的值只由Next按鈕改變,所以我們讓page通過props獲取App.vue中傳來的page值。

最后則是補充get方法觸發的條件。一是在組件的生命周期函數created中調用this.get()獲取初始內容,另一是在page值變化時對應獲取,所以我們watch了page屬性,當其變化時,調用this.get()。

最后List.vue的script長這樣:

<script>import news from '../api/news'export default { data () { return { list: [], limit: 10 } }, props: { page: { type: Number, default: 1 } }, created () { this.get() }, watch: { page (val) { this.get() } }, methods: { get () { news.getList({ page: this.page, limit: this.limit }, (err, list) => { if (err) {  console.log(err) } else {  list.data.forEach((data) => {  const d = new Date(data.create_at)  data.create_at = `${d.getFullYear()}-${d.getMonth() + 1}-${d.getDate()}`  })  this.list = this.list.concat(list.data) } }) } }}</script>

同時我們將App.vue中的<list>修改為:

<list :page="page"></list>

再為page在App.vue中添加一個初始值以及對應的方法next:

data () { return { page: 1 }},methods: { next () { this.page++ }}

這樣我們就已經完成了加載更多的功能。

改寫為分頁

因為之前我們的思路非常清晰,代碼結構也很明了,所以改寫起來會非常簡單,只需要將List.vue中拼接數組改為賦值數組就可以了:

// 常規loadmore// this.list = this.list.concat(list.data)// 分頁this.list = list.data

就這么簡單的一行就完成了功能的改變,這就是Vue.js中核心的數據驅動視圖的威力。當然,接下來我們還要做點更cooooool的。

添加功能

因為分頁替換了原來的數組,所以僅僅一個Next按鈕不夠用了,我們還需要一個Previous按鈕返回上一頁。同樣的,也給Previous按鈕綁定一個previous方法,除了用this.page--改變page的值以外,還需要對this.page === 1的邊界條件進行一個判斷。

同時為了方便知道我們當前的頁數,在按鈕中,加入{{ page }}顯示頁數。

<a class="button" @click="next" >GO NEXT<span>CURRENT:{{page}}</span></a>

transition動畫
編寫和完善功能的過程中,已經充分體現了Vue.js清晰和便利的一面,接下來繼續看看其它好用的功能,首先就是transition動畫。

為了展示transition的威力,首先我找到了一個模仿的對象:lavalamp.js( Demo地址 )。

在Demo中可以看到頁面以一種非常優雅的動畫過渡完成了切換內容的過程,其本身是用JQuery+CSS動畫完成的,我準備用Vue.js進行改寫。

首先學習了一下原作者的實現思路以后,發現是將一個div作為loader,position設定為fixed。當翻頁時,根據點擊的按鈕不同,loader從頂部或者底部擴展高度,達到100%。數據加載完畢后,再折疊高度,最終隱藏。

那么初步的思路如下:

1.添加一個loader,最小高度與按鈕一致,背景同為黑色,讓過渡顯得更自然。

2.loader高度需要達到一個屏幕的高度,所以設置html和body的height為100%。

3.需要有一個值,作為loader是否顯示的依據,我定為finish,其默認值值為true,通過給loader添加v-show="!finish"來控制其顯示。

4.在next和previous方法中添加this.finish = false觸發loader的顯示。

5.在App.vue和List.vue建立一個雙向的props屬性綁定至finish,當List.vue中的get方法執行完畢后,通過props將App.vue中的finish設定為true,隱藏loader。

6.給loader添加一個transition。由于動畫分為頂部展開和底部展開兩種,所以使用動態的transition為其指定正確的transition名稱。

7.新增一個值up,用于判斷動畫從哪個方向開始,其默認值為false。在previous方法中,執行this.up = true,反之在next方法中,則執行this.up = false。

根據思路,寫出的loader應該是這樣的(style等樣式設定在最后統一展示):

<div id="loader" v-show="!finish" :transition="up? 'up-start':'down-start'"> <span>Loading</span></div>

可以看到我設定了up-start和down-start兩種transition方式,對應的css動畫代碼如下:

.down-start-transition { bottom: 0; height: 100%; } .down-start-enter { animation: expand .5s 1 cubic-bezier(0, 1, 0, 1) both; } .down-start-leave { animation: collapse .5s 1 cubic-bezier(0, 1, 0, 1) both; top: 0; bottom: auto; } .up-start-transition { top: 0; height: 100%; } .up-start-enter { animation: expand .5s 1 cubic-bezier(0, 1, 0, 1) both; } .up-start-leave { animation: collapse .5s 1 cubic-bezier(0, 1, 0, 1) both; top: auto; bottom: 0; } @keyframes expand { 0% { height: 3em; transform: translate3d(0, 0, 0); } 100% { height: 100%; transform: translate3d(0, 0, 0); } } @keyframes collapse { 0% { height: 100%; transform: translate3d(0, 0, 0); } 100% { height: 3em; transform: translate3d(0, 0, 0); } }

設置了expand和collapse兩個animation,再在transition的各個生命周期鉤子中做對應的綁定,就達到了和lavalamp.js相接近的效果。

為了保證動畫能執行完整,在List.vue的get方法執行完之后,還使用了一個setTimeout定時器讓finish延時0.5秒變為true。

優化體驗
動畫效果完成之后,實際使用時發現lavalamp.js還有個巧妙地設計,就是點擊Previous后,頁面前往底部,反之點擊Next后則前往頂部。

實現后者并不復雜,在next方法中加入以下一行代碼調整位置即可:

document.body.scrollTop = 0

previous前往底部則略微復雜一點,因為獲取到數據之后,頁面高度會發生改變,如果在previous中執行scrollTop的改變,有可能會出現新的內容填充后高度變長,頁面不到底的情況。所以我watch了finish的值,僅當點擊按鈕為previous且finish變化為false至true時前往底部,代碼如下:

watch: { finish (val, oldVal) { if (!oldVal && val && this.up) { document.body.scrollTop = document.body.scrollHeight } }}

前端路由
完成以上內容之后,發現不論翻到第幾頁,一旦刷新,就會回到第一頁。vue-router就是為解決這類問題而生的。

首先我們引入VueRouter,方式可以參考上文中的“引入第三方JS庫”。然后在main.js對路由規則進行一些配置。

我們的思路包括:

1.我們需要在url上反映出當前所處的頁數。

2.url中的頁數應該與所有組件中的page值保持一致。

3.點擊Next和Previous按鈕要跳轉到對應的url去。

4.在這個例子中我們沒有router-view。

因此main.js的配置如下:

import Vue from 'vue'import App from './App'import VueRouter from 'VueRouter'Vue.use(VueRouter)const router = new VueRouter()router.map({ '/page/:pageNum': { name: 'page', component: {} }})router.redirect({ '/': '/page/1'})router.beforeEach((transition) => { if (transition.to.path !== '/page/0') { transition.next() } else { transition.abort() }})router.start(App, 'app')

首先定義了一個名為page的具名路徑。之后將所有目標路徑為'/',也就是初始頁的請求,重定向到'/page/1'上保證一致性。最后再在每次路由執行之前做一個判斷,如果到了'/page/0'這樣的非法路徑上,就不執行transition.next()。

根據之前的思路,在App.vue中,獲取路由對象的參數值,賦值給page。同時給兩個按鈕添加對應的v-link。

本文已被整理到了《Vue.js前端組件學習教程》,歡迎大家學習閱讀。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
久久久精品一区二区三区| 久久久久久久电影一区| 国产精品久久久久久久午夜| 久久国产天堂福利天堂| 日韩在线视频导航| 一本色道久久综合狠狠躁篇的优点| 欧美在线一级va免费观看| 国产精品视频白浆免费视频| 国产美女搞久久| 亚洲一区二区黄| 91理论片午午论夜理片久久| 亚洲在线免费观看| 中文字幕日韩欧美| 成人网址在线观看| 亚洲激情视频网站| 国产一区欧美二区三区| 性色av一区二区三区| 精品少妇一区二区30p| 日韩av手机在线看| 日韩欧美成人精品| 精品国产一区二区三区久久狼黑人| 精品美女永久免费视频| 亚洲欧美中文字幕在线一区| 欧美日韩福利视频| 国产精品r级在线| 亚洲欧洲在线播放| 国产精品一香蕉国产线看观看| 日本久久91av| 国产精品专区一| 深夜福利91大全| 色偷偷噜噜噜亚洲男人| 亚洲视频欧洲视频| 欧美激情一区二区三区成人| 青青草99啪国产免费| 久久久久久久影院| 欧美精品videossex88| 性欧美xxxx| 亚洲欧美制服丝袜| 蜜臀久久99精品久久久久久宅男| 九九热最新视频//这里只有精品| 91丝袜美腿美女视频网站| 国产精品久久在线观看| 26uuu另类亚洲欧美日本老年| 国模叶桐国产精品一区| 成人性教育视频在线观看| 欧美成人免费全部观看天天性色| 91精品久久久久久久久久| 国产日韩在线免费| 日韩在线观看免费网站| 日韩免费不卡av| 精品久久久久久电影| 成人欧美一区二区三区在线湿哒哒| 欧美一级淫片丝袜脚交| 欧美影院久久久| 亚洲大胆美女视频| 国内精品久久久久影院优| 亚洲国产天堂网精品网站| 精品一区二区三区电影| 欧美猛交免费看| 国产成人精品久久久| 综合国产在线视频| 性金发美女69hd大尺寸| 亚洲日本aⅴ片在线观看香蕉| 日韩免费在线看| 亚洲aaa激情| 社区色欧美激情 | 日韩欧美中文字幕在线播放| 国产精品女人久久久久久| 4438全国亚洲精品在线观看视频| 亚洲综合最新在线| 国产精品视频26uuu| 亚洲欧美国产精品va在线观看| 国产欧美在线观看| 欧美日韩亚洲视频| 青青草精品毛片| 中文字幕精品一区久久久久| 欧美性生交大片免网| 欧美精品在线免费播放| 中文字幕久热精品视频在线| 国产一区红桃视频| 国产在线观看精品| 国产精品男女猛烈高潮激情| 国产精品第一视频| 成人激情视频小说免费下载| 麻豆国产精品va在线观看不卡| 欧美性受xxx| 国产成人亚洲综合91精品| 大量国产精品视频| 欧美黄色性视频| 久久亚洲私人国产精品va| 久久久精品欧美| 久久久av一区| 国产成人久久精品| 日本精品视频在线播放| 欧美风情在线观看| 国产午夜精品全部视频在线播放| 51午夜精品视频| 亚洲国产精品va在线观看黑人| 26uuu日韩精品一区二区| 国产精品美女久久久久av超清| 亚洲人成77777在线观看网| 精品无码久久久久久国产| 国产主播喷水一区二区| 国产午夜精品美女视频明星a级| 午夜精品www| 色综合久久天天综线观看| 欧美激情一区二区三区在线视频观看| 久久精品亚洲94久久精品| 欧美精品午夜视频| 91精品久久久久久久久久久| 亚洲国产精品成人av| 日韩有码视频在线| 国产99久久精品一区二区 夜夜躁日日躁| 国产一区二中文字幕在线看| 国内精品久久久久影院优| 在线观看日韩视频| 欧美日韩国产区| 国产精品欧美久久久| 最近2019好看的中文字幕免费| 欧美xxxx18国产| 久久久久免费视频| 国产精品极品尤物在线观看| 日韩精品在线观看一区| 亚洲片在线观看| 欧美成人久久久| 国产精品自产拍在线观看中文| 91chinesevideo永久地址| 亚洲国产成人久久综合一区| 4k岛国日韩精品**专区| 亚洲精品一区中文| 色99之美女主播在线视频| www.久久色.com| 国产精品美女呻吟| 在线观看国产成人av片| 欧美性资源免费| 成人欧美在线观看| 亚洲第一区在线| 久久综合久久美利坚合众国| 欧美裸体男粗大视频在线观看| 精品久久久久久亚洲国产300| 欧美激情2020午夜免费观看| 亚洲xxxx在线| 久久中国妇女中文字幕| 亚洲精品国精品久久99热一| 国内精品久久久久伊人av| 精品国产一区二区三区久久狼5月| 欧美激情a∨在线视频播放| 精品成人乱色一区二区| 欧美成人午夜剧场免费观看| 亚洲天堂一区二区三区| 成人有码在线播放| 欧美裸体视频网站| 精品成人在线视频| 欧美在线www| 亚洲国产中文字幕在线观看| 最近更新的2019中文字幕| 亚洲欧美另类人妖| 国产ts一区二区| 中文字幕综合在线| 日韩av在线网址| 亚洲精品永久免费精品| 日本久久亚洲电影| 欧美成人一区二区三区电影| 日本成熟性欧美|