初探React,將我們的View標籤化

發表於2015-10-08

前言

我之前喜歡玩一款遊戲:全民飛機大戰,而且有點痴迷其中,如果你想站在遊戲的第一階梯,便需要不斷的練技術練裝備,但是騰訊的遊戲一般而言是有點噁心的,他會不斷的出新飛機、新裝備、新寵物,所以,很多時候你一個飛機以及裝備還沒滿級,新的裝備就又出來了,並且一定是更強!

於是很多人便直接拋棄當前的飛機與裝備,追求更好的,這個時候如果是人民幣玩家或者骨灰級大神玩家的話,基本可以很快站在世界的頂端,一者是裝備好,一者是技術好,但是我不願意投入太多錢,也不願意投入過多精力,於是在一套極品裝備滿級後會積累資源,因為一代之間變化不會太大,到第二代甚至第三代才開始換飛機換裝備,也基本處於了第一階梯,一直到一次遊戲大更新,直接導致我當前的飛機與裝備完全二逼了,我當時一時腦熱投入了所有資源去重新整理的極品裝備,最後鬧的血本無歸,於是便刪除了該遊戲,一年時間付諸東流!!!

再回過頭來看最近兩年前端的變化,單是一個前端工程化工具就變了幾次,而且新出來的總是叫嚷著要替換之前的,grunt->gulp->webpack->es6

再看前端框架的一些產量:backbone、angularJS、react、canJS、vueJS……

真有點亂花漸欲迷人眼的意思,似乎前端技術也開始想要坑前端玩家,因為人家會了新技能,你就落後了,於是很多技術沉澱已經足夠的大神便直接在團隊使用某一技術,帶領團隊組員深入瞭解了該技術的好,並大勢宣傳新技術。

很多人在這種情況下就中招了!他們可能會拋棄現有技術棧,直接跟風新的技術,在現有裝備都沒滿級的情況下又去重新整理裝備,如果哪天一次遊戲玩法大更新,大神依舊一套極品裝備在那風騷,而炮灰倉庫中積累著一籮筐低等級的極品裝備,卻沒有可用的,不可謂不悲哀!

一門技術從入門到精通,是需要時間的,在有限的時間中要學習那麼多的新技術,還得落地到實際工作中,而每一次新技術的落地都是對曾經架構的否定與推翻,這個成本不可謂不高,對一些創業團隊甚至是致命的。工作中也沒那麼多時間讓你折騰新東西,所以一定是瞭解清楚了一門再去學習其它的,不要半途而廢也不要盲目選型。

我最近回顧了這幾年所學,可以說技術棧沒有怎麼更新,但是我對我所習的每一個技術基本上進入了深入的瞭解:

① 在MVVM還不太火的時候使用了MVC框架一直到最近,對為什麼要使用這種模式,這種模式的好處有了比較深入的瞭解,並且已經碰到了更復雜的業務邏輯

② 當一個頁面過於複雜時(比如1000多行程式碼的訂單填寫頁),我能通過幾年沉澱,將之拆分為多個業務元件模組,保持主控制器的業務清晰,程式碼量維護在500行之內,並且各子模組業務也清晰,根據model進行通訊

③ 使用Grunt完成前端工程化,從構建專案,到打包壓縮專案,到優化專案,總的來說無往不利

④ ……

就程式設計方法,思維習慣,解決問題的方法來說,與兩年前有了很大的變化,而且感覺很難提高了。於是我認識到,就現有的裝備下,可能已經玩到極限了,可能到了跟風的時候了,而時下熱門的ReactJS似乎是一個很好的切入點,React一端程式碼多端執行的噱頭也足夠。

初識ReactJS

我最初接觸ReactJS的時候,最火的好像是angular,React Native也沒有出現,看了他的demo,對其區域性重新整理的實現很感興趣。結果,翻看原始碼一看洋洋灑灑一萬多行程式碼,於是馬上便退卻了。卻不想現在火成了這般模樣,身邊學習的人多,用於生產的少,我想幕後必然有黑手在推動!也可以預測的是,1,2年後會有更好的框架會取代他,可能是原團隊的自我推翻,也有可能是Google公司又新出了什麼框架,畢竟前端最近幾年才開始真正富客戶端,還有很長的路要走。當然,這不是我們關心的重點,我們這裡的重點是Hello world。

ReactJS的Hello World是這樣寫的:

React一來就搞了一個標新立異的地方:jsx(js擴充套件),說實話,這種做法真的有點大手筆,最初的這種宣告式標籤寫法,在我腦中基本可以追溯到5年前的.net控制元件了,比如gridview與datalist元件。

在text/jsx中的程式碼最初不會被瀏覽器理會,他會被react的JSXTransformer編譯為常規的JS,然後瀏覽器才能解析。這裡與html模板會轉換為js函式是一個道理,我們有一種優化方案是模板預編譯,即:

