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

資訊專欄INFORMATION COLUMN

React Redux: 從文檔看源碼 - Utils篇

VishKozus / 619人閱讀

注:這篇文章只是講解React Redux這一層,并不包含Redux部分。Redux有計劃去學習,等以后學習了Redux源碼以后再做分析
注:代碼基于現在(2016.12.29)React Redux的最新版本(5.0.1)

Utils篇

這一小節里面先把基礎的Utils代碼過一遍,以后看核心代碼的時候方便一點。由于是Utils不涉及文檔,所以沒有文檔方面的展示

shallowEqual.js

從名字中就能看出這個的作用,其實就只是做了一個淺比較,對象中的value直接用 === 來比較,所以如果遇到復雜的object進行比較,就會返回false。最基礎的就是shallowEqual({a:{}}, {a:{}}) === false

const hasOwn = Object.prototype.hasOwnProperty

export default function shallowEqual(a, b) {
  if (a === b) return true

  let countA = 0
  let countB = 0
  
  for (let key in a) {
    if (hasOwn.call(a, key) && a[key] !== b[key]) return false
    countA++
  }

  for (let key in b) {
    if (hasOwn.call(b, key)) countB++
  }

  return countA === countB
}

代碼比較簡單,基本思路就是:

比較a對象中的自身屬性是否在b中也存在并且相等,如果不存在或不想等,返回false

比較b中自身屬性的數量是否等于a中自身屬性的數量,如果不相同,返回false

對代碼的一點疑問:

countA++之前,要不要檢查一下是否是OwnProperty?我已經在segmentfault里面提了問題,LionKissDeer在github上發了issue,并得到回復,確實是一個問題。

用countA, countB來記錄數量,而不是用Object.keys(a).length來進行對比,可以理解為減少操作和不必要的內存使用。那么是否只用一個countA,然后再第二個for...in中進行countA--,最后比較countA === 0更好?

storeShape.js

這個真的只是store的shape,不需要解釋

import { PropTypes } from "react"

export default PropTypes.shape({
  subscribe: PropTypes.func.isRequired,
  dispatch: PropTypes.func.isRequired,
  getState: PropTypes.func.isRequired
})
warning.js

簡單的一個報錯的方法,主要是檢查了console是否存在的情況。中間有提到一點,如果console打開了"break on all exception"選項,那么就會在這個warning的地方停下

/**
 * Prints a warning in the console if it exists.
 *
 * @param {String} message The warning message.
 * @returns {void}
 */
export default function warning(message) {
  /* eslint-disable no-console */
  if (typeof console !== "undefined" && typeof console.error === "function") {
    console.error(message)
  }
  /* eslint-enable no-console */
  try {
    // This error was thrown as a convenience so that if you enable
    // "break on all exceptions" in your console,
    // it would pause the execution at this line.
    throw new Error(message)
    /* eslint-disable no-empty */
  } catch (e) {}
  /* eslint-enable no-empty */
}
verifyPlainObject.js

通過判斷是否是plainObject,使用lodash來判斷,并給個warning。這個方法主要是用在非production環境下,對數據格式進行檢測,并拋出異常

import isPlainObject from "lodash/isPlainObject"
import warning from "./warning"

export default function verifyPlainObject(value, displayName, methodName) {
  if (!isPlainObject(value)) {
    warning(
      `${methodName}() in ${displayName} must return a plain object. Instead received ${value}.`
    )
  }
}
wrapActionCreators.js

這里是通過這個方法生成一個(actionCreators)=>(dispatch)=>()=>binded actions的方法。bindActionCreators的作用是返回用dispatch綁定過的actions,具體方法可以在Redux文檔中查看。
然而,我并沒有在react-redux的代碼里看到這個方法的調用,文檔中也沒有提到過這個方法,雖然在mapDispatchToProps.js中看到了一樣的代碼片段…不知道是不是老代碼沒有刪除干凈,還是新功能需要,但是還沒有上線…

import { bindActionCreators } from "redux"

export default function wrapActionCreators(actionCreators) {
  return dispatch => bindActionCreators(actionCreators, dispatch)
}
Subscription.js

這里是用一個典型的訂閱發布模式,通過對store或父級subscription進行監聽,來進行組件的更新(onStateChange)。

createListenerCollection function

先放一個工廠模式的代碼,這段主要是用來生成listener的工廠:

const CLEARED = null
const nullListeners = { notify() {} }

function createListenerCollection() {
  // the current/next pattern is copied from redux"s createStore code.
  // TODO: refactor+expose that code to be reusable here?
  let current = []
  let next = []

  return {
    clear() {
      next = CLEARED
      current = CLEARED
    },

    notify() {
      const listeners = current = next
      for (let i = 0; i < listeners.length; i++) {
        listeners[i]()
      }
    },

    subscribe(listener) {
      let isSubscribed = true
      if (next === current) next = current.slice()
      next.push(listener)

      return function unsubscribe() {
        if (!isSubscribed || current === CLEARED) return
        isSubscribed = false

        if (next === current) next = current.slice()
        next.splice(next.indexOf(listener), 1)
      }
    }
  }
}

這段很簡單的實現了一個listener的工廠,包含notify, subscribe, unsubscribe和clear等訂閱發布模式的基本功能。
值得注意的是,這里使用了current/next模式,這里主要是為了防止在notify中,listeners[i]()運行的時候對current對象作出內容刪除操作,從而導致notify出錯。
舉個栗子,我們要運行下面這段代碼:

var listener = createListenerCollection();

