A place to cache linked articles (think custom and personal wayback machine)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

преди 4 години
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542
  1. <!doctype html><!-- This is a valid HTML5 document. -->
  2. <!-- Screen readers, SEO, extensions and so on. -->
  3. <html lang=fr>
  4. <!-- Has to be within the first 1024 bytes, hence before the <title>
  5. See: https://www.w3.org/TR/2012/CR-html5-20121217/document-metadata.html#charset -->
  6. <meta charset=utf-8>
  7. <!-- Why no `X-UA-Compatible` meta: https://stackoverflow.com/a/6771584 -->
  8. <!-- The viewport meta is quite crowded and we are responsible for that.
  9. See: https://codepen.io/tigt/post/meta-viewport-for-2015 -->
  10. <meta name=viewport content="width=device-width,minimum-scale=1,initial-scale=1,shrink-to-fit=no">
  11. <!-- Required to make a valid HTML5 document. -->
  12. <title>Server-First Apps are a Good Idea (archive) — David Larlet</title>
  13. <!-- Generated from https://realfavicongenerator.net/ such a mess. -->
  14. <link rel="apple-touch-icon" sizes="180x180" href="/static/david/icons/apple-touch-icon.png">
  15. <link rel="icon" type="image/png" sizes="32x32" href="/static/david/icons/favicon-32x32.png">
  16. <link rel="icon" type="image/png" sizes="16x16" href="/static/david/icons/favicon-16x16.png">
  17. <link rel="manifest" href="/manifest.json">
  18. <link rel="mask-icon" href="/static/david/icons/safari-pinned-tab.svg" color="#5bbad5">
  19. <link rel="shortcut icon" href="/static/david/icons/favicon.ico">
  20. <meta name="apple-mobile-web-app-title" content="David Larlet">
  21. <meta name="application-name" content="David Larlet">
  22. <meta name="msapplication-TileColor" content="#da532c">
  23. <meta name="msapplication-config" content="/static/david/icons/browserconfig.xml">
  24. <meta name="theme-color" content="#f0f0ea">
  25. <!-- That good ol' feed, subscribe :p. -->
  26. <link rel=alternate type="application/atom+xml" title=Feed href="/david/log/">
  27. <meta name="robots" content="noindex, nofollow">
  28. <meta content="origin-when-cross-origin" name="referrer">
  29. <!-- Canonical URL for SEO purposes -->
  30. <link rel="canonical" href="http://ponyfoo.com/articles/server-first-apps">
  31. <style>
  32. /* http://meyerweb.com/eric/tools/css/reset/ */
  33. html, body, div, span,
  34. h1, h2, h3, h4, h5, h6, p, blockquote, pre,
  35. a, abbr, address, big, cite, code,
  36. del, dfn, em, img, ins,
  37. small, strike, strong, tt, var,
  38. dl, dt, dd, ol, ul, li,
  39. fieldset, form, label, legend,
  40. table, caption, tbody, tfoot, thead, tr, th, td,
  41. article, aside, canvas, details, embed,
  42. figure, figcaption, footer, header, hgroup,
  43. menu, nav, output, ruby, section, summary,
  44. time, mark, audio, video {
  45. margin: 0;
  46. padding: 0;
  47. border: 0;
  48. font-size: 100%;
  49. font: inherit;
  50. vertical-align: baseline;
  51. }
  52. /* HTML5 display-role reset for older browsers */
  53. article, aside, details, figcaption, figure,
  54. footer, header, hgroup, menu, nav, section { display: block; }
  55. body { line-height: 1; }
  56. blockquote, q { quotes: none; }
  57. blockquote:before, blockquote:after,
  58. q:before, q:after {
  59. content: '';
  60. content: none;
  61. }
  62. table {
  63. border-collapse: collapse;
  64. border-spacing: 0;
  65. }
  66. /* http://practicaltypography.com/equity.html */
  67. /* https://calendar.perfplanet.com/2016/no-font-face-bulletproof-syntax/ */
  68. /* https://www.filamentgroup.com/lab/js-web-fonts.html */
  69. @font-face {
  70. font-family: 'EquityTextB';
  71. src: url('/static/david/css/fonts/Equity-Text-B-Regular-webfont.woff2') format('woff2'),
  72. url('/static/david/css/fonts/Equity-Text-B-Regular-webfont.woff') format('woff');
  73. font-weight: 300;
  74. font-style: normal;
  75. font-display: swap;
  76. }
  77. @font-face {
  78. font-family: 'EquityTextB';
  79. src: url('/static/david/css/fonts/Equity-Text-B-Italic-webfont.woff2') format('woff2'),
  80. url('/static/david/css/fonts/Equity-Text-B-Italic-webfont.woff') format('woff');
  81. font-weight: 300;
  82. font-style: italic;
  83. font-display: swap;
  84. }
  85. @font-face {
  86. font-family: 'EquityTextB';
  87. src: url('/static/david/css/fonts/Equity-Text-B-Bold-webfont.woff2') format('woff2'),
  88. url('/static/david/css/fonts/Equity-Text-B-Bold-webfont.woff') format('woff');
  89. font-weight: 700;
  90. font-style: normal;
  91. font-display: swap;
  92. }
  93. @font-face {
  94. font-family: 'ConcourseT3';
  95. src: url('/static/david/css/fonts/concourse_t3_regular-webfont-20190806.woff2') format('woff2'),
  96. url('/static/david/css/fonts/concourse_t3_regular-webfont-20190806.woff') format('woff');
  97. font-weight: 300;
  98. font-style: normal;
  99. font-display: swap;
  100. }
  101. /* http://practice.typekit.com/lesson/caring-about-opentype-features/ */
  102. body {
  103. /* http://www.cssfontstack.com/ Palatino 99% Win 86% Mac */
  104. font-family: "EquityTextB", Palatino, serif;
  105. background-color: #f0f0ea;
  106. color: #07486c;
  107. font-kerning: normal;
  108. -moz-osx-font-smoothing: grayscale;
  109. -webkit-font-smoothing: subpixel-antialiased;
  110. text-rendering: optimizeLegibility;
  111. font-variant-ligatures: common-ligatures contextual;
  112. font-feature-settings: "kern", "liga", "clig", "calt";
  113. }
  114. pre, code, kbd, samp, var, tt {
  115. font-family: 'TriplicateT4c', monospace;
  116. }
  117. em {
  118. font-style: italic;
  119. color: #323a45;
  120. }
  121. strong {
  122. font-weight: bold;
  123. color: black;
  124. }
  125. nav {
  126. background-color: #323a45;
  127. color: #f0f0ea;
  128. display: flex;
  129. justify-content: space-around;
  130. padding: 1rem .5rem;
  131. }
  132. nav:last-child {
  133. border-bottom: 1vh solid #2d7474;
  134. }
  135. nav a {
  136. color: #f0f0ea;
  137. }
  138. nav abbr {
  139. border-bottom: 1px dotted white;
  140. }
  141. h1 {
  142. border-top: 1vh solid #2d7474;
  143. border-bottom: .2vh dotted #2d7474;
  144. background-color: #e3e1e1;
  145. color: #323a45;
  146. text-align: center;
  147. padding: 5rem 0 4rem 0;
  148. width: 100%;
  149. font-family: 'ConcourseT3';
  150. display: flex;
  151. flex-direction: column;
  152. }
  153. h1.single {
  154. padding-bottom: 10rem;
  155. }
  156. h1 span {
  157. position: absolute;
  158. top: 1vh;
  159. left: 20%;
  160. line-height: 0;
  161. }
  162. h1 span a {
  163. line-height: 1.7;
  164. padding: 1rem 1.2rem .6rem 1.2rem;
  165. border-radius: 0 0 6% 6%;
  166. background: #2d7474;
  167. font-size: 1.3rem;
  168. color: white;
  169. text-decoration: none;
  170. }
  171. h2 {
  172. margin: 4rem 0 1rem;
  173. border-top: .2vh solid #2d7474;
  174. padding-top: 1vh;
  175. }
  176. h3 {
  177. text-align: center;
  178. margin: 3rem 0 .75em;
  179. }
  180. hr {
  181. height: .4rem;
  182. width: .4rem;
  183. border-radius: .4rem;
  184. background: #07486c;
  185. margin: 2.5rem auto;
  186. }
  187. time {
  188. display: bloc;
  189. margin-left: 0 !important;
  190. }
  191. ul, ol {
  192. margin: 2rem;
  193. }
  194. ul {
  195. list-style-type: square;
  196. }
  197. a {
  198. text-decoration-skip-ink: auto;
  199. text-decoration-thickness: 0.05em;
  200. text-underline-offset: 0.09em;
  201. }
  202. article {
  203. max-width: 50rem;
  204. display: flex;
  205. flex-direction: column;
  206. margin: 2rem auto;
  207. }
  208. article.single {
  209. border-top: .2vh dotted #2d7474;
  210. margin: -6rem auto 1rem auto;
  211. background: #f0f0ea;
  212. padding: 2rem;
  213. }
  214. article p:last-child {
  215. margin-bottom: 1rem;
  216. }
  217. p {
  218. padding: 0 .5rem;
  219. margin-left: 3rem;
  220. }
  221. p + p,
  222. figure + p {
  223. margin-top: 2rem;
  224. }
  225. blockquote {
  226. background-color: #e3e1e1;
  227. border-left: .5vw solid #2d7474;
  228. display: flex;
  229. flex-direction: column;
  230. align-items: center;
  231. padding: 1rem;
  232. margin: 1.5rem;
  233. }
  234. blockquote cite {
  235. font-style: italic;
  236. }
  237. blockquote p {
  238. margin-left: 0;
  239. }
  240. figure {
  241. border-top: .2vh solid #2d7474;
  242. background-color: #e3e1e1;
  243. text-align: center;
  244. padding: 1.5rem 0;
  245. margin: 1rem 0 0;
  246. font-size: 1.5rem;
  247. width: 100%;
  248. }
  249. figure img {
  250. max-width: 250px;
  251. max-height: 250px;
  252. border: .5vw solid #323a45;
  253. padding: 1px;
  254. }
  255. figcaption {
  256. padding: 1rem;
  257. line-height: 1.4;
  258. }
  259. aside {
  260. display: flex;
  261. flex-direction: column;
  262. background-color: #e3e1e1;
  263. padding: 1rem 0;
  264. border-bottom: .2vh solid #07486c;
  265. }
  266. aside p {
  267. max-width: 50rem;
  268. margin: 0 auto;
  269. }
  270. /* https://fvsch.com/code/css-locks/ */
  271. p, li, pre, code, kbd, samp, var, tt, time, details, figcaption {
  272. font-size: 1rem;
  273. line-height: calc( 1.5em + 0.2 * 1rem );
  274. }
  275. h1 {
  276. font-size: 1.9rem;
  277. line-height: calc( 1.2em + 0.2 * 1rem );
  278. }
  279. h2 {
  280. font-size: 1.6rem;
  281. line-height: calc( 1.3em + 0.2 * 1rem );
  282. }
  283. h3 {
  284. font-size: 1.35rem;
  285. line-height: calc( 1.4em + 0.2 * 1rem );
  286. }
  287. @media (min-width: 20em) {
  288. /* The (100vw - 20rem) / (50 - 20) part
  289. resolves to 0-1rem, depending on the
  290. viewport width (between 20em and 50em). */
  291. p, li, pre, code, kbd, samp, var, tt, time, details, figcaption {
  292. font-size: calc( 1rem + .6 * (100vw - 20rem) / (50 - 20) );
  293. line-height: calc( 1.5em + 0.2 * (100vw - 50rem) / (20 - 50) );
  294. margin-left: 0;
  295. }
  296. h1 {
  297. font-size: calc( 1.9rem + 1.5 * (100vw - 20rem) / (50 - 20) );
  298. line-height: calc( 1.2em + 0.2 * (100vw - 50rem) / (20 - 50) );
  299. }
  300. h2 {
  301. font-size: calc( 1.5rem + 1.5 * (100vw - 20rem) / (50 - 20) );
  302. line-height: calc( 1.3em + 0.2 * (100vw - 50rem) / (20 - 50) );
  303. }
  304. h3 {
  305. font-size: calc( 1.35rem + 1.5 * (100vw - 20rem) / (50 - 20) );
  306. line-height: calc( 1.4em + 0.2 * (100vw - 50rem) / (20 - 50) );
  307. }
  308. }
  309. @media (min-width: 50em) {
  310. /* The right part of the addition *must* be a
  311. rem value. In this example we *could* change
  312. the whole declaration to font-size:2.5rem,
  313. but if our baseline value was not expressed
  314. in rem we would have to use calc. */
  315. p, li, pre, code, kbd, samp, var, tt, time, details, figcaption {
  316. font-size: calc( 1rem + .6 * 1rem );
  317. line-height: 1.5em;
  318. }
  319. p, li, pre, details {
  320. margin-left: 3rem;
  321. }
  322. h1 {
  323. font-size: calc( 1.9rem + 1.5 * 1rem );
  324. line-height: 1.2em;
  325. }
  326. h2 {
  327. font-size: calc( 1.5rem + 1.5 * 1rem );
  328. line-height: 1.3em;
  329. }
  330. h3 {
  331. font-size: calc( 1.35rem + 1.5 * 1rem );
  332. line-height: 1.4em;
  333. }
  334. figure img {
  335. max-width: 500px;
  336. max-height: 500px;
  337. }
  338. }
  339. figure.unsquared {
  340. margin-bottom: 1.5rem;
  341. }
  342. figure.unsquared img {
  343. height: inherit;
  344. }
  345. @media print {
  346. body { font-size: 100%; }
  347. a:after { content: " (" attr(href) ")"; }
  348. a, a:link, a:visited, a:after {
  349. text-decoration: underline;
  350. text-shadow: none !important;
  351. background-image: none !important;
  352. background: white;
  353. color: black;
  354. }
  355. abbr[title] { border-bottom: 0; }
  356. abbr[title]:after { content: " (" attr(title) ")"; }
  357. img { page-break-inside: avoid; }
  358. @page { margin: 2cm .5cm; }
  359. h1, h2, h3 { page-break-after: avoid; }
  360. p3 { orphans: 3; widows: 3; }
  361. img {
  362. max-width: 250px !important;
  363. max-height: 250px !important;
  364. }
  365. nav, aside { display: none; }
  366. }
  367. ul.with_columns {
  368. column-count: 1;
  369. }
  370. @media (min-width: 20em) {
  371. ul.with_columns {
  372. column-count: 2;
  373. }
  374. }
  375. @media (min-width: 50em) {
  376. ul.with_columns {
  377. column-count: 3;
  378. }
  379. }
  380. ul.with_two_columns {
  381. column-count: 1;
  382. }
  383. @media (min-width: 20em) {
  384. ul.with_two_columns {
  385. column-count: 1;
  386. }
  387. }
  388. @media (min-width: 50em) {
  389. ul.with_two_columns {
  390. column-count: 2;
  391. }
  392. }
  393. .gallery {
  394. display: flex;
  395. flex-wrap: wrap;
  396. justify-content: space-around;
  397. }
  398. .gallery figure img {
  399. margin-left: 1rem;
  400. margin-right: 1rem;
  401. }
  402. .gallery figure figcaption {
  403. font-family: 'ConcourseT3'
  404. }
  405. footer {
  406. font-family: 'ConcourseT3';
  407. display: flex;
  408. flex-direction: column;
  409. border-top: 3px solid white;
  410. padding: 4rem 0;
  411. background-color: #07486c;
  412. color: white;
  413. }
  414. footer > * {
  415. max-width: 50rem;
  416. margin: 0 auto;
  417. }
  418. footer a {
  419. color: #f1c40f;
  420. }
  421. footer .avatar {
  422. width: 200px;
  423. height: 200px;
  424. border-radius: 50%;
  425. float: left;
  426. -webkit-shape-outside: circle();
  427. shape-outside: circle();
  428. margin-right: 2rem;
  429. padding: 2px 5px 5px 2px;
  430. background: white;
  431. border-left: 1px solid #f1c40f;
  432. border-top: 1px solid #f1c40f;
  433. border-right: 5px solid #f1c40f;
  434. border-bottom: 5px solid #f1c40f;
  435. }
  436. </style>
  437. <h1>
  438. <span><a id="jumper" href="#jumpto" title="Un peu perdu ?">?</a></span>
  439. Server-First Apps are a Good Idea (archive)
  440. <time>Pour la pérennité des contenus liés. Non-indexé, retrait sur simple email.</time>
  441. </h1>
  442. <section>
  443. <article>
  444. <h3><a href="http://ponyfoo.com/articles/server-first-apps">Source originale du contenu</a></h3>
  445. <p>Earlier today, <a href="http://tomdale.net/2015/02/youre-missing-the-point-of-server-side-rendered-javascript-apps/" aria-label="You&#x2019;re Missing the Point of Server-Side Rendered JavaScript Apps">Tom Dale published an article</a> sharing his views on the whole &quot;server-side vs client-side rendered apps&quot; debacle. I was tempted to call this article <strong>No. You&#x2019;re Missing the Point of Server-Side Rendered JavaScript Apps!</strong>, but that would&apos;ve been way too long, <em>and kind of childish</em>. I agree with the first sentence in his article, where he states that there&apos;s a lot of confusion about <em>&quot;the push to rendering JavaScript apps on the server-side&quot;</em>. There were many other parts of the article I agreed with, and a few I didn&apos;t agree so much with. There&apos;s also some points which weren&apos;t discussed but I&apos;d like to raise myself.</p>
  446. <p>In the article, Tom goes on to explain how applications rendered on the server-side are clumsy when it comes to responsiveness. This is a fair point, server-side rendered applications typically rely on the <a href="http://stackoverflow.com/questions/tagged/post-redirect-get" aria-label="Questions tagged POST-Redirect-Get on StackOverflow">PRG <em>(POST-Redirect-GET)</em> pattern</a>. They have HTML <code class="hljs">&lt;form&gt;</code>s, users <code class="hljs">POST</code> some data, the server processes the request, responds with a redirect to another page, and the client <code class="hljs">GET</code>s that other page. These are pretty much the basics of the web. What&apos;s worse, as Tom notes, is that as you start adding AJAX calls to this server-side rendered content, you are now a slave to both state <em>(what you initially pulled from the server)</em> and behavior <em>(users clicking on things)</em> when it comes to updating the UI. <strong>That is the ultimate nightmare of a web developer.</strong></p>
  447. <p>Client-side rendered apps, in contrast, can be <em>way faster than that</em>. Once the initial payload is downloaded, interpreted, and executed, client-side JavaScript can set up its own <strong>smart caching on the client-side</strong>, avoiding roundtrips to the server for data it already has, and it can also set up routing on the client-side to emulate incredibly-fast roundtrips. It can even have the server spewing information downstream as soon as it has any fresh data to offer, <a href="http://socket.io/" aria-label="Socket.IO is one way to use WebSockets">using WebSockets</a>. The issue of updating the UI as a slave to many masters is long gone, since you just update the UI as the client-side JavaScript engine demands of you.</p>
  448. <p><em>And so the story goes...</em></p>
  449. <p>The answer to a productive and maintainable web development orientation, <em>that also favors customers</em>, doesn&apos;t lie in one or the other, but rather in <em>the combination of both approaches</em>. In this article, I&apos;ll explore what all of this means.</p>
  450. <p></section><section class="md-markdown at-body"><h1>Angular, Ember, and React</h1><p>I&apos;ve <a href="/articles/stop-breaking-the-web" aria-label="Stop Breaking the Web">already summed up my thoughts on Angular</a>. The more I think of it, the more convinced I am that Angular is <em>the <strong>Bootstrap</strong> of JavaScript</em>. It&apos;s a great way of prototyping an application or building a backend service, but there&apos;s <em>plenty of reasons</em> why you shouldn&apos;t be building a customer-facing application with it.</p><blockquote><p>I hope 2015 is the year where we take out <em>&quot;dedicated client-side rendering&quot;</em> like Angular&apos;s from our <strong>metaphorical best practices grab-bags</strong>. React and Ember are doing a good job of bringing people to their senses when it comes to one-sided rendering.</p></blockquote><p>I&apos;m not sure how the initiative to move Ember to shared-rendering, <strong>FastBoot</strong> will work, but if it hijacks <code class="hljs">&lt;form&gt;</code> submissions and generally does the right thing with those <em>(both before and after JavaScript gets executed on the client)</em>, then I&apos;ll be quite sold on the idea. I&apos;m glad to see that Tom Dale seems to have come around from clamouring that <a href="http://tomdale.net/2013/09/progressive-enhancement-is-dead/" aria-label="Progressive Enhancement is Dead">&quot;Progressive Enhancement is Dead&quot;</a>, but I still think developing client-first applications and then rebuilding what should have been the original HTML is <strong>just backwards</strong>.</p><p>React is more of a &quot;shared-rendering&quot; native citizen, which makes it friendlier when it comes to progressive enhancement. It&apos;s shared-rendering capabilities used to be mostly an option for Node.js developers, but <a href="https://www.youtube.com/watch?v=KVZ-P-ZI6W4" aria-label="&apos;Introducing React Native&apos; talk at ReactConf">Facebook recently revealed <code class="hljs">react-native</code></a> as a way to write native Android and iOS applications on React, making it even more appealing as it now enables cross-platform development, a lot like how <a href="http://arstechnica.com/information-technology/2014/11/how-google-inbox-shares-70-of-its-code-across-android-ios-and-the-web/" aria-label="How Google Inbox shares 70% of its code across Android, iOS, and the Web">Google shares code</a> across platforms using <a href="https://github.com/google/j2objc" aria-label="google/j2objc on GitHub">j2objc</a>.</p><h1>Progressive Enhancement</h1><p><strong>The web is not native</strong>, though. React and Ember don&apos;t hinder our ability to develop a progressively enhanced application, but they don&apos;t exactly encourage it either. I&apos;d call them &#x2014; along with Angular &#x2014; <strong>client-first frameworks</strong>. Client-first doesn&apos;t encourage progressive enhancement. Quite the contrary, client-first actively discourages progressive. That&apos;s a real problem.</p><p>These frameworks are nice and definitely boost our productivity, but we should never stop thinking about building applications in such a way that they&apos;ll actually work well <em>(not just <strong>render</strong> well)</em> for people <a href="http://ponyfoo.com/articles/critical-path-performance-optimization" aria-label="Critical Path Performance Optimization at Pony Foo">on slow or intermittent networks</a>.</p><p>I think these last few months we did a good job of thinking critically about whether the Angular way is the right way. I&apos;m convinced that the web would be a far better place if we developed most applications in a <strong>content-first</strong> manner.</p><p>Principles of a progressively enhanced user experience should be commonplace by now. You build an application on pure HTML and CSS, in such a way that it&apos;s able to deliver most of your core experience<em> right off the bat. This is important because sometimes JavaScript may take a few seconds to download. That&apos;s why I made the point about using <code class="hljs">&lt;form&gt;</code> elements to allow users to interact with the site, it enables more parts of the core experience.</p><blockquote><p>It&apos;s insane, what we are doing. <strong>We are deferring JavaScript and loading it asynchronously and then depending on it to deliver our core experience?</strong></p></blockquote><p><em></em> Unless you&apos;re a <strong>realtime video-conferencing service</strong> (or anything canvas-based), but even then you could take a progressive approach, where you inline the absolutely necessary JavaScript to enable the video-calling functionality, and defer the rest. Much like you&apos;d do <a href="http://ponyfoo.com/articles/critical-path-performance-optimization" aria-label="Critical Path Performance Optimization at Pony Foo">when deferring non-critical CSS</a>.</em></p><p>Suppose you have a TODO list. Checking items off would just be a matter of clicking on them in a client-first application, then the changes would be persisted in the background. In contrast, a server-first approach probably would&apos;ve had a <code class="hljs">&lt;form&gt;</code> with the TODO list and some sort of <kbd>Submit</kbd> button. Right? But what if each TODO item was a <code class="hljs">&lt;form&gt;</code>? What if each of them was a <code class="hljs">&lt;button&gt;</code> within its own <code class="hljs">&lt;form&gt;</code>? Then you could have almost the same functionality as people have come to expect from client-first applications, but in an entirely progressive way!</p><p>The HTML would look like this, except with proper form <code class="hljs">action</code>s, <code class="hljs">href</code>s, and CSS classes for styling.</p><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-title">ul</span>&gt;</span>
  451. <span class="hljs-tag">&lt;<span class="hljs-title">li</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-title">form</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-title">button</span>&gt;</span>This is an option<span class="hljs-tag">&lt;/<span class="hljs-title">button</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-title">form</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-title">li</span>&gt;</span>
  452. <span class="hljs-tag">&lt;<span class="hljs-title">li</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-title">form</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-title">button</span>&gt;</span>This is another option<span class="hljs-tag">&lt;/<span class="hljs-title">button</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-title">form</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-title">li</span>&gt;</span>
  453. <span class="hljs-tag">&lt;<span class="hljs-title">li</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-title">form</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-title">button</span>&gt;</span>This is yet another option<span class="hljs-tag">&lt;/<span class="hljs-title">button</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-title">form</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-title">li</span>&gt;</span>
  454. <span class="hljs-tag">&lt;/<span class="hljs-title">ul</span>&gt;</span>
  455. </code></pre><p>It could look like the screenshot shown below, which comes from <a href="http://blog.stompflow.com/articles/iterative-prototyping-for-the-web" aria-label="Iterative Prototyping for the Web">a product I&apos;m building</a>, and doesn&apos;t involve any client-side JavaScript just yet.</p><p><a href="http://blog.stompflow.com/articles/iterative-prototyping-for-the-web" aria-label="Iterative Prototyping for the Web"><img alt="stompflow.png" class="js-only" src="http://i.imgur.com/NqHl1zm.png" /><noscript><img src="http://i.imgur.com/NqHl1zm.png" alt="stompflow.png"></noscript></a></p><p>Not any client-side JavaScript yet? <strong><em>That</em> can&apos;t be good!</strong> You must be thinking. Turns out <em>developing applications like its the year 2002</em> is super productive &#x2014; you don&apos;t have to spend any time carefully picking a delightful animated loader gif, or debating with your staff about <strong>what&apos;s the best way to do data-binding</strong>.</p><p>That&apos;s the main argument against not using your fancy frameworks, right? But they&apos;re <em>so productive!</em> Well, using HTML and <strong>PRG</strong> is fast, too! You just forgot they even existed.</p><p>Sure, the <strong>PRG</strong> pattern is &quot;slow&quot;, and client-first is perceptively faster &#x2014; <em>but guess what?</em> Upgrading an <strong>HTML-PRG</strong> experience into an AJAX experience is a matter of writing a few lines of code, if it&apos;s done right. From there, turning the experience into a real-time experience is the only challenge left. And, honestly? That&apos;s just a matter of listening for the appropriate events and responding to them!</p><h1>A Server-First Web</h1><p><a href="http://taunus.bevacqua.io/" aria-label="Taunus: Micro Isomorphic MVC Engine for Node.js">Taunus</a> is a server-first shared-rendering MVC engine that prioritizes content and encourages progressive enhancement. It&apos;s what <a href="https://github.com/ponyfoo/ponyfoo" aria-label="ponyfoo.com source code on GitHub">this blog</a> runs on top of, it&apos;s what the <a href="https://github.com/taunus/taunus.bevacqua.io" aria-label="taunus.bevacqua.io source code on GitHub">documentation mini-site</a> runs on top of, and it&apos;s what I&apos;m using in Stompflow &#x2014; pictured in the screenshot above, <a href="http://www.stompflow.com" aria-label="Stompflow: Hassle-free Project Management">but yet to be released</a>.</p><p>Being server-first has its perks as well, just like client-first does. For instance, I <a href="https://github.com/ponyfoo/ponyfoo/blob/master/views/server/emails/article-published.jade" aria-label="This template will be running hot when the article gets published!">maintain email templates</a> using the <strong>same templating engine</strong> that I use on web views. Being server-first also means that I don&apos;t have to worry about <strong>state vs behavior</strong> as much, because I have the same views on both sides and I can re-render them at any time in the client-side.</p><p>Being server-first means above all that the application will work well no matter what. It&apos;ll work well if client-side JavaScript takes a few seconds to execute, because it was designed to do so. It&apos;ll continue to do well after client-side JavaScript lands. You can take advantage of the conventionality of HTML forms and hijack form submissions just as conventionally, making the form submission via AJAX and then handling the response by redirecting or rendering some data.</p><blockquote><p>You no longer fight against the disgrace IE8 unleashed onto you, but <em>embrace it</em>. You now <strong>fight against the idea that you should cram every single new feature onto users on old browsers</strong>.</p></blockquote><p>Server-first is <em>non-commitment</em> to client-side technologies. With <a href="https://github.com/taunus/taunus" aria-label="taunus on GitHub">Taunus</a> you don&apos;t <em>have</em> to pick a client-side data-binding library, but you can choose to do so. We already agree that it&apos;s easier to deal with vanilla components than jQuery plugins, or Angular directives, or React components, or even Web Components.</p><p>So, why not use a framework that empowers you to work this way? Using modular components that aren&apos;t tied to the framework itself, which is more of a glorified &quot;stay-out-of-the-way router&quot; that dictates you how to do shared rendering by convention.</p><p>Fine, <a href="https://github.com/taunus/taunus" aria-label="taunus on GitHub">Taunus</a> is tied to a server-side technology: Node.js <em>(or io.js!)</em></p><p>That&apos;s not an issue for React, or Ember. Not even for Angular.js.</p><p>But you know what? <em>That&apos;s a good thing.</em></p><p>Because you are forced to <strong>commit to a server-side technology <em>first</em></strong>.</p></p>
  456. </article>
  457. </section>
  458. <nav id="jumpto">
  459. <p>
  460. <a href="/david/blog/">Accueil du blog</a> |
  461. <a href="http://ponyfoo.com/articles/server-first-apps">Source originale</a> |
  462. <a href="/david/stream/2019/">Accueil du flux</a>
  463. </p>
  464. </nav>
  465. <footer>
  466. <div>
  467. <img src="/static/david/david-larlet-avatar.jpg" loading="lazy" class="avatar" width="200" height="200">
  468. <p>
  469. Bonjour/Hi!
  470. Je suis <a href="/david/" title="Profil public">David&nbsp;Larlet</a>, je vis actuellement à Montréal et j’alimente cet espace depuis 15 ans. <br>
  471. Si tu as apprécié cette lecture, n’hésite pas à poursuivre ton exploration. Par exemple via les <a href="/david/blog/" title="Expériences bienveillantes">réflexions bimestrielles</a>, la <a href="/david/stream/2019/" title="Pensées (dés)articulées">veille hebdomadaire</a> ou en t’abonnant au <a href="/david/log/" title="S’abonner aux publications via RSS">flux RSS</a> (<a href="/david/blog/2019/flux-rss/" title="Tiens c’est quoi un flux RSS ?">so 2005</a>).
  472. </p>
  473. <p>
  474. Je m’intéresse à la place que je peux avoir dans ce monde. En tant qu’humain, en tant que membre d’une famille et en tant qu’associé d’une coopérative. De temps en temps, je fais aussi des <a href="https://github.com/davidbgk" title="Principalement sur Github mais aussi ailleurs">trucs techniques</a>. Et encore plus rarement, <a href="/david/talks/" title="En ce moment je laisse plutôt la place aux autres">j’en parle</a>.
  475. </p>
  476. <p>
  477. Voici quelques articles choisis :
  478. <a href="/david/blog/2019/faire-equipe/" title="Accéder à l’article complet">Faire équipe</a>,
  479. <a href="/david/blog/2018/bivouac-automnal/" title="Accéder à l’article complet">Bivouac automnal</a>,
  480. <a href="/david/blog/2018/commodite-effondrement/" title="Accéder à l’article complet">Commodité et effondrement</a>,
  481. <a href="/david/blog/2017/donnees-communs/" title="Accéder à l’article complet">Des données aux communs</a>,
  482. <a href="/david/blog/2016/accompagner-enfant/" title="Accéder à l’article complet">Accompagner un enfant</a>,
  483. <a href="/david/blog/2016/senior-developer/" title="Accéder à l’article complet">Senior developer</a>,
  484. <a href="/david/blog/2016/illusion-sociale/" title="Accéder à l’article complet">L’illusion sociale</a>,
  485. <a href="/david/blog/2016/instantane-scopyleft/" title="Accéder à l’article complet">Instantané Scopyleft</a>,
  486. <a href="/david/blog/2016/enseigner-web/" title="Accéder à l’article complet">Enseigner le Web</a>,
  487. <a href="/david/blog/2016/simplicite-defaut/" title="Accéder à l’article complet">Simplicité par défaut</a>,
  488. <a href="/david/blog/2016/minimalisme-esthetique/" title="Accéder à l’article complet">Minimalisme et esthétique</a>,
  489. <a href="/david/blog/2014/un-web-omni-present/" title="Accéder à l’article complet">Un web omni-présent</a>,
  490. <a href="/david/blog/2014/manifeste-developpeur/" title="Accéder à l’article complet">Manifeste de développeur</a>,
  491. <a href="/david/blog/2013/confort-convivialite/" title="Accéder à l’article complet">Confort et convivialité</a>,
  492. <a href="/david/blog/2013/testament-numerique/" title="Accéder à l’article complet">Testament numérique</a>,
  493. et <a href="/david/blog/" title="Accéder aux archives">bien d’autres…</a>
  494. </p>
  495. <p>
  496. On peut <a href="mailto:david%40larlet.fr" title="Envoyer un courriel">échanger par courriel</a>. Si éventuellement tu souhaites que l’on travaille ensemble, tu devrais commencer par consulter le <a href="http://larlet.com">profil dédié à mon activité professionnelle</a> et/ou contacter directement <a href="http://scopyleft.fr/">scopyleft</a>, la <abbr title="Société coopérative et participative">SCOP</abbr> dont je fais partie depuis six ans. Je recommande au préalable de lire <a href="/david/blog/2018/cout-site/" title="Attention ce qui va suivre peut vous choquer">combien coûte un site</a> et pourquoi je suis plutôt favorable à une <a href="/david/pro/devis/" title="Discutons-en !">non-demande de devis</a>.
  497. </p>
  498. <p>
  499. Je ne traque pas ta navigation mais mon
  500. <abbr title="Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33.184162340">hébergeur</abbr>
  501. conserve des logs d’accès.
  502. </p>
  503. </div>
  504. </footer>
  505. <script type="text/javascript">
  506. ;(_ => {
  507. const jumper = document.getElementById('jumper')
  508. jumper.addEventListener('click', e => {
  509. e.preventDefault()
  510. const anchor = e.target.getAttribute('href')
  511. const targetEl = document.getElementById(anchor.substring(1))
  512. targetEl.scrollIntoView({behavior: 'smooth'})
  513. })
  514. })()
  515. </script>