天天看点

受控组件分析(React & Vue)

受控组件:组件的值的更新依赖状态值来更新,一定程度上,组件值是恒等于状态值的。

非受控组件:组件值更新由组件自己维护,需要以操作 DOM 的方式来获取组件的值。

在 React 中表现如下:

input 中设置了 value,则为受控组件,onChange 事件设置如下:

不设置 onChange 事件,输入内容,input 并不会渲染出来。因为 input 值的更新,是依赖状态值来更新的(渲染),所以 input 不会渲染输入的内容。

设置 onChange 事件,但 onChange 事件中不更新状态值/设置状态值为某个值,并不是用户输入的值的时候,input 也不会正确地渲染。理由同上。

设置 onChange 事件,且设置状态值为输入值(e.target.value)时,input 将正确渲染内容。

如不设置 value,则 input 组件属于非受控组件。用户输入的内容,将由 input 组件维护(更新渲染行为不受到程序控制)。

在 Vue 中,有两种常用的设置方式,一种怪异的设置方式,不同设置,表现不同。

表现正常,效果如 React 的设置 value 和正确设置 onChange 事件的情况一致。

input 的表现(渲染)跟值分离了。this.value 还是设置为 123 ,但 input 却正确地响应了用户的输入。

这种情况,日常开发是不会这样发生的。这里的 input 表现是符合逻辑的。如设置 this.value=123 则 input 的渲染不会渲染用户输入的内容,而是 123。如不设置 this.value 的值,则 input 表现跟 v-model 的情况一致。

首先,不会去分析 v-model & @input 这种怪异的用法的。

v-model 的表现表明组件是可控的。所以分析一下 :value & @input 表现跟状态不一的情况。

为什么这种情况下,表现和状态不一呢?

我们知道 Vue 是采取依赖收集的这种方式去发现更新情况,然后更新视图的。当 input 输入时,会触发 @input 事件,但 @input 事件中,对 value 的处理是赋一个常数值(this.value = 123)。此时,this.value 值相同,并没有触发更新,所以 Vue 认为 input 组件不需要跟新,也就不会去更新视图,所以 input 会响应用户的输入,但却没有触发 Vue 更新。

如何证明以上结论是正确的呢?请看下面 2 种情况:

如将 this.value = 456  ,那么 input 会先从 123 变成 456,而后表现跟上面分析一致。因为 @input 中 this.value 赋值是常数,不会触发 Vue 更新视图,而 input 则是响应了用户的输入。

如将 this.value = Math.random() ,input 的内容会随着用户输入而变化(并不是用户输入的值),因为 this.value 得到了更新,所以 Vue 更新了视图,将 input 的内容同步为 this.value 的值。

从上文可知,React 跟 Vue 的区别在于:React 强制性使 input 的更新跟状态值的更新同步;而 Vue 则只会在状态值更新的时候才会同步更新视图,如状态值不变(如设置常亮this.value = 123),组件的视图更新将表现为非受控。

是因为它们的更新方式是不一样的。

Vue:依赖收集,数据更新后更新视图。

React:依靠 Render 和 Diff 配合更新,每次更新都会将状态值和视图同步。

所以,input 组件,绑定的状态值是 this.value ,onChange/@input 事件的处理是设置状态值等于一个常量:setValue(123) / this.value = 123,那么当用户在 input 输入内容时:

Vue:咦,状态没更新,不用更新视图,美滋滋!

React:嗯?状态是这个,视图渲染了那个,闹啥呢,同步一下!于是将视图上的值改为状态值。

受控组件的关键点在于,状态值更新会同步视图更新,一定条件下组件的值恒等于状态值;

React 的受控组件,状态值跟视图是两两强绑定关系的,组件值恒等于状态值;

Vue 的受控组件,状态值更新一定会同步视图更新,此时组件值恒等于状态值;但视图更新在特殊情况下(如视图更新了,但状态值保持不变),组件值不等于状态值;

React 和 Vue 的受控组件表现略微差异的原因在于它们不同的更新方式,Vue 着重于状态值的更新情况来更新视图,而 React 会强制状态值和视图保持同步。