作者|Sunil Sandhu
譯者|無(wú)明
在工作中使用了 Vue 之后,我已經(jīng)對它有了相當深入的了解。同時(shí),我也對 React 感到好奇。我閱讀了 React 的文檔,也看了一些教程視頻,雖然它們很棒,但我真正想知道的是 React 與 Vue 有哪些區別。這里所說(shuō)的區別,并不是指它們是否都具有虛擬 DOM 或者它們如何渲染頁(yè)面。我真正想要做的是對它們的代碼進(jìn)行并排比較,并搞清楚在使用這兩個(gè)框架開(kāi)發(fā)應用時(shí)究竟有哪些差別。
我決定構建一個(gè)標準的待辦事項應用程序,用戶(hù)可以添加和刪除待辦事項。我分別使用它們默認的 CLI(React 的 create-react-app 和 Vue 的 vue-cli)來(lái)創(chuàng )建這個(gè)應用。先讓我們看一下這兩個(gè)應用的外觀(guān):
兩個(gè)應用程序的 CSS 代碼幾乎完全相同,但代碼存放的位置存在差別。
它們的結構也幾乎完全相同,唯一的區別是 React 有三個(gè) CSS 文件,而 Vue 則沒(méi)有。這是因為 React 組件需要一個(gè)附帶的文件來(lái)保存樣式,而 Vue 采用包含的方式,將樣式聲明在組件文件中。
從理論上講,你可以使用老式的 style.css 文件來(lái)保存整個(gè)頁(yè)面的樣式,這完全取決于你自己。不管怎樣,還是展示一下.vue 文件中的 CSS 代碼長(cháng)什么樣:
看完樣式方面的問(wèn)題,現在讓我們深入了解其他細節!
如何改變數據?
我們說(shuō)“改變數據”(mutate data),實(shí)際上就是指修改已經(jīng)保存好的數據。比如,如果我們想將一個(gè)人的名字從 John 改成 Mark,我們就要“改變數據”。這就是 React 和 Vue 的關(guān)鍵區別之一。Vue 創(chuàng )建了一個(gè)數據對象,我們可以自由地更新數據對象,而 React 創(chuàng )建了一個(gè)狀態(tài)對象,要更新?tīng)顟B(tài)對象,需要做更多瑣碎的工作。下面是 React 的狀態(tài)對象和 Vue 的數據對象之間的對比:
React 的狀態(tài)對象:
Vue 的數據對象:

