1.项目介绍
柠檬记账是一个移动端的本地记账项目,具备以下功能:
- 记账页面:金额、标签、备注、收入/支出类型切换
- 标签管理:添加、改名、删除标签
- 统计页面:数据显示、按天分组、收入/支出类型切换
- 持久化:页面关闭再打开,数据不会丢失(localStorage)
开发方式
- React版:TypeScript + 函数式组件 + 自定义hooks + React Router + styled-components
- Vue版: TypeScript + 单文件组件 + Vuex + Vue Router + Sass
2.Vue VS React 数据绑定
Vue 数据响应式
- vue会在初始化时收集所有的依赖(在
data选项内声明的数据),数据改变view自动跟着改变。 - 以项目中数字板(NumberPad)模块为例,模块的功能为:点击对于数字的
button触发按键事件,改变数字板中显示的金额output,我们可以直接改变this.output,Vue就会自动更新到视图层。
React useState
- react的话改变数据需要手动调用
setState方法。 - 我们还是以项目中数字板(NumberPad)模块为例:先调用
useState()函数,拿到output的值,并同时拿到setOutput方法。当数据需要改变时,我们需要手动调用setOutput方法,将新的值覆盖以前的值。
3.Vue VS React 组件化
Vue 单文件组件(类组件+装饰器)
Vue提供的文件扩展名为 .vue 的单文件组件,组件由 模板(HTML)+ 脚本(JS) + 样式(CSS)三兄弟构成。 同时基于Vue中TypeScript对于类组件的支持本次项目采用 类组件+装饰器 的方式构筑项目。 单文件组件结构如下:
<template>
<div>
<button class="btn" @click="onClick">Click!</button>
</div>
</template>
<script lang='ts'>
import Vue from 'vue'
import Component from 'vue-class-component'
// @Component 修饰符注明了此类为一个 Vue 组件
@Component({
// 所有的组件选项都可以放在这里
})
export default class MyComponent extends Vue {
// 初始数据可以直接声明为实例的 property(相当于data)
message: string = 'Hello!'
// 组件方法也可以直接声明为实例的方法(相当于method)
onClick (): void {
window.alert(this.message)
}
}
</script>
<style lang='scss' scoped >
.btn{
background:red;
}
</style>
React 函数组件
与Vue的三段式不同,在React里,万物皆是JS(TS),在.tsx文件内我们可以直接在ts里写html标签,函数组件也不例外。
import React from "react";
const MyComponent: React.FC = (props) => {
//属性
const message = 'Hello!'
//方法
const onClickHandler = () => {
window.alert(message)
}
//组件模板写在return内
return (
<div>
<button onClick={onClickHandler}>Click!</button>
</div>
)
}
export default MyComponent;
4.Vue VS React 外部数据Props
Props即Property(属性)的简称,通过它组件可以拿到外部数据(即HTML中的attribute)。
Vue Prop 装饰器
- 在传统的Vue对象组件中我们通常通过
props选项拿到父组件传入的数据。本次项目Class组件中我们采用**装饰器@Prop()**拿到外部数据。 - 1.首先在第三方库
vue-property-decorator引入Prop装饰器 - 2.利用装饰器语法
@Prop() readonly value!: number;声明变量名和数据类型。 - 3.最后通过
this.value拿到外部数据。
(PS:Vue会人性化地自动将HTML中 kebab-case (短横线分隔命名) 的属性名在JS中变成 camelCase (驼峰命名法) 的属性名)

