ImmerJavaScriptの免疫に察する新しいアプロヌチ

構造情報ブロックを共有する技術を実装する䞍倉のデヌタ構造は、アプリケヌションの状態を保存するための優れた技術のように芋えたす。 特にむベントベヌスのアヌキテクチャず組み合わせお。 ただし、すべおを支払う必芁がありたす。 免疫を提䟛する機胜が暙準ではないJavaScriptのような蚀語では、以前の状態から新しい状態を䜜成するのは退屈なテンプレヌトタスクです。 問題の芏暡ずそれを解決するための力を理解するために、Reduxで䞍倉デヌタ構造の操䜜を簡単にするために蚭蚈された67のパッケヌゞのリストがあるこのペヌゞを芋おください 。







残念ながら、これらのラむブラリはすべお、䞻芁な問題である蚀語耐性のサポヌトの欠劂を解決しおいたせん。 たずえば、 update-in



は矎しいClojureScriptコンストラクトですが、JavaScriptで実装される同様のアむデアは、䞻に䞍䟿な文字列パスに䟝存したす。 このアプロヌチぱラヌを起こしやすく、型チェックを耇雑にし、特別なAPIの調査が必芁です。



JavaScriptのむミュニティ問題を解決する方法は おそらく、代わりにその機胜を利甚しお、蚀語ずの戊いをやめるべきです。 このアプロヌチにより、暙準のデヌタ構造が提䟛する䟿利さず単玔さを倱わないようにできたす。 実際のずころ、今日お話しする没入型ラむブラリは、䞍倉の状態で䜜業するずきに暙準のJSツヌルを䜿甚するこずを目的ずしおいたす。



プロデュヌサヌ



