Browse Source

Links

master
David Larlet 1 month ago
parent
commit
5f153369e1
Signed by: David Larlet <david@larlet.fr> GPG Key ID: 3E2953A359E7E7BD

+ 364
- 0
cache/2024/64ec164fc6c5c23ede3fba6006a47f36/index.html View File

@@ -0,0 +1,364 @@
<!doctype html><!-- This is a valid HTML5 document. -->
<!-- Screen readers, SEO, extensions and so on. -->
<html lang="en">
<!-- Has to be within the first 1024 bytes, hence before the `title` element
See: https://www.w3.org/TR/2012/CR-html5-20121217/document-metadata.html#charset -->
<meta charset="utf-8">
<!-- Why no `X-UA-Compatible` meta: https://stackoverflow.com/a/6771584 -->
<!-- The viewport meta is quite crowded and we are responsible for that.
See: https://codepen.io/tigt/post/meta-viewport-for-2015 -->
<meta name="viewport" content="width=device-width,initial-scale=1">
<!-- Required to make a valid HTML5 document. -->
<title>The quiet, pervasive devaluation of frontend (archive) — David Larlet</title>
<meta name="description" content="Publication mise en cache pour en conserver une trace.">
<!-- That good ol' feed, subscribe :). -->
<link rel="alternate" type="application/atom+xml" title="Feed" href="/david/log/">
<!-- Generated from https://realfavicongenerator.net/ such a mess. -->
<link rel="apple-touch-icon" sizes="180x180" href="/static/david/icons2/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/static/david/icons2/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/static/david/icons2/favicon-16x16.png">
<link rel="manifest" href="/static/david/icons2/site.webmanifest">
<link rel="mask-icon" href="/static/david/icons2/safari-pinned-tab.svg" color="#07486c">
<link rel="shortcut icon" href="/static/david/icons2/favicon.ico">
<meta name="msapplication-TileColor" content="#f7f7f7">
<meta name="msapplication-config" content="/static/david/icons2/browserconfig.xml">
<meta name="theme-color" content="#f7f7f7" media="(prefers-color-scheme: light)">
<meta name="theme-color" content="#272727" media="(prefers-color-scheme: dark)">
<!-- Is that even respected? Retrospectively? What a shAItshow…
https://neil-clarke.com/block-the-bots-that-feed-ai-models-by-scraping-your-website/ -->
<meta name="robots" content="noai, noimageai">
<!-- Documented, feel free to shoot an email. -->
<link rel="stylesheet" href="/static/david/css/style_2021-01-20.css">
<!-- See https://www.zachleat.com/web/comprehensive-webfonts/ for the trade-off. -->
<link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
<link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
<link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
<link rel="preload" href="/static/david/css/fonts/triplicate_t3_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
<link rel="preload" href="/static/david/css/fonts/triplicate_t3_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
<link rel="preload" href="/static/david/css/fonts/triplicate_t3_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
<script>
function toggleTheme(themeName) {
document.documentElement.classList.toggle(
'forced-dark',
themeName === 'dark'
)
document.documentElement.classList.toggle(
'forced-light',
themeName === 'light'
)
}
const selectedTheme = localStorage.getItem('theme')
if (selectedTheme !== 'undefined') {
toggleTheme(selectedTheme)
}
</script>

<meta name="robots" content="noindex, nofollow">
<meta content="origin-when-cross-origin" name="referrer">
<!-- Canonical URL for SEO purposes -->
<link rel="canonical" href="https://joshcollinsworth.com/blog/devaluing-frontend">

<body class="remarkdown h1-underline h2-underline h3-underline em-underscore hr-center ul-star pre-tick" data-instant-intensity="viewport-all">


<article>
<header>
<h1>The quiet, pervasive devaluation of frontend</h1>
</header>
<nav>
<p class="center">
<a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
</svg> Accueil</a> •
<a href="https://joshcollinsworth.com/blog/devaluing-frontend" title="Lien vers le contenu original">Source originale</a>
<br>
Mis en cache le 2024-03-09
</p>
</nav>
<hr>
<p>There’s a trend I’ve noticed—or at least, I <em>think</em> I’ve noticed. It’s the kind of thing that’s hard to be sure of; the kind that might genuinely be true, or might simply <em>appear</em> to be true if you look at it a certain way.</p>
<p>I can’t tell if I’m right, or if the shapes I’m envisioning in arbitrary ink blots say more about me than they do about what I’m observing.</p>
<p>Maybe it’s both. Maybe it’s all subjective gray area and I’m just picking a spot to draw a line.</p>
<p>I guess you can decide for yourself.</p>
<hr>
<aside class="pull-quote svelte-1wy3zzl" aria-hidden="true" hidden><p>I feel like I’m seeing a widespread diminishment of the practice of frontend. Nearly everywhere I look, I notice its importance minimized, and its challenges trivialized.</p>
</aside>

<p class="callout svelte-9qgyge"></p>
<p>I feel like I’m seeing a widespread diminishment of the practice of frontend. Nearly everywhere I look, I notice its importance minimized, and its challenges trivialized.</p>

<p>This effect might be invisible to you right now. And thus, you might be reflexively inclined to say it doesn’t exist.</p>
<p>Maybe you’re right. Maybe it doesn’t. Maybe I just need to step away from my little screens in my little basement office a little more often.</p>
<p>Or, maybe it’s just like any other implicit bias; maybe it seems like it doesn’t exist because it feels so <em>ordinary</em> until you know what to look for.</p>
<p>So: let’s talk about what I see. Maybe you’ll see it, too.</p>
<h2 id="i-see-it-in-the-way-we-talk-about-frontend-languages"><a aria-hidden="true" tabindex="-1" href="#i-see-it-in-the-way-we-talk-about-frontend-languages"><span class="icon icon-link"></span></a>I see it in the way we talk about frontend languages</h2>
<p>I notice CSS is widely considered some or all of the following: unmaintainable; subjective; messy; unruly; unpredictable; a footgun; overly complicated; unscalable; and a nightmare.</p>
<p>(I generally disagree with all of those. But then, I’m also fond of saying CSS is the only language that gets blamed when the <em>author</em> is bad.)</p>
<p>But despite all these claims, CSS is <em>also</em> somehow “not a real programming language.” Many people online will tell you so, often quite loudly, and sometimes even using memes. Same with HTML.</p>
<aside class="pull-quote svelte-1wy3zzl" aria-hidden="true" hidden><p>It’s like CSS exists in some bizarre quantum state; somehow both too complex to use, yet too simple to take seriously, all at once.</p>
</aside>

<p class="callout svelte-9qgyge"></p>
<p>It’s like CSS exists in some bizarre quantum state; somehow both too complex to use, yet too simple to take seriously, all at once.</p>

<p>I find what most of those people actually <em>mean</em> is: HTML and CSS aren’t <em>scripting</em> languages. Which: a) of course they aren’t; and b) is not really the point at all.</p>
<p>CSS is a programming language because that’s what you’re doing when you write it; you’re <em>programming</em> the application’s presentation logic. And that’s important, because CSS has power enough to either maximize the useability of software, or to ruin it beyond all utility. (Really; you’d probably be astounded how many ways there are to utterly destroy anything on the web with hardly any CSS.)</p>
<aside class="pull-quote svelte-1wy3zzl" aria-hidden="true" hidden><p>In many ways, CSS has greater impact than any other language on a user’s experience, which often directly influences success. Why, then, is its role so belittled?</p>
</aside>

<p class="callout svelte-9qgyge"></p>
<p>In many ways, CSS has greater impact than any other language on a user’s experience, which often directly influences success. Why, then, is its role so belittled?</p>

<p>HTML is similar; HTML might not have things like loops and conditionals, but it is still a programming interface nonetheless because it’s how you <em>program</em> a UI—and to do it well requires as much care and expertise as any other language.</p>
<h3 id="language-is-reality"><a aria-hidden="true" tabindex="-1" href="#language-is-reality"><span class="icon icon-link"></span></a>Language is reality</h3>
<p>This might all sound trivial. It might seem like I’m just asserting my stance in some petty online argument. Who cares what developers argue about online? Get a life (as we used to say in the ’90s).</p>
<p>However, I believe there are actually very important reasons to care.</p>
<p>To assert that frontend languages are not programming languages is to assert that what one is doing when writing them is not programming, but something <em>else</em>. Something <em>different</em>.</p>
<p>Something—perhaps not explicitly spoken, but undeniably implied—<em>lesser</em>.</p>
<p>And yes, as biases go, even if there <em>is</em> one against frontend developers, it would be pretty hard to rank it above any of the myriad <em>other</em> biases present in everyday society…</p>
<p>…unless this bias is actually a <em>part of</em> those other biases?</p>
<p><a href="https://cord.co/insights/diversity-and-inclusion/articles/gender-representation#:~:text=Front%20End%20is%20the%20most,and%200.07%25%20non%2Dbinary." rel="nofollow">Frontend tends to be the most diverse job title among developers</a>. If you wanted to find the dev specialization with the <em>most</em> people who aren’t cishet white males, you’d pick frontend.</p>
<p>Do we honestly believe the language around frontend is different purely by mere coincidence?</p>
<h2 id="i-see-it-in-the-way-we-talk-about-practices"><a aria-hidden="true" tabindex="-1" href="#i-see-it-in-the-way-we-talk-about-practices"><span class="icon icon-link"></span></a>I see it in the way we talk about practices</h2>
<p>Certain pursuits are validated with importance, dignity, and honor.</p>
<p>Doctors; lawyers; architects; CEOs; software engineers.</p>
<p>Some kinds of work are “serious” work, which is well and good—except that, implicitly, that means other kinds are <em>not</em> serious.</p>
<p>We might not ever say it, or even think it, but when we cast some people as heroes, we relegate others to the role of the sidekick—even though their labor is no less important, and they do at least as much to push the work toward success.</p>
<p>Nurses; paralegals; interior designers; executive assistants; frontend developers.</p>
<p>(Surely it’s a coincidence the first group tends to be more male than the second.)</p>
<p>Other forms of development are generally considered <em>serious</em> work. They’re <em>important</em>. They’re <em>real</em> computer science. (Computer science itself being a higher level of things we’ve decided are real, serious, and important—maybe not quite as much as medicine or law, but then again, maybe so in some circles.)</p>
<p>Engineering is unquestionably difficult and respectable work. The idea that other engineers are smart—even smarter than we are—is the kind of stereotype that feels so common and true it’s rarely even questioned.</p>
<aside class="pull-quote svelte-1wy3zzl" aria-hidden="true" hidden><p>Mostly, nobody actually <em>says</em> frontend is less important, or less real, or that you don’t have to be as smart to do it. But it often seems to be implied.</p>
</aside>

<p class="callout svelte-9qgyge"></p>
<p>Mostly, nobody actually <em>says</em> frontend is less important, or less real, or that you don’t have to be as smart to do it. But it often seems to be implied.</p>

<p>If I were to guess at the origins of this perception, I’d propose it might all come down to optics. Frontend engineers work in languages that, while notoriously difficult to master, are relatively straightforward to learn. A lot of engineers stop at the basics and move on to other things—which is fine, of course.</p>
<p>But I posit this leads to a <a href="https://en.wikipedia.org/wiki/Dunning%E2%80%93Kruger_effect" rel="nofollow">Dunning-Kruger effect</a> where those who haven’t taken the time to learn what they don’t know never actually become aware of that hazardous chasm, or the mistakes they might have <em>already made</em> relying on their established knowledge.</p>
<p>I’d guess most engineering managers and other folks in charge of developers probably know some HTML, CSS and JavaScript. These tend to be basics most developers touch on early on.</p>
<p>I think when those people in charge look at a frontend developer, there’s an unconscious “I could do that.”</p>
<aside class="pull-quote svelte-1wy3zzl" aria-hidden="true" hidden><p>Our output is artistic, to some degree, and artistic things have a long, storied history of being tragically devalued merely because they seem simple and enjoyable.</p>
</aside>

<p class="callout svelte-9qgyge"></p>
<p>Our output is artistic, to some degree, and artistic things have a long, storied history of being tragically devalued merely because they seem simple and enjoyable.</p>

