跪拜 Guibai
← Back to the summary

A Vue3 Low-Code Form Engine That Runs Linkage on Reactivity, Not DOM Nodes


theme: channing-cyan

Article cover

Foreword

Low-code has been around for over a decade, from early hype and capital frenzy to today's industry shakeout and differentiation. The market has long shed the "low-code is a silver bullet" bubble. It's not that low-code has cooled off — the AI era has redefined R&D labor costs: AI coding tools are no longer simple snippet completions; they let developers return to product thinking, balancing architecture design with detail experience, and rapidly iterating on user feedback. Low-code is precisely hitting a new breakout window in this cycle. More critically, AI agents are bridging the last mile from low-code to no-code, enabling business users to generate usable forms directly through natural language. Low-code's productivity boundary has been thoroughly expanded.

The vast majority of Vue3 low-code form solutions on the market either wrap open-source projects, still using DOM-like node APIs for linkage underneath — developers must manually call imperative interfaces like getWidgetByName and setValue to complete field interactions, essentially never breaking free from the old "node manipulation" mindset — or they are deeply tied to a specific ecosystem, with high cross-system integration costs and constraints everywhere in complex business scenarios.

Our self-developed Vue3 native low-code form is built directly on Vue3's reactivity. Linkage follows data, not control nodes. A field's value, visibility, and cascading all sit on the same reactive scope chain. Changing data drives the UI; there's no need to locate nodes or set controls. This aligns more closely with Vue developers' habits and eliminates the common problems in traditional low-code forms: data misalignment, hard-to-close linkage loops, and difficult long-term maintenance. In plain terms, for the extensibility coding openings of a low-code platform, it's simply moving the way front-end developers write Vue files onto page configuration.

Below, we'll use real cases to introduce and compare the field linkage configuration features under these two mechanisms that everyone cares about.

Linkage Cases

Exam Volunteer Application

1.gif

Linkage logic:

Transcript Linkage

2.gif

Linkage logic:

Linkage Configuration

Traditional Low-Code Forms

Here we use vform3 as a representative to show how traditional low-code form platforms driven by node APIs implement page linkage configuration. Let's take our first simple linkage case as the requirement and see how the vform3 platform configures it.

First, look at the preview effect:

3.gif

Next, let's look at the linkage configuration in vform3. First, the total score: this relies on the number field's calculation formula feature. The operation is point-and-click; providing a direct expression editing mode would be more straightforward.

image-20260827100152562.png

The calculation capability provided for number fields above does achieve association listening for other numeric fields, but the handling is single-purpose — only arithmetic evaluation. For the linkage and update requirements between the volunteer text box and other fields, vform3 does not support directly setting associated listening fields on the target field. We can consider two hooks:

image-20260827103336100.png

It is used for unified processing logic when form data changes, and as a fallback for binding multi-source field processing logic to a specific target field.

image-20260827110315651.png

Here we add control logic on the total score's onChange field hook:

image-20260827111239015.png

From the code style, it's clear that vform3's underlying component interaction is not Vue's mechanism, but falls back to node API operations. Vue is mostly used for rendering individual components; the inability to achieve data reactivity between components is still regrettable.

For slightly more complex linkage requirements, we won't demonstrate further with this platform.

Vue3 Native Low-Code Form

Just as front-end development frameworks evolved from DOM-based jQuery to MVVM-based Vue, React, and Angular, low-code forms are moving from node APIs to fully data-driven approaches. In the AI era, this creates better conditions for the ultimate goal of no-code platforms, because a data-driven JSON schema is undoubtedly more flexible, concise, and extensible in configuration than a node-API-driven low-code platform. As the foundation of a no-code platform, it makes it easier for AI agents to read, write, and maintain.

Looking back at our initial low-code platform, basic input controls achieve two-way binding through field names:

image-20260827131804140.png

Text Expressions

For text elements, evaluation is done via expressions; whether the result is displayed on the interface and written to the model is controlled by toggles.

image-20260827132142006.png

Double-clicking the expression input box opens a code editor with intelligent autocomplete and syntax highlighting.

image-20260827132716745.png

Here you can write expressions directly. Fields are auto-suggested based on the scope they belong to, and some built-in helper functions are supported, such as: isEmpty, sumAll (if any operand is empty, no calculation occurs), sumSkip (ignores empty operands for summation), etc. Besides expressions, you can also write arrow functions. The implementation below is equivalent to the sumAll(...) logic; let's expand on it:

(scope, api) => api.hasEmpty(scope.chinese, scope.math, scope.english)
  ? undefined
  : scope.chinese + scope.math + scope.english

This provides the scope where the current field resides, through which associated fields are obtained. The scope also supports addressing; for example, you can directly write in expressions: $parent.role, $root.totalScore, $parent.$parent.obj.remark, etc. For arrow functions, use the second parameter api's related methods to access them, such as calling the helper function api.hasEmpty(...). To get field values through addressing, call something like api.get('$root.name').

