手把手教你使用Vue/React/Angular三大框架开发Pagination分页组件(上)丨【WEB前端大作战】
DevUI是一支兼具设计视角和工程视角的团队,服务于华为云DevCloud平台和华为内部数个中后台系统,服务于设计师和前端工程师。
官方网站:devui.design
Ng组件库:ng-devui(欢迎Star)
引言
“他在正午、黄昏,在一天里的许多时刻去感受它、记录它,结果也就让我们看到了那么多的不同。他描绘它的角度没变,但它的面目却极大地改变了。”
19世纪著名的印象派画家莫奈,喜欢对着同一处景物,分别画出对象在不同时间,不同光线下的色彩变化。
比如不同季节的三株白杨:
比如一天中不同时刻的浮翁大教堂:
如果同一个组件,用不同的框架实现,会有什么不同呢?
带着这个想法,我分别选用目前最火的Vue/React/Angular三大框架,去实现一个简单的Pagination分页组件。
1 组件需求
我们要实现的分页组件大致效果如下:
主要包含以下功能:
- 点击左右分页按钮可以跳转到上一页/下一页;
- 点击中间的页码按钮可能跳转到相应的页码;
- 首页尾页需要始终显示出来(如果只有1页则不显示尾页);
- 除首尾页之外,当前页码左右最多只显示2页(共5页);
- 页码太多时显示更多页码按钮,点击更多页码按钮跳转5页。
2 模块设计
从设计稿可以看出,Pagination组件主要由2个模块组成:
- Button - 左右分页按钮
- Pager - 中间的分页器
3 空的Pagination组件
我们采用自上而下的方式创建组件,先创建一个空的Pagination组件。
注意⚠️
我使用的框架版本号如下:
node@10.15.1
vue-cli@3.7.0
vue@2.6.10
create-react-app@3.0.1
react@16.8.6
angular-cli@7.3.9
angular@7.2.0
3.1 Vue版本
使用Vue CLI创建一个基础Vue项目,并输入npm run serve命令启动起来。
然后在components文件夹新建一个pagination文件夹,里面新建我们需要的3个组件文件:
- 按钮组件 - Button.vue
- 分页器组件 - Pager.vue
- 分页组件 - Pagination.vue
在Pagination.vue文件中增加以下代码:
<template>
<div class="x-pagination">
Pagination组件
</div>
</template>
<script>
export default {
name: 'Pagination',
};
</script>
Vue组件的特点是将HTML/CSS/JavaScript都统一放在一个.vue后缀的文件中。
对于习惯将HTML/CSS/JavaScript分开编写的前端开发者来说,显得非常自然,加上Vue的语法非常简洁,入门门槛比较低,所以2014年一经推出,很快便席卷全球。
在views/Home.vue中使用Pagination组件:
<template>
<div class="home">
<img alt="Vue logo" src="../assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js App by kagol"/>
<Pagination />
</div>
</template>
<script>
// @ is an alias to /src
import HelloWorld from '@/components/HelloWorld.vue';
import Pagination from '@/components/pagination/Pagination.vue';
export default {
name: 'home',
components: {
HelloWorld,
Pagination,
},
};
</script>
组件的使用方式也和普通HTML元素很类似:
<Pagination />
需要注意的是使用Vue局部组件之前需要在components中声明该组件。
这只是一个空组件,只显示了“Pagination组件”文字,没有太大的意义,不过不要着急,后面我们会一步步完善该组件,实现我们想要的功能,并能不断扩展和演进。在继续开发Vue版本的Pagination组件之前,我们先来看看其他框架如何实现和使用一个组件。
以下是显示效果:
3.2 React版本
先来看看React框架,我们同样使用Create React App创建一个基础的React项目,并输入命令npm start命令启动。
和Vue项目一样,创建以下3个组件文件:
- 按钮组件 - Button.js
- 分页器组件 - Pager.js
- 分页组件 - Pagination.js
在Pagination.js文件中增加以下代码:
import React from 'react';
function Pagination() {
return (
<div className="x-pagination">
Pagination组件
</div>
);
}
export default Pagination;
可以看到React开发组件的方式和Vue相差非常大,React推崇函数式编程(FP,Functional Programming),每个React组件都是一个函数,HTML/CSS/JavaScript都在函数里面,在函数里面返回模板内容。
需要注意⚠️的是在React中HTML元素的class需要写成className,原因是class是JavaScript中的保留关键字,而React使用的JSX是JavaScript的扩展,使用class会导致命名冲突。
React这种写法很特别,初学者可能会不太习惯,不过一旦用习惯了,会觉得非常爽,觉得一切都非常合理,组件就应该这样写。
在App.js中使用Pagination组件:
import React from 'react';
import Pagination from './components/pagination/Pagination';
import './App.scss';
function App() {
return (
<div className="App">
<Pagination />
</div>
);
}
export default App;
使用React组件的方式也很简单,和使用普通HTML元素类似:
<Pagination />
显示的效果与Vue版本无异。
3.3 Angular版本
和Vue/React这种专注View视图层的轻量级框架不同,Angular是一个很重的框架,配备非常完整,Web开发过程中你需要的一切,Angular框架都给你提供好了,你只需要随手取用即可。
我们一起来看看怎么开发一个Angular组件吧。
同样是使用Angular CLI创建一个基础的Angular项目,并输入命令npm start命令启动。
和React/Vue组件不同,Angular组件不能单独使用,需要包一层Module,因此我们需要创建1个模块文件和3个组件文件:
- Pagination模块 - pagination.module.ts
- 按钮组件 - button.component.ts
- 分页器组件 - pager.component.ts
- 分页组件 - pagination.component.ts
HTML/CSS可以放在ts文件里面,也可以放在单独的文件里。
一般而言,HTML/CSS内容较少时,会将它们放到ts文件里。
先创建Pagination模块,在pagination.module.ts文件中增加以下代码:
import { NgModule } from "@angular/core";
@NgModule()
export class PaginationModule { }
然后是创建Pagination组件,在pagination.component.ts文件中增加以下代码:
import { Component } from "@angular/core";
@Component({
selector: 'x-pagination',
template: `
<div class="x-pagination">
Pagination组件
</div>
`,
})
export class PaginationComponent { }
Angular和Vue/React非常明显的区别已经显示出来:
首先是组件需要依托于Module存在;
然后是不管是定义Module还是Component都需要使用装饰器;
比如定义一个Angular模块需要使用@NgModule装饰器,定义一个Angular组件需要使用@Component装饰器。
还有就是Angular推崇的是面向对象的编程范式,Angular里面的几乎一切都是类和对象,除了刚才一经介绍的模块和组件,还有服务(Service)、管道(Pipe)等,都是类(class)。
为了使用Pagination组件,我们需要先导入Pagination模块,并声明Pagination组件,在app.module.ts文件中增加以下代码:
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { PaginationModule } from './components/pagination/pagination.module';
import { PaginationComponent } from './components/pagination/pagination.component';
@NgModule({
declarations: [
AppComponent,
PaginationComponent, // 声明Pagination组件
],
imports: [
BrowserModule,
PaginationModule, // 导入Pagination模块
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule
然后就能使用Pagination组件了,在app.component.ts文件中增加以下代码:
<div style="text-align:center">
<h1>
Welcome to {{ title }}!
</h1>
<img width="300" alt="Angular Logo" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERDAwMzEiIGQ9Ik0xMjUgMzBMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjMuMXoiIC8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJGIiBkPSJNMTI1IDMwdjIyLjItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0aCAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQwLjl6IiAvPgogIDwvc3ZnPg==">
</div>
<x-pagination></x-pagination>
使用Angular组件的方式和普通的HTML元素类似:
<x-pagination></x-pagination>
显示的效果与Vue/React一样。
4 List组件和假数据
在添加实际的分页功能之前我们需要先做一个List组件,用来模拟分页数据的展示。
根据我们之前介绍的3个框架实现组件的方式,然后稍微增加些额外的知识,我们就能很快做一个数据渲染组件List。
还是先看Vue框架吧。
4.1 Vue版本
新建List.vue组件文件,输入以下代码:
<template>
<ul>
<li v-for="list in lists" :key="list.id">
{{ list.name }}
</li>
</ul>
</template>
<script>
export default {
name: 'List',
props: {
dataSource: Array
},
data() {
return {
lists: this.dataSource
}
},
watch: {
// 对dataSource进行监听,如果发生变化则重新将新值赋给lists
dataSource: {
handler(newValue, oldValue) {
this.lists = newValue;
}
}
}
};
</script>
在template模板部分,我们使用Vue的v-for指令,在li元素中循环lists数组,并将name值显示出来。其中的:key是v-bind:key的简写形式,为元素绑定唯一的key值,用于DOM对比时的性能优化。
1) 通过props传入数据
原本我打算直接将lists的值放到props中,通过外部传进来,如下:
<template>
<ul>
<li v-for="list in lists" :key="list.id">
{{ list.name }}
</li>
</ul>
</template>
<script>
export default {
name: 'List',
props: {
lists: Array
}
};
</script>
这样有一个问题,就是外部传入的lists如果发生变化,template里绑定的lists不会相应的变化。
2) 维护内部状态
为了监听props中的值的变化,我把lists放到组件内部状态中(data),外部传入的数据叫dataSource,如下:
<script>
export default {
name: 'List',
props: {
dataSource: Array
},
data() {
return {
lists: this.dataSource
}
},
};
</script>
3) 监听外部props的变化
然后监听dataSource的变化,当dataSource变化时,将新值赋值给lists:
watch: {
// 对dataSource进行监听,如果发生变化则重新将新值赋给lists
dataSource: {
handler(newValue, oldValue) {
this.lists = newValue;
}
}
}
传入List组件的lists数组如下:
export const lists = [
{ id: 1, name: 'Curtis' },
{ id: 2, name: 'Cutler' },
{ id: 3, name: 'Cynthia' },
{ id: 4, name: 'Cyril' },
{ id: 5, name: 'Cyrus' },
{ id: 6, name: 'Dagmar' },
{ id: 7, name: 'Dahl' },
{ id: 8, name: 'Dahlia' },
{ id: 9, name: 'Dailey' },
{ id: 10, name: 'Daine' },
];
使用List组件展示数据:
<List :data-source="lists" />
这里需要注意⚠️的是,所有绑定的数据需要使用短横线命名法,比如上面的data-source,对应data中驼峰命名法的dataSource。
展示的效果如下:
4.2 React版本
React编写的是函数组件,props的变化会直接反映到模板中,不需要单独监听,所以写起来非常简洁:
import React from 'react';
function List({ dataSource }) {
return (
<ul className="m-list">
{
dataSource.map(list => {
return <li key={ list.id }>{ list.name }</li>;
})
}
</ul>
);
}
export default List
外部数据通过函数的props参数传入,这里将props进行了对象解构,直接取到了dataSource字段。
还有一点和Vue不太一样,就是React是函数式编程的写法,列表数据的渲染不需要v-for之类的指令,而是通过数组的map方法,直接返回相应的li元素即可,看着非常自然。其中li元素上绑定的key值与Vue中key值的作用类似。
使用方式和Vue的类似:
<List dataSource={dataSource} />
4.3 Angular版本
Angular稍微麻烦些,需要同时定义Module和Component:
- List模块 - list.module.ts
- List组件:list.component.ts
先编写list.module.ts:
import { NgModule } from "@angular/core";
@NgModule()
export class ListModule { }
然后编写List组件list.component.ts:
import { Component, Input } from "@angular/core";
@Component({
selector: 'x-list',
template: `
<ul>
<li *ngFor="let list of dataSource; trackBy: trackByIndex">
{{ list.name }}
</li>
</ul>
`,
})
export class ListComponent {
@Input() dataSource;
trackByIndex(index, list){
return list.id;
}
}
Angular和Vue/React的差别比较大:
- 一是外部传参方式不同,Angular使用@Input这个装饰器表示外部参数;
- 二是Angular使用ngFor指令渲染列表数据;
- 三是Angular优化DOM对比的方式是使用trackBy。
Angular组件的使用方式,倒是和其他框架大同小异:
<x-list [dataSource]="dataSource"></x-list>
下篇我们将继续实现基本分页功能和最核心的分页器组件,尽情期待!
附上3大框架的Pagination组件源码地址:
https://github.com/kagol/components
本文参考DevUI分页组件写成,该组件源码地址:
https://github.com/DevCloudFE/ng-devui/tree/master/devui/pagination
.
手把手教你使用Vue/React/Angular三大框架开发Pagination分页组件系列文章:
手把手教你使用Vue/React/Angular三大框架开发Pagination分页组件(中)丨【WEB前端大作战】
手把手教你使用Vue/React/Angular三大框架开发Pagination分页组件(下)丨【WEB前端大作战】
.
欢迎大家关注DevUI组件库,给我们提意见和建议,也欢迎Star。
- 点赞
- 收藏
- 关注作者
评论(0)