浅析怎么使用Vue3开发一个Pagination公共组件

怎么使用vue3开发一个pagination公共组件?下面本篇文章给大家介绍一下基于vue3 封装一个 pagination 公共组件的方法,希望对大家有所帮助!

浅析怎么使用Vue3开发一个Pagination公共组件

最近有幸参与了我们部门基于 vue3 的公共组件库的开发,记录一下 vue3 实际使用的过程的一些经验,以及开发公共组件注意的一些问题。【相关推荐:《vue.js教程》】

要实现的功能

属性

浅析怎么使用Vue3开发一个Pagination公共组件

事件

浅析怎么使用Vue3开发一个Pagination公共组件

立即学习“前端免费学习笔记(深入)”;

实现后的效果

浅析怎么使用Vue3开发一个Pagination公共组件

理论开发流程

我们采用的是测试驱动开发(TDD)开发的流程为

写对应功能点的文档

写对应功能点的单元测试

跑测试案例,确保案例失败

写代码实现

跑测试案例,确保案例成功

实际开发过程

开发之前需要掌握的知识点

Vue3 的新特性

用 jest 测试,vue.js教程 测试 Vue 组件

jsx 语法

组织结构

项目组织结构

组织结构参考 vue.js教程 文档

写对应功能点的文档

主要根据设计师给出的 UI 效果图,再结合其他优秀的 UI 库中的功能点,最后讨论得到我们需要实现的效果,最后写文档。

测试用例编写

测试覆盖率的4个指标

  1. 行覆盖率(line coverage):每个可执行代码行是否都执行了?函数覆盖率(function coverage):每个函数是否都调用了?分支覆盖率(branch coverage):每个流程控制的各个分支是否都执行了?语句覆盖率(statement coverage):每个语句是否都执行了?

登录后复制

如何编写测试用例

测试驱动开发要求测试用例编写优先于单元功能的实现,这就需要先去思考组件该如何取拆分,拆分以后每个部分需要哪些功能。

对这些想象中的功能进行测试。但是在实际开发过程中发现功能没有开发出来之前编写测试用例很难去做到一个比较高的一个测试覆盖率,只好在功能开发完成以后对测试进行一个补充。

pagination 组件的结构

如下是我在编写功能之前给给的组织结构,功能实现在 jumper、pager、pagination、simpler、sizes、total 等 jsx 文件

  1. - _tests_    - pagination.js- style    - index.js    - index.less    - mixin.less- const.js- index.js- jumper.jsx- pager.jsx- pagination.jsx- simpler.jxs- sizes.jsx- total.jsx

登录后复制

对 jumper 功能编写测试用例举例

其他部分的测试也类似

ShowQuickJumper 树形为 true 的时候 jumper 相关功能才会展示,而判断 jumper 是否展示可以通过渲染后的组件是否拥有的的特殊的 jumper 相关 class 来实现,@vue/test-utils 里面的 classes api 很好的实现这样一个效果。

当 jumper 输入的值不为数字、数字超出边界、数字为 NaN 等问题的一个测试。

功能测试,当输入完成,失去焦点的时候是否可以跳转到相应的页面。

达到的测试覆盖率

功能完成之前

测试都不能通过

功能完成之后

测试覆盖率不足 70%,可惜忘了截图

测试用例补充之后

如下图,最终测试覆盖率是100%

浅析怎么使用Vue3开发一个Pagination公共组件

关于测试的总结

追求 100% 的测试覆盖率是否有必要呢?

非常有必要,因为在追求 100% 的测试覆盖率的时候我回去审查每一行代码,看实际过程中是否每一行代码都可以覆盖到,而在这个过程中发现了一些冗余代码,比如说我已经在 jumper.jsx 里面已经对回传 pagination.jsx 中的数据进行了一个处理,确保里面不会产生非数字,且不会超出边界,而又在 pagination.jsx 中处理了一遍, 这样导致 pagination 里面的处理变得多余,而永远不会执行。因为每一行,每一个分支都可以执行到,这样也可以有效的减少潜在的 bug。

功能实现过程遇到的问题

样式规范

