【中文】React組件熱加載原理及實(shí)現(xiàn)

Hot Reloading in React

本文是Dan Abramov發(fā)表于Medium文章的部分譯文,只截取了其中部分內(nèi)容,僅供學(xué)習(xí)交流,主要作為本人其他博客的素材使用,禁止轉(zhuǎn)載

React Hto Loader是我第一個(gè)比較知名的開源項(xiàng)目,據(jù)我所知,這是第一個(gè)可以實(shí)現(xiàn)讓使用者無需重新掛載組件組件或是丟失組件state的工具,在剛開始的時(shí)候我只是做出了一個(gè)Demo用于在臺上進(jìn)行演示,不過在之后我發(fā)現(xiàn)了大家對這個(gè)工具的巨大熱情,所以我花費(fèi)了幾周的時(shí)間開發(fā)出了這個(gè)工具。

預(yù)覽圖片
預(yù)覽圖片

第一次嘗試: 直接使用HMR

在創(chuàng)建這個(gè)特別的項(xiàng)目之前,我曾想到直接使用webpack提供的HMR來替換項(xiàng)目中的根節(jié)點(diǎn)并且重新渲染我們的整個(gè)React樹。

但是需要注意的是HMR根本不是為React定制的。James Long在這片文章中說的很不錯(cuò)。簡單來說他只是一個(gè)「當(dāng)新版本的模塊可以使用過后,調(diào)用設(shè)置好的回調(diào)函數(shù),讓我們便于對新模塊做一些處理」的作用。這件事情會發(fā)生在你每次保存修改好的文件的時(shí)候。

一個(gè)純粹的HMR來實(shí)現(xiàn)熱加載就像是這樣的:

var App = require('./App')
var React = require('react')
var ReactDOM = require('react-dom')

// Render the root component normally
var rootEl = document.getElementById('root')
ReactDOM.render(<App />, rootEl)

// Are we in development mode?
if (module.hot) {
  // Whenever a new version of App.js is available
  module.hot.accept('./App', function () {
    // Require the new version and render it instead
    var NextApp = require('./App')
    ReactDOM.render(<NextApp />, rootEl)
  })
}

只要你按照類似上面的配置處理好了你的react引用,那么只要出現(xiàn)了文件的修改,你的應(yīng)用狀態(tài)就可以直接刷新而不用刷新瀏覽器界面啦。

這樣的實(shí)現(xiàn)是最純粹的,沒有用到React-Hot-Loader,React-Transform,沒有對你的模塊做任何有關(guān)語義化的改變,只是做到了監(jiān)聽變化,適時(shí)地插入新的<script/>標(biāo)簽用于引入模塊,并且調(diào)用一個(gè)回調(diào)函數(shù)而已

內(nèi)部的模塊改變也會被外界的hot.accept捕捉到,如果只是在子組件中發(fā)生了變化而其上沒有設(shè)置accept來捕捉這次的變化的話,它將會往上冒泡直到被捕捉進(jìn)行回調(diào)的處理(就像上文一樣),否則會給用戶拋出一個(gè)警告。

因?yàn)槲覀兪窃贏pp中進(jìn)行了熱模塊的處理,也就是說我們在App中使用的子孫組件都會被重新掛載處理。

舉個(gè)栗子,我們有一個(gè)Button組件被UserProfile和NavBar使用,并且這兩個(gè)組件都被App引用。

因?yàn)閕ndex是唯一一個(gè)引入了App的模塊,并且他其中還配置了module.hot.accept('./App', callback)的處理操作,webpack會生成一個(gè)包含了我們所有被影響的module的「update bundle」給他處理(就是我們的XXX.hot-update.js文件)

當(dāng)發(fā)現(xiàn)一個(gè)升級的App的時(shí)候,直接就能重新渲染React啦,

// Whenever a new version of App.js is available
module.hot.accept('./App', function () {
  // Require the new version and render it instead
  var NextApp = require('./App')
  ReactDOM.render(<NextApp />, rootEl)
})

不過這是我們想要的結(jié)果嗎?當(dāng)然,不完全是

