写点什么

我用 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:553575
用户头像

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

关注

评论

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

如期而至-用户购买时间预测(下)

索信达控股

模型 购买预测 购买行为 精准营销

DataPipeline与TiDB推出异构数据实时同步解决方案,共筑安全可信基础设施

DataPipeline数见科技

数据库 大数据 中间件 数据融合 数据管理

这套神奇的个人信息管理系统,带我一步步进入互联网大厂

博文视点Broadview

恒拓高科WorkPlus助力南兴装备移动数字化升级

WorkPlus

让工具成为双手的延伸

Zilliz

数据库 书籍推荐 程序 书籍

大厂面试算法题之数组

程序员学长

数据结构 算法 大厂面试

从 0-15 套存储集群,YRCloudFile 助力 AI 训练效率“超线性增长”

焱融科技

人工智能 云计算 分布式 高性能 文件存储

【LeetCode】最短补全词Java题解

Albert

算法 LeetCode 12月日更

从0到1教你如何使用 p5.js 绘制简单的动画

海拥(haiyong.site)

大前端 动画 28天写作 签约计划第二季 12月日更

进程崩溃/应用卡死,故障频频怎么办?|HDC2021技术分论坛

HarmonyOS开发者

HarmonyOS

数据库事务的三个元问题

Zilliz

数据库 数据库设计

为啥你写的代码老有大串的if/else?

华为云开发者联盟

代码 if for循环 else

MySQL性能测试之select&update【FunTester框架】

FunTester

MySQL 性能测试 测试框架 FunTester FunTester框架

Python enumerate():使用计数器简化循环

华为云开发者联盟

Python 变量 计数器 循环 enumerate

浅谈服务网关和联邦云

星环科技

联邦云 服务网关

Linux运维,Linux系统学习

侠盗安全

Linux linux运维 运维工程师 云计算架构师

Java 程序员必须掌握的 10 款开源工具

编程江湖

java编程 java开发工具

双 11 大促会场开发提效解析:前端智能化落地实践

凹凸实验室

机器学习 大前端 智能代码

前端性能优化 24 条建议

编程江湖

大前端

一文读懂 Apache Pulsar

晓双

云原生 Apache Pulsar 消息系统 数据流 Apache Pulsar 社区

高并发多方案秒杀架构

MetaThoughts

前端开发:npm run serve和npm run dev的区别

三掌柜

28天写作 21天挑战 12月日更

安全办公 |企业级IM即时通讯私有云解决方案

WorkPlus

科创人·StreamNative翟佳:开源模式价值为王,基础软件的未来在国内社区

科创人

Java开发之环境搭建基础教程

@零度

Java 开发环境搭建

Android C++系列:Linux网络(三)协议格式

轻口味

c++ android 28天写作 12月日更

Go语言学习查缺补漏ing Day7

恒生LIGHT云社区

golang Go 语言 编程语言‘

全链路数据血缘在满帮的实践

华为云开发者联盟

sql 图数据库 图计算 全链路数据血缘 数据血缘

【干货分享】研效优化实践:AI算法助力深层BUG挖掘

WeTest

Flutter 自定义 ACEFoldTextView 折叠文本

阿策小和尚

28天写作 0 基础学习 Flutter 内容合集 签约计划第二季 12月日更

CANN5.0黑科技解密 | 高并发图片视频处理,为出行保驾,为生活添彩!

华为云开发者联盟

高并发 图像预处理 CANN 昇腾 异构计算

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