您好,登錄后才能下訂單哦!
這篇文章主要講解了“如何使用Vue3開發一個Pagination公共組件”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“如何使用Vue3開發一個Pagination公共組件”吧!
我們采用的是測試驅動開發(TDD)開發的流程為
寫對應功能點的文檔
寫對應功能點的單元測試
跑測試案例,確保案例失敗
寫代碼實現
跑測試案例,確保案例成功
Vue3 的新特性
用 jest 測試,@vue/test-utils: 2.0.0-rc.6 測試 Vue 組件
jsx 語法
組織結構參考 vitepress 文檔
主要根據設計師給出的 UI 效果圖,再結合其他優秀的 UI 庫中的功能點,最后討論得到我們需要實現的效果,最后寫文檔。
行覆蓋率(line coverage):每個可執行代碼行是否都執行了? 函數覆蓋率(function coverage):每個函數是否都調用了? 分支覆蓋率(branch coverage):每個流程控制的各個分支是否都執行了? 語句覆蓋率(statement coverage):每個語句是否都執行了?
測試驅動開發要求測試用例編寫優先于單元功能的實現,這就需要先去思考組件該如何取拆分,拆分以后每個部分需要哪些功能。
對這些想象中的功能進行測試。但是在實際開發過程中發現功能沒有開發出來之前編寫測試用例很難去做到一個比較高的一個測試覆蓋率,只好在功能開發完成以后對測試進行一個補充。
如下是我在編寫功能之前給給的組織結構,功能實現在 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
其他部分的測試也類似
ShowQuickJumper 樹形為 true 的時候 jumper 相關功能才會展示,而判斷 jumper 是否展示可以通過渲染后的組件是否擁有的的特殊的 jumper 相關 class 來實現,@vue/test-utils 里面的 classes api 很好的實現這樣一個效果。
當 jumper 輸入的值不為數字、數字超出邊界、數字為 NaN 等問題的一個測試。
功能測試,當輸入完成,失去焦點的時候是否可以跳轉到相應的頁面。
測試都不能通過
測試覆蓋率不足 70%,可惜忘了截圖
如下圖,最終測試覆蓋率是100%
非常有必要,因為在追求 100% 的測試覆蓋率的時候我回去審查每一行代碼,看實際過程中是否每一行代碼都可以覆蓋到,而在這個過程中發現了一些冗余代碼,比如說我已經在 jumper.jsx 里面已經對回傳 pagination.jsx 中的數據進行了一個處理,確保里面不會產生非數字,且不會超出邊界,而又在 pagination.jsx 中處理了一遍, 這樣導致 pagination 里面的處理變得多余,而永遠不會執行。因為每一行,每一個分支都可以執行到,這樣也可以有效的減少潛在的 bug。
需要兼容切換風格、后期可能會調整字體顏色、按鈕大小、按鈕之間的距離等,開發的時候所有顏色、常規的一些距離等等都需要再公共文件里面取。這樣每一個小的樣式都需要思考是否必須,是否需要在庫里取,開發過程可能會比直接寫顏色等要慢一些。
所以可能帶有的 class 都要有 is 屬性,is-disabled、is-double-jumper 啥的
盡量不用元素選擇器,一個是效率低,一個是變更時候容易照成必須要的影響
jsx 里面使用 bind 綁定事件時候,如果里面的第一個參數沒有意義,直接設為 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, 比如實現 v-model
setup 函數接受兩個參數,一個是 props, 一個是 context
不能用 es6 解構,解構出來的數據會失去響應式,一般會使用 toRef 或者 toRefs 去處理
該參數包含 attrs、slot、emit,如果里面需要使用到某個 emit,則要在 emits 配置中聲明
import { definedComponent } from 'vue'; export default definedComponent({ emits: ['update:currentPage'], setup(props, { emit, slot, attrs }) { emit('update:currentPage'); ... } })
pageSize 和 currentPage 兩個屬性都需要實現 v-model。
vue 2 實現自定義組件一到多個v-model雙向數據綁定的方法
https://blog.csdn.net/Dobility/article/details/110147985
假如只需要實現 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 的好處,不需要使用 sync 加 computed 里面去觸發這種怪異的寫法,直接用 v-model 統一,更加簡便和易于維護。
實際組件中未使用,這里舉例說明
import { reactive } from 'vue'; // setup 中 const data = reactive({ count: 0 }) console.log(data.count); // 0
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 用于為源響應式對象上的屬性新建一個 ref,從而保持對其源對象屬性的響應式連接。接收兩個參數:源響應式對象和屬性名,返回一個 ref 數據,本質上是值的引用,改變了原始值也會改變
實際組件并未使用,下面是舉例說明
import { toRef } from 'vue'; export default { props: { totalCount: { type: number, default: 0 } }, setup(props) { const myTotalCount = toRef(props, totalCount); console.log(myTotalCount.value); } }
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 解構響應式對象;
如果想要復用是一個功能,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
感覺和 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公共組件”的內容了,經過本文的學習后,相信大家對如何使用Vue3開發一個Pagination公共組件這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。