硬核干货——《中小企业 AI 实战指南》免费下载! 了解详情
写点什么

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

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

关注

评论

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

区块链下乡

CECBC

区块链

面试拜佛保过?圈内罕见阿里面试官手册,2021最强面试笔记非它莫属

Java 程序员 架构 面试

产品经理能力不够,产品方法论来凑

lenka

3月日更

Python API 邮件发送测试

HoneyMoose

领跑行业!浪潮云斩获“2021云管和云网大会”多项殊荣

云计算 云原生

时间复杂度总结

我是程序员小贱

3月日更

开启Python学习之旅,分享学习路上的神器集合!

王小王-123

Python 学习 资源分享 工具分享

区块链圈频现百万元年薪招聘 现金+股票仍难觅良才

CECBC

区块链人才

中国最可靠的 Go 模块代理

happlyfox

学习 3月日更

moviepy音视频剪辑:moviepy中的剪辑基类Clip的属性和方法详解

老猿Python

Three.js杂记(十一)—— 精灵与粒子(绘制中国地图)

空城机

大前端 WebGL 3D渲染 3D可视化 three.js

2021春招Java后端开发面试总结:25个技术专题(最全面试攻略)

比伯

Java 编程 架构 面试 程序人生

前端开发:数据处理方法分享(其一)

三掌柜

vue.js 大前端 3月日更

开源的 Switch 模拟器——GitHub 热点速览 v.21.12

HelloGitHub

GitHub 开源

LiteOS内核源码分析:位操作模块

华为云开发者联盟

LiteOS 源代码 位操作 bit Huawei LiteOS

moviepy音视频剪辑:视频剪辑基类VideoClip的属性及方法详解

老猿Python

Python 编程语言 音视频 Moviepy

去了解一下区块链

空城机

区块链 笔记 区块链发展

《Redis 核心技术与实战》学习笔记 07

escray

redis 学习 极客时间 3月日更 Redis 核心技术与实战

Python-计算机视觉-OpenCV-video

Aldeo

Python OpenCV Video

纸币会消失吗:数字货币如何走进我们生活

CECBC

货币

uni-app跨端开发H5、小程序、IOS、Android(七):uni-app渲染

程序员潘Sir

html5 uni-app 大前端 3月日更

如何减少加班导致的离职?

石云升

项目管理 28天写作 职场经验 管理经验 3月日更

「产品经理训练营」作业 06:用户路径地图与漏斗模型

狷介

产品经理训练营

Wireshark数据包分析学习笔记Day19

穿过生命散发芬芳

Wireshark 数据包分析 3月日更

(Day30) 谁来驱动变革

mtfelix

28天写作 bewriting

教你如何用霍夫变换完成扭曲车牌识别

程序媛观澜

机器学习 图像识别

域名和服务器的购买和配置

空城机

阿里云 轻量级服务器 云翼计划

渣硕试水字节跳动,本以为简历都过不了,123+HR面直接拿到意向书

Java 程序员 架构 面试

Kubernetes认证管理员

云原生

Kubernetes 云原生 k8s CKA

【Axure9百例NO.45】中继器的不同场景下的样式处理

zhuchuanming

原型设计 Axure 交互原型

一个有情怀的PPT模板下载网站

happlyfox

学习 3月日更 工具分享

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