VanX 0.4.0: Smart diff, global app state, server-driven UI, serialization and more #292
Tao-VanJS
announced in
Announcements
Replies: 2 comments 2 replies
-
const countries = [...]
const data = vanX.reactive({filter: ""})
const derived = vanX.reactive({
filteredCountries: vanX.calc(
() => countries.filter(c => c.toLowerCase().includes(data.filter.toLowerCase()))),
})
...
vanX.list(ul, filteredCountries, v => li(v)) Hi, I'm confused with this example. Where did |
Beta Was this translation helpful? Give feedback.
2 replies
-
Nice improvements to vanX.list! |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
Hi fellow VanJSers,
I'm happy to announce the release of VanX
0.4.0
. 🎉🎉🎉VanX
0.4.0
is a significant update to VanJS's ultra-lightweight extension. In this release, many new features and improvements are being delivered, which opens up a new spectrum of modern programming paradigms with VanJS. We believe the release will further improve developer experience of VanJS and simplify the workload of building robust, performant and easy-to-maintain UI apps.✨ What's in the release?
1.
vanX.replace
is revamped to allow smart diff / updateWe revamped
vanX.replace
to make substantial improvements. First, it can take plain objects (including arrays), in addition to replacement functions, as its second argument. This allows more straightforward code instead of the workaround like:More importantly, we enabled smart diff / update feature for
vanX.replace
. This allows efficient replacement for a complex object with multiple hierarchies. Specifically,vanX.replace
will traverse the entire object tree, do a diff betweenreplacement
andobj
, and only update leaf-level fields with different values. For instance, if you have a global reactive objectappState
for a TODO app like that:Calling
will only get the
done
field of the 2nd element initems
updated, which is equivalent toWith the revamp on
vanX.replace
, you can consolidate the entire app state into a single reactive object, and freely replace the entire app state in one single line without worrying about the performance implication.We believe this will greatly ease the development of your apps. Not only you can easily serialize / deserialize your global app state, it also enables you with convenient programming paradigms such as server-driven UI (SDUI), where the client completely relies on server events to update the UI. As an example of this technique, you can build a Chat app that updates the online / offline status of your friend list based on server-side events.
2. Calculated fields (
vanX.calc(() => ...)
) are allowed invanX.list
With VanX
0.4.0
, you can now use calculated fields invanX.list
to build reactive lists. This makes it easier for use cases such as building a filtered list. Before0.4.0
, to build a filtered list, you need to have some ad-hoc solution withvanX.replace
like this:With this feature in VanX
0.4.0
, building the filtered list becomes much more intuitive:You can refer to this section for the complete example.
Under the hood, whenever the derived value of the calculated field changes, VanX will call
vanX.replace
to perform the smart diff / update, which is essentially the same as the ad-hoc implementation withvanX.replace
shown earlier.3.
vanX.compact
to eliminate "holes" in reactive objectsBecause of the deletion of items in reactive lists, "holes" might exist in arrays inside the reactive objects, which are undesirable when the reactive objects are serialized (e.g.: they are converted to
null
inJSON.stringify
). Before VanX0.4.0
, users are advised to useitems.filter(_ => 1)
to eliminate holes, which is hacky and hard-to-understand. In VanX0.4.0
, we provide the official support for hole-elimination -vanX.compact
.vanX.compact
eliminates holes recursively. i.e.: it traverses the entire object tree of the input reactive object and returns a new object with holes in all encountered arrays eliminated. It's recommended to callvanX.compact
before serializing your reactive object in all circumstances. You can refer to this section for more details.4.
vanX.list
allows DOM elements as its 1st argumentPrior to VanX
0.4.0
, users are required to provide a function() => Element
as the 1st argument for the container element of reactive list. This is convenient when tag functions such asul
,ol
ordiv
are directly used there. However, if you want some customized property values of the container element, such as<div class="item-list">
, you need to specify something likeWith VanX
0.4.0
, the code can be simplified toas DOM elements can be directly specified there.
5.
vanX.raw
allows access to deeply nested fields without registering the dependencyvanX.raw was introduced in VanX
0.3.0
for accessing a field of a reactive object without registering the dependency. In VanX0.4.0
, we extendedvanX.raw
to allow access to deeply nested fields (without registering the dependency). For instance, before VanX0.4.0
,vanX.raw(data).a.a
registers the dependency ofdata.a.a
but notdata.a
. After VanX0.4.0
release,vanX.raw(data).a.a
will just access the fielddata.a.a
without registering any dependency.6.
van-x.d.ts
changesWe made some changes to the type systems of VanX. In VanX
0.4.0
we relaxed the type checking for functions likevanX.list
andvanX.replace
, which checked whether the 1st argument passed-in is a reactive object. This kind of type checkings brings inconvenience when sub-fields of reactive objects are being passed there.Specifically, to illustrate the problem, since reactive objects and their non-reactive counterparts don't have any difference in shape, before VanX
0.4.0
, we were introducing an artificial "reactivity marker" to indicate whether an object is reactive or not. However, the reactivity marker only applies to the top-level reactive objects, not to their sub-fields. Thus sub-fields of reactivity objects lose the "reactivity marker" and are not considered reactive by the type system, which breaks the type checking when they are passed into functions likevanX.list
andvanX.replace
, even though the usage is completely legitimate.As a result, users usually need the workaround like
any
-casting to soothe the type system, like the code below:which is cumbersome and defeats the purpose of type-checking.
Thus, essentially, we're facing 2 choices to proceed:
We decided to go with 2nd option in VanX
0.4.0
. Our current assessment is that people rarely mistakenly pass non-reactive objects into places expecting reactivity (as reactivity is the fundamental concept of reactive programming), and when this does happen, it should be relatively easy to figure it out. After all, TypeScript is duck-typing-based with primary focus on shape-conformance. That being said, I am open to actual feedback from users to see if we need to introduce more complex and strict type system (or other kinds of mechanism) for reactivity checking in future releases.With all the improvement, the bundle size of VanX increases slightly. Gzipped bundle increases to
1106 bytes
(1.1kB) from1047 bytes
(1.0kB) (59 bytes
increase), while minified bundle increases to2191 bytes
(2.1kB) from1952 bytes
(1.9kB) (239 bytes
increase) - still being an ultra-lightweight extension to VanJS.❤️ Hope you can enjoy!
Beta Was this translation helpful? Give feedback.
All reactions