DOM和組件的舊State都不見了

新版本的module其實(shí)就是放在script中重新執(zhí)行的模塊罷了!由于App的重新運(yùn)行,他的Class定義和之前的class不是同一個(gè)了,只是從我們的理解上來說覺得他們是一個(gè)class的不同版本而已

對于React來說,你所做的只是在渲染一個(gè)全新的組件,所以他會幫你把舊的組件給卸載掉,因?yàn)樗麤]有辦法幫你把這個(gè)已經(jīng)存在的組件實(shí)例的class替換一下!官方的接口實(shí)際上都是創(chuàng)建新節(jié)點(diǎn)的create相關(guān)接口

所以啦~其實(shí)React必須這樣做才能夠應(yīng)用上新的組件

或許可行的方式:提取出state

James Kyle近期指出我們應(yīng)用可以使用維護(hù)單一狀態(tài)樹,就像Redux一樣。在這樣的應(yīng)用里,我們重要的數(shù)據(jù)狀態(tài)往往是存在Redux中的,所以并不需要去想著維護(hù)在每個(gè)組件中的state(可能是一些展示類的需求)。

受到他的啟發(fā),我準(zhǔn)備了這個(gè)PR來移除了Redux應(yīng)用中對state的維護(hù),轉(zhuǎn)而使用純凈的HMR。James同樣建議使用isolated-core來應(yīng)對這種情況,我還沒有查看過,但是如果你對這個(gè)實(shí)現(xiàn)感興趣的話我建議你去看看這個(gè)demo。

通過這種方式,你甚至可以把狀態(tài)存儲到localStorage里面,就連刷新頁面也不能阻止你保留以前的狀態(tài),豈不是美滋滋。

總之,如果你是使用Redux來存儲幾乎所有有價(jià)值的信息的話,那么我強(qiáng)烈建議你就使用HMR就好了,這樣已經(jīng)完全夠用并且很簡潔。但是我們還有些用戶需要在組件自身中存儲比較復(fù)雜的狀態(tài),那我們還是得繼續(xù)研究下去了

保留DOM和本地State

現(xiàn)在你知道這中間會出現(xiàn)哪些問題了,我想出來了兩個(gè)法子或許能解決這個(gè)問題

  1. 想個(gè)法子把我們的React實(shí)例和DOM與State的聯(lián)系解開,利用新獲得的class創(chuàng)建新實(shí)例的時(shí)候再把他們結(jié)合起來,這樣達(dá)到我們保留狀態(tài)的目的
  2. 或者是,通過代理的方式在每個(gè)組件外面加上一層代理組件,讓React從她的視角看到我們的組件始終是保持一致的都是外面那層代理,但是實(shí)際上每次進(jìn)行升級替換的時(shí)候我們都是更新的內(nèi)部的實(shí)例

惜敗的嘗試:重新結(jié)合樹結(jié)構(gòu)

這個(gè)方法可能更適合React的長期發(fā)展,但是目前官方還沒有提供合適的接口來完成相關(guān)工作(合并state到React組件上,替換實(shí)例而不卸載DOM和執(zhí)行生命周期函數(shù))即使我們通過內(nèi)部的API解決了上面的問題,我們?nèi)匀贿€有其他問題需要解決

再舉個(gè)例子,我們的組件常常訂閱了Flux/Reudx的Store或者是在componentDidMount中會有其他帶來副作用的操作。就算我們可以悄悄的把舊的實(shí)例換成新的(不破壞DOM和state),還會出現(xiàn)事件綁定無法轉(zhuǎn)移的問題

為了為我們的實(shí)例添加上訂閱,在替換過程中,我們需要將生命周期函數(shù)重新執(zhí)行一下子。但是這樣就會出現(xiàn)componentDidMount執(zhí)行兩次的情況,鬼知道會做出什么動(dòng)作,是自己寫的代碼還能盡量避免,但是使用三方的庫時(shí)就沒那么好說了

