1个简单表单,4种编码风格(react、vue2、vue3写法对比)

发布于 2022年 01月 23日 21:08

Vue3版本发布了,不仅更改了底层对数据响应的实现方式,还推出了全新的组合式API (Composition API),带来了一种不一样的开发体验。

虽然有些人说组合式API是抄袭了React的Hooks。但在开源的世界中,没有什么所谓的抄不抄袭。只有谁更好用,能够解决更多的问题,开发体验更好。

从vue3的组合式api,再追溯到更早之前react的hooks,大家似乎都在朝着一个方向奔跑,这个方向或许将成为前端开发的一股新的浪潮,那这股浪潮是什么呢?它就是———函数式编程

在这里不发表过多对函数式编程的看法,只是作为一名小小的前端开发人员,在无力改变浪潮的前提下,只能顺势成为一个随波逐流的人儿。

一、简单的需求

本次的需求很简单,就是实现一个注册表单,用户输入用户名、手机号、密码、确认密码,点击注册打印出用户填入的信息即可。

下面就从使用不同的技术栈,来体验一下4种不同风格的编码吧。

二、使用vue2实现

// RegisterForm.vue

<template>
  <form class="reg-form" @submit.prevent="handleRegister">
    <div class="reg-form-item">
      <div>用户名:</div>
      <input
        type="text"
        :value="formData.userName"
        @change="handleUserChange('userName', $event)"
        placeholder="请输入用户名"
      />
    </div>
    <div class="reg-form-item">
      <div>手机号:</div>
      <input
        type="text"
        :value="formData.userPhone"
        @change="handleUserChange('userPhone', $event)"
        placeholder="请输入手机号"
      />
    </div>
    <div class="reg-form-item">
      <div>密码:</div>
      <input
        type="password"
        :value="formData.userPassword"
        @change="handleUserChange('passwordAgain', $event)"
        placeholder="请输入密码"
      />
    </div>
    <div class="reg-form-item">
      <div>确认密码:</div>
      <input
        type="password"
        :value="formData.passwordAgain"
        @change="handleUserChange('userPassword', $event)"
        placeholder="请输入确认密码"
      />
    </div>
    <div class="reg-btn">
      <button>确认注册</button>
    </div>
  </form>
</template>

<script>
export default {
  data() {
    return {
      formData: {
        userName: "",
        userPhone: "",
        userPassword: "",
        passwordAgain: "",
      },
    };
  },
  methods: {
    // 处理注册事件
    handleRegister() {
      console.log(
        `用户名:${this.formData.userName},手机号:${this.formData.userPhone},密码:${this.formData.userPassword}。`
      );
    },

    // 处理用户数据收集
    handleUserChange(type, e) {
      if (type in this.formData) {
        this.formData[type] = e.target.value;
      }
    },
  },
};
</script>

<style scoped>
.reg-form {
  width: 300px;
  padding: 20px;
  margin: 20px;
  border: 1px solid #ddd;
}
.reg-form-item {
  display: flex;
  padding: 5px 0;
}
.reg-form-item > div {
  width: 80px;
  line-height: 30px;
  text-align: right;
}
.reg-form-item > input {
  padding: 5px;
  width: 200px;
}
.reg-btn {
  text-align: center;
  margin-top: 20px;
}
.reg-btn button {
  width: 90%;
}
</style>


技术总结:

1、vue2采用三段式(template、script、style 标签)实现一个页面一个组件的开发体验。

2、使用v-bind(:value) 绑定数据,v-on绑定事件(@change)。

3、如果事件回调方法需要传参数,event会丢失,vue为我们提供了$event参数来获取event。

4、prevent是事件修饰符,可以阻止事件默认行为,等同event.preventDefault()

其实上例采用的是单向数据流的方式实现,vue其实是可以对表单作双向数据绑定,只需要使用v-model

<template>
  <form class="reg-form" @submit.prevent="handleRegister">
    <div class="reg-form-item">
      <div>用户名:</div>
      <input
        type="text"
        v-model="formData.userName"
        placeholder="请输入用户名"
      />
    </div>
    <div class="reg-form-item">
      <div>手机号:</div>
      <input
        type="text"
        v-model="formData.userPhone"
        placeholder="请输入手机号"
      />
    </div>
    <div class="reg-form-item">
      <div>密码:</div>
      <input
        type="password"
        v-model="formData.userPassword"
        placeholder="请输入密码"
      />
    </div>
    <div class="reg-form-item">
      <div>确认密码:</div>
      <input
        type="password"
        v-model="formData.passwordAgain"
        placeholder="请输入确认密码"
      />
    </div>
    <div class="reg-btn">
      <button>确认注册</button>
    </div>
  </form>
