手把手教你使用Vue/React/Angular三大框架开发Pagination分页组件(上)


DevUI是一支兼具设计视角和工程视角的团队,服务于华为云DevCloud平台和华为内部数个中后台系统,服务于设计师和前端工程师。
官方网站:devui.design
Ng组件库:ng-devui(欢迎Star)

引言

“他在正午、黄昏,在一天里的许多时刻去感受它、记录它,结果也就让我们看到了那么多的不同。他描绘它的角度没变,但它的面目却极大地改变了。”

19世纪著名的印象派画家莫奈,喜欢对着同一处景物,分别画出对象在不同时间,不同光线下的色彩变化。

比如不同季节的三株白杨:

比如一天中不同时刻的浮翁大教堂:

如果同一个组件,用不同的框架实现,会有什么不同呢?

带着这个想法,我分别选用目前最火的Vue/React/Angular三大框架,去实现一个简单的Pagination分页组件。


1 组件需求

我们要实现的分页组件大致效果如下:

主要包含以下功能:

  1. 点击左右分页按钮可以跳转到上一页/下一页;
  2. 点击中间的页码按钮可能跳转到相应的页码;
  3. 首页尾页需要始终显示出来(如果只有1页则不显示尾页);
  4. 除首尾页之外,当前页码左右最多只显示2页(共5页);
  5. 页码太多时显示更多页码按钮,点击更多页码按钮跳转5页。

2 模块设计

从设计稿可以看出,Pagination组件主要由2个模块组成:

  1. Button - 左右分页按钮
  2. 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个组件文件:

  1. 按钮组件 - Button.vue
  2. 分页器组件 - Pager.vue
  3. 分页组件 - 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个组件文件:

  1. 按钮组件 - Button.js
  2. 分页器组件 - Pager.js
  3. 分页组件 - 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个组件文件:

  1. Pagination模块 - pagination.module.ts
  2. 按钮组件 - button.component.ts
  3. 分页器组件 - pager.component.ts
  4. 分页组件 - 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:

  1. List模块 - list.module.ts
  2. 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的差别比较大:

  1. 一是外部传参方式不同,Angular使用@Input这个装饰器表示外部参数;
  2. 二是Angular使用ngFor指令渲染列表数据;
  3. 三是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


欢迎大家关注DevUI组件库,给我们提意见和建议,也欢迎Star。


加入我们

我们是DevUI团队,欢迎来这里和我们一起打造优雅高效的人机设计/研发体系。招聘邮箱:muyang2@huawei.com。

文/DevUI Kagol


往期文章推荐

《2021 年最值得推荐的 7 个 Angular 前端组件库》

《在瀑布下用火焰烤饼:三步法助你快速定位网站性能问题》

《从零搭建一个灰度发布环境》


合智互联客户成功服务热线:400-1565-661

admin
admin管理员

上一篇:华为云薛浩:走进视频“新时代”
下一篇:【技术补给站】第8期:用好ModelArts+HiLens,轻松上手端云协同AI开发

留言评论

暂无留言
取消
扫码支持