日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

QQ音乐:React v16 新特性实践

發(fā)布時(shí)間:2025/3/17 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 QQ音乐:React v16 新特性实践 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

歡迎大家前往騰訊云+社區(qū),獲取更多騰訊海量技術(shù)實(shí)踐干貨哦~

本文由QQ音樂技術(shù)團(tuán)隊(duì)發(fā)表于云+社區(qū)專欄

自從去年9月份 React 團(tuán)隊(duì)發(fā)布了 v16.0 版本開始,到18年3月剛發(fā)布的 v16.3 版本,React 陸續(xù)推出了多項(xiàng)重磅新特性,并改進(jìn)了原有功能中反饋呼聲很高的一些問(wèn)題,例如 render 方法內(nèi)單節(jié)點(diǎn)層級(jí)嵌套問(wèn)題,提供生命周期錯(cuò)誤捕捉,組件指定 render 到任意 DOM 節(jié)點(diǎn) (Portal) 等能力,以及最新的 Context API 和 Ref API。我們?cè)趯?duì)以上新特性經(jīng)過(guò)一段時(shí)間的使用過(guò)后,通過(guò)本文進(jìn)行一些細(xì)節(jié)分享和總結(jié)。

一、render 方法優(yōu)化

為了符合 React 的 component tree 和 diff 結(jié)構(gòu)設(shè)計(jì),在組件的 render() 方法中頂層必須包裹為單節(jié)點(diǎn),因此實(shí)際組件設(shè)計(jì)和使用中總是需要注意嵌套后的層級(jí)變深,這是 React 的一個(gè)經(jīng)常被人詬病的問(wèn)題。比如以下的內(nèi)容結(jié)構(gòu)就必須再嵌套一個(gè) div 使其變?yōu)閱喂?jié)點(diǎn)進(jìn)行返回:

render() {return (<div>注:<p>產(chǎn)品說(shuō)明一</p><p>產(chǎn)品說(shuō)明二</p></div>); } 復(fù)制代碼

現(xiàn)在在更新 v16 版本后,這個(gè)問(wèn)題有了新的改進(jìn),render 方法可以支持返回?cái)?shù)組了:

render() {return ["注:",<p key="t-1">產(chǎn)品說(shuō)明一</h2>,<p key="t-2">產(chǎn)品說(shuō)明二</h2>,]; } 復(fù)制代碼

這樣確實(shí)少了一層,但大家又繼續(xù)發(fā)現(xiàn)代碼還是不夠簡(jiǎn)潔。首先 TEXT 節(jié)點(diǎn)需要用引號(hào)包起來(lái),其次由于是數(shù)組,每條內(nèi)容當(dāng)然還需要添加逗號(hào)分隔,另外 element 上還需要手動(dòng)加 key 來(lái)輔助 diff。給人感覺就是不像在寫 JSX 了。

于是 React v16.2 趁熱打鐵,提供了更直接的方法,就是 Fragment:

render() {return (<React.Fragment>注: <p>產(chǎn)品說(shuō)明一</p><p>產(chǎn)品說(shuō)明二</p></React.Fragment>); } 復(fù)制代碼

可以看到是一個(gè)正常單節(jié)點(diǎn)寫法,直接包裹里面的內(nèi)容。但是 Fragment 本身并不會(huì)產(chǎn)生真實(shí)的 DOM 節(jié)點(diǎn),因此也不會(huì)導(dǎo)致層級(jí)嵌套增加。

另外 Fragment 還提供了新的 JSX 簡(jiǎn)寫方式 <></>:

render() {return (<>注:<p>產(chǎn)品說(shuō)明一</p><p>產(chǎn)品說(shuō)明二</p></>);} 復(fù)制代碼

看上去是否舒服多了。不過(guò)注意如果需要給 Fragment 添加 key prop,是不支持使用簡(jiǎn)寫的(這也是 Fragment 唯一會(huì)遇到需要添加props的情況):