React Props 对象
- React由于 “组件本身就是函数” ,所以外部数据理所当然的就是函数的参数了,这种简洁明了的编程思维是React中最具特色的风格。
- 1.React的数据存放在传入的参数
props对象中。在TypeScript的加持下我们可以通过泛型<>去声明props对象的数据类型。 - 2.再同过
props对象对象的属性拿到外部数据。
5.Vue VS React CSS支持
Vue 单文件组件的局部样式
- 由于Vue的单文件组件的格式是传统前端三段式,所以我们可以方便的在组件内部通过
<style></style>标签书写CSS(Vue支持SASS LESS Stylus等)。 - 同时Vue人性化的提供了
scoped属性:可以使得CSS内容仅仅作用在组件内部。
<!-- 通过scoped属性,可以使得CSS内容局部作用在组件内部 -->
<style lang="scss" scoped>
.numberPad {
.output {
/* ... */
}
.buttons {
/* ... */
}
}
}
</style>
React styled-components 样式标签
- 由于React组件本身是函数,没有办法将利用Style标签书写CSS,传统的做法是将CSS和组件分离,单独存放在一个.css文件中,再在
index.html文件引入。这么做首先造成了样式和组件的割裂。其次CSS没有局部作用域容易造成全局类名的污染。 - 为了解决上述问题,React中使用的
styled-components利用**模板字符串``**的一个特性,给予一个函数组件CSS样式并且使其拥有局部的类名作用域。由此我们可以在.JSX(.TSX)文件中愉悦的书写样式了。
6.Vue VS React 全局状态管理
- 全局状态管理即将组件的数据以及与数据相关的操作进行抽离与封装,即MVC模型中的Model层。本项目中Vue版本使用的是Vue官方的全局状态管理库Vuex,React版本则使用自定义hooks进行全局状态管理。
- 全局状态管理中最核心的两个概念即如何存储状态(数据)以及封装对状态的操作。
Vue Vuex
Vuex通过调用Vuex.storeAPI创建一个Store实例,其中最核心的两个选项是state和mutations
state:组件的状态,通过this.$store.state来获取状态对象。mutations:更改状态的方法,通过this.$store.commit("handler",payload)来修改状态。其中"handler"为方法名,载荷payload为传入的参数(只能传一个参数,所以一般都传入一个对象)。由于this.$store.commit("handler",payload)中的"handler"是字符串,就导致在调用这个mutation时没有代码提示,写错了TS也不会静态地检测出错误,这一点导致Vuex并没有想象中的那么好用。

React 自定义Hooks
- Hooks:'Hooks'的单词意思为“钩子”。
React Hooks 的意思是,组件尽量写成纯函数,如果需要外部功能和副作用,就用钩子把外部代码"钩"进来。如
useState、useEffect、useRef等。 - React 的自定义Hook 是一个函数,其名称以 “use” 开头,函数内部可以调用其他的Hook。
- 那么我们是如何使用自定义Hooks进行全局状态管理的呢?以项目中对记录records的状态管理
useRecords为例:- 1.创建一个useTags函数。
- 2.调用
useState函数声明状态records和对状态的修改useRecords。 - 3.调用
useEffect函数来进行初始化records以及监视变化。 - 4.在
useRecords内部定义方法createRecord并在其内部调用useRecords对状态进行修改。 - 5.将需要用的的状态
records和方法createRecord暴露export出去。
- 当我们要用到
records对应的状态和方法时我们只需要调用useRecords函数,并用结构语法获取状态和方法即可。const { records,createRecords } = useRecords();
7.Vue VS React 路由Router
vue-router
- vue-router是全局配置方式
- vue-router仅支持对象形式的配置
- vue-router任何路由组件都会被渲染到位置

react-router
- react-router是全局组件方式
- react-router支持对象形式和JSX语法的组件形式配置
- react-router子组件作为children被传入父组件,而根组件被渲染到位置

8.Vue VS React 总结
- 在使用Vue开发项目的时候,最大的感受就是方便。例如我们可以方便的通过指令
v-mode和修饰符.sync方便的实现双向绑定。通过v-for和v-if方便的进行对DOM的条件渲染。mixin混入复用的代码片段等等。同时Vue的作者常常在一些细节的地方给与开发者“小小的关怀”,列如之前讲的:短横线分隔命名——驼峰命名法的转换。 - 在使用React开发项目时,给我的最大感受是简洁。例如组件就是函数,外部数据props对象就是函数的参数,return返回的是需要渲染的DOM。在构筑React项目时我们不需要了解太多其他的知识(比如指令、选项什么的),大部分时间都是在使用原生JS的特性在编写项目。