vue和reacts数据响应式的差异

Vue 的数据响应式:

原理

  • Vue 使用 Object.definePropertyProxy(在 Vue 3 中)来实现数据的响应式。当创建 Vue 实例时,会对 data 对象中的属性进行遍历,将其转换为响应式属性。对于 Object.defineProperty,它会为每个属性定义 gettersetter 函数,getter 会收集依赖(如模板中的表达式、计算属性或 watch 监听器),而 setter 会触发更新,通知依赖进行重新渲染或执行相应的操作。对于 Proxy,它会拦截对象的操作(如属性访问、赋值、删除等),当属性发生变化时,会触发相应的更新操作。

特点

  • 细粒度的依赖收集 :能够精确地收集到哪些地方使用了某个属性,当该属性发生变化时,只会更新与之相关的部分。例如,在模板中使用了 { { message }},Vue 会精确地知道 message 被使用了,当 message 改变时,只更新这一处,而不是整个组件。

  • 深度响应式 :默认情况下,Vue 的数据响应式是深度的,即对于嵌套对象或数组,内部的属性变化也会触发响应式更新。这意味着即使是 data 中的嵌套对象或数组的元素发生变化,也会触发相应的更新操作。

  • 自动更新:在 Vue 中,当数据发生变化时,会自动触发视图的更新,开发者不需要手动调用更新视图的操作。这是因为 Vue 的响应式系统会自动处理依赖关系,一旦数据发生变化,就会通知相关的订阅者。

    <template>

    {

    复制代码
    { message }}</p>
      <button @click="changeMessage">Change Message</button>
    </div>
    </template> <script> export default { data() { return { message: 'Hello, Vue!' }; }, methods: { changeMessage() { this.message = 'Hello, Updated Vue!'; } } }; </script>

在上述 Vue 示例中,当点击 Change Message 按钮,messagesetter 被触发,会自动更新模板中使用 { { message }} 的部分。

React 的数据响应式:

  • React 的数据响应式基于状态(state)和属性(props)的不可变原则。当状态或属性发生变化时,需要通过调用 setState (对于类组件)或 useState (对于函数组件)提供的更新函数来更新状态,React 会比较前后状态的差异(虚拟 DOM 比较),然后根据差异更新真实的 DOM。React 不会直接修改状态对象,而是创建一个新的状态对象来触发更新。

  • 虚拟 DOM 比较:React 通过比较虚拟 DOM 的前后状态,找出需要更新的部分,最小化实际 DOM 的操作。这种方式提高了性能,避免了频繁的直接 DOM 操作。

  • 状态不可变 :在 React 中,状态应该是不可变的,这意味着不能直接修改状态对象的属性,而是要创建一个新的状态对象。例如,不能使用 this.state.count = 10,而是使用 this.setState({ count: 10 })setCount(10) (在函数组件中)。

  • 手动触发更新 :React 需要手动调用 setStateuseState 的更新函数来触发更新,不会自动监测状态的变化。

    import React, { useState } from 'react';

    function App() {
    const [message, setMessage] = useState('Hello, React!');

    复制代码
    const changeMessage = () => {
      setMessage('Hello, Updated React!');
    };
    
    return (
      <div>
        <p>{message}</p>
        <button onClick={changeMessage}>Change Message</button>
      </div>
    );

    }

    export default App;

在上述 React 示例中,点击 Change Message 按钮,调用 setMessage 函数来更新 message 的值,React 会比较前后的虚拟 DOM 并更新实际 DOM。

差异总结:

  • 更新触发方式
    • Vue 通过 Object.definePropertyProxysetter 自动触发更新,而 React 需要手动调用 setStateuseState 的更新函数。
  • 数据处理原则
    • Vue 可以直接修改数据对象,且支持深度响应式,而 React 遵循状态不可变原则,需要创建新的状态对象。
  • 依赖收集
    • Vue 进行细粒度的依赖收集,而 React 主要通过虚拟 DOM 的比较来找出需要更新的部分,不进行依赖收集。

优缺点:

  • Vue 的优点
    • 对于习惯了面向对象编程和自动响应式更新的开发者来说,使用起来可能更直观,因为它自动处理数据更新和依赖关系。
    • 深度响应式使得处理复杂的数据结构(如嵌套对象和数组)更加方便。
    • 可以直接修改数据,代码可能看起来更简洁。
  • Vue 的缺点
    • 对于复杂的对象和数组,使用 Object.defineProperty 可能会有性能问题,而 Proxy 有一定的兼容性问题(但在现代浏览器中已经得到了很好的支持)。
    • 依赖于 Vue 的响应式系统,对于一些高级用法或集成外部库可能需要额外的学习成本。
  • React 的优点
    • 虚拟 DOM 的比较方式可以在复杂应用中提供较好的性能,尤其是对于频繁更新的场景。
    • 状态不可变原则使得代码更容易理解和调试,避免了意外的副作用。
    • 更灵活,适合构建大型、复杂的应用,可与各种库和架构集成。
  • React 的缺点
    • 需要手动调用 setStateuseState 的更新函数,对于初学者可能会忘记更新状态。
    • 不支持深度响应式,对于嵌套对象或数组的处理可能需要更多的代码,例如使用展开运算符或库(如 immer)来更新状态。

