在工做中应用 Vue 以后,我对它有了至关深入的理解。 不过,俗话说「外国的月亮比较圆」,我好奇「外国的」 React 是怎么样的。javascript
我阅读了 React 文档并观看了一些教程视频,虽然它们很棒,但我真正想知道的是 React 与 Vue 的不一样之处。 这里的“不一样”,并非指它们是否具备虚拟 DOM 或者它们如何渲染页面。 我但愿有人能向我解释代码并告诉我其中发生了什么! 我想找一篇能解释这些差别的文章,以便 Vue 或 React(或整个 Web 开发)的新手能够更好地理解二者之间的差别。vue
但我没有找到任何解决这个问题的方法。 因此我意识到我必须本身动手,来发现类似/不一样之处。 在作的时候,我想我会记录整个过程,以便产出一篇关于此的文章。java
我决定尝试构建一个至关标准的 To Do 应用,它容许用户添加和删除列表中的项目。 这两个应用都是使用默认的 CLI 构建的(React 的 create-react-app 和 Vue 的 vue-cli)。 BTW,CLI 表明命令行界面。react
好吧,这个介绍已经超出了个人预期。 咱们先快速看看两个应用的界面:git
两个应用的 CSS 代码彻底相同,不过它们所在的位置有差异。 考虑到这一点,咱们接下来看看两个应用的文件结构:github
你会发现它们的结构几乎彻底相同。 惟一的区别是 React 应用有三个 CSS 文件,而 Vue 应用一个都没有。 这样作的缘由是,在 create-react-app 中,React 组件会有一个附带文件来控制其样式,而 Vue CLI 采用全包方法,具体样式在实际组件文件中声明。vue-cli
最终,他们都达到了一样的目的,对于不能在 React 或 Vue 中以不一样的方式构建 CSS 的问题,目前还真没什么办法。这真的取决于我的偏好 - 你会听到开发者社区关于如何构建 CSS 的大量讨论。 如今,咱们将遵循两个 CLI 中列出的结构。数组
但在咱们进一步讨论以前,咱们先快速看一下典型的 Vue 和 React 组件的是什么样的:app
扯远了。如今让咱们开始研究细节中的细节!框架
如何变异数据?
首先咱们要搞清楚什么是“变异数据”? 听起来有点科技感是吧? 它基本上只是意味着改变咱们存储的数据。 所以,若是咱们想将一我的的名字取值从 John 改成 Mark,咱们就是在“变异数据”。 这就是 React 和 Vue 之间的关键区别所在。 虽然 Vue 本质上建立了一个 data 对象,对象数据能够自由更新,而 React 建立了一个 state 对象,要实现更新必须多作一点工做。 如今 React 有充分的理由实现额外的 legwork,咱们稍微深刻一下。 但首先,让咱们看看 Vue 中的 data 对象和 React 中的 state 对象:
Vue data 对象在左侧。 React state 对象在右侧。
你能够看到咱们已经将相同的数据传递到二者中,只是标记不一样。 所以,将初始数据传递到咱们的组件很是很是类似。 但正如上文提到的,咱们对这些数据的修改方式在两个框架之间有所不一样。
假设咱们有一个name 为 'Sunil'的数据元素 。
在 Vue 中,咱们经过调用 this.name 来引用它。 咱们也能够调用 this.name ='John' 来更新它。 这会把个人名字改为 John。 我不肯定被称为John的感受如何,但嘿嘿,它就是发生了!
在 React 中,咱们经过调用 this.state.name 来引用同一份数据。 如今关键的区别在于咱们不能简单地写 this.state.name='John',由于 React 作了限制来防止这种简单、无忧无虑的 mutate。 因此在 React 中,咱们会使用 this.setState({name:'John'}) 的方式来编写。
虽然这基本上与咱们在 Vue 中实现的相同,可是额外的写入是由于 Vue 基本上在每次更新数据时都会合并本身的 setState 版本。 简而言之,React 须要 setState 并传入须要更新的内部数据,而 Vue 假设当你把更新的值传入 data 对象时,你是想更新它的。那么为何 React 不肯意这样作,为何还须要 setState 呢? 让 Revanth Kumar 来为咱们解释下:
“这是由于 React 但愿在状态发生变化时从新运行某些生命周期钩子,[如] componentWillReceiveProps,shouldComponentUpdate,componentWillUpdate,render,componentDidUpdate。 当你调用 setState 函数时,它会知道状态已经改变了。 若是你直接改变状态,React 将须要作更多工做来跟踪更改以及运行生命周期钩子等等。因此为了简单起见,React 使用 setState。“
如今咱们已经完成了 mutations,让咱们研究下如何在咱们的 To Do 应用中添加新项目,以深刻了解细节。
如何建立新的 To Do 项
React 是如何作的?
在 React 中,咱们的输入字段有一个叫 value 的属性。 这个值经过使用几个函数自动更新,这些函数结合起来会建立一个很是相似于双向绑定的东西(若是你之前从未据说过这个,你能够看看以后的 How did Vue do that 部分)。 咱们经过在 input 上添加一个 onChange事件监听器来建立这种形式的双向绑定。 咱们简单看一下 input 字段,以便你了解发生了什么:
只要 input 框的值发生更改,handleInput 函数就会运行。 它会将 input 框中的任意输入更新到 state 中的 todo。 这个函数看起来像这样:
如今,只要用户按下页面上的 + 按钮添加新 item,createNewToDoItem 函数就会运行 this.setState 并向其传递一个函数。 这个函数接收两个参数,第一个是来自状态对象的 list数组,第二个是 todo(由 handleInput 函数更新)。 该函数返回一个新对象,该对象包含以前的整个 list,而后在其末尾添加 todo。 整个列表是经过使用扩展运算符添加的(若是你以前没有看过这个 ES6 语法,Google 一下吧)。
最后,咱们将 todo 设置为空字符串,它会自动更新 input 框中的值。
在 Vue 中,咱们的 input 框有一个名为 v-model 的指令。 它容许咱们作一些称为双向绑定的事情。 咱们先看看 input 框,而后解释下发生了什么:
V-Model 将此字段的输入绑定到名为 toDoItem 的 data 对象中的 key。 当页面加载时,toDoItem 会被置为空字符串,如 todo: ''。 若是 todo 非空,例如 todo: ‘add some text here’,咱们的 input 框将加载‘add some text’。 不管如何,回到将其做为空字符串,咱们在输入字段中输入的任何文本都将绑定到 todo 的值。 这正是双向绑定(input 能够更新 data 对象,data 对象能够更新 input)。
回到前面的 createNewToDoItem() 代码块,咱们看到咱们将 todo 的内容 push 到 list 数组中,而后将 todo 更新为空字符串。
如何删除列表数据?
虽然 deleteItem 函数位于 ToDo.js 内部,但我能够直接在 ToDoItem.js 中引用它,首先将deleteItem() 函数做为 <ToDoItem /> 上的 prop 传递:
首先将该功能传递给子组件,使其能够被访问。 你能够看到咱们也绑定了 this 以及传递了 key 参数,由于 key 是函数将用于区分 ToDoItem 在单击时尝试删除的内容。 而后,在ToDoItem 组件内部,咱们执行如下操做:
我须要作的就是引用一个位于父组件内部的函数来引用 this.props.deleteItem。
Vue 是如何作的?
Vue 的方法稍有不一样。 咱们基本上要作三件事:
首先,在咱们想要调用函数的元素上:
而后咱们必须建立一个 emit 函数做为子组件内部的方法(在本例中为 ToDoItem.vue),以下所示:
除此以外,你会发现咱们在 ToDo.vue 中引入 ToDoItem.vue 时实际引用了一个函数:
这就是所谓的自定义事件监听器。 它会监放任何使用 'delete' 字符串触发 emit 的操做。 若是监听到,它会触发一个名为 onDeleteItem 的函数。 此函数位于 ToDo.vue 内部,而不是ToDoItem.vue。 如前所述,此函数只是过滤 data 对象内的 todo 数组,以删除被点击的 item。
这里也值得注意的是,在 Vue 示例中,我能够简单地在 @click 监听器中编写 $emit 部分,以下所示:
这会将步数从 3 减小到 2,这仅仅取决于我的偏好。
简而言之,React 中的子组件能够经过 this.props 访问父组件方法(假设你正在传递 props,这是至关标准的作法,你会在其余 React 示例中遇到许屡次),而在 Vue 中, 你必须从子组件中触发由父组件全部的事件。
如何传递事件监听器?
React:
简单事件(如点击事件)的事件监听器是直截了当的。 如下是咱们如何为建立新 ToDo item 的按钮建立 click 事件的示例:
这里很是简单,就像咱们使用原生 JS 处理内联 onClick 同样。 正如 Vue 部分所述,在按下回车按钮的状况下设置事件监听器就须要花点时间了。 咱们能够在 input 标签中处理一下 onKeyPress 事件,以下:
只要它识别出按下了 '回车(Enter)' 键,这个函数就会触发 createNewToDoItem 函数,以下所示:
在 Vue,它是超级直接的。 咱们只使用 @ 符号,而后使用咱们想要事件监听器的类型。 例如,要添加 click 事件侦听器,咱们能够这样:
注意:@click 其实是 v-on: click 的简写。 Vue 事件监听器的一个很酷的事情是,在它以后咱们能够链式调用许多方法,例如 .once,它能够防止事件监听器被屡次触发。 在编写用于处理键击的特定事件侦听器时,还有一些快捷方式。 我发如今按下回车按钮的状况下,在 React 中建立一个事件监听器须要花费很长时间来建立新的 ToDo item。而在Vue,我可以简单地这样写:
如何向子组件传递数据?
React:
在 react 中,咱们将 props 传递到子组件。 如:
在这里,咱们看到两个传递给 ToDoItem 组件的 props。 从如今开始,咱们就能够经过 this.props 在子组件中引用它们了。 所以,要访问 item.todo prop,咱们只需调用 this.props.item。
在 Vue 中,咱们将 props 传递到子组件。 如:
完成后,咱们将它们传递给子组件中的 props 数组,如 props: ['todo']。 而后能够经过他们的名字在子组件中引用它们 - 因此在咱们的例子中,'todo'。
如何将数据发送回父组件?
React:
咱们首先将函数传递给子组件,方法是在咱们调用子组件的位置将其引用为 prop。 而后,咱们经过引用 this.props.whateverTheFunctionIsCalled,经过任何方式(例如 onClick)添加对子函数的调用。 而后,这将触发位于父组件中的函数。 咱们能够在“如何从列表中删除”一节中看到整个过程的示例。
在咱们的子组件中,咱们只需编写一个函数,将一个值发送回父函数。 在咱们的父组件中,咱们编写一个函数来侦听什么时候 emit 该值,而后能够触发函数调用。 咱们能够在“如何从列表中删除”一节中看到整个过程的示例。
大功告成!
咱们研究了如何添加,删除和更改数据,将数据以表单 props 形式从父组件传递到子组件,以及以事件侦听器的形式将数据从子组件发送到父组件。 固然,在 React 和 Vue 之间存在许多其余的小差异,但但愿本文的内容能做为理解两个框架如何处理东西的基础。
Vue ToDo: https://github.com/sunil-sandhu/vue-todo
React ToDo: 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