<dl>{props.items.map(item => (// 要傳key用不了 <></><Fragment key={item.id}><dt>{item.term}</dt><dd>{item.description}</dd></Fragment>))} </dl> 復(fù)制代碼

二、錯(cuò)誤邊界 (Error Boundaries)

錯(cuò)誤邊界是指以在組件上定義 componentDidCatch 方法的方式來(lái)創(chuàng)建一個(gè)有錯(cuò)誤捕捉功能的組件,在其內(nèi)嵌套的組件在生命過(guò)程中發(fā)生的錯(cuò)誤都會(huì)被其捕捉到,而不會(huì)上升到外部導(dǎo)致整個(gè)頁(yè)面和組件樹異常 crash。

例如下面的例子就是通過(guò)一個(gè) ErrorBoundary 組件對(duì)其內(nèi)的內(nèi)容進(jìn)行保護(hù)和錯(cuò)誤捕捉,并在發(fā)生錯(cuò)誤時(shí)進(jìn)行兜底的UI展示:

class ErrorBoundary extends Component {constructor(props) {super(props);this.state = { error: null };}componentDidCatch(error, {componentStack}) {this.setState({error,componentStack,});}render() {if (this.state.error) {return (<><h1>報(bào)錯(cuò)了.</h1><ErrorPanel {...this.state} /></>);}return this.props.children;} }export default function App(){return (<ErrorBoundary><Content /></ErrorBoundary>); } 復(fù)制代碼

需要注意的是錯(cuò)誤邊界只能捕捉生命周期中的錯(cuò)誤 (willMount / render 等方法內(nèi))。無(wú)法捕捉異步的、事件回調(diào)中的錯(cuò)誤,要捕捉和覆蓋所有場(chǎng)景依然需要配合 window.onerror、Promise.catch、 try/catch 等方式。

三、React.createPortal()

這個(gè) API 是用來(lái)將部分內(nèi)容分離式地 render 到指定的 DOM 節(jié)點(diǎn)上。不同于使用 ReactDom.render 新創(chuàng)建一個(gè) DOM tree 的方式,對(duì)于要通過(guò) createPortal() “分離”出去的內(nèi)容,其間的數(shù)據(jù)傳遞,生命周期,甚至事件冒泡,依然存在于原本的抽象組件樹結(jié)構(gòu)當(dāng)中。

class Creater extends Component {render(){return (<div onClick={() => alert("clicked!")}><Portal><img src={myImg} /></Portal></div>); } }class Portal extends Component {render(){const node = getDOMNode();return createPortal(this.props.children,node ); } } 復(fù)制代碼

例如以上代碼, 通過(guò) 把里面的 內(nèi)容渲染到了一個(gè)獨(dú)立的節(jié)點(diǎn)上。在實(shí)際的 DOM 結(jié)構(gòu)中,img 已經(jīng)脫離了 Creater 本身的 DOM 樹存在于另一個(gè)獨(dú)立節(jié)點(diǎn)。但當(dāng)點(diǎn)擊 img 時(shí),仍然可以神奇的觸發(fā)到 Creater 內(nèi)的 div 上的 onclick 事件。這里實(shí)際依賴于 React 代理和重寫了整套事件系統(tǒng),讓整個(gè)抽象組件樹的邏輯得以保持同步。

四、Context API

以前的版本中 Context API 是作為未公開的實(shí)驗(yàn)性功能存在的,隨著越來(lái)越多的聲音要求對(duì)其進(jìn)行完善,在 v16.3 版本,React 團(tuán)隊(duì)重新設(shè)計(jì)并發(fā)布了新的官方 Context API。

使用 Context API 可以更方便的在組件中傳遞和共享某些 "全局" 數(shù)據(jù),這是為了解決以往組件間共享公共數(shù)據(jù)需要通過(guò)多余的 props 進(jìn)行層層傳遞的問(wèn)題 (props drilling)。比如以下代碼:

const HeadTitle = (props) => {return (<Text>{props.lang.title}</Text>;); };// 中間組件 const Head = (props) => {return (<div><HeadTitle lang={props.lang} /></div>); };class App extends React.Component {render() {return (<Head lang={this.props.lang} />;);} }export default App = connect((state) => {return {lang:state.lang} })(App); 復(fù)制代碼

我們?yōu)榱耸褂靡粋€(gè)語(yǔ)言包,把語(yǔ)言配置存儲(chǔ)到一個(gè) store 里,通過(guò) Redux connect 到頂層組件,然而僅僅是最底端的子組件才需要用到。我們也不可能為每個(gè)組件都單獨(dú)加上 connect,這會(huì)造成數(shù)據(jù)驅(qū)動(dòng)更新的重復(fù)和不可維護(hù)。因此中間組件需要一層層不斷傳遞下去,就是所謂的 props drilling。

對(duì)于這種全局、不常修改的數(shù)據(jù)共享,就比較適合用 Context API 來(lái)實(shí)現(xiàn):

首先第一步,類似 store,我們可以先創(chuàng)建一個(gè) Context,并加入默認(rèn)值:

const LangContext = React.createContext({title:"默認(rèn)標(biāo)題" }); 復(fù)制代碼

然后在頂層通過(guò) Provider 向組件樹提供 Context 的訪問(wèn)。這里可以通過(guò)傳入 value 修改 Context 中的數(shù)據(jù),當(dāng)value變化的時(shí)候,涉及的 Consumer 內(nèi)整個(gè)內(nèi)容將重新 render:

class App extends React.Component {render() {return (<LangContext.Providervalue={this.state.lang}><Head /></LangContext.Provider>);} } 復(fù)制代碼

在需要使用數(shù)據(jù)的地方,直接用 Context.Consumer 包裹,里面可以傳入一個(gè) render 函數(shù),執(zhí)行時(shí)從中取得 Context 的數(shù)據(jù)。

const HeadTitle = (props) => {return (<LangContext.Consumer>{lang => <Text>{lang.title}</Text>}</LangContext.Consumer>); }; 復(fù)制代碼

之后的中間組件也不再需要層層傳遞了,少了很多 props,減少了中間漏傳導(dǎo)致出錯(cuò),代碼也更加清爽:

// 中間組件 const Head = () => {return (<div><HeadTitle /></div>); }; 復(fù)制代碼

那么看了上面的例子,我們是否可以直接使用 Context API 來(lái)代替掉所有的數(shù)據(jù)傳遞,包括去掉 redux 這些數(shù)據(jù)同步 library 了?其實(shí)并不合適。前面也有提到,Context API 應(yīng)該用于需要全局共享數(shù)據(jù)的場(chǎng)景,并且數(shù)據(jù)最好是不用頻繁更改的。因?yàn)樽鳛樯蠈哟嬖诘?Context,在數(shù)據(jù)變化時(shí),容易導(dǎo)致所有涉及的 Consumer 重新 render。

比如下面這個(gè)例子:

render() {return (<Provider value={{title:"my title"}} ><Content /></Provider>); } 復(fù)制代碼

實(shí)際每次 render 的時(shí)候,這里的 value 都是傳入一個(gè)新的對(duì)象。這將很容易導(dǎo)致所有的 Consumer 都重新執(zhí)行 render 影響性能。

因此不建議濫用 Context,對(duì)于某些非全局的業(yè)務(wù)數(shù)據(jù),也不建議作為全局 Context 放到頂層中共享,以免導(dǎo)致過(guò)多的 Context 嵌套和頻繁重新渲染。

五、Ref API

除了 Context API 外,v16.3 還推出了兩個(gè)新的 Ref API,用來(lái)在組件中更方便的管理和使用 ref。

在此之前先看一下我們之前使用 ref 的兩種方法。

// string命名獲取 componentDidMount(){console.log(this.refs.input); } render() {return (<input ref="input"/>); } 復(fù)制代碼// callback 獲取 render() {return (<input ref={el => {this.input = el;}}/>); } 復(fù)制代碼

前一種 string 的方式比較局限,不方便于多組件間的傳遞或動(dòng)態(tài)獲取。后一種 callback 方法是之前比較推薦的方法。但是寫起來(lái)略顯麻煩,而且 update 過(guò)程中有發(fā)生清除可能會(huì)有多次調(diào)用 (callback 收到 null)。

為了提升易用性,新版本推出了 CreateRef API 來(lái)創(chuàng)建一個(gè) ref object, 傳遞到 component 的 ref 上之后可以直接獲得引用:

constructor(props) {super(props);this.input = React.createRef(); } componentDidMount() {console.log(this.input); } render() {return <input ref={this.input} />; } 復(fù)制代碼

另外還提供了 ForwardRef API 來(lái)輔助簡(jiǎn)化嵌套組件、component 至 element 間的 ref 傳遞,避免出現(xiàn) this.ref.ref.ref 的問(wèn)題。

例如我們有一個(gè)包裝過(guò)的 Button 組件,想獲取里面真正的 button DOM element,本來(lái)需要這樣做:

class MyButton extends Component {constructor(props){super(props);this.buttonRef = React.createRef();}render(){return (<button ref={this.buttonRef}>{props.children}</button>);} } class App extends Component {constructor(props){super(props);this.myRef = React.createRef();}componentDidComponent{// 通過(guò)ref一層層訪問(wèn)console.log(this.myRef.buttonRef);}render(){return (<MyButton ref={this.myRef}>Press here</MyButton>);} } 復(fù)制代碼

這種場(chǎng)景使用 forwardRef API 的方式做一個(gè)“穿透”,就能簡(jiǎn)便許多:

import { createRef, forwardRef } from "react";const MyButton = forwardRef((props, ref) => (<button ref={ref}>{props.children}</button> ));class App extends Component {constructor(props){super(props);this.realButton = createRef();}componentDidComponent{//直接拿到 inner element refconsole.log(this.realButton);}render(){return (<MyButton ref={this.realButton}>Press here</MyButton>);} } 復(fù)制代碼

總結(jié)

以上就是 React v16 發(fā)布以來(lái)幾個(gè)比較重要和有用的新特性,優(yōu)化的同時(shí)也帶來(lái)了開發(fā)體驗(yàn)的提升。另外 v16 對(duì)比之前版本還有不錯(cuò)的包大小降低,也是非常具有優(yōu)勢(shì)的:

除此之外,想要了解更多的一些變更比如生命周期的更新 (getDerivedStateFromProps, getSnapshotBeforeUpdate) 和 SSR 的優(yōu)化 (hydrate),以及即將推出的 React Fiber (async render) 動(dòng)向,可以點(diǎn)擊查看原文了解更多的官方信息。

這么多激動(dòng)人心的特性,如果你還在用 v15 甚至舊版,就趕快升級(jí)體驗(yàn)吧!


問(wèn)答

如何從jQuery轉(zhuǎn)到React.js?

相關(guān)閱讀

React Native在全民K歌APP中的使用分享

Android Native 開發(fā)之 NewString 與 NewStringUtf 解析

React-Native 分包實(shí)踐


此文已由作者授權(quán)騰訊云+社區(qū)發(fā)布,原文鏈接:https://cloud.tencent.com/developer/article/1137778?fromSource=waitui

歡迎大家前往騰訊云+社區(qū)或關(guān)注云加社區(qū)微信公眾號(hào)(QcloudCommunity),第一時(shí)間獲取更多海量技術(shù)實(shí)踐干貨哦~

總結(jié)

以上是生活随笔為你收集整理的QQ音乐:React v16 新特性实践的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。