最終來說,如果狀態(tài)的訂閱能夠脫離生命周期存在或者說React不再那么依賴class和實(shí)例,這個(gè)方法也可能有實(shí)現(xiàn)的方式;當(dāng)然上面所說的其實(shí)也是我認(rèn)為的React的一些改進(jìn)方向吧

成功的嘗試:代理組件Class

這個(gè)方法是我在React Hot Loader和React Transform中使用的方法,這是一個(gè)相當(dāng)有入侵性的方法,他會改變我們代碼結(jié)構(gòu)。不過實(shí)際上他現(xiàn)在還是工作的非常好在當(dāng)前版本和以后的一段時(shí)間看來。

主要是路就是把每個(gè)組件放到一個(gè)Proxy里面,在這里說的Proxy并不是指的ES2015中的那個(gè),我想用的是HOC就能夠?qū)崿F(xiàn)這個(gè)功能(口嫌體正直,現(xiàn)在的版本就是直接用的Proxy)

這樣的代理可以正常的工作,把我們的實(shí)際用到的組件放到里面包起來,進(jìn)行了更改只需要HOC把新的組件更新一個(gè)并把State放過去

出現(xiàn)的問題:到哪兒去做這層代理

一個(gè)大家經(jīng)常誤會的問題:React Hot Loader其實(shí)并不是一個(gè)webpack中使用的loader(錯(cuò)的)

她是一個(gè)loader,因?yàn)閘oader做的就是對我們的代碼進(jìn)行transform的操作,把我們的代碼進(jìn)行格式化變形,像json-loader把json處理為js文件,style-loader也處理成js文件這樣子。

相似的,React Hot Loader也是一個(gè)編譯時(shí)轉(zhuǎn)換(compile time transform),并且所做的也不是簡單的加上一段代碼這么簡單,他會找到所有module.exports導(dǎo)出的Component把他們放入想對應(yīng)的代理中,并把外面這層代理給返回出去。

通過這樣的方法,當(dāng)App在渲染NavBar的時(shí)候,他實(shí)際上渲染的是<NavBarProxy/>,render會通過調(diào)用NavBar#render()的方法進(jìn)行實(shí)際的渲染,保證內(nèi)部的模塊時(shí)刻是最新的

代理的對象通過拼湊出來的uid(文件名+組件名)存儲在某個(gè)全局的對象中,當(dāng)一次更新進(jìn)行時(shí),與之匹配的Proxy就會吸收新版本的class,并且進(jìn)行重新的渲染

從module.exports中尋找組件開始看起來非常合理,這是符合我們平時(shí)一般的開發(fā)習(xí)慣的,但是隨著前端的不斷發(fā)展,慢慢的這么做開始不能覆蓋另一些出現(xiàn)較多的情況了

  • 由于高階組件的盛行,人們開始export高階組件而不是原生的組件,從結(jié)果來說,React Hot Loader就沒辦法發(fā)現(xiàn)這些組件了,當(dāng)然也就無法代理。他們的DOMinate和狀態(tài)無法保留,每次都會重新掛載。尤其是哪些提供樣式的比如React JSS
  • React 0.14介紹了無狀態(tài)的組件,鼓勵(lì)我們在單個(gè)文件中進(jìn)行微組件化處理(即把組件拆分為最小單位,然后進(jìn)行組合)。即使我們檢查輸出函數(shù)的toString方法,尋找createElement方法的調(diào)用,并假設(shè)他們都是React組件,還是沒辦法找到那些本地組件(未export)。這些組件也沒辦法保留狀態(tài),脆弱的不堪一擊

不過,就算只是讓開發(fā)者把所有的組件都export出來都還是不夠的,他們在文件內(nèi)部的相互依賴,我們沒有辦法通過簡單的改變module.exports進(jìn)行改變。

聽到這里肯定有很多人會說了:隨便啦,就用一個(gè)全局存儲就好了嘛,管那么多組件內(nèi)的狀態(tài)干嘛,肯定不會有多大用處。。。?;蛟S我真該信了你們的邪

問題:對于webpack系統(tǒng)的依賴

