<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Uncle Pushui</title>
    <description>The latest articles on DEV Community by Uncle Pushui (@uncle-pushui).</description>
    <link>https://dev.to/uncle-pushui</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1868785%2F4af20aa0-38f0-4c64-97c1-4c18ab16e060.png</url>
      <title>DEV Community: Uncle Pushui</title>
      <link>https://dev.to/uncle-pushui</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/uncle-pushui"/>
    <language>en</language>
    <item>
      <title>Why Frontends Still Need Wrapping After Hooks</title>
      <dc:creator>Uncle Pushui</dc:creator>
      <pubDate>Sat, 18 Jul 2026 04:49:19 +0000</pubDate>
      <link>https://dev.to/uncle-pushui/why-frontends-still-need-wrapping-after-hooks-f63</link>
      <guid>https://dev.to/uncle-pushui/why-frontends-still-need-wrapping-after-hooks-f63</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Hooks replaced many inappropriate HOC use cases. They did not remove the need to apply independent rules around an existing rendering target.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When we ask whether HOCs are obsolete, it is easy to mix up two different questions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;How should components reuse &lt;strong&gt;internal&lt;/strong&gt; state, requests, subscriptions, and interaction logic?&lt;/li&gt;
&lt;li&gt;How should an application independently handle props, &lt;code&gt;ref&lt;/code&gt;s, layout, and presentation policies &lt;strong&gt;around the rendering boundary&lt;/strong&gt; of an existing component or native element?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For the first question, a custom Hook is usually the right starting point today. If an HOC only moves &lt;code&gt;useQuery&lt;/code&gt;, &lt;code&gt;useEffect&lt;/code&gt;, or &lt;code&gt;useState&lt;/code&gt; outward and passes the result back through props, it often adds little more than another component layer and prop forwarding.&lt;/p&gt;

&lt;p&gt;The second question did not disappear with Hooks. The key is not whether a few lines of code &lt;em&gt;can&lt;/em&gt; live inside a component. It is this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Should this rule be owned by the target component itself, or should it remain an optional, replaceable, and composable rendering policy?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Hooks are for reusing internal logic
&lt;/h2&gt;

&lt;p&gt;Custom Hooks are the natural modern React mechanism for reusing stateful and side-effectful logic. If several components need to subscribe to online status, for example, that subscription can live in &lt;code&gt;useOnlineStatus()&lt;/code&gt;. Each calling component still decides how to use the result and what UI to render.&lt;/p&gt;

&lt;p&gt;The ownership stays inside the caller's boundary: the component uses the data, handles the interaction, and owns the final output. A Hook does not introduce a new UI boundary, which is why it fits data access, state, subscriptions, and event handling so well.&lt;/p&gt;

&lt;p&gt;This also explains why many classic HOCs now feel cumbersome: they were often used for internal logic reuse that a Hook can express more directly.&lt;/p&gt;

&lt;p&gt;A Hook, however, is not a universal syntax for wrapping rendering elsewhere. If a requirement needs to alter props passed to a target, intercept a &lt;code&gt;ref&lt;/code&gt;, or place the rendered result inside a shared layout, it is dealing with a different boundary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with React: what does an autofocus HOC solve?
&lt;/h2&gt;

&lt;p&gt;Consider an input that should receive focus after it mounts. If we want that behavior to be attachable to multiple input-compatible components, a traditional React HOC might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;withAutoFocus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;WrappedInput&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forwardRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;AutoFocus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;outerRef&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;inputRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;inputRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;focus&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;WrappedInput&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;inputRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

          &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;outerRef&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;outerRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;outerRef&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;outerRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Used like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;AutoFocusInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;withAutoFocus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;TextInput&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AutoFocusInput&lt;/span&gt; &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Enter something"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works, and the requirement it expresses is perfectly valid: &lt;strong&gt;add a “focus after mount” rule to a render target without changing &lt;code&gt;TextInput&lt;/code&gt; itself.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It also exposes the cost of the classic HOC expression:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it creates a new component type;&lt;/li&gt;
&lt;li&gt;if callers need the input node, it must deliberately preserve the outer ref contract;&lt;/li&gt;
&lt;li&gt;the wrapped component must support the relevant ref contract too;&lt;/li&gt;
&lt;li&gt;production HOCs often also need to consider debugging names and static properties.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the useful conclusion is not that HOCs can never be used. It is that &lt;strong&gt;an HOC is rarely the first choice when the goal is only to reuse internal stateful logic; when the rule really belongs at a rendering boundary, it is still describing a real problem.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The decision test: who owns this rule?
&lt;/h2&gt;

&lt;p&gt;When a requirement should be reused, start with three questions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Is it intrinsic behavior of the target component, or a policy needed only in certain contexts?&lt;/li&gt;
&lt;li&gt;Should callers be able to attach, remove, or combine the rule independently?&lt;/li&gt;
&lt;li&gt;Does it need to alter render input, a &lt;code&gt;ref&lt;/code&gt;, output structure, or presentation around the target?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The answers point to different abstractions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the rule is intrinsic to the component, keep it inside the component, using a Hook where appropriate.&lt;/li&gt;
&lt;li&gt;If it must expose a stable, standalone UI contract, make it a normal component.&lt;/li&gt;
&lt;li&gt;If it only transforms data and has no rendering concern, use a pure helper.&lt;/li&gt;
&lt;li&gt;If it intercepts or wraps an existing render target and must compose independently, it needs some form of wrapping capability.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;React HOCs, other wrappers, and Cabloy/Zova Behaviors are different implementations of that last category. They are not the same runtime mechanism, but they all address cases where a rule should not be fused into its target.&lt;/p&gt;

&lt;h2&gt;
  
  
  Zova Behavior: attach a rule to the render target
&lt;/h2&gt;

&lt;p&gt;In Cabloy/Zova, the same autofocus requirement can be expressed with a Behavior:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;bs-behaviors-focus&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; is still just an input. Autofocus is a capability attached to its rendering process. Its essential implementation looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Behavior&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IBehaviorOptionsFocus&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BehaviorFocus&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;BeanBehaviorBase&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;
  &lt;span class="nx"&gt;IBehaviorOptionsFocus&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;IBehaviorPropsInputFocus&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;IBehaviorPropsOutputFocus&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;inputRef&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;HTMLElement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;refOuter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nx"&gt;props&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;always&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inputRef&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;focus&lt;/span&gt;&lt;span class="p"&gt;?.();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inputRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;refOuter&lt;/span&gt;&lt;span class="p"&gt;?.(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It does a small number of things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;receives the current render props;&lt;/li&gt;
&lt;li&gt;preserves the original &lt;code&gt;ref&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;injects a new &lt;code&gt;ref&lt;/code&gt; that calls &lt;code&gt;focus()&lt;/code&gt; once the element is available;&lt;/li&gt;
&lt;li&gt;calls the original &lt;code&gt;ref&lt;/code&gt; as well;&lt;/li&gt;
&lt;li&gt;continues the original render with &lt;code&gt;next(props)&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is direct render-time composition: a Behavior can adjust the input to the next render step, while the target element remains unaware of whether autofocus is needed.&lt;/p&gt;

&lt;p&gt;Behavior is not a “Vue HOC.” Zova is built on Vue runtime and reactivity, but it provides an application architecture based on controllers, beans, and IoC. Behavior is Zova's native facility for render-time interception and composition. The important point here is not its lower-level runtime machinery, but that it expresses autofocus as a rule that can be attached to a target independently.&lt;/p&gt;

&lt;h2&gt;
  
  
  Not only props: wrapping rendered output
&lt;/h2&gt;

&lt;p&gt;Autofocus demonstrates a &lt;strong&gt;before-render&lt;/strong&gt; shape: change the input to the next render step, such as props or a &lt;code&gt;ref&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A second kind of requirement makes the value of wrapping even clearer: form-field layout. A field control owns input, values, and validation, but different pages may need to present it differently: add a label, display validation errors, insert prefix or suffix content, or apply a block or inline layout.&lt;/p&gt;

&lt;p&gt;A layout Behavior can first obtain the field's original rendering result, then add presentation structure around it. Its core shape is like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;vnode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;renderContext&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;fieldset&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;legend&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;legend&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;vnode&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;errorMessage&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;fieldset&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the layout rule is intended only for a native input, a separate layout Behavior can be attached directly to &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt;. For example, define &lt;code&gt;demo-ui:inputLayout&lt;/code&gt; to accept ordinary native props, call &lt;code&gt;const vnode = next(props)&lt;/code&gt;, and return a layout shell. Its usage can be as simple as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;bs-demo-ui-inputLayout&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Enter something"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That attaches &lt;code&gt;demo-ui:inputLayout&lt;/code&gt; to the rendering process of a native &lt;code&gt;input&lt;/code&gt;: the Behavior continues rendering the input, then places its resulting vnode in its own layout structure. It does not change the responsibility of &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt;, and it does not require a dedicated input component for this case.&lt;/p&gt;

&lt;p&gt;Cabloy Basic's existing form-field layout Behavior follows the same “render first, then wrap” pattern, but it runs inside a form-field host and depends on field state and layout configuration. It should therefore be used through &lt;code&gt;ZFormField&lt;/code&gt; and a form provider, not attached directly to an arbitrary &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt;. Native-element use cases need a separate Behavior such as &lt;code&gt;demo-ui:inputLayout&lt;/code&gt; that handles ordinary input props.&lt;/p&gt;

&lt;p&gt;A page can also choose a form-field layout Behavior. For example, the login page selects &lt;code&gt;home-login:formFieldLayoutLogin&lt;/code&gt; through &lt;code&gt;ZForm&lt;/code&gt;'s &lt;code&gt;formProvider&lt;/code&gt;, so every field in that form automatically receives the layout Behavior and renders with a consistent visual style:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ZForm&lt;/span&gt;
  &lt;span class="na"&gt;formProvider&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;behaviors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;FormFieldLayout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;home-login:formFieldLayoutLogin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* fields */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;ZForm&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The useful separation is: &lt;strong&gt;the field owns the field itself; the layout policy owns how that field is wrapped and presented in this page.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If the login layout were hard-coded into every field component, layout policies for different pages would contaminate one another. If every page copied fields and layout code, there would be no shared point of adjustment. A selectable rendering rule preserves the field's stable contract while preserving page-level presentation freedom.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which reuse mechanism should you choose?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Need&lt;/th&gt;
&lt;th&gt;First choice&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Reuse state, data, subscriptions, and interaction inside a component&lt;/td&gt;
&lt;td&gt;Custom Hook&lt;/td&gt;
&lt;td&gt;The logic still belongs to the calling component's own boundary.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Provide stable, standalone UI structure with explicit inputs and outputs&lt;/td&gt;
&lt;td&gt;Component&lt;/td&gt;
&lt;td&gt;The abstraction owns a clear UI contract.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Transform data or normalize options without rendering context&lt;/td&gt;
&lt;td&gt;Pure helper&lt;/td&gt;
&lt;td&gt;No framework-level render composition is needed.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Alter props/&lt;code&gt;ref&lt;/code&gt;s or wrap an existing render target&lt;/td&gt;
&lt;td&gt;HOC / Wrapper / Behavior&lt;/td&gt;
&lt;td&gt;The rule belongs around the target and may need independent attachment and composition.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This table does not mean every visual requirement should become a Behavior. When an abstraction owns a stable interface and interaction contract of its own, a normal component is usually clearer. HOCs, wrappers, and Behaviors matter because they let a rule live around an existing target instead of becoming an inseparable part of that target.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Classic React HOCs can seem obsolete mainly because they were once used to solve many problems that Hooks now solve better.&lt;/p&gt;

&lt;p&gt;Wrapping itself is not obsolete. As long as a requirement is fundamentally about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;intercepting a target's props or &lt;code&gt;ref&lt;/code&gt; before render;&lt;/li&gt;
&lt;li&gt;adding a shared presentation or interaction policy outside the target;&lt;/li&gt;
&lt;li&gt;independently declaring and combining cross-cutting rules;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;there is still a place for HOCs, wrappers, and Zova Behaviors.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What became outdated was not the idea of wrapping. It was the habit of turning every reuse problem into an HOC.\&lt;br&gt;
Hooks are for internal logic reuse; wrapping capabilities attach and compose cross-cutting rules at a rendering boundary.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Further Reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://react.dev/learn/reusing-logic-with-custom-hooks" rel="noopener noreferrer"&gt;React: Reusing Logic with Custom Hooks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://legacy.reactjs.org/docs/higher-order-components.html" rel="noopener noreferrer"&gt;React: Higher-Order Components (legacy documentation)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://react.dev/reference/react/forwardRef" rel="noopener noreferrer"&gt;React: &lt;code&gt;forwardRef&lt;/code&gt; reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.cabloy.com/frontend/behavior-guide" rel="noopener noreferrer"&gt;Cabloy: Behavior Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/cabloy/cabloy/blob/main/zova/src/suite-vendor/a-zova/modules/a-behaviors/src/bean/behavior.focus.ts" rel="noopener noreferrer"&gt;BehaviorFocus source&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/cabloy/cabloy/blob/main/zova/src/suite/cabloy-basic/modules/basic-form/src/bean/behavior.formFieldLayout.tsx" rel="noopener noreferrer"&gt;Form-field layout Behavior source&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/cabloy/cabloy/blob/main/zova/src/suite/a-home/modules/home-login/src/page/login/render.tsx" rel="noopener noreferrer"&gt;Login-page form-field layout selection&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>react</category>
      <category>hooks</category>
      <category>hoc</category>
      <category>zova</category>
    </item>
    <item>
      <title>Cabloy Form Layout: Organize Complex Forms in DTOs with Tabs, Groups, and Sections</title>
      <dc:creator>Uncle Pushui</dc:creator>
      <pubDate>Sat, 18 Jul 2026 02:04:16 +0000</pubDate>
      <link>https://dev.to/uncle-pushui/cabloy-form-layout-organize-complex-forms-in-dtos-with-tabs-groups-and-sections-3811</link>
      <guid>https://dev.to/uncle-pushui/cabloy-form-layout-organize-complex-forms-in-dtos-with-tabs-groups-and-sections-3811</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Automatically rendered forms work well when there are only a few fields. As a form grows to include profile information, contact details, and business records, structure starts to matter: how should fields be grouped? Which fields should sit side by side? Which content belongs in a separate tab?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Cabloy Form Layout is designed for exactly this. It lets you describe the placement and hierarchy of multiple form fields in a DTO, then lets the frontend render that structure from the configuration.&lt;/p&gt;

&lt;p&gt;Put simply: &lt;strong&gt;Form Layout decides where fields go. The existing schema and form configuration still decide field types, labels, validation, and other field behavior.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What Form Layout Can Do
&lt;/h2&gt;

&lt;p&gt;With Form Layout, you can configure common form requirements in a DTO:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Change the order in which fields appear.&lt;/li&gt;
&lt;li&gt;Put related fields into a titled business group.&lt;/li&gt;
&lt;li&gt;Use multiple columns on wider screens and a single column on smaller screens.&lt;/li&gt;
&lt;li&gt;Split independent content, such as Basic Information and Training Records, into separate tabs.&lt;/li&gt;
&lt;li&gt;Apply the same structural approach across create, update, and view forms.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The main nodes are straightforward:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;field&lt;/code&gt;: places one field.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;section&lt;/code&gt;: arranges fields in one or more columns.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;group&lt;/code&gt;: organizes related fields together.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tabs&lt;/code&gt; / &lt;code&gt;tab&lt;/code&gt;: divides separate content areas into tabs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Example: Structuring the Student Form
&lt;/h2&gt;

