溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

如何使用Vue3開發(fā)一個Pagination公共組件

發(fā)布時間:2021-11-20 08:48:40 來源:億速云 閱讀:161 作者:iii 欄目:編程語言

這篇文章主要講解了“如何使用Vue3開發(fā)一個Pagination公共組件”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“如何使用Vue3開發(fā)一個Pagination公共組件”吧!

如何使用Vue3開發(fā)一個Pagination公共組件

要實現(xiàn)的功能

屬性

如何使用Vue3開發(fā)一個Pagination公共組件

事件

如何使用Vue3開發(fā)一個Pagination公共組件

實現(xiàn)后的效果

如何使用Vue3開發(fā)一個Pagination公共組件

理論開發(fā)流程

我們采用的是測試驅(qū)動開發(fā)(TDD)開發(fā)的流程為

  • 寫對應(yīng)功能點的文檔

  • 寫對應(yīng)功能點的單元測試

  • 跑測試案例,確保案例失敗

  • 寫代碼實現(xiàn)

  • 跑測試案例,確保案例成功

實際開發(fā)過程

開發(fā)之前需要掌握的知識點

  • Vue3 的新特性

  • 用 jest 測試,@vue/test-utils: 2.0.0-rc.6 測試 Vue 組件

  • jsx 語法

組織結(jié)構(gòu)

項目組織結(jié)構(gòu)

組織結(jié)構(gòu)參考 vitepress 文檔

寫對應(yīng)功能點的文檔

主要根據(jù)設(shè)計師給出的 UI 效果圖,再結(jié)合其他優(yōu)秀的 UI 庫中的功能點,最后討論得到我們需要實現(xiàn)的效果,最后寫文檔。

測試用例編寫

測試覆蓋率的4個指標(biāo)

行覆蓋率(line coverage):每個可執(zhí)行代碼行是否都執(zhí)行了?
函數(shù)覆蓋率(function coverage):每個函數(shù)是否都調(diào)用了?
分支覆蓋率(branch coverage):每個流程控制的各個分支是否都執(zhí)行了?
語句覆蓋率(statement coverage):每個語句是否都執(zhí)行了?

如何編寫測試用例

  • 測試驅(qū)動開發(fā)要求測試用例編寫優(yōu)先于單元功能的實現(xiàn),這就需要先去思考組件該如何取拆分,拆分以后每個部分需要哪些功能。

  • 對這些想象中的功能進行測試。但是在實際開發(fā)過程中發(fā)現(xiàn)功能沒有開發(fā)出來之前編寫測試用例很難去做到一個比較高的一個測試覆蓋率,只好在功能開發(fā)完成以后對測試進行一個補充。

pagination 組件的結(jié)構(gòu)

如下是我在編寫功能之前給給的組織結(jié)構(gòu),功能實現(xiàn)在 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 功能編寫測試用例舉例

其他部分的測試也類似

  • ShowQuickJumper 樹形為 true 的時候 jumper 相關(guān)功能才會展示,而判斷 jumper 是否展示可以通過渲染后的組件是否擁有的的特殊的 jumper 相關(guān) class 來實現(xiàn),@vue/test-utils 里面的 classes api 很好的實現(xiàn)這樣一個效果。

  • 當(dāng) jumper 輸入的值不為數(shù)字、數(shù)字超出邊界、數(shù)字為 NaN 等問題的一個測試。

  • 功能測試,當(dāng)輸入完成,失去焦點的時候是否可以跳轉(zhuǎn)到相應(yīng)的頁面。

達(dá)到的測試覆蓋率

功能完成之前

測試都不能通過

功能完成之后

測試覆蓋率不足 70%,可惜忘了截圖

測試用例補充之后

如下圖,最終測試覆蓋率是100%

如何使用Vue3開發(fā)一個Pagination公共組件

關(guān)于測試的總結(jié)

追求 100% 的測試覆蓋率是否有必要呢?

非常有必要,因為在追求 100% 的測試覆蓋率的時候我回去審查每一行代碼,看實際過程中是否每一行代碼都可以覆蓋到,而在這個過程中發(fā)現(xiàn)了一些冗余代碼,比如說我已經(jīng)在 jumper.jsx 里面已經(jīng)對回傳 pagination.jsx 中的數(shù)據(jù)進行了一個處理,確保里面不會產(chǎn)生非數(shù)字,且不會超出邊界,而又在 pagination.jsx 中處理了一遍, 這樣導(dǎo)致 pagination 里面的處理變得多余,而永遠(yuǎn)不會執(zhí)行。因為每一行,每一個分支都可以執(zhí)行到,這樣也可以有效的減少潛在的 bug。

功能實現(xiàn)過程遇到的問題

樣式規(guī)范

  • 需要兼容切換風(fēng)格、后期可能會調(diào)整字體顏色、按鈕大小、按鈕之間的距離等,開發(fā)的時候所有顏色、常規(guī)的一些距離等等都需要再公共文件里面取。這樣每一個小的樣式都需要思考是否必須,是否需要在庫里取,開發(fā)過程可能會比直接寫顏色等要慢一些。

  • 所以可能帶有的 class 都要有 is 屬性,is-disabled、is-double-jumper 啥的

  • 盡量不用元素選擇器,一個是效率低,一個是變更時候容易照成必須要的影響

js 規(guī)范

  • jsx 里面使用 bind 綁定事件時候,如果里面的第一個參數(shù)沒有意義,直接設(shè)為 null 即可,handleXxx.bind(null, aaa)

  • jsx 語句盡量格式化,簡短一點

