Hi fellow VanJSers,
After many weeks of hard work, also with the great help from the community with all the constructive feedback in Discussions, I am delighted to announce: VanJS 1.0.0 is finally here! ๐๐๐
1.0.0 is a significant milestone for VanJS. Not only it is by far the biggest feature update, it also marks the commitment of API stability and forward compatibility for future releases.
โจ What's new?
1. More intuitive API for complex state binding
As a UI framework that features simplicity and ergonomics, VanJS enables you to write concise and expressive code. We have examples where applications built with VanJS are 3~4 times smaller than the React counterpart (1, 2). However, prior to 1.0.0 release, the API for complex state binding is not in its best form. Taking the following complex binding code as an example:
div(van.bind(renderPre, renderPre => (renderPre ? pre : div)(text)))
The code is not elegant. Specifically, renderPre has to repeat itself 3 times. In VanJS 1.0.0, the code above will be simplified as:
div(() => (renderPre.val ? pre : div)(text))
What makes it possible is a technique employed in 1.0.0 implementation, where dependencies are automatically detected during the execution of the binding function (so that you don't need to explicitly declare the dependencies). The dependency detection technique is smart enough for all kinds of complex binding functions. For instance, the binding function:
() => cond.val ? a.val + b.val : c.val + d.val
will (only) be triggered by updates of state a, b and cond if cond.val is true, and will (only) be triggered by updates of state c, d and cond if cond.val is false, as illustrated in the code here.
State-derived properties works similarly:
button({style: {deps: [color], f: color => `background-color: ${color};`}})
will be simplified to:
button({style: () => `background-color: ${color.val};`})
You can find the detailed documentation of the new API in State-derived properties and State-derived child nodes section of the tutorial.
The new API, and the implementation technique are inspired by the great discussion post with @onsclom. Thank you @onsclom so much for making it happen!
2. Derived states with van.derive
Derived states is a commonly requested feature for VanJS, as shown from several discussion posts. In 1.0.0 release, we introduce a new, unified API for defining derived states - van.derive. With van.derive, defining derived states is simple and intuitive:
const firstName = van.state("Tao"), lastName = van.state("Xin") const fullName = van.derive(() => `${firstName.val} ${lastName.val}`)
Similar to UI binding functions, the dependencies are automatically detected and the values of derived states are always in sync with the result of derivation functions.
3. Side effects
van.derive can also define side effects. In this case you can simply discard the return value of van.derive. For example, the piece of code below:
const counter = van.state(0) van.derive(() => console.log("Counter: ", counter.val))
defines a side effect to log the value of counter whenever it changes.
4. More advanced state derivations
Moreover, van.derive can enable much more advanced state derivations, such as delaying state propagation, throttling state updates, publishing state updates via data streams. You can refer to this section for the details.
5. NPM support
1.0.0 release introduces the NPM support. The NPM package vanjs-core is published. NPM support makes it handy to build web applications with tools like Vite or Parcel. You can also build your own NPM packages that depend on VanJS. For instructions, refer to NPM Integration section of "Getting Started" page.
Here is a sample Hello World app built with VanJS NPM + Vite (source code).
6. Supports null as a valid property value for tag functions
1.0.0 adds the support of null as a valid property value for tag functions. null is a valid value for many properties of DOM elements, such as on... handlers, aria... properties, etc.
7. More graceful behavior for invalid property values provided in tag functions
In addition, VanJS 1.0.0 handles the situation of invalid property values more gracefully. For instance, the following code:
div({id: undefined}, "Text")
will produce the element <div id="undefined">Text</div> in 1.0.0, whereas an error will be thrown in a pre-1.0.0 version.
The more graceful handling can make sure web applications provide a more robust user experience even when there are bugs in the application code.
Note that an error will still be thrown to indicate an invalid property is provided if van-{version}.debug.js is used.
8. More graceful behavior if an error is thrown in the binding function
If an error is thrown during the execution of a binding function, VanJS 1.0.0 will handle this situation more gracefully: an error is logged; the corresponding UI node remains unchanged; and the updates to other UI nodes proceed as usual. This can be demonstrated in the following code:
const num = van.state(0) van.add(document.body, div( num, () => { if (num.val > 0) throw new Error() return span("ok") }, num, ), div(button({onclick: () => ++num.val}, "Increment")), )
9. Other improvements
- Adds another demo application, a 28-line
package-lock.jsoninspector that can examine thepackage-lock.jsonfile and display all dependency packages and their versions. - Some performance optimization in the implementation.
๐ซ Migration from a pre-1.0 version
The vast majority of your existing code will continue to work after upgrading to VanJS 1.0.0.
- If your code uses
van.bindor State-derived properties, you need to update to the new unified binding API. See State-derived properties and State-derived child nodes section for detailed documentation. - If your code uses
onnewmethod to register event handlers, you need to update to the unified APIvan.deriveto define derived states or side effects. See the API referencevan.derivefor details.
All of the pre-1.0 versions are still usable via CDN and manual download. The pre-1.0 document is archived at https://legacy.vanjs.org/ for your reference.
๐ฏ What's next?
Below are the things I'm considering as next steps:
- Build and publish a set of grab-n-go UI components based on VanJS - things like modal, tabs, banner, message box to further eliminate the entry barrier of building UI apps.
- Submit VanJS for performance benchmark. I personally believe that, given its simplicity in design and performance-aware implementation, the performance of VanJS should be closer to Vanilla JavaScript than to React, and the benchmark result can validate that.
With all the added features and performance optimization, the bundle size increases slightly, gzipped bundle increases to 0.9kB (926 bytes) from 0.8kB, while minified bundle increases to 1.6kB (1625 bytes) from 1.3kB - still being the smallest reactive UI framework in the world.
โค๏ธ Hope you can enjoy!