最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

React團(tuán)隊(duì)測(cè)試并發(fā)特性詳解

 更新時(shí)間:2022年08月23日 14:55:50   作者:魔術(shù)師卡頌  
這篇文章主要為大家介紹了React團(tuán)隊(duì)測(cè)試并發(fā)特性詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

引言

React18進(jìn)入大家視野已經(jīng)有一段時(shí)間了,不知道各位有沒(méi)有嘗試并發(fā)特性呢?

當(dāng)啟用并發(fā)特性后,React會(huì)從同步更新變?yōu)楫惒健?yōu)先級(jí)、可中斷的更新。

這也為編寫單元測(cè)試帶來(lái)了一些難度。

本文來(lái)聊聊React團(tuán)隊(duì)如何測(cè)試并發(fā)特性。

遇到的困境

主要有兩個(gè)問(wèn)題需要面對(duì)。

1. 如何表達(dá)渲染結(jié)果?

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

對(duì)于一些場(chǎng)景,可以用ReactDOM的輸出結(jié)果做測(cè)試。

比如,下面是使用ReactDOM的輸出結(jié)果測(cè)試無(wú)狀態(tài)組件的渲染結(jié)果是否符合預(yù)期(測(cè)試框架是jest):

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

這里有個(gè)不方便的地方 —— 這個(gè)用例依賴瀏覽器環(huán)境與DOM API(比如用到document.createElement)。

對(duì)于測(cè)試React內(nèi)部運(yùn)行機(jī)制這樣的場(chǎng)景,摻雜了宿主環(huán)境相關(guān)信息顯然會(huì)讓測(cè)試用例編寫起來(lái)更繁瑣。

2. 如何測(cè)試并發(fā)環(huán)境?

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

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

這是因?yàn)樵谛碌募軜?gòu)下,很多同步更新變成了并發(fā)更新,當(dāng)render執(zhí)行后,頁(yè)面還沒(méi)完成渲染。

要讓上述用例成功,最簡(jiǎn)單的修改方式是:

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

如何優(yōu)雅的應(yīng)對(duì)這種變化?

React的應(yīng)對(duì)策略

接下來(lái)我們來(lái)看React團(tuán)隊(duì)的應(yīng)對(duì)方式。

首先來(lái)看第一個(gè)問(wèn)題 —— 如何表達(dá)渲染結(jié)果?

既然ReactDOM渲染器對(duì)應(yīng)瀏覽器、Node環(huán)境,ReactNative渲染器對(duì)應(yīng)Native環(huán)境。

那能不能為測(cè)試內(nèi)部運(yùn)行流程專門開(kāi)發(fā)一個(gè)渲染器呢?

答案是肯定的。

這個(gè)渲染器叫React-Noop-Renderer

簡(jiǎn)單的說(shuō),這個(gè)渲染器會(huì)渲染出純JS對(duì)象。

實(shí)現(xiàn)一個(gè)渲染器

React內(nèi)部有個(gè)叫Reconciler的包,他會(huì)引用一些操作宿主環(huán)境的API

比如如下方法用于向容器中插入節(jié)點(diǎn):

function appendChildToContainer(child, container) {
	// 具體實(shí)現(xiàn)
}

對(duì)于瀏覽器環(huán)境(ReactDOM),使用appendChild方法實(shí)現(xiàn)即可:

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

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

React-Noop-Renderer中,與ReactDOM中的DOM節(jié)點(diǎn)對(duì)標(biāo)的是如下數(shù)據(jù)結(jié)構(gòu):

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

注意其中的children字段,用于保存子節(jié)點(diǎn)。

所以appendChildToContainer方法在React-Noop-Renderer中可以實(shí)現(xiàn)的很簡(jiǎn)單:

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

打包工具將Reconciler包與上述這類針對(duì)React-Noop的API打包起來(lái),就是React-Noop-Renderer包。

基于React-Noop-Renderer,可以完全脫離正常的宿主環(huán)境,測(cè)試Reconciler內(nèi)部的邏輯。

接下來(lái)來(lái)看第二個(gè)問(wèn)題。

如何測(cè)試并發(fā)環(huán)境?

并發(fā)特性再?gòu)?fù)雜,說(shuō)到底也只是各種異步執(zhí)行代碼的策略,最終執(zhí)行策略的API不外乎setTimeout、setInterval、Promise等。

jest中,可以模擬這些異步API,控制他們的執(zhí)行時(shí)機(jī)。

比如上面的異步代碼,在React中的測(cè)試用例會(huì)這么寫:

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

act方法來(lái)自jest-react包,他的內(nèi)部會(huì)執(zhí)行jest.runOnlyPendingTimers方法,讓所有等待中的計(jì)時(shí)器觸發(fā)回調(diào)。

比如如下代碼:

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

執(zhí)行jest.runOnlyPendingTimers后會(huì)立刻打印執(zhí)行。

通過(guò)這種方式,人為控制React并發(fā)更新的速度,同時(shí)對(duì)框架代碼0侵入。

除此之外,用于驅(qū)動(dòng)并發(fā)更新的Scheduler(調(diào)度器)模塊,本身也有一個(gè)針對(duì)測(cè)試的版本。

在這個(gè)版本中,開(kāi)發(fā)者可以手動(dòng)控制Scheduler的輸入、輸出。

比如,我想測(cè)試組件卸載時(shí)useEffect回調(diào)的執(zhí)行順序。

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

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 />);
});

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

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

總結(jié)

