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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

QQ音乐:React v16 新特性实践

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

歡迎大家前往騰訊云+社區,獲取更多騰訊海量技術實踐干貨哦~

本文由QQ音樂技術團隊發表于云+社區專欄

自從去年9月份 React 團隊發布了 v16.0 版本開始,到18年3月剛發布的 v16.3 版本,React 陸續推出了多項重磅新特性,并改進了原有功能中反饋呼聲很高的一些問題,例如 render 方法內單節點層級嵌套問題,提供生命周期錯誤捕捉,組件指定 render 到任意 DOM 節點 (Portal) 等能力,以及最新的 Context API 和 Ref API。我們在對以上新特性經過一段時間的使用過后,通過本文進行一些細節分享和總結。

一、render 方法優化

為了符合 React 的 component tree 和 diff 結構設計,在組件的 render() 方法中頂層必須包裹為單節點,因此實際組件設計和使用中總是需要注意嵌套后的層級變深,這是 React 的一個經常被人詬病的問題。比如以下的內容結構就必須再嵌套一個 div 使其變為單節點進行返回:

render() {return (<div>注:<p>產品說明一</p><p>產品說明二</p></div>); } 復制代碼

現在在更新 v16 版本后,這個問題有了新的改進,render 方法可以支持返回數組了:

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

這樣確實少了一層,但大家又繼續發現代碼還是不夠簡潔。首先 TEXT 節點需要用引號包起來,其次由于是數組,每條內容當然還需要添加逗號分隔,另外 element 上還需要手動加 key 來輔助 diff。給人感覺就是不像在寫 JSX 了。

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

render() {return (<React.Fragment>注: <p>產品說明一</p><p>產品說明二</p></React.Fragment>); } 復制代碼

可以看到是一個正常單節點寫法,直接包裹里面的內容。但是 Fragment 本身并不會產生真實的 DOM 節點,因此也不會導致層級嵌套增加。

另外 Fragment 還提供了新的 JSX 簡寫方式 <></>:

render() {return (<>注:<p>產品說明一</p><p>產品說明二</p></>);} 復制代碼

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

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

二、錯誤邊界 (Error Boundaries)

錯誤邊界是指以在組件上定義 componentDidCatch 方法的方式來創建一個有錯誤捕捉功能的組件,在其內嵌套的組件在生命過程中發生的錯誤都會被其捕捉到,而不會上升到外部導致整個頁面和組件樹異常 crash。

例如下面的例子就是通過一個 ErrorBoundary 組件對其內的內容進行保護和錯誤捕捉,并在發生錯誤時進行兜底的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>報錯了.</h1><ErrorPanel {...this.state} /></>);}return this.props.children;} }export default function App(){return (<ErrorBoundary><Content /></ErrorBoundary>); } 復制代碼

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

三、React.createPortal()

這個 API 是用來將部分內容分離式地 render 到指定的 DOM 節點上。不同于使用 ReactDom.render 新創建一個 DOM tree 的方式,對于要通過 createPortal() “分離”出去的內容,其間的數據傳遞,生命周期,甚至事件冒泡,依然存在于原本的抽象組件樹結構當中。

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 ); } } 復制代碼

例如以上代碼, 通過 把里面的 內容渲染到了一個獨立的節點上。在實際的 DOM 結構中,img 已經脫離了 Creater 本身的 DOM 樹存在于另一個獨立節點。但當點擊 img 時,仍然可以神奇的觸發到 Creater 內的 div 上的 onclick 事件。這里實際依賴于 React 代理和重寫了整套事件系統,讓整個抽象組件樹的邏輯得以保持同步。

四、Context API

以前的版本中 Context API 是作為未公開的實驗性功能存在的,隨著越來越多的聲音要求對其進行完善,在 v16.3 版本,React 團隊重新設計并發布了新的官方 Context API。

使用 Context API 可以更方便的在組件中傳遞和共享某些 "全局" 數據,這是為了解決以往組件間共享公共數據需要通過多余的 props 進行層層傳遞的問題 (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); 復制代碼

我們為了使用一個語言包,把語言配置存儲到一個 store 里,通過 Redux connect 到頂層組件,然而僅僅是最底端的子組件才需要用到。我們也不可能為每個組件都單獨加上 connect,這會造成數據驅動更新的重復和不可維護。因此中間組件需要一層層不斷傳遞下去,就是所謂的 props drilling。

對于這種全局、不常修改的數據共享,就比較適合用 Context API 來實現:

首先第一步,類似 store,我們可以先創建一個 Context,并加入默認值:

const LangContext = React.createContext({title:"默認標題" }); 復制代碼

然后在頂層通過 Provider 向組件樹提供 Context 的訪問。這里可以通過傳入 value 修改 Context 中的數據,當value變化的時候,涉及的 Consumer 內整個內容將重新 render:

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

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

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

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

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

那么看了上面的例子,我們是否可以直接使用 Context API 來代替掉所有的數據傳遞,包括去掉 redux 這些數據同步 library 了?其實并不合適。前面也有提到,Context API 應該用于需要全局共享數據的場景,并且數據最好是不用頻繁更改的。因為作為上層存在的 Context,在數據變化時,容易導致所有涉及的 Consumer 重新 render。

比如下面這個例子:

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

實際每次 render 的時候,這里的 value 都是傳入一個新的對象。這將很容易導致所有的 Consumer 都重新執行 render 影響性能。

因此不建議濫用 Context,對于某些非全局的業務數據,也不建議作為全局 Context 放到頂層中共享,以免導致過多的 Context 嵌套和頻繁重新渲染。

五、Ref API

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

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

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

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

為了提升易用性,新版本推出了 CreateRef API 來創建一個 ref object, 傳遞到 component 的 ref 上之后可以直接獲得引用:

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

另外還提供了 ForwardRef API 來輔助簡化嵌套組件、component 至 element 間的 ref 傳遞,避免出現 this.ref.ref.ref 的問題。

例如我們有一個包裝過的 Button 組件,想獲取里面真正的 button DOM element,本來需要這樣做:

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{// 通過ref一層層訪問console.log(this.myRef.buttonRef);}render(){return (<MyButton ref={this.myRef}>Press here</MyButton>);} } 復制代碼

這種場景使用 forwardRef API 的方式做一個“穿透”,就能簡便許多:

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>);} } 復制代碼

總結

以上就是 React v16 發布以來幾個比較重要和有用的新特性,優化的同時也帶來了開發體驗的提升。另外 v16 對比之前版本還有不錯的包大小降低,也是非常具有優勢的:

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

這么多激動人心的特性,如果你還在用 v15 甚至舊版,就趕快升級體驗吧!


問答

如何從jQuery轉到React.js?

相關閱讀

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

Android Native 開發之 NewString 與 NewStringUtf 解析

React-Native 分包實踐


此文已由作者授權騰訊云+社區發布,原文鏈接:https://cloud.tencent.com/developer/article/1137778?fromSource=waitui

歡迎大家前往騰訊云+社區或關注云加社區微信公眾號(QcloudCommunity),第一時間獲取更多海量技術實踐干貨哦~

總結

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

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。