无码人妻丰满熟妇奶水区码,麻豆一区区三,亚洲日韩欧美精品综合,亚洲无码地址

React 18發(fā)布,僅用400行代碼就能實現(xiàn)一個Mini-React(react 17)

CSDN 編者按】400行代碼在React 18中實現(xiàn)可中斷的異步更新的最小模型!

原文鏈接:https://betterpr

ogramming.pub/react-18-has-been-released-implement-mini-react-in-400-lines-of-code-837559761758

聲明:本文為 CSDN 翻譯,轉(zhuǎn)載請注明來源。

作者 | Zachary Lee 譯者 | 彭慧中

責編 | 屠敏

出品 | CSDN(ID:CSDNnews)

以下為譯文:

React v18已經(jīng)發(fā)布,它給我們帶來了許多特性,但最重要的特性是可中斷的異步更新,許多新的上層API都是通過它創(chuàng)建的??梢哉f,它是React v18的底層引擎。

本文將使用大約400行代碼帶你實現(xiàn)一個可以異步更新和可中斷更新的Mini-React,如下圖所示:

React 18發(fā)布,僅用400行代碼就能實現(xiàn)一個Mini-React(react 17)

我使用了React官方網(wǎng)站提供的tic-tac-toe教程示例(以下是鏈接:https://reactjs.org/tutorial/tutorial.html#what-are-we-building),可以看到它非常有效。此外,它目前支持函數(shù)組件和類組件,可以滿足開發(fā)者80%的需求!我也把它放在GitHub上(以下是鏈接:https://github.com/islizeqiang/mini-react),你也可以在本地復制它,并按照我的文章一步一步地調(diào)試。

這是我在閱讀了大量React的源代碼后創(chuàng)建的,在整體邏輯和函數(shù)命名上基本上和React一樣,如果你對React的內(nèi)部原理感興趣,這篇文章就是為你準備的!

React 18發(fā)布,僅用400行代碼就能實現(xiàn)一個Mini-React(react 17)

JSX和createEelement

我相信你對 React 中的 JSX 并不陌生。我們使用 JSX 來描述 DOM,它們最終會被 babel 轉(zhuǎn)換成 React 提供的 API。例如下面的代碼:

React 18發(fā)布,僅用400行代碼就能實現(xiàn)一個Mini-React(react 17)

你也可以自己在StackBlitz上試試(在終端輸入node transform-JSX.js):

// run `node transform-JSX.js` in the terminal
const babel = require(\'@babel/core\');const optionsObject = { presets: [\'@babel/preset-env\'], plugins: [[\'@babel/plugin-transform-react-jsx\']],};
const { code } = babel.transformSync( \'const element = <div id=\"test\"><h1>Hello</h1></div>\', optionsObject);
console.log(code);

你還可以在編譯好的字符串中加入更多的元素,再看看最終的結(jié)果,我在這里直接給出React.createElement提供的選項。

1.type:表示當前節(jié)點的類型,如上圖中的div。

2.config:表示當前元素節(jié)點上的屬性,如上圖中的{id: \”test\”}。

3.children:子元素,可以是多個、簡單的文本,也可以由React.createElement創(chuàng)建的子節(jié)點。

然后根據(jù)這個要求實現(xiàn)你自己的React.createElement,就像下面的代碼一樣,我們定義一個自定義的數(shù)據(jù)結(jié)構(gòu)。

React 18發(fā)布,僅用400行代碼就能實現(xiàn)一個Mini-React(react 17)

渲染

然后我們可以根據(jù)上面創(chuàng)建的數(shù)據(jù)結(jié)構(gòu)實現(xiàn)一個簡化版的渲染函數(shù),將JSX渲染到真實的DOM上。

下面的代碼演示將使用CodeSandbox,拖動左欄查看代碼,點擊上方的菜單按鈕查看目錄結(jié)構(gòu)。你也可以直接編輯,查看顯示的結(jié)果。

import React from \"./mini-react\";
const App = ( <div id=\"test\"> <h1>Hello</h1> </div>);
// eslint-disable-next-line react/no-deprecatedReact.render(App, document.getElementById(\"root\"));

所以你可以看到它工作,但現(xiàn)在它只渲染一次,不能與我們互動。

另外,請注意我們在這里使用react-scripts@3.4.4來幫助編譯JSX,API在以后的版本中已經(jīng)發(fā)生了變化,但是React.createElement在結(jié)束時仍然被調(diào)用。我提供的GitHub庫使用了Vite而不是react-scripts。

接下來,是React的核心纖程架構(gòu)和并發(fā)模式,這是從React 17開始提出的,主要是為了解決一旦完整的元素樹被遞歸,就無法終止的問題,這可能導致主線程長時間被阻塞,那些高優(yōu)先級的任務(比如那些用戶輸入或動畫等)無法及時處理。

所以在React源代碼中,工作被分解成小單元。一旦瀏覽器處于空閑狀態(tài),它將處理這些小的工作單元,然后將結(jié)果映射到實際的DOM,直到所有結(jié)果都被處理完。

requestIdleCallback是一個實驗性API,它在瀏覽器空閑時執(zhí)行回調(diào)。接下來,我們將使用這個API來簡單地實現(xiàn)這個功能。我將在最后給出React目前使用的調(diào)度程序包的模擬實現(xiàn)。

在開始編寫下一個代碼之前,我想再次介紹一下工作單元之間的連接。

React 18發(fā)布,僅用400行代碼就能實現(xiàn)一個Mini-React(react 17)

就像上面的圖片一樣,我們將像鏈表一樣創(chuàng)建每個纖程節(jié)點之間的連接,它們是:

1.child:父節(jié)點指向第一個子元素的指針。

2.return/parent:所有子元素都有一個指向父元素的指針。

3.sibling:從第一個子元素指向下一個同級元素。

所以現(xiàn)在你可以愉快地編寫代碼:

import React from \"./mini-react\";
const App = ( <div id=\"test\"> <h1>Hello</h1> </div>);
// eslint-disable-next-line react/no-deprecatedReact.render(App, document.getElementById(\"root\"));

盡管添加了這么多代碼,我們只是重構(gòu)了渲染邏輯。重構(gòu)后的調(diào)用順序為workLoop →performUnitOfWork→reconcileChildren。下面讓我來總結(jié)一下各個功能的作用:

1.workLoop:通過連續(xù)調(diào)用requestIdleCallback來獲得空閑時間。如果當前空閑且有單元任務要執(zhí)行,則執(zhí)行每個單元任務。

2.performUnitOfWork:執(zhí)行的特定單元任務。這是鏈表思想的體現(xiàn)。即一次只處理一個纖程節(jié)點,并返回下一個要處理的節(jié)點。

3.reconcileChildren:協(xié)調(diào)當前纖程節(jié)點,它實際上是虛擬DOM的比較,并記錄要進行的更改。你可以看到,我們直接在每個纖程節(jié)點上修改和保存,因為現(xiàn)在它只是對JavaScript對象的修改,而不涉及真正的DOM。

4.最后一步是commitRoot。如果當前需要更新(根據(jù)wipRoot),并且沒有下一個單元任務要處理(根據(jù)!nextUnitOfWork),這意味著需要將虛擬更改映射到實際的DOM。commitRoot負責根據(jù)纖程節(jié)點的變化修改真實的DOM。

到目前為止,我們已經(jīng)實現(xiàn)了纖程架構(gòu),是時候見證它的威力了。

我們想給組件添加狀態(tài),讓我們實現(xiàn)一個useState。

import React from \"./mini-react\";import \"./styles.css\";
function calculateWinner(squares) { const lines = [ [0, 1, 2], [3, 4, 5], [6, 7, 8], [0, 3, 6], [1, 4, 7], [2, 5, 8], [0, 4, 8], [2, 4, 6] ]; for (let i = 0; i < lines.length; i = 1) { const [a, b, c] = lines[i]; if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) { return squares[a]; } } return ;}
class Square extends React.Component { render { return ( <button onClick={this.props.onClick} className=\"square\"> {this.props.value} </button> ); }}
class Board extends React.Component { renderSquare(i) { return ( <Square value={this.props.squares[i]} onClick={ => { this.props.onClick(i); }} /> ); }
render { return ( <div> <div className=\"board-row\"> {this.renderSquare(0)} {this.renderSquare(1)} {this.renderSquare(2)} </div> <div className=\"board-row\"> {this.renderSquare(3)} {this.renderSquare(4)} {this.renderSquare(5)} </div> <div className=\"board-row\"> {this.renderSquare(6)} {this.renderSquare(7)} {this.renderSquare(8)} </div> </div> ); }}
class App extends React.Component { constructor(props) { super(props); this.State = { history: [ { squares: Array(9).fill() } ], stepNumber: 0, xIsNext: true }; }
handleClick(i) { const history = this.state.history.slice(0, this.state.stepNumber 1); const current = history[history.length - 1]; const squares = current.squares.slice; if (calculateWinner(squares) || squares[i]) { return; }
squares[i] = this.state.xIsNext ? \"X\" : \"O\"; this.setState({ history: history.concat([ { squares } ]), stepNumber: history.length, xIsNext: !this.state.xIsNext }); }
jumpTo(step) { this.setState({ stepNumber: step, xIsNext: step % 2 === 0 }); }
render { const { history } = this.state; const current = history[this.state.stepNumber]; const winner = calculateWinner(current.squares);
const moves = history.map((step, move) => { const desc = move ? `Go to move #${move}` : \"Go to game start\"; return ( <li key={move}> <button onClick={ => this.jumpTo(move)}>{desc}</button> </li> ); });
let status; if (winner) { status = `Winner: ${winner}`; } else { status = `Next player: ${this.state.xIsNext ? \"X\" : \"O\"}`; }
return ( <div className=\"game\"> <div className=\"game-board\"> <Board squares={current.squares} onClick={(i) => { this.handleClick(i); }} /> </div> <div className=\"game-info\"> <div>{status}</div> <ol>{moves}</ol> </div> </div> ); }}
// eslint-disable-next-line react/no-deprecatedReact.render(<App />, document.getElementById(\"root\"));

useState巧妙地將hook的狀態(tài)保留在纖程節(jié)點上,并通過隊列修改狀態(tài)。從這里,我們也可以知道為什么React-hooks要求每次調(diào)用的順序不能改變。

除此以外,我們還實現(xiàn)了一個Component ,這里只是簡單地轉(zhuǎn)換為一個渲染的方法,并添加了一點它的獨特身份。

React 18發(fā)布,僅用400行代碼就能實現(xiàn)一個Mini-React(react 17)

模擬requestIdleCallback

現(xiàn)在我們幾乎實現(xiàn)了所有的功能,讓我解釋一下React目前采用的調(diào)度器包,它實際上是一個比requestIdleCallback更復雜的調(diào)度邏輯,包括更新任務的優(yōu)先級等等。

上面是我實現(xiàn)模擬requestIdleCallback的參考調(diào)度程序,它結(jié)合了requestAnimationFrame和MessageChannel。這里使用MessageChannel的目的是使用宏任務來處理每一輪的單元任務。

那么為什么要使用宏任務呢?

為了放棄主線程,瀏覽器可以在空閑期間更新DOM或接收事件。因為瀏覽器更新DOM是一個獨立的任務,而JavaScript在這個時候不會被執(zhí)行,因為主線程一次只能運行一個功能,要么執(zhí)行JS,要么處理DOM計算樣式,要么接收輸入事件,等等。

為什么不使用微任務呢?

因為微任務包含在每一輪宏任務中,所以在所有微任務執(zhí)行完畢之前,也就是當前宏任務未完成時,主線程不能放棄。

為什么不使用setTimeout呢?

因為如果setTimeout被嵌套調(diào)用超過5次,該函數(shù)將被視為阻塞,瀏覽器將把最小時間設置為4ms,所以它不夠精確。

最終版本

下面是最終的版本,你可以看到,在去掉注釋后,不到400行代碼就實現(xiàn)了React的核心思想。

import React from \"./mini-react\";import \"./styles.css\";
function calculateWinner(squares) { const lines = [ [0, 1, 2], [3, 4, 5], [6, 7, 8], [0, 3, 6], [1, 4, 7], [2, 5, 8], [0, 4, 8], [2, 4, 6] ]; for (let i = 0; i < lines.length; i = 1) { const [a, b, c] = lines[i]; if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) { return squares[a]; } } return ;}
class Square extends React.Component { render { return ( <button onClick={this.props.onClick} className=\"square\"> {this.props.value} </button> ); }}
class Board extends React.Component { renderSquare(i) { return ( <Square value={this.props.squares[i]} onClick={ => { this.props.onClick(i); }} /> ); }
render { return ( <div> <div className=\"board-row\"> {this.renderSquare(0)} {this.renderSquare(1)} {this.renderSquare(2)} </div> <div className=\"board-row\"> {this.renderSquare(3)} {this.renderSquare(4)} {this.renderSquare(5)} </div> <div className=\"board-row\"> {this.renderSquare(6)} {this.renderSquare(7)} {this.renderSquare(8)} </div> </div> ); }}
class App extends React.Component { constructor(props) { super(props); this.state = { history: [ { squares: Array(9).fill() } ], stepNumber: 0, xIsNext: true }; }
handleClick(i) { const history = this.state.history.slice(0, this.state.stepNumber 1); const current = history[history.length - 1]; const squares = current.squares.slice; if (calculateWinner(squares) || squares[i]) { return; }
squares[i] = this.state.xIsNext ? \"X\" : \"O\"; this.setState({ history: history.concat([ { squares } ]), stepNumber: history.length, xIsNext: !this.state.xIsNext }); }
jumpTo(step) { this.setState({ stepNumber: step, xIsNext: step % 2 === 0 }); }
render { const { history } = this.state; const current = history[this.state.stepNumber]; const winner = calculateWinner(current.squares);
const moves = history.map((step, move) => { const desc = move ? `Go to move #${move}` : \"Go to game start\"; return ( <li key={move}> <button onClick={ => this.jumpTo(move)}>{desc}</button> </li> ); });
let status; if (winner) { status = `Winner: ${winner}`; } else { status = `Next player: ${this.state.xIsNext ? \"X\" : \"O\"}`; }
return ( <div className=\"game\"> <div className=\"game-board\"> <Board squares={current.squares} onClick={(i) => { this.handleClick(i); }} /> </div> <div className=\"game-info\"> <div>{status}</div> <ol>{moves}</ol> </div> </div> ); }}
// eslint-disable-next-line react/no-deprecatedReact.render(<App />, document.getElementById(\"root\"));

我還在GitHub中添加了一個TypeScript版本的Mini-React(https://github.com/islizeqiang/mini-react/blob/master/src/mini-react.ts),如果你有興趣,可以去看看。

React 18發(fā)布,僅用400行代碼就能實現(xiàn)一個Mini-React(react 17)

END

成就一億技術人

React 18發(fā)布,僅用400行代碼就能實現(xiàn)一個Mini-React(react 17)

版權(quán)聲明:本文內(nèi)容由互聯(lián)網(wǎng)用戶自發(fā)貢獻,該文觀點僅代表作者本人。本站僅提供信息存儲空間服務,不擁有所有權(quán),不承擔相關法律責任。如發(fā)現(xiàn)本站有涉嫌抄襲侵權(quán)/違法違規(guī)的內(nèi)容, 請發(fā)送郵件至 舉報,一經(jīng)查實,本站將立刻刪除。

(0)
上一篇 2024年5月16日 下午3:14
下一篇 2024年5月16日 下午3:26

相關推薦

  • it項目進度管理

    IT項目進度管理 隨著信息技術的快速發(fā)展,IT項目已經(jīng)成為企業(yè)成功的關鍵因素之一。然而,在實施IT項目時,進度管理仍然是一個挑戰(zhàn)。進度管理不僅僅是確保項目按時完成,還包括確保項目在…

    科研百科 2025年1月2日
    2
  • 版本管理最好用的工具,你懂多少?(版本管理工具有哪些)

    版本控制(Revision control)是一種在開發(fā)的過程中用于管理我們對文件、目錄或工程等內(nèi)容的修改歷史,方便查看更改歷史記錄,備份以便恢復以前的版本的軟件工程技術。 簡單來…

    2022年5月28日
    432
  • 課題研究中期總結(jié)報告寫法及案例模板(課題研究中期總結(jié)報告寫法及案例模板怎么寫)

    課題研究中期總結(jié)報告寫法及案例模板 文章來源:悅教研共同體 如何撰寫課題的《中期報告》 ◆立項課題《中期報告》的文本格式 一、標題:課題名稱 “中期報告” 二、課題簡介:簡要介紹課…

    科研百科 2024年4月19日
    73
  • 肇州管理系統(tǒng)開發(fā)項目

    肇州管理系統(tǒng)開發(fā)項目 隨著信息技術的不斷發(fā)展,管理系統(tǒng)開發(fā)項目已經(jīng)成為了企業(yè)和個人開發(fā)的重要項目之一。肇州管理系統(tǒng)開發(fā)項目是一個涉及到多個方面的項目,包括系統(tǒng)需求分析、系統(tǒng)設計、開…

  • 項目管理軟件中文

    項目管理軟件中文 項目管理軟件是一種用于管理項目進度、質(zhì)量和成本的軟件工具,能夠幫助項目經(jīng)理和團隊成員更好地協(xié)同工作,確保項目按時完成并達到預期目標。本文將介紹項目管理軟件中文的基…

    科研百科 2024年7月28日
    6
  • 商業(yè)管理系統(tǒng)軟件

    商業(yè)管理系統(tǒng)軟件:助力企業(yè)高效管理 隨著商業(yè)競爭的加劇,企業(yè)需要更加高效地管理自己的業(yè)務。商業(yè)管理系統(tǒng)軟件是一種可以幫助企業(yè)高效管理業(yè)務的工具,通過自動化業(yè)務流程,提高員工的工作效…

    科研百科 2024年8月28日
    6
  • 工會經(jīng)費使用管理制度

    工會經(jīng)費使用管理制度 工會經(jīng)費是工會組織用于支持工會工作的基本經(jīng)費,其使用管理至關重要。本文旨在制定工會經(jīng)費使用管理制度,以確保工會經(jīng)費的使用高效、合理、透明、安全。 一、工會經(jīng)費…

    科研百科 2024年7月12日
    28
  • 共享辦公公司名稱(共享辦公公司)

    共享辦公公司: 讓工作更美好 隨著全球化和數(shù)字化的加速發(fā)展,越來越多的人選擇在城市中心設立辦公室,以追求更好的工作體驗和更高效的工作效率。然而,傳統(tǒng)的辦公室模式面臨著越來越多的挑戰(zhàn)…

    科研百科 2024年6月3日
    38
  • 企業(yè)crm客戶關系管理系統(tǒng)的價格(crm報價系統(tǒng))

    企業(yè)crm客戶關系管理系統(tǒng)的價格   CRM客戶關系管理系統(tǒng)的價格通常會因用戶數(shù)量和功能等因數(shù)而異,這一點在任何CRM供應商的報價中都一樣,畢竟人多代表性能要足夠,并且功能也多,供…

    2022年8月28日
    555
  • 文物科研項目怎么做

    文物科研項目怎么做 文物是人類文明的重要遺產(chǎn),是人類歷史的見證者。隨著人類社會的發(fā)展,文物的保護和研究工作也在不斷推進。文物科研項目是文物保護工作的重要組成部分,也是一項具有挑戰(zhàn)性…

    科研百科 2025年4月8日
    3
巴青县| 国产丝袜伦理电影一区二区三区| 6 9人人精品| 综合人妻中文字幕| 亚洲精品中文字母123区高清无| 日本不卡a∨二区| 久久久今儿久久久少妇| 国产精品蜜臀Av| 久久人人做人人妻人人| 日韩一本日| 搭讪日本人妻,久久网| 国产高潮视频一区二区三区 | 国产黄片区| 亚洲成人午夜网站免费| 好吊日视频在线观看| 桃乃木香奈 在线 一区| 丁香成人区视频| 蜜臀av网站在线观看| 色婷婷久久91精品一区二区三区| 成人春色综合网| 久久中午在线| 黄色影院欧美| 黄色三级av在线| 一区色av| 日韩外围不卡一区二区| 在线亚洲-AV| 日本Awww.一区| 五月天国产传媒高清无码| 揉B无码一区二区| 韩国美女鸡巴视频| 国产精品一区一区| 红桃一起草AV| 美女午夜消魂三区| 国产美女和男生一起桶桶的网站| 极品久久久二区| 天天射久久久久久熟老女| 日韩欧美亚洲中文无码| 十九色综合网| 少妇高潮久久久久久古代| 啊啊啊啊~好湿| 黄片网站91|