液浞の実際の䜿甚は、生産者の䜜成に基づいおいたす。 非垞にシンプルなプロデュヌサヌは次のようになりたす。



 import produce from "immer" const nextState = produce(currentState, draft => { //   }) console.log(nextState === currentState) // true
      
      





この空のプロデュヌサヌが解決する唯䞀のタスクは、初期状態を返すこずです。



produce



関数は2぀の匕数を取りたす。 これは、 currentState



、珟圚の状態、およびプロデュヌサヌ関数です。 珟圚の状態は開始䜍眮であり、プロデュヌサヌは珟圚の状態に察しお行う必芁のある倉曎を衚珟したす。



プロデュヌサヌ関数は、 draft



ずいう1぀の匕数を取りたす。これは、将来の状態のドラフトのようなもので、枡された珟圚の状態のプロキシオブゞェクトです。 draft



ぞの倉曎は蚘録され、新しい状態の䜜成に䜿甚されたす。 珟圚の状態currentState



、このプロセス䞭に倉曎されるこずはありたせん。



䞊蚘の䟋では、immerは共通のデヌタ構造を䜿甚し、プロデュヌサヌは䜕も倉曎しないため、次の状態は、 produce



関数の入力で受信した状態ず同じになりたす。



次に、プロデュヌサヌでdraft



オブゞェクトを倉曎した堎合に䜕が起こるかを芋おみたしょう。 プロデュヌサヌ関数は䜕も返さないこずに泚意しおください。圹割を果たすのは、その䞭で行われた倉曎だけです。



 import produce from "immer" const todos = [ /*  2  todo */ ] const nextTodos = produce(todos, draft => {   draft.push({ text: "learn immer", done: true })   draft[1].done = true }) //     console.log(todos.length)        // 2 console.log(todos[1].done)       // false //    ,   draft console.log(nextTodos.length)    // 3 console.log(nextTodos[1].done)   // true //   console.log(todos === nextTodos)       // false console.log(todos[0] === nextTodos[0]) // true console.log(todos[1] === nextTodos[1]) // false
      
      





ここでは、実際のプロデュヌサヌの䟋を芋るこずができたす。 draft



ぞのすべおの倉曎は新しい状態に反映され、以前の状態ずずもに䞍倉芁玠を䜿甚したす。



ここで、 produce



機胜の動䜜を確認できたす。 1぀の远加のtodo



芁玠を含む新しい状態ツリヌを䜜成したした。 さらに、2番目の芁玠が倉曎されたした。 これらはdraft



オブゞェクトに加えられた倉曎であり、結果の状態に反映されたす。



ただし、それだけではありたせん。 リストの最埌の衚珟は、 draft



倉曎された状態の郚分が新しいオブゞェクトになったこずを実際に瀺しおいたす。 ただし、新しい状態ず前の状態の䞍倉郚分は䞀緒に䜿甚されたす。 この堎合、これが最初のtodo



芁玠です。



レデュヌサヌずプロデュヌサヌ



プロデュヌサヌの助けを借りお新しい状態を䜜成する基本を孊習したので、この知識を䜿甚しお兞型的なReduxレデュヌサヌを䜜成したす。 次の䟋は公匏のショッピングカヌトの䟋に基づいおおり、おそらく新補品に関する情報を州にロヌドしたす。 補品情報は、 reduce



を䜿甚しお倉換された配列の圢匏で提䟛され、 ID



をキヌずしお䜿甚しおコレクションに保存されたす。 以䞋はコヌドの簡略版です 。完党版はこちらにありたす 。



 const byId = (state, action) => { switch (action.type) {   case RECEIVE_PRODUCTS:     return {       ...state,       ...action.products.reduce((obj, product) => {         obj[product.id] = product         return obj       }, {})     }   default:          return state } }
      
      





ここで、この完党に通垞のReduxレデュヌサヌでは、最初に、基本状態が保存され、新しい補品のコレクションが远加される新しい状態オブゞェクトを構築する必芁がありたす。 この単玔なケヌスでは、これはそれほど悪くはありたせんが、このプロセスはアクションごずに、たた䜕かを倉曎する必芁がある各レベルで繰り返す必芁がありたす。 第二に、ここでは、リデュヌサヌが状態に倉曎を加えおいない堎合、既存の状態の戻りを保蚌する必芁がありたす。



蚘述された状況でimmerが䜿甚される堎合、行う必芁がある唯䞀の決定は、珟圚の状態に察しお行う必芁がある倉曎です。 新しい状態を䜜成するために远加の䜜業は必芁ありたせん。 その結果、リデュヌサヌでproduce



関数を䜿甚するず、次のコヌドになりたす。



 const byId = (state, action) => produce(state, draft => {   switch (action.type) {     case RECEIVE_PRODUCTS:       action.products.forEach(product => {         draft[product.id] = product       })       break   } })
      
      





没入機胜を䜿甚したレデュヌサヌの簡略化を以䞋に瀺したす。 RECEIVE_PRODUCTS



圹割を理解するこずがどれほど簡単かに泚意しおください。 情報ノむズのみを远加するコヌドは排陀されたす。 たた、ここではデフォルトのアクションを凊理しおいないこずに泚意しおください。 draft



オブゞェクトが倉曎されおいない堎合、これは基本状態に戻るこずず同等です。 元のレデュヌサヌず新しいレデュヌサヌの䞡方がたったく同じアクションを実行したす。



immerが䜿甚しない文字列識別子に぀いお



䞀時的な「粗い」オブゞェクトを倉曎しお次の䞍倉の状態を生成するずいう考え方は新しいものではありたせん。 たずえば、ImmutableJSには同様のメカニズム withMutationsがありたす。 ただし、液浞の倧きな利点は、このラむブラリを䜿甚するためにデヌタ構造のたったく新しいラむブラリを孊習たたはロヌドする必芁がないこずです。 Immerは、通垞のJavaScriptオブゞェクトず配列で動䜜したす。



没入感の匷さはそれだけではありたせん。 ボむラヌプレヌトコヌドを枛らすために、ImmutableJSおよびその他の倚くのラむブラリを䜿甚しお、特別な方法を䜿甚しお詳现な曎新およびその他の倚くの操䜜を衚珟できたす。 ただし、ここでは通垞の文字列識別子が䜿甚されおいるため、型チェックシステムが機胜したせん。 このアプロヌチぱラヌの原因です。 たずえば、次のリストでは、ImmutableJSを䜿甚しおlist



タむプを掚枬するこずはできたせん。 他のラむブラリは同様のメカニズムを拡匵し、より耇雑なコマンドを実行できるようにしたす。 これらすべおの機胜の䟡栌は、既存のプログラミング蚀語で特別なミニ蚀語を䜜成するこずです。 ImmutableJSずimmerの機胜を䜿甚したコヌドの䟋を次に瀺したす。



 // ImmutableJS const newMap = map.updateIn(['inMap', 'inList'], list => list.push(4)) // Immer draft.inMap.inList.push(4)
      
      





ここでは、むマヌムでの䜜業䞭に、詳现曎新䞭にタむプ情報が倱われない方法を瀺したす。 ご芧のずおり、immerは型の問題に悩たされおいたせん。 このラむブラリは、組み蟌みのJavaScriptデヌタ構造で動䜜し、デヌタの倉曎は暙準のメカニズムを䜿甚しお実行されたす。 これはすべおのタむプチェックシステムによっお完党に認識されたす。



オブゞェクトの自動凍結



没入のもう1぀の優れた機胜は、このラむブラリが 、 produce



関数を䜿甚しproduce



䜜成されたデヌタ構造を自動的にフリヌズ produce



こずです。 開発モヌドで。 その結果、デヌタは本圓に䞍倉になりたす。 マむケルティラヌは圌のツむヌトでこれに぀いお曞いおいたす。 状態党䜓をフリヌズするのは非垞に倚くのリ゜ヌスを消費する手順ですが、immerが倉曎された郚分をフリヌズできるずいう事実により、このラむブラリは非垞に効率的です。 そしお、状態党䜓がproduce



関数を䜿甚しproduce



取埗される堎合、最終結果は状態党䜓が垞に凍結されるこずです。 ぀たり、䜕らかの方法で倉曎しようずするず䟋倖が発生したす。



カレヌ



これが、最埌の没入機胜です。 ここたでは、 baseState



ずbaseState



関数ずいう2぀の匕数を䜿甚しお垞にbaseState



関数を呌び出したした。 ただし、堎合によっおは、関数の郚分的な適甚のメカニズムを䜿甚するず䟿利な堎合がありたす。 このアプロヌチを䜿甚するず、producer関数を呌び出しお、producer関数のみを枡すこずができたす。 これにより、状態が枡されたずきにプロデュヌサヌが実行する新しい関数が䜜成されたす。 さらに、このような関数は、任意の数の远加の匕数を取り、それらをプロデュヌサヌに枡したす。



ここで最も重芁なこずは、カリヌ化によりレデュヌサヌのテンプレヌトコヌドをさらに削枛できるこずです。



 const byId = produce((draft, action) => { switch (action.type) {   case RECEIVE_PRODUCTS:     action.products.forEach(product => {       draft[product.id] = product     })     break } })
      
      





カレヌ生産者はここに瀺されおいたす。 このコヌドをよりよく理解するために、前の䟋の1぀を芋おください。



䞀般的に、ここでは没入の䞻な機胜をすべお調べたした。 このラむブラリを䜿い始めるにはこれで十分です。 次に、このラむブラリの内郚メカニズムに぀いお説明したす。



浞挬宀内機



Immerは2぀の抂念に基づいおいたす。 1぀目は録音䞭のコピヌです。 2぀目はプロキシオブゞェクトです。 これを説明したす。





珟圚の状態、プロキシオブゞェクト、および倉曎されたプロキシオブゞェクト



緑の朚は初期状態の朚です。 緑の朚の䞀郚の円に青いフレヌムがあるこずに気付くかもしれたせん。 これらはプロキシオブゞェクトず呌ばれたす。 プロデュヌサヌが䜜業を開始する圓初、そのようなプロキシは1぀しかありたせん。 これは、関数に枡されるdraft



オブゞェクトです。 この最初のプロキシから非プリミティブ倀を読み取るず、この倀のプロキシが䜜成されたす。 ぀たり、最終的にはプロキシツリヌがあり、これは基本状態の「シャドりコピヌ」のようなものです。 これらは、プロデュヌサヌによっおアクセスされた状態の䞀郚です。









プロキシの内郚意思決定メカニズム。 芁求は、ベヌスツリヌたたはベヌスツリヌのクロヌンノヌドのいずれかに向けられたす。



これで、プロキシで䜕かを盎接、たたは任意のAPIを介しお倉曎しようずするずすぐに、参照先の゜ヌスツリヌにノヌドの小さなコピヌがすぐに䜜成modified



、 modified



フラグが蚭定modified



たす。 これ以降、このプロキシに向けられた埌続の読み取りおよび曞き蟌み操䜜は、゜ヌスツリヌではなく、そのコピヌに぀ながりたす。 さらに、ただ倉曎されおいない芪オブゞェクトは、 modified



ずしおマヌクさmodified



たす。



プロデュヌサヌが最終的に䜜業を終了するず、単にプロキシツリヌをバむパスし、プロキシが倉曎された堎合、コピヌを取埗したす。 たたは、プロキシが倉曎されおいない堎合、単に゜ヌスノヌドを返したす。 このプロセスにより、以前の状態ず共通の郚分を持぀状態ツリヌが衚瀺されたす。



プロキシオブゞェクトなしの浞挬



プロキシオブゞェクトは、最新のすべおのブラりザヌで䜿甚できたす。 しかし、それらはただどこにもありたせん。 最も顕著な䟋倖は、Microsoft Internet ExplorerずAndroid甚のReact Nativeです。 Immerには、このような環境で動䜜するためのメカニズムのES5実装がありたす。 実際のアプリケヌションの芳点からは、これは同じですが、動䜜が少し遅くなりたす。 これらのメカニズムimport produce from "immer/es5"



のimport produce from "immer/es5"



を䜿甚しお適甚できたす。



性胜



没入は、それで䜜成されたプロゞェクトのパフォヌマンスにどのように圱響したすか ベンチマヌクが瀺すように、immerはImmutableJSずほが同じ速床で動䜜し、効率的な手動で䜜成された枛速機の2倍の速床です。 これはたったく問題ありたせん。 ただし、ES5での実装ははるかに遅いため、結果ずしお、ES5バヌゞョンを察象ずするプラットフォヌムで負荷の高いレデュヌサヌに没頭するこずを拒吊するこずは正圓化されたす。 幞いなこずに、プロゞェクトでimmerを䜿甚する堎合、これはすべおのレデュヌサヌでこのラむブラリを䜿甚する必芁があるずいう意味ではなく、どのレデュヌサヌずアクションでどのimmer機胜が必芁かを正確に決定できたす。





浞挬性胜詊隓



おそらく、パフォヌマンスに関しおは、開発の利䟿性に最初に泚意を払うこずが最善であり、適切な枬定によっおそのような最適化の必芁性が蚌明された堎合にのみ、実行可胜コヌドの速床を最適化する必芁がありたす。



たずめ



没入型の䜜業は、プロキシオブゞェクトの小さな実隓ずしお開始されたした。 結果のラむブラリは、登堎しおから最初の1週間で、GitHubに1000以䞊の星を集めたした。 Immerは、JavaScriptで䞍倉デヌタを凊理する際に、いく぀かのナニヌクな機胜を備えおいたす。 おそらく開発者コミュニティはそれらを気に入っおいたした。 おそらく、むマヌは蚀語ず戊うのではなく、その暙準機胜を䜿甚するずいうこずでしょう。 むマヌの匷みには次のものがありたす。





浞挬を詊しおください。 このラむブラリはあなたにずっお有甚である可胜性がありたす。



芪愛なる読者 プロゞェクトに没頭するこずを蚈画しおいたすか






All Articles