在打包時候便將模板轉換為js函式,免去線上解析的過程,react當然也可以這樣做,這裡如果要解析的話,會是這個樣子:

因為render中的程式碼可以很複雜,render中的程式碼寫法就是一種語法糖,幫助我們更好的寫表現層程式碼:render方法中可以寫html與js混雜的程式碼:

 

所以,react提供了很多類JS的語法,JSXTransformer相當於一個語言直譯器,而解析邏輯長達10000多行程式碼,這個可不是一般屌絲可以碰的,react從這裡便走出了不平常的路,而他這樣做的意義是什麼,我們還不知道。

標籤化View

react提供了一個方法,將程式碼組裝成一個元件,然後像HTML標籤一樣插入網頁:

所謂,宣告試程式設計,便是將需要的屬性寫到標籤上,以一個文字框為例:

我們想要輸入的是數字,有數字限制,而且在移動端輸入的時候,右邊會有一個X按鈕清除文字,這個便是我們期望的宣告式標籤。

react中,標籤需要和原始的類發生通訊,比如屬性的讀取是這樣的:

上文中Pili便是一個元件,標籤使用法便是一個例項,宣告式寫法最終也會被編譯成一般的js方法,這個不是我們現在關注的重點。

通過this.props物件可以獲取元件的屬性,其中一個例外為children,他表示元件的所有節點:

PS:return的語法與js語法不太一樣,不能隨便加分號

如果想限制某一個屬性必須是某一型別的話,便需要設定PropTypes屬性:

如果想設定屬性的預設值,則需要:

我們仍然需要dom互動,我們有時也需要獲取真實的dom節點,這個時候需要這麼做:

事件觸發的時候通過ref屬性獲取當前dom元素,然後可進行操作,我們這裡看看返回的dom是什麼:

看來是真實的dom結構被返回了,另外一個比較關鍵的事情,便是這裡的dom事件支援,需要細讀文件:http://facebook.github.io/react/docs/events.html#supported-events

表單元素,屬於使用者與元件的互動,內容不能由props獲取,這個時候一般有狀態機獲取,所謂狀態機,便是會經常變化的屬性。

元件有其生命週期,每個階段會觸發相關事件可被使用者捕捉使用:

一般來說,我們會為一個狀態發生前後繫結事件,react也是如此:

根據之前的經驗,會監控元件的生命週期的操作的時候,往往都是比較高階的應用了,我們這裡暫時不予關注。

好了,之前的例子多半來源於阮一峰老師的教程,我們這裡來一個簡單的驗收,便實現上述只能輸入數字的文字框:

通過以上學習,我們對React有了一個初步認識,現在我們進入其todolist,看看其是如何實現的

此段參考:阮一峰老師的入門教程,http://www.ruanyifeng.com/blog/2015/03/react.html

TodoMVC

入口檔案

TodoMVC為MVC框架經典的demo,難度適中,而又可以展示MVC的思想,我們來看看React此處的入口程式碼:

頁面很乾淨,除了react基本js與其模板解析檔案外,還多了一個director.js,因為react本身不提供路由功能,所以路由的工作便需要外掛,director便是路由外掛,這個不是我們今天學習的重點,然後是兩個js檔案:

utils為簡單的工具類,不予理睬;無論什麼時候資料層一定是MVC的重點,這裡稍微給予一點關注:

① model層實現了一個簡單的事件訂閱通知系統

② 從類實現來說,他僅有三個屬性,key(儲存與localstorage的名稱空間),todos(真實的資料物件),changes(事件集合)

③ 與backbone的model不同,backbone的資料操作佔了其實現大部分篇幅,backbone的TodoMVC會完整定義Model的增刪差改依次觸發的事件,所以Model定義結束,程式就有了完整的脈絡,而我們看react這裡有點“弱化”資料處理的感覺

④ 總的來說,整個Model的方法皆在操作todos資料,subscribe用於註冊事件,每次操作皆會通知changes函式響應,並且儲存到localstorage,從重構的角度來說inform其實只應該完成通知的工作,儲存的事情不應該做,但是這與我們今天所學沒有什麼管理,不予理睬,接下來我們進入View層的程式碼。

元件化程式設計

React號稱元件化程式設計,我們從標籤化、宣告式程式設計的角度來一起看看他第一個View TodoItem的實現:

根據我們之前的知識,這裡是建立了一個自定義標籤,而標籤返回的內容是:

要展示這個View需要依賴其屬性與狀態:

這裡沒有屬性的描寫,而他本身也僅僅是標籤元件,更多的資訊我們需要去看呼叫方,該元件顯示的是body部分,TodoMVC還有footer部分的操作工具條,這裡的實現便比較簡單了:

我們現在將關注點放在其所有標籤的呼叫方,app.jsx(TodoApp),因為我沒看見這個TodoMVC的控制器在哪,也就是我沒有看見控制邏輯的js檔案在哪,所以控制流程的程式碼只能在這裡了:

