对比React和Vue中创建应用的区别

开发 前端
几年前,我决定尝试在React和Vue中构建一个相当标准的To Do App。 这两个应用程序都是使用默认的CLI构建的(React的create-react-app和Vue的vue-cli)。

 React vs Vue:现在有了React Hooks Vue 3 Composition API!

React vs Vue:传奇继续

几年前,我决定尝试在React和Vue中构建一个相当标准的To Do App。 这两个应用程序都是使用默认的CLI构建的(React的create-react-app和Vue的vue-cli)。 我的目的是编写无偏见的内容,并提供有关如何使用这两种技术执行某些任务的概述。

当React Hooks发布时,我在原始文章后面加上了" 2019 Edition",该版本将Class Components的使用替换为Functional Hooks。 随着Vue第3版及其成分API的发布,现在是时候以" 2020版"再次更新本文了。

让我们快速看一下这两个应用的外观:

对比React和Vue中创建应用的区别

两个应用程序的CSS代码完全相同,但是它们的位置有所不同。 考虑到这一点,接下来让我们看一下两个应用程序的文件结构:

对比React和Vue中创建应用的区别

最终,他们俩都能达成同一目标,而且无话可说,您无法继续在React或Vue中以不同的方式构造文件。 这实际上取决于个人喜好。 您将听到来自开发人员社区的大量讨论,关于CSS的结构,尤其是关于React的结构,因为存在许多CSS-in-JS解决方案,例如样式组件和情感。 顺便说一句,CSS-in-JS的字面意义就是这样。 尽管这些功能很有用,但到目前为止,我们将仅遵循两个CLI中列出的结构。

但是在进一步介绍之前,让我们快速看一下典型的Vue和React组件的外观:

典型的React文件:

对比React和Vue中创建应用的区别

典型的Vue文件:

对比React和Vue中创建应用的区别

既然已经解决了,让我们深入了解细节吧!

我们如何突变数据?

但是首先,"突变数据"甚至意味着什么? 听起来有点技术性吗? 基本上,这只是意味着更改我们已存储的数据。 因此,如果我们想将一个人的名字的值从John更改为Mark,我们将"对数据进行突变"。 因此,这就是React和Vue之间的关键区别所在。 虽然Vue本质上创建了一个数据对象,可以在其中自由更新数据,但是React通过状态钩子来处理它。

让我们看一下下面两个图片中的设置,然后我们将说明发生了什么:

React状态:

对比React和Vue中创建应用的区别

Vue状态:

对比React和Vue中创建应用的区别

因此,您可以看到我们已经将相同的数据传递给了两者,但是结构有些不同。

使用React或至少从2019年开始,我们通常会通过一系列的Hooks处理状态。 如果您以前没有看过这种概念,那么一开始它们可能看起来有些奇怪。 基本上,它的工作方式如下:

假设我们要创建待办事项列表。 我们可能需要创建一个名为list的变量,它可能需要一个由字符串或对象组成的数组(例如,如果我们想给每个待办事项字符串一个ID和其他一些东西,我们可以通过编写const [ list,setList] = useState([])。这里我们使用React称为Hook的钩子-称为useState。这基本上使我们可以在组件中保留局部状态。

另外,您可能已经注意到我们在useState()内部传入了一个空数组[]。 我们放在其中的是我们希望列表最初设置的内容,在我们的例子中,我们希望是一个空数组。 但是,从上图可以看到,我们在数组内部传入了一些数据,这些数据最终是list的初始化数据。 想知道setList是做什么的? 稍后将对此进行更多说明!

在Vue中,通常会将组件的所有可变数据放置在setup()函数内,该函数返回一个对象,其中包含要公开的数据和函数(这基本上意味着您希望能够使用的东西) 在您的应用中使用)。 您会注意到,应用程序中的每个状态数据(也就是我们希望能够进行突变的数据)都包装在ref()函数内部。 此ref()函数是我们从Vue导入的,可让我们的应用程序在任何更改或更新这些数据时都可以更新。 简而言之,如果您想在Vue中创建可变数据,请为ref()函数分配一个变量,并将任何默认数据放入其中。

那么我们如何在我们的应用程序中引用可变数据呢?

好吧,假设我们有一些名为name的数据被分配了Sunil的值。

在React中,由于我们使用useState()创建了较小的状态,因此很可能已经按照const [name,setName] = useState('Sunil')的方式创建了一些东西。 在我们的应用程序中,我们将通过简单地调用name来引用相同的数据。 现在,这里的主要区别在于我们不能简单地写上name ='John',因为React有适当的限制来防止这种简单,随意的突变产生。 因此,在React中,我们将编写setName('John')。 这是setName位起作用的地方。 基本上,在const [name,setName] = useState('Sunil')中,它将创建两个变量,一个变量将成为const name ='Sunil',而第二个const setName被分配了一个函数,该函数可以使用新的名称重新创建名称。 值。

