惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园_首页
H
Help Net Security
量子位
The Cloudflare Blog
博客园 - Franky
博客园 - 聂微东
博客园 - 司徒正美
Last Week in AI
Last Week in AI
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Apple Machine Learning Research
Apple Machine Learning Research
宝玉的分享
宝玉的分享
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
有赞技术团队
有赞技术团队
罗磊的独立博客
GbyAI
GbyAI
雷峰网
雷峰网
T
The Blog of Author Tim Ferriss
Martin Fowler
Martin Fowler
S
SegmentFault 最新的问题
美团技术团队
阮一峰的网络日志
阮一峰的网络日志
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
U
Unit 42
MongoDB | Blog
MongoDB | Blog

Surmon.me

一人有限集团 你就是不敢 创造力是温柔的谎言 人类正在退出人类 AI 代替不了这样的你 脉冲点火背后的架构设计 基于 Cloudflare 生态的 AI Agent 实现 NodePress 支持用户登录了 从统计学习到通用智能 2025 投资报告:走慢的路 无依之地 会杀人的菩萨 无我不是共识 文化的积重与偏见 当下即安 科学的尽头是态度 无我不是 Egoless 信仰不因恐惧而存在 世间无解的矛与盾 先别急着做些什么 佛不需要你的皈依 真理的幻觉 两扇大门 造心里的浮屠 自胜者强 逻辑与智慧 真的相 快乐的秘密 只需愿意 是名体验
React 与 Vue 的完美融合
Surmon · 2024-08-16 · via Surmon.me

React Hooks 的心智负担

在 React 中,状态管理一直是开发者关注的重点。React 的 useState 和 useEffect 虽然灵活,但当组件复杂度增加时,这种灵活性往往会变成负担。

最常见的,如依赖关系处理不当会导致 useEffect 中的副作用反复执行或者未按照预期执行,甚至引发内存泄漏。因为 useEffect 本质上是一个副作用管理工具,它要求开发者显式处理依赖关系,增加了代码的复杂性和维护难度,也就构成了我们所说的「心智负担」。即:总有一个地方是需要我特别注意的,不然就可能不符合预期

Vue + TypeScript 的类型挑战

得益于 mutable state 的天然属性,Vue 在处理响应式状态方面表现优异,但在结合 TypeScript 时,开发者常常需要面对类型定义的重复性工作。尤其是当项目中包含大量组件时,手动定义 propsemitsslots 和 attrs 的类型,繁琐、不自然,也不够明了。

有没有既要还要的?

有没有办法将两者的优点融合在一起?最好,它能够包含:

  • 完美的 TypeScript + JSX 编码体验
  • 完美的类型定义, 直接 export interface Props {} 一把嗦
  • 完美的数据管理,再也不要关心 deps 这件事,把数据之间的依赖交给数据自己去组织

有!Veact 来了

Veact  将 Vue 的响应式系统引入 React,使得状态管理更加直观且易于维护。通过 Veact,开发者可以利用 Vue 的响应式系统,同时享受 React 的 JSX + TypeScript 强大类型支持。这种结合不仅减少了状态管理的复杂性,还避免了 React 中常见的副作用处理问题。通过 Veact,状态的变化是自动响应式的,无需手动处理依赖关系,这大大简化了开发者的工作。

你可能有疑问:听起来这和 mobx 之类的 state management 库有啥区别?重新造轮子吗?

由于 Veact 是完全基于  @vue/reactivity 实现的,所以 API 设计几乎与 Vue 完全一致,使用起来非常直观、自然,没有学习成本,维护成本也很低,毕竟大部分的核心工作已经由 Vue 实现了。

话不多说,码上见!

Ref

        
        

1234567891011121314151617