<p>Contrast our workflows and our output with a backend developer, or site reliability engineer, as examples. They live in worlds of daunting terminals, oceans of data, and inscrutably interconnected systems. What they do <em>looks</em> scary, like something I couldn’t do and wouldn’t want to.</p>
<p>Their output is easily measurable. A new API feature; a more efficient database; crises averted and crashes prevented. They go on charts and get presented to board members.</p>
<p>Our work? Much harder to measure.</p>
<p>Mostly, people just critique it.</p>
<p>Like art.</p>
<h3 id="naming-things-is-hard"><a aria-hidden="true" tabindex="-1" href="#naming-things-is-hard"><span class="icon icon-link"></span></a>Naming things is hard</h3>
<p>Do you ever notice that the job of a person who works on frontend is, explicitly, developing sofware—and yet, esteemed titles like “software developer” (or the arguably even more esteemed “software <em>engineer</em>”) very strongly imply something else.</p>
<p>If our job title <em>does</em> include the word “engineer,” it will almost certainly specify <em>what</em> we’re engineering. It’ll be <em>UI engineer</em>, or <em>frontend engineer</em>, or maybe the newer (and arguably more fitting) <em>design engineer</em>.</p>
<p>But it’s probably not “software developer” or “software engineer” without any other qualification. Because that, tacitly, <em>is not what we do</em>.</p>
<p>Somebody <em>else</em> does that. What we do is, at best, only a <em>part</em> of that. (Implicitly: <em>less</em> than that.)</p>
<p>Sure, this is nuance of language and these titles serve to disambiguate. I get that. Nobody sat down one day and came up with these titles, let alone maliciously; they’re just functions of language and time, and the way they shook out probably wasn’t anyone’s design.</p>
<p>Still: we ought to know by now the power language has to shape our perceptions, and in turn, our behavior.</p>
<aside class="pull-quote svelte-1wy3zzl" aria-hidden="true" hidden><p>The language implies interfaces are decoupled from the software, and not an actual part of it.</p>
</aside>

<p class="callout svelte-9qgyge"></p>
<p>The language implies interfaces are decoupled from the software, and not an actual part of it.</p>

<p>Even though we engineer software all day every day, by definition, somehow what we do isn’t seen as software engineering. It’s different than that. It’s <em>softer</em> than that.</p>
<p>Certainly, we <em>should</em> venerate the skills required to earn a computer science degree, or pass a whiteboarding exercise on data structures and algorithms. But why must we <em>only</em> respect that type of skill?</p>
<p>Why does <em>anyone’s</em> work have to be “more?”</p>
<p>Why can’t it all be important, and challenging, in its own way?</p>
<h2 id="i-see-it-in-the-feminine-coding-of-frontend"><a aria-hidden="true" tabindex="-1" href="#i-see-it-in-the-feminine-coding-of-frontend"><span class="icon icon-link"></span></a>I see it in the feminine coding of frontend</h2>
<aside class="pull-quote svelte-1wy3zzl" aria-hidden="true" hidden><p>Writing CSS seems to be regarded much like taking notes in a meeting, complete with the implicit sexism and devaluation of the note taker’s importance in the room.</p>
</aside>

<p class="callout svelte-9qgyge"></p>
<p>Writing CSS seems to be regarded much like taking notes in a meeting, complete with the implicit sexism and devaluation of the note taker’s importance in the room.</p>

<p>Though critical to the project, frontend work will quite often be disregarded by those who consider it beneath them (usually men, and usually only tacitly, never explicitly). It’s not serious enough; not important enough; not <em>real</em> enough. Too squishy. Like soft skills.</p>
<p>Yes, of course, it’s important. It’s work that <em>somebody</em> needs to do, certainly. But probably not the important people, whose valuable attention is focused on other, bigger, more important problems.</p>
<p>Design itself is typically conflated with frontend, and design has long been colored as a “feminine” pursuit. The deep technical challenges of frontend functionality are often mistakenly regarded more as a sort of painting with pixels; making things <em>look</em> right, rather than <em>work</em> correctly.</p>
<p>Incidentally, folks involved in UI and design tend to be hit harder in layoffs than their peers, who are perceived as “more technical.” Women, too, are often laid off in greater numbers than men. So it’s hard <em>not</em> to see frontend devaluation and sexism as intertwined. (If they’re even separate things at all, that is.)</p>
<h2 id="i-see-it-in-the-way-we-talk-about-frontend-developers"><a aria-hidden="true" tabindex="-1" href="#i-see-it-in-the-way-we-talk-about-frontend-developers"><span class="icon icon-link"></span></a>I see it in the way we talk about frontend developers</h2>
<p>Frontend is complicated. Few would contend with that.</p>
<p>But for some reason, where the difficulty of other forms of engineering are generally blamed on the daunting complexity of computers themselves (and where, again, we generally revere the people who dare attempt to wrangle that complexity), there’s a pervasive myth that frontend is only complex because <em>frontend developers made it that way</em>.</p>
<p>As though we added frameworks and compilers and heaps of packages to our projects because we simply didn’t know any better.</p>
<aside class="pull-quote svelte-1wy3zzl" aria-hidden="true" hidden><p>As though the nearly impossible job of supporting every possible device, operating system, screen size, browser, user preference, and interface in the past, present and future is so simple we invented all the complexity ourselves, just because we were bored.</p>
</aside>

<p class="callout svelte-9qgyge"></p>
<p>As though the nearly impossible job of supporting every possible device, operating system, screen size, browser, user preference, and interface in the past, present and future is so simple we invented all the complexity ourselves, just because we were bored.</p>

<p>There are lots of jokes about frontend developers. There’s nothing wrong with that. Often, we’re the ones making them; we’re not above laughing at ourselves, and besides, there are plenty of jokes about other specialties, too.</p>
<p>We like to kid about our affinity for new things. How we’re always coming up with new frameworks. How we’re always chasing the new hotness.</p>
<p>That’s all well and good…until the joke starts to become the actual perception. Until we begin to be taken <em>less</em> seriously, because our reputation is a punch line.</p>
<aside class="pull-quote svelte-1wy3zzl" aria-hidden="true" hidden><p>Yes, as a group, we get excited about new things. But why doesn’t that make us curious, or adaptable, or inquisitive? Why don’t we get credit for our joy of learning, instead of denigrated for refusing to stay in place?</p>
<p>Why don’t we get the benefit of the doubt?</p>
</aside>

<p class="callout svelte-9qgyge"></p>
<p>Yes, as a group, we get excited about new things. But why doesn’t that make us curious, or adaptable, or inquisitive? Why don’t we get credit for our joy of learning, instead of denigrated for refusing to stay in place?</p>
<p>Why don’t we get the benefit of the doubt?</p>

<p>Browsers move <em>fast</em>, and they’ve evolved at an unprecedented pace in recent years. So I guess I’m not sure where the assumption that we frontend devs should be so intransigent in our tech choices comes from.</p>
<p>Shouldn’t we be persistently reevaluating, when the landscape is constantly shifting?</p>
<p>Shouldn’t we <em>try</em> to keep up? To be better? To rework old ideas with new capabilities?</p>
<p>Don’t we deserve to be considered just a bit more than flighty little magpies, chasing anything that’s shiny?</p>
<h2 id="i-see-it-in-our-responsibilities"><a aria-hidden="true" tabindex="-1" href="#i-see-it-in-our-responsibilities"><span class="icon icon-link"></span></a>I see it in our responsibilities</h2>
<p>In my experience (yours may vary, of course), frontend engineers are regularly deployed as “fixers” on projects. We often aren’t included at the beginning stages, where we could easily identify costly accessibility, performance, and user experience mistakes; we’re brought in at the end, when things have already been built and it’s too late to change anything significantly. By then, it’s our job just to pretty things up.</p>
<p>“Here; other people already made this.” (i.e., they already did the <em>real</em> work.) “Now we just need you to fix it up.”</p>
<p>But…why is it broken?</p>
<p>Why aren’t we addressing the pipeline that generated a flawed product to begin with?</p>
<aside class="pull-quote svelte-1wy3zzl" aria-hidden="true" hidden><p>If our skills are valuable as duct tape over the cracks of organizational shortcomings, why aren’t they valuable during the planning and decision-making that led to those defects, when we could potentially prevent them?</p>
</aside>

<p class="callout svelte-9qgyge"></p>
<p>If our skills are valuable as duct tape over the cracks of organizational shortcomings, why aren’t they valuable during the planning and decision-making that led to those defects, when we could potentially prevent them?</p>

<p>Why are we considered mere decorators, brought in at the end to spruce the place up a bit?</p>
<p>(As though a decorator could fix a deeply inaccessible building with a fresh coat of paint and some classy furniture.)</p>
<h2 id="i-see-it-in-whats-expected-of-us"><a aria-hidden="true" tabindex="-1" href="#i-see-it-in-whats-expected-of-us"><span class="icon icon-link"></span></a>I see it in what’s expected of us</h2>
<p>The list of skills that a frontend engineer must know expands constantly and unendingly. The world where you could just learn some HTML, CSS, and JavaScript in a couple of months and be ready for a job is mostly in the past.</p>
<p>HTML itself is enough to fill a very thick book, if you want to do it right—and yet, it’s often seen as the <em>simplest</em> thing a frontend developer ought to know.</p>
<p>Then there’s CSS, and JavaScript itself, with all its myriad quirks. Then you’ll of course need to know a framework (probably at least a couple, including the legacy one the company still uses and will never get around to completely replacing), plus a slew of add-ons, plugins, and packages. Probably a system to manage them all, too.</p>
<p>Then there’s accessibility, which is a topic so complex and nuanced almost <em>nobody</em> gets it fully right. But it’s your job, if you’re on the frontend—and not your whole job, either. Just part of it. Good luck!</p>
<p>You’ve got to know things about SEO, too, and that, also, requires nearly a full-time focus to do extremely well, even though you’ll be expected to manage it, too, in a small slice of your time.</p>
<p>Then there’s design, and of course, that includes all the tenets of user interfaces and user experience—all of which a person could spend years learning all on their own.</p>
<p>More and more, you’ll need to know about legal compliance. And marketing, which is its own whole college major.</p>
<p>You also need to know at least a little TypeScript. And come to think of it, you’ll probably be expected to learn at least <em>one</em> backend language, too. (“So you can unblock yourself” is the reason given, although “so you can do your own job, plus a little bit of someone else’s,” is sometimes more accurate.)</p>
<p>From there, it might just be some animation libraries. Charts and data visualization. Testing and assertion libraries. Command line utilities. Cookies. Caching (and cache invalidation). DNS. Networks. Performance. The edge. Serverless. GraphQL. AWS. Docker. At least a little about databases. How’s your knowledge of email? Can you write us some regular expressions? Oh by the way, the marketing team needs you to dig into their analytics, maybe run some A/B tests.</p>
<p>Earlier versions of this post completely left out state management libraries—and said nothing of security. What do you know about preventing XSS attacks? CORS? Content security policies?</p>
<p>The list goes on. Yet, for this unceasingly distended list of skills, frontend is still that silly thing that isn’t the <em>real</em> dev work.</p>
<h2 id="i-see-it-in-the-marketing"><a aria-hidden="true" tabindex="-1" href="#i-see-it-in-the-marketing"><span class="icon icon-link"></span></a>I see it in the marketing</h2>
<aside class="pull-quote svelte-1wy3zzl" aria-hidden="true" hidden><p>Frontend tools market themselves as though frontend is something no one wants to do, and nobody should care about any more than they have to.</p>
</aside>

<p class="callout svelte-9qgyge"></p>
<p>Frontend tools market themselves as though frontend is something no one wants to do, and nobody should care about any more than they have to.</p>