&lt;p&gt;The following Student Create DTO layout is slightly simplified for illustration. It focuses on the Form Layout portion and omits the outer DTO definition, page actions, and other field configuration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;ZovaRender&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;block&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;basic-form:blockFormLayout&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;formLayout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tabs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tab&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;$locale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;BasicInformation&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
              &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;group&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;$locale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;StudentProfile&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                  &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;section&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;md&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
                    &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;field&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
                      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;field&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mobile&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
                      &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;field&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;imageId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="na"&gt;span&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;md&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
                      &lt;span class="p"&gt;},&lt;/span&gt;
                    &lt;span class="p"&gt;],&lt;/span&gt;
                  &lt;span class="p"&gt;},&lt;/span&gt;
                &lt;span class="p"&gt;],&lt;/span&gt;
              &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tab&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;$locale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;TrainingRecords&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
              &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;field&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;level&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
              &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;field&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;trainingRecords&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read the example from the outside in to see what it produces.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. &lt;code&gt;tabs&lt;/code&gt; and &lt;code&gt;tab&lt;/code&gt;: Split the Form by Business Area
&lt;/h3&gt;

&lt;p&gt;The outer &lt;code&gt;tabs&lt;/code&gt; node creates a tabbed area. It contains two &lt;code&gt;tab&lt;/code&gt; nodes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;BasicInformation&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TrainingRecords&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tabs help readers focus on one area at a time when a form contains independent business content. If a form has only a few fields, a simple group is usually easier to scan than a tabbed interface.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. &lt;code&gt;group&lt;/code&gt;: Give Related Fields a Clear Context
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;group&lt;/code&gt; in the Basic Information tab is titled &lt;code&gt;StudentProfile&lt;/code&gt;. It puts the student's name, mobile number, and image under one shared business context.&lt;/p&gt;

&lt;p&gt;Think of a &lt;code&gt;group&lt;/code&gt; as a titled grouping for related fields. Contact details, shipping addresses, and approval information are all common examples.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. &lt;code&gt;section&lt;/code&gt;: Control Responsive Columns
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;section&lt;/code&gt; controls the field grid. &lt;code&gt;columns: { default: 1, md: 2 }&lt;/code&gt; means one column by default and two columns at the &lt;code&gt;md&lt;/code&gt; breakpoint and above.&lt;/p&gt;

&lt;p&gt;As a result, &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;mobile&lt;/code&gt; can appear side by side on wider screens and naturally stack on smaller screens.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. &lt;code&gt;field&lt;/code&gt;: Reference Fields Already Defined in the DTO Schema
&lt;/h3&gt;

&lt;p&gt;Each &lt;code&gt;field&lt;/code&gt; uses &lt;code&gt;name&lt;/code&gt; to reference a field that already exists in the DTO schema, such as &lt;code&gt;name&lt;/code&gt;. Its widget, label, and validation rules continue to use the field's existing configuration.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;imageId&lt;/code&gt; also uses &lt;code&gt;span: { default: 1, md: 2 }&lt;/code&gt;. It takes one column in the default layout and both columns in the two-column layout. This is useful for an image, note, or description that should occupy a full row.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing the Right Node
&lt;/h2&gt;

&lt;p&gt;Start with the smallest structure that expresses what the form needs:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Requirement&lt;/th&gt;
&lt;th&gt;Use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Only change field order&lt;/td&gt;
&lt;td&gt;&lt;code&gt;field&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Show fields side by side on wider screens&lt;/td&gt;
&lt;td&gt;&lt;code&gt;section&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Give related fields a shared title and boundary&lt;/td&gt;
&lt;td&gt;&lt;code&gt;group&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Separate genuinely independent business content&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;tabs&lt;/code&gt; / &lt;code&gt;tab&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A common composition is: &lt;strong&gt;Tabs divide business areas, Groups express business context, Sections handle responsive columns, and Fields place individual fields.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You do not need every container for every form. Use &lt;code&gt;field&lt;/code&gt; alone for a simple form, add a &lt;code&gt;section&lt;/code&gt; when columns help, and add a &lt;code&gt;group&lt;/code&gt; only when fields truly belong to the same business block.&lt;/p&gt;

&lt;h2&gt;
  
  
  One Easy-to-Miss Detail
&lt;/h2&gt;

&lt;p&gt;Form Layout controls field &lt;strong&gt;placement&lt;/strong&gt;, not field visibility.&lt;/p&gt;

&lt;p&gt;If a field should not appear, configure its visibility in the field's schema metadata. Leaving it out of &lt;code&gt;formLayout&lt;/code&gt; does not necessarily hide it; an eligible visible field can still appear at the end of the form.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Form Layout lets a DTO describe the structure of a complex form:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;field&lt;/code&gt; to set field order.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;section&lt;/code&gt; for responsive columns.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;group&lt;/code&gt; for business grouping.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;tabs&lt;/code&gt; / &lt;code&gt;tab&lt;/code&gt; to separate independent content.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key division of responsibility is simple: &lt;strong&gt;DTO metadata describes the form structure; the schema and form runtime continue to govern each field's behavior.&lt;/strong&gt; When fields change, update the layout in the DTO instead of maintaining field placement repeatedly across multiple pages.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further Reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.cabloy.com/frontend/form-layout-guide" rel="noopener noreferrer"&gt;Form Layout Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.cabloy.com/frontend/form-guide" rel="noopener noreferrer"&gt;Form Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/cabloy/cabloy/blob/be077b1efcd0fef8b7842e14932b07f7efc2bcc5/vona/src/suite/a-training/modules/training-student/src/dto/studentCreate.tsx" rel="noopener noreferrer"&gt;Student Create DTO example&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>cabloy</category>
      <category>zova</category>
      <category>dto</category>
      <category>dynamicforms</category>
    </item>
    <item>
      <title>How to Bridge Login State Across SSR and CSR: Next.js, Nuxt, and Cabloy Compared</title>
      <dc:creator>Uncle Pushui</dc:creator>
      <pubDate>Mon, 13 Jul 2026 02:00:38 +0000</pubDate>
      <link>https://dev.to/uncle-pushui/how-to-bridge-login-state-across-ssr-and-csr-nextjs-nuxt-and-cabloy-compared-oee</link>
      <guid>https://dev.to/uncle-pushui/how-to-bridge-login-state-across-ssr-and-csr-nextjs-nuxt-and-cabloy-compared-oee</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Supporting login state across SSR and CSR is not merely a question of whether a cookie travels with the request. The real work is verifying identity on the server, reusing that result for the first client render, recovering a good browser experience later, and still authorizing every protected operation on the server.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Start with three minimal implementations
&lt;/h2&gt;

&lt;p&gt;Before discussing abstractions, here are representative ways that Next.js, Nuxt, and Cabloy carry &lt;strong&gt;server-verified login state&lt;/strong&gt; into the first client render.&lt;/p&gt;

&lt;p&gt;All three examples deliberately expose only a UI-safe public DTO. A raw session token, refresh token, password hash, and server-internal fields do not belong in a hydration payload.&lt;/p&gt;

&lt;h3&gt;
  
  
  Next.js App Router: a Server Component passes a safe DTO explicitly
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/types/session.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;PublicSession&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="nl"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// app/lib/session.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;server-only&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;cookies&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next/headers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PublicSession&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@/app/types/session&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getPublicSession&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;PublicSession&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;cookies&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;session&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)?.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;verifySessionOnServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/dashboard/page.tsx — Server Component&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DashboardClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./dashboard-client&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getPublicSession&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@/app/lib/session&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;DashboardPage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;initialSession&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getPublicSession&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DashboardClient&lt;/span&gt; &lt;span class="na"&gt;initialSession&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;initialSession&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/dashboard/dashboard-client.tsx&lt;/span&gt;
&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;use client&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PublicSession&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@/app/types/session&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;DashboardClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;initialSession&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;initialSession&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PublicSession&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;initialSession&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/login"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Sign in&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Hello, &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;initialSession&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A protected operation still establishes identity and authorizes the exact resource on the server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/lib/projects.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;server-only&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getPublicSession&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./session&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getProjectForViewer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;projectId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getPublicSession&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Unauthenticated&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;assertCanReadProject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;projectId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;project&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findUniqueOrThrow&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;projectId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Nuxt: a server session endpoint plus a &lt;code&gt;useFetch&lt;/code&gt; payload
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// server/api/session.get.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getCookie&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;h3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineEventHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getCookie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;session&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;verifySessionOnServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// composables/useSession.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;useSession&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;useFetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/session&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;session&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- app.vue or a layout --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt; &lt;span class="na"&gt;setup&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"ts"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;useSession&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;AccountMenu&lt;/span&gt; &lt;span class="na"&gt;:session=&lt;/span&gt;&lt;span class="s"&gt;"session"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Cabloy: server &lt;code&gt;mem&lt;/code&gt;, client &lt;code&gt;local&lt;/code&gt;, bridged by the Model Query Cache
&lt;/h3&gt;

&lt;p&gt;This is the current shape of Cabloy Basic's &lt;code&gt;ModelPassport&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// zova/src/suite/a-home/modules/home-passport/src/model/passport.ts&lt;/span&gt;
&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;passport&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CLIENT&lt;/span&gt;
  &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;$useStateLocal&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;queryKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;passport&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;$useStateMem&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;queryKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;passport&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server confirms the passport using the authentication information for the current request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;ensurePassport&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CLIENT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;passport&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ssr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cookieDisabledOnServer&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isAuthenticated&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;passport&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;homeUserPassport&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_setLocaleTz&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;passport&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The surface APIs differ, but the shared principle is the same:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The browser receives a server-confirmed, UI-safe projection of login state. Authentication and authorization decisions remain on the server.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  What each implementation is actually solving
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Next.js: the Server Component is the start of the bridge
&lt;/h3&gt;

&lt;p&gt;The natural boundary in the Next.js App Router is between Server Components and Client Components.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HttpOnly Cookie
  → server-only session verification
  → Server Component
  → serializable initialSession prop
  → Client Component hydration
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;cookies()&lt;/code&gt; is read along a server execution path. &lt;code&gt;getPublicSession()&lt;/code&gt; reads the request cookie, verifies the session, and constructs a small, explicit &lt;code&gt;PublicSession&lt;/code&gt; DTO. The Server Component then passes it as &lt;code&gt;initialSession&lt;/code&gt; to the Client Component.&lt;/p&gt;

&lt;p&gt;Two details matter:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Client Component receives a DTO, not the cookie or raw session.&lt;/strong&gt; &lt;code&gt;HttpOnly&lt;/code&gt; exists specifically to keep the credential unavailable to browser JavaScript.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;initialSession&lt;/code&gt; solves first-render consistency.&lt;/strong&gt; It avoids a server-rendered signed-in UI becoming a signed-out client UI during hydration. It does not authorize a later Server Action, Route Handler, or data-access operation.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Next.js does not decide your global client session store for you. The application may put &lt;code&gt;initialSession&lt;/code&gt; in Context, a state store, a Query cache, or pass it farther through the component tree. That explicitness is a feature: the application owns both the session DTO and its later client-state policy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Nuxt: keyed async data bridges the SSR payload
&lt;/h3&gt;

&lt;p&gt;In Nuxt, the primary bridge is commonly &lt;code&gt;useFetch&lt;/code&gt; or &lt;code&gt;useAsyncData&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HttpOnly Cookie
  → /api/session server endpoint
  → useFetch('/api/session', { key: 'session' })
  → Nuxt SSR payload
  → Client hydration reuses payload
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server &lt;code&gt;/api/session&lt;/code&gt; endpoint reads and validates the request cookie, then returns only public display data. A page or layout calls the same &lt;code&gt;useFetch&lt;/code&gt; path. On SSR, the result enters the Nuxt payload; during initial hydration, the client reuses the result under that async-data identity instead of treating the same session read as an independent request.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;key: 'session'&lt;/code&gt; provides stable identity for this async data. It also makes later refreshes such as &lt;code&gt;refreshNuxtData('session')&lt;/code&gt; possible. It is &lt;strong&gt;not&lt;/strong&gt; a permission key and does not authorize any protected endpoint.&lt;/p&gt;

&lt;p&gt;Projects that want a fuller Nuxt session abstraction can evaluate the official &lt;code&gt;nuxt-auth-utils&lt;/code&gt; module and its &lt;code&gt;useUserSession()&lt;/code&gt;, &lt;code&gt;setUserSession()&lt;/code&gt;, and &lt;code&gt;requireUserSession()&lt;/code&gt; APIs. This article uses &lt;code&gt;/api/session&lt;/code&gt; plus &lt;code&gt;useFetch&lt;/code&gt; because it exposes the fundamental SSR-payload-to-client-reuse chain directly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cabloy: Model-owned Query Cache identity is the bridge
&lt;/h3&gt;

&lt;p&gt;Cabloy's &lt;code&gt;ModelPassport&lt;/code&gt; uses different helpers for the initial SSR source and the browser persistence policy, then joins them through the same effective Query Key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;request cookie / token
  → ModelPassport.ensurePassport()
  → server $useStateMem(['passport'])
  → request-scoped Query Cache
  → dehydrate after render
  → client QueryClient hydrate
  → client $useStateLocal(['passport'])
  → Query Cache first, localStorage fallback
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server selects &lt;code&gt;$useStateMem(...)&lt;/code&gt; because it has no browser &lt;code&gt;localStorage&lt;/code&gt;, and the identity for this request must be confirmed through the request credential and &lt;code&gt;ensurePassport()&lt;/code&gt;. A successful memory Query Cache entry that passes the dehydration policy can enter the SSR snapshot.&lt;/p&gt;

&lt;p&gt;The client selects &lt;code&gt;$useStateLocal(...)&lt;/code&gt; because it must both reuse the initial SSR snapshot and support CSR-only entry or browser-session recovery when no snapshot exists. &lt;code&gt;$useStateLocal(...)&lt;/code&gt; checks the Query Cache first. When a hydration entry exists, the server-confirmed passport wins. Only when no cache value exists does it attempt synchronous &lt;code&gt;localStorage&lt;/code&gt; restoration and then fall back to &lt;code&gt;meta.defaultData&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Two details are frequently misunderstood:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The dehydrated entry is the Query entry created by server &lt;code&gt;$useStateMem(...)&lt;/code&gt;, not the client &lt;code&gt;$useStateLocal(...)&lt;/code&gt; wrapper.&lt;/li&gt;
&lt;li&gt;Hydration restores the Query Cache. Reading that value does not automatically save it to &lt;code&gt;localStorage&lt;/code&gt;; a later assignment through the client local-state wrapper updates the cache and writes the local record.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also, &lt;code&gt;['passport']&lt;/code&gt; is not a global key. Zova prefixes the key with Model identity, and selector-enabled Models include selector identity as well. Reuse therefore requires the same Model, applicable selector, and logical &lt;code&gt;queryKey&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  One table for the three bridge designs
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;Next.js App Router&lt;/th&gt;
&lt;th&gt;Nuxt&lt;/th&gt;
&lt;th&gt;Cabloy&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Server identity confirmation&lt;/td&gt;
&lt;td&gt;Server-only code / Server Component reads and verifies Cookie&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;/api/session&lt;/code&gt; endpoint reads and verifies Cookie&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ModelPassport.ensurePassport()&lt;/code&gt; confirms the passport for the current request&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Initial client-state carrier&lt;/td&gt;
&lt;td&gt;Explicit &lt;code&gt;initialSession&lt;/code&gt; prop through the RSC payload&lt;/td&gt;
&lt;td&gt;Nuxt payload from &lt;code&gt;useFetch&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Dehydrated and hydrated Model Query Cache entry&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Later client-state policy&lt;/td&gt;
&lt;td&gt;Context, store, or Query policy chosen by the application&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;useFetch&lt;/code&gt; cache; higher-level auth/state policy chosen by the application&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;$useStateLocal&lt;/code&gt; restores &lt;code&gt;localStorage&lt;/code&gt; only when hydrated cache is absent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Identity rule&lt;/td&gt;
&lt;td&gt;The Server/Client Component props contract&lt;/td&gt;
&lt;td&gt;Async-data key, such as &lt;code&gt;session&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Model identity + selector when applicable + logical &lt;code&gt;queryKey&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Protected server operations&lt;/td&gt;
&lt;td&gt;Server Actions, Route Handlers, and DAL authorize independently&lt;/td&gt;
&lt;td&gt;Server APIs, handlers, and services authorize independently&lt;/td&gt;
&lt;td&gt;Vona passport guard and business permission logic authorize independently&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Main architectural emphasis&lt;/td&gt;
&lt;td&gt;Server/client component boundary and safe DTOs&lt;/td&gt;
&lt;td&gt;SSR-aware fetching and payload reuse&lt;/td&gt;
&lt;td&gt;Model ownership, Query Cache identity, and helper lifecycle&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;None of these designs "hydrates a cookie into the browser." A cookie is a request credential. Hydrated props, a Nuxt payload, and a Model Query Cache entry are derived state for rendering and interaction.&lt;/p&gt;




