About

VanJS: About

大道至简 (The profound truth is utmost simplicity)

Meet the Author - the Story behind VanJS


Tao Xin

Hello all,

I'm Tao Xin (辛韬), a senior staff software engineer at Google, and I'm the founder of VanJS. I would like to talk about 2 central questions about VanJS: What VanJS really is, and why I think it's good to the world.

So, what is VanJS? Well, it's a reactive UI framework. It's more than 100 times smaller than React. It doesn't require installation, configuration, dependencies or transpiling to use. But I think, in a nutshell, the best way to describe it is: VanJS is the scripting language for UI, just like bash is the scripting language for terminal.

Ever since the birth of GUI, there is no shortage of UI frameworks: MFC, Win Form, WPF, Qt, Flutter, SwiftUI, Jetpack Compose, React, React Native, to name a few. They enabled us to build highly sophisticated UI apps. But on the other hand, frameworks and tools themselves could be the entry barrier for UI programming: high-specialized IDEs, lengthy tutorials, mysterious problems that might arise here and there, being forced to program in a designated style, and most importantly, ONLY people with specialized skills can work on it. Even JavaScript, with "Script" in its name, is trying to become a compiled language: JSX, TSX, transpiling, and plugins/extensions to allow us to work with the transpiled code.

On the flip side, the default way for programmers to interact with computers remains the same for over 50 years - shells, CLI programs, and sometimes, ASCII arts. Why? Is terminal inherently better than GUI? Or does it just make programmers look cooler? I think the fundamental reason that lies behind, is the power of scripting, the power to start coding immediately in any environment, the power to build useful things with just a few lines of code, the power to easily assemble various code snippets together.

Being the scripting language for UI, is the fundamental principle that guides the design of VanJS. It's based on JavaScript so that it can work in as many environments as possibles, not only for websites, but also for webviews which most major OSes support. It has declarative composing API and reactive state binding as it enables an easier way to describe comprehensive UI logic within just a few lines of code. It has strictly 0 dependencies so that it can be used right after the code is typed. It's JSX-free thus REPL can be easily done in the browser console.

So, why is VanJS good to the world? I think the world needs a scripting way to build UI, and there are way more scenarios where UI can be more beneficial than people might have realized, for personal utilities, for teamwide tools, and for user-facing products as well. We are quite used to the categorization between front-end engineers and back-end engineers, and we are quite used to the notion that back-end engineers will never do UI. We think only a very small number of people need to know how to build UI.

But, is it really the case? I've been a back-end engineer for more than a decade. I had been leading a team which manages 100+ data processing pipelines and datasets produced by them. I felt, for many times, that we really needed a way to visualize the status of the pipelines and datasets. "But, ...", pushbacks would immediately arise after the idea, "We're not a front end team. We shouldn't do it. We don't have the expertise.", I think here, "We don't have the expertise", doesn't really mean the team is not technical capable of programming the UI logic. What it actually means is, "We don't really have the experience of dealing with mysterious, and oftentimes undocumented issues here and there that might only occur in our specific development environment, and we can't accurately estimate the amount of time needed to get them resolved." We tried to hire an intern to do the work, but the work couldn't finish because of waves of issues in the internal build systems.

I am never a front-end engineer, and I haven't used any UI framework. But I built lots of UI apps, and I will continue doing it, in a scripting way. And I believe anyone can do that as well.

I'm hoping open sourcing VanJS can help us one step closer to that vision. Hope you can enjoy!

Thanks!

-- Tao Xin

"Who do you truly serve?"
"The Realm. Someone must."

-- George R. R. Martin, Game of Thrones: S1E8

VanJS was built by Tao Xin during his personal time while being employed as a full-time employee at Google. The project was submitted to Invention Assignment Review Committee at Google where Google, upon reviewing the designated scopes, waived its copyright claims. Thus the copyright of VanJS belongs to its creator, all rights reserved. VanJS is open sourced under MIT license. VanJS aims to build a better world by reducing the entry barrier for UI programming, with no intention or plan on commercialization whatsoever.

