Learning Virtual DOM
Looking for React example? Go back to the Start Here page
Create a blank HTML file somewhere on your computer with a name like: mega-millions.html
. If you want to edit in your browser, fork this Stackblitz.
Using a text editor, fill the file with these contents:
<html>
<head></head>
<body>
<script type="module">
import { render } from 'https://cdn.skypack.dev/million';
render(document.body, 'You won $$$!');
</script>
</body>
</html>
Open your file in a web browser, if you see You won $$$!
, you're ready to rumble!
Now that you're all set up to play around, let's look at a couple practical examples as a foundation for teaching you the basics of Million. By the end of this exercise, you should be more than equipped to start building stuff on your own.
- Building a counter
- Building a search input
Building a counter
Let's start with a simple "counter" example to demonstrate the basics of rendering and virtual node construction, two core features.
Insert the following into the <script>
tag:
import { _, m, render } from 'https://cdn.skypack.dev/million';
let seconds = 0;
setInterval(() => {
render(document.body, m('p', _, [`Time elapsed: ${seconds}`]));
seconds++;
}, 1000);
Time Elapsed: 0
Now, you can see with a bit of JavaScript, we've created an interactive "counter".
Let's walk through what's happening briefly:
-
render()
function has a standard interface that is used in many Virtual DOM libraries. First argument is a DOM node that will be used as the parent DOM reference, and the second one is a Virtual DOM to render. -
m()
function will instantiate a "Virtual DOM" node for an element.
Basically, the m()
function is how you construct what you want the UI to look like, and the render()
function is when you want to put it on the screen.
Building a search input
Now that we've seen some basic functionality, let's see how we can build a search input with more complex features.
import {
_,
m,
render,
startTransition,
style,
kebab,
Flags,
} from 'https://cdn.skypack.dev/million';
import { compareTwoStrings } from 'https://cdn.skypack.dev/string-similarity';
const entries = ['Apple', 'Banana', 'Grape', 'Orange', 'Strawberry'];
const update = (highlightedEntries) => {
const vnode = m('div', _, [
m('input', { onInput: ({ target }) => search(target.value) }, []),
m('ul', _, highlightedEntries, Flags.ELEMENT_KEYED_CHILDREN),
]);
startTransition(() => {
render(document.body, vnode);
});
};
const search = (query = '') => {
update(
entries.map((entry) => {
// Compare two strings returns a float based off of similarity
// Ex: 0.8 means high similarity, 0.2 means low similarity
const shouldShow =
compareTwoStrings(entry.toLowerCase(), query.trim().toLowerCase()) >
0.25;
return m(
'li',
{
key: `${entry}-${shouldShow}`,
style: shouldShow
? style(kebab({ textDecoration: 'underline', fontWeight: 'bold' }))
: undefined,
},
[entry],
);
}),
);
};
search();
- Apple
- Banana
- Grape
- Orange
- Strawberry
By default, all of the "entries" (Apple, Banana, Grape, Orange, Strawberry) will be listed, but you can highlight them by typing into the text input. As you type, the list of entries will change to reflect what you're searching for.
Now there's quite a bit happening here, so let's go through this snippet piece by piece.
- VNode props (the second parameter in the
m()
function) reflect the props and attributes on a real DOM node. Just like real DOM nodes, we can useonInput
to bind an event handler to the input element. The handler calls thesearch
function, which takes a query and determines the entries that match the query. - We can use the
<Array>.map()
function to help us construct VNodes from a non-VNode array. - The
key
property on the<li>
element and theELEMENT.ONLY_KEYED_CHILDREN
flags allows Million to shortcut to an optimized fast path. Every element inside the<ul>
tag is unique and non-changing, which is why we can used the keyed diffing algorithm. - The
style
function converts a style object into a string, andkebab
converts camelCase properties to kebab-case. - We can use the
startTransition()
function to schedule a task to be run on the next animation frame, to ensure over 60 FPS.
Recap
If you've made it this far, you've been exposed to the following features of Million:
That's a great start, however, there are many more features to sink your teeth into. The best way to absorb Million is to read through this documentation. No need to comb over every word, but if you at least glance through every page you will be MUCH more effective when creating libraries or applications with Million.
Happy Coding!