mirror of
https://github.com/morris/vanilla-todo.git
synced 2025-08-21 13:21:29 +02:00
readme updates
This commit is contained in:
71
README.md
71
README.md
@@ -19,8 +19,9 @@ Intermediate understanding of the web platform is required to follow through._
|
|||||||
vanilla web development seems viable at scale but comes with significant
|
vanilla web development seems viable at scale but comes with significant
|
||||||
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).
|
||||||
- The resulting product has comparable or favorable UX over the original,
|
- At a fraction of the code size and bandwidth, the resulting product has
|
||||||
better load and rendering performance, at a fraction of the code size and bandwidth.
|
[comparable UX](#51-user-experience) over the original and better load and
|
||||||
|
rendering performance.
|
||||||
- Frameworks and libraries provide lots of value but there's only a few
|
- Frameworks and libraries provide lots of value but there's 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 well-defined [rules](#22-rules)
|
||||||
@@ -210,7 +211,8 @@ with the DOM and styles:
|
|||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
This proved to be a useful, repeatable pattern throughout all of the implementation process.
|
This proved to be a useful, repeatable pattern throughout all of the
|
||||||
|
implementation process.
|
||||||
|
|
||||||
#### 3.2.1. Mount Functions
|
#### 3.2.1. Mount Functions
|
||||||
|
|
||||||
@@ -341,14 +343,15 @@ Note that any part of a mount function is entirely optional.
|
|||||||
For example, a mount function does not have to set any base HTML,
|
For example, a mount function does not have to set any base HTML,
|
||||||
and may instead only set event listeners to enable some behavior.
|
and may instead only set event listeners to enable some behavior.
|
||||||
|
|
||||||
Also note that an element can be mounted with multiple functions.
|
Also note that an element can be mounted with multiple mount functions.
|
||||||
For example, to-do items are mounted with
|
For example, to-do items are mounted with `VT.TodoItem` and `VT.AppDraggable`.
|
||||||
`VT.TodoItem`, `VT.AppDraggable` and `VT.AppLateBlur`
|
|
||||||
|
|
||||||
See for example:
|
Compared to React components, mount functions provide superior flexibility as
|
||||||
|
components and behaviors can be implemented using the same idiom.
|
||||||
|
|
||||||
|
Reference:
|
||||||
|
|
||||||
- [AppIcon.js](./public/scripts/AppIcon.js)
|
- [AppIcon.js](./public/scripts/AppIcon.js)
|
||||||
- [AppLateBlur.js](./public/scripts/AppLateBlur.js)
|
|
||||||
- [TodoItem.js](./public/scripts/TodoItem.js)
|
- [TodoItem.js](./public/scripts/TodoItem.js)
|
||||||
- [TodoItemInput.js](./public/scripts/TodoItemInput.js)
|
- [TodoItemInput.js](./public/scripts/TodoItemInput.js)
|
||||||
|
|
||||||
@@ -362,16 +365,16 @@ I found it effective to implement one-way data flow similar to React's approach.
|
|||||||
usually resulting in some parent component state change which is in turn
|
usually resulting in some parent component state change which is in turn
|
||||||
propagated downwards through `update` functions.
|
propagated downwards through `update` functions.
|
||||||
|
|
||||||
The data store is factored into a separate mount function (`TodoStore`).
|
The data store is factored into a separate mount function (`VT.TodoStore`).
|
||||||
It only receives and dispatches events, and encapsulates any data manipulation.
|
It only receives and dispatches events, and encapsulates any data manipulation.
|
||||||
|
|
||||||
Listening to and dispatching events is slightly verbose with standard APIs and
|
Listening to and dispatching events is slightly verbose with standard APIs and
|
||||||
certainly justifies introducing helpers.
|
certainly justifies introducing helpers.
|
||||||
I didn't need event delegation à la jQuery for this study
|
I didn't need event delegation à la jQuery for this study
|
||||||
but I believe it's a useful concept that is likely hard to do
|
but I believe it's a useful concept that is difficult to do
|
||||||
properly with standard APIs.
|
properly with standard APIs.
|
||||||
|
|
||||||
See for example:
|
Reference:
|
||||||
|
|
||||||
- [TodoDay.js](./public/scripts/TodoDay.js)
|
- [TodoDay.js](./public/scripts/TodoDay.js)
|
||||||
- [TodoStore.js](./public/scripts/TodoStore.js)
|
- [TodoStore.js](./public/scripts/TodoStore.js)
|
||||||
@@ -383,8 +386,9 @@ as this may hurt performance and will likely break important functionality such
|
|||||||
input state, focus, text selection etc. which browsers have already been
|
input state, focus, text selection etc. which browsers have already been
|
||||||
optimizing for years.
|
optimizing for years.
|
||||||
|
|
||||||
As seen in 3.2.1., rendering is therefore split between setting a rigid base HTML
|
As seen in [3.2.1.](#321-mount-functions), rendering is therefore split into
|
||||||
and an idempotent, complete update function which only makes necessary changes.
|
some rigid base HTML and an idempotent, complete update function which only
|
||||||
|
makes necessary changes.
|
||||||
|
|
||||||
- **Idempotency** is key here, i.e. update functions may be called at any time
|
- **Idempotency** is key here, i.e. update functions may be called at any time
|
||||||
and should always render the component correctly.
|
and should always render the component correctly.
|
||||||
@@ -394,12 +398,12 @@ and an idempotent, complete update function which only 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 example.
|
As seen above, this approach is quite verbose and ugly compared to JSX, for
|
||||||
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 easy to understand.
|
It is also simple to understand.
|
||||||
|
|
||||||
See for example:
|
Reference:
|
||||||
|
|
||||||
- [TodoItem.js](./public/scripts/TodoItem.js)
|
- [TodoItem.js](./public/scripts/TodoItem.js)
|
||||||
- [TodoCustomList.js](./public/scripts/TodoCustomList.js)
|
- [TodoCustomList.js](./public/scripts/TodoCustomList.js)
|
||||||
@@ -408,7 +412,7 @@ See for example:
|
|||||||
|
|
||||||
Expectedly, the hardest part of the study was rendering a variable
|
Expectedly, the hardest part of the study was rendering a variable
|
||||||
amount of dynamic components efficiently. Here's a commented example
|
amount of dynamic components efficiently. Here's a commented example
|
||||||
from the implementation outlining the algorithm:
|
from the implementation outlining the reconciliation algorithm:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
/* global VT */
|
/* global VT */
|
||||||
@@ -448,7 +452,11 @@ VT.TodoList = function (el) {
|
|||||||
// otherwise, create new child
|
// otherwise, create new child
|
||||||
child = document.createElement('div');
|
child = document.createElement('div');
|
||||||
child.classList.add('todo-item');
|
child.classList.add('todo-item');
|
||||||
|
|
||||||
|
// set data-key
|
||||||
child.setAttribute('data-key', item.id);
|
child.setAttribute('data-key', item.id);
|
||||||
|
|
||||||
|
// mount component function
|
||||||
VT.TodoItem(child);
|
VT.TodoItem(child);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -463,7 +471,7 @@ VT.TodoList = function (el) {
|
|||||||
container.removeChild(child);
|
container.removeChild(child);
|
||||||
});
|
});
|
||||||
|
|
||||||
// insert new list of children (may reorder existing children)
|
// (re-)insert new list of children (may reorder existing 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]);
|
||||||
@@ -554,7 +562,7 @@ when elements are reordered.
|
|||||||
|
|
||||||
_The latter was an improvement over the original application when I started
|
_The latter was an improvement over the original application when I started
|
||||||
working on the case study some weeks ago. In the meantime, the TeuxDeux
|
working on the case study some weeks ago. In the meantime, the TeuxDeux
|
||||||
team released an update with a much better drag & drop experience. Good job!_
|
team released an update with a much better drag & drop experience. Great job!_
|
||||||
|
|
||||||
One notable missing feature is Markdown support. It would be insensible
|
One notable missing feature is Markdown support. It would be insensible
|
||||||
to implement Markdown from scratch; this is a valid candidate for using
|
to implement Markdown from scratch; this is a valid candidate for using
|
||||||
@@ -563,6 +571,8 @@ 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._
|
||||||
|
|
||||||
The original TeuxDeux application transfers around 435 KB and finishes loading
|
The original TeuxDeux application transfers around 435 KB and finishes loading
|
||||||
around 1000 ms, sometimes up to 2000ms (measured on 10/21 2020).
|
around 1000 ms, sometimes up to 2000ms (measured on 10/21 2020).
|
||||||
|
|
||||||
@@ -576,8 +586,7 @@ _TODO Run more formal performance tests and add figures for the results._
|
|||||||
|
|
||||||
Unfortunately, it is quite hard to find undisputed, objective measurements
|
Unfortunately, it is quite hard to find undisputed, objective measurements
|
||||||
for code quality (besides trivialities like code style, linting, etc.).
|
for code quality (besides trivialities like code style, linting, etc.).
|
||||||
The only generally accepted assessment seems to be peer reviewal
|
The only generally accepted assessment seems to be peer reviewal.
|
||||||
which is only possible after publication.
|
|
||||||
|
|
||||||
To have at least some degree of assessment of the code's quality,
|
To have at least some degree of assessment of the code's quality,
|
||||||
the following sections summarize relevant facts about the codebase
|
the following sections summarize relevant facts about the codebase
|
||||||
@@ -643,11 +652,21 @@ would reduce the comparably low code size (see above) even further.
|
|||||||
- No type safety. I've always been a proponent of dynamic languages
|
- No type safety. I've always been a proponent of dynamic languages
|
||||||
but since TypeScripts' type system provides the best of both worlds,
|
but since TypeScripts' type system provides the best of both worlds,
|
||||||
I cannot recommend using it enough.
|
I cannot recommend using it enough.
|
||||||
- Most frameworks or libraries handle a lot of browser inconsistencies
|
- Most frameworks handle a lot of browser inconsistencies **for free** and
|
||||||
and continuously test for regressions with extensive test suites **for free**.
|
continuously test for regressions with extensive test suites.
|
||||||
The cost of browser testing is surely a lot higher
|
The cost of browser testing is surely a lot higher
|
||||||
when using a vanilla approach.
|
when using a vanilla approach.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Besides the downsides described above, I believe the codebase is well organized
|
||||||
|
and there are clear paths for bugfixes and feature development.
|
||||||
|
Since there's no third party code bugs are easy to find and fix,
|
||||||
|
and there are no dependency limitations to work around.
|
||||||
|
|
||||||
|
A certain degree of DOM API knowledge is required but I believe this
|
||||||
|
should be a goal for any web developer.
|
||||||
|
|
||||||
### 5.3. Generality of Patterns
|
### 5.3. Generality of Patterns
|
||||||
|
|
||||||
Assessing the generality of the discovered techniques objectively is
|
Assessing the generality of the discovered techniques objectively is
|
||||||
@@ -738,7 +757,3 @@ Projects I've inspected for drag & drop architecture:
|
|||||||
- [React DnD](https://react-dnd.github.io)
|
- [React DnD](https://react-dnd.github.io)
|
||||||
- [react-beautiful-dnd](https://github.com/atlassian/react-beautiful-dnd)
|
- [react-beautiful-dnd](https://github.com/atlassian/react-beautiful-dnd)
|
||||||
- [dragula](https://github.com/bevacqua/dragula)
|
- [dragula](https://github.com/bevacqua/dragula)
|
||||||
|
|
||||||
Other interesting articles:
|
|
||||||
|
|
||||||
- [The case for vanilla front-end development (pushdata.io)](https://pushdata.io/blog/1)
|
|
||||||
|
Reference in New Issue
Block a user