mirror of
https://github.com/morris/vanilla-todo.git
synced 2025-09-03 02:42:33 +02:00
prettier: always wrap prose
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
{
|
{
|
||||||
"singleQuote": true
|
"singleQuote": true,
|
||||||
|
"proseWrap": "always"
|
||||||
}
|
}
|
||||||
|
12
LICENSE.md
12
LICENSE.md
@@ -1,5 +1,13 @@
|
|||||||
Copyright 2020-2024 Morris Brodersen <mb@morrisbrodersen.de>
|
Copyright 2020-2024 Morris Brodersen <mb@morrisbrodersen.de>
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
|
Permission to use, copy, modify, and/or distribute this software for any purpose
|
||||||
|
with or without fee is hereby granted, provided that the above copyright notice
|
||||||
|
and this permission notice appear in all copies.
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||||
|
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||||
|
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||||
|
THIS SOFTWARE.
|
||||||
|
581
README.md
581
README.md
@@ -1,28 +1,29 @@
|
|||||||
# VANILLA TODO
|
# VANILLA TODO
|
||||||
|
|
||||||
A [TeuxDeux](https://teuxdeux.com) clone in plain HTML, CSS and JavaScript
|
A [TeuxDeux](https://teuxdeux.com) clone in plain HTML, CSS and JavaScript (no
|
||||||
(no build steps). It's fully animated and runs smoothly at 60 FPS
|
build steps). It's fully animated and runs smoothly at 60 FPS with a total
|
||||||
with a total transfer size of **55 KB** (unminified).
|
transfer size of **55 KB** (unminified).
|
||||||
|
|
||||||
**[Try it online →](https://morris.github.io/vanilla-todo/)**
|
**[Try it online →](https://morris.github.io/vanilla-todo/)**
|
||||||
|
|
||||||
More importantly, it's a case study showing that **vanilla web development** is
|
More importantly, it's a case study showing that **vanilla web development** is
|
||||||
viable in terms of [maintainability](#521-the-good),
|
viable in terms of [maintainability](#521-the-good), and worthwhile in terms of
|
||||||
and worthwhile in terms of [user experience](#51-user-experience)
|
[user experience](#51-user-experience) (**50%** less time to load and **95%**
|
||||||
(**50%** less time to load and **95%** less bandwidth in this case).
|
less bandwidth in this case).
|
||||||
|
|
||||||
**There's no custom framework invented here.**
|
**There's no custom framework invented here.** Instead, the case study was
|
||||||
Instead, the case study was [designed](#22-rules) to discover
|
[designed](#22-rules) to discover minimum viable
|
||||||
minimum viable [patterns](#321-mount-functions) that are truly vanilla.
|
[patterns](#321-mount-functions) that are truly vanilla. The result is
|
||||||
The result is maintainable, albeit [verbose](#522-the-verbose) and with
|
maintainable, albeit [verbose](#522-the-verbose) and with considerable
|
||||||
considerable duplication.
|
duplication.
|
||||||
|
|
||||||
If anything, the case study validates the value of build steps and frameworks,
|
If anything, the case study validates the value of build steps and frameworks,
|
||||||
but also demonstrates that standard web technologies can be used effectively and
|
but also demonstrates that standard web technologies can be used effectively and
|
||||||
there are only a few [critical areas](#523-the-bad) where a vanilla approach is
|
there are only a few [critical areas](#523-the-bad) where a vanilla approach is
|
||||||
clearly inferior.
|
clearly inferior.
|
||||||
|
|
||||||
_While the first version of the case study has been published in 2020, it has received significant [updates](#9-changelog) over time._
|
_While the first version of the case study has been published in 2020, it has
|
||||||
|
received significant [updates](#9-changelog) over time._
|
||||||
|
|
||||||
_Intermediate understanding of the web platform is required to follow through._
|
_Intermediate understanding of the web platform is required to follow through._
|
||||||
|
|
||||||
@@ -68,22 +69,20 @@ _Intermediate understanding of the web platform is required to follow through._
|
|||||||
|
|
||||||
## 1. Motivation
|
## 1. Motivation
|
||||||
|
|
||||||
I believe too little has been invested in researching
|
I believe too little has been invested in researching practical, scalable
|
||||||
practical, scalable methods for building web applications
|
methods for building web applications without third party dependencies.
|
||||||
without third party dependencies.
|
|
||||||
|
|
||||||
It's not enough to describe how to create DOM nodes
|
It's not enough to describe how to create DOM nodes or how to toggle a class
|
||||||
or how to toggle a class without a framework.
|
without a framework. It's also rather harmful to write an article saying you
|
||||||
It's also rather harmful to write an article
|
don't need library X, and then proceed in describing how to roll your own
|
||||||
saying you don't need library X, and then proceed in describing how
|
untested, inferior version of X.
|
||||||
to roll your own untested, inferior version of X.
|
|
||||||
|
|
||||||
What's missing are thorough examples of complex web applications
|
What's missing are thorough examples of complex web applications built only with
|
||||||
built only with standard web technologies, covering as many aspects of
|
standard web technologies, covering as many aspects of the development process
|
||||||
the development process as possible.
|
as possible.
|
||||||
|
|
||||||
This case study is an attempt to fill this gap, at least a little bit,
|
This case study is an attempt to fill this gap, at least a little bit, and
|
||||||
and inspire further research in the area.
|
inspire further research in the area.
|
||||||
|
|
||||||
## 2. Method
|
## 2. Method
|
||||||
|
|
||||||
@@ -99,16 +98,16 @@ This section describes the method in more detail.
|
|||||||
### 2.1. Subject
|
### 2.1. Subject
|
||||||
|
|
||||||
I've chosen to build a (functionally reduced) clone of
|
I've chosen to build a (functionally reduced) clone of
|
||||||
[TeuxDeux](https://teuxdeux.com) for this study.
|
[TeuxDeux](https://teuxdeux.com) for this study. The user interface has
|
||||||
The user interface has interesting challenges,
|
interesting challenges, in particular performant drag & drop when combined with
|
||||||
in particular performant drag & drop when combined with animations.
|
animations.
|
||||||
|
|
||||||
_The original TeuxDeux app deserves praise here. In my opinion it has the
|
_The original TeuxDeux app deserves praise here. In my opinion it has the best
|
||||||
best over-all concept and UX of all the to-do apps out there.
|
over-all concept and UX of all the to-do apps out there.
|
||||||
[Thank you!](https://fictivekin.com/)_
|
[Thank you!](https://fictivekin.com/)_
|
||||||
|
|
||||||
The user interface is arguably small (which is good for a case study)
|
The user interface is arguably small (which is good for a case study) but large
|
||||||
but large enough to require thought on its architecture.
|
enough to require thought on its architecture.
|
||||||
|
|
||||||
However, it is lacking in some key areas:
|
However, it is lacking in some key areas:
|
||||||
|
|
||||||
@@ -119,8 +118,8 @@ However, it is lacking in some key areas:
|
|||||||
|
|
||||||
### 2.2. Rules
|
### 2.2. Rules
|
||||||
|
|
||||||
To produce valid vanilla solutions, and because constraints spark creativity,
|
To produce valid vanilla solutions, and because constraints spark creativity, I
|
||||||
I came up with a set of rules to follow throughout the process:
|
came up with a set of rules to follow throughout the process:
|
||||||
|
|
||||||
- Only use standard web technologies.
|
- Only use standard web technologies.
|
||||||
- Only use widely supported JS features unless they can be polyfilled (1).
|
- Only use widely supported JS features unless they can be polyfilled (1).
|
||||||
@@ -130,9 +129,9 @@ I came up with a set of rules to follow throughout the process:
|
|||||||
|
|
||||||
(1) This is a moving target; the current version is using ES2020.
|
(1) This is a moving target; the current version is using ES2020.
|
||||||
|
|
||||||
(2) These usually end up becoming a custom micro-framework,
|
(2) These usually end up becoming a custom micro-framework, thereby questioning
|
||||||
thereby questioning why you didn't use one of the
|
why you didn't use one of the established and tested libraries/frameworks in the
|
||||||
established and tested libraries/frameworks in the first place.
|
first place.
|
||||||
|
|
||||||
### 2.3. Goals
|
### 2.3. Goals
|
||||||
|
|
||||||
@@ -140,75 +139,73 @@ The results are going to be assessed by three major concerns:
|
|||||||
|
|
||||||
#### 2.3.1. User Experience
|
#### 2.3.1. User Experience
|
||||||
|
|
||||||
The product should be comparable to or better
|
The product should be comparable to or better than the original regarding
|
||||||
than the original regarding functionality, performance and design.
|
functionality, performance and design.
|
||||||
|
|
||||||
This includes testing major browsers and devices.
|
This includes testing major browsers and devices.
|
||||||
|
|
||||||
#### 2.3.2. Code Quality
|
#### 2.3.2. Code Quality
|
||||||
|
|
||||||
The implementation should be _maintainable_ and
|
The implementation should be _maintainable_ and follow established code quality
|
||||||
follow established code quality standards.
|
standards.
|
||||||
|
|
||||||
This will be difficult to assess objectively, as we will see later.
|
This will be difficult to assess objectively, as we will see later.
|
||||||
|
|
||||||
#### 2.3.3. Generality of Patterns
|
#### 2.3.3. Generality of Patterns
|
||||||
|
|
||||||
The discovered techniques and patterns should be applicable in a wide
|
The discovered techniques and patterns should be applicable in a wide range of
|
||||||
range of scenarios.
|
scenarios.
|
||||||
|
|
||||||
## 3. Implementation
|
## 3. Implementation
|
||||||
|
|
||||||
This section walks through the implementation, highlighting techniques
|
This section walks through the implementation, highlighting techniques and
|
||||||
and problems found during the process. You're encouraged to inspect the
|
problems found during the process. You're encouraged to inspect the
|
||||||
[source code](./public) alongside this section.
|
[source code](./public) alongside this section.
|
||||||
|
|
||||||
### 3.1. Basic Structure
|
### 3.1. Basic Structure
|
||||||
|
|
||||||
Since build steps are ruled out, the codebase consists of
|
Since build steps are ruled out, the codebase consists of plain HTML, CSS and JS
|
||||||
plain HTML, CSS and JS files. The HTML and CSS follows
|
files. The HTML and CSS follows [rscss](https://ricostacruz.com/rscss/) (devised
|
||||||
[rscss](https://ricostacruz.com/rscss/) (devised by [Rico Sta. Cruz](https://ricostacruz.com))
|
by [Rico Sta. Cruz](https://ricostacruz.com)) resulting in an intuitive,
|
||||||
resulting in an intuitive, component-oriented structure.
|
component-oriented structure.
|
||||||
|
|
||||||
The stylesheets are slightly verbose.
|
The stylesheets are slightly verbose.
|
||||||
[CSS variables](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties)
|
[CSS variables](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties)
|
||||||
did help but I missed [SCSS](https://sass-lang.com/) here;
|
did help but I missed [SCSS](https://sass-lang.com/) here; I think it's a
|
||||||
I think it's a must-have for bigger projects.
|
must-have for bigger projects. Additionally, the global CSS namespace problem is
|
||||||
Additionally, the global CSS namespace problem is unaddressed
|
unaddressed (see e.g.
|
||||||
(see e.g. [CSS Modules](https://github.com/css-modules/css-modules)).
|
[CSS Modules](https://github.com/css-modules/css-modules)).
|
||||||
|
|
||||||
All JavaScript files are ES modules (`import`/`export`).
|
All JavaScript files are ES modules (`import`/`export`). I added a few
|
||||||
I added a few
|
|
||||||
[JSDoc](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html)
|
[JSDoc](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html)
|
||||||
comments to functions to get additional code completion in VSCode.
|
comments to functions to get additional code completion in VSCode. This helps,
|
||||||
This helps, but using TypeScript would be much safer and less verbose.
|
but using TypeScript would be much safer and less verbose.
|
||||||
|
|
||||||
Note that I've opted out of web components completely.
|
Note that I've opted out of web components completely. My attempts to refactor
|
||||||
My attempts to refactor the implementation using web components
|
the implementation using web components either added more complexity, or did not
|
||||||
either added more complexity, or did not show significant value
|
show significant value over the initial, more basic approach.
|
||||||
over the initial, more basic approach.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
The basic structure comes with some boilerplate,
|
The basic structure comes with some boilerplate, e.g. referencing all the
|
||||||
e.g. referencing all the individual stylesheets and scripts from the HTML;
|
individual stylesheets and scripts from the HTML; probably enough to justify a
|
||||||
probably enough to justify a simple build step.
|
simple build step.
|
||||||
|
|
||||||
It is otherwise straight-forward—literally a bunch of HTML, CSS and JS files.
|
It is otherwise straight-forward—literally a bunch of HTML, CSS and JS
|
||||||
|
files.
|
||||||
|
|
||||||
### 3.2. JavaScript Architecture
|
### 3.2. JavaScript Architecture
|
||||||
|
|
||||||
Naturally, the JavaScript 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
|
||||||
query selectors and DOM events is sufficient
|
sufficient to build a scalable, maintainable codebase, albeit with some
|
||||||
to build a scalable, maintainable codebase,
|
trade-offs as we will see later.
|
||||||
albeit with some trade-offs as we will see later.
|
|
||||||
|
|
||||||
Conceptually, the proposed architecture loosely maps
|
Conceptually, the proposed architecture loosely maps CSS selectors to JS
|
||||||
CSS selectors to JS functions which are _mounted_ (i.e. called) once
|
functions which are _mounted_ (i.e. called) once per matching element. This
|
||||||
per matching element. This simple mental model aligns well
|
simple mental model aligns well with the DOM and styles:
|
||||||
with the DOM and styles:
|
|
||||||
|
|
||||||
```
|
```
|
||||||
TodoList -> .todo-list
|
TodoList -> .todo-list
|
||||||
@@ -227,9 +224,9 @@ implementation process.
|
|||||||
|
|
||||||
#### 3.2.1. Mount Functions
|
#### 3.2.1. Mount Functions
|
||||||
|
|
||||||
_Mount functions_ take a DOM element as their first argument.
|
_Mount functions_ take a DOM element as their first argument. Their
|
||||||
Their responsibility is to set up initial state, event listeners, and
|
responsibility is to set up initial state, event listeners, and provide behavior
|
||||||
provide behavior and rendering for the target element.
|
and rendering for the target element.
|
||||||
|
|
||||||
For example, this mount function implements a simple counter:
|
For example, this mount function implements a simple counter:
|
||||||
|
|
||||||
@@ -290,13 +287,13 @@ export function MyCounter(el) {
|
|||||||
document.querySelectorAll('.my-counter').forEach(MyCounter);
|
document.querySelectorAll('.my-counter').forEach(MyCounter);
|
||||||
```
|
```
|
||||||
|
|
||||||
This comes with quite some boilerplate but has useful properties,
|
This comes with quite some boilerplate but has useful properties, as we will see
|
||||||
as we will see in the following sections.
|
in the following sections.
|
||||||
|
|
||||||
Note that a mount function does not have to set any base HTML,
|
Note that a mount function does not have to set any base HTML, and may instead
|
||||||
and may instead only set event listeners to enable some behavior.
|
only set event listeners to enable some behavior. Also note that an element can
|
||||||
Also note that an element can be mounted with multiple mount functions.
|
be mounted with multiple mount functions. For example, to-do items are mounted
|
||||||
For example, to-do items are mounted with `TodoItem` and `AppDraggable`.
|
with `TodoItem` and `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 and combined
|
components and behaviors can be implemented using the same idiom and combined
|
||||||
@@ -313,28 +310,26 @@ Reference:
|
|||||||
I found it effective to implement one-way data flow similar to React's approach,
|
I found it effective to implement one-way data flow similar to React's approach,
|
||||||
however exclusively using custom DOM events.
|
however exclusively using custom DOM events.
|
||||||
|
|
||||||
- **Data flows downwards** from parent components to child components
|
- **Data flows downwards** from parent components to child components through
|
||||||
through custom DOM events. Data events are in noun-form.
|
custom DOM events. Data events are in noun-form.
|
||||||
- **Actions flow upwards** through custom DOM events (bubbling up),
|
- **Actions flow upwards** through custom DOM events (bubbling up), usually
|
||||||
usually resulting in some parent component state change which is in turn
|
resulting in some parent component state change which is in turn propagated
|
||||||
propagated downwards through data events. Action events are in verb-form.
|
downwards through data events. Action events are in verb-form.
|
||||||
|
|
||||||
The business logic is factored into a pure functional core
|
The business logic is factored into a pure functional core
|
||||||
([TodoLogic.js](./public/scripts/TodoLogic.js)).
|
([TodoLogic.js](./public/scripts/TodoLogic.js)). This is a sensible approach in
|
||||||
This is a sensible approach in most UI architectures as it encapsulates
|
most UI architectures as it encapsulates state transitions in portable, testable
|
||||||
state transitions in portable, testable units.
|
units.
|
||||||
|
|
||||||
The controller is factored into a separate behavior
|
The controller is factored into a separate behavior
|
||||||
([TodoController.js](./public/scripts/TodoController.js)).
|
([TodoController.js](./public/scripts/TodoController.js)). It only receives and
|
||||||
It only receives and dispatches events,
|
dispatches events, calling the business logic to apply changes and emit state.
|
||||||
calling the business logic to apply changes and emit state.
|
|
||||||
It also handles persistence in Local Storage.
|
It also handles persistence in Local Storage.
|
||||||
|
|
||||||
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
|
||||||
I didn't need event delegation à la jQuery for this study
|
jQuery for this study but I believe it's a useful concept that is difficult to
|
||||||
but I believe it's a useful concept that is difficult to do
|
do concisely with standard APIs.
|
||||||
concisely with standard APIs.
|
|
||||||
|
|
||||||
Reference:
|
Reference:
|
||||||
|
|
||||||
@@ -344,8 +339,8 @@ 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
|
||||||
as this may hurt performance and will likely break important functionality like:
|
this may hurt performance and will likely break important functionality like:
|
||||||
|
|
||||||
- `<a>`, `<button>`, `<input>`, etc. may lose focus.
|
- `<a>`, `<button>`, `<input>`, etc. may lose focus.
|
||||||
- Form inputs may lose data.
|
- Form inputs may lose data.
|
||||||
@@ -357,18 +352,17 @@ 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
|
||||||
makes necessary changes.
|
makes necessary changes.
|
||||||
|
|
||||||
- **Idempotence:** Update functions may be called at any time
|
- **Idempotence:** Update functions may be called at any time and should always
|
||||||
and should always render the component correctly.
|
render the component correctly.
|
||||||
- **Completeness:** Update functions should render
|
- **Completeness:** Update functions should render the whole component,
|
||||||
the whole component, regardless of what triggered the update.
|
regardless of what triggered the update.
|
||||||
|
|
||||||
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
|
||||||
by checking for data changes, caching selectors, etc.
|
for data changes, caching selectors, etc. It is also simple to understand.
|
||||||
It is also simple to understand.
|
|
||||||
|
|
||||||
Reference:
|
Reference:
|
||||||
|
|
||||||
@@ -377,9 +371,9 @@ Reference:
|
|||||||
|
|
||||||
#### 3.2.4. Reconciliation
|
#### 3.2.4. Reconciliation
|
||||||
|
|
||||||
Expectedly, the hardest part of the study was rendering a variable
|
Expectedly, the hardest part of the study was rendering a variable amount of
|
||||||
amount of dynamic components efficiently. Here's a commented example
|
dynamic components efficiently. Here's a commented example from the
|
||||||
from the implementation outlining the reconciliation algorithm:
|
implementation outlining the reconciliation algorithm:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
export function TodoList(el) {
|
export function TodoList(el) {
|
||||||
@@ -441,32 +435,30 @@ export function TodoList(el) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
It's very verbose, with lots of opportunities to introduce bugs.
|
It's very verbose, with lots of opportunities to introduce bugs. Compared to a
|
||||||
Compared to a simple loop in JSX, this approach seems unreasonable.
|
simple loop in JSX, this approach seems unreasonable. It is quite efficient as
|
||||||
It is quite efficient as it does minimal work, but it's
|
it does minimal work, but it's definitely a candidate for a utility function or
|
||||||
definitely a candidate for a utility function or library.
|
library.
|
||||||
|
|
||||||
### 3.3. Drag & Drop
|
### 3.3. Drag & Drop
|
||||||
|
|
||||||
Implementing drag & drop from scratch was challenging,
|
Implementing drag & drop from scratch was challenging, especially regarding
|
||||||
especially regarding browser/device consistency.
|
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,
|
||||||
However, having a customized implementation paid off once I started
|
having a customized implementation paid off once I started introducing
|
||||||
introducing animations as both had to be coordinated closely.
|
animations as both had to be coordinated closely. I can imagine this would have
|
||||||
I can imagine this would have been a difficult problem
|
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
|
||||||
It's clearly the most complex part of the study but I was able to implement it
|
study but I was able to implement it without changing existing code besides
|
||||||
without changing existing code besides mounting behaviors and
|
mounting behaviors and adding event handlers.
|
||||||
adding event handlers.
|
|
||||||
|
|
||||||
I suspect the drag & drop implementation to have some subtle problems on
|
I suspect the drag & drop implementation to have some subtle problems on touch
|
||||||
touch devices, as I haven't extensively tested them. Using a library for
|
devices, as I haven't extensively tested them. Using a library for identifying
|
||||||
identifying the gestures could be more sensible and would reduce costs in
|
the gestures could be more sensible and would reduce costs in testing browsers
|
||||||
testing browsers and devices.
|
and devices.
|
||||||
|
|
||||||
Reference:
|
Reference:
|
||||||
|
|
||||||
@@ -478,15 +470,14 @@ Reference:
|
|||||||
|
|
||||||
For the final product I wanted smooth animations for most user interactions.
|
For the final product I wanted smooth animations for most user interactions.
|
||||||
This is a cross-cutting concern which was implemented using the
|
This is a cross-cutting concern which was implemented using the
|
||||||
[FLIP](https://aerotwist.com/blog/flip-your-animations/) technique as devised
|
[FLIP](https://aerotwist.com/blog/flip-your-animations/) technique as devised by
|
||||||
by [Paul Lewis](https://twitter.com/aerotwist).
|
[Paul Lewis](https://twitter.com/aerotwist).
|
||||||
|
|
||||||
Implementing FLIP animations without a large refactoring was the biggest
|
Implementing FLIP animations without a large refactoring was the biggest
|
||||||
challenge of this case study, especially in combination with drag & drop.
|
challenge of this case study, especially in combination with drag & drop. After
|
||||||
After days of work I was able to implement the algorithm in isolation and
|
days of work I was able to implement the algorithm in isolation and coordinate
|
||||||
coordinate it with other concerns at the application's root level.
|
it with other concerns at the application's root level. The `useCapture` mode of
|
||||||
The `useCapture` mode of `addEventListener` proved to be very useful
|
`addEventListener` proved to be very useful in this case.
|
||||||
in this case.
|
|
||||||
|
|
||||||
Reference:
|
Reference:
|
||||||
|
|
||||||
@@ -495,10 +486,11 @@ Reference:
|
|||||||
|
|
||||||
## 4. Tooling
|
## 4. Tooling
|
||||||
|
|
||||||
While no runtime dependencies or build steps were allowed,
|
While no runtime dependencies or build steps were allowed, I did introduce some
|
||||||
I did introduce some local tooling to support the development experience.
|
local tooling to support the development experience.
|
||||||
|
|
||||||
As a quick start, here are the steps to get a local development server up and running:
|
As a quick start, here are the steps to get a local development server up and
|
||||||
|
running:
|
||||||
|
|
||||||
- Install [git](https://git-scm.com/)
|
- Install [git](https://git-scm.com/)
|
||||||
- Install [Node.js](https://nodejs.org/) (>= 20)
|
- Install [Node.js](https://nodejs.org/) (>= 20)
|
||||||
@@ -513,34 +505,32 @@ The following sections describe the tooling in more detail.
|
|||||||
|
|
||||||
### 4.1. Local Development Server
|
### 4.1. Local Development Server
|
||||||
|
|
||||||
Because ES modules are not allowed under the `file://` protocol
|
Because ES modules are not allowed under the `file://` protocol I needed to run
|
||||||
I needed to run a local web server for development.
|
a local web server for development. Initially, I used
|
||||||
Initially, I used [serve](https://www.npmjs.com/package/serve)
|
[serve](https://www.npmjs.com/package/serve) which was good enough to get going
|
||||||
which was good enough to get going but requires manually reloading
|
but requires manually reloading the application on every change.
|
||||||
the application on every change.
|
|
||||||
|
|
||||||
Most modern frameworks support _hot reloading_,
|
Most modern frameworks support _hot reloading_, i.e. updating the application in
|
||||||
i.e. updating the application in place when changing source files.
|
place when changing source files. Hot reloading provides fast feedback during
|
||||||
Hot reloading provides fast feedback during development,
|
development, especially useful for fine-tuning visuals.
|
||||||
especially useful for fine-tuning visuals.
|
|
||||||
|
|
||||||
Unfortunately, I could not find a local development server
|
Unfortunately, I could not find a local development server supporting some form
|
||||||
supporting some form of hot reloading
|
of hot reloading without introducing a framework or build system, but I was able
|
||||||
without introducing a framework or build system,
|
to implement a minimal local development server (~200 LOC) with the following
|
||||||
but I was able to implement a minimal local development server (~200 LOC)
|
behavior:
|
||||||
with the following behavior:
|
|
||||||
|
|
||||||
- Changes to stylesheets or images will hot replace the changed resources.
|
- Changes to stylesheets or images will hot replace the changed resources.
|
||||||
- Other changes (e.g. JavaScript or HTML) will cause a full page reload.
|
- Other changes (e.g. JavaScript or HTML) will cause a full page reload.
|
||||||
|
|
||||||
While it's not proper [hot module replacement](https://webpack.js.org/concepts/hot-module-replacement/)
|
While it's not proper
|
||||||
(which requires immense infrastructure),
|
[hot module replacement](https://webpack.js.org/concepts/hot-module-replacement/)
|
||||||
it requires zero changes to the application source
|
(which requires immense infrastructure), it requires zero changes to the
|
||||||
and provides a similar experience because page reloads are fast.
|
application source and provides a similar experience because page reloads are
|
||||||
|
fast.
|
||||||
|
|
||||||
Note that the local development server is highly experimental and is likely lacking
|
Note that the local development server is highly experimental and is likely
|
||||||
some features to be generally usable. See [/dev](./dev) for the implementation.
|
lacking some features to be generally usable. See [/dev](./dev) for the
|
||||||
Feedback is highly appreciated.
|
implementation. Feedback is highly appreciated.
|
||||||
|
|
||||||
### 4.2. Formatting and Linting
|
### 4.2. Formatting and Linting
|
||||||
|
|
||||||
@@ -550,8 +540,8 @@ Basic code consistency is provided by
|
|||||||
- [ESLint](https://eslint.org), and
|
- [ESLint](https://eslint.org), and
|
||||||
- [stylelint](https://stylelint.io).
|
- [stylelint](https://stylelint.io).
|
||||||
|
|
||||||
I've set the ESLint parser to ES2020 to ensure only ES2020 code is allowed.
|
I've set the ESLint parser to ES2020 to ensure only ES2020 code is allowed. I've
|
||||||
I've also added stylelint rules to check for rscss-compatible CSS.
|
also added stylelint rules to check for rscss-compatible CSS.
|
||||||
|
|
||||||
Run these commands to try it out:
|
Run these commands to try it out:
|
||||||
|
|
||||||
@@ -565,18 +555,18 @@ integrate well with VSCode so I've rarely had to run these manually.
|
|||||||
|
|
||||||
### 4.3. Testing
|
### 4.3. Testing
|
||||||
|
|
||||||
I've implemented some end-to-end and unit tests
|
I've implemented some end-to-end and unit tests using
|
||||||
using [Playwright](https://playwright.dev/).
|
[Playwright](https://playwright.dev/). While running a local web server (see
|
||||||
While running a local web server (see above), you can run the tests with
|
above), you can run the tests with
|
||||||
|
|
||||||
- `npm run test` for headless tests, or
|
- `npm run test` for headless tests, or
|
||||||
- `npm run test-ui` for interactive mode.
|
- `npm run test-ui` for interactive mode.
|
||||||
|
|
||||||
These might ask you to install Playwright; just follow the instructions.
|
These might ask you to install Playwright; just follow the instructions.
|
||||||
|
|
||||||
There's a lot more to explore here, but it's not much different from
|
There's a lot more to explore here, but it's not much different from testing
|
||||||
testing other frontend stacks. It's actually simpler as there was zero
|
other frontend stacks. It's actually simpler as there was zero configuration and
|
||||||
configuration and just one dependency.
|
just one dependency.
|
||||||
|
|
||||||
Reference:
|
Reference:
|
||||||
|
|
||||||
@@ -587,15 +577,14 @@ Reference:
|
|||||||
|
|
||||||
I was able to set up code coverage (at least for end-to-end tests) via
|
I was able to set up code coverage (at least for end-to-end tests) via
|
||||||
[Playwright's code coverage feature](https://playwright.dev/docs/api/class-coverage)
|
[Playwright's code coverage feature](https://playwright.dev/docs/api/class-coverage)
|
||||||
and [c8](https://github.com/bcoe/c8).
|
and [c8](https://github.com/bcoe/c8). This introduced another dependency and was
|
||||||
This introduced another dependency and was slightly more involved to get right,
|
slightly more involved to get right, e.g. mapping localhost URLs to file URLs.
|
||||||
e.g. mapping localhost URLs to file URLs.
|
|
||||||
|
|
||||||
Use `npm run test-coverage` to run the tests and produce an LCOV test coverage
|
Use `npm run test-coverage` to run the tests and produce an LCOV test coverage
|
||||||
report in `./coverage`.
|
report in `./coverage`.
|
||||||
|
|
||||||
Note that the implementation is specific to the project structure,
|
Note that the implementation is specific to the project structure, e.g.
|
||||||
e.g. `/public` as web root and port `8080` are hard-coded.
|
`/public` as web root and port `8080` are hard-coded.
|
||||||
|
|
||||||
Reference:
|
Reference:
|
||||||
|
|
||||||
@@ -604,9 +593,9 @@ Reference:
|
|||||||
|
|
||||||
### 4.4. Pipeline
|
### 4.4. Pipeline
|
||||||
|
|
||||||
I've added a simple CI/CD pipeline via GitHub Actions.
|
I've added a simple CI/CD pipeline via GitHub Actions. It runs linters and
|
||||||
It runs linters and tests, and deploys to GitHub Pages on success.
|
tests, and deploys to GitHub Pages on success. This was straight-forward and is
|
||||||
This was straight-forward and is orthogonal to the application code and other tooling.
|
orthogonal to the application code and other tooling.
|
||||||
|
|
||||||
Reference:
|
Reference:
|
||||||
|
|
||||||
@@ -615,9 +604,9 @@ Reference:
|
|||||||
### 4.5. Debugging
|
### 4.5. Debugging
|
||||||
|
|
||||||
I've mostly used [Chrome DevTools](https://developer.chrome.com/docs/devtools)
|
I've mostly used [Chrome DevTools](https://developer.chrome.com/docs/devtools)
|
||||||
for debugging and the experience was fantastic.
|
for debugging and the experience was fantastic. It feels incredibly _immediate_
|
||||||
It feels incredibly _immediate_ inspecting an application
|
inspecting an application without third-party code or any kind of cruft (e.g.
|
||||||
without third-party code or any kind of cruft (e.g. source maps).
|
source maps).
|
||||||
|
|
||||||
## 5. Assessment
|
## 5. Assessment
|
||||||
|
|
||||||
@@ -635,45 +624,45 @@ and usable:
|
|||||||
- Local Storage persistence
|
- Local Storage persistence
|
||||||
|
|
||||||
Additionally, most interactions are smoothly animated at 60 frames per second.
|
Additionally, most interactions are smoothly animated at 60 frames per second.
|
||||||
In particular, dragging and dropping gives proper visual feedback
|
In particular, dragging and dropping gives proper visual feedback when elements
|
||||||
when elements are reordered.
|
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 in 2019. In the meantime, the TeuxDeux
|
working on the case study in 2019. In the meantime, the TeuxDeux team released
|
||||||
team released an update with a much better drag & drop experience. Great job!_
|
an update with a much better drag & drop experience. Great job!_
|
||||||
|
|
||||||
One notable missing feature is Markdown support. It would be unreasonable
|
One notable missing feature is Markdown support. It would be unreasonable to
|
||||||
to implement Markdown from scratch; this is a valid candidate for using
|
implement Markdown from scratch; this is a valid candidate for using an external
|
||||||
an external library as it is entirely orthogonal to the remaining codebase.
|
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
|
||||||
and Safari on iOS.
|
iOS.
|
||||||
|
|
||||||
_TODO Test more browsers and devices._
|
_TODO Test more browsers and devices._
|
||||||
|
|
||||||
A fresh load of the original TeuxDeux application transfers around **1.2 MB**
|
A fresh load of the original TeuxDeux application transfers around **1.2 MB**
|
||||||
and finishes loading at over **1000 ms**, sometimes up to 2000ms
|
and finishes loading at over **1000 ms**, sometimes up to 2000ms (measured in
|
||||||
(measured in 12/2023).
|
12/2023). Reloads finish at around **700 ms**.
|
||||||
Reloads finish at around **700 ms**.
|
|
||||||
|
|
||||||
With a transferred size of around **55 KB**, the vanilla application consistently
|
With a transferred size of around **55 KB**, the vanilla application
|
||||||
loads in **300-500 ms**—not minified and with each script, stylesheet and icon
|
consistently loads in **300-500 ms**—not minified and with each script,
|
||||||
served as an individual file. Reloads finish at **100-200 ms**; again, not
|
stylesheet and icon served as an individual file. Reloads finish at **100-200
|
||||||
optimized at all (with e.g. asset hashing/indefinite caching).
|
ms**; again, not optimized at all (with e.g. asset hashing/indefinite caching).
|
||||||
|
|
||||||
_To be fair, my implementation misses quite a few features from the original. I suspect a fully equivalent clone to be well below 100 KB transfer, though._
|
_To be fair, my implementation misses quite a few features from the original. I
|
||||||
|
suspect a fully equivalent clone to be well below 100 KB transfer, though._
|
||||||
|
|
||||||
_TODO Run more formal performance tests and add figures for the results._
|
_TODO Run more formal performance tests and add figures for the results._
|
||||||
|
|
||||||
### 5.2. Code Quality
|
### 5.2. Code Quality
|
||||||
|
|
||||||
Unfortunately, it is quite hard to find undisputed, objective measurements
|
Unfortunately, it is quite hard to find undisputed, objective measurements for
|
||||||
for code quality (besides trivialities like code style, linting, etc.).
|
code quality (besides trivialities like code style, linting, etc.). The only
|
||||||
The only generally accepted assessment seems to be peer reviewal.
|
generally accepted assessment seems to be peer reviewal.
|
||||||
|
|
||||||
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
|
||||||
the following sections summarize relevant facts about the codebase
|
sections summarize relevant facts about the codebase and some opinionated
|
||||||
and some opinionated statements based on my experience in the industry.
|
statements based on my experience in the industry.
|
||||||
|
|
||||||
#### 5.2.1. The Good
|
#### 5.2.1. The Good
|
||||||
|
|
||||||
@@ -689,7 +678,8 @@ and some opinionated statements based on my experience in the industry.
|
|||||||
- State separated from the DOM
|
- State separated from the DOM
|
||||||
- Idempotent updates
|
- Idempotent updates
|
||||||
- Data flow using custom events
|
- Data flow using custom events
|
||||||
- Compare the proposed architecture to the API/conceptual surface of Angular or React...
|
- Compare the proposed architecture to the API/conceptual surface of Angular or
|
||||||
|
React...
|
||||||
- Progressive developer experience
|
- Progressive developer experience
|
||||||
- Markup, style, and behavior are orthogonal and can be developed separately.
|
- Markup, style, and behavior are orthogonal and can be developed separately.
|
||||||
- Adding behavior has little impact on the markup besides adding classes.
|
- Adding behavior has little impact on the markup besides adding classes.
|
||||||
@@ -704,23 +694,24 @@ and some opinionated statements based on my experience in the industry.
|
|||||||
All source files (HTML, CSS and JS) combine to **under 3000 lines of code**,
|
All source files (HTML, CSS and JS) combine to **under 3000 lines of code**,
|
||||||
including comments and empty lines.
|
including comments and empty lines.
|
||||||
|
|
||||||
For comparison, prettifying the original TeuxDeux's minified JS assets
|
For comparison, prettifying the original TeuxDeux's minified JS assets yields
|
||||||
yields **81602 LOC** (12/2023).
|
**81602 LOC** (12/2023).
|
||||||
|
|
||||||
_To be fair, my implementation misses quite a few features from the original. I suspect a fully equivalent clone to be well below 10000 LOC, though._
|
_To be fair, my implementation misses quite a few features from the original. I
|
||||||
|
suspect a fully equivalent clone to be well below 10000 LOC, though._
|
||||||
|
|
||||||
#### 5.2.2. The Verbose
|
#### 5.2.2. The Verbose
|
||||||
|
|
||||||
- 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.
|
||||||
- `el.querySelectorAll(':scope ...')` is somewhat default/expected and
|
- `el.querySelectorAll(':scope ...')` is somewhat default/expected and would
|
||||||
would justify a helper.
|
justify a helper.
|
||||||
- Listening to and dispatching events is slightly verbose.
|
- Listening to and dispatching events is slightly verbose.
|
||||||
- Although not used in this study,
|
- Although not used in this study, event delegation seems hard to implement
|
||||||
event delegation seems hard to implement without code duplication.
|
without code duplication.
|
||||||
|
|
||||||
Eliminating verbosity through build steps and a minimal set of helpers
|
Eliminating verbosity through build steps and a minimal set of helpers would
|
||||||
would reduce the comparably low code size (see above) even further.
|
reduce the comparably low code size (see above) even further.
|
||||||
|
|
||||||
#### 5.2.3. The Bad
|
#### 5.2.3. The Bad
|
||||||
|
|
||||||
@@ -728,124 +719,123 @@ would reduce the comparably low code size (see above) even further.
|
|||||||
- Event names share a global namespace.
|
- Event names share a global namespace.
|
||||||
- Especially problematic for events that bubble up.
|
- Especially problematic for events that bubble up.
|
||||||
- No syntax highlighting or code completion in HTML strings.
|
- No syntax highlighting or code completion in HTML strings.
|
||||||
- Can be mitigated with [es6-string-html](https://marketplace.visualstudio.com/items?itemName=Tobermory.es6-string-html)
|
- Can be mitigated with
|
||||||
- The separation between base HTML and dynamic rendering is not ideal
|
[es6-string-html](https://marketplace.visualstudio.com/items?itemName=Tobermory.es6-string-html)
|
||||||
when compared to JSX, for example.
|
- The separation between base HTML and dynamic rendering is not ideal when
|
||||||
|
compared to JSX, for example.
|
||||||
- JSX/virtual DOM techniques provide much better development ergonomics.
|
- 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
|
||||||
I wouldn't recommend the proposed technique
|
proposed technique without a well-tested helper function, at least.
|
||||||
without a well-tested helper function, at least.
|
- You have to remember mounting behaviors correctly when creating new elements.
|
||||||
- You have to remember mounting behaviors correctly when
|
It would be helpful to automate this somehow, e.g. watch elements of selector
|
||||||
creating new elements. It would be helpful to automate this somehow,
|
X (at all times) and ensure the desired behaviors are mounted once on them.
|
||||||
e.g. watch elements of selector X (at all times) and ensure the desired
|
- No type safety. I've always been a proponent of dynamic languages but since
|
||||||
behaviors are mounted once on them.
|
TypeScript's type system provides the best of both worlds, I cannot recommend
|
||||||
- No type safety. I've always been a proponent of dynamic languages
|
using it enough.
|
||||||
but since TypeScript's type system provides the best of both worlds,
|
|
||||||
I cannot recommend using it enough.
|
|
||||||
- We're effectively locked out of using NPM dependencies that don't provide
|
- We're effectively locked out of using NPM dependencies that don't provide
|
||||||
browser-ready builds (ES modules or UMD).
|
browser-ready builds (ES modules or UMD).
|
||||||
- Most frameworks handle a lot of browser inconsistencies and
|
- Most frameworks handle a lot of browser inconsistencies and continuously
|
||||||
continuously monitor regressions with extensive test suites.
|
monitor regressions with extensive test suites. The cost of browser testing is
|
||||||
The cost of browser testing is possibly higher
|
possibly higher when using a vanilla approach.
|
||||||
when using a vanilla approach.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Besides the issues described above, I believe the codebase is well organized
|
Besides the issues described above, I believe the codebase is well organized and
|
||||||
and there are clear paths for bugfixes and feature development.
|
there are clear paths for bugfixes and feature development. Since there's no
|
||||||
Since there's no third party code, bugs are easy to find and fix,
|
third party code, bugs are easy to find and fix, and there are no dependency
|
||||||
and there are no dependency limitations to work around.
|
limitations to work around.
|
||||||
|
|
||||||
A certain degree of DOM API knowledge is required but I believe this
|
A certain degree of DOM API knowledge is required but I believe this should be a
|
||||||
should be a goal for any web developer.
|
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 not really
|
||||||
not really possible without production usage. From my experience, however,
|
possible without production usage. From my experience, however, I can't imagine
|
||||||
I can't imagine any scenario where mount functions, event-based data flow etc.
|
any scenario where mount functions, event-based data flow etc. are not
|
||||||
are not applicable. The underlying principles power the established frameworks,
|
applicable. The underlying principles power the established frameworks, after
|
||||||
after all:
|
all:
|
||||||
|
|
||||||
- State is separated from the DOM (React, Angular, Vue).
|
- State is separated from the DOM (React, Angular, Vue).
|
||||||
- Rendering is idempotent and complete (React's pure `render` function).
|
- Rendering is idempotent and complete (React's pure `render` function).
|
||||||
- One-way data flow (React)
|
- One-way data flow (React)
|
||||||
|
|
||||||
An open question is if these patterns hold for library authors.
|
An open question is if these patterns hold for library authors. Although not
|
||||||
Although not considered during the study, some observations can be made:
|
considered during the study, some observations can be made:
|
||||||
|
|
||||||
- The JavaScript itself would be fine to share as ES modules.
|
- The JavaScript itself would be fine to share as ES modules.
|
||||||
- Event naming needs great care, as dispatching (bubbling) events
|
- Event naming needs great care, as dispatching (bubbling) events from imported
|
||||||
from imported behaviors can trigger parent listeners in consumer code.
|
behaviors can trigger parent listeners in consumer code.
|
||||||
- Can be mitigated by providing options to prefix or map event names.
|
- Can be mitigated by providing options to prefix or map event names.
|
||||||
- CSS names share a global namespace and need to be managed as well.
|
- CSS names share a global namespace and need to be managed as well.
|
||||||
- Can also be mitigated by prefixing, however making the JavaScript
|
- Can also be mitigated by prefixing, however making the JavaScript a bit more
|
||||||
a bit more complex.
|
complex.
|
||||||
|
|
||||||
## 6. Conclusion
|
## 6. Conclusion
|
||||||
|
|
||||||
The result of this study is a working to-do application with decent UI/UX and
|
The result of this study is a working to-do application with decent UI/UX and
|
||||||
most of the functionality of the original TeuxDeux app,
|
most of the functionality of the original TeuxDeux app, built using only
|
||||||
built using only standard web technologies.
|
standard web technologies. It comes with better overall performance at a
|
||||||
It comes with better overall performance
|
fraction of the code size and bandwidth.
|
||||||
at a fraction of the code size and bandwidth.
|
|
||||||
|
|
||||||
The codebase seems manageable through a handful of simple concepts,
|
The codebase seems manageable through a handful of simple concepts, although it
|
||||||
although it is quite verbose and even messy in some areas.
|
is quite verbose and even messy in some areas. This could be mitigated by a
|
||||||
This could be mitigated by a small number of helper functions and
|
small number of helper functions and simple build steps (e.g. SCSS and
|
||||||
simple build steps (e.g. SCSS and TypeScript).
|
TypeScript).
|
||||||
|
|
||||||
The study's method helped discovering patterns and techniques that
|
The study's method helped discovering patterns and techniques that are at least
|
||||||
are at least on par with a framework-based approach for the given subject,
|
on par with a framework-based approach for the given subject, without
|
||||||
without accidentally building a custom framework.
|
accidentally building a custom framework.
|
||||||
|
|
||||||
A notable exception to the latter is rendering variable numbers of elements
|
A notable exception to the latter is rendering variable numbers of elements in a
|
||||||
in a concise way. I was unable to eliminate the verbosity involved
|
concise way. I was unable to eliminate the verbosity involved in basic but
|
||||||
in basic but efficient reconciliation.
|
efficient reconciliation. Further research is needed in this area, but for now
|
||||||
Further research is needed in this area, but for now this appears to be
|
this appears to be a valid candidate for a (possibly external) general-purpose
|
||||||
a valid candidate for a (possibly external) general-purpose utility.
|
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 implementation cannot "rust", by definition, as no dependencies
|
The implementation cannot "rust", by definition, as no dependencies can become
|
||||||
can become out of date.
|
out of date.
|
||||||
|
|
||||||
Another thought to be taken with a grain of salt: I believe frameworks
|
Another thought to be taken with a grain of salt: I believe frameworks make
|
||||||
make simple tasks even simpler, but hard tasks (e.g. implementing cross-cutting
|
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.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Setting some constraints up-front forced me to challenge
|
Setting some constraints up-front forced me to challenge my assumptions and
|
||||||
my assumptions and preconceptions about vanilla web development.
|
preconceptions about vanilla web development. It was quite liberating to avoid
|
||||||
It was quite liberating to avoid general-purpose utilities and
|
general-purpose utilities and get things done with what's readily available.
|
||||||
get things done with what's readily available.
|
|
||||||
|
|
||||||
While I think the study is relatively complete, there's always more to explore.
|
While I think the study is relatively complete, there's always more to explore.
|
||||||
[Ideas, questions, bug reports](https://github.com/morris/vanilla-todo/issues) and
|
[Ideas, questions, bug reports](https://github.com/morris/vanilla-todo/issues)
|
||||||
pull requests are more than welcome!
|
and pull requests are more than welcome!
|
||||||
|
|
||||||
Finally, this case study does not question using dependencies, libraries or frameworks
|
Finally, this case study does not question using dependencies, libraries or
|
||||||
in general—code sharing is an essential part of software engineering.
|
frameworks in general—code sharing is an essential part of software
|
||||||
It was a constrained experiment designed to discover novel methods
|
engineering. It was a constrained experiment designed to discover novel methods
|
||||||
for vanilla web development and, hopefully,
|
for vanilla web development and, hopefully, inspire innovation and further
|
||||||
inspire innovation and further research in the area.
|
research in the area.
|
||||||
|
|
||||||
## 7. Beyond Vanilla
|
## 7. Beyond Vanilla
|
||||||
|
|
||||||
As detailed in the assessment, the result of the case study
|
As detailed in the assessment, the result of the case study could be
|
||||||
could be significantly improved if build steps and helpers were allowed.
|
significantly improved if build steps and helpers were allowed. Beyond the
|
||||||
Beyond the strict rules I've used in this experiment,
|
strict rules I've used in this experiment, here are a few ideas I'd like to see
|
||||||
here are a few ideas I'd like to see explored in the future:
|
explored in the future:
|
||||||
|
|
||||||
- Run another case study with TypeScript, SCSS, and build steps (seems promising).
|
- Run another case study with TypeScript, SCSS, and build steps (seems
|
||||||
- Extrapolate deep utility functions (e.g. `reconcile()`) to mitigate some of the discovered downsides.
|
promising).
|
||||||
- Experiment with architectures based on virtual DOM rendering and standard DOM events.
|
- Extrapolate deep utility functions (e.g. `reconcile()`) to mitigate some of
|
||||||
|
the discovered downsides.
|
||||||
|
- Experiment with architectures based on virtual DOM rendering and standard DOM
|
||||||
|
events.
|
||||||
- Compile discovered rules, patterns and techniques into a comprehensive guide.
|
- Compile discovered rules, patterns and techniques into a comprehensive guide.
|
||||||
|
|
||||||
Case studies constrained by a set of formal rules are an effective way to find
|
Case studies constrained by a set of formal rules are an effective way to find
|
||||||
new patterns and techniques in a wide range of domains.
|
new patterns and techniques in a wide range of domains. I'd love to see similar
|
||||||
I'd love to see similar experiments in the future.
|
experiments in the future.
|
||||||
|
|
||||||
## 8. Appendix
|
## 8. Appendix
|
||||||
|
|
||||||
@@ -903,7 +893,8 @@ Thanks!
|
|||||||
- Added [tooling section](#4-tooling)
|
- Added [tooling section](#4-tooling)
|
||||||
- Refactored business logic into pure functional module
|
- Refactored business logic into pure functional module
|
||||||
- Added support for [code coverage](#431-code-coverage)
|
- Added support for [code coverage](#431-code-coverage)
|
||||||
- Added [local development server](#41-local-development-server) with hot reloading
|
- Added [local development server](#41-local-development-server) with hot
|
||||||
|
reloading
|
||||||
- Fixed some visual issues
|
- Fixed some visual issues
|
||||||
- Updated dependencies
|
- Updated dependencies
|
||||||
|
|
||||||
@@ -932,13 +923,13 @@ Thanks!
|
|||||||
|
|
||||||
### 10/2020
|
### 10/2020
|
||||||
|
|
||||||
- Refactored for `dataset` [#2](https://github.com/morris/vanilla-todo/issues/2) —
|
- Refactored for `dataset` [#2](https://github.com/morris/vanilla-todo/issues/2)
|
||||||
[@opethrocks](https://github.com/opethrocks)
|
— [@opethrocks](https://github.com/opethrocks)
|
||||||
- Fixed [#3](https://github.com/morris/vanilla-todo/issues/3) (navigation bug) —
|
- Fixed [#3](https://github.com/morris/vanilla-todo/issues/3) (navigation bug)
|
||||||
[@anchepiece](https://github.com/anchepiece),
|
— [@anchepiece](https://github.com/anchepiece),
|
||||||
[@jcoussard](https://github.com/jcoussard)
|
[@jcoussard](https://github.com/jcoussard)
|
||||||
- Fixed [#4](https://github.com/morris/vanilla-todo/issues/4) (double item creation) —
|
- Fixed [#4](https://github.com/morris/vanilla-todo/issues/4) (double item
|
||||||
[@n0nick](https://github.com/n0nick)
|
creation) — [@n0nick](https://github.com/n0nick)
|
||||||
- Fixed [#1](https://github.com/morris/vanilla-todo/issues/4) (bad links) —
|
- Fixed [#1](https://github.com/morris/vanilla-todo/issues/4) (bad links)
|
||||||
[@roryokane](https://github.com/roryokane)
|
— [@roryokane](https://github.com/roryokane)
|
||||||
- Initial version
|
- Initial version
|
||||||
|
Reference in New Issue
Block a user