</template>

<script>
export default {
  data() {
    return {
      formData: {
        userName: "",
        userPhone: "",
        userPassword: "",
        passwordAgain: "",
      },
    };
  },
  methods: {
    // 处理注册事件
    handleRegister() {
      console.log(
        `用户名:${this.formData.userName},手机号:${this.formData.userPhone},密码:${this.formData.userPassword}。`
      );
    },
  },
};
</script>

<style scoped>
// ... 省略
</style>

三、使用vue3实现

<script setup>
// 组合式api
import { reactive } from "vue";

const formData = reactive({
  userName: "",
  userPhone: "",
  userPassword: "",
  passwordAgain: "",
});

// 处理注册事件
const handleRegister = () => {
  console.log(
    `用户名:${formData.userName},手机号:${formData.userPhone},密码:${formData.userPassword}。`
  );
};

</script>

<template>
  <form class="reg-form" @submit.prevent="handleRegister">
    <div class="reg-form-item">
      <div>用户名:</div>
      <input
        type="text"
        v-model="formData.userName"
        placeholder="请输入用户名"
      />
    </div>
    <div class="reg-form-item">
      <div>手机号:</div>
      <input
        type="text"
        v-model="formData.userPhone"
        placeholder="请输入手机号"
      />
    </div>
    <div class="reg-form-item">
      <div>密码:</div>
      <input
        type="password"
        v-model="formData.userPassword"
        placeholder="请输入密码"
      />
    </div>
    <div class="reg-form-item">
      <div>确认密码:</div>
      <input
        type="password"
        v-model="formData.passwordAgain"
        placeholder="请输入确认密码"
      />
    </div>
    <div class="reg-btn">
      <button>确认注册</button>
    </div>
  </form>
</template>

<style scoped>
// ... 省略
</style>

技术总结:

1、vue3延续了vue2三段式开发风格,新增了<script setup> 标签,可以便捷的编写setup函数,并且不需要return就可以直接使用变量。

2、reactive 函数可以定义一个对象类型的响应式数据。

注:关于vue3的知识点,可以参考《vue3开发使用体验,来一波技术性总结》

四、使用react类式组件实现

import React, { Component } from 'react'

export default class RegisterForm extends Component {
    state = {
        formData: {
            userName: "",
            userPhone: "",
            userPassword: "",
            passwordAgain: ""
        }
    }

    // 处理注册事件
    handleRegister = (e) => {
        e.preventDefault();
        const { formData: { userName, userPhone, userPassword } } = this.state;

        console.log(
            `用户名:${userName},手机号:${userPhone},密码:${userPassword}。`
        );
    }
    
    // 处理用户数据收集
    handleUserChange = (type) => {
        return (e) => {
            if (type in this.state.formData) {
                this.setState(({ formData }) => ({
                    formData: {
                        ...formData,
                        [type]: e.target.value
                    }
                }))
            }
        }
    }

    render() {
        const { formData: { userName, userPhone, userPassword, passwordAgain } } = this.state;

        return (
            <form className="reg-form" onSubmit={this.handleRegister} >
                <div className="reg-form-item">
                    <div>用户名:</div>
                    <input
                        type="text"
                        value={userName}
                        onChange={this.handleUserChange('userName')}
                        placeholder="请输入用户名"
                    />
                </div>
                <div className="reg-form-item">
                    <div>手机号:</div>
                    <input
                        type="text"
                        value={userPhone}
                        onChange={this.handleUserChange('userPhone')}
                        placeholder="请输入手机号"
                    />
                </div>
                <div className="reg-form-item">
                    <div>密码:</div>
                    <input
                        type="password"
                        value={userPassword}
                        onChange={this.handleUserChange('userPassword')}
                        placeholder="请输入密码"
                    />
                </div>
                <div className="reg-form-item">
                    <div>密码:</div>
                    <input
                        type="password"
                        value={passwordAgain}
                        onChange={this.handleUserChange('passwordAgain')}
                        placeholder="请输入确认密码"
                    />
                </div>
                <div className="reg-btn">
                    <button>确认注册</button>
                </div>
            </form>
        )
    }
}

技术总结:

1、编写react类式组件需要继承React.Component,HTML可以采用jsx语法写在render函数中进行渲染。

2、在表单项中使用state进行管理则会成为受控组件(value={userName})。

3、在react中处理事件的回调要特别注意this的指向,一般回调函数会使用箭头函数,就是为了解决这一问题(handleRegister = (e) => { ... })。

4、不能直接改state的值(this.state.userName = 'new name'),需要使用this.setState()函数进行处理。才能重新渲染新的dom。

5、react没有vue中的事件修饰符,需要自己处理原生事件冒泡事件默认行为等(e.preventDefault())。

6、处理事件的回调函数如果想要传参数,event会丢失,采用高阶函数柯里化函数可以解决这个问题。

handleUserChange = (type) => {
        // 这种函数中返回函数的方式称为高阶函数
        // fun(a)(b) 这种将接收多个参数变为接收单一参数,最终将参数统一处理并返回的编码方式称为柯里化函数。
        return (e) => {
            // ...
        }
    }

五、使用react函数式组件实现

import React, { useState } from 'react'

export default () => {
    const [formData, setFormData] = useState({ // 使用useState Hooks
        userName: '',
        userPhone: '',
        userPassword: '',
        passwordAgain: ''
    })
    const { userName, userPhone, userPassword } = formData;
    const handleRegister = (e) => {
        e.preventDefault();
        console.log(
            `用户名:${userName},手机号:${userPhone},密码:${userPassword}。`
        );
    }

    // 处理用户数据收集
    const handleUserChange = (type) => {
        return (e) => {
            if (type in formData) {
                setFormData({
                    ...formData,
                    [type]: e.target.value
                })
            }
        }
    }
    return (
        <form className="reg-form" onSubmit={handleRegister} >
            <div className="reg-form-item">
                <div>用户名:</div>
                <input
                    type="text"
                    value={userName}
                    onChange={handleUserChange('userName')}
                    placeholder="请输入用户名"
                />
            </div>
            <div className="reg-form-item">
                <div>手机号:</div>
                <input
                    type="text"
                    value={userPhone}
                    onChange={handleUserChange('userPhone')}
                    placeholder="请输入手机号"
                />
            </div>
            <div className="reg-form-item">
                <div>密码:</div>
                <input
                    type="password"
                    value={userPassword}
                    onChange={handleUserChange('userPassword')}
                    placeholder="请输入密码"
                />
            </div>
            <div className="reg-form-item">
                <div>密码:</div>
                <input
                    type="password"
                    value={passwordAgain}
                    onChange={this.handleUserChange('passwordAgain')}
                     placeholder="请输入确认密码"
                />
            </div>     
            <div className="reg-btn">
                <button>确认注册</button>
            </div>
        </form>
    )
}

技术总结:

1、Hooks可以让我们在函数组件中使用类式组件的状态、生命函数钩子等。

2、useState() 函数可以进行状态管理,除此之处还有useEffect()(管理生命周期)、useRef()(处理与DOM交互)等函数钩子。具体参照官网

六、总结

vue和react相关的知识体系是很庞大的,限于篇幅,就不展开讲了。只是想通过上述的简单例子,做一个抛砖引玉,让大家体验不同技术栈不同风格的开发方式。

至于vue和react孰优孰劣,我们没法简单的进行比较,毕竟它们所专注的点不同:

vue,许多API设计都是开箱即用的。我们只需要会用,就能快速轻松的实现功能。这跟框架的设计思想有关,vue是一种渐进式js框架,一步步从易到难,让你快速上手,传播的是一种易用的思想。

react,关注点在于构建易用的UI组件界面,提供一种更接近原生js的开发体验。这是一种优势,可以让组件的设计更加灵活,更加高效。

所以vue和react不是说哪个优哪个劣,对于前端生态来说,他俩都是优秀的框架和库。当然了,我们并不是在做选择题,二选一。做为一名合格的前端工程师来说,两者兼得才是最优解。

推荐文章