• 技术文章 >web前端 >Vue.js

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

    青灯夜游青灯夜游2021-11-19 19:50:12转载208
    怎么使用Vue3开发一个Pagination公共组件?下面本篇文章给大家介绍一下基于Vue3 封装一个 Pagination 公共组件的方法,希望对大家有所帮助!

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

    要实现的功能

    属性

    1.png

    事件

    2.png

    实现后的效果

    3.png

    理论开发流程

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

    实际开发过程

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

    组织结构

    项目组织结构

    组织结构参考 vitepress 文档

    写对应功能点的文档

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

    测试用例编写

    测试覆盖率的4个指标

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

    如何编写测试用例

    pagination 组件的结构

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

    - _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 功能编写测试用例举例

    其他部分的测试也类似

    达到的测试覆盖率

    功能完成之前

    测试都不能通过

    功能完成之后

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

    测试用例补充之后

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

    4.png

    关于测试的总结

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

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

    功能实现过程遇到的问题

    样式规范

    js 规范

    // setup 里面
    // 不好的写法
    return (
        <div>
            {simple.value ? <Simple xxx /> : <Pager xxx/>}
        <div>
    )
    
    // 好的写法
    const renderPage = () => {
        if (simple.value) {
            return <Simele xxx/>;
        }
        return <Pager xxx/>
    }
    return (
        <div>
            {renderPage()}
        </div>
    )

    vue3 新特性的实际使用

    setup 参数

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

    props 参数

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

    context 参数

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

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

    v-model 使用

    pageSize 和 currentPage 两个属性都需要实现 v-model。

    vue2 实现双向绑定的方式

    vue 2 实现自定义组件一到多个v-model双向数据绑定的方法

    https://blog.csdn.net/Dobility/article/details/110147985

    vue3 实现双向绑定的方式

    实现单个数据的绑定

    假如只需要实现 currentPage 双向绑定, vue3 默认绑定的是 modelValue 这个属性

    // 父组件使用时候
    <Pagination v-model="currentPage" />
    
    // 相当于
    <Pagination :modelValue="currentPage" @update:modelValue="(val) => {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)
        }
    })
    实现多个数据的绑定

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

    // 父组件使用时候
    <Pagination v-model:currentPage="currentPage" v-model:pageSize="pageSize" />
    
    // 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)
        }
    })

    Vue3 和 Vue2 v-model 比较

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

    reactive ref toRef toRefs 的实际使用

    用 reactive 为对象添加响应式

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

    import { reactive } from 'vue';
    
    // setup 中
    const data = reactive({
        count: 0
    })
    console.log(data.count); // 0

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

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

    import {
        defineComponent, ref,
    } from 'vue';
    export default defineComponent({
        setup(props) {
            const current = ref('');
               return () => (
                <div>
                    <FInput v-model={current.value}></FInput>
                </div>
            );
        },
    });

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

    import { ref } from 'vue';
    
    // setup 中
    const data = ref({
        count: 0
    })
    console.log(data.value.count); // 0

    toRef

    import { toRef } from 'vue';
    export default {
        props: {
            totalCount: {
                type: number,
                default: 0
            }
        },
        setup(props) {
            const myTotalCount = toRef(props, totalCount);
            console.log(myTotalCount.value); 
        }
    }

    toRefs

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

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

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

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

    是否是原始值的引用

    reactive、toRef、toRefs 处理返回的对象是原始对象的引用,响应式对象改变,原始对象也会改变,ref 则是原始对象的拷贝,和原始对象已经没有关系。

    如何取值

    ref、toRef、toRefs 返回的响应式对象都需要加上 value, 而 reactive 是不需要的

    作用在什么数据上

    reactive 为普通对象;ref 值类型数据;toRef 响应式对象,目的为取出某个属性; toRefs 解构响应式对象;

    用 vue3 hooks 代替 mixins

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

    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',
    });

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

    computed、watch

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

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

    更多编程相关知识,请访问:编程入门!!

    以上就是浅析怎么使用Vue3开发一个Pagination公共组件的详细内容,更多请关注php中文网其它相关文章!

    声明:本文转载于:掘金社区,如有侵犯,请联系admin@php.cn删除
    上一篇:看看Vue中如何封装一个自动化注册全局组件 下一篇:Vue+ElementUI怎么处理超大表单

    相关文章推荐

    • vue路由学习之区分$route和$router,看看它们的区别• 什么是vitepress?怎么将vuepress升为vitepress?• 总结分享一些关于vue的前端基础面试题• 深入浅析Vue中的模板语法:插值和指令• 看看Vue中如何封装一个自动化注册全局组件

    全部评论我要评论

  • 取消发布评论发送
  • 1/1

    PHP中文网