<p>Every tool promises to make frontend uncomplicated and fast; that’s the only concern. They say it the same way I reassure my five-year-old that his vaccinations will be swift and painless; just something necessary to get out of the way as quickly and thoughtlessly as possible.</p>
<p>But when did speed become the main concern? And <em>why</em>?</p>
<p>I understand that “rapidly prototyping” is a good thing, but it feels like it’s become the <em>only</em> thing.</p>
<p>I sometimes find myself wondering whether other developers are simply building things and then throwing them away almost immediately. Is nobody actually <em>maintaining</em> work these days? Has frontend just become an assembly line job, where maximizing output while minimizing hours is the only metric for success?</p>
<p>Is nobody thinking about what we might be trading away for this furious pace? What quality and good ideas and accessibility and user experience we might just be flying by in our pursuit of the rapidest, prototypiest interfaces we can possibly speed-run?</p>
<p>It doesn’t seem like it. These tools, the speedy ones: they promise you can just get on with other things (“more important things” is implied, if not overtly spoken), as long as you use them.</p>
<p>Like every frontend concern is something you just install a WordPress plugin for.</p>
<p>The proliferation of AI tools plays into this as well. I’m sure somebody’s working on some AI product to build your backend for you (if there aren’t already such products on the market). But in the meantime, I’ve already seen a handful for frontend. Their code isn’t accessible, or well built, and it took dedicated frontend engineers to point that out. The companies making them didn’t know, or didn’t care (or both). All that mattered was slapping something up on the screen as quickly and easily as possible.</p>
<h2 id="i-see-it-in-how-good-enough-has-become-the-goal"><a aria-hidden="true" tabindex="-1" href="#i-see-it-in-how-good-enough-has-become-the-goal"><span class="icon icon-link"></span></a>I see it in how “good enough” has become the goal</h2>
<p>The web is positively overrun with inaccessible code. Odds are, even if that specific problem doesn’t currently affect you, you still see forms that won’t submit, invisible buttons, errors surfaced in the UI, utterly broken layouts, things that don’t work on mobile devices or in certain browsers, text and fonts that are difficult to impossible to read, interfaces that are impossible to zoom or scroll, interactions that break down, and silent or unhelpful error messages—all quite likely on a daily or near-daily basis.</p>
<p>This, of course, is to say nothing of the homogeny of the web. The same text, the same colors, the same user dropdown menu, the same little circle with the same little number in it to get your attention; the same marketing, the same three-column layout, the same hero with the image and the big button; the same dimly pulsing skeleton UI that shows up when you click something.</p>
<p>Sites with good design—both aesthetic and functional—are a tragic rarity. And I personally see this as a direct tradeoff we’ve made in our pursuit of shipping as fast as possible.</p>
<aside class="pull-quote svelte-1wy3zzl" aria-hidden="true" hidden><p>It seems like nobody thinks of frontend as a critical part of the product anymore; they only think of it as the nice box the product arrives in.</p>
</aside>

<p class="callout svelte-9qgyge"></p>
<p>It seems like nobody thinks of frontend as a critical part of the product anymore; they only think of it as the nice box the product arrives in.</p>

<p>We need a framework; React will be good enough. No need to look any further.</p>
<p>Just pick a nice color and a serviceable font. No, don’t bother writing any custom CSS; whatever Tailwind comes with will be good enough.</p>
<p>Anything to move on to the next thing. Because whatever the real, important, serious thing is on this project, it isn’t <em>this</em>. Not the frontend.</p>
<p>Not this thing each and every user will look at and interact with every single time they use the product.</p>
<h2 id="i-see-it-everywhere"><a aria-hidden="true" tabindex="-1" href="#i-see-it-everywhere"><span class="icon icon-link"></span></a>I see it everywhere</h2>
<p>But then again…maybe that’s just me.</p>
<p>Maybe I’m feeling sorry for myself. Maybe I’m just a little depressed right now. Maybe I have an inferiority complex and I’m projecting it on everyone else.</p>
<p>Or…maybe there’s some truth to this.</p>
<p>Maybe it’s both.</p>
<p>I guess that’s up to you to decide.</p>
<p>But maybe just keep your eyes open, ok?</p>
<p>You might see something you haven’t noticed before.</p>
</article>


<hr>

<footer>
<p>
<a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
</svg> Accueil</a> •
<a href="/david/log/" title="Accès au flux RSS"><svg class="icon icon-rss2">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-rss2"></use>
</svg> Suivre</a> •
<a href="http://larlet.com" title="Go to my English profile" data-instant><svg class="icon icon-user-tie">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-user-tie"></use>
</svg> Pro</a> •
<a href="mailto:david%40larlet.fr" title="Envoyer un courriel"><svg class="icon icon-mail">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-mail"></use>
</svg> Email</a> •
<abbr class="nowrap" title="Hébergeur : Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33184162340"><svg class="icon icon-hammer2">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-hammer2"></use>
</svg> Légal</abbr>
</p>
<template id="theme-selector">
<form>
<fieldset>
<legend><svg class="icon icon-brightness-contrast">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-brightness-contrast"></use>
</svg> Thème</legend>
<label>
<input type="radio" value="auto" name="chosen-color-scheme" checked> Auto
</label>
<label>
<input type="radio" value="dark" name="chosen-color-scheme"> Foncé
</label>
<label>
<input type="radio" value="light" name="chosen-color-scheme"> Clair
</label>
</fieldset>
</form>
</template>
</footer>
<script src="/static/david/js/instantpage-5.1.0.min.js" type="module"></script>
<script>
function loadThemeForm(templateName) {
const themeSelectorTemplate = document.querySelector(templateName)
const form = themeSelectorTemplate.content.firstElementChild
themeSelectorTemplate.replaceWith(form)

form.addEventListener('change', (e) => {
const chosenColorScheme = e.target.value
localStorage.setItem('theme', chosenColorScheme)
toggleTheme(chosenColorScheme)
})

const selectedTheme = localStorage.getItem('theme')
if (selectedTheme && selectedTheme !== 'undefined') {
form.querySelector(`[value="${selectedTheme}"]`).checked = true
}
}

const prefersColorSchemeDark = '(prefers-color-scheme: dark)'
window.addEventListener('load', () => {
let hasDarkRules = false
for (const styleSheet of Array.from(document.styleSheets)) {
let mediaRules = []
for (const cssRule of styleSheet.cssRules) {
if (cssRule.type !== CSSRule.MEDIA_RULE) {
continue
}
// WARNING: Safari does not have/supports `conditionText`.
if (cssRule.conditionText) {
if (cssRule.conditionText !== prefersColorSchemeDark) {
continue
}
} else {
if (cssRule.cssText.startsWith(prefersColorSchemeDark)) {
continue
}
}
mediaRules = mediaRules.concat(Array.from(cssRule.cssRules))
}

// WARNING: do not try to insert a Rule to a styleSheet you are
// currently iterating on, otherwise the browser will be stuck
// in a infinite loop…
for (const mediaRule of mediaRules) {
styleSheet.insertRule(mediaRule.cssText)
hasDarkRules = true
}
}
if (hasDarkRules) {
loadThemeForm('#theme-selector')
}
})
</script>
</body>
</html>

+ 185
- 0
cache/2024/64ec164fc6c5c23ede3fba6006a47f36/index.md View File

@@ -0,0 +1,185 @@
title: The quiet, pervasive devaluation of frontend
url: https://joshcollinsworth.com/blog/devaluing-frontend
hash_url: 64ec164fc6c5c23ede3fba6006a47f36
archive_date: 2024-03-09
og_image: https://joshcollinsworth.com/images/post_images/deval.webp
description: I keep noticing those of us in the frontend field being treated much the same as nurses, paralegals, and executive assistants. Our work is seen as important, certainly, but just not the same as, or as important as, the “real” work.
favicon: https://joshcollinsworth.com/favicon.png
language: en_US

<p>There’s a trend I’ve noticed—or at least, I <em>think</em> I’ve noticed. It’s the kind of thing that’s hard to be sure of; the kind that might genuinely be true, or might simply <em>appear</em> to be true if you look at it a certain way.</p>
<p>I can’t tell if I’m right, or if the shapes I’m envisioning in arbitrary ink blots say more about me than they do about what I’m observing.</p>
<p>Maybe it’s both. Maybe it’s all subjective gray area and I’m just picking a spot to draw a line.</p>
<p>I guess you can decide for yourself.</p>
<hr>
<aside class="pull-quote svelte-1wy3zzl" aria-hidden="true" hidden><p>I feel like I’m seeing a widespread diminishment of the practice of frontend. Nearly everywhere I look, I notice its importance minimized, and its challenges trivialized.</p>
</aside>

<p class="callout svelte-9qgyge"></p><p>I feel like I’m seeing a widespread diminishment of the practice of frontend. Nearly everywhere I look, I notice its importance minimized, and its challenges trivialized.</p>

<p>This effect might be invisible to you right now. And thus, you might be reflexively inclined to say it doesn’t exist.</p>
<p>Maybe you’re right. Maybe it doesn’t. Maybe I just need to step away from my little screens in my little basement office a little more often.</p>
<p>Or, maybe it’s just like any other implicit bias; maybe it seems like it doesn’t exist because it feels so <em>ordinary</em> until you know what to look for.</p>
<p>So: let’s talk about what I see. Maybe you’ll see it, too.</p>
<h2 id="i-see-it-in-the-way-we-talk-about-frontend-languages"><a aria-hidden="true" tabindex="-1" href="#i-see-it-in-the-way-we-talk-about-frontend-languages"><span class="icon icon-link"></span></a>I see it in the way we talk about frontend languages</h2>
<p>I notice CSS is widely considered some or all of the following: unmaintainable; subjective; messy; unruly; unpredictable; a footgun; overly complicated; unscalable; and a nightmare.</p>
<p>(I generally disagree with all of those. But then, I’m also fond of saying CSS is the only language that gets blamed when the <em>author</em> is bad.)</p>
<p>But despite all these claims, CSS is <em>also</em> somehow “not a real programming language.” Many people online will tell you so, often quite loudly, and sometimes even using memes. Same with HTML.</p>
<aside class="pull-quote svelte-1wy3zzl" aria-hidden="true" hidden><p>It’s like CSS exists in some bizarre quantum state; somehow both too complex to use, yet too simple to take seriously, all at once.</p>
</aside>

<p class="callout svelte-9qgyge"></p><p>It’s like CSS exists in some bizarre quantum state; somehow both too complex to use, yet too simple to take seriously, all at once.</p>

<p>I find what most of those people actually <em>mean</em> is: HTML and CSS aren’t <em>scripting</em> languages. Which: a) of course they aren’t; and b) is not really the point at all.</p>
<p>CSS is a programming language because that’s what you’re doing when you write it; you’re <em>programming</em> the application’s presentation logic. And that’s important, because CSS has power enough to either maximize the useability of software, or to ruin it beyond all utility. (Really; you’d probably be astounded how many ways there are to utterly destroy anything on the web with hardly any CSS.)</p>
<aside class="pull-quote svelte-1wy3zzl" aria-hidden="true" hidden><p>In many ways, CSS has greater impact than any other language on a user’s experience, which often directly influences success. Why, then, is its role so belittled?</p>
</aside>

<p class="callout svelte-9qgyge"></p><p>In many ways, CSS has greater impact than any other language on a user’s experience, which often directly influences success. Why, then, is its role so belittled?</p>

