跪拜 Guibai
← All articles
Vue.js · React.js · Functional Programming

Functional vs Object-Oriented Frontend Architecture: Two Cost Curves

By 濮水大叔 ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Vue teams with large codebases are paying the functional-style tax as a tangle of composables, stores, and provide/inject conventions; Zova's container model is one coherent alternative with a hard SSR correctness argument. React teams get a structural warning: an object-graph state model like Zova's cannot follow React's move toward serializable, concurrently rendered data.

Summary

React's function components are not pure functions: Hooks require a stable Fiber node per component to hold the hooks list, and Vue's setup() runs inside a component instance backed by Proxy objects and ref boxes. Both are object systems wearing function syntax. Zova, an IoC-oriented framework built on Vue, makes the underlying model explicit by turning state into container-managed beans with declared roles, scopes, and lifecycles.

The cost curves differ. A single Vue component costs almost nothing to write, but Vue ships four orthogonal sharing mechanisms: props/emits, composables, provide/inject, and stores. Once dozens of modules and multiple teams share a codebase, all four coexist, conventions multiply linearly, and deviations grow exponentially. Zova front-loads ceremony: every bean declares its container and scope, and in return the four mechanisms collapse into one IoC semantics with ctx, host, app, and sys injection scopes.

Three requirements only containerization handles cleanly: SSR request isolation, interceptable bean method calls, and cross-module contracts via bean identifiers. The honest costs are runtime magic from decorators and metadata, coarser granularity, and a structural conflict with React's RSC and concurrent rendering direction. The closing criterion: anything that must be found, replaced, shared across scopes, or destroyed with a lifecycle is an object; everything else should be a function.

Takeaways
— React's Hooks depend on a fixed call order, so every function component needs a persistent Fiber node to store its hooks list; React components have identity, scheduling, and side-effect cleanup that pure functions do not.
— Vue's setup() is a function, but each component instance is an object; ref is a getter/setter box and reactive is a proxied object graph.
— State identity, scope, and lifecycle ownership is the actual split: closures versus container-managed beans.
— Vue officially ships four orthogonal state-sharing mechanisms: props/emits, composables, provide/inject, and a store layer such as Pinia.
— In functional-style codebases, convention density grows linearly with headcount while deviations grow exponentially, which is the deferred architecture-entropy cost.
— Zova beans declare role, scope, and lifecycle upfront, and all sharing collapses into one IoC mechanism with ctx, host, app, and sys injection scopes.
— SSR demands cross-request state isolation; module-level singletons leak user data, and rebuilding the module graph per request is expensive. Zova models app as the request boundary and sys as cross-request.
— Bean method calls pass through the container, giving AOP, permission injection, and logging a stable interception point; pure function calls are anonymous and unaddressable.
— Cross-module dependencies in Zova depend on bean identifiers rather than provider file paths, so implementations can be swapped without touching consumers.
— Object style pays three costs: decorator-and-metadata runtime magic, coarser granularity that invites over-modeling, and a structural mismatch with React's RSC and concurrent rendering direction.
— Zova itself is a hybrid: Vue reactivity underneath, with functional APIs like $computed and $watch; objects own identity and scope, not computation.
— Anything that must be found, replaced, shared across scopes, or destroyed with its lifecycle is an object; otherwise it should be a function.
Conclusions

The functional-versus-object-oriented label conflates syntax, state host, and architectural ownership; once Fiber nodes and component instances are visible, React and Vue are both object systems, so the debate should move to who manages the objects.

Vue's four sharing mechanisms are the hidden tax of its functional style: the framework never enforces a single path, every team writes its own convention, and entropy arrives exactly when the codebase is too large to refactor cheaply.

The SSR request-scope argument is the strongest case for containerized beans because it is a correctness constraint rather than a preference; one user's data leaking into another request is a bug, not a style tradeoff.

The claim that Zova cannot run on React because beans are non-serializable while RSC wants serializable state is the most consequential structural limit; it ties Zova's future to Vue's component model rather than to any universal architecture principle.

Zova adopting functional APIs while the React ecosystem adopts identity-bearing stores cuts against any claim that either paradigm is inherently superior; both sides are converging on the same split between expression and identity.

Concepts & terms
bean
In Zova, a framework-managed object with an explicit identity, scope, and lifecycle. It lives in an IoC container and is received through injection (for example @Use()) instead of being constructed by the consumer.
architecture entropy
The delayed cost of functional-style state sharing: the number of conventions grows with team size, and the number of violations grows even faster, so complexity accumulates long after individual components look cheap.
injection scope
The container level in Zova at which a bean is shared: ctx for a component instance, host for parent-child lookup, app for application or request scope, and sys for system-wide scope.
SSR request scope
The server-side rendering requirement that state must be isolated per HTTP request. A module-level singleton can leak one user's data into another request; modeling the app container as the request boundary prevents that.
Fiber node
React's persistent internal object behind a function component. It stores the hooks list and component lifecycle information, giving the component an identity that a pure function lacks.
From the discussion
Featured comments
卷土的土 1 likes

State attribution matters more than syntactic form.

See top comments, translated →
Source: juejin.cn ↗ Google Translate ↗ Backup ↗