阅读 772

简述Vue和React开发体验的异同(状态层)

前言

接上篇文章,本文我们来聊聊对于状态这块,Vue和React有何异同。

state vs data

先说关键词命名,在Vue中,通过声明data来定义内部变量,data翻译为中文是数据的意思,这也符合Vue的整体设计,响应式数据,一个数据的变化,会引发一系列的关联动作。而在React中,则变成了关键词state,翻译过来为状态的含义,通过状态驱动视图的更新。

在Vue中,数据是响应式的,这个响应式包含两方面:

  • JS内存中的变量值发生变化,通知DOM进行绘制
  • DOM中元素内容发生变化,通知JS内存中的变量值改变

在实现上,Vue利用JS的API,实现了点运算符的重载,利用如v-model等显式声明,通过对DOM元素的监听,及时反馈至内存变量的更新。Vue的这种做法其实是有一定的性能损耗的,但是带来是开发者的低门槛、高效率。

React在状态更新这块采用的是显式声明(setState),状态更新后,调用render函数,通知DOM进行绘制,数据流的传递是单向的(当然想实现Vue中的反向更新也是简单的,只是官方并不提倡)

我们来看个简单的例子,实现input组件输入值的绑定:

Vue:
<template>
    <input type="text" v-model="value" />
</template>
<script>
    export default {
        data() {
            return {
                value: ""
            }
        }
    }
</script>

React:
function render() {
    const [value, setValue] = useState("");
    return (
       <input type="text" onChange={(e) => setValue(e.target.value)} />
    )
}
复制代码

Vue可以通过v-model这个指令把数据进行了响应式关联,可以减少写监听回调的一部分代码,但事实上,Vue也可以写成这样:

Vue:
<template>
    <input type="text" :value="value" @input="setValue">
</template>
<script>
    export default {
        data() {
            return {
                value: ""
            }
        },
        methods: {
            setValue(e) {
                this.value = e.target.value;
            }
        }
    }
</script>
复制代码

虽然真实开发中大家不会这么写,谁叫Vue已经提供了现成的指令了呢。

在Vue中,有一个比较好用的功能:computed,可以监听依赖项的改变而进行时时计算,比如我们想实现第三个输入框为前两个输入框的值,我们会这么写:

Vue:
<template>
    <input type="text" v-model="a" /> +
    <input type="text" v-model="b" /> =
    <input type="text" v-model="sum" />
</template>
<script>
    export default {
        data() {
            return {
                a: "",
                b: "",
            }
        },
        computed: {
            sum() {
                return this.a + this.b
            }
        }
    }
</script>
复制代码

computed的依赖收集

写到这里的时候,我产生了一个好奇的点,sum函数的调用时机如何确定,直观反应是需要收集该函数的依赖项,在React中,直接会要求开发者传入一个deps数组,只要每次比较数组的引用地址就可以确定,而在Vue中并没有要求开发者声明。常见的依赖收集有以下两种方式

  • 静态词法分析
  • 执行函数后获取

静态分析会比较消耗CPU性能,举个例子:

computed: {
    sum() {
        const a = "x";
        const b = a;
        const c = b;
        return this[c] * 2
    }
}
复制代码

很明显,这个函数的依赖是x变量,但是需要推到x变量需要依次推导 c => b => a => "x",如果里面涉及到循环引用还会更加复杂,显然Vue不会走这条路。那么执行代码呢,这条路貌似是可行的,因为在Vue中数据的读取操作可以被监听到,那么思路可以是:执行一次sum函数,完毕后取所有被调用get的变量,组成依赖项,查阅相关资料后,证明Vue也是这么做的。

如果代码写成这样的话:

computed: {
    sum() {
        if (Math.random() < 0.5) {
            return;
        }
        return this.a + this.b
    }
}
复制代码

这种情况下,有一半的概率导致数据无法及时更新,因为是首次执行的时候确定依赖项的。如果想在React实现这个需求,可以自己封装自定义hooks,可以模拟实现computed的功能。

逻辑复用

再来谈谈逻辑的复用性,在React还没出Hooks之前,和Vue差不多,UI描述和逻辑是混在一起的,想抽也抽不出来。所以在平时开发中大部分场景都是复制代码的形式来做的,能做的一些事情也比较有限,比如抽工具库、精心设计一个组件的规格。在React Hooks出现后,这件事发生了一些变化,UI描述和逻辑天然被拆开了,逻辑的复用也变得顺理成章了。

总结

  • 从数据驱动UI上来说,React和Vue没有什么本质上的区别,状态改变通知UI层重绘,只是Vue会比较隐晦,React则通过setState显式调用

  • Vue面向开发者做了很多“小工具”,来帮助开发者提高生产效率

  • 从开发体验上来说,还是仁者见仁智者见智了,毕竟大家的编码习惯都受着很多因素的影响

文章分类
前端