import React from 'react' import { useRef } from 'veact' export const Component: React.FC = () => { const count = useRef(0) const increment = () => { // 数据改变,组件 rerender count.value++ } return ( <div> <p>{count.value}</p> <button onClick={increment}>increment</button> </div> ) }

ShallowRef

        
        

1234567891011121314151617

import React from 'react' import { useShallowRef } from 'veact' export const Component: React.FC = () => { // 对应 Vue 中的 shallowRef const numbers = useShallowRef([1, 2, 3]) const resetNumbers = () => { numbers.value = [] } return ( <div> <p>{numbers.value.length}</p> <button onClick={resetNumbers}>resetNumbers</button> </div> ) }

Reactive

        
        

123456789101112131415161718192021222324252627

import React from 'react' import { useReactive } from 'veact' export const Component: React.FC = () => { // 数据的任何改变,都会导致 rerender const data = useReactive({ count: 10, nested: { count: 1 }, }) const incrementCount = () => { data.count++ } const incrementNestedCount = () => { data.nested.count++ } return ( <div> <p>{data.count}</p> <p>{data.nested.count}</p> <button onClick={incrementCount}>incrementCount</button> <button onClick={incrementNestedCount}>incrementNestedCount</button> </div> ) }

Computed

        
        

12345678910111213141516171819202122232425

import React from 'react' import { useReactive, useComputed } from 'veact' export const Component: React.FC = () => { const data = useReactive({ year: 3, count: 4, }) // Vue 中的 computed,类似于 React 的 useMemo,但不需要指定任何依赖 const total = useComputed(() => { return data.count * data.year }) const incrementCount = () => { data.count++ } return ( <div> <span>{total.value}</span> <button onClick={incrementCount}>incrementCount</button> </div> ) }

Watch

        
        

1234567891011121314151617181920212223242526272829303132

import React from 'react' import { useReactive, useWatch } from 'veact' export const Component: React.FC = () => { const data = useReactive({ count: 0, }) const incrementCount = () => { data.count++ } // Vue 中的 watch,监听数据对象的变化 useWatch(data, (newData) => { console.log('data changed', newData) }) // API 设计实现与 Vue 完全一致 useWatch( () => data.count, (newCount) => { console.log('count changed', newCount) }, ) return ( <div> <span>{data.count}</span> <button onClick={incrementCount}>incrementCount</button> </div> ) }

Reactivity

        
        

1234567891011121314151617181920

import React from 'react' import { ref, useReactivity } from 'veact' // 组件之外的一些响应式数据对象,可能来自于 global state,也可能来自第三方组件库 const countRef = ref(0) export const Component: React.FC = () => { // 通过 useReactivity() 转换后可以直接在组件内消费 const count = useReactivity(() => countRef) const increment = () => { count.value++ } return ( <div> <span>{count.value}</span> <button onClick={increment}>increment</button> </div> ) }

当然,最后还有 Vue 风格的生命周期。

        
        

123456789101112131415161718

import React from 'react' import { onMounted, onBeforeUnmount, onUpdated } from 'veact' export const Component: React.FC = () => { onMounted(() => { console.log('component mounted') }) onUpdated(() => { console.log('component updated') }) onBeforeUnmount(() => { console.log('component will unmount') }) return <div>component</div> }

如码所见,无论是定义响应式状态还是监听状态变化,Veact 都能让开发者在不牺牲灵活性的前提下,享受到更低的心智负担和更高的开发效率。并且 Veact 的数据系统与 React 自身的 state 系统是完全兼容的,并支持接近 100% 的互操作性。这也意味着,你并不需要以某种数据系统为基准对现有应用大改特改,可以从下一次的小页面、小组件开始尝试,如果对项目有益,再深入实践甚至推广到团队也是非常有价值的。

总的来说,Veact 是 Vue 与 React 最佳特性的结合体,它不仅简化了状态管理,还提供了强大的类型支持和直观的 API 设计,为前端开发者提供了一种全新的开发体验。

如果你厌倦了 React 中繁琐的副作用处理,或是困扰于 Vue 中的类型定义工作,那么 Veact 无疑是一个还不错的新选择。

GitHub 项目: https://github.com/veactjs/veact