// 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>
)
  • 功能的功能盡量封裝成 hooks, 比如實現(xiàn) v-model

vue3 新特性的實際使用

setup 參數(shù)

setup 函數(shù)接受兩個參數(shù),一個是 props, 一個是 context

props 參數(shù)

不能用 es6 解構(gòu),解構(gòu)出來的數(shù)據(jù)會失去響應(yīng)式,一般會使用 toRef 或者 toRefs 去處理

context 參數(shù)

該參數(shù)包含 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 兩個屬性都需要實現(xiàn) v-model。

vue2 實現(xiàn)雙向綁定的方式

vue 2 實現(xiàn)自定義組件一到多個v-model雙向數(shù)據(jù)綁定的方法

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

vue3 實現(xiàn)雙向綁定的方式
實現(xiàn)單個數(shù)據(jù)的綁定

假如只需要實現(xiàn) currentPage 雙向綁定, vue3 默認(rèn)綁定的是 modelValue 這個屬性

// 父組件使用時候
<Pagination v-model="currentPage" />

// 相當(dāng)于
<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 }) {
        當(dāng) Pagaintion 組件改變的時候,去觸發(fā) update:currentPage 就行
        emit('update:currentPage', newCurrentPage)
    }
})
實現(xiàn)多個數(shù)據(jù)的綁定

pageSize 和 currentPage 兩個屬性都需要實現(xiàn) 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 }) {
        當(dāng) Pagaintion 組件改變的時候,去觸發(fā)相應(yīng)的事件就行
        emit('update:currentPage', newCurrentPage)
        emit('update:pageSize', newPageSize)
    }
})
Vue3 和 Vue2 v-model 比較

對于綁定單個屬性來說感覺方便程度區(qū)別不大,而綁定多個值可以明顯感覺 Vue3 的好處,不需要使用 sync 加 computed 里面去觸發(fā)這種怪異的寫法,直接用 v-model 統(tǒng)一,更加簡便和易于維護。

reactive ref toRef toRefs 的實際使用

用 reactive 為對象添加響應(yīng)式

實際組件中未使用,這里舉例說明

import { reactive } from 'vue';

// setup 中
const data = reactive({
    count: 0
})
console.log(data.count); // 0
用 ref 給值類型數(shù)據(jù)添加響應(yīng)式

jumper 文件中用來給用戶輸入的數(shù)據(jù)添加響應(yīng)式

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

當(dāng)然,其實用 ref 給對象添加響應(yīng)式也是可以的,但是每次使用的時候都需要帶上一個value, 比如,變成 data.value.count 這樣使用, 可以 ref 返回的數(shù)據(jù)是一個帶有 value 屬性的對象,本質(zhì)是數(shù)據(jù)的拷貝,已經(jīng)和原始數(shù)據(jù)沒有關(guān)系,改變 ref 返回的值,也不再影響原始數(shù)據(jù)

import { ref } from 'vue';

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

toRef

  • toRef 用于為源響應(yīng)式對象上的屬性新建一個 ref,從而保持對其源對象屬性的響應(yīng)式連接。接收兩個參數(shù):源響應(yīng)式對象和屬性名,返回一個 ref 數(shù)據(jù),本質(zhì)上是值的引用,改變了原始值也會改變

  • 實際組件并未使用,下面是舉例說明

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 用于將響應(yīng)式對象轉(zhuǎn)換為結(jié)果對象,其中結(jié)果對象的每個屬性都是指向原始對象相應(yīng)屬性的 ref。常用于es6的解構(gòu)賦值操作,因為在對一個響應(yīng)式對象直接解構(gòu)時解構(gòu)后的數(shù)據(jù)將不再有響應(yīng)式,而使用 toRefs 可以方便解決這一問題。本質(zhì)上是值的引用,改變了原始值也會改變

// setup 中
const {
    small, pageSizeOption, totalCount, simple, showSizeChanger, showQuickJumper, showTotal,
} = toRefs(props);

// 這樣就可以把里面所有的 props '解構(gòu)'出來
console.log(small.value)

由于 props 是不能用 es6 解構(gòu)的,所以必須用 toRefs 處理

四種給數(shù)據(jù)添加響應(yīng)式的區(qū)別
是否是原始值的引用

reactive、toRef、toRefs 處理返回的對象是原始對象的引用,響應(yīng)式對象改變,原始對象也會改變,ref 則是原始對象的拷貝,和原始對象已經(jīng)沒有關(guān)系。

如何取值

ref、toRef、toRefs 返回的響應(yīng)式對象都需要加上 value, 而 reactive 是不需要的

作用在什么數(shù)據(jù)上

reactive 為普通對象;ref 值類型數(shù)據(jù);toRef 響應(yīng)式對象,目的為取出某個屬性; toRefs 解構(gòu)響應(yīng)式對象;

用 vue3 hooks 代替 mixins

如果想要復(fù)用是一個功能,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 的來源。復(fù)用起來很簡單快捷,pager、simpler 等頁面的 v-model 都可以用上 這個 hook

computed、watch

感覺和 Vue2 中用法類似,不同點在于 Vue3 中使用的時候需要引入。 舉例 watch 用法由于 currentPage 改變時候需要觸發(fā) change 事件,所以需要使用到 watch 功能

import { watch } from 'vue';

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

感謝各位的閱讀,以上就是“如何使用Vue3開發(fā)一個Pagination公共組件”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對如何使用Vue3開發(fā)一個Pagination公共組件這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關(guān)知識點的文章,歡迎關(guān)注!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI