1. 程式人生 > >使用 React 與 Vue 建立同一款 App,差別究竟有多大?

使用 React 與 Vue 建立同一款 App,差別究竟有多大?

原文連結:https://www.jianshu.com/p/7cf...

眾所周知,Vue 和 React 都是目前非常著名的前端框架。我在工作中經常使用 Vue,因此我對它有很深入的瞭解。同時,我也對 React 充滿了好奇,想要學習一下,一探究竟。

於是我閱讀了 React 文件並觀看了一些視訊教程,雖然這些資料很不錯,但是我真正想了解的是 React 與 Vue 之間的不同之處。所謂“不同之處”,我並非想知道它們是否都具有虛擬 DOMS 或者它們如何渲染頁面,而是希望有人能夠從程式碼的角度解釋這兩者之間的差異。我想找到一篇解釋這些差異的文章,以便 Vue 或者 React 的初學者可以更好地理解它們兩者之間的差異。

很遺憾,我並未找到一篇這樣的文章。於是我意識到必須自己動手來比較 Vue 與 React 之間的異同。在我自力更生的過程中,我用這篇文章記錄下了具體過程。

1.目標

我將會構建一個標準的待辦事項應用程式,允許使用者新增和刪除列表中的專案。這兩個應用程式都使用預設的 CLI(command-line interface,命令列介面) 構建,React 使用 create-react-app,Vue 使用 vue-cli。

兩個應用程式的外觀如下:

兩個應用程式的 CSS 程式碼幾乎一樣,但這些程式碼的位置存在差異。考慮到這一點,我們來看看這兩個應用程式的檔案結構:

你會發現它們的結構幾乎完全相同。唯一的區別在於 React App 擁有三個 CSS 檔案,而 Vue App 中沒有 CSS 檔案。這是因為 React 的 create-react-app 元件需要一個附帶檔案來儲存其樣式,而 Vue CLI 採用全包方法,其樣式在實際元件檔案中宣告。

兩種不同的策略得到的結果是一樣的,相信開發者很快能夠掌握這兩種不同的策略。開發者可以根據自己的偏好做出選擇,你會聽到開發社群關於如何構建 CSS 的大量討論。以上,我們遵循兩個 CLI 列出了程式碼結構。

在我們進一步討論之前,先快速看一下典型的 Vue 和 React 元件的外觀:

左側為 Vue,右側為 React

現在讓我們正式開始,深入其中的細節!

2.如何修改資料

首先,我們需要明白“修改資料”的意思是什麼。它聽起來有些學術,但實際上很簡單,就是把我們已經儲存好的資料進行更改。比如,如果我們想把一個人的名字變數從“Jhon”改為“Mark”,我們就需要執行“修改資料”的操作。在這一點上,React 和 Vue 的處理方式有所區別。Vue 本質上會建立一個數據物件,其中的資料可以自由更改;React 則建立一個狀態物件,更改資料需要一些額外的操作。React 之所以需要額外的操作有著自己的理由,稍後我會深入介紹。在此之前,我們先看看 Vue 中的資料物件和 React 中的狀態物件:

Vue 資料物件

React 狀態物件

從圖中可以看出,我們傳入了相同的資料,但它們的標記方法不同。因此,將初始資料傳遞到元件的方式非常相似。但正如我們提到的那樣,在兩個框架中更改資料的方式有所不同。

假設我們有一個名為 name: ‘Sunil’ 的資料元素。

在 Vue 中,我們通過呼叫 this.name 來引用它。我們也可以通過呼叫 this.name ='John' 來更新它。這樣一來,名字就被成功改為了 “Jhon”。

在 React 中,我們通過呼叫 this.state.name 來引用同一段資料。現在關鍵的區別在於,我們不能簡單地寫成 this.state.name ='John',因為 React 有限制機制,它會阻止這種簡單的修改方式。在 React 中,我們需要這樣寫:this.setState({name:'John'})。

雖然這基本上與我們在 Vue 中實現的結果一樣,但是 React 的操作更為繁瑣,那是因為 Vue 在每次更新資料時預設組合了自己的 setState 版本。 簡單來說就是,React 需要 setState,然後更新其內部資料,而對於 Vue 來說,當你更新資料物件的值時它就默認了你的更改意圖。 那麼為什麼 React 沒有進行簡化,為什麼需要 setState 呢? Revanth Kumar 對此做出瞭解釋:

“這是因為 React 希望在狀態發生變化時重新執行某些生命週期 hook,比如 componentWillReceiveProps、shouldComponentUpdate、componentWillUpdate、render、componentDidUpdate。當你呼叫 setState 函式時,它知道狀態已經改變。如果你直接改變狀態,React 將需要做更多工作來跟蹤更改以及執行生命週期 hook 等等。所以為了簡單起見,React 使用 setState。"

現在我們知道如何更改資料了,接下來看看如何在待辦應用程式中新增新的事項。

3.新增新的待辦事項

React 的實現方法

createNewToDoItem = () => {    
this.setState( ({ list, todo }) => ({      
list: [         
...list,       
 {          todo
        }     
 ],      
todo: ''    
})
  );
};

在 React 中,我們的輸入欄位有一個名為 value 的屬性。這個 value 通過使用幾個函式自動更新,這些函式繫結在一起以建立雙向繫結。我們通過在輸入欄位上附加一個 onChange 事件監聽器來建立這種形式的雙向繫結。看看程式碼,一探究竟:

<input type="text"        
value={this.state.todo}        
onChange={this.handleInput}/>

只要輸入欄位的值發生更改,handleInput 函式就會執行。它通過將狀態物件設定為輸入欄位中的任何內容來更新狀態物件內的 todo。handleInput 函式如下:

handleInput = e => {
  this.setState({
    todo: e.target.value
  });
};

現在,只要使用者按下頁面上的 + 按鈕新增新專案,createNewToDoItem 函式就會執行 this.setState 並向其傳遞一個函式。該函式有兩個引數,第一個是來自狀態物件的整個列表陣列,第二個是由 handleInput 函式更新的todo。然後該函式返回一個新物件,該物件包含之前的整個列表,並在其末尾新增todo。整個列表是通過使用擴充套件運算子新增的。

最後,我們將 todo 設定為空字串,它會自動更新輸入欄位中的 value。

Vue 的實現方法

createNewToDoItem() {
    this.list.push(
        {
            'todo': this.todo
        }    );    this.todo = '';
}

在 Vue 中,我們的輸入欄位中有一個名為 v-model 的控制代碼。這實現了**雙向繫結。輸入欄位程式碼如下:

<input type="text" v-model="todo"/>

V-Model 將輸入欄位的內容繫結到名為 toDoItem 的資料物件的鍵(key)上。當頁面載入時,我們將 toDoItem 設定為空字串,比如:todo:' '。如果已經存在資料,例如 todo:'新增文字處',輸入欄位將載入新增文字處的輸入內容。無論如何,將其作為空字串,我們在輸入欄位中鍵入的任何文字都會繫結到 todo。這實際上是雙向繫結(輸入欄位可以更新資料物件,資料物件可以更新輸入欄位)。

因此,回顧前面的 createNewToDoItem() 程式碼塊,我們將 todo 的內容存放到列表陣列中 ,然後將 todo 改為空字串。

4.刪除待辦事項

React 的實現方法

deleteItem = indexToDelete => {
    this.setState(({ list }) => ({
      list: list.filter((toDo, index) => index !== indexToDelete)
    }));
};

儘管 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 的實現方法

onDeleteItem(todo){
  this.list = this.list.filter(item => item !== todo);
}

Vue 的實現方法稍有不同,我們需要做到以下三點:

Step 1:首先,在元素上呼叫函式:

<div class=”ToDoItem-Delete” @click=”deleteItem(todo)”>-</div>

Step 2:然後我們必須建立一個 emit 函式,將其作為子元件的內部方法(在本例中為ToDoItem.vue),如下所示:

deleteItem(todo) {
    this.$emit('delete', todo)
}

Step 3:之後,你會發現,當我們新增 ToDo.vue的 ToDoItem.vue 時,實際上引用了一個函式:

<ToDoItem v-for="todo in list"
           :todo="todo"
           @delete="onDeleteItem" // <-- this :)
          :key="todo.id" />

這就是所謂的自定義事件監聽器。它會監聽任何使用 'delete' 字串的觸發事件。一旦監聽到事件,它會觸發一個名為 onDeleteItem 的函式。此函式位於 ToDo.vue 內部,而不是 ToDoItem.vue。如前所述,該函式只是過濾資料物件內的 todo 陣列 ,以刪除被點選的待辦事項。

在 Vue 示例中還需要注意的是,我們可以在 @click 偵聽器中編寫 $emit 部分,這樣更加簡單,如下所示:

 <div class=”ToDoItem-Delete” @click=”$emit(‘delete’, todo)”>-</div> 

如果你喜歡,這樣做可以把 3 步減少到 2 步。

React 中的子元件可以通過 this.props 訪問父函式,而在 Vue 中,你需要從子元件中發出事件,父元件來收集事件。

5.如何傳遞事件監聽器

React 的實現方法

事件監聽器處理簡單事件(比如點選)非常直接。我們為待辦事項建立了點選事件,用於建立新的待辦事項,程式碼如下:

<div className=”ToDo-Add” onClick={this.createNewToDoItem}>+</div>.

非常簡單,就像使用 vanilla JS 處理內聯 onClick 一樣。正如前文所述,只要按下回車按鈕,設定事件監聽器就需要花費更長的時間。這需要輸入標籤處理 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 就需要花費更長的時間來建立事件監聽器,從而建立新的 ToDo 專案。在 Vue,程式碼如下:

<input type=”text” v-on:keyup.enter=”createNewToDoItem”/>

6.如何將資料傳遞給子元件

React 的實現方法

在 React 中,我們將 props 傳遞到子元件的建立處。比如:

<ToDoItem key={key} item={todo} />

此處我們向 ToDoItem 元件傳遞了兩個 prop。之後,我們可以在子元件中通過 this.props 引用它們。因此,想要訪問 item.todo prop,我們只需呼叫this.props.item 。

Vue 的實現方法

在 Vue 中,我們將 props 傳遞到子元件建立處的方式如下:

<ToDoItem v-for="todo in list"
             :todo="todo"
            :key="todo.id"
            @delete="onDeleteItem" />

我們將它們傳遞給子元件中的 props 陣列,如:props:['id','todo']。然後可以在子元件中通過名字引用它們。

7.如何將資料傳送回父元件

React 的實現方法

我們首先將函式傳遞給子元件,方法是在我們呼叫子元件時將其引用為 prop。然後我們通過引用 this.props.whateverTheFunctionIsCalled,為子元件新增呼叫函式,例如 onClick。然後,這將觸發父元件中的函式。刪除待辦事項一節中詳細介紹了整個過程。

Vue 的實現方法

在子元件中我們只需編寫一個函式,將一個值傳送回父函式。在父元件中編寫一個函式來監聽子元件何時發出該值的事件,監聽到事件之後觸發函式呼叫。同樣,刪除待辦事項一節中詳細介紹了整個過程。

8.總結

我們研究了新增、刪除和更改資料,以 prop 形式從父元件到子元件傳遞資料,以及通過事件監聽器的形式將資料從子元件傳送到父元件。當然,React 和 Vue 之間存在一些小差異,希望本文的內容有助於理解這兩個框架。

兩個應用程式的 GitHub 地址:

Vue ToDo:https://github.com/sunil-sandhu/vue-todo

React ToDo:https://github.com/sunil-sandhu/react-todo


本次給大家推薦一個免費的學習群,裡面概括移動應用網站開發,css,html,webpack,vue node angular以及面試資源等。
對web開發技術感興趣的同學,歡迎加入Q群:943129070,不管你是小白還是大牛我都歡迎,還有大牛整理的一套高效率學習路線和教程與您免費分享,同時每天更新視訊資料。
最後,祝大家早日學有所成,拿到滿意offer,快速升職加薪,走上人生巔峰。