mirror of
https://github.com/morris/vanilla-todo.git
synced 2025-08-08 23:26:58 +02:00
update readme
This commit is contained in:
54
README.md
54
README.md
@@ -8,7 +8,7 @@ with a total transferred size of roughly **35KB**.
|
|||||||
More importantly, it's also a
|
More importantly, it's also a
|
||||||
**case study on viable techniques for vanilla web development.**
|
**case study on viable techniques for vanilla web development.**
|
||||||
|
|
||||||
**[Try it online →](https://rawcdn.githack.com/morris/vanilla-todo/b4ce72a87b83015b593d270aa7f31adbd7e13f98/public/index.html)**
|
**[Try it online →](https://raw.githack.com/morris/vanilla-todo/main/public/index.html)**
|
||||||
|
|
||||||
_This document presents a "live" case study, expected to evolve a bit over time.
|
_This document presents a "live" case study, expected to evolve a bit over time.
|
||||||
Intermediate understanding of the web platform is required to follow through._
|
Intermediate understanding of the web platform is required to follow through._
|
||||||
@@ -20,12 +20,13 @@ Intermediate understanding of the web platform is required to follow through._
|
|||||||
verbosity and effort in browser testing.
|
verbosity and effort in browser testing.
|
||||||
The former may be solved by simple build steps (SCSS, TypeScript).
|
The former may be solved by simple build steps (SCSS, TypeScript).
|
||||||
- At a fraction of the code size and bandwidth, the resulting product has
|
- At a fraction of the code size and bandwidth, the resulting product has
|
||||||
[comparable UX](#51-user-experience) over the original and better load and
|
[comparable UX](#51-user-experience) over the original and much better load and
|
||||||
rendering performance.
|
rendering performance.
|
||||||
- Frameworks and libraries provide lots of value but there's only a few
|
- Frameworks and libraries provide lots of value but there are only a few
|
||||||
[critical areas](#523-the-bad) where a vanilla approach is clearly inferior.
|
[critical areas](#523-the-bad) where a vanilla approach is clearly inferior.
|
||||||
- Case studies constrained by a set of well-defined [rules](#22-rules)
|
- Case studies constrained by a set of formal [rules](#22-rules)
|
||||||
are an effective way to find new patterns and techniques in any domain.
|
are an effective way to find new patterns and techniques
|
||||||
|
in a wide range of domains.
|
||||||
|
|
||||||
## Table of Contents
|
## Table of Contents
|
||||||
|
|
||||||
@@ -70,7 +71,7 @@ It's also rather harmful to write an article
|
|||||||
saying you don't need library X, and then proceed in describing how
|
saying you don't need library X, and then proceed in describing how
|
||||||
to roll your own untested, inferior version of X.
|
to roll your own untested, inferior version of X.
|
||||||
|
|
||||||
We're missing thorough examples of complex web applications
|
What's missing are thorough examples of complex web applications
|
||||||
built only with standard web technologies, covering as many aspects of
|
built only with standard web technologies, covering as many aspects of
|
||||||
the development process as possible.
|
the development process as possible.
|
||||||
|
|
||||||
@@ -189,7 +190,7 @@ It is otherwise straight-forward and trivial to understand
|
|||||||
|
|
||||||
### 3.2. JavaScript Architecture
|
### 3.2. JavaScript Architecture
|
||||||
|
|
||||||
Naturally, the JS architecture is the most interesting part of this study.
|
Naturally, the JavaScript architecture is the most interesting part of this study.
|
||||||
|
|
||||||
I found that using a combination of functions,
|
I found that using a combination of functions,
|
||||||
query selectors and DOM events is sufficient
|
query selectors and DOM events is sufficient
|
||||||
@@ -255,6 +256,7 @@ MYAPP.HelloWorld = function (el) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// expose public interface
|
// expose public interface
|
||||||
|
// use lower-case function name
|
||||||
el.helloWorld = {
|
el.helloWorld = {
|
||||||
update: update,
|
update: update,
|
||||||
};
|
};
|
||||||
@@ -321,6 +323,7 @@ MYAPP.MyCounter = function (el) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// expose public interface
|
// expose public interface
|
||||||
|
// use lower-case function name
|
||||||
el.myCounter = {
|
el.myCounter = {
|
||||||
update: update,
|
update: update,
|
||||||
};
|
};
|
||||||
@@ -349,7 +352,8 @@ Also note that an element can be mounted with multiple mount functions.
|
|||||||
For example, to-do items are mounted with `VT.TodoItem` and `VT.AppDraggable`.
|
For example, to-do items are mounted with `VT.TodoItem` and `VT.AppDraggable`.
|
||||||
|
|
||||||
Compared to React components, mount functions provide interesting flexibility as
|
Compared to React components, mount functions provide interesting flexibility as
|
||||||
components and behaviors can be implemented using the same idiom.
|
components and behaviors can be implemented using the same idiom and combined
|
||||||
|
arbitrarily.
|
||||||
|
|
||||||
Reference:
|
Reference:
|
||||||
|
|
||||||
@@ -383,10 +387,10 @@ Reference:
|
|||||||
|
|
||||||
#### 3.2.3. Rendering
|
#### 3.2.3. Rendering
|
||||||
|
|
||||||
Naively re-rendering a whole component using `.innerHTML` should be avoided,
|
Naively re-rendering a whole component using `.innerHTML` should be avoided
|
||||||
as this may hurt performance and will likely break important functionality such
|
as this may hurt performance and will likely break important functionality such
|
||||||
as input state, focus, text selection etc. which browsers have already been
|
as input state, focus, text selection etc. which browsers have already been
|
||||||
optimizing for years.
|
optimizing for decades.
|
||||||
|
|
||||||
As seen in [3.2.1.](#321-mount-functions), rendering is therefore split into
|
As seen in [3.2.1.](#321-mount-functions), rendering is therefore split into
|
||||||
some rigid base HTML and an idempotent, complete update function which only
|
some rigid base HTML and an idempotent, complete update function which only
|
||||||
@@ -400,7 +404,7 @@ makes necessary changes.
|
|||||||
In effect, this means almost all DOM manipulation is done in update functions,
|
In effect, this means almost all DOM manipulation is done in update functions,
|
||||||
which greatly contributes to robustness and readability of the codebase.
|
which greatly contributes to robustness and readability of the codebase.
|
||||||
|
|
||||||
As seen above, this approach is quite verbose and ugly compared to JSX, for
|
As seen above this approach is quite verbose and ugly compared to JSX, for
|
||||||
example. However, it's very performant and can be further optimized
|
example. However, it's very performant and can be further optimized
|
||||||
by checking for data changes, caching selectors, etc.
|
by checking for data changes, caching selectors, etc.
|
||||||
It is also simple to understand.
|
It is also simple to understand.
|
||||||
@@ -458,7 +462,7 @@ VT.TodoList = function (el) {
|
|||||||
// set data-key
|
// set data-key
|
||||||
child.setAttribute('data-key', item.id);
|
child.setAttribute('data-key', item.id);
|
||||||
|
|
||||||
// mount component function
|
// mount component
|
||||||
VT.TodoItem(child);
|
VT.TodoItem(child);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -473,7 +477,7 @@ VT.TodoList = function (el) {
|
|||||||
container.removeChild(child);
|
container.removeChild(child);
|
||||||
});
|
});
|
||||||
|
|
||||||
// (re-)insert new list of children (may reorder existing children)
|
// (re-)insert new list of children
|
||||||
children.forEach(function (child, index) {
|
children.forEach(function (child, index) {
|
||||||
if (child !== container.children[index]) {
|
if (child !== container.children[index]) {
|
||||||
container.insertBefore(child, container.children[index]);
|
container.insertBefore(child, container.children[index]);
|
||||||
@@ -499,13 +503,13 @@ especially regarding browser/device consistency.
|
|||||||
|
|
||||||
Using a library would have been a lot more cost-effective initially.
|
Using a library would have been a lot more cost-effective initially.
|
||||||
However, having a customized implementation paid off once I started
|
However, having a customized implementation paid off once I started
|
||||||
introducing animations, as both had to be coordinated closely.
|
introducing animations as both had to be coordinated closely.
|
||||||
I can imagine this would have been a difficult problem
|
I can imagine this would have been a difficult problem
|
||||||
when using third party code for either.
|
when using third party code for either.
|
||||||
|
|
||||||
The drag & drop implementation is (again) based on DOM events and integrates
|
The drag & drop implementation is (again) based on DOM events and integrates
|
||||||
well with the remaining architecture.
|
well with the remaining architecture.
|
||||||
It's clearly the most complex part of the study, but I was able to implement it
|
It's clearly the most complex part of the study but I was able to implement it
|
||||||
without changing existing code besides mounting behaviors and
|
without changing existing code besides mounting behaviors and
|
||||||
adding event handlers.
|
adding event handlers.
|
||||||
|
|
||||||
@@ -573,10 +577,10 @@ an external library as it is entirely orthogonal to the remaining codebase.
|
|||||||
The application has been tested on latest Chrome, Firefox, Safari,
|
The application has been tested on latest Chrome, Firefox, Safari,
|
||||||
and Safari on iOS.
|
and Safari on iOS.
|
||||||
|
|
||||||
_TODO test more browsers and devices._
|
_TODO Test more browsers and devices._
|
||||||
|
|
||||||
A fresh load of the original TeuxDeux application transfers around **435 KB** and
|
A fresh load of the original TeuxDeux application transfers around **435 KB** and
|
||||||
finishes loading around **1000 ms**, sometimes up to 2000ms
|
finishes loading at around **1000 ms**, sometimes up to 2000ms
|
||||||
(measured on 10/21 2020).
|
(measured on 10/21 2020).
|
||||||
Reloads finish at around **500ms**.
|
Reloads finish at around **500ms**.
|
||||||
|
|
||||||
@@ -636,6 +640,7 @@ I suspect a fully equivalent clone to be well below 10000 LOC, though._
|
|||||||
|
|
||||||
- Stylesheets are a bit verbose. SCSS would help here.
|
- Stylesheets are a bit verbose. SCSS would help here.
|
||||||
- Simple components require quite some boilerplate code.
|
- Simple components require quite some boilerplate code.
|
||||||
|
- Writing HTML templates as an array of lines is ugly (and sub-optimal).
|
||||||
- ES5 is generally a lot more verbose than ES6.
|
- ES5 is generally a lot more verbose than ES6.
|
||||||
- Especially arrow functions, template literals,
|
- Especially arrow functions, template literals,
|
||||||
and async/await would make the code more readable.
|
and async/await would make the code more readable.
|
||||||
@@ -653,10 +658,10 @@ would reduce the comparably low code size (see above) even further.
|
|||||||
|
|
||||||
- The separation between base HTML and dynamic rendering is not ideal
|
- The separation between base HTML and dynamic rendering is not ideal
|
||||||
when compared to JSX, for example.
|
when compared to JSX, for example.
|
||||||
|
- JSX/virtual DOM techniques provide much better development ergonomics.
|
||||||
- Reconciliation is verbose, brittle and repetitive.
|
- Reconciliation is verbose, brittle and repetitive.
|
||||||
I wouldn't recommend the proposed technique
|
I wouldn't recommend the proposed technique
|
||||||
without a well-tested helper function, at least.
|
without a well-tested helper function, at least.
|
||||||
- JSX/virtual DOM techniques provide much better development ergonomics.
|
|
||||||
- You have to remember mounting behaviors correctly when
|
- You have to remember mounting behaviors correctly when
|
||||||
creating new elements. It would be helpful to automate this somehow,
|
creating new elements. It would be helpful to automate this somehow,
|
||||||
e.g. watch elements of selector X (at all times) and ensure the desired
|
e.g. watch elements of selector X (at all times) and ensure the desired
|
||||||
@@ -708,17 +713,20 @@ simple build steps (e.g. SCSS and TypeScript).
|
|||||||
|
|
||||||
The study's method helped discovering patterns and techniques that
|
The study's method helped discovering patterns and techniques that
|
||||||
are at least on par with a framework-based approach for the given subject,
|
are at least on par with a framework-based approach for the given subject,
|
||||||
without diverging into building a custom framework—except for
|
without diverging into building a custom framework.
|
||||||
rendering variable numbers of elements efficiently.
|
|
||||||
|
A notable exception to the latter is rendering variable numbers of elements
|
||||||
|
in a concise way. I was unable to eliminate the verbosity involved
|
||||||
|
in basic but efficient reconciliation.
|
||||||
Further research is needed in this area, but for now this appears to be
|
Further research is needed in this area, but for now this appears to be
|
||||||
a valid candidate for a (possibly external) general-purpose utility.
|
a valid candidate for a (possibly external) general-purpose utility.
|
||||||
|
|
||||||
When looking at the downsides remember that all of the individual parts are
|
When looking at the downsides, remember that all of the individual parts are
|
||||||
self-contained, highly decoupled, portable, and congruent to the web platform.
|
self-contained, highly decoupled, portable, and congruent to the web platform.
|
||||||
The resulting implementation cannot "rust", by definition.
|
The resulting implementation cannot "rust", by definition.
|
||||||
|
|
||||||
Another thought be taken with a grain of salt: I believe frameworks
|
Another thought to be taken with a grain of salt: I believe frameworks
|
||||||
make simple tasks even simpler but hard tasks (e.g. implementing cross-cutting
|
make simple tasks even simpler, but hard tasks (e.g. implementing cross-cutting
|
||||||
concerns or performance optimizations) often more difficult.
|
concerns or performance optimizations) often more difficult.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
Reference in New Issue
Block a user