在開發中使用了vue之后,會感受到vue比jquery高效不少,一個指令可以省略不少代碼,但當學習和使用了react之后感覺更有趣的也出現了,就是喜歡上了比較vue和react的異同。今天就簡單談談兩者的部分差異。
首先,除了react使用虛擬DOM一大亮點外,我們先從直觀的地方比較下兩者的差異:在使用cli(React 的 create-react-app 和 Vue 的 vue-cli)創建應用時,應用的外觀展示如下:
兩個應用程序代碼結構及存放位置存在的差別:
它們的結構也幾乎完全相同,唯一的區別是 React 有三個 CSS 文件,而 Vue 則沒有。這是因為 React 組件需要一個附帶的文件來保存樣式,而 Vue 采用包含的方式,將樣式聲明在組件文件中。
從理論上講,你可以使用老式的 style.css 文件來保存整個頁面的樣式,這完全取決於你自己。不管怎樣,還是展示一下.vue 文件中的 CSS 代碼長什么樣:
然后讓我們在看看兩者在數據的傳遞,增刪改方面的差異。
數據如何傳給子組件?
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:
在我們的子組件中,我們只需寫一個函數,讓它向父函數發回一個值。在父組件中,我們寫了一個函數來監聽這個值,然后觸發函數調用。我們可以在“如何刪除待辦事項”一節中看到整個過程的示例。
怎樣傳遞事件監聽器?
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:
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() 函數作為的 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>
這樣可以減少一些代碼,不過也取決於個人偏好。
怎樣改變數據?
我們說“改變數據”(mutate data),實際上就是指修改已經保存好的數據。比如,如果我們想將一個人的名字從 John 改成 Mark,我們就要“改變數據”。這就是 React 和 Vue 的關鍵區別之一。Vue 創建了一個數據對象,我們可以自由地更新數據對象,而 React 創建了一個狀態對象,要更新狀態對象,需要做更多瑣碎的工作。下面是 React 的狀態對象和 Vue 的數據對象之間的對比:
React 的狀態對象:
Vue 的數據對象:
從圖中可以看到,我們傳入的是相同的數據,它們只是標記的方式不一樣。但它們在如何改變這些數據方面卻有很大的區別。
假設我們有一個數據元素 name:'david'。
在 Vue 中,我們通過 this.name 來引用它。我們也可以通過 this.name='John'來更新它,這樣會把名字改成 John。
在 React 中,我們通過 this.state.name 來引用它。關鍵的區別在於,我們不能簡單地通過 this.state.name='John'來更新它,因為 React 對此做出了限制。在 React 中,我們需要使用 this.setState({name:'John'}) 的方式來更新數據。
示例參考指南:
Vue:https://github.com/sunil-sandhu/vue-todo
React:https://github.com/sunil-sandhu/react-todo