在Vue中,它位于setup()函数内部,并且被称为const name = ref('Sunil')。 在我们的应用程序中,我们将通过调用name.value来引用它。 使用Vue,如果我们要使用在ref()函数内部创建的值,我们将在变量上寻找.value而不是简单地调用该变量。 换句话说,如果我们想要一个持有状态的变量的值,我们将寻找name.value而不是name。 如果要更新name的值,可以通过更新name.value来完成。 例如,假设我想将我的名字从Sunil更改为John。 我可以通过写name.value =" John"来做到这一点。 我不确定自己被称为约翰的感觉,但是嘿,事情发生了!

实际上,React和Vue在这里做着同样的事情,即创建可以更新的数据。 每当ref()函数内部包装的一条数据被更新时,Vue本质上都会默认结合其自己的name和setName版本。 React要求您使用内部值调用setName()来更新状态,如果您试图更新数据对象内部的值,Vue会假设您要这样做。 那么,为什么React还要从函数中分离值呢?为什么还要使用useState()呢? 本质上,每当状态改变时,React都希望能够重新运行某些生命周期挂钩。 在我们的示例中,如果调用setName(),React将知道某些状态已更改,因此可以运行这些生命周期挂钩。 如果直接改变状态,React将不得不做更多的工作来跟踪更改以及要运行的生命周期挂钩等。

现在,我们已经有了一些变通的方式,让我们通过研究如何将新项目添加到两个"待办事项"中来了解更多细节。

我们如何创建新的待办事项?

React:

  1. const createNewToDoItem = () => { 
  2.  
  3. const newId = generateId(); 
  4.  
  5. const newToDo = { id: newId, text: toDo }; 
  6.  
  7. setList([...list, newToDo]); 
  8.  
  9. setToDo(""); 
  10.  
  11. }; 

React是如何做的?

在React中,我们的输入字段具有一个名为value的属性。 每次通过onChange事件侦听器更改其值时,都会自动更新此值。 JSX(基本上是HTML的变体)如下所示:

  1. <input type="text" placeholder="I need to..." value={toDo} onChange={handleInput} onKeyPress={handleKeyPress}/> 

因此,每次更改值时,它都会更新状态。 handleInput函数如下所示:

  1. const handleInput = (e) => { 
  2.  
  3. setToDo(e.target.value); 
  4.  
  5. }; 

现在,每当用户按下页面上的+按钮添加新项目时,都会触发createNewToDoItem函数。 让我们再次看一下该功能,以了解发生了什么:

  1. const createNewToDoItem = () => { 
  2.  
  3. const newId = generateId(); 
  4.  
  5. const newToDo = { id: newId, text: toDo }; 
  6.  
  7. setList([...list, newToDo]); 
  8.  
  9. setToDo(""); 
  10.  
  11. }; 

本质上,newId函数基本上是在创建一个新ID,该ID将提供给我们的新toDo项。 newToDo变量是一个具有ID密钥的对象,该ID密钥具有newId的值。 它还具有一个文本键,该键将toDo中的值用作其值。 这与输入值更改时要更新的toDo相同。

然后,我们用完setList函数,并传入一个包含整个列表以及新创建的newToDo的数组。

如果… list位看起来很奇怪,则开头的三个点称为散布运算符,它基本上将列表中的所有值作为单独的项目传递,而不是简单地传递完整的 项目作为数组。 困惑? 如果是这样,我强烈建议您仔细阅读散布,因为它很棒!

无论如何,最后我们运行setToDo()并传入一个空字符串。 这样我们的输入值为空,可以输入新的toDos了。