這可是個(gè)大問題,我們現(xiàn)在的系統(tǒng)非常依賴webpack的打包和HMR機(jī)制,如果不是用的webpack,我們要支持rollupheFIS一類的工具該咋辦呢?(譯者:巧了嗎這不是,你只針對webpack我不知道又得改多少東西)所以還是盡量脫離這個(gè)系統(tǒng),便于別人移植的時(shí)候不用做那么多的修改

React Transform

差不多就是那段時(shí)間我寫下了這篇博客 The Death of React Hot Loader,我在尋找方法來解決上述的一些問題

Babel似風(fēng)暴一樣影響了整個(gè)前端的生態(tài)圈,我正好也需要某種靜態(tài)分析的工具來定位組件(甚至沒有被export出去)并給他們外面包上一層代理。babel看起來正合適

除此之外,我剛好還在想個(gè)法子實(shí)現(xiàn)錯(cuò)誤處理。我們的組件如果在render中發(fā)生錯(cuò)誤每次都會直接導(dǎo)致渲染失敗進(jìn)入一個(gè)錯(cuò)誤的state狀態(tài)并變得無法更新,我想通過babel更加優(yōu)雅的處理這個(gè)問題

所以我想到:咦,為什么不寫一個(gè)babel插件來定位組件的位置,并把他們直接的按照我們定義好的模式來進(jìn)行變換呢?如果別人在這個(gè)基礎(chǔ)上也來開發(fā)其他的卡法工具不會也很酷嗎?比如說在頁面上對應(yīng)組件顯示性能熱力圖什么的

這就是React Transform要做的事情了

實(shí)現(xiàn)1:過度模塊化

我不確定哪些功能是以后還需要進(jìn)行使用和維護(hù)的,所以我在React Transform下面創(chuàng)建了很多獨(dú)立的模塊:

  1. React Proxy,實(shí)現(xiàn)了底層的Component代理
  2. React Transform HMR,為找到的Component做一個(gè)代理并把代理的列表(Map)放到全局中,當(dāng)發(fā)現(xiàn)子組件需要更新的時(shí)候,proxy也會應(yīng)用上新的組件
  3. React Transform Catch Errors,把render()函數(shù)通過try/catch包起來并展示一個(gè)可自定義的組件而不是任其發(fā)展
  4. Babel Plugin for React Transform,盡最大努力找到你代碼中所有的React組件,在編譯階段提取出他們的信息并對他們使用你需要的Transform進(jìn)行處理(如:React Transform HMR)
  5. React Transform Boilerplate,展示了如何結(jié)合使用上述這些東西

問題:過多可活動(dòng)部分?

上述的實(shí)現(xiàn)是一個(gè)雙刃劍,好的方面可以讓我們更加利于實(shí)驗(yàn)開發(fā),但壞處又是大大的增加了普通開發(fā)者的使用成本,有些東西其實(shí)沒必要向他們暴露出來,比如:「proxies」,「HMR」,「hot middleware」,「error catcher」等等

我還想著Babel6能夠盡快出來,然后做一個(gè)presets,那就可以把我們的默認(rèn)配置直接封裝好放到上面了。然而Babel6比我預(yù)期的發(fā)布時(shí)間晚太多了(不過畢竟工程量那么大,能做出來我也很感激了)

React Transform比我想象中還快的流行開來,并且現(xiàn)在在boilerplate中的完整配置需要盡快處理掉。不然會給用戶帶來一些誤解。事實(shí)上我們在Redux中的使用讓情況變得更糟糕了

解決方案:合理的默認(rèn)值

在Babel6升級并發(fā)布了官方的preset之后,模塊化就不再是一個(gè)問題了,事實(shí)上還變成了一件好事,我們針對不同的環(huán)境可以只取到需要的部分(比如React Naive)。這也是給我上了一課,在將某個(gè)東西進(jìn)行模塊化的時(shí)候,你必須提供一個(gè)好的默認(rèn)值,如果有人想要搞明白其中的原理他們必定會自己去看其中的東西

問題:高階組件又搞事情

有時(shí)候問題總是對立的,你沒辦法同時(shí)兼顧兩面