&lt;h2&gt;
  
  
  The real security boundary: client login state improves UX only
&lt;/h2&gt;

&lt;p&gt;Many implementations go wrong here: a page can render the user name and hide buttons by role, so the client state is treated as an authorization result.&lt;/p&gt;

&lt;p&gt;It is not.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. An &lt;code&gt;HttpOnly&lt;/code&gt; cookie is not client state
&lt;/h3&gt;

&lt;p&gt;Browser JavaScript should not read an &lt;code&gt;HttpOnly&lt;/code&gt; cookie. The browser attaches it to requests within the cookie's scope; the server then validates its signature, expiry, session record, revocation state, or user status.&lt;/p&gt;

&lt;p&gt;Do not conflate these two channels:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cookie / session credential
  → server-side authentication input

hydrated public session DTO / passport cache
  → client rendering input
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. A client route guard is a UX guard, not an authorization point
&lt;/h3&gt;

&lt;p&gt;A client-side guard can redirect early, avoid a visible unauthorized page, or hide unavailable controls. A caller can still issue an HTTP request directly and can still alter display state in browser memory.&lt;/p&gt;

&lt;p&gt;Every protected Server Action, Route Handler, Nuxt server API, Vona Controller/service, or equivalent operation must independently:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;establish the current identity from request authentication data;&lt;/li&gt;
&lt;li&gt;validate that the user, tenant, and session remain valid;&lt;/li&gt;
&lt;li&gt;authorize the exact action against the exact resource;&lt;/li&gt;
&lt;li&gt;validate its input.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  3. A smaller DTO is both safer and more stable
&lt;/h3&gt;

&lt;p&gt;The first client render commonly needs no more than:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;avatar&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;admin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It does not need a raw session, refresh token, upstream API token, password field, internal audit field, or an entire database user record. A safe DTO reduces both direct exposure and accidental leakage as server-side data models evolve.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Stale display state is normal
&lt;/h3&gt;

&lt;p&gt;Logout, expiration, role revocation, tenant changes, and a logout on another device can all make a browser's previous login-state projection stale.&lt;/p&gt;

&lt;p&gt;The objective of an SSR/CSR bridge is therefore:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Make the first paint and ordinary interactions coherent, not give the client a permanently trusted copy of identity.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Cookie configuration should match the application: &lt;code&gt;HttpOnly&lt;/code&gt;, &lt;code&gt;Secure&lt;/code&gt;, &lt;code&gt;SameSite&lt;/code&gt;, &lt;code&gt;path&lt;/code&gt;, expiry, and rotation strategy all matter. Cookie-authenticated state-changing requests also need a CSRF approach appropriate to the deployment, cross-site requirements, embedded contexts, and session architecture.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Cabloy's bridge deserves separate attention
&lt;/h2&gt;

&lt;p&gt;The Next.js and Nuxt examples usually keep one visible application abstraction across the boundary:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Next: server DTO → client prop / provider
Nuxt: server endpoint → same useFetch key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cabloy's distinguishing feature is that the same business field can deliberately use different state helpers on server and client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SSR Server: $useStateMem(['passport'])
SSR Client: $useStateLocal(['passport'])
Bridge: Model-owned effective Query Cache identity
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This separates two needs instead of conflating them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;mem&lt;/code&gt; means request-confirmed runtime state for the current SSR pass, without a browser storage backend;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;local&lt;/code&gt; means client state that can persist and recover in the browser, without overriding a hydrated server conclusion;&lt;/li&gt;
&lt;li&gt;the common effective key joins those paths during the first hydration.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The trade-off is equally explicit. Developers must understand Model ownership, Query Cache lifecycle, dehydrate/hydrate behavior, selectors, and helper families rather than treating this as an ordinary Vue &lt;code&gt;ref&lt;/code&gt; or one global store. For applications where SSR, caching, persistence, and resource models coexist, that explicit ownership can be easier to sustain than an accumulation of local conventions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common mistakes and better formulations
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Misleading shortcut&lt;/th&gt;
&lt;th&gt;More accurate formulation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;"Next.js hydrates the session cookie into React."&lt;/td&gt;
&lt;td&gt;Next.js reads the Cookie in a Server Component or server-only path and passes a serializable safe DTO to a Client Component.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"Nuxt &lt;code&gt;useCookie&lt;/code&gt; lets the client read an &lt;code&gt;HttpOnly&lt;/code&gt; login cookie."&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;useCookie&lt;/code&gt; is SSR-aware, but browser JavaScript still cannot read an &lt;code&gt;HttpOnly&lt;/code&gt; cookie; the client should consume a safe DTO returned by the server.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"A &lt;code&gt;useFetch&lt;/code&gt; key is a permission key."&lt;/td&gt;
&lt;td&gt;The key identifies async data and payload/cache reuse. The server still authorizes requests from their credentials and target resources.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"Cabloy's &lt;code&gt;$useStateLocal&lt;/code&gt; is dehydrated directly by the server."&lt;/td&gt;
&lt;td&gt;In this flow, the server &lt;code&gt;$useStateMem&lt;/code&gt; entry is dehydrated; the client &lt;code&gt;$useStateLocal&lt;/code&gt; wrapper reuses hydrated cache through the same effective key.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"An SSR snapshot automatically persists to localStorage."&lt;/td&gt;
&lt;td&gt;SSR hydration restores initial cache. Cabloy saves browser-local state through later assignments to the local-state wrapper.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"An API is safe because the UI hides its button by role."&lt;/td&gt;
&lt;td&gt;UI control is an experience layer; every protected server operation must authenticate, authorize, and validate input independently.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  When each structure fits
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Choose an explicit Next.js DTO bridge
&lt;/h3&gt;

&lt;p&gt;This fits applications centered on the App Router and Server Components that want precise control over every server-to-client boundary. The exposure surface is highly visible; the trade-off is that the application chooses and maintains its own client session provider, cache, and refresh policy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Choose a keyed Nuxt &lt;code&gt;useFetch&lt;/code&gt; bridge
&lt;/h3&gt;

&lt;p&gt;This fits Nuxt applications centered on server APIs and SSR data fetching. It offers SSR-payload reuse with little plumbing; the trade-off is that a team must keep async-data keys, endpoint response shapes, and refresh/invalidation policy disciplined as the application grows.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use Cabloy's &lt;code&gt;ModelPassport&lt;/code&gt; bridge
&lt;/h3&gt;

&lt;p&gt;This fits Cabloy/Zova Model applications that want SSR handoff, client cache identity, and browser persistence fallback under one Model-owned state runtime. It provides an existing passport bridge pattern; the trade-off is following the architectural rules around Model/selector/Query Key identity and helper lifecycle.&lt;/p&gt;

&lt;p&gt;Whichever framework you choose, the stable final principle is unchanged:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The server verifies identity, the client reuses display state, and protected server operations authorize again.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  References and source material
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Next.js
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://nextjs.org/docs/app/guides/authentication" rel="noopener noreferrer"&gt;Next.js Authentication&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nextjs.org/docs/app/api-reference/functions/cookies" rel="noopener noreferrer"&gt;Next.js &lt;code&gt;cookies()&lt;/code&gt; API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nextjs.org/docs/app/getting-started/server-and-client-components" rel="noopener noreferrer"&gt;Next.js Server and Client Components&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nextjs.org/docs/app/guides/data-security" rel="noopener noreferrer"&gt;Next.js Data Security&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Nuxt
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://nuxt.com/docs/4.x/api/composables/use-fetch" rel="noopener noreferrer"&gt;Nuxt &lt;code&gt;useFetch&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nuxt.com/docs/4.x/getting-started/data-fetching" rel="noopener noreferrer"&gt;Nuxt Data Fetching&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nuxt.com/docs/4.x/api/composables/use-request-fetch" rel="noopener noreferrer"&gt;Nuxt &lt;code&gt;useRequestFetch&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nuxt.com/docs/4.x/api/composables/use-cookie" rel="noopener noreferrer"&gt;Nuxt &lt;code&gt;useCookie&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nuxt.com/docs/3.x/guide/recipes/sessions-and-authentication" rel="noopener noreferrer"&gt;Nuxt Sessions and Authentication&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cabloy
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/cabloy/cabloy/blob/5d2aeab0c994fb8e222088ebceebc4c9ab3af649/zova/src/suite/a-home/modules/home-passport/src/model/passport.ts" rel="noopener noreferrer"&gt;ModelPassport source&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/cabloy/cabloy/blob/5d2aeab0c994fb8e222088ebceebc4c9ab3af649/cabloy-docs/frontend/model-state-guide.md" rel="noopener noreferrer"&gt;Model State Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/cabloy/cabloy/blob/5d2aeab0c994fb8e222088ebceebc4c9ab3af649/cabloy-docs/frontend/ssr-init-data.md" rel="noopener noreferrer"&gt;SSR Init Data&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/cabloy/cabloy/blob/5d2aeab0c994fb8e222088ebceebc4c9ab3af649/cabloy-docs/frontend/a-model-under-the-hood.md" rel="noopener noreferrer"&gt;A-Model Under the Hood&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ssr</category>
      <category>csr</category>
      <category>nextjs</category>
      <category>nuxt</category>
    </item>
    <item>
      <title>How to Manage Complex State in Large Vue Projects: "Scattered" vs "Systematic"</title>
      <dc:creator>Uncle Pushui</dc:creator>
      <pubDate>Mon, 06 Jul 2026 07:40:04 +0000</pubDate>
      <link>https://dev.to/uncle-pushui/how-to-manage-complex-state-in-large-vue-projects-scattered-vs-systematic-3e9n</link>
      <guid>https://dev.to/uncle-pushui/how-to-manage-complex-state-in-large-vue-projects-scattered-vs-systematic-3e9n</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Large frontend systems rarely hit their limit because they chose the wrong individual state tool. They hit it when ownership, sharing boundaries, cache semantics, persistence, and SSR rules no longer compose into one coherent system.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The problem is not tool shortage. It is boundary drift.
&lt;/h2&gt;

&lt;p&gt;If I had to compress the entire argument into one sentence, it would be this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Large Vue projects usually do not become difficult because they need one more library. They become difficult because state mechanisms keep multiplying while state boundaries keep losing clarity.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is the real subject of this article.&lt;/p&gt;

&lt;p&gt;Not whether Pinia is sufficient.&lt;br&gt;
Not whether composables are overused.&lt;br&gt;
Not whether Vue itself is somehow inadequate.&lt;/p&gt;

&lt;p&gt;The real problem is more structural than that.&lt;/p&gt;

&lt;p&gt;In a large codebase, the usual mechanisms all solve legitimate local problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;local reactive state,&lt;/li&gt;
&lt;li&gt;composables,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;provide/inject&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;stores,&lt;/li&gt;
&lt;li&gt;query cache,&lt;/li&gt;
&lt;li&gt;local persistence,&lt;/li&gt;
&lt;li&gt;SSR hydration rules.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Any one of those choices can be correct.&lt;/p&gt;

&lt;p&gt;And still, taken together, they can produce a state layer that feels progressively less stable.&lt;/p&gt;

&lt;p&gt;Not because one piece is obviously wrong, but because the whole system gradually loses shape.&lt;/p&gt;

&lt;p&gt;That is what I mean by the &lt;strong&gt;scattered&lt;/strong&gt; version.&lt;/p&gt;

&lt;p&gt;Not a broken system. Not an amateur system. Not even an irrational one.&lt;/p&gt;

&lt;p&gt;A scattered system is one where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;each local decision can be justified,&lt;/li&gt;
&lt;li&gt;each mechanism solves a real problem,&lt;/li&gt;
&lt;li&gt;but the overall state architecture becomes harder and harder to explain, modify, and trust.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What Zova/Cabloy tries to offer is not a replacement for Vue, but a more &lt;strong&gt;systematic&lt;/strong&gt; way to organize state on top of Vue:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bean / Controller&lt;/strong&gt; defines ownership,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IoC Scope&lt;/strong&gt; defines how far state is shared,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Model&lt;/strong&gt; defines a unified state layer,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resource Owner&lt;/strong&gt; defines the boundary for resource-level semantics such as queries, schema, permissions, and invalidation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the project is small, short-lived, or handled by a very small team, the scattered version may be perfectly adequate.&lt;/p&gt;

&lt;p&gt;But once the system becomes long-lived, multi-person, SSR-aware, cache-heavy, and persistence-heavy, the difference between scattered and systematic stops being a matter of taste.&lt;/p&gt;

&lt;p&gt;It becomes a question of architecture.&lt;/p&gt;

&lt;p&gt;And that leads to the real question underneath the tooling discussion:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Should complex state continue to be held together mainly by team discipline, or should more of it be held together by structure?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  The decisive questions sit upstream of tooling
&lt;/h2&gt;

&lt;p&gt;Most state-management discussions begin at the tool layer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Should we use Pinia?&lt;/li&gt;
&lt;li&gt;Should we add Query?&lt;/li&gt;
&lt;li&gt;How should we persist local state?&lt;/li&gt;
&lt;li&gt;How should we handle SSR hydration?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those are sensible questions.&lt;/p&gt;

&lt;p&gt;But in large systems, they are not the first questions.&lt;/p&gt;

&lt;p&gt;The first questions are these:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ownership&lt;/strong&gt; — which object actually owns this state?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sharing boundary&lt;/strong&gt; — is it local, parent/child shared, app-level, or system-level?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;State family&lt;/strong&gt; — is it server state, in-memory state, local persistence, cookie-backed state, or async persistent state?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache semantics&lt;/strong&gt; — who owns the key, invalidation, and restore rules?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lifecycle&lt;/strong&gt; — what survives disposal, refresh, route transitions, and SSR request boundaries?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resource semantics&lt;/strong&gt; — is this just a value, or part of a larger resource boundary that also owns schema, permissions, queries, forms, and actions?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When those questions are answered by different local mechanisms, the result is usually familiar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;some state lives in pages,&lt;/li&gt;
&lt;li&gt;some lives in composables,&lt;/li&gt;
&lt;li&gt;some lives in stores,&lt;/li&gt;
&lt;li&gt;some lives in query cache,&lt;/li&gt;
&lt;li&gt;some lives in persistence wrappers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The application still works.&lt;/p&gt;