<p>HTML is similar; HTML might not have things like loops and conditionals, but it is still a programming interface nonetheless because it’s how you <em>program</em> a UI—and to do it well requires as much care and expertise as any other language.</p>
<h3 id="language-is-reality"><a aria-hidden="true" tabindex="-1" href="#language-is-reality"><span class="icon icon-link"></span></a>Language is reality</h3>
<p>This might all sound trivial. It might seem like I’m just asserting my stance in some petty online argument. Who cares what developers argue about online? Get a life (as we used to say in the ’90s).</p>
<p>However, I believe there are actually very important reasons to care.</p>
<p>To assert that frontend languages are not programming languages is to assert that what one is doing when writing them is not programming, but something <em>else</em>. Something <em>different</em>.</p>
<p>Something—perhaps not explicitly spoken, but undeniably implied—<em>lesser</em>.</p>
<p>And yes, as biases go, even if there <em>is</em> one against frontend developers, it would be pretty hard to rank it above any of the myriad <em>other</em> biases present in everyday society…</p>
<p>…unless this bias is actually a <em>part of</em> those other biases?</p>
<p><a href="https://cord.co/insights/diversity-and-inclusion/articles/gender-representation#:~:text=Front%20End%20is%20the%20most,and%200.07%25%20non%2Dbinary." rel="nofollow">Frontend tends to be the most diverse job title among developers</a>. If you wanted to find the dev specialization with the <em>most</em> people who aren’t cishet white males, you’d pick frontend.</p>
<p>Do we honestly believe the language around frontend is different purely by mere coincidence?</p>
<h2 id="i-see-it-in-the-way-we-talk-about-practices"><a aria-hidden="true" tabindex="-1" href="#i-see-it-in-the-way-we-talk-about-practices"><span class="icon icon-link"></span></a>I see it in the way we talk about practices</h2>
<p>Certain pursuits are validated with importance, dignity, and honor.</p>
<p>Doctors; lawyers; architects; CEOs; software engineers.</p>
<p>Some kinds of work are “serious” work, which is well and good—except that, implicitly, that means other kinds are <em>not</em> serious.</p>
<p>We might not ever say it, or even think it, but when we cast some people as heroes, we relegate others to the role of the sidekick—even though their labor is no less important, and they do at least as much to push the work toward success.</p>
<p>Nurses; paralegals; interior designers; executive assistants; frontend developers.</p>
<p>(Surely it’s a coincidence the first group tends to be more male than the second.)</p>
<p>Other forms of development are generally considered <em>serious</em> work. They’re <em>important</em>. They’re <em>real</em> computer science. (Computer science itself being a higher level of things we’ve decided are real, serious, and important—maybe not quite as much as medicine or law, but then again, maybe so in some circles.)</p>
<p>Engineering is unquestionably difficult and respectable work. The idea that other engineers are smart—even smarter than we are—is the kind of stereotype that feels so common and true it’s rarely even questioned.</p>
<aside class="pull-quote svelte-1wy3zzl" aria-hidden="true" hidden><p>Mostly, nobody actually <em>says</em> frontend is less important, or less real, or that you don’t have to be as smart to do it. But it often seems to be implied.</p>
</aside>

<p class="callout svelte-9qgyge"></p><p>Mostly, nobody actually <em>says</em> frontend is less important, or less real, or that you don’t have to be as smart to do it. But it often seems to be implied.</p>

<p>If I were to guess at the origins of this perception, I’d propose it might all come down to optics. Frontend engineers work in languages that, while notoriously difficult to master, are relatively straightforward to learn. A lot of engineers stop at the basics and move on to other things—which is fine, of course.</p>
<p>But I posit this leads to a <a href="https://en.wikipedia.org/wiki/Dunning%E2%80%93Kruger_effect" rel="nofollow">Dunning-Kruger effect</a> where those who haven’t taken the time to learn what they don’t know never actually become aware of that hazardous chasm, or the mistakes they might have <em>already made</em> relying on their established knowledge.</p>
<p>I’d guess most engineering managers and other folks in charge of developers probably know some HTML, CSS and JavaScript. These tend to be basics most developers touch on early on.</p>
<p>I think when those people in charge look at a frontend developer, there’s an unconscious “I could do that.”</p>
<aside class="pull-quote svelte-1wy3zzl" aria-hidden="true" hidden><p>Our output is artistic, to some degree, and artistic things have a long, storied history of being tragically devalued merely because they seem simple and enjoyable.</p>
</aside>

<p class="callout svelte-9qgyge"></p><p>Our output is artistic, to some degree, and artistic things have a long, storied history of being tragically devalued merely because they seem simple and enjoyable.</p>

<p>Contrast our workflows and our output with a backend developer, or site reliability engineer, as examples. They live in worlds of daunting terminals, oceans of data, and inscrutably interconnected systems. What they do <em>looks</em> scary, like something I couldn’t do and wouldn’t want to.</p>
<p>Their output is easily measurable. A new API feature; a more efficient database; crises averted and crashes prevented. They go on charts and get presented to board members.</p>
<p>Our work? Much harder to measure.</p>
<p>Mostly, people just critique it.</p>
<p>Like art.</p>
<h3 id="naming-things-is-hard"><a aria-hidden="true" tabindex="-1" href="#naming-things-is-hard"><span class="icon icon-link"></span></a>Naming things is hard</h3>
<p>Do you ever notice that the job of a person who works on frontend is, explicitly, developing sofware—and yet, esteemed titles like “software developer” (or the arguably even more esteemed “software <em>engineer</em>”) very strongly imply something else.</p>
<p>If our job title <em>does</em> include the word “engineer,” it will almost certainly specify <em>what</em> we’re engineering. It’ll be <em>UI engineer</em>, or <em>frontend engineer</em>, or maybe the newer (and arguably more fitting) <em>design engineer</em>.</p>
<p>But it’s probably not “software developer” or “software engineer” without any other qualification. Because that, tacitly, <em>is not what we do</em>.</p>
<p>Somebody <em>else</em> does that. What we do is, at best, only a <em>part</em> of that. (Implicitly: <em>less</em> than that.)</p>
<p>Sure, this is nuance of language and these titles serve to disambiguate. I get that. Nobody sat down one day and came up with these titles, let alone maliciously; they’re just functions of language and time, and the way they shook out probably wasn’t anyone’s design.</p>
<p>Still: we ought to know by now the power language has to shape our perceptions, and in turn, our behavior.</p>
<aside class="pull-quote svelte-1wy3zzl" aria-hidden="true" hidden><p>The language implies interfaces are decoupled from the software, and not an actual part of it.</p>
</aside>

<p class="callout svelte-9qgyge"></p><p>The language implies interfaces are decoupled from the software, and not an actual part of it.</p>

<p>Even though we engineer software all day every day, by definition, somehow what we do isn’t seen as software engineering. It’s different than that. It’s <em>softer</em> than that.</p>
<p>Certainly, we <em>should</em> venerate the skills required to earn a computer science degree, or pass a whiteboarding exercise on data structures and algorithms. But why must we <em>only</em> respect that type of skill?</p>
<p>Why does <em>anyone’s</em> work have to be “more?”</p>
<p>Why can’t it all be important, and challenging, in its own way?</p>
<h2 id="i-see-it-in-the-feminine-coding-of-frontend"><a aria-hidden="true" tabindex="-1" href="#i-see-it-in-the-feminine-coding-of-frontend"><span class="icon icon-link"></span></a>I see it in the feminine coding of frontend</h2>
<aside class="pull-quote svelte-1wy3zzl" aria-hidden="true" hidden><p>Writing CSS seems to be regarded much like taking notes in a meeting, complete with the implicit sexism and devaluation of the note taker’s importance in the room.</p>
</aside>

<p class="callout svelte-9qgyge"></p><p>Writing CSS seems to be regarded much like taking notes in a meeting, complete with the implicit sexism and devaluation of the note taker’s importance in the room.</p>

<p>Though critical to the project, frontend work will quite often be disregarded by those who consider it beneath them (usually men, and usually only tacitly, never explicitly). It’s not serious enough; not important enough; not <em>real</em> enough. Too squishy. Like soft skills.</p>
<p>Yes, of course, it’s important. It’s work that <em>somebody</em> needs to do, certainly. But probably not the important people, whose valuable attention is focused on other, bigger, more important problems.</p>
<p>Design itself is typically conflated with frontend, and design has long been colored as a “feminine” pursuit. The deep technical challenges of frontend functionality are often mistakenly regarded more as a sort of painting with pixels; making things <em>look</em> right, rather than <em>work</em> correctly.</p>
<p>Incidentally, folks involved in UI and design tend to be hit harder in layoffs than their peers, who are perceived as “more technical.” Women, too, are often laid off in greater numbers than men. So it’s hard <em>not</em> to see frontend devaluation and sexism as intertwined. (If they’re even separate things at all, that is.)</p>
<h2 id="i-see-it-in-the-way-we-talk-about-frontend-developers"><a aria-hidden="true" tabindex="-1" href="#i-see-it-in-the-way-we-talk-about-frontend-developers"><span class="icon icon-link"></span></a>I see it in the way we talk about frontend developers</h2>
<p>Frontend is complicated. Few would contend with that.</p>
<p>But for some reason, where the difficulty of other forms of engineering are generally blamed on the daunting complexity of computers themselves (and where, again, we generally revere the people who dare attempt to wrangle that complexity), there’s a pervasive myth that frontend is only complex because <em>frontend developers made it that way</em>.</p>
<p>As though we added frameworks and compilers and heaps of packages to our projects because we simply didn’t know any better.</p>
<aside class="pull-quote svelte-1wy3zzl" aria-hidden="true" hidden><p>As though the nearly impossible job of supporting every possible device, operating system, screen size, browser, user preference, and interface in the past, present and future is so simple we invented all the complexity ourselves, just because we were bored.</p>
</aside>

<p class="callout svelte-9qgyge"></p><p>As though the nearly impossible job of supporting every possible device, operating system, screen size, browser, user preference, and interface in the past, present and future is so simple we invented all the complexity ourselves, just because we were bored.</p>

<p>There are lots of jokes about frontend developers. There’s nothing wrong with that. Often, we’re the ones making them; we’re not above laughing at ourselves, and besides, there are plenty of jokes about other specialties, too.</p>
<p>We like to kid about our affinity for new things. How we’re always coming up with new frameworks. How we’re always chasing the new hotness.</p>
<p>That’s all well and good…until the joke starts to become the actual perception. Until we begin to be taken <em>less</em> seriously, because our reputation is a punch line.</p>
<aside class="pull-quote svelte-1wy3zzl" aria-hidden="true" hidden><p>Yes, as a group, we get excited about new things. But why doesn’t that make us curious, or adaptable, or inquisitive? Why don’t we get credit for our joy of learning, instead of denigrated for refusing to stay in place?</p>
<p>Why don’t we get the benefit of the doubt?</p>
</aside>

<p class="callout svelte-9qgyge"></p><p>Yes, as a group, we get excited about new things. But why doesn’t that make us curious, or adaptable, or inquisitive? Why don’t we get credit for our joy of learning, instead of denigrated for refusing to stay in place?</p>
<p>Why don’t we get the benefit of the doubt?</p>

<p>Browsers move <em>fast</em>, and they’ve evolved at an unprecedented pace in recent years. So I guess I’m not sure where the assumption that we frontend devs should be so intransigent in our tech choices comes from.</p>
<p>Shouldn’t we be persistently reevaluating, when the landscape is constantly shifting?</p>
<p>Shouldn’t we <em>try</em> to keep up? To be better? To rework old ideas with new capabilities?</p>
<p>Don’t we deserve to be considered just a bit more than flighty little magpies, chasing anything that’s shiny?</p>
<h2 id="i-see-it-in-our-responsibilities"><a aria-hidden="true" tabindex="-1" href="#i-see-it-in-our-responsibilities"><span class="icon icon-link"></span></a>I see it in our responsibilities</h2>
<p>In my experience (yours may vary, of course), frontend engineers are regularly deployed as “fixers” on projects. We often aren’t included at the beginning stages, where we could easily identify costly accessibility, performance, and user experience mistakes; we’re brought in at the end, when things have already been built and it’s too late to change anything significantly. By then, it’s our job just to pretty things up.</p>
<p>“Here; other people already made this.” (i.e., they already did the <em>real</em> work.) “Now we just need you to fix it up.”</p>
<p>But…why is it broken?</p>
<p>Why aren’t we addressing the pipeline that generated a flawed product to begin with?</p>
<aside class="pull-quote svelte-1wy3zzl" aria-hidden="true" hidden><p>If our skills are valuable as duct tape over the cracks of organizational shortcomings, why aren’t they valuable during the planning and decision-making that led to those defects, when we could potentially prevent them?</p>
</aside>

<p class="callout svelte-9qgyge"></p><p>If our skills are valuable as duct tape over the cracks of organizational shortcomings, why aren’t they valuable during the planning and decision-making that led to those defects, when we could potentially prevent them?</p>