React-Hot-Loader可以找到你在文件中導(dǎo)出的組件,但是看不到文件中的本地組件,以下圖為例,能夠?qū)seSheet的高階組件做一層代理處理,但是沒辦法對Counter進(jìn)行處理,文件一刷新Counter中的狀態(tài)就會丟失

// React Hot Loader doesn’t see it
// React Transform sees it
class Counter extends Component {
  constructor(props) {
    super(props)
    this.state = { counter: 0 }
    this.handleClick = this.handleClick.bind(this)
  }
  handleClick() {
    this.setState({
      counter: this.state.counter + 1
    })
  }
  render() {
    return (
      <div className={this.props.sheet.container} onClick={this.handleClick}>
        {this.state.counter}
      </div>
    )
  }
}

const styles = {
  container: { 
    backgroundColor: 'yellow'
  }
}

// React Hot Loader sees it
// React Transform doesn’t see it
export default useSheet(styles)(Counter)

React Transform 通過了靜態(tài)分析,在文件中尋找繼承自React.Component的組件或者是通過React.createClass創(chuàng)建的組件的操作,"修復(fù)"了這一問題

猜猜我們忘了什么什么東西,輸出去的高階組件??!在這個(gè)例子里面,React Transform會保存Counter組件的狀態(tài),并且熱替換他的render和handleClick方法,但是任何styles的改變都不會得到回應(yīng)因?yàn)樗恢纔seSheet也會返回一個(gè)需要代理的組件

通常人們發(fā)現(xiàn)這個(gè)問題是在使用Redux的時(shí)候,這里就是因?yàn)闆]有把connect處理后的組件做代理,導(dǎo)致了selector和action creator都不會被替換了

問題:直接的包裹組件是入侵性的

通過尋找繼承或者是createClass創(chuàng)建的對象并不困難。然而這有一個(gè)潛在的問題,我相信你一定不想看到這個(gè)確實(shí)出現(xiàn)的錯(cuò)誤。

在React 0.14之后,這個(gè)問題變得愈發(fā)難以處理。任何返回ReactElement的函數(shù)都有可能是一個(gè)組件。但是你沒辦法保證,所以只能啟發(fā)式的搜尋。比如說,你可以說頂?shù)鬃饔糜蛑惺褂肞ascal命名的,使用JSX的并且接受兩個(gè)參數(shù)以下的或許能夠被稱為組件?這樣能避免問題嗎?或許是不能的

這樣甚至比以前更糟糕了,你還得告訴React Transform那些是組件。如果React推出了新的組件定義方法怎么辦?我們再把Transform重寫一遍嗎?

最后,其實(shí)我們覺得通過靜態(tài)分析吧他們包起來已經(jīng)足夠了。你必須得處理無盡的導(dǎo)出函數(shù)和class,包括:default的啊,有名字的啊,函數(shù)聲明啦,createClass調(diào)用啦等等等等。每種情況你都得想個(gè)法子進(jìn)行處理避免遺漏。

對于函數(shù)式組件的提供支持是目前呼聲最高的一個(gè)特性,但是我現(xiàn)在不能這么搞,畢竟這個(gè)東西的工作量太大了,會給我自己的和其他維護(hù)者帶來巨大的壓力,并且還存在著一些邊緣情況有潛在的風(fēng)險(xiǎn)

那。。是否應(yīng)該選擇放棄呢?

長路漫漫

我仍然覺得React Hot Loader和React Transform是很成功的項(xiàng)目,盡管他們內(nèi)部有些缺點(diǎn)而且有些局限。我仍然相信熱加載總有一天能夠?qū)崿F(xiàn),我們不應(yīng)該停止繼續(xù)嘗試。講真,這是我在這幾個(gè)月以來第一次對熱加載感覺到樂觀。

React Naive在React Transform上面封裝了一個(gè)hot reloading的實(shí)現(xiàn)?,F(xiàn)在已經(jīng)足夠穩(wěn)定了,但是我也相信我們以后會有更好更簡單的解決方案

以下開始介紹目前實(shí)現(xiàn)的解決方案

方案:盡量使用Vanilla HMR