&lt;p&gt;But the architecture becomes progressively harder to narrate.&lt;/p&gt;

&lt;p&gt;And once a system becomes hard to narrate, it usually becomes hard to evolve safely.&lt;/p&gt;

&lt;p&gt;That is the real failure mode of state management in large projects.&lt;/p&gt;

&lt;p&gt;Not that a feature cannot be built.&lt;/p&gt;

&lt;p&gt;But that the team gradually loses a clean answer to the most important question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why does this state live here, why is this mechanism responsible for it, and where exactly does its boundary end?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Large systems usually do not need more state tools.&lt;/p&gt;

&lt;p&gt;They need &lt;strong&gt;a stronger organizational model for the tools they already have&lt;/strong&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  Why large Vue projects drift into the scattered version so easily
&lt;/h2&gt;

&lt;p&gt;Because the road into it is the most natural road available.&lt;/p&gt;

&lt;p&gt;A team starts with local state.&lt;br&gt;
Then extracts a composable.&lt;br&gt;
Then uses &lt;code&gt;provide/inject&lt;/code&gt;.&lt;br&gt;
Then adds a store.&lt;br&gt;
Then adds Query.&lt;br&gt;
Then adds local persistence.&lt;br&gt;
Then patches SSR behavior.&lt;/p&gt;

&lt;p&gt;Every move is locally rational.&lt;/p&gt;

&lt;p&gt;That is exactly why the outcome is so common.&lt;/p&gt;

&lt;p&gt;The issue is not that teams are making obviously bad decisions.&lt;/p&gt;

&lt;p&gt;The issue is that the default growth path is a &lt;strong&gt;local-solution path&lt;/strong&gt;, not a &lt;strong&gt;boundary-first path&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And over time, that difference compounds.&lt;/p&gt;

&lt;p&gt;The same symptoms begin to appear again and again:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the same kind of state lands in different places on different screens,&lt;/li&gt;
&lt;li&gt;refresh rules are hidden inside mutation success handlers,&lt;/li&gt;
&lt;li&gt;persistence is treated as an afterthought rather than part of state semantics,&lt;/li&gt;
&lt;li&gt;"global state" quietly mixes request-level and long-lived system concerns,&lt;/li&gt;
&lt;li&gt;one resource's schema, permissions, forms, queries, and invalidation rules end up split across several layers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the critical distinction:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The scattered version is not wrong because the tools are wrong. It is wrong because each tool solves one slice while the system never grows one stable ownership model.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That creates a very particular form of technical debt.&lt;/p&gt;

&lt;p&gt;Not sudden failure.&lt;/p&gt;

&lt;p&gt;Not dramatic breakage.&lt;/p&gt;

&lt;p&gt;A slower, more corrosive debt: the project becomes increasingly dependent on memory, habit, and tribal knowledge to remain coherent.&lt;/p&gt;

&lt;p&gt;At first, that feels like flexibility.&lt;/p&gt;

&lt;p&gt;Later, it feels like fog.&lt;/p&gt;


&lt;h2&gt;
  
  
  What Zova/Cabloy changes is not the reactive core, but the authoring model
&lt;/h2&gt;

&lt;p&gt;The most important thing to understand about Zova/Cabloy is this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Vue 3 remains the reactive foundation. What changes is the programming model the business layer encounters first.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In mainstream Vue development, the most visible concepts are usually:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;setup()&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ref()&lt;/code&gt; / &lt;code&gt;reactive()&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;computed()&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;composables,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;provide/inject&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;stores.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In Zova, the center of gravity shifts.&lt;/p&gt;

&lt;p&gt;The foreground concepts become:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Controller / Bean instances&lt;/strong&gt; as visible state owners,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;__init__&lt;/code&gt;&lt;/strong&gt; as a lifecycle-aware wiring point,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IoC scopes&lt;/strong&gt; as the sharing model,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Model&lt;/strong&gt; as the unified state layer,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resource Owner&lt;/strong&gt; as the owner of resource-level frontend semantics.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That changes the first design question.&lt;/p&gt;

&lt;p&gt;Instead of asking:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;should this go into a store?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You are pushed to ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;which bean owns this?&lt;/li&gt;
&lt;li&gt;which scope should share it?&lt;/li&gt;
&lt;li&gt;which model family should represent it?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is not a cosmetic shift.&lt;/p&gt;

&lt;p&gt;It changes where architectural decisions happen.&lt;/p&gt;

&lt;p&gt;In the scattered version, state tends to appear first and only later search for a home.&lt;/p&gt;

&lt;p&gt;In the systematic version, the home is chosen first, and implementation follows.&lt;/p&gt;

&lt;p&gt;That sounds subtle.&lt;/p&gt;

&lt;p&gt;In a large system, it is not subtle at all.&lt;/p&gt;

&lt;p&gt;Because the hardest thing in a growing codebase is not shipping one more feature.&lt;/p&gt;

&lt;p&gt;It is continuing to place boundaries correctly as the system gets more complex.&lt;/p&gt;


&lt;h2&gt;
  
  
  Layer 1: make ownership explicit
&lt;/h2&gt;

&lt;p&gt;In many Vue projects, the most natural host for state is a local declaration inside &lt;code&gt;setup()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;countLabel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`=== &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; ===`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is nothing wrong with that pattern.&lt;/p&gt;

&lt;p&gt;But at scale, it often produces a predictable drift: state appears as a local variable first, then gradually leaks outward into composables, stores, parent components, and other abstractions.&lt;/p&gt;

&lt;p&gt;Zova begins from a different assumption. Visible state typically lives directly on a Controller or Bean instance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;countLabel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;$computed&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`=== &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; ===`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important point is not that you wrote less &lt;code&gt;.value&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The important point is this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the state host becomes a &lt;strong&gt;framework-managed instance&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;derived state becomes &lt;strong&gt;lifecycle-aware wiring on that instance&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;behavior and state collapse back under one explicit owner.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That changes how a large codebase is read.&lt;/p&gt;

&lt;p&gt;In ordinary Vue code, the reader often asks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;which composable owns this?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In Zova, the reader is more likely to ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;which Bean or Controller owns this?&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is a healthier question in a large system.&lt;/p&gt;

&lt;p&gt;Because large systems rarely suffer from a lack of flexibility.&lt;/p&gt;

&lt;p&gt;They suffer from a lack of stable ownership.&lt;/p&gt;

&lt;p&gt;And once ownership drifts, scope, persistence, cache identity, SSR behavior, and invalidation tend to drift with it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Layer 2: make sharing a scope problem instead of a mechanism-switching problem
&lt;/h2&gt;

&lt;p&gt;Large Vue projects also become scattered because &lt;strong&gt;sharing is expressed through multiple unrelated mechanisms&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The word "shared" can point to very different strategies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;local component sharing through local state or composables,&lt;/li&gt;
&lt;li&gt;parent-child sharing through props/emits or &lt;code&gt;provide/inject&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;app-level sharing through a store,&lt;/li&gt;
&lt;li&gt;long-lived sharing through module singletons, plugin state, or some implicit global object.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the system quietly learns a rule like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;When the sharing boundary changes, the mechanism changes too.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Zova reframes that into one IoC model with multiple scopes:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Sharing range&lt;/th&gt;
&lt;th&gt;Common Vue 3 pattern&lt;/th&gt;
&lt;th&gt;Zova question framing&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;component-internal&lt;/td&gt;
&lt;td&gt;local state, component-local composables&lt;/td&gt;
&lt;td&gt;should this be a &lt;code&gt;ctx&lt;/code&gt;-scoped Bean?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;between-components&lt;/td&gt;
&lt;td&gt;props/emits, parent-owned composables, &lt;code&gt;provide/inject&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;should this use hierarchical injection such as &lt;code&gt;host&lt;/code&gt; / &lt;code&gt;skipSelf&lt;/code&gt;?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;app-global&lt;/td&gt;
&lt;td&gt;stores, app-level provided state&lt;/td&gt;
&lt;td&gt;should this be an &lt;code&gt;app&lt;/code&gt;-scoped shared Bean?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;system-level&lt;/td&gt;
&lt;td&gt;module singletons, long-lived imported state&lt;/td&gt;
&lt;td&gt;should this be a &lt;code&gt;sys&lt;/code&gt;-scoped state that outlives requests?&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The value is not just cleaner terminology.&lt;/p&gt;

&lt;p&gt;It is a different way of framing the problem:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Do not start with the sharing mechanism. Start with the sharing boundary.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A compact mental model looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ControllerPage&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Use&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nx"&gt;$$localCounterState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CounterState&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Use&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;injectionScope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;host&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="nx"&gt;$$hostCounterState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CounterState&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Use&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;injectionScope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="nx"&gt;$$appCounterState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CounterState&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Use&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;injectionScope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sys&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="nx"&gt;$$sysCounterState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CounterState&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The strength of this model is not only uniform syntax.&lt;/p&gt;

&lt;p&gt;It is also that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the sharing boundary becomes explicit,&lt;/li&gt;
&lt;li&gt;state does not need to jump between unrelated mechanisms as sharing expands,&lt;/li&gt;
&lt;li&gt;app-level and system-level state stop being casually mixed together.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This matters even more in SSR.&lt;/p&gt;

&lt;p&gt;Because many teams casually label both of the following things as "global state":&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;request-level or app-level state&lt;/strong&gt; — state that should follow an app instance or one SSR request,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;system-level state&lt;/strong&gt; — state that should outlive those requests.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If that distinction is not built into the model, SSR bugs become easy to create and hard to reason about.&lt;/p&gt;

&lt;p&gt;Zova moves the difference directly into the architecture through &lt;code&gt;app&lt;/code&gt; versus &lt;code&gt;sys&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That is what a systematic model does well:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;It turns boundaries that used to live in team memory into explicit structural rules.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Layer 3: Model is not just a request layer, and not just another store
&lt;/h2&gt;

&lt;p&gt;Even teams that use both a store and a query library often unify only part of the state problem.&lt;/p&gt;

&lt;p&gt;The usual split is familiar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;stores handle client-shared state,&lt;/li&gt;
&lt;li&gt;query handles server-state cache,&lt;/li&gt;
&lt;li&gt;localStorage utilities handle persistence,&lt;/li&gt;
&lt;li&gt;cookie utilities handle request-related state,&lt;/li&gt;
&lt;li&gt;async persistence is wrapped somewhere else.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is still a scattered model.&lt;/p&gt;

&lt;p&gt;Every state family has a tool.&lt;/p&gt;

&lt;p&gt;But the state families do not share one coherent owner.&lt;/p&gt;

&lt;p&gt;Zova's Model pushes the abstraction one step further.&lt;/p&gt;

&lt;p&gt;It does not only represent remote data. It brings multiple state families under one model-state layer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;data&lt;/code&gt; — server / query-style state,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;mem&lt;/code&gt; — in-memory state,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;local&lt;/code&gt; — localStorage-backed state,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cookie&lt;/code&gt; — cookie-backed state,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;db&lt;/code&gt; — async persistent state.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That changes the conversation in an important way.&lt;/p&gt;

&lt;p&gt;Server state, local persistence, and in-memory state no longer need to be treated as unrelated worlds.&lt;/p&gt;

&lt;p&gt;They still have different semantics, but they can live under one unified model ownership.&lt;/p&gt;

&lt;p&gt;And this is not merely a thin wrapper over other tools.&lt;/p&gt;

&lt;p&gt;Zova's Model is built on TanStack Query, but what the business layer sees is a more structured interface:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;query keys carry model identity,&lt;/li&gt;
&lt;li&gt;mutation keys carry model identity,&lt;/li&gt;
&lt;li&gt;invalidation policy can belong to the model,&lt;/li&gt;
&lt;li&gt;local / cookie / db state still participates in model-owned keying and restore semantics,&lt;/li&gt;
&lt;li&gt;SSR and hydration behavior can follow model rules.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the accurate description is not "Model is another store."&lt;/p&gt;

&lt;p&gt;It is this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Model is a unified state boundary for query, mutation, persistence, invalidation, restore behavior, and SSR semantics.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is also why localStorage should not be understood as merely "saving something somewhere."&lt;/p&gt;

&lt;p&gt;A more precise description is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;local-storage state is not a separate store; it is &lt;strong&gt;model-owned query state with a local-storage persister&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That may sound like a wording change, but it affects the whole architecture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;which model owns the state,&lt;/li&gt;
&lt;li&gt;how its key space is isolated,&lt;/li&gt;
&lt;li&gt;how restore works,&lt;/li&gt;
&lt;li&gt;who owns invalidation,&lt;/li&gt;
&lt;li&gt;whether it participates in SSR dehydration and hydration behavior.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the scattered version, those answers are spread across utilities and conventions.&lt;/p&gt;

&lt;p&gt;In the systematic version, they are pulled back into one model boundary.&lt;/p&gt;

&lt;p&gt;That is the kind of abstraction large systems actually need.&lt;/p&gt;




&lt;h2&gt;
  
  
  Layer 4: pull resource semantics back under a Resource Owner
&lt;/h2&gt;

&lt;p&gt;Even teams with both a store and a query layer often continue to fragment at the resource boundary.&lt;/p&gt;

&lt;p&gt;Take a typical list / form / detail resource.&lt;/p&gt;

&lt;p&gt;The frontend still needs to know all of this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;how the API path is built,&lt;/li&gt;
&lt;li&gt;how the list is fetched,&lt;/li&gt;
&lt;li&gt;how one row is fetched,&lt;/li&gt;
&lt;li&gt;how create / update / delete work,&lt;/li&gt;
&lt;li&gt;where schema comes from,&lt;/li&gt;
&lt;li&gt;how permissions are checked,&lt;/li&gt;
&lt;li&gt;which caches must be invalidated after success,&lt;/li&gt;
&lt;li&gt;how the form layer is wired.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At that point, the problem is no longer "a piece of state."&lt;/p&gt;

&lt;p&gt;It is a bundle of &lt;strong&gt;resource semantics&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This is where &lt;code&gt;ModelResource&lt;/code&gt; becomes especially important:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;It allows a model to become not just a query wrapper, but the frontend owner of a resource boundary.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That means the model can own:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;resource bootstrap,&lt;/li&gt;
&lt;li&gt;schema access,&lt;/li&gt;
&lt;li&gt;permission access,&lt;/li&gt;
&lt;li&gt;form integration,&lt;/li&gt;
&lt;li&gt;query state,&lt;/li&gt;
&lt;li&gt;mutation state,&lt;/li&gt;
&lt;li&gt;cache invalidation policy.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A large amount of logic that would otherwise be scattered across pages, forms, buttons, request wrappers, schema helpers, and permission helpers gets pulled back into one stable boundary.&lt;/p&gt;

&lt;p&gt;The shortest way to say it is this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The page consumes resource semantics. The model owns query semantics.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That matters because it lifts the discussion from "where should this value live?" to "who owns this entire resource-shaped complexity?"&lt;/p&gt;

&lt;p&gt;And once that resource boundary is stable, pages, forms, permissions, schema, and cache behavior are much more likely to remain stable together.&lt;/p&gt;