<p>Why are we considered mere decorators, brought in at the end to spruce the place up a bit?</p>
<p>(As though a decorator could fix a deeply inaccessible building with a fresh coat of paint and some classy furniture.)</p>
<h2 id="i-see-it-in-whats-expected-of-us"><a aria-hidden="true" tabindex="-1" href="#i-see-it-in-whats-expected-of-us"><span class="icon icon-link"></span></a>I see it in what’s expected of us</h2>
<p>The list of skills that a frontend engineer must know expands constantly and unendingly. The world where you could just learn some HTML, CSS, and JavaScript in a couple of months and be ready for a job is mostly in the past.</p>
<p>HTML itself is enough to fill a very thick book, if you want to do it right—and yet, it’s often seen as the <em>simplest</em> thing a frontend developer ought to know.</p>
<p>Then there’s CSS, and JavaScript itself, with all its myriad quirks. Then you’ll of course need to know a framework (probably at least a couple, including the legacy one the company still uses and will never get around to completely replacing), plus a slew of add-ons, plugins, and packages. Probably a system to manage them all, too.</p>
<p>Then there’s accessibility, which is a topic so complex and nuanced almost <em>nobody</em> gets it fully right. But it’s your job, if you’re on the frontend—and not your whole job, either. Just part of it. Good luck!</p>
<p>You’ve got to know things about SEO, too, and that, also, requires nearly a full-time focus to do extremely well, even though you’ll be expected to manage it, too, in a small slice of your time.</p>
<p>Then there’s design, and of course, that includes all the tenets of user interfaces and user experience—all of which a person could spend years learning all on their own.</p>
<p>More and more, you’ll need to know about legal compliance. And marketing, which is its own whole college major.</p>
<p>You also need to know at least a little TypeScript. And come to think of it, you’ll probably be expected to learn at least <em>one</em> backend language, too. (“So you can unblock yourself” is the reason given, although “so you can do your own job, plus a little bit of someone else’s,” is sometimes more accurate.)</p>
<p>From there, it might just be some animation libraries. Charts and data visualization. Testing and assertion libraries. Command line utilities. Cookies. Caching (and cache invalidation). DNS. Networks. Performance. The edge. Serverless. GraphQL. AWS. Docker. At least a little about databases. How’s your knowledge of email? Can you write us some regular expressions? Oh by the way, the marketing team needs you to dig into their analytics, maybe run some A/B tests.</p>
<p>Earlier versions of this post completely left out state management libraries—and said nothing of security. What do you know about preventing XSS attacks? CORS? Content security policies?</p>
<p>The list goes on. Yet, for this unceasingly distended list of skills, frontend is still that silly thing that isn’t the <em>real</em> dev work.</p>
<h2 id="i-see-it-in-the-marketing"><a aria-hidden="true" tabindex="-1" href="#i-see-it-in-the-marketing"><span class="icon icon-link"></span></a>I see it in the marketing</h2>
<aside class="pull-quote svelte-1wy3zzl" aria-hidden="true" hidden><p>Frontend tools market themselves as though frontend is something no one wants to do, and nobody should care about any more than they have to.</p>
</aside>

<p class="callout svelte-9qgyge"></p><p>Frontend tools market themselves as though frontend is something no one wants to do, and nobody should care about any more than they have to.</p>

<p>Every tool promises to make frontend uncomplicated and fast; that’s the only concern. They say it the same way I reassure my five-year-old that his vaccinations will be swift and painless; just something necessary to get out of the way as quickly and thoughtlessly as possible.</p>
<p>But when did speed become the main concern? And <em>why</em>?</p>
<p>I understand that “rapidly prototyping” is a good thing, but it feels like it’s become the <em>only</em> thing.</p>
<p>I sometimes find myself wondering whether other developers are simply building things and then throwing them away almost immediately. Is nobody actually <em>maintaining</em> work these days? Has frontend just become an assembly line job, where maximizing output while minimizing hours is the only metric for success?</p>
<p>Is nobody thinking about what we might be trading away for this furious pace? What quality and good ideas and accessibility and user experience we might just be flying by in our pursuit of the rapidest, prototypiest interfaces we can possibly speed-run?</p>
<p>It doesn’t seem like it. These tools, the speedy ones: they promise you can just get on with other things (“more important things” is implied, if not overtly spoken), as long as you use them.</p>
<p>Like every frontend concern is something you just install a WordPress plugin for.</p>
<p>The proliferation of AI tools plays into this as well. I’m sure somebody’s working on some AI product to build your backend for you (if there aren’t already such products on the market). But in the meantime, I’ve already seen a handful for frontend. Their code isn’t accessible, or well built, and it took dedicated frontend engineers to point that out. The companies making them didn’t know, or didn’t care (or both). All that mattered was slapping something up on the screen as quickly and easily as possible.</p>
<h2 id="i-see-it-in-how-good-enough-has-become-the-goal"><a aria-hidden="true" tabindex="-1" href="#i-see-it-in-how-good-enough-has-become-the-goal"><span class="icon icon-link"></span></a>I see it in how “good enough” has become the goal</h2>
<p>The web is positively overrun with inaccessible code. Odds are, even if that specific problem doesn’t currently affect you, you still see forms that won’t submit, invisible buttons, errors surfaced in the UI, utterly broken layouts, things that don’t work on mobile devices or in certain browsers, text and fonts that are difficult to impossible to read, interfaces that are impossible to zoom or scroll, interactions that break down, and silent or unhelpful error messages—all quite likely on a daily or near-daily basis.</p>
<p>This, of course, is to say nothing of the homogeny of the web. The same text, the same colors, the same user dropdown menu, the same little circle with the same little number in it to get your attention; the same marketing, the same three-column layout, the same hero with the image and the big button; the same dimly pulsing skeleton UI that shows up when you click something.</p>
<p>Sites with good design—both aesthetic and functional—are a tragic rarity. And I personally see this as a direct tradeoff we’ve made in our pursuit of shipping as fast as possible.</p>
<aside class="pull-quote svelte-1wy3zzl" aria-hidden="true" hidden><p>It seems like nobody thinks of frontend as a critical part of the product anymore; they only think of it as the nice box the product arrives in.</p>
</aside>

<p class="callout svelte-9qgyge"></p><p>It seems like nobody thinks of frontend as a critical part of the product anymore; they only think of it as the nice box the product arrives in.</p>

<p>We need a framework; React will be good enough. No need to look any further.</p>
<p>Just pick a nice color and a serviceable font. No, don’t bother writing any custom CSS; whatever Tailwind comes with will be good enough.</p>
<p>Anything to move on to the next thing. Because whatever the real, important, serious thing is on this project, it isn’t <em>this</em>. Not the frontend.</p>
<p>Not this thing each and every user will look at and interact with every single time they use the product.</p>
<h2 id="i-see-it-everywhere"><a aria-hidden="true" tabindex="-1" href="#i-see-it-everywhere"><span class="icon icon-link"></span></a>I see it everywhere</h2>
<p>But then again…maybe that’s just me.</p>
<p>Maybe I’m feeling sorry for myself. Maybe I’m just a little depressed right now. Maybe I have an inferiority complex and I’m projecting it on everyone else.</p>
<p>Or…maybe there’s some truth to this.</p>
<p>Maybe it’s both.</p>
<p>I guess that’s up to you to decide.</p>
<p>But maybe just keep your eyes open, ok?</p>
<p>You might see something you haven’t noticed before.</p>

+ 420
- 0
cache/2024/6f3cb3c0c6c580407b1cfaa2d7d9005b/index.html View File

@@ -0,0 +1,420 @@
<!doctype html><!-- This is a valid HTML5 document. -->
<!-- Screen readers, SEO, extensions and so on. -->
<html lang="en">
<!-- Has to be within the first 1024 bytes, hence before the `title` element
See: https://www.w3.org/TR/2012/CR-html5-20121217/document-metadata.html#charset -->
<meta charset="utf-8">
<!-- Why no `X-UA-Compatible` meta: https://stackoverflow.com/a/6771584 -->
<!-- The viewport meta is quite crowded and we are responsible for that.
See: https://codepen.io/tigt/post/meta-viewport-for-2015 -->
<meta name="viewport" content="width=device-width,initial-scale=1">
<!-- Required to make a valid HTML5 document. -->
<title>Coroutines and web components (archive) — David Larlet</title>
<meta name="description" content="Publication mise en cache pour en conserver une trace.">
<!-- That good ol' feed, subscribe :). -->
<link rel="alternate" type="application/atom+xml" title="Feed" href="/david/log/">
<!-- Generated from https://realfavicongenerator.net/ such a mess. -->
<link rel="apple-touch-icon" sizes="180x180" href="/static/david/icons2/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/static/david/icons2/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/static/david/icons2/favicon-16x16.png">
<link rel="manifest" href="/static/david/icons2/site.webmanifest">
<link rel="mask-icon" href="/static/david/icons2/safari-pinned-tab.svg" color="#07486c">
<link rel="shortcut icon" href="/static/david/icons2/favicon.ico">
<meta name="msapplication-TileColor" content="#f7f7f7">
<meta name="msapplication-config" content="/static/david/icons2/browserconfig.xml">
<meta name="theme-color" content="#f7f7f7" media="(prefers-color-scheme: light)">
<meta name="theme-color" content="#272727" media="(prefers-color-scheme: dark)">
<!-- Is that even respected? Retrospectively? What a shAItshow…
https://neil-clarke.com/block-the-bots-that-feed-ai-models-by-scraping-your-website/ -->
<meta name="robots" content="noai, noimageai">
<!-- Documented, feel free to shoot an email. -->
<link rel="stylesheet" href="/static/david/css/style_2021-01-20.css">
<!-- See https://www.zachleat.com/web/comprehensive-webfonts/ for the trade-off. -->
<link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
<link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
<link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
<link rel="preload" href="/static/david/css/fonts/triplicate_t3_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
<link rel="preload" href="/static/david/css/fonts/triplicate_t3_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
<link rel="preload" href="/static/david/css/fonts/triplicate_t3_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
<script>
function toggleTheme(themeName) {
document.documentElement.classList.toggle(
'forced-dark',
themeName === 'dark'
)
document.documentElement.classList.toggle(
'forced-light',
themeName === 'light'
)
}
const selectedTheme = localStorage.getItem('theme')
if (selectedTheme !== 'undefined') {
toggleTheme(selectedTheme)
}
</script>

<meta name="robots" content="noindex, nofollow">
<meta content="origin-when-cross-origin" name="referrer">
<!-- Canonical URL for SEO purposes -->
<link rel="canonical" href="https://lorenzofox.dev/posts/component-as-infinite-loop/">

<body class="remarkdown h1-underline h2-underline h3-underline em-underscore hr-center ul-star pre-tick" data-instant-intensity="viewport-all">


