本週遇到的問題

李明發表於2022-11-23

首先是又遇到了一些RXjs運算子,在這裡簡單的說一下。
首先是 combineLatest。
將其直接翻譯過來就是結合最新的(observeble),那麼結合一下下面這張圖片就很容易理解了。
image.png
combineLatest結合作為引數傳遞的所有 Observables 的值。這是透過按順序訂閱每個 Observable 並在任何 Observable 發出時從每個 Observable 收集最新值的陣列來完成的。因此,如果您將nObservable 傳遞給運算子,則返回的 Observable 將始終發出一個值陣列,n其順序與傳遞的 Observable 的順序相對應(第一個 Observable 的值在第一位,依此類推)。

讓我們再結合程式碼試一下

const firstTimer = Rx.Observable.timer(0, 1000); // emit 0, 1, 2... after every second, starting from now
const secondTimer = Rx.Observable.timer(500, 1000); // emit 0, 1, 2... after every second, starting 0,5s from now
const combinedTimers = Rx.Observable.combineLatest(firstTimer, secondTimer);
combinedTimers.subscribe(value => console.log(value));
// Logs
// [0, 0] after 0.5s
// [1, 0] after 1s
// [1, 1] after 1.5s
// [2, 1] after 2s

從此處我們也能更深刻地認識到,只有兩個observble都觸發時才會開始觸發combineLatest。

專案中 用到了狀態管理相關內容,下面來簡單地說一下什麼是狀態模式,之前也有聽老師講過狀態模式,但是由於沒有找到實際程式碼所以對狀態模式總是不理解。

當前專案的狀態模式可以體現為如下形式
未命名檔案 (2).jpg

相當於狀態模式就是代替了之前我們所用的服務層並且狀態模式的特殊之處個人認為就體現在store的使用,store實現了將資料作為快取直接儲存在前臺。
可能這麼說不是很直接,下面再加上程式碼來說明一下。
我們目前可以簡單地認為一個store只用來儲存/操作一種狀態,然後透過繼承store類並將其設定為注射器來實現:
比如我們想對building實體進行管理

@Injectable()
export class BuildingStore extends Store<Building>{
export const BuildingActions = {
    getUnit: 'getUnit'
}

static units(state: Unit) {
    return state.units;
}

// 新增action註解與actions進行對接,使其可以由dispatch呼叫
// 我們可以發現此方法中雖然返回了observable但是data並沒有返回,這是由於此方法將資料作為狀態的更新存進了快取(store)
@Action(taskActions.getUnit)
getUnit(state: Building, payload: number | string) {
    return this.http.get(xxxurl).pipe(
    .tap((data) => {
        // 獲取當前狀態
        const state = this.snapshot;
        // 獲取新狀態
        state.units = data.units;
        // 更新狀態
        this.next(state);
        
    })
}


}

然後我們可以在C層中這樣呼叫

@Injectable()
export class BuildingComponent extends {
    . . .
    // 獲取building對應的unit到快取中
    this.buildingStore
        .dispatch(BuildingActions.getUnit, 1)
        .subscribe(() => {
        console.log('已獲取units')        
    });

    // 在此元件中獲取units(去store中獲取)
    this.buildingStore.select(BuildingStore.units)
        .subscribe(data => {
            console.log("獲取到的單元:");
            console.log(units);
        })
    
}

此外值得一提的是如果我們照上面程式碼操作的話this.buildingStore.select會觸發兩次,第一次是舊資料,第二次是新資料。
猜測其過程為:
未命名檔案 (3).jpg

那麼如果我們要從store中獲取多種資料我們為了避免獲取到舊的資料我們就可以和文章前面說到的combineLatest結合使用,從而保持資料的更新並且便於使用。

然後再來說一下本週遇到的一個奇怪的bug:
先說一下問題:
當我們更改時間時會彈出一個彈窗,將彈窗關閉後修改時間沒有問題,但是點選右側X號清空時間雖然向後臺發出了請求但是前臺顯示沒有實時更新——仍然是顯示11月24,重新整理頁面後顯示為空。
image.png

遇到這個問題期初是認為專案自身就存在的bug,但是將彈窗操作去除後發現一切正常,後來想是不是因為開啟彈窗後由於彈窗元件沒有正常銷燬導致的。

於是在其銷燬時進行打點並且在原元件中也進行打點判斷各個地方的執行順序有沒有受到影響,但是打點後觀察發現執行順序和各個方法沒有任何影響。並且銷燬時間也符合預期。

後來詢問老師後得知如果在子元件中改變了子元件的值也會發生這樣的問題。

比如我們將XXX物件傳給了彈窗元件,我們在彈窗元件中對其進行了更改就會發生問題,因為就想之前說到的那樣,物件之間的傳遞都是傳的地址,而angular並不推薦像這樣改變父元件中的物件。

後來我又嘗試既然直接傳遞物件有問題,那麼將要傳遞的物件深複製後將複製後的物件傳給彈窗元件應該就沒問題了。

嘗試之後發現還是不行,就算深複製後還是會有這樣的問題。

後來老師調查後發現問題還是出在了狀態模式中的store上。

比如我們還拿building舉例,我們在父元件中根據buildingId獲取了units,然後在子元件中再根據這個buildingId獲取units,這兩個地方都是獲取快取並進行操作就可能會出現問題,從而導致上面的問題。

要想更深入瞭解問題應該就需要進一步瞭解它快取的機制——快取是怎麼存的?父元件,子元件快取共享嗎?什麼時候清空快取?快取在什麼情況下會出現問題?
這些問題都需要在之後的專案中進一步瞭解。

相關文章