&lt;p&gt;That is one of the most valuable properties a large codebase can have:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Not just reusable logic, but reusable boundaries.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  One table to summarize the difference
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;Scattered version&lt;/th&gt;
&lt;th&gt;Systematic version&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;State ownership&lt;/td&gt;
&lt;td&gt;whoever touches it first tends to host it&lt;/td&gt;
&lt;td&gt;ownership is assigned first to Bean / Controller / Model&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sharing model&lt;/td&gt;
&lt;td&gt;composables, &lt;code&gt;provide/inject&lt;/code&gt;, stores grow in parallel&lt;/td&gt;
&lt;td&gt;sharing is discussed through IoC scopes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Server data&lt;/td&gt;
&lt;td&gt;owned by a separate request or query layer&lt;/td&gt;
&lt;td&gt;part of Model's unified state boundary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Local persistence&lt;/td&gt;
&lt;td&gt;localStorage / cookie wrappers remain separate utilities&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;local&lt;/code&gt; / &lt;code&gt;cookie&lt;/code&gt; / &lt;code&gt;db&lt;/code&gt; live inside model-state families&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cache keys&lt;/td&gt;
&lt;td&gt;often become team-defined strings&lt;/td&gt;
&lt;td&gt;namespaced by model identity and selectors&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Invalidation&lt;/td&gt;
&lt;td&gt;handled by pages, buttons, and mutation callbacks&lt;/td&gt;
&lt;td&gt;owned as model policy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSR semantics&lt;/td&gt;
&lt;td&gt;usually patched through conventions&lt;/td&gt;
&lt;td&gt;structured through &lt;code&gt;app&lt;/code&gt; / &lt;code&gt;sys&lt;/code&gt; and hydration rules&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reading strategy&lt;/td&gt;
&lt;td&gt;find hooks and variables, then infer boundaries&lt;/td&gt;
&lt;td&gt;inspect owner, scope, and model first&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Complexity control&lt;/td&gt;
&lt;td&gt;mostly maintained by team discipline&lt;/td&gt;
&lt;td&gt;more of it is maintained by structure&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The judgment behind the table is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The scattered version can work, but it manages complexity mainly through discipline. The systematic version moves more of that burden into structure.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Or, more bluntly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the scattered version is easier to start,&lt;/li&gt;
&lt;li&gt;the systematic version is easier to scale.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And large projects are rarely decided by how elegant they felt in week one.&lt;/p&gt;

&lt;p&gt;They are decided by whether the architecture is still legible six months later.&lt;/p&gt;




&lt;h2&gt;
  
  
  Three practical scenarios
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Page filter state
&lt;/h3&gt;

&lt;p&gt;In the scattered version, page-level filter state often evolves like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it starts as a few local &lt;code&gt;ref&lt;/code&gt;s,&lt;/li&gt;
&lt;li&gt;then moves into a composable,&lt;/li&gt;
&lt;li&gt;then gets saved to localStorage,&lt;/li&gt;
&lt;li&gt;then later moves into a store because another page needs it too.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That path can work.&lt;/p&gt;

&lt;p&gt;But after enough steps, the crucial questions become harder to answer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;which page actually owns it?&lt;/li&gt;
&lt;li&gt;is it page-local, resource-level, or app-level?&lt;/li&gt;
&lt;li&gt;is persistence just a UX convenience, or part of the state semantics?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the systematic version, the first question is not "should we extract a composable?"&lt;/p&gt;

&lt;p&gt;It is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;is this page / controller-local state?&lt;/li&gt;
&lt;li&gt;should it instead be &lt;code&gt;mem&lt;/code&gt; or &lt;code&gt;local&lt;/code&gt; state on a Model?&lt;/li&gt;
&lt;li&gt;does it need model identity in its key space?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Answer those questions early, and the state tends to keep a much more stable home.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. List query and refresh after create
&lt;/h3&gt;

&lt;p&gt;In the scattered version, refreshing a list after creating a record often means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;manually refetching the list,&lt;/li&gt;
&lt;li&gt;invalidating queries from the page,&lt;/li&gt;
&lt;li&gt;or letting some store action do it as a side effect.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It works.&lt;/p&gt;

&lt;p&gt;But the refresh rule ends up distributed across multiple call sites.&lt;/p&gt;

&lt;p&gt;In the systematic version, this belongs much more naturally to Model ownership:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the list query belongs to the model,&lt;/li&gt;
&lt;li&gt;the create mutation belongs to the model,&lt;/li&gt;
&lt;li&gt;the invalidation policy after create belongs to the model too.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The page consumes a resource action instead of coordinating cache semantics itself.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Resource forms, permissions, and schema
&lt;/h3&gt;

&lt;p&gt;The scattered version usually loses control not at simple fetching, but at the semantics that accumulate around a resource:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;which fields are editable,&lt;/li&gt;
&lt;li&gt;which buttons are visible,&lt;/li&gt;
&lt;li&gt;where the schema comes from,&lt;/li&gt;
&lt;li&gt;which regions refresh after which action.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If those rules are split across pages, forms, buttons, helper utilities, store logic, and query logic, the codebase becomes only locally understandable.&lt;/p&gt;

&lt;p&gt;The Resource Owner mindset is different:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;pull that semantic bundle back to one stable owner,&lt;/li&gt;
&lt;li&gt;let the page consume it,&lt;/li&gt;
&lt;li&gt;let the model own it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is what effective complex-state management actually looks like.&lt;/p&gt;

&lt;p&gt;Across all three scenarios, the same distinction appears again:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The scattered version solves the immediate problem first. The systematic version places the state inside the right boundary first.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The systematic version is not free
&lt;/h2&gt;

&lt;p&gt;This part matters.&lt;/p&gt;

&lt;p&gt;A more systematic architecture is not a free lunch.&lt;/p&gt;

&lt;p&gt;Its cost includes at least this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you accept stronger architectural constraints,&lt;/li&gt;
&lt;li&gt;you need to learn roles like owner, scope, and model,&lt;/li&gt;
&lt;li&gt;you cannot keep treating every piece of state as a local detail that will be sorted out later,&lt;/li&gt;
&lt;li&gt;for small projects, short projects, or one-off pages, the structure may feel heavier than necessary.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the conclusion is definitely not:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;every Vue project should be written this way.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The more accurate conclusion is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;When complexity is still low, the scattered version feels flexible. When complexity keeps rising, the systematic version becomes far more stable.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What you save early is abstraction cost.&lt;/p&gt;

&lt;p&gt;What you usually pay later is boundary cost.&lt;/p&gt;

&lt;p&gt;That is the real trade-off.&lt;/p&gt;

&lt;p&gt;The systematic version does not guarantee that day one feels fastest.&lt;/p&gt;

&lt;p&gt;It gives you a much better chance that day 180 still feels manageable.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final judgment: the ceiling is architectural, not just tool-level
&lt;/h2&gt;

&lt;p&gt;If state management is reduced to "which library should we choose," the conversation remains too shallow.&lt;/p&gt;

&lt;p&gt;In large frontend systems, the more decisive questions are usually these:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;does state have a stable owner?&lt;/li&gt;
&lt;li&gt;are sharing boundaries explicit?&lt;/li&gt;
&lt;li&gt;is cache identity controlled?&lt;/li&gt;
&lt;li&gt;is persistence part of the model's semantics, or merely a patch?&lt;/li&gt;
&lt;li&gt;are SSR rules structural, or just conventions?&lt;/li&gt;
&lt;li&gt;is resource-level complexity pulled back under one stable owner?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the conclusion I would draw is this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The ceiling of state management in a large project is usually not determined by one library. It is determined by whether the architecture forms a coherent state system.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The scattered version can be fast.&lt;/p&gt;

&lt;p&gt;The systematic version can be steady.&lt;/p&gt;

&lt;p&gt;The former depends more on discipline.&lt;/p&gt;

&lt;p&gt;The latter depends more on structure.&lt;/p&gt;

&lt;p&gt;That is the real value of Zova/Cabloy in large Vue projects: not one isolated capability, but a more consistent system for the things that drift apart most easily:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ownership,&lt;/li&gt;
&lt;li&gt;sharing,&lt;/li&gt;
&lt;li&gt;cache semantics,&lt;/li&gt;
&lt;li&gt;persistence,&lt;/li&gt;
&lt;li&gt;SSR,&lt;/li&gt;
&lt;li&gt;resource boundaries.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is why "Scattered vs Systematic" is more than a catchy title.&lt;/p&gt;

&lt;p&gt;It points to the real engineering question underneath the tooling debate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;should complex state continue to be held together mainly by people,&lt;/li&gt;
&lt;li&gt;or should more of it be held together by structure?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If I had to compress the whole article into one closing line, it would be this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Large Vue projects usually do not need more state tools. They need a state architecture capable of organizing the tools they already have.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;p&gt;If you want to keep following this topic, the most useful public references are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reading Zova for Vue Developers&lt;/strong&gt; — a bridge from the Vue mental model to the Zova one&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zova vs Vue 3 Comparison&lt;/strong&gt; — a broader side-by-side framing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IoC and Beans&lt;/strong&gt; — the sharing boundaries behind &lt;code&gt;ctx&lt;/code&gt;, &lt;code&gt;app&lt;/code&gt;, &lt;code&gt;sys&lt;/code&gt;, and &lt;code&gt;host&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Model Architecture&lt;/strong&gt; — why Model is not just a query wrapper&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Model State Guide&lt;/strong&gt; — the &lt;code&gt;data&lt;/code&gt; / &lt;code&gt;mem&lt;/code&gt; / &lt;code&gt;local&lt;/code&gt; / &lt;code&gt;cookie&lt;/code&gt; / &lt;code&gt;db&lt;/code&gt; state families&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Model Resource Owner Pattern&lt;/strong&gt; — why resource-level query, schema, permissions, and invalidation should be owned together&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And if you are working on a Vue system that keeps getting larger and harder to explain, the most useful question may no longer be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;do we need one more state library?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It may be this instead:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;can we turn this state system from a scattered assembly into a coherent architecture?&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>vue</category>
      <category>statemanagement</category>
      <category>frontendarchitecture</category>
      <category>ssr</category>
    </item>
    <item>
      <title>Why Large Frontend Apps Keep Getting Slower Even After the Usual Optimizations</title>
      <dc:creator>Uncle Pushui</dc:creator>
      <pubDate>Tue, 30 Jun 2026 10:02:00 +0000</pubDate>
      <link>https://dev.to/uncle-pushui/why-large-frontend-apps-keep-getting-slower-even-after-the-usual-optimizations-4f70</link>
      <guid>https://dev.to/uncle-pushui/why-large-frontend-apps-keep-getting-slower-even-after-the-usual-optimizations-4f70</guid>
      <description>&lt;p&gt;&lt;em&gt;Large frontend performance is often more of an architecture problem than a bundle problem.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Most large frontend teams are not ignoring performance basics.&lt;/p&gt;

&lt;p&gt;They already split bundles, lazy-load routes, compress assets, optimize images, cache requests, and add SSR where it clearly helps.&lt;/p&gt;

&lt;p&gt;And yet the app still gets heavier.&lt;/p&gt;

&lt;p&gt;A dashboard gets faster, and another one becomes the new bottleneck. A bundle goes down, then creeps back up a few sprints later. Caching improves one flow, while invalidation gets messy somewhere else.&lt;/p&gt;

&lt;p&gt;If that sounds familiar, the issue may not be that your team needs one more round of optimization. It may be that the frontend is structurally designed to keep accumulating weight as it grows.&lt;/p&gt;

&lt;p&gt;That is the argument of this article.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;weak boundaries tend to produce frontend monolith growth&lt;/li&gt;
&lt;li&gt;repeated CRUD and resource page construction creates hidden system weight&lt;/li&gt;
&lt;li&gt;query caching works best as infrastructure, not as a team-by-team habit&lt;/li&gt;
&lt;li&gt;SSR becomes more useful when it includes admin flows, not just public pages&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The real problem is not just slowness. It is slowness that keeps coming back.
&lt;/h2&gt;

&lt;p&gt;A lot of teams approach performance in a familiar loop:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;shrink the bundle&lt;/li&gt;
&lt;li&gt;optimize the slow screen&lt;/li&gt;
&lt;li&gt;cache a few expensive requests&lt;/li&gt;
&lt;li&gt;move selected routes to SSR&lt;/li&gt;
&lt;li&gt;repeat when the next bottleneck appears&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That approach works for a while.&lt;/p&gt;

&lt;p&gt;What it does not explain is why large business applications so often slide back into the same state. In many cases, the reason is structural. The architecture keeps creating new weight faster than the team can remove it.&lt;/p&gt;

&lt;p&gt;You can usually see that happening through four patterns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the frontend keeps drifting toward a monolith&lt;/li&gt;
&lt;li&gt;similar CRUD and resource pages are rebuilt over and over&lt;/li&gt;
&lt;li&gt;query and cache behavior varies from team to team&lt;/li&gt;
&lt;li&gt;SSR exists, but only as a special-case feature&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When those patterns are present, performance work becomes reactive by default.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Weak boundaries quietly turn the frontend into one growing shell
&lt;/h2&gt;

&lt;p&gt;A codebase can look modular and still behave like a monolith.&lt;/p&gt;

&lt;p&gt;It may have feature folders, shared utilities, route splitting, and a decent naming convention. But that often amounts to &lt;strong&gt;directory modularity&lt;/strong&gt;, not real architectural separation.&lt;/p&gt;

&lt;p&gt;The harder question is whether the system has durable boundaries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;business boundaries&lt;/li&gt;
&lt;li&gt;bundle boundaries&lt;/li&gt;
&lt;li&gt;runtime boundaries&lt;/li&gt;
&lt;li&gt;delivery-target boundaries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without them, every new feature adds a little more shared dependency surface, a little more startup cost, and a little more cross-cutting complexity. No single change looks fatal. The problem is the accumulation.&lt;/p&gt;

&lt;p&gt;That is why large-app performance is often misdiagnosed as “one more bundle problem.” In reality, it is frequently a boundary problem. Once the frontend loses the ability to resist monolith growth, every optimization becomes harder to keep.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Rebuilding the same resource surfaces again and again also makes the app heavier
&lt;/h2&gt;

&lt;p&gt;Another source of weight is repetition at the page layer.&lt;/p&gt;

&lt;p&gt;Large business systems tend to contain many variations of the same basic surfaces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;list pages&lt;/li&gt;
&lt;li&gt;create and edit forms&lt;/li&gt;
&lt;li&gt;filters&lt;/li&gt;
&lt;li&gt;detail views&lt;/li&gt;
&lt;li&gt;row actions&lt;/li&gt;
&lt;li&gt;resource-oriented admin flows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When teams hand-build these again and again, the cost is not just duplicated effort. It is also duplicated structure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;more page code&lt;/li&gt;
&lt;li&gt;more repeated view logic&lt;/li&gt;
&lt;li&gt;more initialization work per screen&lt;/li&gt;
&lt;li&gt;more inconsistency across similar resources&lt;/li&gt;
&lt;li&gt;more rework when backend fields change&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is where contract-driven UI becomes interesting.&lt;/p&gt;

&lt;p&gt;The point is not that everything should be generated. That is rarely the right target. The more practical goal is to let backend contracts shape the routine parts of the frontend, while keeping custom work for the genuinely product-specific pieces.&lt;/p&gt;

&lt;p&gt;Done well, that has a few compounding benefits: thinner page layers, less drift between backend and frontend, and fewer repeated decisions around the same CRUD patterns.&lt;/p&gt;

&lt;p&gt;At scale, that is not just a DX win. It changes the weight profile of the application.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Query caching works best as infrastructure, not as a team habit
&lt;/h2&gt;

&lt;p&gt;A surprising number of “slow frontend” complaints are really data-path problems.&lt;/p&gt;

