成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

immer.js 實戰(zhàn)講解文檔

zhiwei / 1007人閱讀

摘要:無奈網(wǎng)絡上完善的文檔實在太少,所以自己寫了一份,本篇文章以貼近實戰(zhàn)的思路和流程,對進行了全面的講解。這使得成為了真正的不可變數(shù)據(jù)。的使用非常靈活,多多思考,相信你還可以發(fā)現(xiàn)更多其他的妙用參考文檔官方文檔

文章在 github 開源, 歡迎 Fork 、Star
前言

Immer 是 mobx 的作者寫的一個 immutable 庫,核心實現(xiàn)是利用 ES6 的 proxy,幾乎以最小的成本實現(xiàn)了 js 的不可變數(shù)據(jù)結構,簡單易用、體量小巧、設計巧妙,滿足了我們對JS不可變數(shù)據(jù)結構的需求。
無奈網(wǎng)絡上完善的文檔實在太少,所以自己寫了一份,本篇文章以貼近實戰(zhàn)的思路和流程,對 Immer 進行了全面的講解。

數(shù)據(jù)處理存在的問題

先定義一個初始對象,供后面例子使用:
首先定義一個currentState對象,后面的例子使用到變量currentState時,如無特殊聲明,都是指這個currentState對象

let currentState = {
  p: {
    x: [2],
  },
}

哪些情況會一不小心修改原始對象?

// Q1
let o1 = currentState;
o1.p = 1; // currentState 被修改了
o1.p.x = 1; // currentState 被修改了

// Q2
fn(currentState); // currentState 被修改了
function fn(o) {
  o.p1 = 1;
  return o;
};

// Q3
let o3 = {
  ...currentState
};
o3.p.x = 1; // currentState 被修改了

// Q4
let o4 = currentState;
o4.p.x.push(1); // currentState 被修改了
解決引用類型對象被修改的辦法

深度拷貝,但是深拷貝的成本較高,會影響性能;

ImmutableJS,非常棒的一個不可變數(shù)據(jù)結構的庫,可以解決上面的問題,But,跟 Immer 比起來,ImmutableJS 有兩個較大的不足:

需要使用者學習它的數(shù)據(jù)結構操作方式,沒有 Immer 提供的使用原生對象的操作方式簡單、易用;

它的操作結果需要通過toJS方法才能得到原生對象,這使得在操作一個對象的時候,時刻要注意操作的是原生對象還是 ImmutableJS 的返回結果,稍不注意,就會產(chǎn)生意想不到的 bug。

看來目前已知的解決方案,我們都不甚滿意,那么 Immer 又有什么高明之處呢?

immer功能介紹 安裝immer

欲善其事必先利其器,安裝 Immer 是當前第一要務

npm i --save immer
immer如何fix掉那些不爽的問題

Fix Q1、Q3

import produce from "immer";
let o1 = produce(currentState, draft => {
  draft.p.x = 1;
})

Fix Q2

import produce from "immer";
fn(currentState);
function fn(o) {
  return produce(o, draft => {
    draft.p1 = 1;
  })
};

Fix Q4

import produce from "immer";
let o4 = produce(currentState, draft => {
  draft.p.x.push(1);
})

是不是使用非常簡單,通過小試牛刀,我們簡單的了解了 Immer ,下面將對 Immer 的常用 api 分別進行介紹。

概念說明

Immer 涉及概念不多,在此將涉及到的概念先行羅列出來,閱讀本文章過程中遇到不明白的概念,可以隨時來此處查閱。

currentState
被操作對象的最初狀態(tài)

draftState
根據(jù) currentState 生成的草稿狀態(tài),它是 currentState 的代理,對 draftState 所做的任何修改都將被記錄并用于生成 nextState 。在此過程中,currentState 將不受影響

nextState
根據(jù) draftState 生成的最終狀態(tài)

produce 生產(chǎn)
用來生成 nextState 或 producer 的函數(shù)

producer 生產(chǎn)者
通過 produce 生成,用來生產(chǎn) nextState ,每次執(zhí)行相同的操作

recipe 生產(chǎn)機器
用來操作 draftState 的函數(shù)

常用api介紹

使用 Immer 前,請確認將immer包引入到模塊中

import produce from "immer"

or

import { produce } from "immer"

這兩種引用方式,produce 是完全相同的

produce

備注:出現(xiàn)PatchListener先行跳過,后面章節(jié)會做介紹

第1種使用方式:

語法:
produce(currentState, recipe: (draftState) => void | draftState, ?PatchListener): nextState

例子1:

let nextState = produce(currentState, (draft) => {

})

currentState === nextState; // true

例子2:

let currentState = {
  a: [],
  p: {
    x: 1
  }
}

let nextState = produce(currentState, (draft) => {
  draft.a.push(2);
})

currentState.a === nextState.a; // false
currentState.p === nextState.p; // true

由此可見,對 draftState 的修改都會反應到 nextState 上,而 Immer 使用的結構是共享的,nextState 在結構上又與 currentState 共享未修改的部分,共享效果如圖(借用的一篇 Immutable 文章中的動圖,侵刪):

