譯者 jonjia 愛貝睿技術團隊javascript
在工做中使用 Vue 一段時間後,對它的工做原理有了至關深刻的瞭解。然而,我很想知道籬笆另外一邊的草地是什麼樣 - React。vue
我已經閱讀了 React 文檔,也觀看了一些教程視頻,雖然它們都很棒,但我真正想知道的是 React 與 Vue 到底有什麼不一樣。這裏的「不一樣」不是指它們是否具備虛擬 DOM 或者它們如何渲染頁面。我但願有人能直接解釋代碼並告訴我會發生什麼!我想找到一篇能解釋這些差別的文章,那樣 Vue、React (或者 Web 開發)的新手就能夠更好地理解二者之間的差別了。java
但我找不到任何解決這個問題的資源。因此我意識到必須靠本身來解決這個問題,發現它們之間的類似之處和不一樣之處。在這樣作時,我想記錄下整個過程,因此最終就有了這樣一篇文章。react
你 pick 誰?git
我決定構建一個標準的 Todo 列表應用,容許用戶添加、刪除列表中的項目。兩個應用都使用默認的 CLI (React 的 create-react-app,Vue 的 vue-cli) 來構建。順便說一下,CLI 表示命令行界面。🤓github
Vue vs React:平分秋色vue-cli
兩個應用的 CSS 代碼徹底相同,但這些代碼的位置不一樣。爲了說明這一點,咱們先看看兩個應用的文件結構,以下:數組
你 pick 誰?bash
能夠發現,它們的結構幾乎相同。惟一不一樣是:React 應用有 3 個 CSS 文件;Vue 應用一個也沒有。這樣作的緣由是:在 create-react-app 中,每一個 React 組件都會附帶一個樣式文件來保存其樣式;而 Vue CLI 採起單文件組件,每一個組件的樣式都會在組件內部聲明。app
最終,它們都達到了一樣目的,你也能夠在 React 或 Vue 中以不一樣方式構建本身的 CSS。這徹底取決於我的偏好 - 你會在開發社區中聽到許多關於如何構建 CSS 的討論。如今,咱們會遵循兩個 CLI 中列出的結構。
但在進一步討論以前,讓咱們先看看典型的 Vue 和 React 組件是什麼樣的:
左邊是 Vue 組件,右邊是 React 組件。
如今開始,讓咱們深刻了解細節吧!
首先,「改變數據」是什麼意思?聽起來有些技術含量不是嗎?它基本上表示改變咱們存儲的數據。所以,若是咱們想將一我的的名字從 John 改成 Mark,咱們就須要「改變數據」。這是 React 和 Vue 之間的關鍵區別所在。Vue 本質上建立了一個數據對象,其中的數據能夠自由更新;而 React 建立了一個狀態對象,就須要更多的工做來完成更新。React 有充分的理由要求額外的工做,咱們會稍微介紹一下。但首先,讓咱們看一下 Vue 中的 data 對象和 React 中的 state 對象:
左邊是 Vue data 對象,右邊是 React state 對象。
你能夠看到咱們給兩個對象傳遞了相同的數據,只是標識符不一樣。將初始數據傳入組件的方式很是類似。但正如上面提到的,如何改變這些數據在兩個框架之間會有所不一樣。
假設有一個名爲 name 的數據元素,它的值是:'Sunil'。
在 Vue 中,咱們經過 this.name
來引用它。也能夠經過 this.name = 'John'
來更新它。這會把個人名字改成 John。
在 React 中,咱們須要經過 this.state.name
來引用相同的數據。如今關鍵區別在於咱們不能簡單地經過 this.state.name = 'John'
,由於 React 內部機制會防止這種簡單、輕易的改變。因此在 React 中,咱們會經過 this.setState({ name: 'John' })
來更新數據。
雖然這和咱們在 Vue 中的方法都能實現相同目的,但 React 內部爲防止咱們意外地覆蓋 this.state
有一些額外代碼,this.state
和 this.setState
之間區別明顯。有些理由說明了爲何 React 改變數據的方式與 Vue 不一樣,Revanth Kumar 的解釋以下:
這是由於 React 但願在狀態發生變化時從新執行某些生命週期方法,如 componentWillReceiveProps、shouldComponentUpdate、componentWillUpdate、render 和 componentDidUpdate。當你調用 setState 方法時,它會很快知道狀態發生了改變。若是你直接修改 state,React 須要作更多工做來跟蹤修改以及從新運行生命週期方法等等。因此爲了簡單起見,React 使用 setState 方法。
肖恩·賓頗有經驗(Sean Bean:《指環王:護戒使者》中博羅米爾扮演者,其中臺詞 「One does not simply walk into Mordor(魔多不是你想去就能去的),此處爲:this.state
不是你想用就能用的)
如今咱們已經知道如何改變數據,而後來看看如何在 Todo 列表應用中添加新項目。
createNewToDoItem = () => {
this.setState( ({ list, todo }) => ({
list: [
...list,
{
todo
}
],
todo: ''
})
);
};
複製代碼
在 React 中,input 元素的 value
屬性被綁定到了 this.state.todo
這個值上。這個值能夠經過調用一些函數實現自動更新,這些函數綁定到一塊兒就建立了雙向數據綁定(若是你以前沒聽過,在後面的使用 Vue 如何實現部分有更詳細的解釋)。React 經過在 input 元素上綁定 onChange 方法來實現雙向綁定。讓咱們來看看 input 元素是什麼樣的,而後再來解釋原理:
<input type="text"
value={this.state.todo}
onChange={this.handleInput}/>
複製代碼
若是 input 元素的值發生變化,handleInput 方法就會被調用。它會使用 input 中的內容來更新 state 對象中 todo 的值。這個函數以下:
handleInput = e => {
this.setState({
todo: e.target.value
});
};
複製代碼
如今,每當用戶按下頁面上的 + 按鈕添加新項目時,createNewToDoItem 函數就會運行 this.setState
方法並向其傳遞一個函數做爲參數。這個函數有兩個參數,第一個是來自 state 對象的整個 list 數組,第二個是新的 todo(由 handleInput 函數更新)項。而後該函數返回一個新對象,該對象包含以前的整個 list,而後在 list 末尾添加新的 todo 項。整個 list 是使用擴展運算符添加的(若是你之前沒有看過這個語法能夠搜索一下,這是 ES6 語法)。
最後,咱們將 todo 設置爲空字符串,它會自動更新 input 元素的值。
createNewToDoItem() {
this.list.push(
{
'todo': this.todo
}
);
this.todo = '';
}
複製代碼
在 Vue 中,input 元素有一個名爲 v-model 的指令。能夠幫助咱們建立雙向數據綁定。先看看 input 元素是什麼樣的,而後再來解釋原理:
<input type="text" v-model="todo"/>
複製代碼
V-Model 指令將 input 元素的值綁定到數據對象中的 toDoItem。當頁面加載時,咱們經過 todo: ''
將 toDoItem 設置爲空字符串。若是這裏已經有一些數據,例如 todo: '原有 todo'
,input 元素會使用已有數據原有 todo 做爲初始值。不管如何,假設使用空字符串做爲初始值,咱們在 input 元素輸入的任何文本都綁定到 todo 上。這實際上就是雙向綁定(input 元素能夠更新數據對象,數據對象也能夠更改 input 元素的值)。
因此回顧前面 createNewToDoItem() 代碼,咱們看到它將 todo 的內容添加到 list 數組,而後將 todo 更新爲空字符串。
deleteItem = indexToDelete => {
this.setState(({ list }) => ({
list: list.filter((toDo, index) => index !== indexToDelete)
}));
};
複製代碼
雖然 deleteItem 方法定義在 ToDo.js 文件中,但先將 deleteItem() 方法做爲 組件的 prop 傳遞進去,在 ToDoItem.js 內部引用它也就很容易了,寫法以下:
<ToDoItem deleteItem={this.deleteItem.bind(this, key)}/>
複製代碼
首先將方法傳遞到子組件,使其能夠訪問。一樣能夠看到咱們綁定了 this,並把 key 做爲參數傳遞,key 用來區分點擊刪除的是哪一個 ToDoItem。而後,在 ToDoItem 內部,代碼以下:
<div className="ToDoItem-Delete" onClick={this.props.deleteItem}>-</div>
複製代碼
全部須要引用父組件中的一個方法只經過 this.props.deleteItem 就能夠實現。
onDeleteItem(todo){
this.list = this.list.filter(item => item !== todo);
}
複製代碼
Vue 應用須要稍微不一樣的方法。基本上分爲三步:
首先,在元素上綁定點擊事件處理方法:
<div class="ToDoItem-Delete" @click="deleteItem(todo)">-</div>
複製代碼
而後,建立一個調用 emit 方法的函數做爲子組件(這個例子中,就是 ToDoItem.vue 組件)內部方法,以下:
deleteItem(todo) {
this.$emit('delete', todo)
}
複製代碼
除此以外,當咱們在 ToDo.vue 中添加 ToDoItem.vue 時,咱們實際引用了一個函數:
<ToDoItem v-for="todo in list"
:todo="todo"
@delete="onDeleteItem" // <-- 這裏 :)
:key="todo.id" />
複製代碼
這就是所謂的自定義事件監聽器。它會監放任何由 emit 觸發名爲 delete 的事件發生的場合。若是監聽到,就會觸發執行名爲 onDeleteItem 的方法。這個方法定義在 ToDoItem.vue 組件內部而不是 ToDoItem.vue 組件。這個方法,正如上面所示,會過濾 data 對象內的 todo 數組並移除點擊的項目。
這裏值得注意的是:在 Vue 應用中,也能夠把 $emit
部分寫到**@click** 指令中,以下:
<div class="ToDoItem-Delete" @click="this.$emit('delete', todo)">-</div>
複製代碼
這樣能夠將步驟從 3 步減小到 2 步,這也僅僅取決於我的偏好。
簡而言之,React 中的子組件能夠經過 this.props 訪問父組件(假設你向下傳遞 props,這是至關標準的作法,你會在其它 React 示例中屢次看到)中的方法,而在 Vue 中,你必須從子組件內部發出一般在父組件內監聽的事件。
簡單事件(如點擊事件)的事件監聽器是直截了當的。如下是咱們爲新建 ToDo 項按鈕綁定 click 事件監聽的示例:
<div className="ToDo-Add" onClick={this.createNewToDoItem}>+</div>.
複製代碼
這裏的實現很是簡單,看起來很像使用原生 JS 來處理行內的 onClick 事件。正如 Vue 部分提到的,若是是爲按下回車按鈕設置事件監聽器就須要花費更長的時間了。input 標籤一般會處理 onKeyPress 事件,以下:
<input type="text" onKeyPress={this.handleKeyPress}/>.
複製代碼
只要這個方法監聽到了回車鍵按下,它就會調用 createNewToDoItem 函數,以下所示:
handleKeyPress = (e) => {
if (e.key === 'Enter') {
this.createNewToDoItem();
}
};
複製代碼
Vue 中的實現超級直接。只需使用 @ 符號,而後綁定相應的事件監聽器。例如,要添加 click 事件監聽器,只需以下編寫代碼:
<div class="ToDo-Add" @click="createNewToDoItem()">+</div>
複製代碼
注意:@click 其實是 v-on:click 的簡寫。Vue 事件監聽器另外一個很酷的事情是:有不少修飾符能夠連接到後面,例如 .once,它能夠防止事件監聽器被屢次觸發。在編寫用於處理鍵盤事件偵聽器時,也有一些快捷方式。我發如今 React 中爲建立新的 ToDo 項綁定一個事件監聽器須要花費更長的時間。而在 Vue 中,我可以像下面這樣簡單實現:
<input type="text" v-on:keyup.enter="createNewToDoItem"/>
複製代碼
在 React 中,咱們在使用子組件的地方經過 prop 傳遞數據,以下:
<ToDoItem key={key} item={todo} />
複製代碼
上面有兩個 props 傳遞給了 ToDoItem 組件。這樣傳遞以後,就能夠在子組件內部經過 this.props
來引用它們了。所以,就能夠經過 this.props.item
訪問 todo 變量了。
在 Vue 中,也是在使用子組件的地方傳遞數據,以下:
<ToDoItem v-for="todo in list"
:todo="todo" :id="todo.id"
:key="todo.id"
@delete="onDeleteItem" />
複製代碼
這樣傳遞以後,咱們會把這些數據傳遞到子組件的 props 數組中:props: [ 'id', 'todo' ]。而後就能夠在子組件中經過它們的名字進行引用了,好比 id 和 todo。
咱們首先將函數傳遞給子組件,方法就是在使用子組件時將其做爲 prop 傳入。而後在形如 onClick 方法中經過 this.props.whateverTheFunctionIsCalled 引用這個函數。這將觸發位於父組件中定義的函數。能夠在如何從列表中刪除 Todo 項一節中看到整個過程的一個示例。
在子組件中,咱們只需編寫一個函數,將一個事件名發送回父組件。在父組件中,咱們編寫一個函數來監聽這個事件,它會觸發函數調用。能夠在如何從列表中刪除 Todo 項一節中看到整個過程的一個示例。
咱們研究瞭如何添加、刪除和更改數據,以 prop 形式將數據從父組件傳入到子組件,以及經過事件偵聽器形式將數據從子組件發送到父組件。固然,在 React 和 Vue 之間還存在許多其它差別,但但願本文的內容對你理解兩個框架如何處理問題打下一個好的基礎 🤓
Vue ToDo:github.com/sunil-sandh…
React ToDo:github.com/sunil-sandh…
翻譯自 I created the exact same app in React and Vue. Here are the differences.,祝好。
公衆號:
博客 ![]()