Vue:

  1. function createNewToDoItem() { 
  2.  
  3. const newId = generateId(); 
  4.  
  5. list.value.push({ id: newId, text: todo.value }); 
  6.  
  7. todo.value = ""
  8.  

Vue是如何做的?

在Vue中,输入字段上有一个称为v-model的句柄。 这使我们能够执行称为双向绑定的操作。 让我们快速查看一下输入字段,然后说明发生了什么:

  1. <input type="text" placeholder="I need to..." v-model="todo" v-on:keyup.enter="createNewToDoItem"/> 

V-Model将该字段的输入与我们在setup()函数顶部创建的变量绑定在一起,然后将其作为键公开给我们返回的对象。 到目前为止,我们还没有介绍对象返回的内容,因此,对于您的信息,这是我们从ToDo.vue内部的setup()函数返回的内容:

  1. return { list, todo, showError, generateId, createNewToDoItem, onDeleteItem, displayError}; 

这里,list,todo和showError是我们的有状态值,而其他所有功能都是我们希望能够在应用程序其他位置调用的函数。 好的,从切线返回,当页面加载时,我们必须将todo设置为空字符串,例如:const todo = ref("")。 如果这里已经有一些数据,例如const todo = ref("在此处添加一些文本"):我们的输入字段将在输入字段内部已经添加一些文本的情况下加载。 无论如何,回到它作为一个空字符串,无论我们在输入字段中键入什么文本都必须绑定到todo.value。 这实际上是双向绑定-输入字段可以更新ref()值,而ref()值可以更新输入字段。

因此,回顾一下前面的createNewToDoItem()代码块,我们看到将todo.value的内容推入列表数组-通过将todo.value推入list.value-然后将todo.value更新为空字符串。

我们还使用了与React示例中相同的newId()函数。

我们如何从列表中删除?

React:

  1. const deleteItem = (id) => { 
  2.  
  3. setList(list.filter((item) => item.id !== id)); 
  4.  
  5. }; 

React是如何做的?

因此,尽管deleteItem()函数位于ToDo.js内,但我很容易能够通过首先将deleteItem()函数作为道具传递给ToDoItem.js来对其进行引用:

  1. <ToDoItem key={item.id} item={item} deleteItem={deleteItem} /> 

首先,该函数向下传递,以使儿童可以使用它。 然后,在ToDoItem组件内部,执行以下操作:

  1. <button className="ToDoItem-Delete" onClick={() => deleteItem(item.id)}> -</button> 

我要做的就是引用位于父组件内部的函数,就是引用props.deleteItem。 现在您可能已经注意到,在代码示例中,我们只是编写了deleteItem而不是props.deleteItem。 这是因为我们使用了一种称为解构的技术,该技术允许我们获取props对象的一部分并将其分配给变量。 因此,在我们的ToDoItem.js文件中,我们具有以下内容:

  1. const ToDoItem = (props) => { 
  2.  
  3. const { item, deleteItem } = props; 
  4.  

这为我们创建了两个变量,一个称为item,它被分配与props.item相同的值,另一个是deleteItem,它从props.deleteItem分配了值。 我们本可以通过仅使用props.item和props.deleteItem来避免整个破坏性的事情,但是我认为值得一提!

Vue:

  1. function onDeleteItem(id) { 
  2.  
  3. list.value = list.value.filter(item => item.id !== id); 
  4.  

Vue是如何做的?

Vue中需要一种略有不同的方法。 我们基本上必须在这里做三件事:

首先,在元素上我们要调用函数:

  1. <button class="ToDoItem-Delete" @click="deleteItem(item.id)"> -</button> 

然后,我们必须在子组件(在本例中为ToDoItem.vue)中创建一个emit函数作为方法,如下所示:

  1. function deleteItem(id) { emit("delete", id);} 

同时,您会注意到,当我们在ToDo.vue中添加ToDoItem.vue时,我们实际上引用了一个函数:

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

这就是所谓的自定义事件侦听器。 它会侦听在任何情况下使用字符串" delete"触发发射的情况。 如果听到此消息,它将触发一个名为onDeleteItem的函数。 此函数位于ToDo.vue内部,而不是ToDoItem.vue中。 如前所述,此函数仅从list.value数组中过滤ID。

在这里还值得注意的是,在Vue示例中,我可以简单地将$ emit部分写在@click监听器中,如下所示:

  1. <button class="ToDoItem-Delete" @click="emit("delete", item.id)"> -</button> 

这将使步数从3减少到2,而这完全取决于个人喜好。

简而言之,React中的子组件可以通过props来访问父函数(前提是您要传递props,这是相当标准的做法,在其他React示例中,您会遇到大量的工作),而在Vue中,您可以 从孩子发出事件,通常将其收集在父组件中。

我们如何传递事件监听器?

React:

简单事件(例如单击事件)的事件侦听器很简单。 这是我们如何为创建新的ToDo项目的按钮创建click事件的示例:

  1. <button className="ToDo-Add" onClick={createNewToDoItem}> +</button> 

在这里超级简单,几乎就像我们将如何使用香草JS处理嵌入式onClick。 如Vue部分所述,每当按下Enter键时,设置事件侦听器进行处理就花费了更长的时间。 这本质上需要由输入标签处理onKeyPress事件,例如:

  1. <input type="text" placeholder="I need to..." value={toDo} onChange={handleInput} onKeyPress={handleKeyPress}/> 

只要该函数识别出已按下" enter"键,便会触发createNewToDoItem函数,例如:

  1. const handleKeyPress = (e) => { 
  2.  
  3. if (e.key === "Enter") { 
  4.  
  5. createNewToDoItem(); 
  6.  
  7.  
  8. }; 

Vue:

在Vue中,它非常简单明了。 我们只使用@符号,然后使用我们想要做的事件监听器的类型。 因此,例如,要添加一个click事件监听器,我们可以编写以下代码:

  1. <button class="ToDo-Add" @click="createNewToDoItem"> +</button> 

注意:@click实际上是编写v-on:click的简写。 Vue事件侦听器很酷的事情是,您还可以绑定很多东西,例如.once,它可以防止事件侦听器被多次触发。 在编写用于处理按键的特定事件侦听器时,还有许多捷径。 我发现,每当按下Enter键时,在React中创建一个事件侦听器以创建新的ToDo项就花费了相当长的时间。 在Vue中,我能够简单地编写:

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

我们如何将数据传递给子组件?

反应:

在react中,我们将道具传递到子组件的创建位置。 如:

  1. <ToDoItem key={item.id} item={item} deleteItem={deleteItem} />; 

在这里,我们看到两个传递给ToDoItem组件的道具。 从现在开始,我们现在可以通过this.props在子组件中引用它们。 因此,要访问item.todo prop,我们只需调用props.item。 您可能已经注意到,还有一个关键道具(因此从技术上讲,我们实际上正在传递三个道具)。 这主要用于React的内部,因为它使在同一组件的多个版本之间进行更新和跟踪更改变得容易(我们在这里拥有它,因为每个todo是ToDoItem组件的一个副本)。 确保您的组件具有唯一键也很重要,否则React会在控制台中警告您。

Vue:

在Vue中,我们将道具传递到子组件的创建位置。 如:

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

完成此操作后,我们将它们传递到子组件的props数组中,如下所示:props:[" todo"]。 然后可以通过子组件的名字在子组件中引用这些名字,因此在我们的案例中是todo。 如果您不确定该prop键的放置位置,则这是整个导出默认对象在子组件中的外观:

  1. export default { 
  2.  
  3. name: "ToDoItem", props: ["item"], setup(props, { emit }) { 
  4.  
  5. function deleteItem(id) { 
  6.  
  7. emit("delete", id); 
  8.  
  9.  
  10. return { deleteItem, }; 
  11.  
  12. }, 
  13.  
  14. }; 

您可能已经注意到的一件事是,当在Vue中遍历数据时,我们实际上只是遍历了list而不是list.value。 尝试遍历list.value在这里行不通

我们如何将数据发送回父组件?

React:

首先,通过在调用子组件的地方将其作为道具引用,将函数传递给子组件。 然后,通过引用props.whateverTheFunctionIsCalled(如果使用了解构)或whatTheFunctionIsCalled,可以通过诸如onClick之类的任何方式在子元素上添加对函数的调用。 然后,这将触发位于父组件中的函数。 我们可以在"我们如何从列表中删除"部分中看到整个过程的示例。

Vue:

在子组件中,我们只编写了一个将值返回给父函数的函数。 在父组件中,我们编写了一个函数,该函数侦听何时发出该值,然后可以触发函数调用。 我们可以在"我们如何从列表中删除"部分中看到整个过程的示例。

我们终于得到它了!

我们已经研究了如何添加,删除和更改数据,以道具形式将数据从父级传递到子级,以及以事件侦听器的形式将数据从子级发送到父级。 当然,React和Vue之间还有许多其他的小差异和怪异之处,但是希望本文的内容有助于为理解这两个框架如何处理事物奠定基础。

如果您有兴趣分叉本文中使用的样式,并想制作自己的同等作品,请随时这样做!

Github链接到两个应用程序:

Vue待办事项:https://github.com/sunil-sandhu/vue-todo-2020

React待办事项:https://github.com/sunil-sandhu/react-todo-2020

最初发布于:sunilsandhu.com

 

责任编辑:张燕妮 来源: 今日头条
相关推荐

2021-04-26 18:48:48

微应用React

2020-09-02 11:43:24

开发技能代码

2018-07-30 08:41:48

VueReact区别

2020-09-14 15:57:53

Vue和React

2023-10-27 09:22:27

框架开发

2019-10-16 18:00:44

AngularVueReact

2021-06-26 06:29:14

Vue 2Vue 3开发

2018-08-24 08:35:07

前端JavaScript框架

2022-10-08 00:01:00

ssrvuereact

2024-01-16 08:05:53

2009-09-17 15:10:57

RefrenceEqu

2022-09-23 10:25:00

VueReact

2023-01-27 11:13:04

WebReactVue

2024-01-05 08:46:50

ReactVue

2021-03-04 22:31:02

Vue进阶函数

2012-12-24 09:55:24

Web应用云计算

2016-09-07 15:35:06

VueReact脚手架

2016-11-01 21:02:47

javascriptreact.jsreact-route

2023-09-11 07:36:35

2019-07-17 13:41:36

VueReactJSX
点赞
收藏

51CTO技术栈公众号