自動凍結功能

Immer 還在內部做了一件很巧妙的事情,那就是通過 produce 生成的 nextState 是被凍結(freeze)的,(Immer 內部使用Object.freeze方法,只凍結 nextState 跟 currentState 相比修改的部分),這樣,當直接修改 nextState 時,將會報錯。
這使得 nextState 成為了真正的不可變數(shù)據(jù)。

例子:

let nextState = produce(currentState, (draft) => {
  draft.p.x.push(2);
})

currentState === nextState; // true
第2種使用方式

利用高階函數(shù)的特點,提前生成一個生產(chǎn)者 producer

語法:
produce(recipe: (draftState) => void | draftState, ?PatchListener)(currentState): nextState

例子:

let producer = produce((draft) => {
  draft.x = 2
});
let nextState = producer(currentState);
recipe的返回值

recipe 是否有返回值,nextState 的生成過程是不同的:
recipe 沒有返回值時:nextState 是根據(jù) recipe 函數(shù)內的 draftState 生成的;
recipe 有返回值時:nextState 是根據(jù) recipe 函數(shù)的返回值生成的;

let nextState = produce(
  currentState, 
  (draftState) => {
    return {
      x: 2
    }
  }
)

此時,nextState 不再是通過 draftState 生成的了,而是通過 recipe 的返回值生成的。

recipe中的this

recipe 函數(shù)內部的this指向 draftState ,也就是修改this與修改 recipe 的參數(shù) draftState ,效果是一樣的。
注意:此處的 recipe 函數(shù)不能是箭頭函數(shù),如果是箭頭函數(shù),this就無法指向 draftState 了

produce(currentState, function(draft){
  // 此處,this 指向 draftState
  draft === this; // true
})
patch補丁功能

通過此功能,可以方便進行詳細的代碼調試和跟蹤,可以知道 recipe 內的做的每次修改,還可以實現(xiàn)時間旅行。

Immer 中,一個 patch 對象是這樣的:

interface Patch {
  op: "replace" | "remove" | "add" // 一次更改的動作類型
  path: (string | number)[] // 此屬性指從樹根到被更改樹杈的路徑
  value?: any // op為 replace、add 時,才有此屬性,表示新的賦值
}

語法:

produce(
  currentState, 
  recipe,
  // 通過 patchListener 函數(shù),暴露正向和反向的補丁數(shù)組
  patchListener: (patches: Patch[], inversePatches: Patch[]) => void
)

applyPatches(currentState, changes: (patches | inversePatches)[]): nextState

例子:

import produce, { applyPatches } from "immer"

let state = {
  x: 1
}

let replaces = [];
let inverseReplaces = [];

state = produce(
  state,
  draft => {
    draft.x = 2;
    draft.y = 2;
  },
  (patches, inversePatches) => {
    replaces = patches.filter(patch => patch.op === "replace");
    inverseReplaces = inversePatches.filter(patch => patch.op === "replace");
  }
)

state = produce(state, draft => {
  draft.x = 3;
})
console.log("state1", state); // { x: 3, y: 2 }

state = applyPatches(state, replaces);
console.log("state2", state); // { x: 2, y: 2 }

state = produce(state, draft => {
  draft.x = 4;
})
console.log("state3", state); // { x: 4, y: 2 }

state = applyPatches(state, inverseReplaces);
console.log("state4", state); // { x: 1, y: 2 }

state.x的值4次打印結果分別是:3、2、4、1,實現(xiàn)了時間旅行,
可以分別打印patchesinversePatches看下,

patches數(shù)據(jù)如下:

[
  {
    op: "replace",
    path: ["x"],
    value: 2
  },
  {
    op: "add",
    path: ["y"],
    value: 2
  },
]

inversePatches數(shù)據(jù)如下:

[
  {
    op: "replace",
    path: ["x"],
    value: 1
  },
  {
    op: "remove",
    path: ["y"],
  },
]

可見,patchListener內部對數(shù)據(jù)操作做了記錄,并分別存儲為正向操作記錄和反向操作記錄,供我們使用。

至此,Immer 的常用功能和 api 我們就介紹完了。

接下來,我們看如何用 Immer ,提高 React 、Redux 項目的開發(fā)效率。

用immer優(yōu)化react項目的探索

首先定義一個state對象,后面的例子使用到變量state或訪問this.state時,如無特殊聲明,都是指這個state對象

state = {
  members: [
    {
      name: "ronffy",
      age: 30
    }
  ]
}
拋出需求

就上面定義的state,我們先拋一個需求出來,好讓后面的講解有的放矢:
members 成員中的第1個成員,年齡增加1歲

優(yōu)化setState方法 錯誤示例
this.state.members[0].age++;

只所以有的新手同學會犯這樣的錯誤,很大原因是這樣操作實在是太方便了,以至于忘記了操作 State 的規(guī)則。

下面看下正確的實現(xiàn)方法