需要兼容切换风格、后期可能会调整字体颜色、按钮大小、按钮之间的距离等,开发的时候所有颜色、常规的一些距离等等都需要再公共文件里面取。这样每一个小的样式都需要思考是否必须,是否需要在库里取,开发过程可能会比直接写颜色等要慢一些。

所以可能带有的 class 都要有 is 属性,is-disabled、is-double-jumper 啥的

尽量不用元素选择器,一个是效率低,一个是变更时候容易照成必须要的影响

js 规范

jsx 里面使用 bind 绑定事件时候,如果里面的第一个参数没有意义,直接设为 null 即可,handleXxx.bind(null, aaa)

jsx 语句尽量格式化,简短一点

  1. // setup 里面// 不好的写法return (    
            {simple.value ?  : }    
    )// 好的写法const renderPage = () => {    if (simple.value) {        return ;    }    return }return (    
            {renderPage()}    
    )
    • 功能的功能尽量封装成 hooks, 比如实现 v-model

    vue3 新特性的实际使用

    setup 参数

    setup 函数接受两个参数,一个是 props, 一个是 context

    props 参数

    不能用 es6 解构,解构出来的数据会失去响应式,一般会使用 toRef 或者 toRefs 去处理

    context 参数

    该参数包含 attrsslotemit,如果里面需要使用到某个 emit,则要在 emits 配置中声明

    import { definedComponent } from 'vue';export default definedComponent({    emits: ['update:currentPage'],    setup(props, { emit, slot, attrs }) {        emit('update:currentPage');        ...    }})
  2. 登录后复制

  3. v-model 使用

  4. pageSize currentPage 两个属性都需要实现 v-model

  5. vue2 实现双向绑定的方式

  6. vue 2 实现自定义组件一到多个v-model双向数据绑定的方法https://blog.csdn.net/Dobility/article/details/110147985

  7. vue3 实现双向绑定的方式

  8. 实现单个数据的绑定
  9. 假如只需要实现 currentPage 双向绑定, vue3 默认绑定的是 modelValue 这个属性

  10. // 父组件使用时候// 相当于 {currentPage = val}" />// Pagination 组件import { defineComponent } from vue;export default defineComponent({    props: {        currentPage: {            type: Number,            default: 1        }    }    emits: ['update:currentPage'],    setup(props, { emit }) {        当 Pagaintion 组件改变的时候,去触发 update:currentPage 就行        emit('update:currentPage', newCurrentPage)    }})
  11. 登录后复制

  12. 实现多个数据的绑定
  13. pageSize currentPage 两个属性都需要实现 v-model

  14. // 父组件使用时候// Pagination 组件import { defineComponent } from vue;export default defineComponent({    pageSize: {        type: Number,        default: 10,    },    currentPage: {        type: Number,        default: 1,    },    emits: ['update:currentPage', 'update:pageSize'],    setup(props, { emit }) {        当 Pagaintion 组件改变的时候,去触发相应的事件就行        emit('update:currentPage', newCurrentPage)        emit('update:pageSize', newPageSize)    }})
  15. 登录后复制

  16. Vue3 Vue2 v-model 比较

  17. 对于绑定单个属性来说感觉方便程度区别不大,而绑定多个值可以明显感觉 Vue3 的好处,不需要使用 sync computed 里面去触发这种怪异的写法,直接用 v-model 统一,更加简便和易于维护。

  18. reactive ref toRef toRefs 的实际使用

  19. reactive 为对象添加响应式

  20. 实际组件中未使用,这里举例说明

  21. import { reactive } from 'vue';// setup 中const data = reactive({    count: 0})console.log(data.count); // 0
  22. 登录后复制

  23. ref 给值类型数据添加响应式

  24. jumper 文件中用来给用户输入的数据添加响应式

  25. import {    defineComponent, ref,} from 'vue';export default defineComponent({    setup(props) {        const current = ref('');           return () => (            
                                
            );    },});
  26. 登录后复制

  27. 当然,其实用 ref 给对象添加响应式也是可以的,但是每次使用的时候都需要带上一个value, 比如,变成 data.value.count 这样使用, 可以 ref 返回的数据是一个带有 value 属性的对象,本质是数据的拷贝,已经和原始数据没有关系,改变 ref 返回的值,也不再影响原始数据

  28. import { ref } from 'vue';// setup 中const data = ref({    count: 0})console.log(data.value.count); // 0
  29. 登录后复制

  30. toRef

  31. toRef 用于为源响应式对象上的属性新建一个 ref,从而保持对其源对象属性的响应式连接。接收两个参数:源响应式对象和属性名,返回一个 ref 数据,本质上是值的引用,改变了原始值也会改变

  32. 实际组件并未使用,下面是举例说明

  33. import { toRef } from 'vue';export default {    props: {        totalCount: {            type: number,            default: 0        }    },    setup(props) {        const myTotalCount = toRef(props, totalCount);        console.log(myTotalCount.value);     }}
  34. 登录后复制

  35. toRefs

  36. toRefs 用于将响应式对象转换为结果对象,其中结果对象的每个属性都是指向原始对象相应属性的 ref。常用于es6的解构赋值操作,因为在对一个响应式对象直接解构时解构后的数据将不再有响应式,而使用 toRefs 可以方便解决这一问题。本质上是值的引用,改变了原始值也会改变

  37. // setup 中const {    small, pageSizeOption, totalCount, simple, showSizeChanger, showQuickJumper, showTotal,} = toRefs(props);// 这样就可以把里面所有的 props '解构'出来console.log(small.value)
  38. 登录后复制

  39. 由于 props 是不能用 es6 解构的,所以必须用 toRefs 处理

  40. 四种给数据添加响应式的区别

  41. 是否是原始值的引用
  42. reactivetoReftoRefs 处理返回的对象是原始对象的引用,响应式对象改变,原始对象也会改变,ref 则是原始对象的拷贝,和原始对象已经没有关系。

  43. 如何取值
  44. reftoReftoRefs 返回的响应式对象都需要加上 value, reactive 是不需要的

  45. 作用在什么数据上
  46. reactive 为普通对象;ref 值类型数据;toRef 响应式对象,目的为取出某个属性; toRefs 解构响应式对象;

  47. vue3 hooks 代替 mixins

  48. 如果想要复用是一个功能,vue2 可能会采用 mixins 的方法,mixins 有一个坏处,来源混乱,就是有多个 mixin 的时候,使用时不知道方法来源于哪一个 mixins。而 Vue3 hooks 可以很好解决这一个问题。我们把 v-model 写成一个 hook

  49. const useModel = (    props,    emit,    config = {        prop: 'modelValue',        isEqual: false,    },) => {    const usingProp = config?.prop ?? 'modelValue';    const currentValue = ref(props[usingProp]);    const updateCurrentValue = (value) => {        if (            value === currentValue.value            || (config.isEqual && isEqual(value, currentValue.value))        ) { return; }        currentValue.value = value;    };    watch(currentValue, () => {        emit(`update:${usingProp}`, currentValue.value);    });    watch(        () => props[usingProp],        (val) => {            updateCurrentValue(val);        },    );    return [currentValue, updateCurrentValue];};// 使用的时候import useModel from '.../xxx'// setup 中const [currentPage, updateCurrentPage] = useModel(props, emit, {    prop: 'currentPage',});
  50. 登录后复制

  51. 可以看到,我们可以清晰的看到 currentPage, updateCurrentPage 的来源。复用起来很简单快捷,pagersimpler 等页面的 v-model 都可以用上 这个 hook

  52. computedwatch

  53. 感觉和 Vue2 中用法类似,不同点在于 Vue3 中使用的时候需要引入。举例 watch 用法由于 currentPage 改变时候需要触发 change 事件,所以需要使用到 watch 功能

  54. import { watch } from 'vue';// setup 中const [currentPage, updateCurrentPage] = useModel(props, emit, {    prop: 'currentPage',});watch(currentPage, () => {    emit('change', currentPage.value);})
  55. 登录后复制

  56. 更多编程相关知识,请访问:vue.js教程!!

  57. 以上就是浅析怎么使用Vue3开发一个Pagination公共组件的详细内容,更多请关注【创想鸟】其它相关文章!

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。
编程技术

Vue+ElementUI怎么处理超大表单

2025-4-1 17:24:47

编程技术

看看Vue中如何封装一个自动化注册全局组件

2025-4-1 17:24:57

0 条回复 A文章作者 M管理员
欢迎您,新朋友,感谢参与互动!
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
私信列表
搜索