這篇文章將為大家詳細講解有關(guān)開發(fā)React應(yīng)用的技巧,文章內(nèi)容質(zhì)量較高,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。
創(chuàng)新互聯(lián)基于分布式IDC數(shù)據(jù)中心構(gòu)建的平臺為眾多戶提供重慶服務(wù)器托管 四川大帶寬租用 成都機柜租用 成都服務(wù)器租用。
背景
React 起源于 Facebook 的內(nèi)部項目,因為該公司對市場上所有 JavaScript MVC 框架,都不滿意,就決定自己寫一套,用來架設(shè)Instagram 的網(wǎng)站。做出來以后,發(fā)現(xiàn)這套東西很好用,就在2013年5月開源了。由于 React的設(shè)計思想極其獨特,屬于革命性創(chuàng)新,性能出眾,代碼邏輯卻非常簡單。所以,越來越多的人開始關(guān)注和使用,認(rèn)為它可能是將來 Web 開發(fā)的主流工具。
正文
1. 使用字符串來定義一個React元素
舉個簡單的例子:
// 我們可以通過把一個字符串'p' 賦值給一個變量, 就像: import React from 'react' const MyComponent = 'p' function App() { return ( <>> ) } I am inside a {'
'} element
React 內(nèi)部會調(diào)用 React.createElement
, 使用這個字符串來生成這個元素。
另外, 你也可以顯式的定義component
來決定渲染的內(nèi)容, 比如:
// 定義一個MyComponent function MyComponent({ component: Component = 'p', name, age, email }) { return () } Hi {name}
<>You are {age} years old Your email is {email} >
適用方式:
function App() { return ( <>> ) }
這種方式, 你也可以傳入一個自定義的組件, 比如:
function Dashboard({ children }) { return ({children}
) } function App() { return ( <>> ) }
如果你遇到處理一類相似的
元素或者組件,可以通過這種自定義的方式抽象出來,簡化你的代碼。
舉個現(xiàn)實的例子:
比如我們現(xiàn)在要做一個貨物打包的需求, 可以單個打, 也可以批量打, 針對共同點可以寫自定義組件:
import React from 'react' import withTranslate from '@components/withTranslate' import PackComponent from './PackComponent' import usePack, { check } from './usePack' let PackEditor = (props) => { const packRes = usePack(props) return () } PackEditor = withTranslate(PackEditor) PackEditor.check = check export default PackEditor
這樣在不同的業(yè)務(wù)模塊中, 就可以靈活的使用了, 非常方便。
2. 定義錯誤邊界
在Javascript里,我們都是使用 try/catch
來捕捉可能發(fā)生的異常,在catch
中處理錯誤。 比如:
function getFromLocalStorage(key, value) { try { const data = window.localStorage.get(key) return JSON.parse(data) } catch (error) { console.error } }
這樣, 即便發(fā)生了錯誤, 我們的應(yīng)用也不至于崩潰白屏。
React 歸根結(jié)底也是Javascript,本質(zhì)上沒什么不同, 所以同樣的使用try/catch
也沒有問題。
然而, 由于React 實現(xiàn)機制的原因, 發(fā)生在組件內(nèi)部的Javascript 錯誤會破壞內(nèi)部狀態(tài), render會產(chǎn)生錯誤:
https://github.com/facebook/react/issues/4026
基于以上原因,React 團隊引入了Error Boundaries
:
https://reactjs.org/docs/error-boundaries.html
Error boundaries
, 其實就是React組件, 你可以用找個組件來處理它捕捉到的任何錯誤信息。
當(dāng)組件樹崩潰的時候,也可以顯示你自定義的UI,作為回退。
看 React 官方提供的例子:
https://reactjs.org/docs/error-boundaries.html#introducing-error-boundaries
class ErrorBoundary extends React.Component { constructor(props) { super(props) this.state = { hasError: false } } static getDerivedStateFromError(error) { // Update state so the next render will show the fallback UI. return { hasError: true } } componentDidCatch(error, errorInfo) { // You can also log the error to an error reporting service logErrorToMyService(error, errorInfo) } render() { if (this.state.hasError) { // You can render any custom fallback UI returnSomething went wrong.
} return this.props.children } }
使用方式:
Live Demo By Dan Abramov:
https://codepen.io/gaearon/pen/wqvxGa?editors=0010
3.高階組件
通俗點講, 所謂高階組件就是, 你丟一個組件進去, 增加一些屬性或操作, 再丟出來。
一般來說, 你可以把一些具備共同點的
組件抽象成一個高階組件
, 然后再不同的模塊中復(fù)用
。
比如, 我們的系統(tǒng)中, 有一類按鈕要加個border
, 很多地方都要用到, 我們把它抽象出來:
import React from 'react' // Higher order component const withBorder = (Component, customStyle) => { class WithBorder extends React.Component { render() { const style = { border: this.props.customStyle ? this.props.customStyle.border : '3px solid teal' } return} } return WithBorder } function MyComponent({ style, ...rest }) { return (
This is my component and I am expecting some styles.
) } export default withBorder(MyComponent, { border: '4px solid teal' })
經(jīng)過withBorder裝飾的MyComponent組件, 就具備了統(tǒng)一border這項功能, 后面如果如果要做修改, 就可以在這個中間層
統(tǒng)一處理, 非常方便。
在我的項目里, 也用了一些高階組件, 舉個具體的例子:
PackEditor = withTranslate(PackEditor)
我們的這個 PackEditor
就是一個增強過的組件, 增加了什么功能呢?
正如名字表述的, withTranslate
, 增加了一個翻譯功能, 下面也給大家看看這個組件是怎么實現(xiàn)的:
import React from 'react' import { Provider } from 'react-redux' import { injectIntl } from 'react-intl' import { store } from '@redux/store' import { Intl } from './Locale' const withTranslate = BaseComponent => (props) => { // avoid create a new component on re-render const IntlComponent = React.useMemo(() => injectIntl( ({ intl, ...others }) => ({ // 注入翻譯方法 if (!id) { return '' } return intl.formatMessage( typeof id === 'string' ? { id } : id, values ) }} {...others} /> ) ), []) IntlComponent.displayName = `withTranslate(${BaseComponent.displayName || 'BaseComponent'})` return ( ) } export default withTranslate
用法很靈過:
const Editor = withTranslate(({ // ... translate, }) => { // ... return ( <> {translate('xxx')}} > ) })
十分的方便。
4. Render props
Rrender prop 是指一種在 React 組件之間
使用一個值為函數(shù)的 prop 共享代碼
的簡單技術(shù), 和 HOC 類似, 都是組件間的邏輯復(fù)用問題
。
更具體地說,Render prop 是一個用于告知組件需要渲染什么內(nèi)容
的函數(shù)。
下面看一下簡單的例子:
以下組件跟蹤 Web 應(yīng)用程序中的鼠標(biāo)位置:
class Mouse extends React.Component { state = { x: 0, y: 0 }; handleMouseMove = (event) => { this.setState({ x: event.clientX, y: event.clientY }); } render() { return (
The current mouse position is ({this.state.x}, {this.state.y})
); } } class MouseTracker extends React.Component { render() { return ( <>移動鼠標(biāo)!
> ); } }
當(dāng)光標(biāo)在屏幕上移動時,組件顯示其(x,y)坐標(biāo)。
現(xiàn)在的問題是:
我們?nèi)绾卧诹硪粋€組件中復(fù)用這個行為?
換個說法,若另一個組件需要知道鼠標(biāo)位置,我們能否封裝這一行為,以便輕松地與其他組件共享它 ??
假設(shè)產(chǎn)品想要這樣一個功能: 在屏幕上呈現(xiàn)一張在屏幕上追逐鼠標(biāo)的貓的圖片。
我們或許會使用 這個需求如此簡單,你可能就直接修改Mouse組件了:
簡單粗暴, 一分鐘完成任務(wù)。 可是,如果下次產(chǎn)品 以上的例子,雖然可以完成了貓追鼠標(biāo)的需求,還沒有達到以 當(dāng)我們想要鼠標(biāo)位置用于不同的用例時,我們必須創(chuàng)建一個新的組件,專門為該用例呈現(xiàn)一些東西. 這也是 render prop 的來歷: 我們可以提供一個帶有函數(shù) prop 的 修改一下上面的代碼:
{this.props.render(this.state)}
class Cat extends React.Component {
render() {
const mouse = this.props.mouse;
return (
);
}
}
class Mouse extends React.Component {
state = { x: 0, y: 0 };
handleMouseMove = (event) => {
this.setState({
x: event.clientX,
y: event.clientY
});
}
render() {
return (
再要想加條狗呢
?可復(fù)用的方式
真正封裝行為的目標(biāo)。
組件,它能夠動態(tài)決定
什么需要渲染的,而不是將 硬編碼
到 class Cat extends React.Component {
render() {
const mouse = this.props.mouse;
return (
);
}
}
class Mouse extends React.Component {
state = { x: 0, y: 0 };
handleMouseMove = (event) => {
this.setState({
x: event.clientX,
y: event.clientY
});
}
render() {
return (
移動鼠標(biāo)!
提供了一個render 方法,讓動態(tài)決定什么需要渲染。
事實上,render prop 是因為模式才被稱為 render prop ,不一定要用名為 render 的 prop 來使用這種模式。
任何被用于告知組件需要渲染什么內(nèi)容的函數(shù) prop, 在技術(shù)上都可以被稱為 "render prop".
另外,關(guān)于 render prop 一個有趣的事情是你可以使用帶有 render prop 的常規(guī)組件來實現(xiàn)大多數(shù)高階組件 (HOC)。
例如,如果你更喜歡使用 withMouse HOC 而不是
function withMouse(Component) { return class extends React.Component { render() { return (( )}/> ); } } }
也是非常的簡潔清晰。
有一點需要注意的是, 如果你在定義的render函數(shù)里創(chuàng)建函數(shù), 使用 render prop 會抵消
使用 React.PureComponent 帶來的優(yōu)勢。
因為淺比較 props 的時候總會得到 false,并且在這種情況下每一個 render 對于 render prop 將會生成一個新的值
。
class Mouse extends React.PureComponent { // 與上面相同的代碼...... } class MouseTracker extends React.Component { render() { return ( <>( // 這是不好的! 每個渲染的 `render` prop的值將會是不同的。 )}/> > ); } }
在這樣例子中,每次
為了繞過這一問題
,有時你可以定義一個 prop 作為實例方法
,類似這樣:
class MouseTracker extends React.Component { renderTheCat(mouse) { return; } render() { return (
Move the mouse around!
); } }
5.組件性能
性能優(yōu)化是永恒的主題, 這里不一一細說, 提供積分資源供你參考:
以上就是開發(fā)React應(yīng)用的技巧,看完之后是否有所收獲呢?如果想了解更多相關(guān)內(nèi)容,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊,感謝各位的閱讀。