中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

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

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

React并發特性實例分析

發布時間:2022-08-23 16:21:04 來源:億速云 閱讀:151 作者:iii 欄目:開發技術

這篇文章主要介紹“React并發特性實例分析”,在日常操作中,相信很多人在React并發特性實例分析問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”React并發特性實例分析”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

    1. 如何表達渲染結果?

    React可以對接不同宿主環境的渲染器,大家最熟悉的渲染器想必是ReactDOM,用于對接瀏覽器與Node環境(SSR)。

    對于一些場景,可以用ReactDOM的輸出結果做測試。

    比如,下面是使用ReactDOM的輸出結果測試無狀態組件的渲染結果是否符合預期(測試框架是jest):

    	it('should render stateless component', () => {
    		const el = document.createElement('div');
    		ReactDOM.render(<FunctionComponent name="A" />, el);
    		expect(el.textContent).toBe('A');
    	});

    這里有個不方便的地方 &mdash;&mdash; 這個用例依賴瀏覽器環境與DOM API(比如用到document.createElement)。

    對于測試React內部運行機制這樣的場景,摻雜了宿主環境相關信息顯然會讓測試用例編寫起來更繁瑣。

    2. 如何測試并發環境?

    如果將上文的用例中ReactDOM.render改為ReactDOM.createRoot,那么用例就會失敗:

    // 之前
    ReactDOM.render(<FunctionComponent name="A" />, el);
    expect(el.textContent).toBe('A');
    // 之后
    ReactDOM.createRoot(el).render(<FunctionComponent name="A" />);
    expect(el.textContent).toBe('A');

    這是因為在新的架構下,很多同步更新變成了并發更新,當render執行后,頁面還沒完成渲染。

    要讓上述用例成功,最簡單的修改方式是:

    ReactDOM.createRoot(el).render(<FunctionComponent name="A" />);
    setTimeout(() => {
      // 異步獲取結果
      expect(el.textContent).toBe('A');
    })

    如何優雅的應對這種變化?

    React的應對策略

    接下來我們來看React團隊的應對方式。

    首先來看第一個問題 &mdash;&mdash; 如何表達渲染結果?

    既然ReactDOM渲染器對應瀏覽器、Node環境,ReactNative渲染器對應Native環境。

    那能不能為測試內部運行流程專門開發一個渲染器呢?

    答案是肯定的。

    這個渲染器叫React-Noop-Renderer

    簡單的說,這個渲染器會渲染出純JS對象。

    實現一個渲染器

    React內部有個叫Reconciler的包,他會引用一些操作宿主環境的API

    比如如下方法用于向容器中插入節點:

    function appendChildToContainer(child, container) {
    	// 具體實現
    }

    對于瀏覽器環境(ReactDOM),使用appendChild方法實現即可:

    function appendChildToContainer(child, container) {
    	// 使用appendChild方法
      container.appendChild(child);
    }

    打包工具(rollup)將Reconciler包與上述這類針對瀏覽器環境的API打包起來,就是ReactDOM包。

    React-Noop-Renderer中,與ReactDOM中的DOM節點對標的是如下數據結構:

    const instance = {
      id: instanceCounter++,
      type: type,
      children: [],
      parent: -1,
      props
    };

    注意其中的children字段,用于保存子節點。

    所以appendChildToContainer方法在React-Noop-Renderer中可以實現的很簡單:

    function appendChildToContainer(child, container) {
    	const index = container.children.indexOf(child);
    	if (index !== -1) {
    		container.children.splice(index, 1);
    	}
    	container.children.push(child);
    };

    打包工具將Reconciler包與上述這類針對React-Noop的API打包起來,就是React-Noop-Renderer包。

    基于React-Noop-Renderer,可以完全脫離正常的宿主環境,測試Reconciler內部的邏輯。

    接下來來看第二個問題。

    如何測試并發環境?

    并發特性再復雜,說到底也只是各種異步執行代碼的策略,最終執行策略的API不外乎setTimeoutsetIntervalPromise等。

    jest中,可以模擬這些異步API,控制他們的執行時機。

    比如上面的異步代碼,在React中的測試用例會這么寫:

    // 測試用例修改后:
    await act(() => {
      ReactDOM.createRoot(el).render(<FunctionComponent name="A" />);
    })
    expect(el.textContent).toBe('A');

    act方法來自jest-react包,他的內部會執行jest.runOnlyPendingTimers方法,讓所有等待中的計時器觸發回調。

    比如如下代碼:

    setTimeout(() => {
      console.log('執行')
    }, 9999999)

    執行jest.runOnlyPendingTimers后會立刻打印執行。

    通過這種方式,人為控制React并發更新的速度,同時對框架代碼0侵入。

    除此之外,用于驅動并發更新的Scheduler(調度器)模塊,本身也有一個針對測試的版本。

    在這個版本中,開發者可以手動控制Scheduler的輸入、輸出。

    比如,我想測試組件卸載時useEffect回調的執行順序。

    如下面代碼所示,其中Parent為掛載的被測試組件:

    function Parent() {
      useEffect(() => {
        return () => Scheduler.unstable_yieldValue('Unmount parent');
      });
      return <Child />;
    }
    function Child() {
      useEffect(() => {
        return () => Scheduler.unstable_yieldValue('Unmount child');
      });
      return 'Child';
    }
    await act(async () => {
      root.render(<Parent />);
    });

    根據yieldValue的插入順序是否符合預期,就能確定useEffect的邏輯是否符合預期:

    expect(Scheduler).toHaveYielded(['Unmount parent', 'Unmount child']);

    到此,關于“React并發特性實例分析”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    连南| 嘉荫县| 大理市| 咸丰县| 那坡县| 辰溪县| 苍梧县| 望城县| 专栏| 丘北县| 禹城市| 台南县| 会东县| 山西省| 扬州市| 古丈县| 定陶县| 中山市| 桑日县| 北川| 新巴尔虎右旗| 汉寿县| 玉林市| 即墨市| 偃师市| 肥东县| 木里| 隆昌县| 洪雅县| 松溪县| 长乐市| 望都县| 清远市| 南乐县| 迁西县| 商洛市| 巨野县| 内丘县| 巴东县| 聂荣县| 兰西县|