不吹不黑丨用Vue和React構建相同應用程序,區別在哪?
作者 | Sunil Sandhu
譯者 | 無明
在工作中使用了 Vue 之後,我已經對它有了相當深入的了解。同時,我也對 React 感到好奇。我閱讀了 React 的文檔,也看了一些教程視頻,雖然它們很棒,但我真正想知道的是 React 與 Vue 有哪些區別。這裡所說的區別,並不是指它們是否都具有虛擬 DOM 或者它們如何渲染頁面。我真正想要做的是對它們的代碼進行並排比較,並搞清楚在使用這兩個框架開發應用時究竟有哪些差別。
我決定構建一個標準的待辦事項應用程序,用戶可以添加和刪除待辦事項。我分別使用它們默認的 CLI(React 的 create-react-app 和 Vue 的 vue-cli)來創建這個應用。先讓我們看一下這兩個應用的外觀:
兩個應用程序的 CSS 代碼幾乎完全相同,但代碼存放的位置存在差別。
它們的結構也幾乎完全相同,唯一的區別是 React 有三個 CSS 文件,而 Vue 則沒有。這是因為 React 組件需要一個附帶的文件來保存樣式,而 Vue 採用包含的方式,將樣式聲明在組件文件中。
從理論上講,你可以使用老式的 style.css 文件來保存整個頁面的樣式,這完全取決於你自己。不管怎樣,還是展示一下.vue 文件中的 CSS 代碼長什麼樣:
看完樣式方面的問題,現在讓我們深入了解其他細節!
如何改變數據?
我們說「改變數據」(mutate data),實際上就是指修改已經保存好的數據。比如,如果我們想將一個人的名字從 John 改成 Mark,我們就要「改變數據」。這就是 React 和 Vue 的關鍵區別之一。Vue 創建了一個數據對象,我們可以自由地更新數據對象,而 React 創建了一個狀態對象,要更新狀態對象,需要做更多瑣碎的工作。下面是 React 的狀態對象和 Vue 的數據對象之間的對比:
React 的狀態對象:
Vue 的數據對象:
從圖中可以看到,我們傳入的是相同的數據,它們只是標記的方式不一樣。但它們在如何改變這些數據方面卻有很大的區別。
假設我們有一個數據元素 name:"Sunil"。
在 Vue 中,我們通過 this.name 來引用它。我們也可以通過 this.name="John"來更新它,這樣會把名字改成 John。
在 React 中,我們通過 this.state.name 來引用它。關鍵的區別在於,我們不能簡單地通過 this.state.name="John"來更新它,因為 React 對此做出了限制。在 React 中,我們需要使用 this.setState({name:"John"}) 的方式來更新數據。
在了解了如何修改數據之後,接下來讓我們通過研究如何在待辦事項應用中添加新項目來深入了解其他細節。
如何創建新待辦事項?
React:
createNewToDoItem = () => {
this.setState( ({ list, todo }) => ({
list: [
...list,
{
todo
}
],
todo: ""
})
);
};
Vue:
createNewToDoItem() {
this.list.push(
{
"todo": this.todo
}
);
this.todo = "";
}
React 是怎麼做到的?
在 React 中,input 有一個叫作 value 的屬性。我們通過幾個與創建雙向綁定相關的函數來自動更新 value。React 通過為 input 附加 onChange 函數來處理雙向綁定。
<input type="text"
value={this.state.todo}
onChange={this.handleInput}/>
只要 input 的值發生變化,就會執行 handleInput 函數。這個函數會將狀態對象中 todo 欄位的值改為 input 中的值。這個函數看起來像這樣:
handleInput = e => {
this.setState({
todo: e.target.value
});
};
現在,只要用戶按下頁面上的 + 按鈕,createNewToDoItem 就會調用 this.setState,並傳入一個函數。這個函數有兩個參數,第一個是狀態對象的 list 數組,第二個是 todo(由 handleInput 函數更新)。然後函數會返回一個新對象,這個對象包含之前的整個 list,然後將 todo 添加到 list 的末尾。
最後,我們將 todo 設置為空字元串,它也會自動更新 input 中的值。
Vue 是怎麼做到的?
在 Vue 中,input 有一個叫作 v-model 的屬性。我們可以用它來實現雙向綁定。
<input type="text" v-model="todo"/>
v-model 將 input 綁定到數據對象 toDoItem 的一個 key 上。在載入頁面時,我們將 toDoItem 設置為空字元串,比如 todo:』』。如果 todo 不為空,例如 todo:』add some text here",那麼 input 就會顯示這個字元串。我們在 input 中輸入的任何文本都會綁定到 todo。這實際上就是雙向綁定(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() 函數作為
<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:
this.$on(『delete』, (event) => {
this.list = this.list.filter(item => item.todo !== event)
})
Vue 是怎麼做到的?
Vue 的方式稍微有點不同,我們基本上要做三件事。
首先,我們需要在元素上調用函數:
<div class=」ToDoItem-Delete」 @click=」deleteItem(todo)」>-</div>
然後我們必須創建一個 emit 函數作為子組件內部的一個方法(在本例中為 ToDoItem.vue),如下所示:
deleteItem(todo) {
this.$parent.$emit(『delete』, todo)
}
然後我們的父函數,也就是 this.$on(』delete』) 事件監聽器會在它被調用時觸發過濾器函數。
簡單地說,React 中的子組件可以通過 this.props 訪問父函數,而在 Vue 中,必須從子組件中向父組件發送事件,然後父組件需要監聽這些事件,並在它被調用時執行函數。
這裡值得注意的是,在 Vue 示例中,我也可以直接將 $emit 部分的內容寫在 @click 監聽器中,如下所示:
<div class=」ToDoItem-Delete」 @click=」this.$parent.$emit(『delete』, todo)」>-</div>
這樣可以減少一些代碼,不過也取決於個人偏好。
如何傳遞事件監聽器?
React:
簡單事件(如點擊事件)的事件監聽器很簡單。以下是我們為添加新待辦事項的按鈕創建 click 事件的示例:
<div className=」ToDo-Add」 onClick={this.createNewToDoItem}>+</div>
非常簡單,看起來很像是使用純 JS 處理內聯的 onClick 事件。而在 Vue 中,需要花費更長的時間來設置事件監聽器。input 標籤需要處理 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:click 的簡寫。在 Vue 中,我們可以將很多東西鏈接到事件監聽器上,例如.once 可以防止事件監聽器被多次觸發。在編寫用於處理按鍵特定事件偵聽器時,還可以使用一些快捷方式。我發現,在 React 中為添加待辦事項按鈕創建一個事件監聽器需要花費更長的時間。而在 Vue 中,我可以簡單地寫成:
<input type=」text」 v-on:keyup.enter=」createNewToDoItem」/>
如何將數據傳給子組件?
React:
在 React 中,當創建子組件時,我們將 props 傳給它。
<ToDoItem key={key} item={todo} />
我們將 todo props 傳給了 ToDoItem 組件。從現在開始,我們可以在子組件中通過 this.props 引用它們。因此,要訪問 item.todo,我們只需調用 this.props.todo。
Vue:
在 Vue 中,當創建子組件時,我們將 props 傳給它。
<ToDoItem v-for="item in this.list"
:todo="item.todo"
:key="list.indexOf(item)"
:id="list.indexOf(item)"
>
</ToDoItem>
然後,我們將它們加入到子組件的 props 數組,如:props:[『id』,"todo"]。然後可以在子組件中通過名字來引用它們,入"id"和"todo"。
如何將數據發送回父組件?
React:
我們在調用子組件時將函數作為 prop 傳給子組件,然後通過任意方式調用子組件的函數,這將觸發位於父組件中的函數。我們可以在「如何刪除待辦事項」一節中看到整個過程的示例。
Vue:
在我們的子組件中,我們只需寫一個函數,讓它向父函數發回一個值。在父組件中,我們寫了一個函數來監聽這個值,然後觸發函數調用。我們可以在「如何刪除待辦事項」一節中看到整個過程的示例。
示例代碼鏈接:
Vue:
https://github.com/sunil-sandhu/vue-todo
React:
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
今日薦文
點擊下方圖片即可閱讀
如何用 2 個 Unix 命令給 SQL 提速
從 Facebook、Oracle、京東等頭部大廠看進階中高級程序員,你還差點什麼?許多程序員所做的業務僅僅是功能上的實現,完全不會遇到諸如「雙十一」、「六一八」的高並發量挑戰。那沒有身處大型互聯網公司就沒有機會了?當然不,我們可以通過一些 BATJ 大牛的技術分享,學習到這些大型項目的設計和優化經驗。
識別下圖二維碼
或
戳閱讀原文
,解鎖更多!TAG:InfoQ |