搜尋
首頁php教程PHP开发Vue.js實現無限載入與分頁功能開發

本篇文章是一篇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 &#39;./components/List&#39;
 
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中加入了:


效果:

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

效果:


。 vue


目前我們主要的業務邏輯都是圍繞著資訊清單展開的,也就是我們所建立的List.vue。首先,我們需要取得目標數據,我選用了cnodejs.org社群的API作為範例進行寫。如果你也想用一個封裝好的ajax函式庫的話,應該這麼做:🎜🎜引入第三方JS函式庫🎜將目標JS函式庫檔案放在static資料夾中,例如我選擇的是reqwest.js,然後在index. html先引入。 🎜


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

externals: {
 &#39;reqwest&#39;: &#39;reqwest&#39;
}
   
这样我们在我们的项目中,就可以随时加载第三方库了。
import reqwest from &#39;reqwest&#39;

写个API接口
在这个例子中,我们只需要调用文章列表这一个接口,但是实际项目中,可能你需要调用很多接口,而这些接口又会在多个组件中被用到。那么调用接口的逻辑四散在各个组件中肯定是不好的,想象一下对方的url发生了变化,你就得在无数个组件中一个个检查是否要修改。

所以我在src文件夹中新建了一个api文件夹,用于存放各类API接口。当前例子中,要获取的是新闻列表,所以新建一个news.js文件:

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

   

这样我们就拥有了一个获取新闻列表的API:getList。

编写组件

我们用一个

    作为新闻列表,内部的每一个
  1. 就是一条新闻,其中包括标题、时间和作者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 &#39;../api/news&#39;
     
    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中的修改为:


    再为page在App.vue中添加一个初始值以及对应的方法next:

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

       

    这样我们就已经完成了加载更多的功能。

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

    改写为分页

    因为之前我们的思路非常清晰,代码结构也很明了,所以改写起来会非常简单,只需要将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 }}显示页数。

    GO NEXTCURRENT:{{page}}

    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? &#39;up-start&#39;:&#39;down-start&#39;">
     <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 &#39;vue&#39;
    import App from &#39;./App&#39;
    import VueRouter from &#39;VueRouter&#39;
     
    Vue.use(VueRouter)
     
    const router = new VueRouter()
    router.map({
     &#39;/page/:pageNum&#39;: {
     name: &#39;page&#39;,
     component: {}
     }
    })
     
    router.redirect({
     &#39;/&#39;: &#39;/page/1&#39;
    })
     
    router.beforeEach((transition) => {
     if (transition.to.path !== &#39;/page/0&#39;) {
     transition.next()
     } else {
     transition.abort()
     }
    })
     
    router.start(App, &#39;app&#39;)

       

    首先定义了一个名为page的具名路径。之后将所有目标路径为'/',也就是初始页的请求,重定向到'/page/1'上保证一致性。最后再在每次路由执行之前做一个判断,如果到了'/page/0'这样的非法路径上,就不执行transition.next()。

    根据之前的思路,在App.vue中,获取路由对象的参数值,赋值给page。同时给两个按钮添加对应的v-link。


陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

EditPlus 中文破解版

EditPlus 中文破解版

體積小,語法高亮,不支援程式碼提示功能

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

mPDF

mPDF

mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),

DVWA

DVWA

Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中