我用Vue和React構建了相同的應用程序,這是他們的差異
在工作中使用了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/>的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:
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
感謝覃云對本文的審校。
來自:http://www.infoq.com/cn/articles/differences-between-react-and-vue