&lt;p&gt;You see them in patterns like these:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;too many requests on first entry&lt;/li&gt;
&lt;li&gt;unnecessary refetches when returning to a screen&lt;/li&gt;
&lt;li&gt;multiple components requesting the same data independently&lt;/li&gt;
&lt;li&gt;inconsistent invalidation rules&lt;/li&gt;
&lt;li&gt;awkward SSR prefetch and hydration behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Small apps can get away with this for a while. Large apps usually cannot.&lt;/p&gt;

&lt;p&gt;That is why query caching should be treated as infrastructure. The specific library matters less than the role it plays in the system.&lt;/p&gt;

&lt;p&gt;What matters is whether server-data behavior is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;consistent&lt;/li&gt;
&lt;li&gt;reusable&lt;/li&gt;
&lt;li&gt;centrally modeled&lt;/li&gt;
&lt;li&gt;compatible with SSR and hydration&lt;/li&gt;
&lt;li&gt;understandable across teams&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If caching arrives late, each team tends to wrap it in its own way. Cache keys drift. Invalidation becomes uneven. SSR support is added later and never feels fully integrated.&lt;/p&gt;

&lt;p&gt;When query behavior is part of the runtime model, the frontend is much more likely to get shared semantics, predictable reuse, and cleaner transitions between server-rendered and client-resumed states.&lt;/p&gt;

&lt;p&gt;In large systems, that kind of coherence matters as much as bundle size.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. SSR becomes more valuable when it includes admin flows, not just public pages
&lt;/h2&gt;

&lt;p&gt;SSR is still often discussed as a public-web technique:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SEO pages&lt;/li&gt;
&lt;li&gt;marketing pages&lt;/li&gt;
&lt;li&gt;content pages&lt;/li&gt;
&lt;li&gt;landing pages&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But some of the heaviest first-load experiences in real applications are internal:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;admin dashboards&lt;/li&gt;
&lt;li&gt;workbenches&lt;/li&gt;
&lt;li&gt;reporting screens&lt;/li&gt;
&lt;li&gt;entry pages that need identity, permissions, menus, layout state, and initial data before they become useful&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If all of that is handled purely on the client, users often sit through a long startup chain before they see the first meaningful screen.&lt;/p&gt;

&lt;p&gt;That is exactly where SSR can help.&lt;/p&gt;

&lt;p&gt;But SSR is most effective when it is not bolted on as a special case. It works best when it lines up with the rest of the architecture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;modular frontend boundaries&lt;/li&gt;
&lt;li&gt;shared contract surfaces&lt;/li&gt;
&lt;li&gt;query and cache infrastructure&lt;/li&gt;
&lt;li&gt;coherent hydration paths&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That matters not only for public web apps, but also for serious back-office systems where first-use responsiveness shapes the overall feel of the product.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Cabloy is a useful case study
&lt;/h2&gt;

&lt;p&gt;Cabloy is interesting here not because it “solves performance automatically,” but because it brings several of these ideas together in one place.&lt;/p&gt;

&lt;p&gt;In Cabloy Basic:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Frontend structure is explicitly modular.&lt;/strong&gt; Zova organizes capabilities through modules and suites instead of treating the UI as one flat application surface.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backend contracts can shape routine frontend surfaces.&lt;/strong&gt; DTO, entity, and OpenAPI metadata can feed SDK generation and schema-aware UI helpers for forms, tables, filters, detail views, and actions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TanStack Query sits in the foundation.&lt;/strong&gt; Zova’s model-state layer builds on it through framework-native abstractions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSR exists for both Admin and Web flavors.&lt;/strong&gt; That treats first-load quality as a whole-system concern rather than something reserved for public pages.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The takeaway is not that every team should adopt Cabloy. The more portable lesson is that large frontend performance improves when these concerns are treated as design choices early, not cleanup tasks later.&lt;/p&gt;




&lt;h2&gt;
  
  
  A better question to ask
&lt;/h2&gt;

&lt;p&gt;If your team has already done the obvious optimizations and the app still feels heavy, ask this instead:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Is our architecture structurally designed to keep getting heavier over time?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That question usually reveals more than another isolated bundle audit.&lt;/p&gt;

&lt;p&gt;A strong answer tends to include four things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;real modular boundaries&lt;/li&gt;
&lt;li&gt;thinner routine UI through shared contracts&lt;/li&gt;
&lt;li&gt;query caching as infrastructure&lt;/li&gt;
&lt;li&gt;SSR integrated into the system model, including admin flows where it matters&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If those pieces are missing, performance wins tend to be temporary. If they are present, the frontend has a much better chance of staying fast as it grows.&lt;/p&gt;

&lt;p&gt;That is the real challenge in frontend performance at scale:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;not making one page faster today, but building a system that does not keep getting slower by default.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>frontend</category>
      <category>performance</category>
      <category>architecture</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Vite: How to resolve bundle fragmentation?</title>
      <dc:creator>Uncle Pushui</dc:creator>
      <pubDate>Mon, 14 Oct 2024 11:12:00 +0000</pubDate>
      <link>https://dev.to/uncle-pushui/vite-how-to-resolve-bundle-fragmentation-3jm7</link>
      <guid>https://dev.to/uncle-pushui/vite-how-to-resolve-bundle-fragmentation-3jm7</guid>
      <description>&lt;h2&gt;
  
  
  Preface
&lt;/h2&gt;

&lt;p&gt;When we use Vite for bundle code files, we often encounter this problem: With the development of the business, there are more and more pages, more and more third-party dependencies, and the chunks are getting bigger and bigger. If the pages are imported dynamically, then all the files shared by several pages will be independently bundled to the same chunk, which will create a large number of tiny js chunk files, such as: 1K, 2K, 3K, which significantly increases the resource request of the browser.&lt;/p&gt;

&lt;p&gt;Although you can customize the chunks-merging strategy through the &lt;code&gt;rollupOptions.output.manualChunks&lt;/code&gt;, the dependencies between the files are intricate. If the chunks-merging configuration is unreasonable, it will cause the initial chunks to be too large, or the circular reference will occur. Therefore, the mental burden is heavy. Is there an automated chunks-merging mechanism to completely solve the problem of bundle fragmentation?&lt;/p&gt;

&lt;h2&gt;
  
  
  Two potential issues of chunks-merging
&lt;/h2&gt;

&lt;p&gt;As mentioned earlier, there are two potential issues to use the &lt;code&gt;rollupOptions.output.manualChunks&lt;/code&gt; custom chunks-merging strategy.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Cause the initial chunks to large
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faidf7su40x9y69z63581.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faidf7su40x9y69z63581.png" alt="Image description" width="364" height="221"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As shown in the figure, file A originally only depends on file C, but according to the chunks-merging configuration in the figure, Chunk1 and Chunk2 must be downloaded before using file A. In a complex project, due to the complexity of the dependencies between the files, this dependency will spread quickly with the merger of a large number of small files, resulting in the large size of the initial bundles.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Cause circular reference error
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frz8e276iqguygwq9htug.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frz8e276iqguygwq9htug.png" alt="Image description" width="263" height="221"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As shown in the figure, due to the mutual dependence between the files, Chunk1 and Chunk2 will cause circular dependence error. Then in complex projects, the scenes of mutual dependence between code files are more common.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution: modular system
&lt;/h2&gt;

&lt;p&gt;Because the chunks-merging configuration will lead to the above two potential issues, it is often difficult to follow, and it is difficult to have a simple and easy-to-use configuration rules that can be followed. Because the chunks-merging configuration is closely related to the current state of the project. Once the code of the project has changed, the chunks-merging configuration also needs to be changed accordingly.&lt;/p&gt;

&lt;p&gt;To solve this problem, I introduced a modular system in the project. That is, the code of the project is split according to business characteristics to form a combination of several modules. Each module can include pages, components, configurations, languages, tools and other resources. Then a module is a natural bundle boundary, and automatically bundled into an independent asynchronous chunk when building, bidding farewell to the hassle of Vite configuration and effectively avoiding the fragmentation of bundles. Especially in large business systems, this advantage is particularly evident. Of course, the use of a modular system is also conducive to code decoupling and facilitating division of labor collaboration.&lt;/p&gt;

&lt;p&gt;Since a module is a bundle boundary, we can control the content and quantity of the chunks by controlling the content and quantity of the modules. And we divide the modules based on business characteristics to make the chunks-merging configuration have obvious business significance. Compared with the customization of the &lt;code&gt;rollupOptions.output.manualChunks&lt;/code&gt;, it is obviously low mental burden.&lt;/p&gt;

&lt;h2&gt;
  
  
  Directory Structure
&lt;/h2&gt;

&lt;p&gt;As the project continues to evolve iteratively, the business modules created will also expand. In addition, for some business scenarios, multiple modules are often required to be implemented together. To solve the above problems, We can introduces the concept of suite. In short, a suite is a combination of a group of business modules. In this way, a project is composed of several suites and several modules. Below is the directory structure of a project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;project
├── src
│  ├── module
│  ├── module-vendor
│  ├── suite
│  │  ├── a-demo
│  │  └── a-home
│  │    ├── modules
│  │    │  ├── home-base
│  │    │  ├── home-icon
│  │    │  ├── home-index
│  │    │  └── home-layout
│  └── suite-vendor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;src/module&lt;/td&gt;
&lt;td&gt;Standalone module (not part of a suite)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;src/module-vendor&lt;/td&gt;
&lt;td&gt;Standalone module (from third-party)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;src/suite&lt;/td&gt;
&lt;td&gt;Suite&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;src/suite-vendor&lt;/td&gt;
&lt;td&gt;Suite (from third-party)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;a-demo&lt;/td&gt;
&lt;td&gt;demo suite: Put the test code into a suite, so that it is convenient to disable at any time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;a-home&lt;/td&gt;
&lt;td&gt;business suite: including 4 business modules&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Chunks-merging effect
&lt;/h2&gt;

&lt;p&gt;Let's take a look at the actual bundle effect:&lt;/p&gt;

&lt;p&gt;Taking the module &lt;strong&gt;home-base&lt;/strong&gt; as an example, the left shows the code of the module, and the right shows the chunk size is 12k, and 3K with gzip. To achieve this chunks-merging effect, no configuration is required.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvogkpcyzqnhjwhxwkh89.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvogkpcyzqnhjwhxwkh89.png" alt="Image description" width="800" height="464"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For another example, we can also concentrate the layout components into the module &lt;strong&gt;home-layout&lt;/strong&gt; for management. The module is bundled into an independent chunk, which size is 29K and 6K with gzip.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F736wonr8supw785qaoyk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F736wonr8supw785qaoyk.png" alt="Image description" width="800" height="481"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Source code inside
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Dynamic import modules
&lt;/h3&gt;

&lt;p&gt;Since the module directory structure of the project is regular, we can extract all the module list before the project startup, and then generate a js file to centrally implement the dynamic import of modules:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;modules&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="nx"&gt;modules&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;home-base&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;home-base&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)};&lt;/span&gt;
&lt;span class="nx"&gt;modules&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;home-layout&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;home-layout&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)};&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;modulesMeta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;modules&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since all modules are dynamically imported through the import method, it will automatically split into independent chunks when performing Vite building.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Chunks-merging Configuration
&lt;/h3&gt;

&lt;p&gt;We also need to customize the chunks-merging configuration through &lt;code&gt;rollupOptions.output.manualChunks&lt;/code&gt; to ensure that the code inside the module is uniformly bundled together to avoid fragmented files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;__ModuleLibs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="sr"&gt;/src&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;module&lt;/span&gt;&lt;span class="se"&gt;\/([^\/]&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;?)\/&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="sr"&gt;/src&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;module-vendor&lt;/span&gt;&lt;span class="se"&gt;\/([^\/]&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;?)\/&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="sr"&gt;/src&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;suite&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;.*&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;modules&lt;/span&gt;&lt;span class="se"&gt;\/([^\/]&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;?)\/&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="sr"&gt;/src&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;suite-vendor&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;.*&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;modules&lt;/span&gt;&lt;span class="se"&gt;\/([^\/]&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;?)\/&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;build&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;rollupOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;manualChunks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;customManualChunk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;customManualChunk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;moduleLib&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;__ModuleLibs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;matched&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;moduleLib&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;matched&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;matched&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each file path is matched with a regular expression. If the matching is successful, use the corresponding module name as chunk name.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solutions to two potential issues
&lt;/h2&gt;

&lt;p&gt;If the modules are interdependent, there may be two potential issues mentioned above, as shown in the figure:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcj3ieql8owgd8n4pzgzz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcj3ieql8owgd8n4pzgzz.png" alt="Image description" width="263" height="221"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In order to prevent the occurrence of the two potential issues, we can implement a dynamic loading and resource positioning mechanism. In short, when we access the resources of &lt;strong&gt;Module 2&lt;/strong&gt; in &lt;strong&gt;Module 1&lt;/strong&gt;, we must first dynamically load module 2, then find the resources of module 2 and return them to the caller.&lt;/p&gt;

&lt;p&gt;For example, there is a Vue component &lt;code&gt;Card&lt;/code&gt; in module 2, and a page component &lt;code&gt;FirstPage&lt;/code&gt; in module 1. We need to use the &lt;code&gt;Card&lt;/code&gt; component in the page component &lt;code&gt;FirstPage&lt;/code&gt;. Then, we need to do it like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// dynamically load module&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loadModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;moduleName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;moduleRepo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;modulesMeta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;modules&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;moduleName&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;moduleRepo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// create dynamic component&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createDynamicComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;moduleName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;defineAsyncComponent&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// dynamically load module&lt;/span&gt;
      &lt;span class="nf"&gt;loadModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;moduleName&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;moduleResource&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// return the component of module&lt;/span&gt;
        &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;moduleResource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;components&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ZCard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createDynamicComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Module2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Card&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RenderFirstPage&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ZCard&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Advanced import mechanism
&lt;/h2&gt;

&lt;p&gt;Although using &lt;code&gt;createDynamicComponent&lt;/code&gt; can achieve the desired purpose, the code is not concise enough and cannot fully utilize the automatic import mechanism provided by Typescript. We still hope to use the component in the usual way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ZCard&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Module2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RenderFirstPage&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ZCard&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Such code is in the form of static import, which will cause &lt;strong&gt;Module 1&lt;/strong&gt; and &lt;strong&gt;Module 2&lt;/strong&gt; to be strongly dependent on each other. So, is there a way to have the best of both worlds? Yes. We can develop a Babel plug-in to parse the AST syntax tree and automatically change the &lt;code&gt;ZCard&lt;/code&gt; import to a dynamic import form. In this way, our code is not only concise and intuitive, but also can implement dynamic import, avoiding the occurrence of two potential issues when bundling. In order to avoid distracting the topic, how to develop the Babel plug-in will not be expanded here. If you are interested, you can directly refer to the source code: &lt;a href="https://github.com/cabloy/zova/blob/a6088acf4f520a65fa206b6864329b1712ae0921/zova-dev/packages-utils/babel-plugin-zova-component/src/index.ts" rel="noopener noreferrer"&gt;babel-plugin-zova-component&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This article analyzes the causes of Vite bundle fragmentation and proposes a modular system to simplify the chunks-merging configuration. At the same time, it adopts a dynamic loading mechanism to perfectly avoid the occurrence of two potential issues during bundling.&lt;/p&gt;

