<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                重構是保持項目優秀的必經之路,在前面的小節中,我們發現分頁模塊在數據量大時表示的差強人意: ![image-20210522090115935](https://img.kancloud.cn/e1/ac/e1acb520c3475effd5d92c762184dede_1710x462.png) 上圖的總頁碼數為21,這明顯有些過長。本節我們嘗試將其其默認的最大頁碼數設置為7。 當我們解決此類問題時,首要的任務的是重現錯誤。在重現錯誤的過程中,包括了對錯誤產生原因的猜想,以及最終對該猜想的驗證(這個過程可能是不自覺的)。 ## 啟動分頁組件 我們來到分頁組件所在的位置:`src/app/clazz/page`,找到其對應的單元測試文件。并添加一個測試用例: ```typescript +++ b/first-app/src/app/clazz/page/page.component.spec.ts @@ -68,4 +68,7 @@ describe('PageComponent', () => { expect(navHtml.style.visibility).toEqual('visible'); }); + fit('將總頁碼的最大數量控制在7頁', () => { + + }); }); ``` 然后使用`ng t`來啟動單元測試。 ## 分析問題 想修正或完善一些功能時,我們往往使用的按數據流的方向進行逆向推導的方法。比如當前出現的問題是分頁數量過多,則應該先想看是直接生成html的V層的代碼是哪些: ```html <li *ngFor="let p of pages①" [ngClass]="{active: currentPage === p}" class="page-item"> <span class="page-link" (click)="onPage(p)">{{p + 1}}</span> </li> ``` 由上述代碼直接推導出,分頁數據過多的原因是由于①標注的`pages`數組中的元素過多引起的。 然后接著向前推導:`pages`變量的值是由C層傳過來的,則接下來應該找到`pages`變量在C層中的賦值情況: ```typescript pages: number[] = []; ① ?? currentPage = 0; @Input() set page(page: Page<any>) { this.inputPage = page; ④ ?? console.log('set page被調用'); console.log('當前頁', this.inputPage.number); console.log('總頁數', this.inputPage.totalPages); // 生成頁數數組 this.pages = []; ② ?? for (let i = 0; i < this.inputPage.totalPages; i++) { this.pages.push(i); ③ ?? } // 設置當前頁 this.currentPage = this.inputPage.number; } ``` 查閱C層的代碼(可以使用ctrl+f進行快速查找),發現對`pages`變量進行設置的地點有3處;第①處為初始化、第②處為初始化,此兩處操作會將`pages`清空,所以問題應該在第③處,即按照傳入的`inputPage.totalPages`的值進行遍歷,而`pages`最終的個數取決于`inputPage.totalPages`的值的大小。 按照逆向的理論繼續向上找`inputPage.totalPages`的值是由 ④決定的,而④正好是`Input()`調用,即父組件傳入。 所以最終得到以下結論:父組件向當前組件傳入`page`時,將按傳入的`page`對象上的`totalPages`的值的大小來初始化組件的頁碼。 ## 重現問題 那么如果想重現問題,則傳入一個具有較大的`totalPages`的`page`對象即可,我們在測試用例中構造這個對象: ```typescript +++ b/first-app/src/app/clazz/page/page.component.spec.ts @@ -69,6 +69,11 @@ describe('PageComponent', () => { }); fit('將總頁碼的最大數量控制在7頁', () => { - + component.page = { + number: 2, + size: 20, + totalPages: 20 + } as Page<any>; + fixture.autoDetectChanges(); }); }); ``` 問題成功被重現: ![image-20210607093427541](https://img.kancloud.cn/4a/b5/4ab56ec002309f298d8fcaacf20cf129_2500x302.png) ## 單元測試 在解決問題時,我們常常會“故此失彼”。所以在動手前,盡可能多的考慮一些情況是非常有必要的。我們在此給出幾種分頁情況: 第一種:當總頁數小于7時能夠準確顯示,比如:`1 2 3 [4] 5`、`1 [2] 3` 第二種:總頁數等于7時,比如:`1 2 3 4 [5] 6 7`、`1 2 3 4 5 6 [7]` 第三種:總頁數大于7頁,需要能夠正確的處理以下各種情況: 第1頁,共8頁:`[1] 2 3 4 5 6 7` 第3頁,共8頁:`1 2 [3] 4 5 6 7` 第4頁,共8頁:`1 2 3 [4] 5 6 7` 第10頁,共19頁:`7 8 9 [10] 11 12 13` 第15頁,共18頁:`12 13 14 [15] 16 17 18` 第16頁,共18頁:`12 13 14 15 [16] 17 18` 第18頁,共18頁:`12 13 14 15 16 17 [18]` ### 測試用例 為了避免不小心把哪個功能給遺漏掉或KILL掉,可以為每個小功能點建立一個測試用例,然后使用斷言的方法來對功能進行保證。在完善功能后可以統一的執行單元測試,當每個單元測試都順利通過時,就說明整個功能開發的沒有問題了。 #### 總頁碼小于7 `1 2 3 [4] 5`、`1 [2] 3` ```typescript fit('總頁碼小于7', () => { // 共5頁,當前第4頁 component.page = { number: 3, size: 20, totalPages: 5 } as Page<any>; fixture.detectChanges(); expect(component.pages.length).toBe(5); // 共3頁,當前第2頁 component.page = { number: 1, size: 20, totalPages: 3 } as Page<any>; fixture.detectChanges(); expect(component.pages.length).toBe(3); }); ``` 測試通過,說明當前代碼可以滿足此要求。無需對代碼進行改動。 #### 總頁數等于7時 `1 2 3 4 [5] 6 7`、`1 2 3 4 5 6 [7]` ```typescript fit('總頁碼等于7', () => { // 共7頁,當前第5頁 component.page = { number: 4, size: 20, totalPages: 7 } as Page<any>; fixture.detectChanges(); expect(component.pages.length).toBe(7); // 共7頁,當前第7頁 component.page = { number: 6, size: 20, totalPages: 7 } as Page<any>; fixture.detectChanges(); expect(component.pages.length).toBe(7); }); ``` 測試通過,說明當前代碼可以滿足此要求。無需對代碼進行改動。 #### 總頁數大于7頁 第1頁,共8頁:`[1] 2 3 4 5 6 7` ```typescript fit('總頁數大于7, 第1頁,共8頁', () => { // 共1頁,當前第8頁 component.page = { number: 0, size: 20, totalPages: 8 } as Page<any>; fixture.detectChanges(); // 共顯示7頁 expect(component.pages.length).toBe(7); // 頭是第1頁 expect(component.pages[0]).toBe(0); // 尾是第7頁 expect(component.pages.pop()).toBe(6); }); ``` 發生異常如下: ![image-20210607100555414](https://img.kancloud.cn/8b/d8/8bd86a4a7d634ac7076b889c0b4215b3_950x128.png) 這說明當前的代碼已經不能夠滿足當前需求了。 雖然單元測試報錯了,但我們并不著急下手寫代碼。這是由于只有當充分的了解所有的需求后,再上手寫代碼才是效率最高做無用功最少的。 第3頁,共8頁:`1 2 [3] 4 5 6 7` ```typescript fit('1 2 [3] 4 5 6 7', () => { // 共3頁,當前第8頁 component.page = { number: 2, size: 20, totalPages: 8 } as Page<any>; fixture.detectChanges(); // 共顯示7頁 expect(component.pages.length).toBe(7); // 頭是第1頁 expect(component.pages[0]).toBe(0); // 尾是第7頁 expect(component.pages.pop()).toBe(6); }); ``` 第4頁,共8頁:`1 2 3 [4] 5 6 7` ```typescript fit('1 2 3 [4] 5 6 7', () => { // 共4頁,當前第8頁 component.page = { number: 3, size: 20, totalPages: 8 } as Page<any>; fixture.detectChanges(); // 共顯示7頁 expect(component.pages.length).toBe(7); // 頭是第1頁 expect(component.pages[0]).toBe(0); // 尾是第7頁 expect(component.pages.pop()).toBe(6); }); ``` 第10頁,共18頁:`7 8 9 [10] 11 12 13` ```typescript fit('7 8 9 [10] 11 12 13`', () => { component.page = { number: 9, size: 20, totalPages: 18 } as Page<any>; fixture.detectChanges(); // 共顯示7頁 expect(component.pages.length).toBe(7); expect(component.pages[0]).toBe(6); expect(component.pages.pop()).toBe(12); }); ``` 第15頁,共18頁:`12 13 14 [15] 16 17 18` ```typescript fit('12 13 14 [15] 16 17 18', () => { component.page = { number: 14, size: 20, totalPages: 18 } as Page<any>; fixture.detectChanges(); // 共顯示7頁 expect(component.pages.length).toBe(7); expect(component.pages[0]).toBe(11); expect(component.pages.pop()).toBe(17); }); ``` 第16頁,共18頁:`12 13 14 15 [16] 17 18` ```typescript fit('12 13 14 15 [16] 17 18', () => { component.page = { number: 15, size: 20, totalPages: 18 } as Page<any>; fixture.detectChanges(); // 共顯示7頁 expect(component.pages.length).toBe(7); expect(component.pages[0]).toBe(11); expect(component.pages.pop()).toBe(17); }); ``` 第18頁,共18頁:`12 13 14 15 16 17 [18]` ```typescript fit('12 13 14 15 16 17 [18]', () => { component.page = { number: 17, size: 20, totalPages: 18 } as Page<any>; fixture.detectChanges(); // 共顯示7頁 expect(component.pages.length).toBe(7); expect(component.pages[0]).toBe(11); expect(component.pages.pop()).toBe(17); }); ``` ## 解決問題 所有的單元測試都寫完后,現在開始解決問題。這種先寫單元測試再寫功能代碼的方法被稱為:`TDD`,學名叫做測試驅動開發,即非常出名的 Test-Driven Development。 寫了這么多單元測試用例以后,我使用以下代碼來嘗試解決當前最大數量為7的問題。 > [info] 建議先自己寫寫,最后再參考教程中的實現代碼。相信教程中的代碼也不是最簡潔的,期待你給出更加簡潔的實現方案。 ```typescript +++ b/first-app/src/app/clazz/page/page.component.ts @@ -22,10 +22,33 @@ export class PageComponent implements OnInit { console.log('set page被調用'); console.log('當前頁', this.inputPage.number); console.log('總頁數', this.inputPage.totalPages); + // 初始化最大頁碼,起始頁碼 + let maxCount; + let begin; + + if (this.inputPage.totalPages > 7) { + // 大于7頁時,僅顯示7頁 + maxCount = 7; + + // 起始頁為當前頁-3.比如當前頁為10,則應該由7頁開始 + begin = this.inputPage.number - 3; + if (begin < 0) { + // 判斷是否越界,可以刪除下一行代碼查看錯誤的效果 + begin = 0; + } else if (begin > this.inputPage.totalPages - 7) { + // 判斷是否越界,可以刪除下一行代碼查看錯誤的效果 + begin = this.inputPage.totalPages - 7; + } + } else { + // 小于等于7頁時,使用原算法。頁碼數為總頁數,頁碼由0開始 + maxCount = this.inputPage.totalPages; + begin = 0; + } + // 生成頁數數組 this.pages = []; - for (let i = 0; i < this.inputPage.totalPages; i++) { - this.pages.push(i); + for (let i = 0; i < maxCount; i++, begin++) { + this.pages.push(begin); } ``` 最終所有的單元測試全部通過,說明滿足了所有的要求。 ![image-20210607103851567](https://img.kancloud.cn/bc/9a/bc9a6aae5296523d7421d0477e349949_1786x550.png) 最后移除所有的`fit`,看單元測試是否都成功通過。成功通過則說明我們當前的功能完善未對其它的組件造成影響。 ## 總結 本節中我們使用了`TDD`測試驅動開發的思想,先寫了單元測試用例,最后補功的功能代碼。這特別適用于某些輸入與輸出都比較簡單,但邏輯實現稍微復雜的方法。 其實所有的方法都不是萬能的,TDD的開發思想雖然好,但卻并不適合于新手。但如若一直把自己當前新手來看待,將可能永遠也用不到TDD的開發思想。所以我們建議是,在單元測試這里,看自己的能力能寫多少寫多少,在寫的過程中,如果感覺特別難就換一種方式。寫單元測試代碼時間應該不大于寫功能代碼的2倍,如果時間超過了2倍,則應該考慮減小單元測試的難度。對于是先書功能代碼還是單元測試代碼的問題,則應該:哪個容易寫哪個。 | 鏈接 | 名稱 | | ------------------------------------------------------------ | --------------- | | [https://github.com/mengyunzhi/angular11-guild/archive/step7.4.5.zip](https://github.com/mengyunzhi/angular11-guild/archive/step7.4.5.zip) | 本節源碼 | | [https://baike.baidu.com/item/TDD/9064369](https://baike.baidu.com/item/TDD/9064369) ---- 注意:它的視頻錯了 | TDD測試驅動開發 | | [https://zh.wikipedia.org/zh-hans/%E6%B5%8B%E8%AF%95%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91](https://zh.wikipedia.org/zh-hans/%E6%B5%8B%E8%AF%95%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91) | TDD測試驅動開發 |
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看