從圖中可以看到,我們傳入的是相同的數據,它們只是標記的方式不一樣。但它們在如何改變這些數據方面卻有很大的區別。
假設我們有一個(gè)數據元素 name:'Sunil'。
在 Vue 中,我們通過(guò) this.name 來(lái)引用它。我們也可以通過(guò) this.name='John'來(lái)更新它,這樣會(huì )把名字改成 John。
在 React 中,我們通過(guò) this.state.name 來(lái)引用它。關(guān)鍵的區別在于,我們不能簡(jiǎn)單地通過(guò) this.state.name='John'來(lái)更新它,因為 React 對此做出了限制。在 React 中,我們需要使用 this.setState({name:'John'}) 的方式來(lái)更新數據。
在了解了如何修改數據之后,接下來(lái)讓我們通過(guò)研究如何在待辦事項應用中添加新項目來(lái)深入了解其他細節。
如何創(chuàng )建新待辦事項?
React:
createNewToDoItem = () => {
this.setState( ({ list, todo }) => ({
list: [
...list,
{
todo
}
],
todo: ''
})
);
};
Vue:
createNewToDoItem() {
this.list.push(
{
'todo': this.todo
}
);
this.todo = '';
}
React 是怎么做到的?
在 React 中,input 有一個(gè)叫作 value 的屬性。我們通過(guò)幾個(gè)與創(chuàng )建雙向綁定相關(guān)的函數來(lái)自動(dòng)更新 value。React 通過(guò)為 input 附加 onChange 函數來(lái)處理雙向綁定。
value={this.state.todo}
onChange={this.handleInput}/>
只要 input 的值發(fā)生變化,就會(huì )執行 handleInput 函數。這個(gè)函數會(huì )將狀態(tài)對象中 todo 字段的值改為 input 中的值。這個(gè)函數看起來(lái)像這樣:
handleInput = e => {
this.setState({
todo: e.target.value
});
};
現在,只要用戶(hù)按下頁(yè)面上的 + 按鈕,createNewToDoItem 就會(huì )調用 this.setState,并傳入一個(gè)函數。這個(gè)函數有兩個(gè)參數,第一個(gè)是狀態(tài)對象的 list 數組,第二個(gè)是 todo(由 handleInput 函數更新)。然后函數會(huì )返回一個(gè)新對象,這個(gè)對象包含之前的整個(gè) list,然后將 todo 添加到 list 的末尾。
最后,我們將 todo 設置為空字符串,它也會(huì )自動(dòng)更新 input 中的值。
Vue 是怎么做到的?
在 Vue 中,input 有一個(gè)叫作 v-model 的屬性。我們可以用它來(lái)實(shí)現雙向綁定。
v-model 將 input 綁定到數據對象 toDoItem 的一個(gè) key 上。在加載頁(yè)面時(shí),我們將 toDoItem 設置為空字符串,比如 todo:’’。如果 todo 不為空,例如 todo:’add some text here',那么 input 就會(huì )顯示這個(gè)字符串。我們在 input 中輸入的任何文本都會(huì )綁定到 todo。這實(shí)際上就是雙向綁定(input 可以更新數據對象,數據對象也可以更新 input)。
因此,回看之前的 createNewToDoItem() 代碼塊,我們將 todo 的內容放到 list 數組中,然后將 todo 更新為空字符串。
如何刪除待辦事項?
React:
deleteItem = indexToDelete => {
this.setState(({ list }) => ({
list: list.filter((toDo, index) => index !== indexToDelete)
}));
};
React 是怎么做到的?
雖然 deleteItem 函數位于 ToDo.js 中,我仍然可以在 ToDoItem.js 中引用它,就是將 deleteItem() 函數作為的 prop 傳入:
這樣可以讓子組件訪(fǎng)問(wèn)傳入的函數。我們還綁定了 this 和參數 key,傳入的函數需要通過(guò) key 來(lái)判斷要刪除哪個(gè) ToDoItem。在 ToDoItem 組件內部,我們執行以下操作:
我使用 this.props.deleteItem 來(lái)引用父組件中的函數。
Vue:
this.$on(‘delete’, (event) => {
this.list = this.list.filter(item => item.todo !== event)
})
Vue 是怎么做到的?
Vue 的方式稍微有點(diǎn)不同,我們基本上要做三件事。
首先,我們需要在元素上調用函數:
然后我們必須創(chuàng )建一個(gè) emit 函數作為子組件內部的一個(gè)方法(在本例中為 ToDoItem.vue),如下所示:
deleteItem(todo) {
this.$parent.$emit(‘delete’, todo)
}
然后我們的父函數,也就是 this.$on(’delete’) 事件監聽(tīng)器會(huì )在它被調用時(shí)觸發(fā)過(guò)濾器函數。
簡(jiǎn)單地說(shuō),React 中的子組件可以通過(guò) this.props 訪(fǎng)問(wèn)父函數,而在 Vue 中,必須從子組件中向父組件發(fā)送事件,然后父組件需要監聽(tīng)這些事件,并在它被調用時(shí)執行函數。
這里值得注意的是,在 Vue 示例中,我也可以直接將 $emit 部分的內容寫(xiě)在 @click 監聽(tīng)器中,如下所示:
這樣可以減少一些代碼,不過(guò)也取決于個(gè)人偏好。
如何傳遞事件監聽(tīng)器?
React:
簡(jiǎn)單事件(如點(diǎn)擊事件)的事件監聽(tīng)器很簡(jiǎn)單。以下是我們?yōu)樘砑有麓k事項的按鈕創(chuàng )建 click 事件的示例:
非常簡(jiǎn)單,看起來(lái)很像是使用純 JS 處理內聯(lián)的 onClick 事件。而在 Vue 中,需要花費更長(cháng)的時(shí)間來(lái)設置事件監聽(tīng)器。input 標簽需要處理 onKeyPress 事件,如下所示:
只要用戶(hù)按下了'enter'鍵,這個(gè)函數就會(huì )觸發(fā) createNewToDoItem 函數,如下所示:
handleKeyPress = (e) => {
if (e.key === ‘Enter’) {
this.createNewToDoItem();
}
};
Vue:
在 Vue 中,要實(shí)現這個(gè)功能非常簡(jiǎn)單。我們只需要使用 @符號和事件監聽(tīng)器的類(lèi)型。例如,要添加 click 事件偵聽(tīng)器,我們可以這樣寫(xiě):
注意:@click 實(shí)際上是寫(xiě) v-on:click 的簡(jiǎn)寫(xiě)。在 Vue 中,我們可以將很多東西鏈接到事件監聽(tīng)器上,例如.once 可以防止事件監聽(tīng)器被多次觸發(fā)。在編寫(xiě)用于處理按鍵特定事件偵聽(tīng)器時(shí),還可以使用一些快捷方式。我發(fā)現,在 React 中為添加待辦事項按鈕創(chuàng )建一個(gè)事件監聽(tīng)器需要花費更長(cháng)的時(shí)間。而在 Vue 中,我可以簡(jiǎn)單地寫(xiě)成:
如何將數據傳給子組件?
React:
在 React 中,當創(chuàng )建子組件時(shí),我們將 props 傳給它。
我們將 todo props 傳給了 ToDoItem 組件。從現在開(kāi)始,我們可以在子組件中通過(guò) this.props 引用它們。因此,要訪(fǎng)問(wèn) item.todo,我們只需調用 this.props.todo。
Vue:
在 Vue 中,當創(chuàng )建子組件時(shí),我們將 props 傳給它。
:todo='item.todo'
:key='list.indexOf(item)'
:id='list.indexOf(item)'
>
然后,我們將它們加入到子組件的 props 數組,如:props:[‘id’,'todo']。然后可以在子組件中通過(guò)名字來(lái)引用它們,入'id'和'todo'。
如何將數據發(fā)送回父組件?
React:
我們在調用子組件時(shí)將函數作為 prop 傳給子組件,然后通過(guò)任意方式調用子組件的函數,這將觸發(fā)位于父組件中的函數。我們可以在“如何刪除待辦事項”一節中看到整個(gè)過(guò)程的示例。
Vue:
在我們的子組件中,我們只需寫(xiě)一個(gè)函數,讓它向父函數發(fā)回一個(gè)值。在父組件中,我們寫(xiě)了一個(gè)函數來(lái)監聽(tīng)這個(gè)值,然后觸發(fā)函數調用。我們可以在“如何刪除待辦事項”一節中看到整個(gè)過(guò)程的示例。
聯(lián)系客服