React和Vue的逻辑复用”武器库”:谁更强大?
逻辑复用的核心问题
逻辑复用是前端开发中的关键挑战,让我们能够避免编写重复代码,React和Vue各自提供了不同的”武器”来解决这个问题。
🔁 生活类比:想象组件是厨师,而可复用逻辑是烹饪技巧。不同厨师(组件)需要使用相同的切菜、腌制技巧(可复用逻辑),但每位厨师又有自己的特色菜(UI呈现)。
React逻辑复用武器库
1. 高阶组件(HOC) – “包装纸”模式
HOC是接收组件并返回新组件的函数,像是给组件披上了一件”能力外衣”。
// 高阶组件示例
function withMousePosition(WrappedComponent) {
  return class extends React.Component {
    state = { x: 0, y: 0 };
    
    handleMouseMove = (e) => {
      this.setState({
        x: e.clientX,
        y: e.clientY
      });
    };
    
    componentDidMount() {
      window.addEventListener('mousemove', this.handleMouseMove);
    }
    
    componentWillUnmount() {
      window.removeEventListener('mousemove', this.handleMouseMove);
    }
    
    render() {
      return <WrappedComponent {...this.props} mousePosition={this.state} />;
    }
  };
}
// 使用HOC
const ComponentWithMouse = withMousePosition(MyComponent);
🎁 生活类比:就像礼品包装服务 – 你带来自己的礼物(组件),包装店给它套上精美的包装纸(额外功能),但里面的礼物本身(组件核心功能)并没有变。
2. Render Props – “填空题”模式
Render Props通过传递带有渲染逻辑的函数prop,让父组件决定如何渲染子组件内容。
// Render Props示例
class MouseTracker extends React.Component {
  state = { x: 0, y: 0 };
  
  handleMouseMove = (e) => {
    this.setState({
      x: e.clientX,
      y: e.clientY
    });
  };
  
  render() {
    return (
      <div onMouseMove={this.handleMouseMove}>
        {/* 调用render prop,传递state */}
        {this.props.render(this.state)}
      </div>
    );
  }
}
// 使用Render Props
<MouseTracker 
  render={({x, y}) => (
    <div>鼠标位置:{x}, {y}</div>
  )}
/>
📝 生活类比:就像填空题 – MouseTracker组件是一张试卷,它处理所有计算(状态管理),但留下了空白(render prop)由使用者填写答案(决定如何渲染)。
3. 自定义Hooks – “乐高积木”模式
React Hooks让我们能够从组件中提取状态逻辑,制作可复用的逻辑单元,是React目前最推荐的逻辑复用方式。
// 自定义Hook示例
function useMousePosition() {
  const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });
  
  useEffect(() => {
    const handleMouseMove = (e) => {
      setMousePosition({ x: e.clientX, y: e.clientY });
    };
    
    window.addEventListener('mousemove', handleMouseMove);
    return () => {
      window.removeEventListener('mousemove', handleMouseMove);
    };
  }, []);
  
  return mousePosition;
}
// 在组件中使用Hook
function MyComponent() {
  const { x, y } = useMousePosition();
  return <div>鼠标位置:{x}, {y}</div>;
}
🧩 生活类比:就像乐高积木 – 每个Hook是一块功能积木,可以自由组合搭建出各种复杂的应用,想要什么功能就拿相应的积木插上去。
Vue逻辑复用武器库
1. 混入(Mixins) – “调料包”模式
Mixins允许你定义可被多个组件复用的方法、生命周期钩子等,但因容易造成命名冲突和来源不明确,现在不推荐使用。
// 定义Mixin
const mousePositionMixin = {
            
  data() {
            
    return {
            
      x: 0,
      y: 0
    }
  },
  mounted() {
            
    window.addEventListener('mousemove', this.updateMousePosition);
  },
  beforeDestroy() {
            
    window.removeEventListener('mousemove', this.updateMousePosition);
  },
  methods: {
            
    updateMousePosition(e) {
            
      this.x = e.clientX;
      this.y = e.clientY;
    }
  }
};
// 使用Mixin
export default {
            
  mixins: [mousePositionMixin],
  template: `<div>鼠标位置:{
             { x }}, {
             { y }}</div>`
};
🧂 生活类比:就像往菜里加入调料包 – 快捷方便,但你不确定里面具体有什么,多个调料混合时可能味道冲突。
2. 作用域插槽(Scoped Slots) – “定制模板”模式
作用域插槽允许子组件将数据回传给父组件,父组件决定如何渲染,类似React的Render Props。
<!-- MouseTracker.vue -->
<template>
  <div @mousemove="updateMousePosition">
    <!-- 将数据传递给插槽 -->
    <slot :x="x" :y="y"></slot>
  </div>