&lt;p&gt;Of course, there are still many details to consider in order to realize a complete modular system. If you want to experience the out-of-the-box effect, you can visit my open source framework: &lt;a href="https://github.com/cabloy/zova" rel="noopener noreferrer"&gt;https://github.com/cabloy/zova&lt;/a&gt;. You can contact me on twitter: &lt;a href="https://twitter.com/zhennann2024" rel="noopener noreferrer"&gt;https://twitter.com/zhennann2024&lt;/a&gt;&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>vite</category>
      <category>vue3</category>
    </item>
    <item>
      <title>How to achieve unified management of four types of global state data in Vue3?</title>
      <dc:creator>Uncle Pushui</dc:creator>
      <pubDate>Fri, 11 Oct 2024 02:22:00 +0000</pubDate>
      <link>https://dev.to/uncle-pushui/how-to-achieve-unified-management-of-four-types-of-global-state-data-in-vue3-4344</link>
      <guid>https://dev.to/uncle-pushui/how-to-achieve-unified-management-of-four-types-of-global-state-data-in-vue3-4344</guid>
      <description>&lt;h2&gt;
  
  
  Four types of global state data
&lt;/h2&gt;

&lt;p&gt;In actual development, you will encounter four types of global state data: &lt;code&gt;asynchronous data (usually from the server)&lt;/code&gt; and &lt;code&gt;synchronous data&lt;/code&gt;, while &lt;code&gt;synchronous data&lt;/code&gt; is divided into three types: &lt;code&gt;localstorage&lt;/code&gt;, &lt;code&gt;cookie&lt;/code&gt;, and &lt;code&gt;memory&lt;/code&gt;. In the traditional Vue3, different mechanisms are used to handle these state data, while only a unified &lt;code&gt;Model&lt;/code&gt; mechanism is needed in Zova&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Global State Data&lt;/th&gt;
&lt;th&gt;Traditional Vue3&lt;/th&gt;
&lt;th&gt;Zova&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;asynchronous data&lt;/td&gt;
&lt;td&gt;Pinia&lt;/td&gt;
&lt;td&gt;Model&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;localstorage&lt;/td&gt;
&lt;td&gt;Pinia + Localstorage&lt;/td&gt;
&lt;td&gt;Model&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;cookie&lt;/td&gt;
&lt;td&gt;Pinia + Cookie&lt;/td&gt;
&lt;td&gt;Model&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;memory&lt;/td&gt;
&lt;td&gt;Pinia&lt;/td&gt;
&lt;td&gt;Model&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;By using the &lt;code&gt;Model&lt;/code&gt; mechanism to uniformly manage these global state data, some common system capabilities can be provided, including &lt;code&gt;Memory Optimization&lt;/code&gt;, &lt;code&gt;Persistence&lt;/code&gt; and &lt;code&gt;SSR Support&lt;/code&gt;, etc., thereby standardizing data usage, simplifying code structure, and improving code maintainability&lt;/p&gt;

&lt;h2&gt;
  
  
  Feature 1. Support for async data and sync data
&lt;/h2&gt;

&lt;p&gt;The base of Zova Model is &lt;a href="https://tanstack.com/query/latest/docs/framework/vue/overview" rel="noopener noreferrer"&gt;TanStack Query&lt;/a&gt;. &lt;code&gt;TanStack Query&lt;/code&gt; provides powerful data acquisition, caching and update capabilities. If you have not used similar data management mechanisms like TanStack Query, it is strongly recommended that you learn about it, and you will definitely be impressed by the thought&lt;/p&gt;

&lt;p&gt;The core of TanStack Query is to manage &lt;code&gt;asynchronous data (usually from the server)&lt;/code&gt;. Zova provides some extension capabilities based on TanStack Query, so as to support the management of &lt;code&gt;synchronous data&lt;/code&gt;. In other words, all the features and capabilities described below apply to both &lt;code&gt;asynchronous data&lt;/code&gt; and &lt;code&gt;synchronous data&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Feature 2. Automatic caching
&lt;/h2&gt;

&lt;p&gt;Locally cache the acquired asynchronous data to avoid repeated acquisition. For synchronous data, read and write operations will be automatically performed on localstorage or cookie&lt;/p&gt;

&lt;h2&gt;
  
  
  Feature 3. Automatic update
&lt;/h2&gt;

&lt;p&gt;Provide data expiration strategy and automatically update at the right time&lt;/p&gt;

&lt;h2&gt;
  
  
  Feature 4. Reduce duplicate requests
&lt;/h2&gt;

&lt;p&gt;When accessing data in multiple places in the program at the same time, the server API will only be called once. If it is synchronous data, only one operation will be called for localstorage or cookie&lt;/p&gt;

&lt;h2&gt;
  
  
  Feature 5. Memory optimization
&lt;/h2&gt;

&lt;p&gt;Although the data managed by Zova Model is in a global state, it does not always occupy memory, but provides a mechanism for memory release and recycling. Specifically, it is to create cache data according to business needs when creating a Vue component instance, release the reference to the cache data when the Vue component instance is unmounted, and if no reference and expiration time reached, the garbage collection mechanism (GC) will be triggered to complete the release of memory, thereby saving memory usage. This has significant benefits for large projects and scenarios where users need to interact with the interface for a long time&lt;/p&gt;

&lt;h2&gt;
  
  
  Feature 6. Persistence
&lt;/h2&gt;

&lt;p&gt;Local cache can be persisted and can be automatically restored when the page is refreshed to avoid server requests. If it is asynchronous data, it will be automatically persisted to &lt;code&gt;IndexDB&lt;/code&gt; to meet the storage needs of large amounts of data. If it is synchronous data, it will be automatically persisted to &lt;code&gt;localstorage&lt;/code&gt; or &lt;code&gt;cookie&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Memory optimization&lt;/code&gt; and &lt;code&gt;persistence&lt;/code&gt; work together, and the effect is more obvious for large projects. For example, the data obtained from the server will generate a local cache and automatically persist. When no longer used and expires, the local cache will be automatically destroyed to release memory. When the data is accessed again, the local cache data will be automatically restored from persistence instead of requesting the data from the server again&lt;/p&gt;

&lt;h2&gt;
  
  
  Feature 7. SSR support
&lt;/h2&gt;

&lt;p&gt;Different types of state data will also have different implementation mechanisms in SSR mode. Zova Model smoothes out the differences in these state data and uses a unified mechanism to hydrate them, making the implementation of SSR more natural and intuitive, significantly reducing the mental burden&lt;/p&gt;

&lt;h2&gt;
  
  
  Feature 8. Automatic namespace isolation
&lt;/h2&gt;

&lt;p&gt;Zova manages data through Model Bean. The Bean itself has a unique identifier and can be used as a namespace for data, thereby automatically ensuring the uniqueness of the state data naming inside the Bean and avoiding data conflicts&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;See: &lt;a href="https://zova.js.org/guide/essentials/ioc/bean-identifier.html" rel="noopener noreferrer"&gt;Bean Identifier&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to create a model bean
&lt;/h2&gt;

&lt;p&gt;Zova provides a VSCode extension that allows you to easily create a model bean through the context menu&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Context Menu - [Module Path]: &lt;code&gt;Zova Create/Bean: Model&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Enter the name of model bean according to the prompt, such as &lt;code&gt;todo&lt;/code&gt;. The VSCode extension will automatically create the code skeleton of &lt;code&gt;model bean&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;For example, create a Model Bean &lt;code&gt;todo&lt;/code&gt; in the &lt;code&gt;demo-todo&lt;/code&gt; module&lt;/p&gt;

&lt;p&gt;&lt;code&gt;demo-todo/src/bean/model.todo.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Model&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zova&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;BeanModelBase&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zova-module-a-model&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ModelTodo&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;BeanModelBase&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;@Model&lt;/code&gt; decorator&lt;/li&gt;
&lt;li&gt;Inherited from base class &lt;code&gt;BeanModelBase&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Async Data
&lt;/h2&gt;

&lt;p&gt;The core of TanStack Query is to manage server-side data. For simplicity, only the definition and use of the &lt;code&gt;select&lt;/code&gt; method are shown here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For complete code examples, please see: &lt;a href="https://github.com/cabloy/zova/blob/main/zova-dev/src/suite/a-demo/modules/demo-todo/src/bean/model.todo.ts" rel="noopener noreferrer"&gt;demo-todo&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How to define
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ModelTodo&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;$useQueryExisting&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;queryKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;select&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;queryFn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Invoke &lt;code&gt;$useQueryExisting&lt;/code&gt; to create a Query object

&lt;ul&gt;
&lt;li&gt;Why not use the &lt;code&gt;$useQuery&lt;/code&gt; method? Because asynchronous data is generally loaded asynchronously when needed. Therefore, we need to ensure that the same Query object is always returned when the &lt;code&gt;select&lt;/code&gt; method is invoked multiple times, so the &lt;code&gt;$useQueryExisting&lt;/code&gt; method must be used&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Pass in &lt;code&gt;queryKey&lt;/code&gt; to ensure the uniqueness of the local cache&lt;/li&gt;

&lt;li&gt;Pass in &lt;code&gt;queryFn&lt;/code&gt; and call this function at the appropriate time to obtain server data

&lt;ul&gt;
&lt;li&gt;service.todo.select: see &lt;a href="https://zova.js.org/guide/essentials/scope/service.html" rel="noopener noreferrer"&gt;Api service&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  How to use
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;demo-todo/src/page/todo/controller.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ModelTodo&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../../bean/model.todo.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ControllerPageTodo&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Use&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nx"&gt;$$modelTodo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ModelTodo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Inject Model Bean instance: &lt;code&gt;$$modelTodo&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;demo-todo/src/page/todo/render.tsx&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RenderTodo&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;todos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$$modelTodo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;          &lt;span class="p"&gt;})}&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Invoke &lt;code&gt;select&lt;/code&gt; method to obtain the Query object

&lt;ul&gt;
&lt;li&gt;The render method will be executed multiple times, and repeated calls to the &lt;code&gt;select&lt;/code&gt; method return the same Query object&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Directly use the state and data of the Query object

&lt;ul&gt;
&lt;li&gt;See: &lt;a href="https://tanstack.com/query/latest/docs/framework/vue/guides/queries" rel="noopener noreferrer"&gt;TanStack Query: Queries&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  How to support SSR
&lt;/h3&gt;

&lt;p&gt;In SSR mode, we need to use asynchronous data like this: load the state data on the server, and then render it into an HTML string through the render method. The state data and HTML string will be sent to the client at the same time, and the client will still use the same state data when hydrating so as to maintain state consistency&lt;/p&gt;

&lt;p&gt;To implement the above logic, only one step is required in Zova Model:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;demo-todo/src/page/todo/controller.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ModelTodo&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../../bean/model.todo.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ControllerPageTodo&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Use&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nx"&gt;$$modelTodo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ModelTodo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;queryTodos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$$modelTodo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;queryTodos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;suspense&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queryTodos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;queryTodos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Just invoke &lt;code&gt;suspense&lt;/code&gt; in the &lt;code&gt;__init__&lt;/code&gt; method to wait for asynchronous data loading to complete&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Sync Data: localstorage
&lt;/h2&gt;

&lt;p&gt;Since the server does not support &lt;code&gt;window.localStorage&lt;/code&gt;, the localstorage state data does not participate in the SSR hydration process&lt;/p&gt;

&lt;p&gt;The following demonstrates storing user information in localstorage, and the state will be retained when the page is refreshed&lt;/p&gt;

&lt;h3&gt;
  
  
  How to define
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ModelUser&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;BeanModelBase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;user&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;ServiceUserEntity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;$useQueryLocal&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;queryKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Unlike &lt;code&gt;async data&lt;/code&gt; definition, &lt;code&gt;sync data&lt;/code&gt; is defined directly in the initialization method &lt;code&gt;__init__&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Invoke &lt;code&gt;$useQueryLocal&lt;/code&gt; to create a Query object&lt;/li&gt;
&lt;li&gt;Pass in &lt;code&gt;queryKey&lt;/code&gt; to ensure the uniqueness of the local cache&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How to use
&lt;/h3&gt;

&lt;p&gt;Read and set data directly like regular variables&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;newUser&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Sync Data: cookie
&lt;/h2&gt;

&lt;p&gt;Cookies in &lt;code&gt;Request Header&lt;/code&gt; are automatically used on the server side, and &lt;code&gt;document.cookie&lt;/code&gt; is automatically used on the client side, thus automatically ensuring the consistency of cookie state data during SSR hydration&lt;/p&gt;

&lt;p&gt;The following demonstrates storing the user Token in a cookie, and the state will be retained when the page is refreshed. Thus, in SSR mode, both the client and the server can use the same &lt;code&gt;jwt token&lt;/code&gt; to access the backend API services&lt;/p&gt;

&lt;h3&gt;
  
  
  How to define
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ModelUser&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;BeanModelBase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;token&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;$useQueryCookie&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;queryKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Unlike &lt;code&gt;async data&lt;/code&gt; definition, &lt;code&gt;sync data&lt;/code&gt; is defined directly in the initialization method &lt;code&gt;__init__&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Invoke &lt;code&gt;$useQueryCookie&lt;/code&gt; to create a Query object&lt;/li&gt;
&lt;li&gt;Pass in &lt;code&gt;queryKey&lt;/code&gt; to ensure the uniqueness of the local cache&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How to use
&lt;/h3&gt;

&lt;p&gt;Read and set data directly like regular variables&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;newToken&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Sync Data: memory
&lt;/h2&gt;

&lt;p&gt;In SSR mode, the global state data defined by the server will be synchronized to the client and automatically complete the hydration&lt;/p&gt;

&lt;p&gt;The following demonstrates the memory-based global state data&lt;/p&gt;

&lt;h3&gt;
  
  
  How to define
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;zova-ui-quasar/src/suite-vendor/a-quasar/modules/quasar-adapter/src/bean/model.theme.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ModelTheme&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;BeanModelBase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;cBrand&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cBrand&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;$useQueryMem&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;queryKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cBrand&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Unlike &lt;code&gt;async data&lt;/code&gt; definition, &lt;code&gt;sync data&lt;/code&gt; is defined directly in the initialization method &lt;code&gt;__init__&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Invoke &lt;code&gt;$useQueryMem&lt;/code&gt; to create a Query object&lt;/li&gt;
&lt;li&gt;Pass in &lt;code&gt;queryKey&lt;/code&gt; to ensure the uniqueness of the local cache&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How to use
&lt;/h3&gt;

&lt;p&gt;Read and set data directly like regular variables&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cBrand&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cBrand&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cBrand&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Zova is a vue3 framework with ioc container. It combines the advantages of Vue/React/Angular in code style, while avoiding their shortcomings to make our development experience more elegant and reduce the mental burden. Zova has built-in a lot of interesting features, and the Model mechanism is just one of them.&lt;/p&gt;

&lt;p&gt;Zova has been open sourced, welcome to follow and participate: &lt;a href="https://github.com/cabloy/zova" rel="noopener noreferrer"&gt;https://github.com/cabloy/zova&lt;/a&gt;. You can contact me on twitter: &lt;a href="https://twitter.com/zhennann2024" rel="noopener noreferrer"&gt;https://twitter.com/zhennann2024&lt;/a&gt; &lt;/p&gt;

</description>
      <category>vue3</category>
      <category>pinia</category>
      <category>tanstackquery</category>
      <category>ssr</category>
    </item>
    <item>
      <title>How to provide types for route query parameters in Vue3</title>
      <dc:creator>Uncle Pushui</dc:creator>
      <pubDate>Tue, 06 Aug 2024 10:07:40 +0000</pubDate>
      <link>https://dev.to/uncle-pushui/how-to-provide-types-for-route-query-parameters-in-vue3-4cih</link>
      <guid>https://dev.to/uncle-pushui/how-to-provide-types-for-route-query-parameters-in-vue3-4cih</guid>
      <description>&lt;h2&gt;
  
  
  Preface
