<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          工作半年期間調(diào)研的一個庫,很有價值

          共 5620字,需瀏覽 12分鐘

           ·

          2021-10-24 02:15

          大家好,我是 HearLing。不知道大家還記得不記得我呀,許久未發(fā)文章,一直都在忙工作,這段時間就沒有輸出文章了,經(jīng)過幾個月的修煉,還是沉淀了許多知識。今后和 Chocolate 會多多更新文章,大家可以多多關(guān)注呀。

          這篇文章是我工作中調(diào)研的一個庫,使用 proxy-memoize 代替 reselect??赡茉S多小伙伴聽到還沒了解過這兩個庫,我還問了下 Chocolate,他也不知道哈哈哈。

          在工作中我還調(diào)研了許多的庫,在后期都會總結(jié)一下,我覺得沉淀這些知識很有幫助,今天分享給大家,以后說不定大家能用到呢,趕快收藏一波~

          引言

          在像 React 這樣的前端框架中,對象不變性非常重要。但其實它本身并不支持強制不變性。那這個庫利用了 ProxyWeakMap,并提供了記憶功能。僅當參數(shù)(對象)的使用部分發(fā)生變化時,記憶函數(shù)才會重新計算原始函數(shù)。

          通過引言我們已經(jīng)知道了它的優(yōu)點,那么你可能會好奇他是如何實現(xiàn)的,那么你可以看看下面這個介紹,如果你只關(guān)心它是如何使用你也可以跳過這一小節(jié):

          如何工作

          當它(重新)計算一個函數(shù)時,它將用代理(遞歸地,根據(jù)需要)包裝一個輸入對象并調(diào)用該函數(shù)。當它完成時,它將檢查什么是受影響的。這個受影響其實是在函數(shù)調(diào)用期間訪問的輸入對象的路徑列表。

          當它下一次接收到一個新的輸入對象時,它將檢查受影響路徑中的值是否被更改。如果是被更改,那么它將重新計算函數(shù)。否則,它將返回一個緩存結(jié)果。默認緩存大小為1,可配置。

          一個個說吧,首先要包裝成對象:顯然這里需要注意:一個要被記憶的函數(shù)必須是一個只接受一個對象作為參數(shù)的函數(shù)。

          //要為對象
          const?fn?=?(x)?=>?({?foo:?x.foo?});
          const?memoizedFn?=?memoize(fn);
          //不支持
          const?unsupportedFn1?=?(number)?=>?number?*?2;
          const?unsupportedFn2?=?(obj1,?obj2)?=>?[obj1.foo,?obj2.foo];

          再來說它是如何檢查受影響的。下面這個例子是一個實例不是解釋哈,我們先理解表層,再來更深一層的理解如何實現(xiàn):

          const?fn?=?(obj)?=>?obj.arr.map((x)?=>?x.num);
          const?memoizedFn?=?memoize(fn);

          const?result1?=?memoizedFn({
          ??arr:?[
          ????{?num:?1,?text:?'hello'?},
          ????{?num:?2,?text:?'world'?},
          ??],
          })

          //?受影響的是?"arr[0].num",?"arr[1].num"?and?"arr.length"

          const?result2?=?memoizedFn({
          ??arr:?[
          ????{?num:?1,?text:?'hello'?},
          ????{?num:?2,?text:?'proxy'?},
          ??],
          ??extraProp:?[1,?2,?3],
          })

          //?受影響的對象num的值并沒有改變,于是:
          console.log('result1?===?result2?=>',result1?===?result2)?//true

          這個神奇的效果是如何實現(xiàn)的呢?

          你可以通過proxy-memoize(https://github.com/dai-shi/proxy-memoize)了解到其中使用跟蹤和影響的比較是通過內(nèi)部庫proxy-compare(https://github.com/dai-shi/proxy-compare)完成的。

          簡單介紹一下 proxy-compare :這是一個從 react-tracked 中提取的庫,只提供與代理的比較特性。(實際上,react-tracked v2將使用這個庫作為依賴項。)

          該庫導出了兩個主要功能: createDeepProxy 和 isDeepChanged

          工作原理:

          const?state?=?{?a:?1,?b:?2?};
          const?affected?=?new?WeakMap();
          const?proxy?=?createDeepProxy(state,?affected);
          proxy.a?//?touch?a?property
          isDeepChanged(state,?{?a:?1,?b:?22?},?affected)?//?is?false
          isDeepChanged(state,?{?a:?11,?b:?2?},?affected)?//?is?true

          狀態(tài)可以是嵌套對象,只有當觸及某個屬性時,才會創(chuàng)建新的代理。當然如果你想深究createDeepProxy和isDeepChanged是如何實現(xiàn)的,你可以去看proxy-compare源碼,我這里就不過多介紹了。

          接下來介紹它配合React Context和React Redux這兩個主要場景的使用,我這里放的是自己寫的例子,當然你也可以看官網(wǎng)給出的例子都行。

          Usage with React Context

          如果將proxy-memoizeuseMemo 一起使用,我們將能夠獲得類似 react-tracked 的好處。

          官方實例Sandbox:https://codesandbox.io/s/proxy-memoize-demo-vrnze

          import?memoize?from?'proxy-memoize';

          const?MyContext?=?createContext();

          const?Component?=?()?=>?{
          ??const?[state,?dispatch]?=?useContext(MyContext);
          ??const?render?=?useMemo(()?=>?memoize(({?firstName,?lastName?})?=>?(
          ????<div>
          ??????First?Name:?{firstName}
          ??????<input
          ????????value={firstName}
          ????????onChange={(event)?=>
          ?{
          ??????????dispatch({?type:?'setFirstName',?firstName:?event.target.value?});
          ????????}}
          ??????(Last?Name:?{lastName})
          ??????/>
          ????div>

          ??)),?[dispatch]);
          ??return?render(state);
          };

          const?App?=?({?children?})?=>?(
          ??<MyContext.Provider?value={useReducer(reducer,?initialState)}>
          ????{children}
          ??MyContext.Provider>

          );

          當上下文發(fā)生變化時,組件將re-render。怎樣才不會每次re-render呢,在這個例子中我們可以發(fā)現(xiàn)除非 firstName 沒有改變,否則它返回memoized的react 元素樹,re-render 將不會發(fā)生。這種行為不同于react-tracked,但還是有優(yōu)化的。

          Usage with React Context 實際上使用可能沒有那么廣泛,但是如果你們項目中有使用了許多 ReactContext 確實是可以用這個來優(yōu)化。

          接下來要說的我覺得是最廣泛的應用場景(當然我是說的大部分項目)

          Usage with React Redux

          Instead of?reselect:? https://github.com/reduxjs/reselect.

          他兩都是解決這個問題的:可以創(chuàng)建可記憶的(Memoized)、可組合的 selector 函數(shù)、可以用來高效地計算 Redux store 里的衍生數(shù)據(jù)。

          如果你沒用過proxy-memoize,你大概率是使用的reselect來編寫選擇器 selector 函數(shù) ,這里我們來對比兩個庫,我這里舉一個簡單的例子,但是往往state結(jié)構(gòu)是沒有這么簡單的,這里只是個演示。

          其實在對比中你就可以知道m(xù)emoize如何使用以及他的優(yōu)化好處了。

          為啥說代替reselect

          相信看了下面的例子你能明白:

          const?fn?=?memoize((x:State)?=>?({?sum:?x.a?+?x.b,?diff:?x.a?-?x.b?}));

          const?fn1?=?createSelector(
          ????[(state:State)=>state],
          ????(state)?=>?{
          ????????return?{
          ????????????sum?:state.a+state.b,
          ????????????diff:state.a-state.b
          ????????}
          ????}
          )

          console.log("fn=>",(fn({?a:?1,?b:?2?})))//{sum:?3,?diff:?-1}
          console.log("fn?=>",(fn({?a:?1,?b:?2?,c:3})?===?fn({?a:?1,?b:?2?,c:1})))//true
          console.log("fn1=>",(fn1({?a:?1,?b:?2})?===?fn1({?a:?1,?b:?2})))//false

          當然我發(fā)現(xiàn)如果擴展成這樣也是可以的(偶然的發(fā)現(xiàn),可能確實是因為這個state太簡單了吧),但是寫起來就更復雜(尤其是層級深需要的值多的時候,并且當需要的是數(shù)組中屬性值時,這就實現(xiàn)不了)

          const?selectA?=?(state:State)=>state.a
          const?selectB?=?(state:State)=>state.b
          const?selectSub?=?createSelector(
          ????selectA,
          ????selectB,
          ????(a,b)?=>?{
          ????????return?{
          ????????????sum?:a+b,
          ????????????diff:a-b
          ????????}
          ????}
          )
          console.log("fn1=>",(fn1({?a:?1,?b:?2})?===?fn1({?a:?1,?b:?2})))//true

          那么久來個稍微復雜一點的例子吧

          import?{?useDispatch,?useSelector?}?from?'react-redux';
          import?memoize?from?'proxy-memoize';

          const?Component?=?({?id?})?=>?{
          ??const?dispatch?=?useDispatch();
          ??const?selector?=?useMemo(()?=>?memoize((state)?=>?({
          ????firstName:?state.users[id].firstName,
          ????lastName:?state.users[id].lastName,
          ??})),?[id]);
          ??const?{?firstName,?lastName?}?=?useSelector(selector);
          ??return?(
          ????<div>
          ??????First?Name:?{firstName}
          ??????<input
          ????????value={firstName}
          ????????onChange={(event)?=>
          ?{
          ??????????dispatch({?type:?'setFirstName',?firstName:?event.target.value?});
          ????????}}
          ??????/>
          ??????(Last?Name:?{lastName})
          ????div>

          ??);
          };

          同理我們也來對比一下:

          /**
          *?對比
          */

          const?fn?=?memoize((state:State)?=>?state.users.map((user)?=>?user.firstName))
          const?fn1?=?createSelector(
          [(state:State)=>state.users],
          (users)?=>?{
          ????return?users.map((user)=>user.firstName)
          })
          console.log("fn?=>",fn({count:1?,text:?'',?users:?[{firstName:"hh",lastName:"ll"}]})?===?fn({count:1?,text:?'',?users:?[{firstName:"hh",lastName:"lllll"}]}))//true
          console.log("fn1?=>",fn1({count:1?,text:?'1',?users:?[{firstName:"hh",lastName:"ll"}]})?===?fn({count:1?,text:?'',?users:?[{firstName:"hh",lastName:"ll"}]}))//false
          ??

          可以發(fā)現(xiàn),我們要取的值是在一個數(shù)組里,并且我們只要數(shù)組里的firstName這個屬性,按reselect來的話我們要先拿到數(shù)組再去遍歷拿到里面的值,所以檢測變化就是檢測這個數(shù)組變化咯。這時你就能發(fā)現(xiàn)memoize的簡潔和優(yōu)化

          memoize((state)?=>?state.users.map((user)?=>?user.firstName))

          它不會每次都創(chuàng)建,只有在用戶長度更改或 firstName 中的一個更改時,才會重新計算這個值。

          總結(jié)

          這個其實是我工作中調(diào)研的一個庫,這個知識無償分享給大家,也不知道大家喜不喜歡這種硬核一點的知識分享哈,那如果你覺得寫的還不錯的話,點個贊再走吧??

          瀏覽 63
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  影音先锋成人在线视频 | 九九影院精品 | 日韩无码乱伦av 日韩无码视频不卡 | 婷婷丁香成人五月天 | 免费观看一区二区三区四区五区 |