</template>
<script>
export default {
  data() {
    return { x: 0, y: 0 };
  },
  methods: {
    updateMousePosition(e) {
      this.x = e.clientX;
      this.y = e.clientY;
    }
  }
};
</script>
<!-- 使用作用域插槽 -->
<MouseTracker v-slot="{x, y}">
  <div>鼠标位置:{
           { x }}, {
           { y }}</div>
</MouseTracker>
🎨 生活类比:就像定制T恤 – T恤工厂(子组件)提供基础款式和材料(数据),但由顾客(父组件)决定印上什么图案(渲染内容)。
3. 组合式API(Composition API) – “化学实验室”模式
Vue 3的组合式API允许我们将相关逻辑提取到可复用的函数中,是Vue当前最推荐的逻辑复用方式。
// 可复用的组合式函数
import {
             ref, onMounted, onUnmounted } from 'vue';
export function useMousePosition() {
            
  const x = ref(0);
  const y = ref(0);
  
  const updateMousePosition = (e) => {
            
    x.value = e.clientX;
    y.value = e.clientY;
  };
  
  onMounted(() => {
            
    window.addEventListener('mousemove', updateMousePosition);
  });
  
  onUnmounted(() => {
            
    window.removeEventListener('mousemove', updateMousePosition);
  });
  
  return {
             x, y };
}
// 在组件中使用
import {
             useMousePosition } from './useMousePosition';
export default {
            
  setup() {
            
    const {
             x, y } = useMousePosition();
    
    return {
             x, y }; // 返回给模板使用
  }
};
🧪 生活类比:就像化学实验室 – 你可以将不同的试剂(功能)精确混合,创造出各种复合物(组合功能),每个步骤都清晰可见,不会产生意外反应。
React VS Vue:逻辑复用对比
┌─────────────────────────────────────────────────────────────────────┐
│                     React VS Vue 逻辑复用对比                        │
├──────────────┬────────────────────────┬────────────────────────────┤
│ React方案    │ Vue对应方案            │ 各自特点                    │
├──────────────┼────────────────────────┼────────────────────────────┤
│ 高阶组件(HOC)│ Mixins(不推荐)         │ React: 组件套组件,嵌套深   │
│              │                        │ Vue: 直接混入,但易冲突     │
├──────────────┼────────────────────────┼────────────────────────────┤
│ Render Props │ 作用域插槽             │ React: 通过props传递渲染函数 │
│              │ (Scoped Slots)         │ Vue: 更符合模板语法直觉     │
├──────────────┼────────────────────────┼────────────────────────────┤
│ 自定义Hooks  │ 组合式API              │ React: 使用useState等钩子   │
│              │ (Composition API)      │ Vue: 使用ref、reactive等    │
└──────────────┴────────────────────────┴────────────────────────────┘
使用流程对比
React Hooks与Vue组合式API工作流程
React Hooks:                Vue组合式API:
┌──────────────┐           ┌──────────────┐
│ 创建自定义Hook│           │创建组合式函数 │
└──────┬───────┘           └──────┬───────┘
       │                          │
       ▼                          ▼
┌──────────────┐           ┌──────────────┐
│ 使用useState  │           │  使用ref/    │
│ useEffect等  │           │  reactive等  │
└──────┬───────┘           └──────┬───────┘
       │                          │
       ▼                          ▼
┌──────────────┐           ┌──────────────┐
│ 返回状态和   │           │ 返回响应式   │
│ 操作方法    │           │ 数据和方法   │
└──────┬───────┘           └──────┬───────┘
       │                          │
       ▼                          ▼