<article>
<header>
<h1>Coroutines and web components</h1>
</header>
<nav>
<p class="center">
<a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
</svg> Accueil</a> •
<a href="https://lorenzofox.dev/posts/component-as-infinite-loop/" title="Lien vers le contenu original">Source originale</a>
<br>
Mis en cache le 2024-03-09
</p>
</nav>
<hr>
<p>In the <a href="https://lorenzofox.dev/posts/coroutine">previous article</a> we learned what coroutines are and saw some patterns they can help implement.
In this article, we will see how coroutines can be used to model web components in a different way, and why you might like it.</p>
<h2>Rendering loop</h2>
<p>Among other things, coroutines have a few properties that we will use in this short essay:</p>
<ul>
<li>They are primarily <strong>functions</strong> and can benefit from the whole functional arsenal of Javascript (composition, higher order function, delegation, etc.).</li>
<li>They are <strong>stateful</strong>.</li>
<li>You can inject pretty much any kind of data when they are paused. For example, an infinite loop within the body of the routine can be considered as a public API function.</li>
<li>You cannot, by design, call the <code>next</code> function concurrently.</li>
</ul>
<h2>Introduction example</h2>
<p>Consider the following generator:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">function</span><span class="token operator">*</span> <span class="token function">someComponent</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span>$host<span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> <span class="token punctuation">{</span>content <span class="token operator">=</span> <span class="token string">''</span><span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token keyword">yield</span><span class="token punctuation">;</span>
$host<span class="token punctuation">.</span>textContent <span class="token operator">=</span> content<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>It takes a <code>$host</code> DOM element and has a rendering loop. You can wrap this generator with a function that produces a <code>render</code> function:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> <span class="token function-variable function">createComponent</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">generator</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span>$host<span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> gen <span class="token operator">=</span> <span class="token function">generator</span><span class="token punctuation">(</span><span class="token punctuation">{</span>$host<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
gen<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">return</span> <span class="token punctuation">(</span><span class="token parameter">state <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> gen<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span>state<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> HelloWorldComponent <span class="token operator">=</span> <span class="token function">createComponent</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token operator">*</span> <span class="token punctuation">(</span><span class="token punctuation">{</span>$host<span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> <span class="token punctuation">{</span>name <span class="token operator">=</span> <span class="token string">''</span><span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token keyword">yield</span><span class="token punctuation">;</span>
$host<span class="token punctuation">.</span>textContent <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">hello </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>name<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>


<span class="token keyword">const</span> render <span class="token operator">=</span> <span class="token function">HelloWorldComponent</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
<span class="token literal-property property">$host</span><span class="token operator">:</span> div
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token function">render</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'Laurent'</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">render</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'Bernadette'</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<h2>The power of functions</h2>
<p>For now, the rendering loop is a piece of imperative code, but it can use any rendering library you want (react and so on).
The first point above says that functions (and therefore coroutines) are very versatile in Javascript. We could easily go back to a known paradigm if we wanted to. For example, we use <a href="https://lorenzofox.dev/todo">lit-html</a> to have a declarative view instead of a bunch of imperative code:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">import</span> <span class="token punctuation">{</span>render<span class="token punctuation">,</span> html<span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'lit-element'</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> HelloWorldComponent <span class="token operator">=</span> <span class="token function">createComponent</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token operator">*</span> <span class="token punctuation">(</span><span class="token punctuation">{</span>$host<span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> <span class="token punctuation">{</span>name<span class="token operator">=</span><span class="token string">''</span><span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token keyword">yield</span>
<span class="token keyword">const</span> template <span class="token operator">=</span> html<span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">&lt;p&gt;hello </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>name<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">&lt;/p&gt;</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>
<span class="token function">render</span><span class="token punctuation">(</span>$host<span class="token punctuation">,</span> template<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>you can draw the template into a function:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">import</span> <span class="token punctuation">{</span>html<span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'lit-element'</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> template <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">{</span>name <span class="token operator">=</span> <span class="token string">''</span><span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> html<span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">&lt;p&gt;hello </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>name<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">&lt;/p&gt;</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span></code></pre>
<p>And compose with a new higher order function:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">import</span> <span class="token punctuation">{</span>render<span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'lit-element'</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> <span class="token function-variable function">withView</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">templateFn</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token keyword">function</span><span class="token operator">*</span> <span class="token punctuation">(</span><span class="token punctuation">{</span>$host<span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token function">render</span><span class="token punctuation">(</span>$host<span class="token punctuation">,</span> <span class="token function">templateFn</span><span class="token punctuation">(</span><span class="token keyword">yield</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> HelloWorldComponent <span class="token operator">=</span> <span class="token function">createComponent</span><span class="token punctuation">(</span><span class="token function">withView</span><span class="token punctuation">(</span>template<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>All right, we are on familiar ground: our component is now a simple function of the state</p>
<pre class="language-js"><code class="language-js"><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span>name<span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> html<span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">&lt;p&gt;hello </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>name<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">&lt;/p&gt;</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span> </code></pre>
<h2>Maintaining a state</h2>
<p>Having an infinite rendering loop to model our component can actually be more interesting than it seems at first: you can have a state in the closure of that loop.</p>
<p>If we first modify the higher-level <code>createComponent</code> function a little to bind the <code>render</code> function to the host element:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> <span class="token function-variable function">createComponent</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">generator</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span>$host<span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> gen <span class="token operator">=</span> <span class="token function">generator</span><span class="token punctuation">(</span><span class="token punctuation">{</span>$host<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
gen<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
$host<span class="token punctuation">.</span><span class="token function-variable function">render</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">state <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> gen<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span>state<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">return</span> $host<span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<p>We can now make the component trigger its own rendering:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> CountClick <span class="token operator">=</span> <span class="token function">createComponent</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token operator">*</span><span class="token punctuation">(</span><span class="token punctuation">{</span>$host<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
<span class="token keyword">let</span> clickCount <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>

$host<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'click'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
clickCount<span class="token operator">+=</span><span class="token number">1</span><span class="token punctuation">;</span>
$host<span class="token punctuation">.</span><span class="token function">render</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">while</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
$host<span class="token punctuation">.</span>textContent <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">I have been clicked </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>clickCount<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> time(s)</span><span class="token template-punctuation string">`</span></span>
<span class="token keyword">yield</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>In frameworks like React, where you only have access to the equivalent of what is inside the loop, you rely on the framework extension points (the hooks in the case of React) to build this sort of mechanism, and have very little control over rendering scheduling.</p>
<h2>More HOF function to reduce the coupling.</h2>
<p>The component embeds its view and some logic at the same time. Again, we can easily decouple them so that we can reuse either the view or the logic:
All we need to do is take advantage of the third property of coroutines mentioned in the introduction, and a simple delegation mechanism inherent to generators: <code>yield*</code>.</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> <span class="token function-variable function">countClickable</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">view</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token keyword">function</span> <span class="token operator">*</span><span class="token punctuation">(</span><span class="token punctuation">{</span>$host<span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">let</span> clickCount <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>

$host<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'click'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
clickCount<span class="token operator">+=</span><span class="token number">1</span><span class="token punctuation">;</span>
$host<span class="token punctuation">.</span><span class="token function">render</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token literal-property property">count</span><span class="token operator">:</span> clickCount<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">yield</span><span class="token operator">*</span> <span class="token function">view</span><span class="token punctuation">(</span><span class="token punctuation">{</span>$host<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>This type of mixin is responsible for holding the state and triggering the rendering of any <em>view</em>. Rendering is left to the view thanks to <strong>delegation</strong>, while the state is passed whenever the view coroutine is paused and requires a new render:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> CountClick <span class="token operator">=</span> <span class="token function">createComponent</span><span class="token punctuation">(</span><span class="token function">countClickable</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token operator">*</span> <span class="token punctuation">(</span><span class="token punctuation">{</span>$host<span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> <span class="token punctuation">{</span>count <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token keyword">yield</span><span class="token punctuation">;</span>
$host<span class="token punctuation">.</span>textContent <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">I have been clicked </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>count<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> time(s)</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Neat ! You can now use the "clickable" behaviour independently, on different views. In the same way, you can plug the view into a different controller logic, as long as it passes the expected data interface (<code>{ count: number | string}</code>): note how the data comes from the <code>yield</code> assignation.</p>
<p>We will see more patterns like this in future articles.</p>
<h2>Web components and lifecycle mapping</h2>
<p>So far we have designed our component to be a function of the host. We can go further and ensure that the rendering routine is actually private to the host, so that the rendering code is encapsulated inside along with any potential behaviour enhancements (the <code>countClickable</code> mixin for example), while both remain reusable.</p>
<p>Let's look at another way of modelling <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements">custom elements</a>. To enhance your HTML document, you can teach the browser new ones using its registry and the <code>define</code> method.</p>
<pre class="language-js"><code class="language-js">customElements<span class="token punctuation">.</span><span class="token function">define</span><span class="token punctuation">(</span><span class="token string">'hello-world'</span><span class="token punctuation">,</span> <span class="token keyword">class</span> <span class="token class-name">extends</span> HTMLElement <span class="token punctuation">{</span>
<span class="token function">connectedCallback</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">this</span><span class="token punctuation">.</span>textContent <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">hello </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">'name'</span><span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span> </code></pre>
<p>And then use the <code>hello-world</code> tag in the markup like any other regular HTML tag.</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>hello-world</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Laurent<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>hello-world</span><span class="token punctuation">&gt;</span></span></code></pre>
<p>Instead of using a class that extends the <code>HTMLElement</code> class (or any other valid built-in element class), we want the second argument to be a generator function. This means our custom <code>define</code> would need to turn the generator into a class.</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> <span class="token function-variable function">define</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">tag<span class="token punctuation">,</span> gen</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
customElements<span class="token punctuation">.</span><span class="token function">define</span><span class="token punctuation">(</span>tag<span class="token punctuation">,</span> <span class="token keyword">class</span> <span class="token class-name">extends</span> HTMLElement <span class="token punctuation">{</span>
#loop<span class="token punctuation">;</span>

<span class="token function">constructor</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">super</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">this</span><span class="token punctuation">.</span>#loop <span class="token operator">=</span> <span class="token function">gen</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
<span class="token literal-property property">$host</span><span class="token operator">:</span> <span class="token keyword">this</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">this</span><span class="token punctuation">.</span>render <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">render</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">this</span><span class="token punctuation">.</span>#loop<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token function">connectedCallback</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">render</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token function">render</span><span class="token punctuation">(</span><span class="token parameter">state <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">this</span><span class="token punctuation">.</span>#loop<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span>state<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token function">define</span><span class="token punctuation">(</span><span class="token string">'hello-world'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token operator">*</span> <span class="token punctuation">(</span><span class="token punctuation">{</span>$host<span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">while</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">yield</span><span class="token punctuation">;</span>
$host<span class="token punctuation">.</span>textContent <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">hello </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>$host<span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">'name'</span><span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Using a class expression, we create the custom element class on the fly. The <code>#loop</code> rendering routine is instantiated inside the constructor and advanced to its first <code>yield</code> point. Note that we pass the host as a parameter to the routine, although the routine is specifically bound to the host so that we could just use <code>this</code> inside the generator to refer to the host. This is a personal preference as I find the use of <code>this</code> in Javascript very error-prone.</p>
<p>When the <code>connectedCallback</code> is called (this happens when the component is mounted into the DOM). We call <code>next</code> again, which in our previous example corresponds to the first iteration of the loop. Then, whenever the component needs to be rendered (when <code>render</code> is called) again, we continue the loop.</p>
<p>This is very interesting because we are able to match the different component lifecycles to a location within the generator function:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">function</span><span class="token operator">*</span> <span class="token function">comp</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span>$host<span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>

console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'I am being instantiated'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">yield</span><span class="token punctuation">;</span>

console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'Iam being mounted'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
$host<span class="token punctuation">.</span>textContent <span class="token operator">=</span> <span class="token string">'I have just been mounted'</span><span class="token punctuation">;</span>

<span class="token keyword">yield</span><span class="token punctuation">;</span>

<span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'I am being rendered'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
$host<span class="token punctuation">.</span>textContent <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">hello </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>$host<span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">'name'</span><span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>
<span class="token keyword">yield</span> <span class="token string">'I have been rendered'</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>Yet, one important lifecycle remains to be implemented. When the component is unmounted, the <code>disconnectedCallbak</code> of the class definition is normally called, allowing us to run cleanup code and avoid memory leaks for example.</p>
<p>In the generator we can force the exit of the loop into a <code>finally</code> clause. This is as simple as calling the loop's <code>return</code> function instead of the usual <code>next</code>.</p>
<p>Altogether:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> <span class="token function-variable function">define</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">tag<span class="token punctuation">,</span> gen</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
customElements<span class="token punctuation">.</span><span class="token function">define</span><span class="token punctuation">(</span>tag<span class="token punctuation">,</span> <span class="token keyword">class</span> <span class="token class-name">extends</span> HTMLElement <span class="token punctuation">{</span>
#loop<span class="token punctuation">;</span>

<span class="token function">constructor</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">super</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">this</span><span class="token punctuation">.</span>#loop <span class="token operator">=</span> <span class="token function">gen</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
<span class="token literal-property property">$host</span><span class="token operator">:</span> <span class="token keyword">this</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">this</span><span class="token punctuation">.</span>render <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">render</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">this</span><span class="token punctuation">.</span>#loop<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token function">connectedCallback</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">render</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token function">disconnectedCallback</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">this</span><span class="token punctuation">.</span>#loop<span class="token punctuation">.</span><span class="token function">return</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token function">render</span><span class="token punctuation">(</span><span class="token parameter">state <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">this</span><span class="token punctuation">.</span>#loop<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span>state<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">function</span><span class="token operator">*</span> <span class="token function">comp</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span>$host<span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>

console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'I am being instantiated'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">yield</span><span class="token punctuation">;</span>

console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'Iam being mounted'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
$host<span class="token punctuation">.</span>textContent <span class="token operator">=</span> <span class="token string">'I have just been mounted'</span>

<span class="token keyword">yield</span><span class="token punctuation">;</span>

<span class="token keyword">try</span> <span class="token punctuation">{</span>
<span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'I am being rendered'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
$host<span class="token punctuation">.</span>textContent <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">hello </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>$host<span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">'name'</span><span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>
<span class="token keyword">yield</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span> <span class="token keyword">finally</span> <span class="token punctuation">{</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'cleanup here !!!'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>This "linear" representation of the component and its lifetime makes things easier to reason about: there are no surprises when a callback or a hook is called, everything is read from top to bottom!</p>
<h2>Concurrent updates</h2>
<p>Before we conclude, we can illustrate the fourth point mentioned in the introduction: if you try to advance a generator function while it is already advancing, you will get an error. In the component world, this means that concurrent rendering is impossible by design!</p>
<p>This code:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">function</span><span class="token operator">*</span> <span class="token function">comp</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span>$host<span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'I am being rendered'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

$host<span class="token punctuation">.</span><span class="token function">render</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

$host<span class="token punctuation">.</span>textContent <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">hello </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>$host<span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">'name'</span><span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>
<span class="token keyword">yield</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>will trigger an error <code>Uncaught TypeError: already executing generator</code>.</p>
<h2>conclusion</h2>
<p>We have seen throughout this article that the functional nature of a generator combined with its intrinsic properties can be useful to build a very flexible and simple abstraction of UI component, with the ability to split behaviour and view into reusable bits, to maintain internal state or to have at reach all component lifecycles in the same place.</p>
<p>In the next article, we will see how we can further improve and optimise our generator-to-class conversion.</p>
</article>


<hr>

<footer>
<p>
<a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
</svg> Accueil</a> •
<a href="/david/log/" title="Accès au flux RSS"><svg class="icon icon-rss2">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-rss2"></use>
</svg> Suivre</a> •
<a href="http://larlet.com" title="Go to my English profile" data-instant><svg class="icon icon-user-tie">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-user-tie"></use>
</svg> Pro</a> •
<a href="mailto:david%40larlet.fr" title="Envoyer un courriel"><svg class="icon icon-mail">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-mail"></use>
</svg> Email</a> •
<abbr class="nowrap" title="Hébergeur : Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33184162340"><svg class="icon icon-hammer2">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-hammer2"></use>
</svg> Légal</abbr>
</p>
<template id="theme-selector">
<form>
<fieldset>
<legend><svg class="icon icon-brightness-contrast">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-brightness-contrast"></use>
</svg> Thème</legend>
<label>
<input type="radio" value="auto" name="chosen-color-scheme" checked> Auto
</label>
<label>
<input type="radio" value="dark" name="chosen-color-scheme"> Foncé
</label>
<label>
<input type="radio" value="light" name="chosen-color-scheme"> Clair
</label>
</fieldset>
</form>
</template>
</footer>
<script src="/static/david/js/instantpage-5.1.0.min.js" type="module"></script>
<script>
function loadThemeForm(templateName) {
const themeSelectorTemplate = document.querySelector(templateName)
const form = themeSelectorTemplate.content.firstElementChild
themeSelectorTemplate.replaceWith(form)

form.addEventListener('change', (e) => {
const chosenColorScheme = e.target.value
localStorage.setItem('theme', chosenColorScheme)
toggleTheme(chosenColorScheme)
})

const selectedTheme = localStorage.getItem('theme')
if (selectedTheme && selectedTheme !== 'undefined') {
form.querySelector(`[value="${selectedTheme}"]`).checked = true
}
}

const prefersColorSchemeDark = '(prefers-color-scheme: dark)'
window.addEventListener('load', () => {
let hasDarkRules = false
for (const styleSheet of Array.from(document.styleSheets)) {
let mediaRules = []
for (const cssRule of styleSheet.cssRules) {
if (cssRule.type !== CSSRule.MEDIA_RULE) {
continue
}
// WARNING: Safari does not have/supports `conditionText`.
if (cssRule.conditionText) {
if (cssRule.conditionText !== prefersColorSchemeDark) {
continue
}
} else {
if (cssRule.cssText.startsWith(prefersColorSchemeDark)) {
continue
}
}
mediaRules = mediaRules.concat(Array.from(cssRule.cssRules))
}

// WARNING: do not try to insert a Rule to a styleSheet you are
// currently iterating on, otherwise the browser will be stuck
// in a infinite loop…
for (const mediaRule of mediaRules) {
styleSheet.insertRule(mediaRule.cssText)
hasDarkRules = true
}
}
if (hasDarkRules) {
loadThemeForm('#theme-selector')
}
})
</script>
</body>
</html>

+ 253
- 0
cache/2024/6f3cb3c0c6c580407b1cfaa2d7d9005b/index.md View File

@@ -0,0 +1,253 @@
title: Coroutines and web components
url: https://lorenzofox.dev/posts/component-as-infinite-loop/
hash_url: 6f3cb3c0c6c580407b1cfaa2d7d9005b
archive_date: 2024-03-09
og_image: https://lorenzofox.dev/posts/component-as-infinite-loop/public/logo.webp
description: Custom elements (web components) modelling with coroutines
favicon: https://lorenzofox.dev/posts/component-as-infinite-loop/public/favicon.ico
language: en_US

<p>In the <a href="https://lorenzofox.dev/posts/coroutine">previous article</a> we learned what coroutines are and saw some patterns they can help implement.
In this article, we will see how coroutines can be used to model web components in a different way, and why you might like it.</p>
<h2>Rendering loop</h2>
<p>Among other things, coroutines have a few properties that we will use in this short essay:</p>
<ul>
<li>They are primarily <strong>functions</strong> and can benefit from the whole functional arsenal of Javascript (composition, higher order function, delegation, etc.).</li>
<li>They are <strong>stateful</strong>.</li>
<li>You can inject pretty much any kind of data when they are paused. For example, an infinite loop within the body of the routine can be considered as a public API function.</li>
<li>You cannot, by design, call the <code>next</code> function concurrently.</li>
</ul>
<h2>Introduction example</h2>
<p>Consider the following generator:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">function</span><span class="token operator">*</span> <span class="token function">someComponent</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span>$host<span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> <span class="token punctuation">{</span>content <span class="token operator">=</span> <span class="token string">''</span><span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token keyword">yield</span><span class="token punctuation">;</span>
$host<span class="token punctuation">.</span>textContent <span class="token operator">=</span> content<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>It takes a <code>$host</code> DOM element and has a rendering loop. You can wrap this generator with a function that produces a <code>render</code> function:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> <span class="token function-variable function">createComponent</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">generator</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span>$host<span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> gen <span class="token operator">=</span> <span class="token function">generator</span><span class="token punctuation">(</span><span class="token punctuation">{</span>$host<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
gen<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">return</span> <span class="token punctuation">(</span><span class="token parameter">state <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> gen<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span>state<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> HelloWorldComponent <span class="token operator">=</span> <span class="token function">createComponent</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token operator">*</span> <span class="token punctuation">(</span><span class="token punctuation">{</span>$host<span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> <span class="token punctuation">{</span>name <span class="token operator">=</span> <span class="token string">''</span><span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token keyword">yield</span><span class="token punctuation">;</span>
$host<span class="token punctuation">.</span>textContent <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">hello </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>name<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>


<span class="token keyword">const</span> render <span class="token operator">=</span> <span class="token function">HelloWorldComponent</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
<span class="token literal-property property">$host</span><span class="token operator">:</span> div
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token function">render</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'Laurent'</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">render</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'Bernadette'</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<h2>The power of functions</h2>
<p>For now, the rendering loop is a piece of imperative code, but it can use any rendering library you want (react and so on).
The first point above says that functions (and therefore coroutines) are very versatile in Javascript. We could easily go back to a known paradigm if we wanted to. For example, we use <a href="https://lorenzofox.dev/todo">lit-html</a> to have a declarative view instead of a bunch of imperative code:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">import</span> <span class="token punctuation">{</span>render<span class="token punctuation">,</span> html<span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'lit-element'</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> HelloWorldComponent <span class="token operator">=</span> <span class="token function">createComponent</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token operator">*</span> <span class="token punctuation">(</span><span class="token punctuation">{</span>$host<span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> <span class="token punctuation">{</span>name<span class="token operator">=</span><span class="token string">''</span><span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token keyword">yield</span>
<span class="token keyword">const</span> template <span class="token operator">=</span> html<span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">&lt;p&gt;hello </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>name<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">&lt;/p&gt;</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>
<span class="token function">render</span><span class="token punctuation">(</span>$host<span class="token punctuation">,</span> template<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>you can draw the template into a function:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">import</span> <span class="token punctuation">{</span>html<span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'lit-element'</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> template <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">{</span>name <span class="token operator">=</span> <span class="token string">''</span><span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> html<span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">&lt;p&gt;hello </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>name<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">&lt;/p&gt;</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span></code></pre>
<p>And compose with a new higher order function:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">import</span> <span class="token punctuation">{</span>render<span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'lit-element'</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> <span class="token function-variable function">withView</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">templateFn</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token keyword">function</span><span class="token operator">*</span> <span class="token punctuation">(</span><span class="token punctuation">{</span>$host<span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token function">render</span><span class="token punctuation">(</span>$host<span class="token punctuation">,</span> <span class="token function">templateFn</span><span class="token punctuation">(</span><span class="token keyword">yield</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> HelloWorldComponent <span class="token operator">=</span> <span class="token function">createComponent</span><span class="token punctuation">(</span><span class="token function">withView</span><span class="token punctuation">(</span>template<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>All right, we are on familiar ground: our component is now a simple function of the state</p>
<pre class="language-js"><code class="language-js"><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span>name<span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> html<span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">&lt;p&gt;hello </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>name<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">&lt;/p&gt;</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span> </code></pre>
<h2>Maintaining a state</h2>
<p>Having an infinite rendering loop to model our component can actually be more interesting than it seems at first: you can have a state in the closure of that loop.</p>
<p>If we first modify the higher-level <code>createComponent</code> function a little to bind the <code>render</code> function to the host element:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> <span class="token function-variable function">createComponent</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">generator</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span>$host<span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> gen <span class="token operator">=</span> <span class="token function">generator</span><span class="token punctuation">(</span><span class="token punctuation">{</span>$host<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
gen<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
$host<span class="token punctuation">.</span><span class="token function-variable function">render</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">state <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> gen<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span>state<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">return</span> $host<span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<p>We can now make the component trigger its own rendering:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> CountClick <span class="token operator">=</span> <span class="token function">createComponent</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token operator">*</span><span class="token punctuation">(</span><span class="token punctuation">{</span>$host<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
<span class="token keyword">let</span> clickCount <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>

$host<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'click'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
clickCount<span class="token operator">+=</span><span class="token number">1</span><span class="token punctuation">;</span>
$host<span class="token punctuation">.</span><span class="token function">render</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">while</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
$host<span class="token punctuation">.</span>textContent <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">I have been clicked </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>clickCount<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> time(s)</span><span class="token template-punctuation string">`</span></span>
<span class="token keyword">yield</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>In frameworks like React, where you only have access to the equivalent of what is inside the loop, you rely on the framework extension points (the hooks in the case of React) to build this sort of mechanism, and have very little control over rendering scheduling.</p>
<h2>More HOF function to reduce the coupling.</h2>
<p>The component embeds its view and some logic at the same time. Again, we can easily decouple them so that we can reuse either the view or the logic:
All we need to do is take advantage of the third property of coroutines mentioned in the introduction, and a simple delegation mechanism inherent to generators: <code>yield*</code>.</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> <span class="token function-variable function">countClickable</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">view</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token keyword">function</span> <span class="token operator">*</span><span class="token punctuation">(</span><span class="token punctuation">{</span>$host<span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">let</span> clickCount <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>

$host<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'click'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
clickCount<span class="token operator">+=</span><span class="token number">1</span><span class="token punctuation">;</span>
$host<span class="token punctuation">.</span><span class="token function">render</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token literal-property property">count</span><span class="token operator">:</span> clickCount<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">yield</span><span class="token operator">*</span> <span class="token function">view</span><span class="token punctuation">(</span><span class="token punctuation">{</span>$host<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>This type of mixin is responsible for holding the state and triggering the rendering of any <em>view</em>. Rendering is left to the view thanks to <strong>delegation</strong>, while the state is passed whenever the view coroutine is paused and requires a new render:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> CountClick <span class="token operator">=</span> <span class="token function">createComponent</span><span class="token punctuation">(</span><span class="token function">countClickable</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token operator">*</span> <span class="token punctuation">(</span><span class="token punctuation">{</span>$host<span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> <span class="token punctuation">{</span>count <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token keyword">yield</span><span class="token punctuation">;</span>
$host<span class="token punctuation">.</span>textContent <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">I have been clicked </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>count<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> time(s)</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Neat ! You can now use the "clickable" behaviour independently, on different views. In the same way, you can plug the view into a different controller logic, as long as it passes the expected data interface (<code>{ count: number | string}</code>): note how the data comes from the <code>yield</code> assignation.</p>
<p>We will see more patterns like this in future articles.</p>
<h2>Web components and lifecycle mapping</h2>
<p>So far we have designed our component to be a function of the host. We can go further and ensure that the rendering routine is actually private to the host, so that the rendering code is encapsulated inside along with any potential behaviour enhancements (the <code>countClickable</code> mixin for example), while both remain reusable.</p>
<p>Let's look at another way of modelling <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements">custom elements</a>. To enhance your HTML document, you can teach the browser new ones using its registry and the <code>define</code> method.</p>
<pre class="language-js"><code class="language-js">customElements<span class="token punctuation">.</span><span class="token function">define</span><span class="token punctuation">(</span><span class="token string">'hello-world'</span><span class="token punctuation">,</span> <span class="token keyword">class</span> <span class="token class-name">extends</span> HTMLElement <span class="token punctuation">{</span>
<span class="token function">connectedCallback</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">this</span><span class="token punctuation">.</span>textContent <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">hello </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">'name'</span><span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span> </code></pre>
<p>And then use the <code>hello-world</code> tag in the markup like any other regular HTML tag.</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>hello-world</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Laurent<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>hello-world</span><span class="token punctuation">&gt;</span></span></code></pre>
<p>Instead of using a class that extends the <code>HTMLElement</code> class (or any other valid built-in element class), we want the second argument to be a generator function. This means our custom <code>define</code> would need to turn the generator into a class.</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> <span class="token function-variable function">define</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">tag<span class="token punctuation">,</span> gen</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
customElements<span class="token punctuation">.</span><span class="token function">define</span><span class="token punctuation">(</span>tag<span class="token punctuation">,</span> <span class="token keyword">class</span> <span class="token class-name">extends</span> HTMLElement <span class="token punctuation">{</span>
#loop<span class="token punctuation">;</span>

<span class="token function">constructor</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">super</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">this</span><span class="token punctuation">.</span>#loop <span class="token operator">=</span> <span class="token function">gen</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
<span class="token literal-property property">$host</span><span class="token operator">:</span> <span class="token keyword">this</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">this</span><span class="token punctuation">.</span>render <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">render</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">this</span><span class="token punctuation">.</span>#loop<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token function">connectedCallback</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">render</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token function">render</span><span class="token punctuation">(</span><span class="token parameter">state <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">this</span><span class="token punctuation">.</span>#loop<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span>state<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token function">define</span><span class="token punctuation">(</span><span class="token string">'hello-world'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token operator">*</span> <span class="token punctuation">(</span><span class="token punctuation">{</span>$host<span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">while</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">yield</span><span class="token punctuation">;</span>
$host<span class="token punctuation">.</span>textContent <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">hello </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>$host<span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">'name'</span><span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Using a class expression, we create the custom element class on the fly. The <code>#loop</code> rendering routine is instantiated inside the constructor and advanced to its first <code>yield</code> point. Note that we pass the host as a parameter to the routine, although the routine is specifically bound to the host so that we could just use <code>this</code> inside the generator to refer to the host. This is a personal preference as I find the use of <code>this</code> in Javascript very error-prone.</p>
<p>When the <code>connectedCallback</code> is called (this happens when the component is mounted into the DOM). We call <code>next</code> again, which in our previous example corresponds to the first iteration of the loop. Then, whenever the component needs to be rendered (when <code>render</code> is called) again, we continue the loop.</p>
<p>This is very interesting because we are able to match the different component lifecycles to a location within the generator function:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">function</span><span class="token operator">*</span> <span class="token function">comp</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span>$host<span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>

console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'I am being instantiated'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">yield</span><span class="token punctuation">;</span>

console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'Iam being mounted'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
$host<span class="token punctuation">.</span>textContent <span class="token operator">=</span> <span class="token string">'I have just been mounted'</span><span class="token punctuation">;</span>

<span class="token keyword">yield</span><span class="token punctuation">;</span>

<span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'I am being rendered'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
$host<span class="token punctuation">.</span>textContent <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">hello </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>$host<span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">'name'</span><span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>
<span class="token keyword">yield</span> <span class="token string">'I have been rendered'</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>Yet, one important lifecycle remains to be implemented. When the component is unmounted, the <code>disconnectedCallbak</code> of the class definition is normally called, allowing us to run cleanup code and avoid memory leaks for example.</p>
<p>In the generator we can force the exit of the loop into a <code>finally</code> clause. This is as simple as calling the loop's <code>return</code> function instead of the usual <code>next</code>.</p>
<p>Altogether:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> <span class="token function-variable function">define</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">tag<span class="token punctuation">,</span> gen</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
customElements<span class="token punctuation">.</span><span class="token function">define</span><span class="token punctuation">(</span>tag<span class="token punctuation">,</span> <span class="token keyword">class</span> <span class="token class-name">extends</span> HTMLElement <span class="token punctuation">{</span>
#loop<span class="token punctuation">;</span>

<span class="token function">constructor</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">super</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">this</span><span class="token punctuation">.</span>#loop <span class="token operator">=</span> <span class="token function">gen</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
<span class="token literal-property property">$host</span><span class="token operator">:</span> <span class="token keyword">this</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">this</span><span class="token punctuation">.</span>render <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">render</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">this</span><span class="token punctuation">.</span>#loop<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token function">connectedCallback</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">render</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token function">disconnectedCallback</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">this</span><span class="token punctuation">.</span>#loop<span class="token punctuation">.</span><span class="token function">return</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token function">render</span><span class="token punctuation">(</span><span class="token parameter">state <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">this</span><span class="token punctuation">.</span>#loop<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span>state<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">function</span><span class="token operator">*</span> <span class="token function">comp</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span>$host<span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>

console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'I am being instantiated'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">yield</span><span class="token punctuation">;</span>

console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'Iam being mounted'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
$host<span class="token punctuation">.</span>textContent <span class="token operator">=</span> <span class="token string">'I have just been mounted'</span>

<span class="token keyword">yield</span><span class="token punctuation">;</span>

<span class="token keyword">try</span> <span class="token punctuation">{</span>
<span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'I am being rendered'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
$host<span class="token punctuation">.</span>textContent <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">hello </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>$host<span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">'name'</span><span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>
<span class="token keyword">yield</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span> <span class="token keyword">finally</span> <span class="token punctuation">{</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'cleanup here !!!'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>This "linear" representation of the component and its lifetime makes things easier to reason about: there are no surprises when a callback or a hook is called, everything is read from top to bottom!</p>
<h2>Concurrent updates</h2>
<p>Before we conclude, we can illustrate the fourth point mentioned in the introduction: if you try to advance a generator function while it is already advancing, you will get an error. In the component world, this means that concurrent rendering is impossible by design!</p>
<p>This code:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">function</span><span class="token operator">*</span> <span class="token function">comp</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span>$host<span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'I am being rendered'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

$host<span class="token punctuation">.</span><span class="token function">render</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

$host<span class="token punctuation">.</span>textContent <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">hello </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>$host<span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">'name'</span><span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>
<span class="token keyword">yield</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>will trigger an error <code>Uncaught TypeError: already executing generator</code>.</p>
<h2>conclusion</h2>
<p>We have seen throughout this article that the functional nature of a generator combined with its intrinsic properties can be useful to build a very flexible and simple abstraction of UI component, with the ability to split behaviour and view into reusable bits, to maintain internal state or to have at reach all component lifecycles in the same place.</p>
<p>In the next article, we will see how we can further improve and optimise our generator-to-class conversion.</p>

+ 4
- 0
cache/2024/index.html View File

@@ -118,6 +118,8 @@
<li><a href="/david/cache/2024/ff566a58892db07815a327802fea66d3/" title="Accès à l’article dans le cache local : In Loving Memory of Square Checkbox">In Loving Memory of Square Checkbox</a> (<a href="https://tonsky.me/blog/checkbox/" title="Accès à l’article original distant : In Loving Memory of Square Checkbox">original</a>)</li>
<li><a href="/david/cache/2024/6f3cb3c0c6c580407b1cfaa2d7d9005b/" title="Accès à l’article dans le cache local : Coroutines and web components">Coroutines and web components</a> (<a href="https://lorenzofox.dev/posts/component-as-infinite-loop/" title="Accès à l’article original distant : Coroutines and web components">original</a>)</li>
<li><a href="/david/cache/2024/d74f376e53fc6a1108c59b6e6fa57ba1/" title="Accès à l’article dans le cache local : 25th work anniversary">25th work anniversary</a> (<a href="https://blog.koalie.net/2024/02/29/25th-work-anniversary/" title="Accès à l’article original distant : 25th work anniversary">original</a>)</li>
<li><a href="/david/cache/2024/7d2d2bce96cbc020423299e7de03d2cf/" title="Accès à l’article dans le cache local : Disillusioned with Deno">Disillusioned with Deno</a> (<a href="https://www.baldurbjarnason.com/2024/disillusioned-with-deno/" title="Accès à l’article original distant : Disillusioned with Deno">original</a>)</li>
@@ -246,6 +248,8 @@
<li><a href="/david/cache/2024/a005801f0e596f9ecb99037a992ecc1b/" title="Accès à l’article dans le cache local : What’s the fun in writing on the internet anymore?">What’s the fun in writing on the internet anymore?</a> (<a href="https://jamesshelley.com/blog/writing-on-the-internet.html" title="Accès à l’article original distant : What’s the fun in writing on the internet anymore?">original</a>)</li>
<li><a href="/david/cache/2024/64ec164fc6c5c23ede3fba6006a47f36/" title="Accès à l’article dans le cache local : The quiet, pervasive devaluation of frontend">The quiet, pervasive devaluation of frontend</a> (<a href="https://joshcollinsworth.com/blog/devaluing-frontend" title="Accès à l’article original distant : The quiet, pervasive devaluation of frontend">original</a>)</li>
<li><a href="/david/cache/2024/2cbc47f0ebded9d54fe6163fa4ea0667/" title="Accès à l’article dans le cache local : Where I’m at on the whole CSS-Tricks thing">Where I’m at on the whole CSS-Tricks thing</a> (<a href="https://chriscoyier.net/2024/02/28/where-im-at-on-the-whole-css-tricks-thing/" title="Accès à l’article original distant : Where I’m at on the whole CSS-Tricks thing">original</a>)</li>
<li><a href="/david/cache/2024/e8748af541273328d9aa9f1aeb1087b2/" title="Accès à l’article dans le cache local : Redeployment Part Three">Redeployment Part Three</a> (<a href="https://brr.fyi/posts/redeployment-part-three" title="Accès à l’article original distant : Redeployment Part Three">original</a>)</li>

Loading…
Cancel
Save