综上所述,Vue 和 React 在数据响应式方面有不同的实现方式和特点,选择使用哪个框架取决于具体的开发需求、团队的技术栈和个人偏好。

Vue 和 React 都是流行的前端JavaScript库或框架,它们都提供了构建用户界面的方法。虽然两者都可以实现数据的响应式更新,但它们在实现这一功能上有着不同的方法和哲学。

Vue 的响应式系统

Vue 的核心特性之一是它的响应式数据绑定。Vue 使用的是基于对象的观察者模式来追踪数据的变化。当你创建一个 Vue 实例时,Vue 会遍历实例的数据对象中的所有属性,并使用 Object.defineProperty()(在 Vue 3 中使用的是 Proxy)将这些属性转换为 getter 和 setter。这使得当属性值被访问或修改时,Vue 能够捕捉到这些操作并作出相应的反应,比如更新视图。

Vue 的模板语法允许开发者直接在HTML中编写声明式的渲染逻辑,它会自动处理所有的 DOM 更新,因此对于开发者来说,数据变化和DOM更新之间的关系非常直观。

React 的响应式系统

React 并不自带数据的响应式机制。相反,它依赖于组件的状态(state)和属性(props)来管理数据。React 组件本质上是函数或类,它们接收 props 作为输入并返回描述UI的元素树。当状态或属性发生变化时,React 会重新执行组件函数(对于函数组件)或调用其 render 方法(对于类组件),以计算出新的 UI 表示。React 然后比较新旧两棵元素树,以确定需要对实际 DOM 进行哪些最小化的更新。

React 16.8 引入了 Hooks,这是一个重要的API增强,它允许函数组件拥有状态和其他特性,如副作用管理等。Hooks 例如 useState、useReducer 提供了一种更简洁的方式来管理组件内的状态变化。

总结

  • Vue 更加注重声明式的双向数据绑定,开发者可以更直接地看到数据与视图之间的联系。
  • React 则鼓励单向数据流,通过状态和属性的变化来触发整个应用的更新,这通常意味着更多的手动控制,但也提供了更大的灵活性。

这两种方式各有优劣,选择哪一个取决于开发者的偏好以及项目的具体需求。

与 Vue 基于 Proxy 实现响应式系统不同,React 并没有像 Vue 那样内置的响应式系统,而是基于状态管理和组件更新机制来实现数据变化驱动 UI 更新,下面从不同层面为你介绍:

1. 基础的 this.statesetState(类组件)

在 React 的类组件中,this.state 用于存储组件的状态数据,当调用 this.setState 方法更新状态时,React 会重新渲染组件,从而更新 UI。

复制代码
import React, { Component } from 'react';
import ReactDOM from 'react-dom/client';

class Counter extends Component {
    constructor(props) {
        super(props);
        this.state = {
            count: 0
        };
    }

    increment = () => {
        this.setState({ count: this.state.count + 1 });
    }

    render() {
        return (
            <div>
                <p>Count: {this.state.count}</p>
                <button onClick={this.increment}>Increment</button>
            </div>
        );
    }
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Counter />);

在上述代码中,当点击按钮调用 increment 方法时,this.setState 会更新 count 状态,React 会重新调用 render 方法来更新 UI。

2. useState 钩子(函数组件)

在 React 的函数组件中,可以使用 useState 钩子来管理状态。当调用 setState 函数更新状态时,React 会重新渲染函数组件。

复制代码
import React, { useState } from 'react';
import ReactDOM from 'react-dom/client';

const Counter = () => {
    const [count, setCount] = useState(0);

    const increment = () => {
        setCount(count + 1);
    };

    return (
        <div>
            <p>Count: {count}</p>
            <button onClick={increment}>Increment</button>
        </div>
    );
};

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Counter />);

这里,useState 返回一个状态变量 count 和一个更新状态的函数 setCount。当点击按钮调用 increment 方法时,setCount 会更新 count 状态,React 会重新渲染 Counter 组件。

3. 状态管理库

对于复杂的应用,React 通常会使用状态管理库,如 Redux、MobX 等。

Redux 是一个可预测的状态容器,用于管理 React 应用的状态。它采用单向数据流的思想,通过 action 触发 reducer 来更新 store 中的状态,当 store 中的状态发生变化时,会通知订阅的组件进行更新。

复制代码
import React from 'react';
import ReactDOM from 'react-dom/client';
import { createStore } from 'redux';
import { Provider, useSelector, useDispatch } from 'react-redux';

// 定义 reducer
const counterReducer = (state = { count: 0 }, action) => {
    switch (action.type) {
        case 'INCREMENT':
            return { count: state.count + 1 };
        default:
            return state;
    }
};

// 创建 store
const store = createStore(counterReducer);