┌──────────────┐           ┌──────────────┐
│ 在组件中直接 │           │ 在setup中    │
│ 使用返回值   │           │ 返回给模板   │
└──────────────┘           └──────────────┘
实际使用场景示例
场景:创建一个带有倒计时功能的组件
React实现 (使用Hook)
// useCountdown.js
import { useState, useEffect } from 'react';
export function useCountdown(initialTime) {
  const [seconds, setSeconds] = useState(initialTime);
  const [isActive, setIsActive] = useState(false);
  
  const start = () => setIsActive(true);
  const pause = () => setIsActive(false);
  const reset = () => {
    setIsActive(false);
    setSeconds(initialTime);
  };
  
  useEffect(() => {
    let interval = null;
    if (isActive && seconds > 0) {
      interval = setInterval(() => {
        setSeconds(seconds => seconds - 1);
      }, 1000);
    } else if (seconds === 0) {
      setIsActive(false);
    }
    return () => clearInterval(interval);
  }, [isActive, seconds]);
  
  return { seconds, isActive, start, pause, reset };
}
// 在组件中使用
function CountdownComponent() {
  const { seconds, isActive, start, pause, reset } = useCountdown(60);
  
  return (
    <div>
      <h1>倒计时: {seconds}秒</h1>
      <button onClick={start}>开始</button>
      <button onClick={pause}>暂停</button>
      <button onClick={reset}>重置</button>
    </div>
  );
}
Vue实现 (使用组合式API)
// useCountdown.js
import {
             ref, watch } from 'vue';
export function useCountdown(initialTime) {
            
  const seconds = ref(initialTime);
  const isActive = ref(false);
  
  let interval = null;
  
  const start = () => isActive.value = true;
  const pause = () => isActive.value = false;
  const reset = () => {
            
    isActive.value = false;
    seconds.value = initialTime;
  };
  
  watch(isActive, (newValue) => {
            
    if (newValue) {
            
      interval = setInterval(() => {
            
        if (seconds.value > 0) {
            
          seconds.value--;
        } else {
            
          isActive.value = false;
        }
      }, 1000);
    } else {
            
      clearInterval(interval);
    }
  });
  
  return {
            
    seconds,
    isActive,
    start,
    pause,
    reset
  };
}
// 在组件中使用
<script setup>
import {
             useCountdown } from './useCountdown';
const {
             seconds, isActive, start, pause, reset } = useCountdown(60);
</script>
<template>
  <div>
    <h1>倒计时: {
            {
             seconds }}秒</h1>
    <button @click="start">开始</button>
    <button @click="pause">暂停</button>
    <button @click="reset">重置</button>
  </div>
</template>
如何选择?
React和Vue的逻辑复用方案最终殊途同归,现代的Hook和组合式API都实现了相似的目标。选择取决于:
          团队熟悉度:使用团队更熟悉的框架
          项目需求:考虑项目的特定需求和复杂性
          个人偏好:两者都很强大,选择你更喜欢的语法风格
🚀 生活类比:就像选择汽车 – 无论丰田还是本田,都能把你从A点送到B点,关键是选择适合你驾驶习惯的那一款。
最佳实践
React最佳实践
          优先使用自定义Hook进行逻辑复用
          保持Hook简单,每个Hook只关注一个功能点
          避免Hook之间的复杂依赖,降低维护难度
Vue最佳实践
          避免使用Mixins,优先选择组合式API
          按功能组织代码,而非生命周期
          **使用<script setup>**简化组合式API的使用
总结
现代前端框架的逻辑复用手段在不断进化:
          老旧方案(HOC、Mixins)正被逐渐淘汰,因为它们有命名冲突、来源不明确等问题
          最新方案(Hooks、组合式API)是目前的主流,它们提供了更清晰、可组合的逻辑复用
          两个框架在思想上正在融合,都走向了基于函数的逻辑组合方式
无论选择React还是Vue,它们的现代逻辑复用方案都能有效提高代码复用性和可维护性,核心是理解背后的设计思想,灵活运用于实际开发中。






















暂无评论内容