Usually, when obtaining scope variables in arrow functions, we can simplify using destructuring. For example, the above form destructured as:

({ chinese, math, english }, api) => {
  if (api.hasEmpty(chinese, math, english)) return undefined
  return chinese + math + english
}

Vue3 Alignment Mechanism

The expression binding here corresponds to Vue3's computed property.

Having understood the expression property of text elements, let's configure a score-tier field that is hidden on the page and not written to the model, calculating its expression based on the previous total score field:

image-20260827140616098.png

Control Styles

Following Vue's mechanism of dynamically binding styles to HTML elements via computed properties, similar page configuration is naturally supported here:

image-20260827141317277.png

Styles here directly use pre-compiled TailwindCSS classes from the application file. Let's see the effect:

4.gif

Single-Source Linkage Handling

When a target field box depends on the value change of a certain field to perform update logic, you can directly apply linkage configuration to the target element. For example, here is the listening configuration of the volunteer text box on the score-tier text:

image-20260827143256521.png

The fields to listen to follow the data model hierarchy; here it would be a multi-level dropdown. For now, we won't cover cross-data-object-level field listening.

When the source field value changes, the handler executes. Double-click to open the editor. The first parameter is the changed value of the listened source field, and the second is the field object of the configured host element — a Vue reactive object. Update the field configuration metadata according to the logic here.

image-20260827143752635.png

Our initial low-code platform's components currently only consider ant design vue as the UI. Therefore, for a large text box control, you can actually directly update the field.props.status property to error to achieve the control style configuration requirement mentioned earlier. This also shows that data-driven configuration channels are diversified and more flexible, giving users more choices.

Revealing Single-Source Linkage Vue3 Alignment Mechanism

The form engine's underlying listening on source fields aligns with Vue3's watch feature. A target field can add multiple linkage configurations to listen to different source fields. For the same source field, it only watches once, and multiple registered handlers are triggered in array form.

If a source field is unmounted due to page linkage, the corresponding watch stops, and the listener first receives undefined for resetting; if the source field reappears, it re-subscribes. Deleting a source field in the designer triggers form recompilation, and old subscriptions are released together.

If the target field (dependent) is hidden, listening is meaningless; it is removed from the source's active table, and handlers are no longer executed. If it is the last listener of this source, the watch on the source is also stopped and removed. When the target field is shown again, it reactivates and runs the handler again based on the source field's current value.

Multi-Source Linkage Handling

For complex scenarios where multiple source fields need to be listened to for different processing on a target field, we provide a plugin configuration feature for forms and sub-containers. One or more plugin configurations can be added to complete different complex linkage processing tasks.

For the large text box linkage example in the exam volunteer application we demonstrated earlier, here we configure a form-level plugin:

image-20260827152056288.png

The when part is the code that triggers the plugin execution condition; it can be triggered based on other field linkages. Here we trigger it always. The handler is essentially a "universal" listener processor. It can handle multi-source field listening at the current data object level and even across levels, and update target fields at the current level and across levels. Generally, the recommended practice is: plugins configured at a certain level act on fields at that level.

Let's see how we replace the previous single-source field listening with a plugin. Clearly, here we inject computed properties into the form engine layer through the callback provided in exp to listen to the fields involved in processing. The target fields to update are obtained via api.at('...'). Compared to linkage binding on input controls, this has a larger scope and is more flexible.

image-20260827152700519.png

Here exp can be called multiple times to build different listening chains. For example, here we split the binding of the three associated source fields into two:

(exp, api) => {
  const cm = exp((scope) => (scope.chinese || 0) + (scope.math || 0))
  const english = exp((scope) => scope.english || 0)
  const t = Number(cm) + Number(english)
  const band = t >= 280 ? 'tier1' : t >= 250 ? 'tier2' : t >= 220 ? 'tier3' : 'fail'
  ...
}

The execution chain: the inner execution of exp is watched by Vue's computed properties. Modifying the Chinese or Math field, the bound english computed property caches and does not recalculate; updating the English field alone works similarly. The execution of handler is watched by our form engine, which watches changes in the execution results of when and exp. So the entire handler processing function is completed by the collaboration of these two layers of "watching" mechanisms.

Summary

Writing this far, I feel a mix of trepidation and relief. Through over a month of diligent self-research on the form engine and the low-code drag-and-drop configuration platform, a prototype of a new type of low-code platform has emerged. Many iterations were made in between, solving numerous performance issues in testing the data-driven form engine. I also referenced the user experience of similar products on the market and wrote this modest piece through comparison. I never thought of challenging mature low-code form solutions from peers like an egg against a rock. I simply moved the direct data manipulation experience of writing forms in Vue3 code from code files to page configuration. Unexpectedly, it can solve very complex and flexible enterprise form linkage requirements, which brings deep satisfaction. Keep pushing forward!