var helloUnsub = listener.subscribe(()=>{ console.log("Hello");  });
var worldUnsub = listener.subscribe(()=>{ console.log("world"); helloUnsub(); });
var unsub = listener.subscribe(()=>{ console.log("!!"); });

listener.notify(); // 期望輸出的是Hello world !!

listener.notify(); // 期望輸出的是world !!

然后我們用修改過沒有用next/current模式的代碼運行:

function createListenerCollection() {
  let current = []

  return {
    notify() {
      const listeners = current
      for (let i = 0; i < listeners.length; i++) {
        listeners[i]()
      }
    },

    subscribe(listener) {
      let isSubscribed = true
      current.push(listener)

      return function unsubscribe() {
        if (!isSubscribed || current === CLEARED) return
        isSubscribed = false

        current.splice(current.indexOf(listener), 1)
      }
    }
  }
}

看一下輸出結果:

發現第一次輸出的時候,少輸出了一次。
在這里,我們在world的輸出后,取消hello的subscribe。這里就會造成:輸出完world以后,刪除了hello,代碼里listeners.length的判斷就自動減少了1,所以導致!!沒有輸出。
而如果使用next/current模式的話,由于我們對unsubscribe的操作都是對新的next進行操作,所以不會影響listeners,就不會出現上面的問題。

Subscription class

一個簡單的封裝:

export default class Subscription {
  constructor(store, parentSub) {
    this.store = store
    this.parentSub = parentSub
    this.unsubscribe = null
    this.listeners = nullListeners
  }

  addNestedSub(listener) {
    this.trySubscribe()
    return this.listeners.subscribe(listener)
  }

  notifyNestedSubs() {
    this.listeners.notify()
  }

  isSubscribed() {
    return Boolean(this.unsubscribe)
  }

  trySubscribe() {
    if (!this.unsubscribe) {
      // this.onStateChange is set by connectAdvanced.initSubscription()
      this.unsubscribe = this.parentSub
        ? this.parentSub.addNestedSub(this.onStateChange)
        : this.store.subscribe(this.onStateChange)
 
      this.listeners = createListenerCollection()
    }
  }

  tryUnsubscribe() {
    if (this.unsubscribe) {
      this.unsubscribe()
      this.unsubscribe = null
      this.listeners.clear()
      this.listeners = nullListeners
    }
  }
}

唯一需要注意的一點是,他們的onStateChange事件其實是綁定在父級(parentSub)或者store的subscription上面的。至于為什么要綁定在父級或者store上面,是因為父級發生了改變,就會通知下級,下級再通知下下級…所以下級需要連接到上級上。
調用模式大概是這樣子的,由上到下由頂層store一層一層到leaf:

不明白的一點:
這樣子綁定上級的方法,和所有都直接綁定到store上面有什么不同?已提問

一點總結

shallowEqual的簡單實現,感覺完全可以不用插件自己寫一個,比較簡單。在上面關于shallowEqual的github上面,React Redux的作者有提到建議使用fbjs的shallowEqual

next/current模式,特別適用于在循環中可能會對循環對象進行增刪的情況,可以考慮使用這個模式。通過生成一個影對象,對影對象進行修改,需要循環的時候,再賦值給current對象

一個簡單的訂閱發布模式,多層級的情況下,可以通過監聽上一級來進行從root到leaf的調用

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

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

相關文章

  • 深入redux技術棧

    摘要:另外,內置的函數在經過一系列校驗后,觸發,之后被更改,之后依次調用監聽,完成整個狀態樹的更新??偠灾?,遵守這套規范并不是強制性的,但是項目一旦稍微復雜一些,這樣做的好處就可以充分彰顯出來。 這一篇是接上一篇react進階漫談的第二篇,這一篇主要分析redux的思想和應用,同樣參考了網絡上的大量資料,但代碼同樣都是自己嘗試實踐所得,在這里分享出來,僅供一起學習(上一篇地址:個人博客/s...

    imingyu 評論0 收藏0
  • 深入redux技術棧

    摘要:另外,內置的函數在經過一系列校驗后,觸發,之后被更改,之后依次調用監聽,完成整個狀態樹的更新??偠灾?,遵守這套規范并不是強制性的,但是項目一旦稍微復雜一些,這樣做的好處就可以充分彰顯出來。 這一篇是接上一篇react進階漫談的第二篇,這一篇主要分析redux的思想和應用,同樣參考了網絡上的大量資料,但代碼同樣都是自己嘗試實踐所得,在這里分享出來,僅供一起學習(上一篇地址:個人博客/s...

    VPointer 評論0 收藏0
  • 探索 Redux4.0 版本迭代 論基礎談展望(對比 React context)

    摘要:在幾天前發布了新版本,被合入。但是在版本迭代的背后很多有趣的設計值得了解。參數處理這項改動由提出。對透明化處理中的,達到將包裹起來的目的。對的凍結認為,在中使用和方法是一種反模式。尤其是這樣的新,某些開發者認為將逐漸取代。 showImg(https://segmentfault.com/img/remote/1460000014571148); Redux 在幾天前(2018.04....

    xialong 評論0 收藏0
  • 探索 Redux4.0 版本迭代 論基礎談展望(對比 React context)

    摘要:在幾天前發布了新版本,被合入。但是在版本迭代的背后很多有趣的設計值得了解。參數處理這項改動由提出。對透明化處理中的,達到將包裹起來的目的。對的凍結認為,在中使用和方法是一種反模式。尤其是這樣的新,某些開發者認為將逐漸取代。 showImg(https://segmentfault.com/img/remote/1460000014571148); Redux 在幾天前(2018.04....

    yiliang 評論0 收藏0

發表評論

0條評論

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