&lt;/h2&gt;

&lt;p&gt;Recently, a Vue3 framework that supports IOC containers was released: &lt;a href="https://github.com/cabloy/zova" rel="noopener noreferrer"&gt;Zova&lt;/a&gt;. Unlike previous OOP or Class solutions, Zova still uses Setup syntax at the ui interaction side and only introduces IOC containers at the business side. &lt;code&gt;IOC containers are like a key that opens the door to business engineering for us, allowing us to explore more engineering designs and capabilities.&lt;/code&gt; A netizen made a very good suggestion: Can you provide some business scenarios to explain what Class can do but Composable can't do, so that it will be more convincing.&lt;/p&gt;

&lt;p&gt;First of all, it should be noted that there are actually no business requirements that can be done by this and not by that. Different programming paradigms bring different code styles and different programming experiences, pointing to the evaluation of development efficiency and code maintainability from different paths. Therefore, it ultimately depends on the user's own habits and actual business requirements.&lt;/p&gt;

&lt;p&gt;So, here, we will take this topic &lt;code&gt;How to provide types for route query parameters in Vue3&lt;/code&gt; as an example to see what the code styles of Composable and IOC containers are different.&lt;/p&gt;

&lt;h2&gt;
  
  
  Business requirements
&lt;/h2&gt;

&lt;p&gt;Here is a page component &lt;code&gt;User&lt;/code&gt;, which can pass three parameters through &lt;code&gt;Query&lt;/code&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Param&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Default&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;id&lt;/td&gt;
&lt;td&gt;number&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;name&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;''&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;married&lt;/td&gt;
&lt;td&gt;boolean&lt;/td&gt;
&lt;td&gt;false&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Composable: native
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Open page
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRouter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/test/demo/user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;kevin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;married&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From the perspective of Typescript types, this code has the following two problems:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;path: no type constraints and autocompletion&lt;/code&gt;. This will have the following three hidden dangers:

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;Can't remember&lt;/code&gt;: If the path is long or the words are complex, you can't remember the path and need to find it from the source file&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Written wrong&lt;/code&gt;: If you accidentally write it wrong, there is no prompt, and the error will only be exposed when it is actually run&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Changed&lt;/code&gt;: If the path is changed during subsequent code maintenance, there will be no prompt in the code here, and the error will only be exposed when it is actually run&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;query: only limited type constraints, inconsistent with business types&lt;/code&gt;

&lt;ol&gt;
&lt;li&gt;For example, &lt;code&gt;Boolean&lt;/code&gt; type is not supported and must be forced to &lt;code&gt;String&lt;/code&gt; type&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;/ol&gt;

&lt;h3&gt;
  
  
  2. Obtain parameters
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;route&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRoute&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;married&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;married&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since no type tool is provided, each parameter needs to be processed separately&lt;/p&gt;

&lt;h2&gt;
  
  
  Composable：useRouteQuery
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Open page
&lt;/h3&gt;

&lt;p&gt;(Same as above)&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Obtain parameters
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useRouteQuery&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@vueuse/router&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRouteQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Number&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRouteQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;married&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRouteQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;married&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;false&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  IOC container
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Define types
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;import { zz } from 'zova';
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;export const QuerySchema = zz.object({
&lt;/span&gt;&lt;span class="gi"&gt;+  id: zz.number().default(0),
+  name: zz.string().default(''),
+  married: zz.boolean().default(false),
&lt;/span&gt;});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;zz&lt;/code&gt; is an enhanced version based on &lt;code&gt;zod&lt;/code&gt;, which is specially processed for route parameters and supports &lt;code&gt;array&lt;/code&gt; and &lt;code&gt;json&lt;/code&gt;. For details, see: &lt;a href="https://zova.js.org/guide/techniques/router/zod.html" rel="noopener noreferrer"&gt;Zova: zod&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;You can specify the default value when defining the types&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcg89u5mcg0ylkxzs5eye.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcg89u5mcg0ylkxzs5eye.gif" alt="Image description" width="700" height="491"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Open page
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolvePath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/test/demo/user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;kevin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;married&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The parameters of &lt;code&gt;resolvePath&lt;/code&gt; have type constraints and autocompletion, and are consistent with the business types&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq1wkd6xbok3ewqgofxul.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq1wkd6xbok3ewqgofxul.gif" alt="Image description" width="700" height="497"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Obtain parameters
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;married&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;married&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Get parameter values ​​directly through &lt;code&gt;this.$query&lt;/code&gt;, with precise types and no need to handle default values&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fquelxhl2s12kr8pwr2dx.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fquelxhl2s12kr8pwr2dx.gif" alt="Image description" width="700" height="497"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;From the above example comparison, we can see that the IOC container can separate the &lt;code&gt;definition&lt;/code&gt; from the &lt;code&gt;usage&lt;/code&gt;, and the scaffolding can be created by tools on the definition side to further simplify the writing of the definition. Since the normative codes such as TS types and default values ​​are completed on the definition side, the code on the usage side is more concise and intuitive. I don't know what your code style is, and if there is a better way to implement it, you are welcome to communicate in the comment area.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://vueuse.org/router/useRouteQuery/" rel="noopener noreferrer"&gt;VueUse: useRouteQuery&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://zova.js.org/guide/techniques/router/route-query.html" rel="noopener noreferrer"&gt;Zova: Route Query&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Come on, provide an ioc container for Vue3 that is comparable to Angular</title>
      <dc:creator>Uncle Pushui</dc:creator>
      <pubDate>Thu, 01 Aug 2024 12:11:17 +0000</pubDate>
      <link>https://dev.to/uncle-pushui/come-on-provide-an-ioc-container-for-vue3-that-is-comparable-to-angular-5ok</link>
      <guid>https://dev.to/uncle-pushui/come-on-provide-an-ioc-container-for-vue3-that-is-comparable-to-angular-5ok</guid>
      <description>&lt;h2&gt;
  
  
  Why provide an ioc container for Vue3
&lt;/h2&gt;

&lt;p&gt;Vue3 is fully capable of developing large-scale business systems due to its excellent responsive system and convenient functional features. However, we must not only be able to do it, but also do it better. The key to large-scale business systems is decoupling, thereby slowing down the growth of messy code. The ioc container is currently the best decoupling tool. Angular introduced the ioc container from the beginning, so it has always been in a leading position in business engineering, and has been waving to other front-end frameworks: "I'm waiting for you in front, and I hope to see you again in three years." So, I will try to take two steps forward, provide the ioc container in Vue3, and introduce other engineering capabilities based on it to get a new framework: &lt;a href="https://github.com/cabloy/zova" rel="noopener noreferrer"&gt;Zova&lt;/a&gt;. Is it easy to use? I look forward to your trial and feedback&lt;/p&gt;

&lt;h2&gt;
  
  
  IOC Containers
&lt;/h2&gt;

&lt;p&gt;There are two types of ioc containers in Zova:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;global ioc container&lt;/code&gt;(referred to as app container): During system initialization, a unique global bean container will be automatically created. Bean instances created in this container are all singleton mode&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;component instance ioc container&lt;/code&gt;(referred to as ctx container): When creating Vue component instances, the system will create a bean container for each of them. Bean instances created in this container can share data and logic within the scope of the component instance&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Bean Class
&lt;/h2&gt;

&lt;p&gt;Zova adopts a modular system, and Bean Classes are provided by different modules. When using the Bean Class inside the same module, you can directly locate it based on &lt;code&gt;Class type&lt;/code&gt;. When using cross-module, you can locate it based on &lt;code&gt;Bean identifier&lt;/code&gt; instead of &lt;code&gt;Class type/file path&lt;/code&gt;, which is conducive to achieving loose coupling between modules&lt;/p&gt;

&lt;p&gt;Therefore, Zova provides two types of Bean Classes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Anonymous bean&lt;/code&gt;: The class decorated with &lt;code&gt;@Local&lt;/code&gt; is an &lt;code&gt;anonymous bean&lt;/code&gt;. This type of bean is only used within the module, there is no naming conflict, and it is easy to define and use&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Named bean&lt;/code&gt;: Except for &lt;code&gt;@Local&lt;/code&gt;, the classes decorated by the other decorator functions are &lt;code&gt;named beans&lt;/code&gt;. Zova provides a unified naming convention for such beans, which can avoid naming conflicts and facilitate cross-module usage&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Injection mechanism
&lt;/h2&gt;

&lt;p&gt;Zova provides the following injection mechanisms:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Bean Class
&lt;/h3&gt;

&lt;p&gt;Use &lt;code&gt;Bean Class&lt;/code&gt; to lookup and inject bean instance in the ioc container, and automatically create one if not exist. This mechanism is generally used for &lt;code&gt;same module injection&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ModelTodo&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../../bean/model.todo.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ControllerTodo&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Use&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nx"&gt;$$modelTodo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ModelTodo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Bean identifier
&lt;/h3&gt;

&lt;p&gt;Use &lt;code&gt;Bean identifier&lt;/code&gt; to lookup and inject bean instance in the ioc container, and automatically create one if not exist. This mechanism is generally used for &lt;code&gt;cross-module injection&lt;/code&gt; and &lt;code&gt;hierarchical injection&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ModelTabs&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zova-module-a-tabs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ControllerLayout&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a-tabs.model.tabs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;$$modelTabs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ModelTabs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Lookup and inject bean instance through &lt;code&gt;a-tabs.model.tabs&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Therefore, only the type of ModelTabs needs to be imported to maintain the loose coupling relationship between modules&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Registration name
&lt;/h3&gt;

&lt;p&gt;Lookup and inject bean instance in the ioc container through the &lt;code&gt;registration name&lt;/code&gt;, and return a null value if not exist. This mechanism is generally used for &lt;code&gt;same module injection&lt;/code&gt; and &lt;code&gt;hierarchical injection&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ModelTodo&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../../bean/model.todo.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ControllerTodo&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Use&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$$modelTodo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="nx"&gt;$$modelTodo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ModelTodo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Lookup and inject the bean instance by the registration name &lt;code&gt;$$modelTodo&lt;/code&gt;. Generally speaking, you should ensure that the bean instance has been injected in the ioc container in advance, otherwise a null value will be returned&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Variable name
&lt;/h3&gt;

&lt;p&gt;Lookup and inject the bean instance in the ioc container by the &lt;code&gt;variable name&lt;/code&gt;, and return a null value if not exist. This mechanism is generally used for &lt;code&gt;same module injection&lt;/code&gt; and &lt;code&gt;hierarchical injection&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ModelTodo&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../../bean/model.todo.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ControllerTodo&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Use&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nx"&gt;$$modelTodo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ModelTodo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Lookup and inject the bean instance by the variable name &lt;code&gt;$$modelTodo&lt;/code&gt;. Generally speaking, you should ensure that the Bean instance has been injected in the ioc container in advance, otherwise a null value will be returned&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Injection scope
&lt;/h2&gt;

&lt;p&gt;The default injection scope of &lt;code&gt;anonymous bean&lt;/code&gt; is &lt;code&gt;ctx&lt;/code&gt;, and the default injection scope of &lt;code&gt;named bean&lt;/code&gt; can be specified when defining it. Different scenes have different default injection scopes. In addition, when injecting, you can also override the default injection scope through the &lt;code&gt;injectionScope&lt;/code&gt; option in @Use&lt;/p&gt;

&lt;p&gt;Zova provides the following injection scopes: &lt;code&gt;app/ctx/new/host/skipSelf&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  1. app
&lt;/h3&gt;

&lt;p&gt;If the injection scope is &lt;code&gt;app&lt;/code&gt;, then inject the bean instance in the global ioc container to achieve the singleton effect&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// in module: test-module1&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Store&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;StoreCounter&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// in module: test-module2&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;StoreCounter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zova-module-test-module1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Test&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test-module1.store.counter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;$$storeCounter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;StoreCounter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The injection scope of &lt;code&gt;Store&lt;/code&gt; is &lt;code&gt;app&lt;/code&gt; by default, so the bean instance will be lookuped and injected in the global ioc container through the bean identifier &lt;code&gt;test-module1.store.counter&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. ctx
&lt;/h3&gt;

&lt;p&gt;If the injection scope is &lt;code&gt;ctx&lt;/code&gt;, then inject the bean instance into the ioc container of the current component instance&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// in module: a-tabs&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ModelTabs&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// in module: test-module2&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ModelTabs&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zova-module-a-tabs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ControllerLayout&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a-tabs.model.tabs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;$$modelTabs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ModelTabs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The injection scope of &lt;code&gt;Model&lt;/code&gt; is &lt;code&gt;ctx&lt;/code&gt; by default, so the bean instance will be lookuped and injected in the ioc container of the current component instance through the bean identifier &lt;code&gt;a-tabs.model.tabs&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. new
&lt;/h3&gt;

&lt;p&gt;If the injection scope is &lt;code&gt;new&lt;/code&gt;, then directly create a new bean instance&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// in module: a-tabs&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ModelTabs&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// in module: test-module2&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ModelTabs&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zova-module-a-tabs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ControllerLayout&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Use&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;beanFullName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a-tabs.model.tabs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;injectionScope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;new&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="nx"&gt;$$modelTabs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ModelTabs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Since the &lt;code&gt;injectionScope&lt;/code&gt; option is specified as &lt;code&gt;new&lt;/code&gt;, a new bean instance will be directly created through the bean identifier &lt;code&gt;a-tabs.model.tabs&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Hierarchical injection
&lt;/h2&gt;

&lt;p&gt;Injection scope supports not only &lt;code&gt;app/ctx/new&lt;/code&gt;, but also &lt;code&gt;host/skipSelf&lt;/code&gt; which is called &lt;code&gt;hierarchical injection&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4. host
&lt;/h3&gt;

&lt;p&gt;If the injection scope is &lt;code&gt;host&lt;/code&gt;, the bean instance will be lookuped in the ioc container of the current component instance and all parent containers in turn. If it does not exist, a null value is returned&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// in parent component&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ModelTabs&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zova-module-a-tabs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Parent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a-tabs.model.tabs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;$$modelTabs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ModelTabs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// in child component&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ModelTabs&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zova-module-a-tabs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Child&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Use&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;injectionScope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;host&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="nx"&gt;$$modelTabs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ModelTabs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Since the parent component has already injected the &lt;code&gt;ModelTabs&lt;/code&gt; bean instance, the child component can directly lookup and inject it&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Hierarchical injection&lt;/code&gt; also supports all injection mechanisms: &lt;code&gt;Bean Class/Bean identifier/Registration name/Variable name&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. skipSelf
&lt;/h3&gt;

&lt;p&gt;If the injection scope is &lt;code&gt;skipSelf&lt;/code&gt;, then lookup the bean instance in all parent containers in turn. If it does not exist, a null value is returned&lt;/p&gt;

&lt;p&gt;Github: &lt;a href="https://github.com/cabloy/zova" rel="noopener noreferrer"&gt;https://github.com/cabloy/zova&lt;/a&gt;&lt;br&gt;
Demo: &lt;a href="https://zova.js.org/zova-demo/" rel="noopener noreferrer"&gt;https://zova.js.org/zova-demo/&lt;/a&gt;&lt;br&gt;
Docs: &lt;a href="https://zova.js.org/guide/essentials/ioc/introduction.html" rel="noopener noreferrer"&gt;https://zova.js.org/guide/essentials/ioc/introduction.html&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
