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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

精读《Epitath 源码 - renderProps 新用法》

發布時間:2025/5/22 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 精读《Epitath 源码 - renderProps 新用法》 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1 引言

很高興這一期的話題是由 epitath 的作者 grsabreu 提供的。

前端發展了 20 多年,隨著發展中國家越來越多的互聯網從業者涌入,現在前端知識玲瑯滿足,概念、庫也越來越多。雖然內容越來越多,但作為個體的你的時間并沒有增多,如何持續學習新知識,學什么將會是個大問題。

前端精讀通過吸引優質的用戶,提供最前沿的話題或者設計理念,雖然每周一篇文章不足以概括這一周的所有焦點,但可以保證你閱讀的這十幾分鐘沒有在浪費時間,每一篇精讀都是經過精心篩選的,我們既討論大家關注的焦點,也能找到倉庫角落被遺忘的珍珠。

2 概述

在介紹 Epitath 之前,先介紹一下 renderProps。

renderProps 是 jsx 的一種實踐方式,renderProps 組件并不渲染 dom,但提供了持久化數據與回調函數幫助減少對當前組件 state 的依賴。

RenderProps 的概念

react-powerplug 就是一個 renderProps 工具庫,我們看看可以做些什么:

<Toggle initial={true}>{({ on, toggle }) => <Checkbox checked={on} onChange={toggle} />} </Toggle>

Toggle 就是一個 renderProps 組件,它可以幫助控制受控組件。比如僅僅利用 Toggle,我們可以大大簡化 Modal 組件的使用方式:

class App extends React.Component {state = { visible: false };showModal = () => {this.setState({visible: true});};handleOk = e => {this.setState({visible: false});};handleCancel = e => {this.setState({visible: false});};render() {return (<div><Button type="primary" onClick={this.showModal}>Open Modal</Button><Modaltitle="Basic Modal"visible={this.state.visible}onOk={this.handleOk}onCancel={this.handleCancel}><p>Some contents...</p><p>Some contents...</p><p>Some contents...</p></Modal></div>);} }ReactDOM.render(<App />, mountNode);

這是 Modal 標準代碼,我們可以使用 Toggle 簡化為:

class App extends React.Component {render() {return (<Toggle initial={false}>{({ on, toggle }) => (<Button type="primary" onClick={toggle}>Open Modal</Button><Modaltitle="Basic Modal"visible={on}onOk={toggle}onCancel={toggle}><p>Some contents...</p><p>Some contents...</p><p>Some contents...</p></Modal>)}</Toggle>);} }ReactDOM.render(<App />, mountNode);

省掉了 state、一堆回調函數,而且代碼更簡潔,更語義化。

renderProps 內部管理的狀態不方便從外部獲取,因此只適合保存業務無關的數據,比如 Modal 顯隱。

RenderProps 嵌套問題的解法

renderProps 雖然好用,但當我們想組合使用時,可能會遇到層層嵌套的問題:

<Counter initial={5}>{counter => {<Toggle initial={false}>{toggle => {<MyComponent counter={counter.count} toggle={toggle.on} />;}}</Toggle>;}} </Counter>

因此 react-powerplugin 提供了 compose 函數,幫助聚合 renderProps 組件:

import { compose } from 'react-powerplug'const ToggleCounter = compose(<Counter initial={5} />,<Toggle initial={false} /> )<ToggleCounter>{(toggle, counter) => (<ProductCard {...} />)} </ToggleCounter>

使用 Epitath 解決嵌套問題

Epitath 提供了一種新方式解決這個嵌套的問題:

const App = epitath(function*() {const { count } = yield <Counter />const { on } = yield <Toggle />return (<MyComponent counter={count} toggle={on} />) })<App />

renderProps 方案與 Epitath 方案,可以類比為 回調 方案與 async/await 方案。Epitath 和 compose 都解決了 renderProps 可能帶來的嵌套問題,而 compose 是通過將多個 renderProps merge 為一個,而 Epitath 的方案更接近 async/await 的思路,利用 generator 實現了偽同步代碼。

3 精讀

Epitath 源碼一共 40 行,我們分析一下其精妙的方式。

下面是 Epitath 完整的源碼:

import React from "react"; import immutagen from "immutagen";const compose = ({ next, value }) =>next? React.cloneElement(value, null, values => compose(next(values))): value;export default Component => {const original = Component.prototype.render;const displayName = `EpitathContainer(${Component.displayName ||"anonymous"})`;if (!original) {const generator = immutagen(Component);return Object.assign(function Epitath(props) {return compose(generator(props));},{ displayName });}Component.prototype.render = function render() {// Since we are calling a new function to be called from here instead of// from a component class, we need to ensure that the render method is// invoked against `this`. We only need to do this binding and creation of// this function once, so we cache it by adding it as a property to this// new render method which avoids keeping the generator outside of this// method's scope.if (!render.generator) {render.generator = immutagen(original.bind(this));}return compose(render.generator(this.props));};return class EpitathContainer extends React.Component {static displayName = displayName;render() {return <Component {...this.props} />;}}; };

immutagen

immutagen 是一個 immutable generator 輔助庫,每次調用 .next 都會生成一個新的引用,而不是自己發生 mutable 改變:

import immutagen from "immutagen";const gen = immutagen(function*() {yield 1;yield 2;return 3; })(); // { value: 1, next: [function] }gen.next(); // { value: 2, next: [function] } gen.next(); // { value: 2, next: [function] }gen.next().next(); // { value: 3, next: undefined }

compose

看到 compose 函數就基本明白其實現思路了:

const compose = ({ next, value }) =>next? React.cloneElement(value, null, values => compose(next(values))): value; const App = epitath(function*() {const { count } = yield <Counter />;const { on } = yield <Toggle />; });

通過 immutagen,依次調用 next,生成新組件,且下一個組件是上一個組件的子組件,因此會產生下面的效果:

yield <A> yield <B> yield <C> // 等價于 <A><B><C /></B> </A>

到此其源碼精髓已經解析完了。

存在的問題

crimx 在討論中提到,Epitath 方案存在的最大問題是,每次 render 都會生成全新的組件,這對內存是一種挑戰。

稍微解釋一下,無論是通過 原生的 renderProps 還是 compose,同一個組件實例只生成一次,React 內部會持久化這些組件實例。而 immutagen 在運行時每次執行渲染,都會生成不可變數據,也就是全新的引用,這會導致廢棄的引用存在大量 GC 壓力,同時 React 每次拿到的組件都是全新的,雖然功能相同。

4 總結

epitath 巧妙的利用了 immutagen 的不可變 generator 的特性來生成組件,并且在遞歸 .next 時,將順序代碼解析為嵌套代碼,有效解決了 renderProps 嵌套問題。

喜歡 epitath 的同學趕快入手吧!同時我們也看到 generator 手動的步驟控制帶來的威力,這是 async/await 完全無法做到的。

是否可以利用 immutagen 解決 React Context 與組件相互嵌套問題呢?還有哪些其他前端功能可以利用 immutagen 簡化的呢?歡迎加入討論。

5 更多討論

討論地址是:精讀《Epitath - renderProps 新用法》 · Issue #106 · dt-fe/weekly

如果你想參與討論,請點擊這里,每周都有新的主題,周末或周一發布。前端精讀 - 幫你篩選靠譜的內容。

總結

以上是生活随笔為你收集整理的精读《Epitath 源码 - renderProps 新用法》的全部內容,希望文章能夠幫你解決所遇到的問題。

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