這是最直接的一個(gè)方案。正如James Kyle建議,如果你把狀態(tài)存在Redux這樣的東西里,其實(shí)不需要考慮在Reload的時(shí)候保留DOM,考慮一下就那么用或者試試isolated-core這樣的,這會讓你的項(xiàng)目簡單很多!

方案:放棄配制Transform

當(dāng)React Native在使用fork的一份React封裝的時(shí)候,配置這些東西是很有用處的,但是現(xiàn)在直接使用的是react這個(gè)package了,他其中也實(shí)現(xiàn)了HMR的一部分需要的功能,所以配置也不那么有意義

Browserify也有一個(gè)HMR Plugin,但是還是有bug,但我還是覺得項(xiàng)目中能夠簡單開啟HMR配制已經(jīng)很好了。我覺著要求其他環(huán)境像React Native一樣提供polyfill是相當(dāng)不公的

React計(jì)劃封裝一套官方的工具API來讓我們更方便使用,像是觀察profile或者是更方便在用戶端實(shí)現(xiàn)無需通過某種手段來包裹就能監(jiān)測組件。事實(shí)上,通過DevTools提供的API來做這些事情會可靠的多,畢竟這些代碼在實(shí)際運(yùn)行的時(shí)候肯定會被剝離出去。

這就是我不樂意作為三方進(jìn)行深度定制庫的原因了,只要相應(yīng)的系統(tǒng)發(fā)生一些改變就可能推倒重來,所以我們還是繼續(xù)研究熱加載吧

方案:使用錯(cuò)誤邊界控制

React15封裝了一個(gè)內(nèi)部接口用于控制錯(cuò)誤的狀態(tài)(這個(gè)接口在目前已經(jīng)在16中有了對應(yīng)的生命周期函數(shù)),實(shí)際上最有效的使用方法就是把她,作為一個(gè)類似于React Native那樣的錯(cuò)誤顯示屏功能了,我們以后可能會將這個(gè)和hot reload結(jié)合起來(確實(shí)是的

方案:代理所有出現(xiàn)調(diào)用的地方

我覺著吧,這是我在寫React Transform的時(shí)候犯的最大的一個(gè)錯(cuò)誤。之前十月份的時(shí)候Sebastian Markba?ge就給我說過她不是很看好我這種Babel-Plugin的寫法,但是我沒有完全理解她的建議直到我這幾天重新在思考的時(shí)候才發(fā)現(xiàn),其實(shí)距離更好的寫法只有一尺之隔

找到組件并把他們包起來確實(shí)有很難做,并伴有很大的風(fēng)險(xiǎn)。很有可能會破壞你的代碼結(jié)構(gòu)。但是從另一方面想,標(biāo)記他們卻是相對安全的,設(shè)想我們沒有直接在組件中進(jìn)行操作,而是將他們標(biāo)記起來,在文件的最下方進(jìn)行遠(yuǎn)程處理,大大減弱了入侵性。

比方說,我們可以為頂層中的function和class還有export出去的東西做這樣的操作:

class Counter extends Component {
  constructor(props) {
    super(props)
    this.state = { counter: 0 }
    this.handleClick = this.handleClick.bind(this)
  }
  handleClick() {
    this.setState({
      counter: this.state.counter + 1
    })
  }
  render() {
    return (
      <div className={this.props.sheet.container} onClick={this.handleClick}>
        {this.state.counter}
      </div>
    )
  }
}

const styles = {
  container: { 
    backgroundColor: 'yellow'
  }
}

const __exports_default = useSheet(styles)(Counter)
export default __exports_default

// generated:
// register anything that *remotely* looks like a React component
register('Counter.js#Counter', Counter)
register('Counter.js#exports#default', __exports_default) // every export too

register具體是要干啥?我的想法是檢查一下至少先檢查一下是不是一個(gè)function,如果是的話那就利用React Proxy對她做一層代理操作。但是!她!不會立即替換你的class或者是function!這才是關(guān)鍵點(diǎn),這個(gè)代理只是會乖乖的把東西存好,然后一直等著你用React.createElement的時(shí)候再說(

如果調(diào)用React.createElement,那么你傳入的就肯定是一個(gè)Class沒得跑了,不管之前聲明了什么亂七八糟的,最后一定是傳過來的一個(gè)組件。

現(xiàn)在看來,只要我們的React Proxy是支持所有類型的組件的,那我們的熱加載就是OK的。這也是為蝦米我們需要為React.createElement做一些monkeypatch(簡單的說就是單純修飾一下這個(gè)方法)讓這個(gè)過程可以觸發(fā)操作,看起來就像是這樣:

import createProxy from 'react-proxy'

let proxies = {}
const UNIQUE_ID_KEY = '__uniqueId'

export function register(uniqueId, type) {
  Object.defineProperty(type, UNIQUE_ID_KEY, {
    value: uniqueId,
    enumerable: false,
    configurable: false
  })
  
  let proxy = proxies[uniqueId]
  if (proxy) {
    proxy.update(type)
  } else {
    proxy = proxies[id] = createProxy(type)
  }
}

// Resolve when elements are created, not during type definition!
const realCreateElement = React.createElement
React.createElement = function createElement(type, ...args)  {
  if (type[UNIQUE_ID_KEY]) {
    type = proxies[type[UNIQUE_ID_KEY]].get()
  }
  
  return realCreateElement(type, ...args)
}

有了React Proxy對我們的組件做了一些里里外外的代理(遞歸),我們可以做到和現(xiàn)在的React Transform有大致相同的表現(xiàn)啦,并且同時(shí)還能解決下面這樣一些問題

  • 這里沒有非組件會被代理,因?yàn)槲覀兊腸reateElement相當(dāng)于已經(jīng)做了一次篩選了
  • 我們能「找到」function,class,createClass的調(diào)用,以及每一個(gè)export,并且我們也不用擔(dān)心這些組件的嵌套遞歸邏輯,一切都被安排的井井有條
  • 改變無狀態(tài)組件不能把DOM重置,也不會讓子組件的state丟失
  • 我們可以通過發(fā)布React Hot Loader 2.0,使用相同的技術(shù)但是不使用靜態(tài)分析。僅為export的組件進(jìn)行處理,這樣立即可以為使用js編譯工具的系統(tǒng)所使用,)其實(shí)說白了就是相當(dāng)于一個(gè)fallback,用不了Transform的時(shí)候使用
  • 處理完成的代碼將會更容易使用理解,因?yàn)槲覀兛梢园焉傻姆诺轿募撞慷皇窍馬eact Transform一樣污染了整段代碼

