原文地址:https://doterlin.github.io/blog/2016/12/24/vue-pager/
所有源碼和示例在這里叉袍。
Demo演示請(qǐng)點(diǎn)這里墩瞳。
1.下載示例源碼
為了更好的理解代碼暖眼,建議通過(guò)以下方式將源碼下載下來(lái):
- 使用git下載(推薦):
git clone git@github.com:doterlin/vue-pagination.git
- 使用npm安裝:
npm i vue-pagination-bs
- 或者點(diǎn)擊下載zip
2.運(yùn)行
直接打開(kāi)indexl.html
3.修改代碼
組件是通過(guò)webpack
打包的,我們修改了代碼是需要輸入webpack
命令進(jìn)行編譯生成最終的js文件才能看到修改后效果。要使用webpack
命令需要在控制臺(tái)安裝好依賴:
npm install
等待依賴安裝需要一段時(shí)間瘾杭,我們可以先看看以下幾節(jié)理解代碼。
4.子組件
組件功能是vue.js
的一個(gè)重要部分哪亿,除了使用Vue.component(tagName, options)
方法注冊(cè)一個(gè)組件外粥烁,我們可以寫一個(gè).vue
文件,把組件的樣式(style)
蝇棉,邏輯(script)
讨阻,模板(template)
存放在一個(gè)獨(dú)立的文件,使得組件的維護(hù)更加容易篡殷,代碼耦合度更小钝吮。但是瀏覽器是無(wú)法識(shí)別.vue
文件的,我們需要借助webpack
或者browserify
這樣的工具把文件打包成可運(yùn)行的js文件就ok了。關(guān)于如何使用webpack
配置vue
可參考官方示例webpack-simple奇瘦。
如果你還不熟悉webpack
棘催,你可以暫時(shí)使用本例子的配置(webpack.config.js
)不進(jìn)行重新不配置。
以下是代碼耳标,代碼的解說(shuō)放在注釋里面醇坝,方便查看:
//pagination.vue bootstrap風(fēng)格分頁(yè)組件
//author: doterlin
//2016-12-16
<template>
<div class="pagination-wrap" v-cloak v-if="totalPage!=0">
<ul class="pagination">
<li :class="currentPage==1?'disabled':''"><a href="javascript:;" @click="turnToPage(1)">首頁(yè)</a></li>
<li :class="currentPage==1?'disabled':''"><a @click="turnToPage(currentPage-1)" href="javascript:;">上一頁(yè)</a></li>
<li><a href="javascript:;" @click="turnToPage(currentPage-3)" v-text="currentPage-3" v-if="currentPage-3>0"></a></li>
<li><a href="javascript:;" @click="turnToPage(currentPage-2)" v-text="currentPage-2" v-if="currentPage-2>0"></a></li>
<li><a href="javascript:;" @click="turnToPage(currentPage-1)" v-text="currentPage-1" v-if="currentPage-1>0"></a></li>
<li class="active"><a href="javascript:;" @click="turnToPage(currentPage)" v-text="currentPage">3</a></li>
<li><a href="javascript:;" @click="turnToPage(currentPage+1)" v-text="currentPage+1" v-if="currentPage+1<totalPage"></a></li>
<li><a href="javascript:;" @click="turnToPage(currentPage+2)" v-text="currentPage+2" v-if="currentPage+2<totalPage"></a></li>
<li><a href="javascript:;" @click="turnToPage(currentPage+3)" v-text="currentPage+3" v-if="currentPage+3<totalPage"></a></li>
<li :class="currentPage==totalPage?'disabled':''"><a href="javascript:;" @click="turnToPage(currentPage+1)" >下一頁(yè)</a></li>
<li :class="currentPage==totalPage?'disabled':''"><a href="javascript:;" @click="turnToPage(totalPage)">尾頁(yè)</a></li>
</ul>
<small class="small nowrap"> 當(dāng)前第 <span class="text-primary" v-text="currentPage"></span> 頁(yè),共有 <span class="text-primary" v-text="totalPage"></span> 頁(yè)</small>
<div class="go">
<div :class="isPageNumberError?'input-group error':'input-group'">
<input class="form-control" type="number" v-model="goToPage"><a href="javascript:;" class="input-group-addon" @click="turnToPage(goToPage)">Go</a>
</div>
</div>
</div>
</template>
<script type="text/javascript">
export default {
props: {
//傳入總頁(yè)數(shù)次坡,默認(rèn)100
totalPage: {
type: Number,
default: 1,
required: true,
validator(value) {
return value >= 0
}
},
//傳入當(dāng)前頁(yè)呼猪,默認(rèn)1
currentPage:{
type: Number,
default: 1,
validator(value) {
return value >= 0
}
},
//傳入頁(yè)面改變時(shí)的回調(diào),用于更新你的數(shù)據(jù)
//回調(diào)默認(rèn)是打印當(dāng)前頁(yè)
//請(qǐng)根據(jù)需要在傳入的回調(diào)函數(shù)里更改函數(shù)體
changeCallback: {
type: Function,
default(cPage) {
console.log("默認(rèn)回調(diào)贸毕,顯示頁(yè)碼:" + cPage);
}
}
},
data(){
return {
myCurrentPage : 1,
isPageNumberError: false
}
},
computed:{
// prop不應(yīng)該在組件內(nèi)部做改變
// 所以我們這里設(shè)置一個(gè)內(nèi)部計(jì)算屬性myCurrentPage來(lái)代替props中的currentPage
// 為什么要這么做郑叠?參考:https://cn.vuejs.org/v2/guide/components.html#單向數(shù)據(jù)流
currentPage(){
return this.myCurrentPage;
}
},
methods:{
//turnToPage為跳轉(zhuǎn)到某頁(yè)
//傳入?yún)?shù)pageNum為要跳轉(zhuǎn)的頁(yè)數(shù)
turnToPage( pageNum ){
var ts = this;
var pageNum = parseInt(pageNum);
//頁(yè)數(shù)不合法則退出
if (!pageNum || pageNum > ts.totalPage || pageNum < 1) {
console.log('頁(yè)碼輸入有誤!');
ts.isPageNumberError = true;
return false;
}else{
ts.isPageNumberError = false;
}
//更新當(dāng)前頁(yè)
ts.myCurrentPage = pageNum;
//頁(yè)數(shù)變化時(shí)的回調(diào)
ts.changeCallback(pageNum);
}
}
}
</script>
<style type="text/css">
.pagination-wrap{
margin: 0 auto;
text-align: center;
}
.pagination {
display: inline-block;
padding-left: 0;
margin: 20px 0;
border-radius: 4px;
}
.small {
margin: 0 10px;
position: relative;
top: -32px;
}
.nowrap {
white-space: nowrap;
}
.input-group {
position: relative;
display: table;
border-collapse: separate;
}
.input-group-addon {
padding: 6px 12px;
font-size: 14px;
font-weight: 400;
line-height: 1;
color: #555;
text-align: center;
background-color: #eee;
border: 1px solid #ccc;
border-radius: 0 4px 4px 0;
}
.input-group-addon, .input-group-btn {
width: 1%;
white-space: nowrap;
vertical-align: middle;
}
.input-group-addon, .input-group-btn, .input-group .form-control {
box-sizing: border-box;
display: table-cell;
}
.input-group .form-control:first-child, .input-group-addon:first-child, .input-group-btn:first-child>.btn, .input-group-btn:first-child>.btn-group>.btn, .input-group-btn:first-child>.dropdown-toggle, .input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle), .input-group-btn:last-child>.btn-group:not(:last-child)>.btn {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.input-group-addon, .input-group-btn, .input-group .form-control {
display: table-cell;
}
.input-group .form-control {
position: relative;
z-index: 2;
float: left;
width: 100%;
margin-bottom: 0;
}
.go .error .form-control{
border: 1px solid #d95656;
}
.form-control {
display: block;
width: 100%;
height: 34px;
padding: 6px 12px;
font-size: 14px;
line-height: 1.42857143;
color: #555;
background-color: #fff;
background-image: none;
border: 1px solid #ccc;
border-radius: 4px;
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
-webkit-transition: border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;
-o-transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s;
transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s;
}
.text-primary {
color: #428bca;
}
.pagination>li:first-child>a, .pagination>li:first-child>span {
margin-left: 0;
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
}
.go {
display: inline-block;
max-width: 140px;
top: -21px;
position: relative;
}
.input-group-addon:last-child {
display: table-cell;
text-decoration: none;
border-left: 0;
}
.pagination>.disabled>span, .pagination>.disabled>span:hover, .pagination>.disabled>span:focus, .pagination>.disabled>a, .pagination>.disabled>a:hover, .pagination>.disabled>a:focus {
color: #777;
cursor: not-allowed;
background-color: #fff;
border-color: #ddd;
}
.pagination>li:last-child>a, .pagination>li:last-child>span {
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
}
.pagination>.active>a, .pagination>.active>span, .pagination>.active>a:hover, .pagination>.active>span:hover, .pagination>.active>a:focus, .pagination>.active>span:focus {
z-index: 2;
color: #fff;
cursor: default;
background-color: #428bca;
border-color: #428bca;
}
.pagination>li>a, .pagination>li>span {
position: relative;
float: left;
padding: 6px 12px;
margin-left: -1px;
line-height: 1.42857143;
color: #428bca;
text-decoration: none;
background-color: #fff;
border: 1px solid #ddd;
}
.pagination>li {
display: inline;
}
</style>
5.使用組件
寫好了分頁(yè)組件之后明棍,就可以引入了乡革。我把一個(gè)簡(jiǎn)單應(yīng)用結(jié)構(gòu)的分析成下圖:
當(dāng)然,一般應(yīng)用都不會(huì)只是一層父子關(guān)系摊腋,而是幾層甚至更多沸版。所以實(shí)際的應(yīng)用的結(jié)構(gòu)應(yīng)該如下圖所示:
現(xiàn)在我們要做的就是寫一個(gè)最大單元組件用于規(guī)劃app,比如我們只做了存放分頁(yè)組件兴蒸,并對(duì)分頁(yè)組件進(jìn)行初始化(傳入總頁(yè)數(shù)视粮,當(dāng)前頁(yè)數(shù),頁(yè)碼變化回調(diào)):
//App.vue
//這是父組件示例
//author: doterlin
//2016-12-16
<template>
<div id="app">
<h1 class="title" v-text="msg"></h1>
<pagination :totalPage="parentTotalPage" :currentPage="parentCurrentpage" :changeCallback="parentCallback"></pagination>
</div>
</template>
<script>
//引入pagination組件
import pagination from './pagination.vue';
export default {
name: 'app',
data () {
return {
msg : "Update your data here.",
//在data中初始化總頁(yè)數(shù)totalPage橙凳,和當(dāng)前頁(yè)currentPage
parentTotalPage: 100,
parentCurrentpage: 1
}
},
//使用pagination組件
components: { pagination },
methods:{
//在這里傳入pagination的跳轉(zhuǎn)頁(yè)碼回調(diào)方法
//cPage參數(shù)是已跳轉(zhuǎn)的當(dāng)前頁(yè)碼
parentCallback( cPage ) {
//這里是頁(yè)碼變化后要做的事
this.msg = 'Update your data here. Page: ' + cPage;
}
}
}
</script>
<style>
body,html{margin:0;padding:0;}
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
.title{
margin: 200px 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
}
a {
color: #42b983;
}
</style>
這樣的話蕾殴,如果有除了分頁(yè)組件之外的其他組件,我們的編碼入口也很明確是app.vue
岛啸。
6.應(yīng)用入口
我們把所有的組件放在根目錄下的component
目錄下钓觉,用于存放所有組件。就此應(yīng)用的組件已經(jīng)布置完成坚踩,然后需要寫一個(gè)vue實(shí)例用于渲染我們的組件大佬(app.vue
):
import Vue from 'vue'
import App from '../component/app.vue'
//一個(gè)vue實(shí)例
var app = new Vue({
data: {},
el: '#app',
render: h => h(App),
mounted() {
}
})
7.編譯
輸入命令并回車:
npm run build
當(dāng)webpack
提示了打包成功信息后就會(huì)發(fā)現(xiàn)多個(gè)一個(gè)文件./dist/build.js
荡灾,這就是編譯后的代碼。
有了可運(yùn)行的js
瞬铸,我們就可以寫一個(gè)html
進(jìn)行引入(剛才在./src/app.js
需要渲染的元素是id="app"
):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no" name="viewport">
<title>vue-pagination-bs</title>
</head>
<body>
<div id="app"></div>
<script src="./dist/build.js"></script>
</body>
</html>
最后運(yùn)行index.html
就可以把應(yīng)用跑起來(lái)了批幌。
總結(jié)
- 組件化是前端不可或缺的一部分。
- vue將前端組件化做的更加明朗了嗓节,我們需要做的不只是他如何進(jìn)行組織荧缘,更需要理解這種別樣的組件化途徑的思想。
- 實(shí)踐永遠(yuǎn)是檢驗(yàn)真理的唯一標(biāo)準(zhǔn)赦政,與其看著別人用了高大上的東西在裝逼胜宇,還不如自己去嘗試并裝逼耀怜。
- 做一個(gè)負(fù)責(zé)人的程序員和一個(gè)有專業(yè)精神的前端恢着。