NVIDIA 初创加速计划,免费加速您的创业启动 了解详情
写点什么

我用 Vue 和 React 构建了相同的应用程序,这是他们的差异

  • 2018-08-18
  • 本文字数:6534 字

    阅读完需:约 21 分钟

在工作中使用了 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

英文原文: https://medium.com/javascript-in-plain-english/i-created-the-exact-same-app-in-react-and-vue-here-are-the-differences-e9a1ae8077fd

感谢覃云对本文的审校。

2018-08-18 16:553443
用户头像

发布了 731 篇内容, 共 434.1 次阅读, 收获喜欢 1997 次。

关注

评论

发布
暂无评论
发现更多内容

[架构实战营]模块一作业:微信业务架构与学生管理系统

Geek_99eefd

架构实战营 「架构实战营」

官方线索 | HDC.Together华为开发者大会2021

穿过生命散发芬芳

1024我在现场

一文读懂公共部门、区块链和数字太空竞赛三者之间的角色关系

CECBC

百万大数据5期-BSM-01课作业

Clarke

通过几个小程式来快速学习Java基本语法 | Java

Regan Yue

Java 10月月更

深空时代来临,探日究竟有何魔力?

脑极体

022云原生之Kubernetes服务

穿过生命散发芬芳

云原生 10月月更

Android 音视频采集那些事

声网

音视频

在线字符串转列表工具

入门小站

工具

再次捕获云上在野容器攻击,TeamTNT黑产攻击方法揭秘

腾讯安全云鼎实验室

容器 云安全

【权限专栏】谁允许你访问了?

趣链科技

区块链 权限管理

把握数字经济发展趋势和规律 推动我国数字经济健康发展

CECBC

Map (映射) 实现

BlockQuant

map 哈希表 hash table

ORM

风翱

ORM 10月月更

ConcurrentHashMap JDK1.8 源码分析

黄敏

java

docker 系列:实践工具

yuexin_tech

数字货币交易所系统开发内容(案例)

JavaScript对象创建的 6 种模式

devpoint

工厂模式 原型链 构造函数 JavaScrip 10月月更

21. 合并两个有序链表(链表)

黄敏

揭秘!探访百度AI反诈第一线

白洞计划

【新】虚拟机深层系列「GC本质底层机制」SafePoint的深入分析和底层原理探究指南

洛神灬殇

JVM safepoint JVm虚拟机 10月月更

区块链的监管架构基本成型

CECBC

Prometheus 查询操作符(四) 示例合集

耳东@Erdong

Prometheus 10月月更

营销CRM软件(销售管理工具)让客户都成为回头客

低代码小观

营销 企业 企业管理 CRM 管理系统

知识计算:华为云要给AI时代开一扇门

脑极体

1万字长文高速你千万级并发架构下如何提高数据库存储性能

Java 编程 架构 面试 分布式

linux之curl命令

入门小站

Linux

音视频:播放器与H.265播放探索

程序员架构进阶

视频流 播放器 H.265 10月月更

第一周作业

沐风

如何应对职场焦虑?

石云升

焦虑 职场经验 10月月更

Go 中如何写注释

baiyutang

golang 10月月更

我用Vue和React构建了相同的应用程序,这是他们的差异_语言 & 开发_Sunil Sandhu_InfoQ精选文章