這裡同樣是建立了一個標籤,然後最後一段程式碼有所不同:

① 這裡建立了一個Model的例項,我們知道建立的時候,todos便由localstorage獲取了資料(如果有的話)

② 這裡了定義了一個方法,以todoapp為容器,裝載標籤

③ 為model訂閱render方法,意思是每次model有變化都將重新渲染頁面,這裡的程式碼比較關鍵,按照程式碼所示,每次資料變化都應該執行render方法,如果list數量比較多的話,每次接重新渲染豈不是浪費效能,但真實使用過程中,可以看到React竟然是區域性重新整理的,他這個機制非常牛逼啊!

④ 最後執行了render方法,開始了TodoApp標籤的渲染,我們這裡再將TodoApp的渲染邏輯貼出來

說句實話,這段程式碼不知為什麼有一些令人感到難受……

① 他首先獲取了注入的model例項,獲取其所需的資料todos,注入點在:

② 然後他由自身狀態機,獲取真實要顯示的專案,其實這裡如果不考慮路由的變化,完全顯示即可

③ 資料獲取成功後,便使用該資料組裝為一個個獨立的TodoItem標籤:

標籤具有很多事件,這裡要注意一下各個事件這裡事件繫結與控制器上繫結有何不同

④ 然後其做了一些工作處理底部工具條或者頭部全部選中的工作

⑤ 最後開始渲染整個標籤:

該標籤事實上為3個模組組成的了:header部分、body部分、footer部分,模組與模組之間的通訊依賴便是model資料了,因為這裡最終的渲染皆在app的render處,而render處渲染所有標籤全部共同依賴於一個model,就算這裡依賴於多個model,只要是統一在render處做展示即可。

流程分析

我們前面理清了整個脈絡,接下來我們理一理幾個關鍵脈絡:

① 新增

TodoApp為其頭部input標籤繫結了一個onKeyDown事件,事件代理到了handleNewTodoKeyDown:

因為使用者輸入的資料不能由屬性或者狀態值獲取,這裡使用了dom操作的方法獲取輸入資料,這裡的鉤子是ref,事件觸發了model新增一條記錄,並且將文字框置為空,現在我們進入model新增的邏輯:

model以最簡的方式構造了一個資料物件,改變了todos的值,然後通知model發生了變化,而我們都知道informa程式幹了兩件事:

儲存localstorage、觸發訂閱model變化的回撥,也就是:

於是整個標籤可恥的重新渲染了,我們再來看看編輯是怎麼回事:

② 編輯

這個編輯便與TodoApp沒有什麼關係了:

當雙擊標籤項時,觸發了代理的處理程式:

這裡他做了兩個事情:

onEdit,為父標籤注入的方法,他這裡執行函式作用域是指向this.props的,所以外層定義時指定了作用域:

其次,他改變了自身狀態機,而狀態機或者屬性的變化皆會引起標籤重新渲染,然後當觸發keydown事件後,完成的邏輯便與上面一致了

思考

經過之前的學習,我們對React有了一個大概的瞭解,是時候搬出React設計的初衷了:

後面兩個概念還沒強烈的感觸,這裡僅僅對Just the ui有一些認識,似乎React僅僅提供了MVC中View的實現,但是這個View又強大到可以拋棄C了,可以看到上述程式碼控制器被無限的弱化了,而我覺得React其實真實想提供的可能是一種開發方式的思路,React便是如何幫你實現這種思路的方案:

我們在組織負責業務邏輯時,也會分模組、分UI,但是我們一般是採用控制器呼叫元件的方式使用,React這裡不同的一點是使用標籤分模組,孰優孰劣要真實開發過生產專案的朋友才能認識,真實的應用路由的功能必不可少,應該有不少外掛會主動抱大腿,但使用靈活性仍然得專案實踐驗證。

react本身很乾淨,不包括模組載入的機制,真正釋出生產前需要通過webpack打包處理,但是對於複雜專案來說,按需載入是必不可少的,這塊不知道如何

而我的關注點仍然落在了樣式上,之前做元件或者做頁面時,有一個優化方案,是將對應的樣式作為一個View的依賴項載入,一個View保持最小的html&css&js量載入,而react對樣式與動畫一塊的支援如何,也需要生產驗證;複雜的專案開發,Model的設計一定是至關重要的,也許借鑑Backbone Model的實現+React的View處理,會是一個不錯的選擇

最後,因為現在沒有生產專案能讓我使用React試水,過多的話基本就是意淫了,根據我之前MVC的使用經驗,感覺靈活性上估計React仍然有一段路要走,但是其模組化程式設計的思路倒是對我的專案有莫大的指導作用,對於這門技術的深入,經過今天的學習,我打算再觀望一下,不知道angularJS怎麼樣,我也許該對這門MVVM的框架展開調研

相關文章