在工做中應用 Vue 以後,我對它有了至關深入的理解。 不過,俗話說「外國的月亮比較圓」,我好奇「外國的」 React 是怎麼樣的。javascript
我閱讀了 React 文檔並觀看了一些教程視頻,雖然它們很棒,但我真正想知道的是 React 與 Vue 的不一樣之處。 這裏的「不一樣」,並非指它們是否具備虛擬 DOM 或者它們如何渲染頁面。 我但願有人能向我解釋代碼並告訴我其中發生了什麼! 我想找一篇能解釋這些差別的文章,以便 Vue 或 React(或整個 Web 開發)的新手能夠更好地理解二者之間的差別。vue
但我沒有找到任何解決這個問題的方法。 因此我意識到我必須本身動手,來發現類似/不一樣之處。 在作的時候,我想我會記錄整個過程,以便產出一篇關於此的文章。java
我決定嘗試構建一個至關標準的 To Do 應用,它容許用戶添加和刪除列表中的項目。 這兩個應用都是使用默認的 CLI 構建的(React 的 create-react-app 和 Vue 的 vue-cli)。 BTW,CLI 表明命令行界面。react
好吧,這個介紹已經超出了個人預期。 咱們先快速看看兩個應用的界面:git
兩個應用的 CSS 代碼徹底相同,不過它們所在的位置有差異。 考慮到這一點,咱們接下來看看兩個應用的文件結構:github
你會發現它們的結構幾乎徹底相同。 惟一的區別是 React 應用有三個 CSS 文件,而 Vue 應用一個都沒有。 這樣作的緣由是,在 create-react-app 中,React 組件會有一個附帶文件來控制其樣式,而 Vue CLI 採用全包方法,具體樣式在實際組件文件中聲明。vue-cli
最終,他們都達到了一樣的目的,對於不能在 React 或 Vue 中以不一樣的方式構建 CSS 的問題,目前還真沒什麼辦法。這真的取決於我的偏好 - 你會聽到開發者社區關於如何構建 CSS 的大量討論。 如今,咱們將遵循兩個 CLI 中列出的結構。數組
但在咱們進一步討論以前,咱們先快速看一下典型的 Vue 和 React 組件的是什麼樣的:app
扯遠了。如今讓咱們開始研究細節中的細節!框架
如何變異數據?
首先咱們要搞清楚什麼是「變異數據」? 聽起來有點科技感是吧? 它基本上只是意味着改變咱們存儲的數據。 所以,若是咱們想將一我的的名字取值從 John 改成 Mark,咱們就是在「變異數據」。 這就是 React 和 Vue 之間的關鍵區別所在。 雖然 Vue 本質上建立了一個 data 對象,對象數據能夠自由更新,而 React 建立了一個 state 對象,要實現更新必須多作一點工做。 如今 React 有充分的理由實現額外的 legwork,咱們稍微深刻一下。 但首先,讓咱們看看 Vue 中的 data 對象和 React 中的 state 對象:
Vue data 對象在左側。 React state 對象在右側。
你能夠看到咱們已經將相同的數據傳遞到二者中,只是標記不一樣。 所以,將初始數據傳遞到咱們的組件很是很是類似。 但正如上文提到的,咱們對這些數據的修改方式在兩個框架之間有所不一樣。
假設咱們有一個name 爲 'Sunil'的數據元素 。
在 Vue 中,咱們經過調用 this.name 來引用它。 咱們也能夠調用 this.name ='John' 來更新它。 這會把個人名字改爲 John。 我不肯定被稱爲John的感受如何,但嘿嘿,它就是發生了!
在 React 中,咱們經過調用 this.state.name 來引用同一份數據。 如今關鍵的區別在於咱們不能簡單地寫 this.state.name='John',由於 React 作了限制來防止這種簡單、無憂無慮的 mutate。 因此在 React 中,咱們會使用 this.setState({name:'John'}) 的方式來編寫。
雖然這基本上與咱們在 Vue 中實現的相同,可是額外的寫入是由於 Vue 基本上在每次更新數據時都會合並本身的 setState 版本。 簡而言之,React 須要 setState 並傳入須要更新的內部數據,而 Vue 假設當你把更新的值傳入 data 對象時,你是想更新它的。那麼爲何 React 不肯意這樣作,爲何還須要 setState 呢? 讓 Revanth Kumar 來爲咱們解釋下:
「這是由於 React 但願在狀態發生變化時從新運行某些生命週期鉤子,[如] componentWillReceiveProps,shouldComponentUpdate,componentWillUpdate,render,componentDidUpdate。 當你調用 setState 函數時,它會知道狀態已經改變了。 若是你直接改變狀態,React 將須要作更多工做來跟蹤更改以及運行生命週期鉤子等等。因此爲了簡單起見,React 使用 setState。「
如今咱們已經完成了 mutations,讓咱們研究下如何在咱們的 To Do 應用中添加新項目,以深刻了解細節。
如何建立新的 To Do 項
React 是如何作的?
在 React 中,咱們的輸入字段有一個叫 value 的屬性。 這個值經過使用幾個函數自動更新,這些函數結合起來會建立一個很是相似於雙向綁定的東西(若是你之前從未據說過這個,你能夠看看以後的 How did Vue do that 部分)。 咱們經過在 input 上添加一個 onChange事件監聽器來建立這種形式的雙向綁定。 咱們簡單看一下 input 字段,以便你瞭解發生了什麼:
只要 input 框的值發生更改,handleInput 函數就會運行。 它會將 input 框中的任意輸入更新到 state 中的 todo。 這個函數看起來像這樣:
如今,只要用戶按下頁面上的 + 按鈕添加新 item,createNewToDoItem 函數就會運行 this.setState 並向其傳遞一個函數。 這個函數接收兩個參數,第一個是來自狀態對象的 list數組,第二個是 todo(由 handleInput 函數更新)。 該函數返回一個新對象,該對象包含以前的整個 list,而後在其末尾添加 todo。 整個列表是經過使用擴展運算符添加的(若是你以前沒有看過這個 ES6 語法,Google 一下吧)。
最後,咱們將 todo 設置爲空字符串,它會自動更新 input 框中的值。
在 Vue 中,咱們的 input 框有一個名爲 v-model 的指令。 它容許咱們作一些稱爲雙向綁定的事情。 咱們先看看 input 框,而後解釋下發生了什麼:
V-Model 將此字段的輸入綁定到名爲 toDoItem 的 data 對象中的 key。 當頁面加載時,toDoItem 會被置爲空字符串,如 todo: ''。 若是 todo 非空,例如 todo: ‘add some text here’,咱們的 input 框將加載‘add some text’。 不管如何,回到將其做爲空字符串,咱們在輸入字段中輸入的任何文本都將綁定到 todo 的值。 這正是雙向綁定(input 能夠更新 data 對象,data 對象能夠更新 input)。
回到前面的 createNewToDoItem() 代碼塊,咱們看到咱們將 todo 的內容 push 到 list 數組中,而後將 todo 更新爲空字符串。
如何刪除列表數據?
雖然 deleteItem 函數位於 ToDo.js 內部,但我能夠直接在 ToDoItem.js 中引用它,首先將deleteItem() 函數做爲 <ToDoItem /> 上的 prop 傳遞:
首先將該功能傳遞給子組件,使其能夠被訪問。 你能夠看到咱們也綁定了 this 以及傳遞了 key 參數,由於 key 是函數將用於區分 ToDoItem 在單擊時嘗試刪除的內容。 而後,在ToDoItem 組件內部,咱們執行如下操做:
我須要作的就是引用一個位於父組件內部的函數來引用 this.props.deleteItem。
Vue 是如何作的?
Vue 的方法稍有不一樣。 咱們基本上要作三件事:
首先,在咱們想要調用函數的元素上:
而後咱們必須建立一個 emit 函數做爲子組件內部的方法(在本例中爲 ToDoItem.vue),以下所示:
除此以外,你會發現咱們在 ToDo.vue 中引入 ToDoItem.vue 時實際引用了一個函數:
這就是所謂的自定義事件監聽器。 它會監放任何使用 'delete' 字符串觸發 emit 的操做。 若是監聽到,它會觸發一個名爲 onDeleteItem 的函數。 此函數位於 ToDo.vue 內部,而不是ToDoItem.vue。 如前所述,此函數只是過濾 data 對象內的 todo 數組,以刪除被點擊的 item。
這裏也值得注意的是,在 Vue 示例中,我能夠簡單地在 @click 監聽器中編寫 $emit 部分,以下所示:
這會將步數從 3 減小到 2,這僅僅取決於我的偏好。
簡而言之,React 中的子組件能夠經過 this.props 訪問父組件方法(假設你正在傳遞 props,這是至關標準的作法,你會在其餘 React 示例中遇到許屢次),而在 Vue 中, 你必須從子組件中觸發由父組件全部的事件。
如何傳遞事件監聽器?
React:
簡單事件(如點擊事件)的事件監聽器是直截了當的。 如下是咱們如何爲建立新 ToDo item 的按鈕建立 click 事件的示例:
這裏很是簡單,就像咱們使用原生 JS 處理內聯 onClick 同樣。 正如 Vue 部分所述,在按下回車按鈕的狀況下設置事件監聽器就須要花點時間了。 咱們能夠在 input 標籤中處理一下 onKeyPress 事件,以下:
只要它識別出按下了 '回車(Enter)' 鍵,這個函數就會觸發 createNewToDoItem 函數,以下所示:
在 Vue,它是超級直接的。 咱們只使用 @ 符號,而後使用咱們想要事件監聽器的類型。 例如,要添加 click 事件偵聽器,咱們能夠這樣:
注意:@click 其實是 v-on: click 的簡寫。 Vue 事件監聽器的一個很酷的事情是,在它以後咱們能夠鏈式調用許多方法,例如 .once,它能夠防止事件監聽器被屢次觸發。 在編寫用於處理鍵擊的特定事件偵聽器時,還有一些快捷方式。 我發如今按下回車按鈕的狀況下,在 React 中建立一個事件監聽器須要花費很長時間來建立新的 ToDo item。而在Vue,我可以簡單地這樣寫:
如何向子組件傳遞數據?
React:
在 react 中,咱們將 props 傳遞到子組件。 如:
在這裏,咱們看到兩個傳遞給 ToDoItem 組件的 props。 從如今開始,咱們就能夠經過 this.props 在子組件中引用它們了。 所以,要訪問 item.todo prop,咱們只需調用 this.props.item。
在 Vue 中,咱們將 props 傳遞到子組件。 如:
完成後,咱們將它們傳遞給子組件中的 props 數組,如 props: ['todo']。 而後能夠經過他們的名字在子組件中引用它們 - 因此在咱們的例子中,'todo'。
如何將數據發送回父組件?
React:
咱們首先將函數傳遞給子組件,方法是在咱們調用子組件的位置將其引用爲 prop。 而後,咱們經過引用 this.props.whateverTheFunctionIsCalled,經過任何方式(例如 onClick)添加對子函數的調用。 而後,這將觸發位於父組件中的函數。 咱們能夠在「如何從列表中刪除」一節中看到整個過程的示例。
在咱們的子組件中,咱們只需編寫一個函數,將一個值發送回父函數。 在咱們的父組件中,咱們編寫一個函數來偵聽什麼時候 emit 該值,而後能夠觸發函數調用。 咱們能夠在「如何從列表中刪除」一節中看到整個過程的示例。
大功告成!
咱們研究瞭如何添加,刪除和更改數據,將數據以表單 props 形式從父組件傳遞到子組件,以及以事件偵聽器的形式將數據從子組件發送到父組件。 固然,在 React 和 Vue 之間存在許多其餘的小差異,但但願本文的內容能做爲理解兩個框架如何處理東西的基礎。
Vue ToDo: https://github.com/sunil-sandhu/vue-todo
React ToDo: https://github.com/sunil-sandhu/react-todo
英文原文: https://medium.com/javascript-in-plain-english/i-created-the-exact-same-app-in-react-and-vue-here-are-the-differences-e9a1ae8077fd