原文:I created the exact same app in React and Vue. Here are the differences.
原文的程式碼:
react-todo、vue-todo
React版本:16.4.1
Vue版本:2.5.16
皆使用預設CLI建立APP
還有另外一篇Part 2,有另外一位作者加入Angular 6的做法來比較,原本要將兩片合一,嗯嗯嗯嗯嗯,後來放棄這個做法,兩片都翻譯。
Google翻譯 + Yahoo字典 + 自己順
作者在工作上是使用Vue,也對Vue有深刻的理解,但是他想知道React和Vue有什麼不同。
他看了React的文件和教學視頻,雖然文件和視頻很好也很全面,但是他想知道React和Vue的不同之處。不同之處並不是指是否有virtual DOMS或是如何渲染頁面,而是想知道程式碼的意思及如何運轉。作者爬過文,想要找可以解釋這些差異的文章,以方便新手可以理解React和Vue的差異。但是找不到,所以自己寫程式並記錄過程。
他建立非常標準的To-Dd App,允許新增、刪除項目,並且都使用CLI建立App(create-react-app for React,and vue-cli for Vue)。
React、Angular、Vue的畫面看起來如下
CSS Code除了放置的位置不同,內容實際上是一樣的。
文件結構:
你會發現兩者的文件結構很像,較大的差別是Vue將一個組件的內容全部放在一起,React將css放在單獨的檔案。css、html、script是否放在一起可以取決於各人偏好,這裡遵循CLI所產生的結構。
先看看組件長什麼樣子吧!
上圖:左邊是Vue(css包在一起),右邊是React
現在開始深入細節
基本上只是指改變我們儲存的資料,而這是Vue和React不同的關鍵之處。Vue建立了data物件之後可以自由更新,而React建立一個state物件之後,需要一些而外工作完成更新。我們先看看Vue的data物件和React的state物件。
|||
上圖:左邊是Vue的data物件,右邊是React的state物件
我們將相同的資料傳遞到兩者,你可以看到這只是標記符號的差異而已。將初始資料傳遞到我們的組件是非常相似的,但是我們才提到,改變資料才是這兩個框架的不同之處。
假設我們有一個資料元素叫做 name: 'Sunil'。
在Vue,我們通過 this.name 來引用,我們可以使用 this.name = 'John' 更新它,這樣name就會變成John。
在React,我們通過 this.state.name 來引用,關鍵的不同是不能使用 this.name = 'John' 更新它,而要使用 this.setState({name: 'John'}) 更新它。
雖然React的實現和Vue是相同的,實際上Vue在每次更新資料時會自己組合setState版本。也就是Vue會幫你做setState,而React要自己做。為什麼React需要自己setState?來看看Revanth Kumar的解釋:
“This is because React wants to re-run certain life cycle hooks, [such as] componentWillReceiveProps, shouldComponentUpdate, componentWillUpdate, render, componentDidUpdate, whenever state changes. It would know that the state has changed when you call the setState function. If you directly mutated state, React would have to do a lot more work to keep track of changes and what lifecycle hooks to run etc. So to make it simple React uses setState.”
這是因為當狀態改變時,React要重新運行某些生命週期的鉤子(life cycle hooks),例如componentWillReceiveProps、 shouldComponentUpdate、componentWillUpdate、render、componentDidUpdate。當你調用setState時,React就會知道狀態改變了。假如你直接異動狀態,React必須花更多功夫去保持變更追蹤和運行生命週期的鉤子。所以就用setState吧。
React:
createNewToDoItem = () => {
this.setState( ({ list, todo }) => ({
list: [
...list,
{
todo
}
],
todo: ''
})
);
};
React如何做到的?
在React,我們的輸入欄位有一個名為 value 的屬性。這個值經由使用幾個function達成自動更新,這幾個function結合在一起產生類似雙向綁定(two-way binding)的東西。(Vue的部分會再解釋)。我們建立一個有額外 onChange 事件傾聽的輸入欄位來達成雙向綁定。讓我們快速看看輸入欄位,你可以看到發什麼事:
<input type="text"
value={this.state.todo}
onChange={this.handleInput}/>
只要輸入欄位的 value改變, handleInput function也會跟著執行。它更新 state 物件的todo值。
這個function看起來像:
handleInput = e => {
this.setState({
todo: e.target.value
});
};
現在,只要使用者按下頁面上的+按鈕來增加新項目, createNewToDoItem function就會執行 this.setState 並傳遞一個function給它。這個function有兩個參數,第一個是來自 state 物件的整個 list ,第二個是 todo (被 handleInput function更新之後的)。 然後將返回一個新的物件,包含之前整個 list 並在其最後添加 todo。( ...是ES6的spread operator)。
最後,我們將todo設定為空字串,它會自動更新輸入欄位的值。
Vue:
createNewToDoItem() {
this.list.push(
{
'todo': this.todo
}
);
this.todo = '';
}
Vue如何做到的?
在Vue,我們的輸入欄位有一個叫 v-model 的句柄(handle)。這允許我們做雙向綁定。我們快速看看輸入欄位,我們會解釋發生什麼事:
<input type="text" v-model="todo"/>
V-Model綁定此欄位的輸入到 data 物件的toDoItem。當頁面載入時,toDoItem設定空字串如 todo: '',假如已經有一些資料,例如 todo: 'add some text here' ,輸入欄位就會載入add some text here。無論怎樣,不管我們在輸入欄位輸入什麼文字,都會更新至todo的值。這實際上是雙向綁定(輸入欄位可以更新data物件,data物件可以更新輸入欄位)。
回顧 createNewToDoItem() 程式碼,看到我們將 todo的內容塞進 list 陣列,然後將 todo 設為空字串。
React:
deleteItem = indexToDelete => {
this.setState(({ list }) => ({
list: list.filter((toDo, index) => index !== indexToDelete)
}));
};
How did React do that?
雖然deleteItem function在 ToDo.js 中,我們可以很容易地在ToDoItem.js 引用它,首先將 deleteItem() 透過 的prop 傳入:
<ToDoItem deleteItem={this.deleteItem.bind(this, key)}/>
首首先將function傳給子組件,你可以看到我們綁定了 this 和 key先參數, key是此function在按下刪除之後用來識別要刪除哪一個ToDoItem。 ToDoItem組件內部,我們執行以下操作:
<div className=”ToDoItem-Delete” onClick={this.props.deleteItem}>-</div>
最後要做的就是用this.props.deleteItem 引用父組件的function。
Vue:
onDeleteItem(todo){
this.list = this.list.filter(item => item !== todo);
}
How did Vue do that?
Vue的做法稍微不同,基本上有三件事:
首先,在元素上呼叫function:
<div class=”ToDoItem-Delete” @click=”deleteItem(todo)”>-</div>
然後在子組件創建一個 emit function(本範例是ToDoItem.vue),看起來像:
deleteItem(todo) {
this.$emit('delete', todo)
}
你應該有注意到當我們在ToDo.vue增加ToDoItem.vue時實際上引用了一個function:
<ToDoItem v-for="todo in list"
:todo="todo"
@delete="onDeleteItem" // <-- this :)
:key="todo.id" />
這叫做客製事件監聽器(customer event-listener),它監聽所有用delete字串觸發的發射(emit),如果監聽到就觸發onDeleteItem function。這個function在ToDo.vue ,而不是ToDoItem.vue。這個function是用來按下刪除時過濾data物件的todo 陣列,並刪除項目。
在Vue中可以簡單地在**@click使用$emit**達成監聽:
<div class=”ToDoItem-Delete” @click=”$emit(‘delete’, todo)”>-</div>
這樣就只需要兩個步驟(三步變成兩步),這取決各人習慣。
簡而言之,React中的子組件可以通過this.props訪問父function(假設你將傳遞props,這是標準做法,你也能在其他React範例看到),而在Vue中則是由子組件發射事件,由父組件搜集這些事件。
React:
Event listeners(例如click 事件)是簡單直接的。下面是建立一個有 click事件按鈕來創建新ToDo項目的範例:
<div className=”ToDo-Add” onClick={this.createNewToDoItem}>+</div>
這裡很簡單。如同下方的Vue部分所描述,它花了更多時間去設定按下Enter 後的事件監聽處理。本質上是輸入標籤要求一個onKeyPress 事件。
<input type=”text” onKeyPress={this.handleKeyPress}/>
當辨識出Enter 被按下時,這個function就會觸發 createNewToDoItem,例如:
handleKeyPress = (e) => {
if (e.key === ‘Enter’) {
this.createNewToDoItem();
}
};
Vue:
在Vue是更簡單直接的。使用 @ 符號,然後加上我們要去做的 event-listener類型。範例如下:
<div class=”ToDo-Add” @click=”createNewToDoItem()”>+</div>
註:@click 是 v-on:click 的簡寫。Vue的事件監聽是很酷的東西,還有很多事件可以鏈結,例如 .once 可以預防事件監聽被觸發多次。處理鍵盤事件監聽還有許多簡潔方法。我發現在React光是按下 *Enter 按鍵來建立新的ToDo項目就需要花更長的時間。在Vue只需要這樣寫:
<input type=”text” v-on:keyup.enter=”createNewToDoItem”/>
譯者:我認為是指React要自己寫程式判斷是不是按下Enter,而Vue有提供v-on:keyup.enter。
React:
在React中,在我們創建子組件時,我們就設定好傳遞資料了。例如:
<ToDoItem key={key} item={todo} />
這裡我們可以看到有兩個 props 傳給 ToDoItem 組件。從此我們可以經過 this.props 引用他們,要去取得 item.todo prop,我們可以呼叫 海伯利昂號 this.props.item。
Vue:
在Vue,一樣是在創建子組件時設定。例如:
<ToDoItem v-for="todo in list"
:todo="todo"
:key="todo.id"
@delete="onDeleteItem" />
完成後,我們傳遞資料給子組件的 props 陣列,如: props: [ 'todo' ]。通過名稱我們可以在子組件引用,本案例是 'todo'。
React:
首先將 function 傳給子組件,讓子組件可以透過prop引用。我們在子組件增加一個call function(例如 onClick),透過這個function去調用 this.props.whateverTheFunctionIsCalled 。這就會觸發父組建中的function。範例可以看如何從list刪除資料?
Vue:
在我們的子組件只需要寫一個function將資料傳回父組件的function。在父組件寫一個function監聽該資料何時發出,以觸發function呼叫。範例一樣可以看如何從list刪除資料?
我們研究了如何增、刪、修資料,父、子組件間的資料傳遞。當然React和Vue之間還存在一些小差異和怪僻,但是希望這篇文章的內容能夠幫助你理解這兩者是如何處理東西。