React中測(cè)試用例的編寫策略為:

  • 可以用ReactDOM測(cè)的用例,一般結(jié)合ReactDOMReactTestUtils(瀏覽器環(huán)境的輔助方法)完成
  • 需要把控中間過(guò)程的用例,使用Scheduler的測(cè)試包,用Scheduler.unstable_yieldValue記錄過(guò)程信息
  • 脫離宿主環(huán)境,單獨(dú)測(cè)試React內(nèi)部運(yùn)行流程的,使用React-Noop-Renderer
  • 測(cè)試并發(fā)下的場(chǎng)景,需要結(jié)合上述工具與jest-react一起使用

如果想深入學(xué)習(xí)下React中與測(cè)試相關(guān)的技巧,可以看下司徒正美老師的作品anu。

這是個(gè)類React框架,但能跑通800+的React用例。里面實(shí)現(xiàn)了ReactTestUtils、React-Noop-Renderer的簡(jiǎn)化版。

以上就是React團(tuán)隊(duì)測(cè)試并發(fā)特性詳解的詳細(xì)內(nèi)容,更多關(guān)于React團(tuán)隊(duì)測(cè)試并發(fā)特性的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • React操作真實(shí)DOM實(shí)現(xiàn)動(dòng)態(tài)吸底部的示例

    React操作真實(shí)DOM實(shí)現(xiàn)動(dòng)態(tài)吸底部的示例

    本篇文章主要介紹了React操作真實(shí)DOM實(shí)現(xiàn)動(dòng)態(tài)吸底部的示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-10-10
  • React-redux?中useSelector使用源碼分析

    React-redux?中useSelector使用源碼分析

    在一個(gè) action 被分發(fā)(dispatch) 后,useSelector() 默認(rèn)對(duì) select 函數(shù)的返回值進(jìn)行引用比較 ===,并且僅在返回值改變時(shí)觸發(fā)重渲染,,這篇文章主要介紹了React-redux?中useSelector使用,需要的朋友可以參考下
    2023-10-10
  • React實(shí)現(xiàn)反向代理和修改打包后的目錄

    React實(shí)現(xiàn)反向代理和修改打包后的目錄

    這篇文章主要介紹了React實(shí)現(xiàn)反向代理和修改打包后的目錄方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-07-07
  • React通過(guò)ref獲取子組件的數(shù)據(jù)和方法

    React通過(guò)ref獲取子組件的數(shù)據(jù)和方法

    這篇文章主要介紹了React如何通過(guò)ref獲取子組件的數(shù)據(jù)和方法,文中有詳細(xì)的總結(jié)內(nèi)容和代碼示例,具有一定的參考價(jià)值,需要的朋友可以參考下
    2023-10-10
  • React+Electron快速創(chuàng)建并打包成桌面應(yīng)用的實(shí)例代碼

    React+Electron快速創(chuàng)建并打包成桌面應(yīng)用的實(shí)例代碼

    這篇文章主要介紹了React+Electron快速創(chuàng)建并打包成桌面應(yīng)用,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-12-12
  • useEvent顯著降低Hooks負(fù)擔(dān)的原生Hook

    useEvent顯著降低Hooks負(fù)擔(dān)的原生Hook

    這篇文章主要為大家介紹了useEvent顯著降低Hooks負(fù)擔(dān)的原生Hook示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-07-07
  • React Navigation 使用中遇到的問(wèn)題小結(jié)

    React Navigation 使用中遇到的問(wèn)題小結(jié)

    本篇文章主要介紹了React Navigation 使用中遇到的問(wèn)題小結(jié),主要是安卓和iOS中相對(duì)不協(xié)調(diào)的地方,特此記錄,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-05-05
  • React高階組件使用教程詳解

    React高階組件使用教程詳解

    高階組件就是接受一個(gè)組件作為參數(shù)并返回一個(gè)新組件(功能增強(qiáng)的組件)的函數(shù)。這里需要注意高階組件是一個(gè)函數(shù),并不是組件,這一點(diǎn)一定要注意,本文給大家分享React 高階組件HOC使用小結(jié),一起看看吧
    2022-12-12
  • React-Native左右聯(lián)動(dòng)List的示例代碼

    React-Native左右聯(lián)動(dòng)List的示例代碼

    本篇文章主要介紹了React-Native左右聯(lián)動(dòng)List的示例代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-09-09
  • React Agent 自定義實(shí)現(xiàn)代碼

    React Agent 自定義實(shí)現(xiàn)代碼

    在使用langchain的ReactAgent遇到問(wèn)題后,作者嘗試自定義ReactAgent實(shí)現(xiàn),通過(guò)詳細(xì)分析langchain中的agent功能和問(wèn)題,結(jié)合React思想,作者設(shè)計(jì)了新的agent邏輯并在GitHub上分享了代碼,新的ReactAgent通過(guò)改進(jìn)prompt和工具調(diào)用邏輯,提升了任務(wù)執(zhí)行的效果和穩(wěn)定性
    2024-10-10

最新評(píng)論

安福县| 洪江市| 梓潼县| 石棉县| 西乌珠穆沁旗| 瓮安县| 德令哈市| 滦南县| 云安县| 湘乡市| 兴业县| 资溪县| 烟台市| 甘肃省| 武功县| 古丈县| 清徐县| 英吉沙县| 威宁| 鹤山市| 遵化市| 栖霞市| 道孚县| 无极县| 屏南县| 镇远县| 徐汇区| 奈曼旗| 洛宁县| 固始县| 德庆县| 扶余县| 河津市| 宁都县| 常州市| 华安县| 邯郸市| 曲松县| 南江县| 启东市| 颍上县|