setState的第1種實現(xiàn)方法
const { members } = this.state;
this.setState({
  members: [
    {
      ...members[0],
      age: members[0].age + 1,
    },
    ...members.slice(1),
  ]
})
setState的第2種實現(xiàn)方法
this.setState(state => {
  const { members } = state;
  return {
    members: [
      {
        ...members[0],
        age: members[0].age + 1,
      },
      ...members.slice(1)
    ]
  }
})

以上2種實現(xiàn)方式,就是setState的兩種使用方法,相比大家都不陌生了,所以就不過多說明了,接下來看下,如果用 Immer 解決,會有怎樣的煙火?

用immer更新state
this.setState(produce(draft => {
  draft.members[0].age++;
}))

是不是瞬間代碼量就少了很多,閱讀起來舒服了很多,而且更易于閱讀了。

優(yōu)化reducer immer的produce的拓展用法

在開始正式探索之前,我們先來看下 produce 第2種使用方式的拓展用法:

例子:

let obj = {};

let producer = produce((draft, arg) => {
  obj === arg; // true
});
let nextState = producer(currentState, obj);

相比 produce 第2種使用方式的例子,多定義了一個obj對象,并將其作為 producer 方法的第2個參數(shù)傳了進去;可以看到, produce 內的 recipe 回調函數(shù)的第2個參數(shù)與obj對象是指向同一塊內存。
ok,我們在知道了 produce 的這種拓展用法后,看看能夠在 Redux 中發(fā)揮什么功效?

普通reducer怎樣解決上面拋出的需求
const reducer = (state, action) => {
  switch (action.type) {
    case "ADD_AGE":
      const { members } = state;
      return {
        ...state,
        members: [
          {
            ...members[0],
            age: members[0].age + 1,
          },
          ...members.slice(1),
        ]
      }
    default:
      return state
  }
}
集合immer,reducer可以怎樣寫
const reducer = (state, action) => produce(state, draft => {
  switch (action.type) {
    case "ADD_AGE":
      draft.members[0].age++;
  }
})

可以看到,通過 produce ,我們的代碼量已經(jīng)精簡了很多;
不過仔細觀察不難發(fā)現(xiàn),利用 produce 能夠先制造出 producer 的特點,代碼還能更優(yōu)雅:

const reducer = produce((draft, action) => {
  switch (action.type) {
    case "ADD_AGE":
      draft.members[0].age++;
  }
})

好了,至此,Immer 優(yōu)化 reducer 的方法也講解完畢。

Immer 的使用非常靈活,多多思考,相信你還可以發(fā)現(xiàn) Immer 更多其他的妙用!

參考文檔

官方文檔

Introducing Immer: Immutability the easy way

文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉載請注明本文地址:http://m.hztianpu.com/yun/99761.html

相關文章

  • immer.js 簡介及源碼解析

    摘要:例如維護一份在內部,來判斷是否有變化,下面這個例子就是一個構造函數(shù),如果將它的實例傳入對象作為第一個參數(shù),就能夠后面的處理對象中使用其中的方法上面這個構造函數(shù)相比源代碼省略了很多判斷的部分。 showImg(https://segmentfault.com/img/bV27Dy?w=1400&h=544); 博客鏈接:下一代狀態(tài)管理工具 immer 簡介及源碼解析 JS 里面的變量類...

    Profeel 評論0 收藏0
  • Immer.js簡析

    摘要:所以整個過程只涉及三個輸入狀態(tài),中間狀態(tài),輸出狀態(tài)關鍵是是如何生成,如何應用修改,如何生成最終的。至此基本把上的模式解析完畢。結束實現(xiàn)還是相當巧妙的,以后可以在狀態(tài)管理上使用一下。 開始 在函數(shù)式編程中,Immutable這個特性是相當重要的,但是在Javascript中很明顯是沒辦法從語言層面提供支持,但是還有其他庫(例如:Immutable.js)可以提供給開發(fā)者用上這樣的特性,所...

    Aceyclee 評論0 收藏0
  • Immer.js簡析

    摘要:所以整個過程只涉及三個輸入狀態(tài),中間狀態(tài),輸出狀態(tài)關鍵是是如何生成,如何應用修改,如何生成最終的。至此基本把上的模式解析完畢。結束實現(xiàn)還是相當巧妙的,以后可以在狀態(tài)管理上使用一下。 開始 在函數(shù)式編程中,Immutable這個特性是相當重要的,但是在Javascript中很明顯是沒辦法從語言層面提供支持,但是還有其他庫(例如:Immutable.js)可以提供給開發(fā)者用上這樣的特性,所...

    dackel 評論0 收藏0
  • 精讀《源碼學習》

    摘要:精讀原文介紹了學習源碼的兩個技巧,并利用實例說明了源碼學習過程中可以學到許多周邊知識,都讓我們受益匪淺。討論地址是精讀源碼學習如果你想?yún)⑴c討論,請點擊這里,每周都有新的主題,周末或周一發(fā)布。 1. 引言 javascript-knowledge-reading-source-code 這篇文章介紹了閱讀源碼的重要性,精讀系列也已有八期源碼系列文章,分別是: 精讀《Immer.js》源...

    aboutU 評論0 收藏0

發(fā)表評論

0條評論

zhiwei

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<