關於 React 框架 和 Vue 框架 的對比,用兩者寫出同一個程序,對比其代碼實現的過程,看看它們的差異究竟有多大?
轉載於:https://blog.csdn.net/csdnnews/article/details/81880378
原文:https://medium.com/javascript-in-plain-english/i-created-the-exact-same-app-in-react-and-vue-here-are-the-differences-e9a1ae8077fd
作者簡介:Sunil Sandhu,一位 Web 全棧工程師 + 空想家。
譯者:安翔,责编:屠敏
眾所周知,Vue 和 React 都是目前非常著名的前端框架。我在工作中經常使用 Vue,因此我對它有很深入的了解。同時,我也對 React 充滿了好奇,想要學習一下,一探究竟。
於是我閱讀了 React 文檔並觀看了一些視頻教程,雖然這些資料很不錯,但是我真正想了解的是 React 與 Vue 之間的不同之處。所謂 “不同之處”,我並非想知道它們是否都具有虛擬 DOMS 或者它們如何渲染頁面,而是希望有人能夠從代碼的角度解釋這兩者之間的差異。我想找到一篇解釋這些差異的文章,以便 Vue 或者 React 的初學者可以更好地理解它們兩者之間的差異。
很遺憾,我並未找到一篇這樣的文章。於是我意識到必須自己動手來比較 Vue 與 React 之間的異同。在我自力更生的過程中,我用這篇文章記錄下了具體過程。
1. 目標#
我將會構建一個標準的待辦事項應用程序,允許用戶添加和刪除列表中的項目。這兩個應用程序都使用默認的 CLI(command-line interface,命令行界面)構建,React 使用 create-react-app,Vue 使用 vue-cli。
兩個應用程序的外觀如下:
兩個應用程序的 CSS 代碼幾乎一樣,但這些代碼的位置存在差異。考慮到這一點,我們來看看這兩個應用程序的文件結構:
你會發現它們的結構幾乎完全相同。唯一的區別在於 React App 擁有三個 CSS 文件,而 Vue App 中沒有 CSS 文件。這是因為 React 的 create-react-app 組件需要一個附帶文件來保存其樣式,而 Vue CLI 採用全包方法,其樣式在實際組件文件中聲明。
兩種不同的策略得到的結果是相同的,相信開發者很快能夠掌握這兩種不同的策略。開發者可以根據自己的偏好做出選擇,你會聽到開發社區關於如何構建 CSS 的大量討論。以上,我們遵循兩個 CLI 列出了代碼結構。
在我們進一步討論之前,先快速看一下典型的 Vue 和 React 組件的外觀:
現在讓我們正式開始,深入其中的細節!
2. 如何修改數據#
首先,我們需要明白 “修改數據” 的意思是什麼。它聽起來有些學術,但實際上很簡單,就是把我們已經存儲好的數據進行更改。比如,如果我們想把一個人的名字變量從 “Jhon” 改為 “Mark”,我們就需要執行 “修改數據” 的操作。在這一點上,React 和 Vue 的處理方式有所區別。Vue 本質上會創建一個數據對象,其中的數據可以自由更改;React 則創建一個狀態對象,更改數據需要一些額外的操作。React 之所以需要額外的操作有著自己的理由,稍後我會深入介紹。在此之前,我們先看看 Vue 中的數據對象和 React 中的狀態對象:
vue 數據對象
React 狀態對象
從圖中可以看出,我們傳入了相同的數據,但它們的標記方法不同。因此,將初始數據傳遞到組件的方式非常相似。但正如我們提到的那樣,在兩個框架中更改數據的方式有所不同。
假設我們有一個名為 name: ‘Sunil’ 的數據元素。
在 Vue 中,我們通過調用 this.name 來引用它。我們也可以通過調用 this.name ='John' 來更新它。這樣一來,名字就被成功改為了 “Jhon”。
在 React 中,我們通過調用 this.state.name 來引用同一段數據。現在關鍵的區別在於,我們不能簡單地寫成 this.state.name ='John',因為 React 有限制機制,它會阻止這種簡單的修改方式。在 React 中,我們需要這樣寫:this.setState ({name:'John'})。
雖然這基本上與我們在 Vue 中實現的結果一樣,但是 React 的操作更為繁瑣,那是因為 Vue 在每次更新數據時默認組合了自己的 setState 版本。 簡單來說就是,React 需要 setState,然後更新其內部數據,而對於 Vue 來說,當你更新數據對象的值時它就默認了你的更改意圖。 那麼為什麼 React 沒有進行簡化,為什麼需要 setState 呢? Revanth Kumar 對此做出了解釋:
“這是因為 React 希望在狀態發生變化時重新運行某些生命週期 hook,比如 componentWillReceiveProps、shouldComponentUpdate、componentWillUpdate、render、componentDidUpdate。當你調用 setState 函數時,它知道狀態已經改變。如果你直接改變狀態,React 將需要做更多工作來跟蹤更改以及運行生命週期 hook 等等。所以為了簡單起見,React 使用 setState。"
3. 添加新的待辦事項#
React 的實現方法#
createNewToDoItem = () => {
this.setState( ({ list, todo }) => ({
list: [
...list,
{
todo
}
],
todo: ''
})
);
};
在 React 中,我們的輸入字段有一個名為 value 的屬性。這個 value 通過使用幾個函數自動更新,這些函數綁定在一起以創建雙向綁定。我們通過在輸入字段上附加一個 onChange 事件監聽器來創建這種形式的雙向綁定。看看代碼,一探究竟:
<input type="text"
value={this.state.todo}
onChange={this.handleInput}/>
只要輸入字段的值發生更改,handleInput 函數就會運行。它通過將狀態對象設置為輸入字段中的任何內容來更新狀態對象內的 todo。handleInput 函數如下:
handleInput = e => {
this.setState({
todo: e.target.value
});
};
現在,只要用戶按下頁面上的 + 按鈕添加新項目,createNewToDoItem 函數就會運行 this.setState 並向其傳遞一個函數。該函數有兩個參數,第一個是來自狀態對象的整個列表數組,第二個是由 handleInput 函數更新的 todo。然後該函數返回一個新對象,該對象包含之前的整個列表,並在其末尾添加 todo。整個列表是通過使用擴展運算符添加的。
最後,我們將 todo 設置為空字符串,它會自動更新輸入字段中的 value。
Vue 的實現方法#
createNewToDoItem() {
this.list.push(
{
'todo': this.todo
}
);
this.todo = '';
}
在 Vue 中,我們的輸入字段中有一個名為 v-model 的句柄。這實現了 ** 雙向綁定。輸入字段代碼如下:
<input type="text" v-model="todo"/>
V-Model 將輸入字段的內容綁定到名為 toDoItem 的數據對象的鍵(key)上。當頁面加載時,我們將 toDoItem 設置為空字符串,比如:todo:' '。如果已經存在數據,例如 todo:' 添加文本處 ',輸入字段將加載添加文本處的輸入內容。無論如何,將其作為空字符串,我們在輸入字段中鍵入的任何文本都會綁定到 todo。這實際上是雙向綁定(輸入字段可以更新數據對象,數據對象可以更新輸入字段)。
因此,回顧前面的 createNewToDoItem () 代碼塊,我們將 todo 的內容存放到列表數組中,然後將 todo 改為空字符串。
4. 刪除待辦事項#
React 的實現方法#
deleteItem = indexToDelete => {
this.setState(({ list }) => ({
list: list.filter((toDo, index) => index !== indexToDelete)
}));
};
儘管 deleteItem 函數位於 ToDo.js 文件中,但是從 ToDoItem.js 文件中引用它也很容易,將 deleteItem () 函數作為 上的 prop 傳遞:
<ToDoItem deleteItem={this.deleteItem.bind(this, key)}/>
這會將該函數傳遞給子組件,使其可以訪問。我們綁定了 this 並傳遞 key 參數,當用戶點擊刪除項時,函數通過 key 區分用戶點擊的是哪一條 ToDoItem 。然後,在 ToDoItem 組件內部,我們執行以下操作:
<div className=”ToDoItem-Delete” onClick={this.props.deleteItem}>-</div>
想要引用位於父組件內部的函數,只需引用 this.props.deleteItem 即可。
Vue 的實現方法#
onDeleteItem(todo){
this.list = this.list.filter(item => item !== todo);
}
Vue 的實現方法稍有不同,我們需要做到以下三點:
- 首先,在元素上調用函數:
<div class=”ToDoItem-Delete” @click=”deleteItem(todo)”>-</div>
- 然後我們必須創建一個 emit 函數,將其作為子組件的內部方法(在本例中為 ToDoItem.vue),如下所示:
deleteItem(todo) {
this.$emit('delete', todo)
}
- 之後,你會發現,當我們添加 ToDo.vue 的 ToDoItem.vue 時,實際上引用了一個函數:
<ToDoItem v-for="todo in list"
:todo="todo"
@delete="onDeleteItem" // <-- this :)
:key="todo.id" />
這就是所謂的自定義事件監聽器。它會監聽任何使用 'delete' 字符串的觸發事件。一旦監聽到事件,它會觸發一個名為 onDeleteItem 的函數。此函數位於 ToDo.vue 內部,而不是 ToDoItem.vue。如前所述,該函數只是過濾數據對象內的 todo 陣列,以刪除被點擊的待辦事項。
在 Vue 示例中還需要注意的是,我們可以在 @click 監聽器中編寫 $emit 部分,這樣更加簡單,如下所示:
<div class=”ToDoItem-Delete” @click=”$emit(‘delete’, todo)”>-</div>
如果你喜歡,這樣做可以把 3 步減少到 2 步。
React 中的子組件可以通過 this.props 訪問父函數,而在 Vue 中,你需要從子組件中發出事件,父組件來收集事件。
5. 如何傳遞事件監聽器#
React 的實現方法#
事件監聽器處理簡單事件(比如點擊)非常直接。我們為待辦事項創建了點擊事件,用於創建新的待辦事項,代碼如下:
<div className=”ToDo-Add” onClick={this.createNewToDoItem}>+</div>
非常簡單,就像使用 vanilla JS 處理內聯 onClick 一樣。正如前文所述,只要按下回車按鈕,設置事件監聽器就需要花費更長的時間。這需要輸入標籤處理 onKeyPress 事件,代碼如下:
<input type=”text” onKeyPress={this.handleKeyPress}/>
該函數只要識別到 'enter' 鍵被按下,它就會觸發 createNewToDoItem 函數,代碼如下所示:
handleKeyPress = (e) => {
if (e.key === ‘Enter’) {
this.createNewToDoItem();
}
};
Vue 的實現方法#
Vue 的事件監聽器更加直接。我們只需要使用一個簡單的 @ 符號,就可以構建出我們想要的事件監聽器。例如,想要添加 click 事件監聽器,代碼:
<div class=”ToDo-Add” @click=”createNewToDoItem()”>+</div>
注意:@click 實際上是 v-on 的簡寫。Vue 事件監聽器很強大,你可以為其選擇屬性,例如 .once 可以防止事件監聽器被多次觸發。此外,它還包含很多快捷方式。按下回車按鈕時,React 就需要花費更長的時間來創建事件監聽器,從而創建新的 ToDo 項目。在 Vue,代碼如下:
<input type=”text” v-on:keyup.enter=”createNewToDoItem”/>
6. 如何將數據傳遞給子組件#
React 的實現方法#
在 React 中,我們將 props 傳遞到子組件的創建處。比如:
<ToDoItem key={key} item={todo} />
此處我們向 ToDoItem 組件傳遞了兩個 prop。之後,我們可以在子組件中通過 this.props 引用它們。因此,想要訪問 item.todo prop,我們只需調用 this.props.item 。
Vue 的實現方法#
在 Vue 中,我們將 props 傳遞到子組件創建處的方式如下:
<ToDoItem v-for="todo in list"
:todo="todo"
:key="todo.id"
@delete="onDeleteItem" />
我們將它們傳遞給子組件中的 props 陣列,如:props:['id','todo']。然後可以在子組件中通過名字引用它們。
7. 如何將數據發送回父組件#
React 的實現方法#
我們首先將函數傳遞給子組件,方法是在我們調用子組件時將其引用為 prop。然後我們通過引用 this.props.whateverTheFunctionIsCalled,為子組件添加調用函數,例如 onClick。然後,這將觸發父組件中的函數。刪除待辦事項一節中詳細介紹了整個過程。
Vue 的實現方法#
在子組件中我們只需編寫一個函數,將一個值發送回父函數。在父組件中編寫一個函數來監聽子組件何時發出該值的事件,監聽到事件之後觸發函數調用。同樣,刪除待辦事項一節中詳細介紹了整個過程。
8. 總結#
我們研究了添加、刪除和更改數據,以 prop 形式從父組件到子組件傳遞數據,以及通過事件監聽器的形式將數據從子組件發送到父組件。當然,React 和 Vue 之間存在一些小差異,希望本文的內容有助於理解這兩個框架。
兩個應用程序的 GitHub 地址:
**Vue ToDo:**https://github.com/sunil-sandhu/vue-todo
**React ToDo:**https://github.com/sunil-sandhu/react-todo