const Counter = () => {
    const count = useSelector(state => state.count);
    const dispatch = useDispatch();

    const increment = () => {
        dispatch({ type: 'INCREMENT' });
    };

    return (
        <div>
            <p>Count: {count}</p>
            <button onClick={increment}>Increment</button>
        </div>
    );
};

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
    <Provider store={store}>
        <Counter />
    </Provider>
);

在上述代码中,useSelector 用于从 store 中获取状态,useDispatch 用于触发 action。当 action 触发 reducer 更新 store 中的状态时,Counter 组件会重新渲染。

MobX

MobX 是另一个流行的状态管理库,它基于响应式编程的思想,通过 observable 定义可观察的状态,当状态发生变化时,会自动更新依赖该状态的组件。

复制代码
import React from 'react';
import ReactDOM from 'react-dom/client';
import { makeObservable, observable, action } from 'mobx';
import { observer } from 'mobx-react-lite';

class CounterStore {
    count = 0;

    constructor() {
        makeObservable(this, {
            count: observable,
            increment: action
        });
    }

    increment = () => {
        this.count++;
    };
}

const counterStore = new CounterStore();

const Counter = observer(() => {
    return (
        <div>
            <p>Count: {counterStore.count}</p>
            <button onClick={counterStore.increment}>Increment</button>
        </div>
    );
});

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Counter />);

在这个例子中,makeObservablecount 标记为可观察的状态,increment 标记为 action。当调用 increment 方法更新 count 状态时,Counter 组件会自动重新渲染。

综上所述,React 通过状态管理和组件更新机制来实现数据变化驱动 UI 更新,不同的方式适用于不同复杂度的应用场景。

Vue 基于 Proxy 的自动更新机制

在 Vue 3 中,响应式系统主要基于 Proxy 来实现。当你使用 reactiveref 创建响应式数据时,Vue 会使用 Proxy 对数据对象进行包装。

复制代码
<template>
  <div>
    <p>{
  
  { count }}</p>
    <button @click="increment">增加</button>
  </div>
</template>

<script setup>
import { ref } from 'vue';

const count = ref(0);

const increment = () => {
  count.value++; // 修改响应式数据
};
</script>
原理说明
  • 当你修改 count.value 时,Proxy 会捕获到这个变化。Vue 内部的依赖收集和追踪机制会记录哪些组件使用了这个 count 数据。
  • 一旦数据发生变化,Vue 会自动通知依赖该数据的组件进行重新渲染,更新对应的 DOM 节点,这个过程是自动完成的,开发者无需手动干预。

React 的手动更新机制

在 React 里,无论是类组件的 this.statethis.setState,还是函数组件的 useState,都需要手动调用更新函数来触发组件的重新渲染。

类组件示例
复制代码
import React, { Component } from 'react';

class Counter extends Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

  increment = () => {
    this.setState({ count: this.state.count + 1 }); // 手动调用 setState 更新状态
  }

  render() {
    return (
      <div>
        <p>{this.state.count}</p>
        <button onClick={this.increment}>增加</button>
      </div>
    );
  }
}

export default Counter;
函数组件示例
复制代码
import React, { useState } from 'react';

const Counter = () => {
  const [count, setCount] = useState(0);

  const increment = () => {
    setCount(count + 1); // 手动调用 setCount 更新状态
  };

  return (
    <div>
      <p>{count}</p>
      <button onClick={increment}>增加</button>
    </div>
  );
};

export default Counter;
原理说明
  • 在 React 中,状态是不可变的。当你想要更新状态时,需要使用特定的更新函数(如 setStatesetCount)。调用这些函数会告诉 React 状态发生了变化,React 会重新渲染使用了该状态的组件。
  • 如果不手动调用这些更新函数,即使状态数据的引用发生了改变,React 也不会知道数据已经变化,也就不会触发组件的重新渲染。

总结

Vue 使用 Proxy 实现的响应式系统能自动追踪数据变化并更新 UI,开发者只需修改数据即可;而 React 需要开发者手动调用更新函数来通知 React 状态发生了变化,从而触发组件的重新渲染。

相关推荐
武昌库里写JAVA34 分钟前
39.剖析无处不在的数据结构
java·vue.js·spring boot·课程设计·宠物管理
Freedom风间4 小时前
前端优秀编码技巧
前端·javascript·代码规范
萌萌哒草头将军5 小时前
🚀🚀🚀 Openapi:全栈开发神器,0代码写后端!
前端·javascript·next.js
萌萌哒草头将军5 小时前
🚀🚀🚀 Prisma 爱之初体验:一款非常棒的 ORM 工具库
前端·javascript·orm
拉不动的猪5 小时前
SDK与API简单对比
前端·javascript·面试
runnerdancer5 小时前
微信小程序蓝牙通信开发之分包传输通信协议开发
前端
BillKu6 小时前
Vue3后代组件多祖先通讯设计方案
开发语言·javascript·ecmascript
山海上的风6 小时前
Vue里面elementUi-aside 和el-main不垂直排列
前端·vue.js·elementui
电商api接口开发6 小时前
ASP.NET MVC 入门指南二
前端·c#·html·mvc