【JavaScript】React 实现 Vue 的 watch 和 computed 详解

React 实现 Vue 的 watch 和 computed 详解

文章目录

  • [React 实现 Vue 的 watch 和 computed 详解](#React 实现 Vue 的 watch 和 computed 详解)
    • [二、实现 Vue 的 computed(计算属性)](#二、实现 Vue 的 computed(计算属性))
    • [三、实现 Vue 的 watch(监听数据变化)](#三、实现 Vue 的 watch(监听数据变化))
      • [场景一:基础监听(函数组件 + useEffect)](#场景一:基础监听(函数组件 + useEffect))
      • [场景二:深度监听(模拟 Vue watch 的 deep: true)](#场景二:深度监听(模拟 Vue watch 的 deep: true))
        • 方案一:手动监听所有深层属性(适用于简单对象)
        • [方案二:自定义 Hook(useDeepCompareEffect,推荐)](#方案二:自定义 Hook(useDeepCompareEffect,推荐))
          • [步骤 1:安装 lodash 依赖](#步骤 1:安装 lodash 依赖)
          • [步骤 2:自定义 Hook 封装(useDeepCompareEffect)](#步骤 2:自定义 Hook 封装(useDeepCompareEffect))
          • [步骤 3:使用示例(监听复杂深层对象)](#步骤 3:使用示例(监听复杂深层对象))
          • 说明
      • [场景三:立即执行(模拟 Vue watch 的 immediate: true)](#场景三:立即执行(模拟 Vue watch 的 immediate: true))
        • [代码示例 1:默认立即执行(简洁写法)](#代码示例 1:默认立即执行(简洁写法))
        • [代码示例 2:取消「立即执行」(仅数据变化时执行)](#代码示例 2:取消「立即执行」(仅数据变化时执行))
      • [进阶:封装 Vue 风格的 useWatch 自定义 Hook(提升复用性)](#进阶:封装 Vue 风格的 useWatch 自定义 Hook(提升复用性))
    • [四、React vs Vue 核心对应关系(汇总表)](#四、React vs Vue 核心对应关系(汇总表))
    • 五、总结与落地说明
      • [1. 核心要点回顾](#1. 核心要点回顾)
      • [2. 实用价值强调](#2. 实用价值强调)
      • [3. 落地补充提示](#3. 落地补充提示)

在前端框架生态中,Vue 的 watchcomputed 是两大核心且极具实用性的特性: computed 用于创建基于依赖的缓存计算属性,避免无效重复计算; watch 用于监听数据变化并执行副作用操作,支撑各类业务逻辑的响应式触发。在 React 开发中,我们同样会遇到「需要缓存计算结果」和「监听数据变化执行回调」的场景,同时这也是前端面试中的高频考点。本文将详细讲解 React 生态中如何实现 Vue 这两大特性,涵盖基础写法与进阶封装,所有代码均可直接落地项目。

二、实现 Vue 的 computed(计算属性)

在开始 React 实现之前,我们先明确 Vue computed 的核心特性:基于响应式依赖进行缓存 。只有当 computed 依赖的数据源发生变化时,才会重新执行计算逻辑并更新结果;如果依赖未发生变化,直接返回缓存的上一次计算结果,从而避免无效的性能消耗,这也是 computed 与普通方法的核心区别。

方式一:基础版(函数组件 + useMemo,推荐)

React 中实现 computed 核心功能的最佳选择是官方提供的 useMemo Hook,它完美匹配 Vue computed 的「缓存」核心特性,是项目开发中的推荐方案。

完整代码示例
jsx 复制代码
import { useState, useMemo } from 'react';

function ComputedDemo() {
  // 1. 定义数据源(模拟 Vue 的 data 选项)
  const [num1, setNum1] = useState(10);
  const [num2, setNum2] = useState(20);
  const [unrelatedNum, setUnrelatedNum] = useState(0); // 与计算逻辑无关的变量

  // 2. 使用 useMemo 实现计算属性(模拟 Vue computed)
  const sum = useMemo(() => {
    console.log('useMemo 计算逻辑执行了------仅依赖变化时触发');
    return num1 + num2;
  }, [num1, num2]); // 依赖数组:仅 num1、num2 变化时,重新计算 sum

  return (
    <div style={{ padding: '20px' }}>
      <h3>React 实现 Vue computed(useMemo 版)</h3>
      <p>num1: {num1}</p>
      <p>num2: {num2}</p>
      <p>无关变量 unrelatedNum: {unrelatedNum}</p>
      <p style={{ color: 'blue', fontWeight: 'bold' }}>计算属性 sum(num1 + num2): {sum}</p>

      {/* 3. 交互按钮:修改相关依赖 */}
      <button onClick={() => setNum1(prev => prev + 1)} style={{ marginRight: '10px' }}>
        num1 + 1
      </button>
      <button onClick={() => setNum2(prev => prev + 1)} style={{ marginRight: '10px' }}>
        num2 + 1
      </button>

      {/* 4. 交互按钮:修改无关依赖 */}
      <button onClick={() => setUnrelatedNum(prev => prev + 1)}>
        无关变量 + 1
      </button>
    </div>
  );
}

export default ComputedDemo;
核心解释
  1. useMemo 两个核心参数的作用:

    • 第一个参数:计算逻辑函数 ,返回值即为计算属性的结果(对应 Vue computed 中的计算函数),该函数仅在依赖变化时执行。
    • 第二个参数:依赖数组 ,存放当前计算逻辑依赖的所有数据源(对应 Vue computed 自动收集的响应式依赖),只有数组中的变量发生变化时,React 才会重新执行第一个参数的计算函数,更新计算结果。
  2. 缓存特性验证(对比「使用 useMemo」与「直接定义变量」):

    • 上述示例中,点击「num1 + 1」或「num2 + 1」(修改相关依赖),控制台会打印日志,sum 也会同步更新,说明计算逻辑重新执行。
    • 点击「无关变量 + 1」(修改不相关依赖),控制台无日志输出,sum 保持不变,说明 useMemo 生效,直接返回了缓存的计算结果。
    • 若直接定义变量 const sum = num1 + num2;,每次组件重新渲染(无论是否修改相关依赖),都会重新执行 num1 + num2 运算,当计算逻辑复杂时(如大量数据过滤、格式化),会造成不必要的性能损耗,这也是 useMemo 与普通变量定义的核心差异。

方式二:简化版(仅简单计算,无需缓存)

该方式适用于计算逻辑极简单 (如简单的数值运算、模板字符串拼接),且完全不关心重复计算带来的性能损耗的场景,对应 Vue 中 computed 关闭缓存(极少使用)的场景。

简短代码示例
jsx 复制代码
import { useState } from 'react';

function SimpleComputedDemo() {
  // 定义数据源
  const [firstName, setFirstName] = useState('Zhang');
  const [lastName, setLastName] = useState('San');

  // 简化版:直接定义普通变量实现计算(无缓存)
  const fullName = `${firstName} ${lastName}`; // 简单模板字符串拼接

  return (
    <div style={{ padding: '20px' }}>
      <h3>React 实现 Vue computed(简化版)</h3>
      <p>firstName: {firstName}</p>
      <p>lastName: {lastName}</p>
      <p style={{ color: 'blue', fontWeight: 'bold' }}>计算结果 fullName: {fullName}</p>

      <button onClick={() => setFirstName('Li')} style={{ marginRight: '10px' }}>
        修改 firstName 为 Li
      </button>
      <button onClick={() => setLastName('Si')}>
        修改 lastName 为 Si
      </button>
    </div>
  );
}

export default SimpleComputedDemo;
说明

该方式无需引入任何 Hook,直接通过普通变量赋值实现计算需求,写法简洁。但缺点是无缓存,每次组件渲染(无论依赖是否变化)都会重新执行计算逻辑,仅适用于计算成本极低的场景,不推荐在复杂计算中使用。

三、实现 Vue 的 watch(监听数据变化)

Vue watch 的核心特性是:监听指定的响应式数据,当数据发生变化时,执行预设的副作用函数 (如发送接口请求、操作本地存储、修改其他关联数据等)。其核心应用场景包括:基础监听、深度监听(deep: true)、立即执行(immediate: true),下面我们逐一在 React 中实现这些场景。

场景一:基础监听(函数组件 + useEffect)

React 中处理副作用的核心 Hook 是 useEffect,通过控制其依赖数组,可以精准匹配 Vue 基础版 watch 的功能,这是实现数据监听的基础方案。

完整代码示例
jsx 复制代码
import { useState, useEffect } from 'react';

function BasicWatchDemo() {
  // 1. 定义数据源(模拟 Vue data)
  const [count, setCount] = useState(0); // 单个基础类型值
  const [user, setUser] = useState({ name: 'Zhang San', age: 25 }); // 对象类型

  // 子场景 1:监听单个基础类型值(对应 Vue 监听单个基础数据)
  useEffect(() => {
    console.log(`[基础监听] count 发生变化,新值为:${count}`);
    // 此处可执行副作用逻辑:如接口请求、本地存储等
  }, [count]); // 依赖数组:仅 count 变化时,执行副作用函数

  // 子场景 2:监听对象的某个具体属性(对应 Vue 监听对象单个属性)
  useEffect(() => {
    console.log(`[基础监听] user.name 发生变化,新值为:${user.name}`);
    // 此处可执行副作用逻辑
  }, [user.name]); // 依赖数组:仅 user.name 变化时,执行副作用函数

  return (
    <div style={{ padding: '20px' }}>
      <h3>React 实现 Vue watch(基础监听版)</h3>
      <p>count: {count}</p>
      <p>user.name: {user.name}</p>
      <p>user.age: {user.age}</p>

      {/* 交互按钮:修改 count */}
      <button onClick={() => setCount(prev => prev + 1)} style={{ marginRight: '10px' }}>
        count + 1
      </button>

      {/* 交互按钮:修改 user.name */}
      <button onClick={() => setUser(prev => ({ ...prev, name: 'Li Si' }))} style={{ marginRight: '10px' }}>
        修改 user.name 为 Li Si
      </button>

      {/* 交互按钮:修改 user.age(不触发 user.name 的监听) */}
      <button onClick={() => setUser(prev => ({ ...prev, age: prev.age + 1 }))}>
        user.age + 1
      </button>
    </div>
  );
}

export default BasicWatchDemo;
核心解释
  1. 依赖数组与监听目标的关联:useEffect 的副作用函数执行时机由依赖数组控制,只有当依赖数组中的变量发生「浅层次变化」时,副作用函数才会执行,这与 Vue 基础 watch 监听数据变化触发回调的逻辑完全一致。
  2. 上述示例中:
    • 点击「count + 1」,仅触发 count 对应的监听副作用。
    • 点击「修改 user.name」,仅触发 user.name 对应的监听副作用。
    • 点击「user.age + 1」,由于未将 user.age 列入依赖数组,因此不会触发任何副作用函数,符合精准监听的需求。

场景二:深度监听(模拟 Vue watch 的 deep: true)

在实际开发中,我们经常需要监听复杂对象或数组的「深层属性变化」(如 user.address.provincelist[0].name),此时简单的依赖数组无法实现需求,这就需要模拟 Vue watchdeep: true 配置,实现深度监听。

方案一:手动监听所有深层属性(适用于简单对象)

对于属性较少的简单对象,可以将所有需要监听的深层属性手动列入 useEffect 的依赖数组,实现近似的深度监听效果。

代码示例
jsx 复制代码
import { useState, useEffect } from 'react';

function ManualDeepWatchDemo() {
  // 定义深层对象数据源
  const [user, setUser] = useState({
    name: 'Zhang San',
    address: {
      province: 'Guangdong',
      city: 'Shenzhen'
    }
  });

  // 手动监听深层属性:user.name、user.address.province、user.address.city
  useEffect(() => {
    console.log(`[手动深层监听] user 深层属性发生变化,当前 user:`, user);
  }, [user.name, user.address.province, user.address.city]);

  return (
    <div style={{ padding: '20px' }}>
      <h3>React 实现 Vue watch(手动深层监听版)</h3>
      <p>user.name: {user.name}</p>
      <p>user.address.province: {user.address.province}</p>
      <p>user.address.city: {user.address.city}</p>

      <button onClick={() => setUser(prev => ({
        ...prev,
        address: { ...prev.address, city: 'Guangzhou' }
      }))} style={{ marginRight: '10px' }}>
        修改城市为 Guangzhou
      </button>

      <button onClick={() => setUser(prev => ({
        ...prev,
        address: { ...prev.address, province: 'Jiangsu' }
      }))}>
        修改省份为 Jiangsu
      </button>
    </div>
  );
}

export default ManualDeepWatchDemo;
局限性说明

该方案仅适用于属性较少的简单对象,当对象结构复杂(如多层嵌套、属性数量众多)时,存在明显弊端:

  1. 维护成本极高:需要手动罗列所有深层属性,遗漏任何一个都可能导致监听失效。
  2. 代码冗余:依赖数组会变得异常冗长,降低代码可读性和可维护性。
  3. 无法应对动态属性:对于数组、动态添加的对象属性,无法提前手动罗列,监听效果受限。

因此,对于复杂对象或数组,推荐使用方案二。

方案二:自定义 Hook(useDeepCompareEffect,推荐)

该方案的核心思路是:通过 lodash.isEqual 实现深层数据对比,结合 useRef 缓存上一次的依赖数据,仅当深层对比发现数据变化时,才执行副作用函数,完美模拟 Vue watchdeep: true

步骤 1:安装 lodash 依赖
bash 复制代码
npm install lodash
# 或
yarn add lodash
步骤 2:自定义 Hook 封装(useDeepCompareEffect)
jsx 复制代码
import { useEffect, useRef } from 'react';
import isEqual from 'lodash/isEqual';

// 自定义深层对比 useEffect Hook(模拟 deep: true)
function useDeepCompareEffect(effect, deps) {
  // 1. 使用 useRef 缓存上一次的依赖数据
  const prevDepsRef = useRef();

  // 2. 深层对比当前依赖与上一次依赖是否一致
  const depsChanged = !isEqual(deps, prevDepsRef.current);

  // 3. 更新缓存的依赖数据(无论是否变化,都更新为当前最新依赖)
  if (depsChanged) {
    prevDepsRef.current = deps;
  }

  // 4. 调用 useEffect,依赖数组为 [depsChanged](仅当深层对比发现变化时,执行 effect)
  useEffect(() => {
    return effect();
  }, [depsChanged]);
}

export default useDeepCompareEffect;
步骤 3:使用示例(监听复杂深层对象)
jsx 复制代码
import { useState } from 'react';
import useDeepCompareEffect from './useDeepCompareEffect';

function DeepWatchDemo() {
  // 定义复杂深层对象数据源
  const [user, setUser] = useState({
    name: 'Zhang San',
    age: 25,
    address: {
      province: 'Guangdong',
      city: 'Shenzhen',
      detail: {
        street: 'Nanshan Road',
        number: '123'
      }
    },
    hobbies: ['reading', 'running']
  });

  // 使用自定义 useDeepCompareEffect 实现深度监听
  useDeepCompareEffect(() => {
    console.log(`[深层监听] user 发生变化(包含深层属性),当前 user:`, user);
  }, [user]); // 依赖数组直接传入整个 user,无需手动罗列深层属性

  return (
    <div style={{ padding: '20px' }}>
      <h3>React 实现 Vue watch(深层监听版)</h3>
      <p>user.name: {user.name}</p>
      <p>user.address.detail.street: {user.address.detail.street}</p>
      <p>user.hobbies: {user.hobbies.join(', ')}</p>

      <button onClick={() => setUser(prev => ({
        ...prev,
        address: {
          ...prev.address,
          detail: { ...prev.address.detail, street: 'Futian Road' }
        }
      }))} style={{ marginRight: '10px' }}>
        修改街道为 Futian Road
      </button>

      <button onClick={() => setUser(prev => ({
        ...prev,
        hobbies: [...prev.hobbies, 'swimming']
      }))}>
        添加爱好 swimming
      </button>
    </div>
  );
}

export default DeepWatchDemo;
说明

该方案完美解决了复杂对象的深度监听问题,无需手动罗列深层属性,维护成本低,是项目开发中处理深度监听的推荐方案,其核心逻辑是通过 lodash.isEqual 忽略引用地址,仅对比数据的深层内容是否一致。

场景三:立即执行(模拟 Vue watch 的 immediate: true)

Vue watchimmediate: true 配置用于实现「监听函数在首次渲染时立即执行一次,后续依赖变化时再正常执行」。而 React 中 useEffect 的默认行为就是:首次组件渲染时执行一次副作用函数,之后仅当依赖变化时再次执行 ,这与 immediate: true 的逻辑完全匹配,实现起来非常简洁。

代码示例 1:默认立即执行(简洁写法)
jsx 复制代码
import { useState, useEffect } from 'react';

function ImmediateWatchDemo1() {
  const [count, setCount] = useState(0);

  // useEffect 默认行为:首次渲染 + count 变化时执行(对应 Vue immediate: true)
  useEffect(() => {
    console.log(`[立即执行] count 监听触发,当前值:${count}`);
    // 副作用逻辑:如初始化请求、首次渲染后的数据处理等
  }, [count]);

  return (
    <div style={{ padding: '20px' }}>
      <h3>React 实现 Vue watch(立即执行版)</h3>
      <p>count: {count}</p>
      <button onClick={() => setCount(prev => prev + 1)}>
        count + 1
      </button>
    </div>
  );
}

export default ImmediateWatchDemo1;
代码示例 2:取消「立即执行」(仅数据变化时执行)

有时我们需要模拟 Vue watch 的默认行为(immediate: false,仅数据变化时执行,首次渲染不执行),此时可以通过 useRef 定义一个标识位,控制首次渲染时不执行副作用逻辑。

jsx 复制代码
import { useState, useEffect, useRef } from 'react';

function ImmediateWatchDemo2() {
  const [count, setCount] = useState(0);
  const isFirstRender = useRef(true); // 标识位:标记是否为首次渲染

  useEffect(() => {
    // 首次渲染时,直接返回,不执行副作用逻辑
    if (isFirstRender.current) {
      isFirstRender.current = false; // 重置标识位,后续渲染不再生效
      return;
    }

    // 非首次渲染(仅 count 变化时),执行副作用逻辑
    console.log(`[取消立即执行] count 发生变化,当前值:${count}`);
  }, [count]);

  return (
    <div style={{ padding: '20px' }}>
      <h3>React 实现 Vue watch(取消立即执行版)</h3>
      <p>count: {count}</p>
      <button onClick={() => setCount(prev => prev + 1)}>
        count + 1
      </button>
    </div>
  );
}

export default ImmediateWatchDemo2;

进阶:封装 Vue 风格的 useWatch 自定义 Hook(提升复用性)

上述方案已经实现了 Vue watch 的核心功能,但在多个组件中使用时会存在重复代码。我们可以封装一个贴近 Vue 写法的 useWatch 自定义 Hook,支持 immediatedeep 两个配置项,提升代码复用性。

完整封装代码
jsx 复制代码
import { useEffect, useRef } from 'react';
import isEqual from 'lodash/isEqual';

// 自定义 Vue 风格 useWatch Hook
function useWatch(watchSource, callback, options = { immediate: false, deep: false }) {
  const { immediate, deep } = options;
  const prevSourceRef = useRef(); // 缓存上一次的监听源数据
  const isFirstRender = useRef(true); // 标记是否为首次渲染

  // 处理监听逻辑
  useEffect(() => {
    // 1. 处理首次渲染立即执行
    if (isFirstRender.current) {
      isFirstRender.current = false;
      if (immediate) {
        callback(watchSource, undefined); // 首次执行:新值为当前监听源,旧值为 undefined
        return;
      }
    }

    // 2. 处理深度对比 vs 浅对比
    const sourceChanged = deep 
      ? !isEqual(watchSource, prevSourceRef.current) 
      : watchSource !== prevSourceRef.current;

    // 3. 监听源变化时,执行回调函数
    if (sourceChanged) {
      callback(watchSource, prevSourceRef.current); // 传递新值和旧值
    }

    // 4. 更新缓存的监听源数据
    prevSourceRef.current = deep ? JSON.parse(JSON.stringify(watchSource)) : watchSource;
  }, [watchSource, callback, immediate, deep]);
}

export default useWatch;
使用示例
jsx 复制代码
import { useState } from 'react';
import useWatch from './useWatch';

function VueStyleWatchDemo() {
  // 定义监听源
  const [user, setUser] = useState({
    name: 'Zhang San',
    age: 25,
    address: {
      city: 'Shenzhen'
    }
  });

  // 使用封装的 useWatch(支持 deep: true + immediate: true)
  useWatch(
    user, // 监听源(对应 Vue watch 的监听目标)
    (newValue, oldValue) => { // 回调函数(对应 Vue watch 的处理函数)
      console.log('[Vue 风格 useWatch] 监听触发');
      console.log('新值:', newValue);
      console.log('旧值:', oldValue);
    },
    { immediate: true, deep: true } // 配置项(对应 Vue watch 的 immediate 和 deep)
  );

  return (
    <div style={{ padding: '20px' }}>
      <h3>React 封装 Vue 风格 useWatch</h3>
      <p>user.name: {user.name}</p>
      <p>user.address.city: {user.address.city}</p>

      <button onClick={() => setUser(prev => ({
        ...prev,
        address: { ...prev.address, city: 'Guangzhou' }
      }))}>
        修改城市为 Guangzhou
      </button>
    </div>
  );
}

export default VueStyleWatchDemo;
说明

该自定义 useWatch Hook 完全贴近 Vue watch 的写法,支持新旧值传递、立即执行和深度监听配置,可直接在项目中复用,同时也是前端面试中的加分项,体现了开发者的 Hook 封装能力和对 React 副作用的理解。

四、React vs Vue 核心对应关系(汇总表)

Vue 特性 React 实现方式 核心匹配点
computed(带缓存) 函数组件 + useMemo Hook 基于依赖缓存,依赖不变时不重复计算,优化性能
computed(无缓存) 普通变量直接赋值(简单计算) 实现基础计算需求,无缓存,写法简洁
watch(基础监听) 函数组件 + useEffect Hook 依赖变化时执行副作用,精准监听单个/多个基础数据
watch(deep: true) 1. 手动罗列深层属性(简单对象) 2. 自定义 useDeepCompareEffect(复杂对象,依赖 lodash.isEqual) 忽略引用地址,监听对象/数组的深层内容变化
watch(immediate: true) 函数组件 + useEffect Hook(默认行为) 首次渲染 + 依赖变化时执行副作用
watch(immediate: false) 函数组件 + useEffect + useRef 标识位 仅依赖变化时执行副作用,首次渲染不执行
完整 Vue 风格 watch 自定义 useWatch Hook(封装 useEffect + 深层对比) 支持 deep、immediate 配置,贴近 Vue 写法,提升复用性

五、总结与落地说明

1. 核心要点回顾

  • React 实现 Vue computed 的核心是 useMemo Hook,其缓存特性与 computed 完全匹配,是复杂计算场景的首选方案;简单计算可直接使用普通变量赋值,兼顾简洁性。
  • React 实现 Vue watch 的基础是 useEffect Hook,通过依赖数组控制副作用执行时机,实现基础监听;深层监听需结合 lodash.isEqual 进行深层数据对比,封装自定义 Hook 提升复用性;立即执行与取消立即执行的核心是通过 useRef 标识位控制首次渲染的逻辑。
  • 所有实现方案均符合 React 官方最佳实践,无额外第三方框架依赖(仅深层监听需引入 lodash),稳定性有保障。

2. 实用价值强调

本文覆盖了日常 React 开发中 99% 的「计算属性」和「数据监听」场景,从基础写法到进阶封装,代码均可直接复制落地。其中,自定义 useDeepCompareEffectuseWatch Hook 不仅能减少项目中的重复代码,还能在前端面试中展现个人的技术深度和工程化思维,是加分的亮点。

3. 落地补充提示

  • 项目中可将 useDeepCompareEffectuseWatch 抽离为公共 Hook(如放在 src/hooks/ 目录下),统一管理和维护,方便所有组件引入使用。
  • 若项目中已引入 lodash,可直接使用 lodash.isEqual;若不想引入完整 lodash,可单独安装 lodash.isEqualnpm install lodash.isEqual),减小打包体积。
  • 对于极致性能优化的场景,可结合 useCallback 缓存回调函数,与 useMemouseEffect 配合使用,进一步减少不必要的组件重渲染。
  • 随着 React 生态的发展,也可选择成熟的第三方 Hook 库(如 react-use),其中已封装了完善的深层监听、数据监听 Hook,快速提升开发效率。

通过本文的学习,相信你已经能够在 React 项目中灵活实现 Vue watchcomputed 的核心功能,从容应对各类响应式场景的开发需求。

相关推荐
谢尔登2 小时前
Vue3 响应式系统——ref 和 reactive
前端·javascript·vue.js
OEC小胖胖2 小时前
16|总复习:把前 15 章串成一张 React 源码主线地图
前端·react.js·前端框架·react·开源库
衫水2 小时前
Ubuntu 系统部署 Vue/Vite 应用到 Nginx
vue.js·nginx·ubuntu
满栀5852 小时前
插件轮播图制作
开发语言·前端·javascript·jquery
切糕师学AI2 小时前
Vue 中的计算属性(computed)
前端·javascript·vue.js
程琬清君2 小时前
Vue3DraggableResizable可移动范围有问题
前端·javascript·vue.js
天天开心a3 小时前
Vue.js 基础教程笔记(一):Vue入门与环境搭建
前端·javascript·vue.js·笔记·前端框架
weixin_446260853 小时前
解锁 React 开发新体验!Puck - 智能可视化编辑器
前端·react.js·编辑器
hzb666663 小时前
xd_day28js原生开发-day31 day41asp.net
开发语言·前端·javascript·安全·web安全