The project was developed, and will be maintained with strict compliance to Google's Outside Work Guidelines as well as requirements imposed by Google's copyright waiver. VanJS was created, and will continue to be maintained, without any use of internal Alphabet resources, including but not limited to, corporate hardware equipments, software licenses, internal tools, internal corporate mailing lists, corporate accounts, proprietary or confidential information, trademarks or brand features of any Alphabet company. Alphabet does not sponsor, endorse or in any form affiliate with VanJS project. To comply with the conflict of interests provisions, Tao Xin does not advocate the adoption of VanJS within Alphabet.

How Do We Ensure the Reliability of VanJS?


As a new UI framework, we put heavy focus on the reliability of the framework. For every single release of VanJS, below is the list of tests that we will run through:

  • A browser-based test suite, with 500+ test cases, covering different versions of VanJS files (.min.js, .debug.js, .nomodule.min.js, etc.), including the coverage of advanced behavior such as garbage collection, as well as error messages shown in the debug mode.
  • Examples used in VanJS tutorial are also covered in the browser-based test suite.
  • The browser-based test suite was implemented in TypeScript, thus TypeScript integration is covered.
  • Sample applications will keep working in every single VanJS release, including applications implemented in TypeScript (which covers TypeScript integration).

For every single release of Mini-Van, below is the list of tests that we will run through:

  • A browser-based test suite, with 60 test cases, covering different versions of Mini-Van files (.min.js, .nomodule.min.js, etc.).
  • The browser-based test suite was implemented in TypeScript, thus TypeScript integration is covered.
  • A Deno test suite for van-plate mode, covering Deno integration.
  • The entire site of vanjs.org was generated with Mini-Van with TypeScript files defining all web pages. Source code can be found here.

For every single release of VanX, below is the list of tests that we will run through:

  • A browser-based test suite, with 100+ test cases, covering different versions of VanX files, including the coverage of advanced behavior such as garbage collection.
  • The browser-based test suite was implemented in TypeScript, thus TypeScript integration is covered.
  • Sample applications in https://vanjs.org/x will keep working in every single VanX release.

A Note on Coding Styles


The sample code snippets throughout this website follow a minimalist approach when it comes to coding styles. When readability is not impacted, we are leaning towards the choice that leads to more concise code, with the belief that brevity and simplicity generally make the code easier to read and write. This means that we're consciously choosing certain coding styles throughout this website: such as omitting optional semicolons, naked if statements, usage of ternary operator when appropriate, etc.

On the other hand, we acknowledge that different people might hold a somewhat different opinion regarding certain coding style choices, and some are among hotly debated issues among programmers. We understand the arguments from the other side that certain coding styles, might occasionally lead to slightly more misleading error messages for incorrect implementation in limited situations. As an unopinionated framework, VanJS doesn't take side on coding styles. If some style in the sample code doesn't align with your personal preference or your team's common practice, feel free to make the corresponding styling changes after copy/past-ing the sample code.

A Guide to Reading VanJS Codebase


We believe that VanJS is a good illustration of how modern UI frameworks work under the hood. The simplicity in its design, and conciseness in its implementation make it the perfect learning material for the core fundamentals of reactive UI programming, as well as advanced techniques in modern JavaScript. Here we recommend this 7-minute video which breaks down and elucidates the underlying principles of VanJS codebase.

On the other hand, we do realize that some parts of VanJS codebase might be hard to read for some people. We believe that this is mostly because VanJS has chosen some programming techniques and language constructs that are not frequently used in the JavaScript community, despite their usefulness. Here we provide a brief explanation of those in the hope of easing the understanding of VanJS codebase, its official extensions, and its sample applications.

JavaScript language features:

  • Proxy: A type of JavaScript objects that allow you to intercept and redefine common operations of another object, such as getting and setting properties. The van.tags object in VanJS leverages this technique to allow you declaring DOM trees like HTML but without the need of JSX. The operation of getting any properties of van.tags will be intercepted and redefined to a function that creates an HTML element with the property name as its tag name. e.g.: van.tags.div() will create a <div> element. In addition, the reactive object in VanX leverages Proxy so that getting and setting its fields will be redefined to getting and setting values of the underlying states.
  • prototype: The foundation of OOP in JavaScript. Any object in JavaScript can specify a prototype object so that property access falls back to the prototype if the property doesn't exist in the object itself. Prototype is a lightweight alternative to classes in JavaScript. VanJS is using prototype instead of classes to keep its size small.

