国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

immer.js 實戰講解文檔

zhiwei / 946人閱讀

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

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

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

數據處理存在的問題

先定義一個初始對象,供后面例子使用:
首先定義一個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,非常棒的一個不可變數據結構的庫,可以解決上面的問題,But,跟 Immer 比起來,ImmutableJS 有兩個較大的不足:

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

它的操作結果需要通過toJS方法才能得到原生對象,這使得在操作一個對象的時候,時刻要注意操作的是原生對象還是 ImmutableJS 的返回結果,稍不注意,就會產生意想不到的 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
被操作對象的最初狀態

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

nextState
根據 draftState 生成的最終狀態

produce 生產
用來生成 nextState 或 producer 的函數

producer 生產者
通過 produce 生成,用來生產 nextState ,每次執行相同的操作

recipe 生產機器
用來操作 draftState 的函數

常用api介紹

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

import produce from "immer"

or

import { produce } from "immer"

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

produce

備注:出現PatchListener先行跳過,后面章節會做介紹

第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 成為了真正的不可變數據。

例子:

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

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

利用高階函數的特點,提前生成一個生產者 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 是根據 recipe 函數內的 draftState 生成的;
recipe 有返回值時:nextState 是根據 recipe 函數的返回值生成的;

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

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

recipe中的this

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

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

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

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

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

語法:

produce(
  currentState, 
  recipe,
  // 通過 patchListener 函數,暴露正向和反向的補丁數組
  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,實現了時間旅行,
可以分別打印patchesinversePatches看下,

patches數據如下:

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

inversePatches數據如下:

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

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

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

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

用immer優化react項目的探索

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

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

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

優化setState方法 錯誤示例
this.state.members[0].age++;

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

下面看下正確的實現方法

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

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

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

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

優化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個參數傳了進去;可以看到, produce 內的 recipe 回調函數的第2個參數與obj對象是指向同一塊內存。
ok,我們在知道了 produce 的這種拓展用法后,看看能夠在 Redux 中發揮什么功效?

普通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 ,我們的代碼量已經精簡了很多;
不過仔細觀察不難發現,利用 produce 能夠先制造出 producer 的特點,代碼還能更優雅:

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

好了,至此,Immer 優化 reducer 的方法也講解完畢。

Immer 的使用非常靈活,多多思考,相信你還可以發現 Immer 更多其他的妙用!

參考文檔

官方文檔

Introducing Immer: Immutability the easy way

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

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

相關文章

  • immer.js 簡介及源碼解析

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

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

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

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

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

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

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

    aboutU 評論0 收藏0

發表評論

0條評論

zhiwei

|高級講師

TA的文章

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