總得而言,謝謝大家的資磁,我們定將繼往開來,一往無前繼續(xù)開發(fā)(懶得翻譯了,我編得

另外新版本也可以看看這里 Say hi to React Hot Loader 3

原文來自:Medium - Dan Abramov - Hot Reloading in React

為了圖簡單,文章中省略了很多的鏈接,有人看的話評論下我會補(bǔ)充上去,沒有就算了哈哈

github首發(fā)鏈接:https://github.com/879479119/879479119.github.io/issues/4

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 無意中看到zhangwnag大佬分享的webpack教程感覺受益匪淺,特此分享以備自己日后查看,也希望更多的人看到...
    小小字符閱讀 8,359評論 7 35
  • 構(gòu)建一個(gè)小項(xiàng)目——FlyBird,學(xué)習(xí)webpack和react。(本文成文于2017/2/25) 從webpac...
    布蕾布蕾閱讀 17,107評論 31 98
  • 項(xiàng)目地址 從頭開始建立一個(gè)React App - 項(xiàng)目基本配置 npm init 生成 package.json ...
    瘦人假嚕嚕閱讀 89,729評論 33 78
  • IELTS 801: teach children to be good members Topic: Some ...
    peimin閱讀 811評論 0 1
  • 上次畫的不滿意,重新畫了一個(gè),所以上次作業(yè)沒有趕上,真的是流著淚畫完的呀,快要崩潰了,這次應(yīng)該比上次好一些,不過離...
    朵拉烘焙閱讀 247評論 2 1

友情鏈接更多精彩內(nèi)容