Less frequently used JavaScript syntaxes:

  • Ternary operator: Ternary operator is way to define conditional computations. Sometimes it can be used as an alternative to if...else statement for more concise and declarative code. For instance, the following code:
    const getFruits(hasApple, hasOrange) = () => {
      const fruits = []
      if (hasApple) {
        fruits.push("apple")
      }
      if (hasOrange) {
        fruits.push("orange")
      }
      return fruits
    }
    
    can be simplified with ternary operators:
    const getFruits(hasApple, hasOrange) = () => [].concat(
      hasApple ? "apple" : [],
      hasOrange ? "orange": [],
    )
    
    Even more complex if...else if...else statement can be simplified with ternary operators as well. For instance, the following code in the Calculator App:
    const calc = (lhs, op, rhs) =>
      !op || lhs === null ? rhs :
      op === "+" ? lhs + rhs :
      op === "-" ? lhs - rhs :
      op === "x" ? lhs * rhs : lhs / rhs
    
    is the simplified version of:
    const calc = (lhs, op, rhs) => {
      if (!op || lhs === null) {
        return rhs
      } else if (op === "+") {
        return lhs + rhs
      } else if (op === "-") {
        return lhs - rhs
      } else if (op === "x") {
        return lhs * rhs
      } else {
        return lhs / rhs
      }
    }
    
  • Comma operator (,): Comma operator evaluates each of its operands sequentially and returns the last value. VanJS leverages comma operators in a few places to make the code concise. For instance, the logic of binding a state to a DOM property:
    bind(() => {
      setter(v.val)
      return dom
    })
    
    is simplified to bind(() => (setter(v.val), dom)) in van.js (don't confuse this with calling a function with 2 arguments).
  • Nullish coalescing operator (??): Nullish coalescing expression a ?? b means:
    if (a !== null && a !== undefined) {
      return a
    } else {
      return b
    }
    
    VanJS leverages this operator in a few places to simplify code. One notable example in van.js is function addAndScheduleOnFirst:
    let addAndScheduleOnFirst = (set, s, f, waitMs) =>
      (set ?? (setTimeout(f, waitMs), new Set)).add(s)
    
    which is equivalent to:
    let addAndScheduleOnFirst = (set, s, f, waitMs) => {
      if (set === null || set === undefined) {
        setTimeout(f, waitMs)
        set = new Set
      }
      set.add(s)
      return set
    }
    
  • Short-circuit evaluation for && and ||: Sometimes, we're leveraging the short-circuit evaluation for && and || to simplify code. For instance, in van-x.js, refDelete(obj[statesSym], name) && onDelete(obj, name) is equivalent to:
    if (refDelete(obj[statesSym], name)) {
      onDelete(obj, name)
    }
    

How Did VanJS Get Its Name?


VanJS is short for Vanilla JavaScript, which is a metaphor that VanJS provides an abbreviated way to write Vanilla JavaScript code. Meanwhile, the logo of VanJS is a symbolic vanilla ice cream, which means VanJS = Vanilla JavaScript + syntax Sugar.

Under the hood, VanJS stays truthful to Vanilla JavaScript as close as possible, as there is no transpiling, virtual DOM or any hidden logic. VanJS code can be translated to Vanilla JavaScript code in a very straightforward way. For instance, the following VanJS code:

a({href: "https://vanjs.org"}, "🍦 VanJS")

is just an abbreviated/sugared form of following code in Vanilla Javascript:

const anchorDom = document.createElement("a")
anchorDom.href = "https://vanjs.org"
anchorDom.appendChild(new Text("🍦 VanJS"))

whereas

ul(
  li("🗺️World"),
  li(a({href: "https://vanjs.org/"}, "🍦VanJS")),
)

is an abbreviated/sugared form of:

const listDom = document.createElement("ul")

const itemDom1 = document.createElement("li")
itemDom1.appendChild(new Text("🗺️World"))
listDom.appendChild(itemDom1)

const itemDom2 = document.createElement("li")
const anchorDom = document.createElement("a")
anchorDom.href = "https://vanjs.org"
anchorDom.appendChild(new Text("🍦 VanJS"))
itemDom2.appendChild(anchorDom)
listDom.appendChild(itemDom2)