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

資訊專(zhuān)欄INFORMATION COLUMN

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

VishKozus / 665人閱讀

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

Utils篇

這一小節(jié)里面先把基礎(chǔ)的Utils代碼過(guò)一遍,以后看核心代碼的時(shí)候方便一點(diǎn)。由于是Utils不涉及文檔,所以沒(méi)有文檔方面的展示

shallowEqual.js

從名字中就能看出這個(gè)的作用,其實(shí)就只是做了一個(gè)淺比較,對(duì)象中的value直接用 === 來(lái)比較,所以如果遇到復(fù)雜的object進(jìn)行比較,就會(huì)返回false。最基礎(chǔ)的就是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
}

代碼比較簡(jiǎn)單,基本思路就是:

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

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

對(duì)代碼的一點(diǎn)疑問(wèn):

countA++之前,要不要檢查一下是否是OwnProperty?我已經(jīng)在segmentfault里面提了問(wèn)題,LionKissDeer在github上發(fā)了issue,并得到回復(fù),確實(shí)是一個(gè)問(wèn)題。

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

storeShape.js

這個(gè)真的只是store的shape,不需要解釋

import { PropTypes } from "react"

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

簡(jiǎn)單的一個(gè)報(bào)錯(cuò)的方法,主要是檢查了console是否存在的情況。中間有提到一點(diǎn),如果console打開(kāi)了"break on all exception"選項(xiàng),那么就會(huì)在這個(gè)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

通過(guò)判斷是否是plainObject,使用lodash來(lái)判斷,并給個(gè)warning。這個(gè)方法主要是用在非production環(huán)境下,對(duì)數(shù)據(jù)格式進(jìn)行檢測(cè),并拋出異常

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

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

import { bindActionCreators } from "redux"

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

這里是用一個(gè)典型的訂閱發(fā)布模式,通過(guò)對(duì)store或父級(jí)subscription進(jìn)行監(jiān)聽(tīng),來(lái)進(jìn)行組件的更新(onStateChange)。

createListenerCollection function

先放一個(gè)工廠模式的代碼,這段主要是用來(lái)生成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)
      }
    }
  }
}

這段很簡(jiǎn)單的實(shí)現(xiàn)了一個(gè)listener的工廠,包含notify, subscribe, unsubscribe和clear等訂閱發(fā)布模式的基本功能。
值得注意的是,這里使用了current/next模式,這里主要是為了防止在notify中,listeners[i]()運(yùn)行的時(shí)候?qū)urrent對(duì)象作出內(nèi)容刪除操作,從而導(dǎo)致notify出錯(cuò)。
舉個(gè)栗子,我們要運(yùn)行下面這段代碼:

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 !!

然后我們用修改過(guò)沒(méi)有用next/current模式的代碼運(yùn)行:

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)
      }
    }
  }
}

看一下輸出結(jié)果:

發(fā)現(xiàn)第一次輸出的時(shí)候,少輸出了一次。
在這里,我們?cè)趙orld的輸出后,取消hello的subscribe。這里就會(huì)造成:輸出完world以后,刪除了hello,代碼里listeners.length的判斷就自動(dòng)減少了1,所以導(dǎo)致!!沒(méi)有輸出。
而如果使用next/current模式的話(huà),由于我們對(duì)unsubscribe的操作都是對(duì)新的next進(jìn)行操作,所以不會(huì)影響listeners,就不會(huì)出現(xiàn)上面的問(wèn)題。

Subscription class

一個(gè)簡(jiǎn)單的封裝:

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
    }
  }
}

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

不明白的一點(diǎn):
這樣子綁定上級(jí)的方法,和所有都直接綁定到store上面有什么不同?已提問(wèn)

一點(diǎn)總結(jié)

shallowEqual的簡(jiǎn)單實(shí)現(xiàn),感覺(jué)完全可以不用插件自己寫(xiě)一個(gè),比較簡(jiǎn)單。在上面關(guān)于shallowEqual的github上面,React Redux的作者有提到建議使用fbjs的shallowEqual

next/current模式,特別適用于在循環(huán)中可能會(huì)對(duì)循環(huán)對(duì)象進(jìn)行增刪的情況,可以考慮使用這個(gè)模式。通過(guò)生成一個(gè)影對(duì)象,對(duì)影對(duì)象進(jìn)行修改,需要循環(huán)的時(shí)候,再賦值給current對(duì)象

一個(gè)簡(jiǎn)單的訂閱發(fā)布模式,多層級(jí)的情況下,可以通過(guò)監(jiān)聽(tīng)上一級(jí)來(lái)進(jìn)行從root到leaf的調(diào)用

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/86683.html

相關(guān)文章

  • 深入redux技術(shù)棧

    摘要:另外,內(nèi)置的函數(shù)在經(jīng)過(guò)一系列校驗(yàn)后,觸發(fā),之后被更改,之后依次調(diào)用監(jiān)聽(tīng),完成整個(gè)狀態(tài)樹(shù)的更新。總而言之,遵守這套規(guī)范并不是強(qiáng)制性的,但是項(xiàng)目一旦稍微復(fù)雜一些,這樣做的好處就可以充分彰顯出來(lái)。 這一篇是接上一篇react進(jìn)階漫談的第二篇,這一篇主要分析redux的思想和應(yīng)用,同樣參考了網(wǎng)絡(luò)上的大量資料,但代碼同樣都是自己嘗試實(shí)踐所得,在這里分享出來(lái),僅供一起學(xué)習(xí)(上一篇地址:個(gè)人博客/s...

    imingyu 評(píng)論0 收藏0
  • 深入redux技術(shù)棧

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

    VPointer 評(píng)論0 收藏0
  • 探索 Redux4.0 版本迭代 論基礎(chǔ)談?wù)雇▽?duì)比 React context)

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

    xialong 評(píng)論0 收藏0
  • 探索 Redux4.0 版本迭代 論基礎(chǔ)談?wù)雇▽?duì)比 React context)

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

    yiliang 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<