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 години
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  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>Meilleure UX, meilleures performances : la nouvelle donne du web statique (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="https://borisschapira.com//2018/02/site-statique-performance-web/">
  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. Meilleure UX, meilleures performances : la nouvelle donne du web statique (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="https://borisschapira.com//2018/02/site-statique-performance-web/">Source originale du contenu</a></h3>
  445. <p>Générateurs de sites statiques, CMS « headless », plateformes d’intégration continue et de déploiement… depuis plusieurs années, une nouvelle gamme de solutions émerge dans le paysage des technologies web. Ces solutions contribuent à une tendance globale qui ressemble à un retour aux sources du Web. On parle de « La mouvance statique » ou de la « JAMStack » mais aucun de ces noms ne caractérise vraiment ce qui n’est pas moins qu’une nouvelle façon d’architecturer des applications web.</p>
  446. <p><p><img src="https://res.cloudinary.com/borisschapira/image/fetch/c_limit,f_auto,q_auto,w_1200/https://borisschapira.com/assets/images/2018-02-21/0_rouages.jpg" srcset="https://res.cloudinary.com/borisschapira/image/fetch/c_limit,f_auto,q_auto,w_320/https://borisschapira.com/assets/images/2018-02-21/0_rouages.jpg 320w, https://res.cloudinary.com/borisschapira/image/fetch/c_limit,f_auto,q_auto,w_540/https://borisschapira.com/assets/images/2018-02-21/0_rouages.jpg 540w, https://res.cloudinary.com/borisschapira/image/fetch/c_limit,f_auto,q_auto,w_760/https://borisschapira.com/assets/images/2018-02-21/0_rouages.jpg 760w, https://res.cloudinary.com/borisschapira/image/fetch/c_limit,f_auto,q_auto,w_980/https://borisschapira.com/assets/images/2018-02-21/0_rouages.jpg 980w, https://res.cloudinary.com/borisschapira/image/fetch/c_limit,f_auto,q_auto,w_1200/https://borisschapira.com/assets/images/2018-02-21/0_rouages.jpg 1200w" sizes="(min-width:20em) and (max-width:50em) 20em, (min-width:50em) 30em" alt="De grands rouages métalliques emboités"/></p> <h2 id="aux-origines-du-web">Aux origines du Web</h2> <p>Lorsqu’un utilisateur tente d’accéder à une page Web, son navigateur envoie une requête au serveur qui l’héberge. Soit le serveur retourne immédiatement la page telle qu’elle est stockée, soit le serveur la génère en exécutant du code, à la demande.</p> <p>Bien que le Web ait été conçu comme un enchevêtrement de fichiers statiques, les langages de programmation côté serveur sont apparus très tôt et sont maintenant largement utilisés. <a href="https://w3techs.com/technologies/overview/programming_language/all">Selon W3Techs</a>, plus de 80 % des serveurs qui utilisent un langage côté serveur tournent en PHP. Dans la mesure où les hébergeurs offrant des serveurs qui n’exécutent pas au moins un langage sont pratiquement introuvables, il y a fort à parier qu’une écrasante majorité de sites web sont aujourd’hui générés dynamiquement.</p> <p>Pourtant, la génération dynamique de réponses HTTP présente des inconvénients importants en termes de performance web. Une page web dynamique est délivrée par un serveur web, qui charge un langage d’exécution, qui analyse la requête HTTP, interroge souvent une base de données (parfois localisée sur un autre serveur dans le datacenter) et des services tiers, alimente un modèle logique qui se révèle à travers un agrégat de vues pour générer une réponse HTML. Ces opérations prennent du temps, donc logiquement, le <a href="https://www.dareboost.com/en/glossary#ttfb">Time To First Byte</a> est plus long.</p> <figure> <img src="https://res.cloudinary.com/borisschapira/image/fetch/c_limit,f_auto,q_auto,w_1200/https://borisschapira.com/assets/images/2018-02-21/1_ttfb_en.png" srcset="https://res.cloudinary.com/borisschapira/image/fetch/c_limit,f_auto,q_auto,w_320/https://borisschapira.com/assets/images/2018-02-21/1_ttfb_en.png 320w, https://res.cloudinary.com/borisschapira/image/fetch/c_limit,f_auto,q_auto,w_540/https://borisschapira.com/assets/images/2018-02-21/1_ttfb_en.png 540w, https://res.cloudinary.com/borisschapira/image/fetch/c_limit,f_auto,q_auto,w_760/https://borisschapira.com/assets/images/2018-02-21/1_ttfb_en.png 760w, https://res.cloudinary.com/borisschapira/image/fetch/c_limit,f_auto,q_auto,w_975/https://borisschapira.com/assets/images/2018-02-21/1_ttfb_en.png 975w" sizes="(min-width:20em) and (max-width:50em) 20em, (min-width:50em) 30em" alt="Un graphique Dareboost Dareboost sur lequel on peut voir plusieurs indicateurs. Les pics de TTFB coincident avec les pics de Speed Index."/> <figcaption><p>Un TTFB elevé pénalise le Speed Index d’une page.</p> </figcaption> </figure> <p>Afin d’optimiser les temps de réponse, de nombreuses solutions de mise en cache sont apparues au fil des ans. Avec ces solutions, le premier utilisateur qui requête la page continue de subir le coût de génération mais le résultat est stocké sur un ou plusieurs serveurs proxy et parfois synchronisé sur différents endroits, partout dans le monde. Ces pages « mises en cache » sont ensuite utilisées pour répondre à toutes les requêtes considérées identiques, ce qui assure des temps de réponse rapides et uniformes. Il est possible de trouver des solutions de mise en cache logicielles (comme <a href="https://varnish-cache.org/">Varnish</a>) mais également des plateformes et des infrastructures (les Content Delivery Networks). À chaque fois, le principe est le même : transformer ce qui est dynamique en pages statiques. Chassez le naturel…</p> <h2 id="mais-lutilisation-de-contenus-statiques-présente-dautres-avantages-">Mais l’utilisation de contenus statiques présente d’autres avantages !</h2> <p>Si vous voulez livrer vos pages statiques, vous devez les avoir compilées à l’avance. Ce fait, aussi banal qu’il puisse paraître, change tout. En effet, la compilation constitue le principal avantage du statique : déplacer la complexité de l’environnement de production vers le processus d’intégration.</p> <p>Si vos pages sont servies par un serveur web sans avoir besoin d’être générées à la volée, vous n’avez pas besoin d’exécuter un langage côté serveur. Sans ce langage, de nombreux vecteurs d’attaque disparaissent. On ne peut pas vous voler des données confidentielles en injectant du code malveillant si vous n’avez ni base de données, ni langage d’exécution côté serveur.</p> <p>Ne pas exécuter de code sur le serveur signifie également que la consommation CPU est très faible pour chaque réponse HTTP, ce qui améliore considérablement votre capacité de montée en charge. Attention toutefois : comme nous le verrons, le déploiement est un facteur clé et peut prendre du temps CPU.</p> <p>Autre avantage : votre infrastructure sera plus résiliente. Imaginons qu’une erreur advienne, cela serait nécessairement pendant la génération. Vous pourriez alors la détecter avant le déploiement (par des tests automatisés, par exemple). Les problèmes techniques résultant d’une mauvaise contribution n’ont donc plus d’impact sur le site consulté par les visiteurs. Au pire des cas, c’est-à-dire si la compilation échoue, le contenu ne sera pas livré et le contenu du site ne sera pas à jour.</p> <p>Ces avantages ne sont que la partie visible de l’iceberg. La tendance statique vous permet de transformer complètement la façon dont un site est publié. Pas étonnant que <a href="https://www.smashingmagazine.com/2017/03/a-little-surprise-is-waiting-for-you-here/">Smashing Magazine ait déjà migré (EN)</a> !</p> <h2 id="la-statique-est-une-modalité-de-distribution-quelle-est-sa-stack-technique-">La statique est une modalité de distribution. Quelle est sa <em>stack</em> technique ?</h2> <p>Un générateur de site statique (SSG) est un logiciel exécuté localement ou en tant que service. Il produit (et parfois déploie) un site Web statique en utilisant certaines sources de données pour le modèle et la configuration, ainsi que des templates contenant de la logique métier.</p> <p>Le marché des SSG est en plein essor, avec <a href="https://www.staticgen.com/">un nouveau produit lancé toutes les deux semaines</a>. La plupart d’entre eux génèrent un site Web à partir d’un ensemble de fichiers, souvent écrits avec une syntaxe légère comme <a href="https://daringfireball.net/projects/markdown/">Markdown</a> ou Asciidoc. La responsabilité de la conversion en HTML est attribuée à la fois à un moteur de templating (Liquid, Go Template, Nunjucks) - responsable de la logique - et à un moteur de rendu (<a href="https://kramdown.gettalong.org/">kramdown</a>, <a href="http://commonmark.org/">commonmark</a>, <a href="https://github.com/russross/blackfriday">blackfriday</a>, <a href="http://asciidoctor.org/…">Asciidoctor</a>) responsable de la transformation du balisage en HTML. Les SSG ne sont rien de plus que les orchestrateurs techniques de la génération du site web. Par conséquent, ils sont principalement un terrain de jeu pour les développeurs front-end qui en connaissent les rouages.</p> <p>En effet, les SSG sont des outils techniques et non des substituts aux CMS. Toutefois, ils deviennent vraiment intéressants quand vous suivez la piste de leur alimentation par des sources de données externes. Parce qu’alors, nous pouvons introduire des CMS qui ne seraient pas utilisés pour le rendu HTML, mais seulement pour stocker et exposer les données. On les appelle les CMS Headless.</p> <p>Un CMS Headless est constitué :</p> <blockquote> <ul> <li>d’un système de stockage de données ;</li> <li>d’une interface CRUD ;</li> <li>d’une API pour accéder au données. <br/> <cite>« <a href="https://css-tricks.com/what-is-a-headless-cms/">What is a Headless CMS? (EN)</a> », Chris Coyier</cite></li> </ul> </blockquote> <p>Et vous pouvez fabriquer des CMS Headless à partir de vos solutions habituelles. Wordpress, par exemple, a une <a href="http://v2.wp-api.org/">API REST</a>. Côté Drupal, il y a tout un <a href="https://groups.drupal.org/headless-drupal">groupe de travail</a> qui travaille sur le Headless. Là encore, le marché est en plein essor avec de nombreux nouveaux <a href="https://www.headlesscms.org/">logiciels et services</a>.</p> <p>Mais pourquoi diable voudrions-nous séparer l’environnement contributif et l’outil de production? Pour une meilleure séparation des préoccupations.</p> <p>L’équipe de développement, libérée du fardeau de la maintenance d’une base de données, peut se concentrer sur l’évolution technique de la plate-forme et le pipeline de production des ressources statiques tandis que l’équipe de contribution, de son côté, peut affiner les contenus.</p> <p>Les contributeurs peuvent travailler sur des fichiers plats faciles à stocker et à modifier. Leur seul langage commun avec les développeurs devient les métadonnées passées dans chaque fichier, souvent écrites avec <a href="https://jekyllrb.com/docs/frontmatter/">Front-Matter</a>. Ils peuvent utiliser un outil dédié à l’édition ou le service en ligne de leur choix, même certains qui facilitent la collaboration. Ils peuvent également bénéficier du versionning de fichiers pour consulter l’historique de leurs fichiers, fusionner plusieurs versions ou créer des branches pour écrire du contenu qu’ils ne veulent pas publier tout de suite.</p> <figure> <img src="https://res.cloudinary.com/borisschapira/image/fetch/c_limit,f_auto,q_auto,w_1200/https://borisschapira.com/assets/images/2018-02-21/2_carrot.png" srcset="https://res.cloudinary.com/borisschapira/image/fetch/c_limit,f_auto,q_auto,w_320/https://borisschapira.com/assets/images/2018-02-21/2_carrot.png 320w, https://res.cloudinary.com/borisschapira/image/fetch/c_limit,f_auto,q_auto,w_540/https://borisschapira.com/assets/images/2018-02-21/2_carrot.png 540w, https://res.cloudinary.com/borisschapira/image/fetch/c_limit,f_auto,q_auto,w_760/https://borisschapira.com/assets/images/2018-02-21/2_carrot.png 760w, https://res.cloudinary.com/borisschapira/image/fetch/c_limit,f_auto,q_auto,w_980/https://borisschapira.com/assets/images/2018-02-21/2_carrot.png 980w, https://res.cloudinary.com/borisschapira/image/fetch/c_limit,f_auto,q_auto,w_1200/https://borisschapira.com/assets/images/2018-02-21/2_carrot.png 1200w" sizes="(min-width:20em) and (max-width:50em) 20em, (min-width:50em) 30em" alt="Un diagramme des flux de contribution et de développement d'un site qui montre clairement la séparation des préoccuppations entre développeurs et contributeurs."/> <figcaption><p>Le flux de contribution du CMS statique de Carrot (une agence numérique), tel que décrit <a href="https://carrot.is/coding/static_cms.html">sur leur blog</a>.</p> </figcaption> </figure> <p>Après la contribution des contenus, la génération du site web et le déploiement sont souvent opérés par le même composant de l’infrastructure. Pour évaluer la performance de cette étape, il faut surveiller la performance du serveur de compilation durant la génération et du déploiement (durée, consommation CPU, mémoire). Mais ce n’est pas tout : pensez à surveiller également l’infrastructure ciblée (composée d’un ou plusieurs serveurs), car les tâches de copie peuvent être gourmandes.</p> <p>Nous ne sommes plus alors dans un système où la métrique principale de montée en charge est le nombre de visiteurs simultanés. Les DevOps doivent complètement changer leur façon de penser pour créer un système capable de s’adapter plutôt à la fréquence de génération et de déploiement demandée par les contributeurs.</p> <p>Là encore, <a href="https://www.thenewdynamic.org/tools/hosting-deployment/">de nouveaux acteurs sont apparus</a>. Le plus connu et probablement le plus efficace d’entre eux est sûrement Netlify. Son interface claire et simple aide à rattacher un dépôt de code source en quelques clics. Netlify va ensuite générer et déployer votre site web pour chaque commit, à la volée.</p> <p>Un CMS Headless, un SSG et un orchestrateur de déploiement : nous avons maintenant notre stack back-end complète. Pourtant, nous produisons toujours un site web statique sans contenu personnalisé. La demande de dynamisme et de personnalisation des utilisateurs n’ayant jamais été aussi forte, ne faisons-nous pas fausse route ?</p> <h2 id="statique--pas-tant-que-ça">Statique ? Pas tant que ça.</h2> <p>Nous avons vu que cette stack statique produit un balisage très standardisé. Afin d’introduire du dynamisme et de la personnalisation, nous devrons importer des données – fournies par le biais d’API – et les traiter du côté client – donc utiliser Javascript.</p> <p>Cette nouvelle stack « <strong>J</strong>avaScript + <strong>A</strong>PI à couplage léger + <strong>M</strong>arkup HTML » a un nom : <a href="https://jamstack.org/">JAMStack</a> et le marché affiche déjà plusieurs acteurs de premier plan, chacun d’entre eux disposant d’une gamme de services bien spécifique : <a href="https://stripe.com/">Stripe</a> pour le paiement, <a href="https://www.algolia.com/">Algolia</a> pour la recherche instantanée, <a href="https://disqus.com/">Disqus</a> ou <a href="https://www.intensedebate.com/">IntenseDebate</a> pour les commentaires, <a href="https://snipcart.com/">Snipcart</a> pour le e-commerce, <a href="https://cloudinary.com/">Cloudinary</a> pour la gestion des médias, <a href="https://formspree.io/">Formspree</a> ou <a href="https://staticman.net/">Staticman</a> pour les formulaires… Notez que tous ces produits ne sont pas conçus pour la JAMStack à proprement parler : vous pouvez parfaitement utiliser les fulgurantes API d’Algolia côté serveur.</p> <p>JAMStack est un véritable changement de paradigme. Le site Web servi au visiteur devient plus que jamais une coquille dans lequel des services, qu’ils soient auto-hébergés ou tiers, sont dynamiquement injectés. Il est même possible de s’appuyer sur plusieurs services pour un seul objectif et passer de l’un à l’autre en cas d’indisponibilité ou pour maximiser la performance.</p> <p>Et puisque vous déportez une grande partie de vos efforts côté Front, pourquoi ne pas aller plus loin et le construire avec un framework Single Page App (SPA) comme Vue, Angular ou React ou le transformer en Progressive Web App (PWA) complète, conçue pour être « offline first » ? Rien de tout ça n’est inhérent à la JAMStack, mais facilité par le changement de paradigme de développement.</p> <p>Pour l’utilisateur, en revanche, la différence est imperceptible. Essayez de rechercher un produit <a href="https://community.algolia.com/instantsearch.js/v2/examples/e-commerce/">sur ce site</a>. Avez-vous l’impression d’utiliser un site différent par rapport à un site développé en PHP ?</p> <p>Pour les contributeurs, le changement de paradigme est total puisqu’il sépare la plateforme de contribution de celle qui héberge le site Web, leur laissant une plus grande liberté dans leurs usages. Certains d’entre eux préféreront utiliser des logiciels qui intègrent des outils d’aide à l’écriture (correcteur de langue, suggestions de vocabulaire, intégrations documentaires). D’autres trouveront leur bonheur auprès de plateformes de contribution en ligne. En supposant que le stockage du contenu soit indépendant de la solution utilisée, chacun d’entre eux pourra utiliser son outil préféré sans interférer avec les autres.</p> <figure> <img src="https://res.cloudinary.com/borisschapira/image/fetch/c_limit,f_auto,q_auto,w_1200/https://borisschapira.com/assets/images/2018-02-21/3_forestry.png" srcset="https://res.cloudinary.com/borisschapira/image/fetch/c_limit,f_auto,q_auto,w_320/https://borisschapira.com/assets/images/2018-02-21/3_forestry.png 320w, https://res.cloudinary.com/borisschapira/image/fetch/c_limit,f_auto,q_auto,w_540/https://borisschapira.com/assets/images/2018-02-21/3_forestry.png 540w, https://res.cloudinary.com/borisschapira/image/fetch/c_limit,f_auto,q_auto,w_760/https://borisschapira.com/assets/images/2018-02-21/3_forestry.png 760w, https://res.cloudinary.com/borisschapira/image/fetch/c_limit,f_auto,q_auto,w_980/https://borisschapira.com/assets/images/2018-02-21/3_forestry.png 980w, https://res.cloudinary.com/borisschapira/image/fetch/c_limit,f_auto,q_auto,w_1200/https://borisschapira.com/assets/images/2018-02-21/3_forestry.png 1200w" sizes="(min-width:20em) and (max-width:50em) 20em, (min-width:50em) 30em" alt="Forestry.io editing interface"/> <figcaption><p>Interface de contribution de <a href="https://forestry.io/">Forestry.io</a>. Les contenus sont sauvegardés dans les fichiers d’un dépôt git et peuvent être également modifiés avec un éditeur de texte.</p> </figcaption> </figure> <p>Mais certains produits vont plus loin et transforment la contribution en expérience de première classe. Les contributeurs peuvent alors bénéficier d’outils de saisie sur mesure, avec des options de contribution riches et une intégration multimédias, sans impact sur le flux de publication ou la performance du site web ciblé.</p> <figure> <p class="videoWrapper"><video playsinline="" controls="" preload="none" poster="/assets/images/2018-02-21/4_prismic.jpg" aria-describedby="9b7f1b64-f110-4697-8055-6a1122c043d9"> <source src="/assets/images/2018-02-21/4_prismic.mp4" type="video/mp4"/></video></p> <span id="9b7f1b64-f110-4697-8055-6a1122c043d9" class="sr-only">Prismic.io slices</span> <figcaption><p><a href="https://prismic.io/">Prismic.io</a> est l’une des plates-formes de contenu les plus personnalisables (ici, des « slices » réutilisables). Les contenus comme les gabarits sont contribués sur la plateforme. Pendant la compilation, le SSG requête l’API de Prismic.io pour récupérer les informations.</p> </figcaption> </figure> <h2 id="statique-ou-dynamique--du-pareil-au-même">Statique ou dynamique ? Du pareil au même.</h2> <p>Bien que l’approche JAMStack présente de nombreux avantages, dont la sécurité, la performance, la montée en charge et la manière dont on développe et contribue, elle implique également de nouveaux risques qu’il serait dangereux d’ignorer.</p> <p>Premier danger : se perdre dans l’offre pléthorique de CMS Headless, générateurs de sites statiques et plateformes de services. Prenez le temps d’évaluer vos besoins car chaque solution a ses avantages et ses inconvénients. <a href="https://jekyllrb.com/">Jekyll</a>, par exemple, est un SSG bien connu, développé en Ruby, bien documenté mais assez lent. <a href="https://gohugo.io/">Hugo</a>, en revanche, est un SSG beaucoup plus rapide mais aussi plus complexe à manier pour un novice. Si vous ne publiez pas du contenu à très haute fréquence, le temps de génération est-il un critère de choix si important, à résultat équivalent ?</p> <p>Pensez également à inspecter les conditions d’utilisation des services tiers que vous envisagez d’utiliser. Chez Dareboost, nous avertissons souvent nos utilisateurs contre les abus de scripts tiers. Si vous avez besoin de commentaires et utilisez Disqus, acceptez-vous pour autant d’injecter dynamiquement leurs publicités ? Heureusement, si vous avez suivi <a href="https://blog.dareboost.com/fr/2016/08/comment-implementer-content-security-policy/">nos conseils sur les Content Security Policies</a>, vous ne devriez pas vous être trop dérangés 😉.</p> <blockquote class="twitter-tweet" data-lang="fr"><p lang="en" dir="ltr">Disqus started injecting adverts into pages that use their comment system, like my blog. Their problem is that my CSP doesn't allow their adverts to be included 😎 <a href="https://t.co/c3lTAhCjS7">pic.twitter.com/c3lTAhCjS7</a></p>— Scott Helme (@Scott_Helme) <a href="https://twitter.com/Scott_Helme/status/961612668992966656?ref_src=twsrc%5Etfw">8 février 2018</a></blockquote> <p>Un autre risque, et pas le moindre : même si votre site Web est moins sujet aux problèmes de sécurité, il peut quand même être attaqué au travers de ses appels à des API. Vous devez absolument vous assurer que les scripts que vous utilisez <a href="https://blog.dareboost.com/fr/2017/01/analyse-sites-maj-bonnes-pratiques-dareboost/#chargement-de-ressources-provenant-de-tierces-parties">n’ont pas été altérés</a> et que chaque échange avec un service tiers est sécurisé via HTTPS. Assurez-vous également que vos API auto-hébergées sont conformes aux bonnes pratiques de sécurité et n’hésitez pas à inspecter minutieusement les garanties contractuelles des services tiers dont vous dépendez.</p> <p>Enfin, pensez à choisir vos API en fonction de la pérennité des organisations qui les exposent. Si vous transférez la responsabilité de vos fonctionnalités clés à des services tiers, vous feriez mieux de vous assurer qu’ils perdurent dans le temps et maintiennent un niveau de qualité constant. Une bonne façon de surveiller ces services est de recourir à l’exécution régulière de <a href="https://www.dareboost.com/fr/service/monitoring-parcours-utilisateur">Parcours Utilisateurs</a> qui sont capables de simuler les interactions fonctionnelles avec votre site web.</p> <h2 id="un-nouvel-horizon">Un nouvel horizon</h2> <p>Une fois que vous avez bien appréhendé les risques spécifiques et mis en place les workflows appropriés, le JAMStack présente de nombreuses opportunités.</p> <p>Le coût de migration d’une solution Headless CMS ou SSG vers une autre sont souvent très réduits. Vous pouvez facilement passer d’une contribution locale sur fichiers à une infrastructure de contenu comme Netlify CMS, Forestry, Contentful ou Prismic, ce qui vous permet d’évaluer rapidement la solution qui répond le mieux à vos besoins. Reste que la création d’un site statique prend du temps et nécessite une architecture capable d’orchestrer plusieurs solutions.</p> <p>Aujourd’hui, cela peut sembler complexe mais rappelez-vous votre premier site dynamique: choisir un hébergeur, maîtriser FTP, jonglez avec les logs du serveur web… ça n’était pas facile à apprendre non plus. Vous découvrirez cette nouvelle façon d’organiser votre projet étape par étape. Pour les utilisateurs expérimentés de la JAMStack, cela devient naturel.</p> <p>D’autant qu’en dépit du risque de centralisation qu’elles constituent, les plates-formes unifiées comme Netlify offrent un catalogue impressionnant de services : génération et déploiement de sites web, enregistrement DNS, gestion des certificats et formulaires SSL, fonctions lambdas, Content Delivery Network, etc.</p> <p>De quoi permettre à votre équipe de se concentrer sur le développement du Front-End et l’optimisation des performances Web. Avec un Time To First Byte aussi bas, l’équipe peut pleinement se concentrer sur l’UX grâce à la mesure du <a href="https://blog.dareboost.com/en/2018/02/speed-index-web-performance/">Speed Index</a> et la maitrise des interactions utilisateurs.</p> <hr/> <p><em>Mes remerciements à Erin Symons, <a href="https://twitter.com/dirtyf">Frank Taillandier</a> et toute la <a href="https://jamstatic.fr/">communauté jamstatic.fr</a>, <a href="https://twitter.com/budparr">Bud Parr</a>, <a href="https://twitter.com/Nico3333fr">Nicolas Hoffmann</a> et mes collègues <a href="https://twitter.com/GuilbertPhil">Philippe Guilbert</a> et <a href="https://twitter.com/DamienJubeau">Damien Jubeau</a> pour leur temps et leurs conseils.</em></p></p>
  447. </article>
  448. </section>
  449. <nav id="jumpto">
  450. <p>
  451. <a href="/david/blog/">Accueil du blog</a> |
  452. <a href="https://borisschapira.com//2018/02/site-statique-performance-web/">Source originale</a> |
  453. <a href="/david/stream/2019/">Accueil du flux</a>
  454. </p>
  455. </nav>
  456. <footer>
  457. <div>
  458. <img src="/static/david/david-larlet-avatar.jpg" loading="lazy" class="avatar" width="200" height="200">
  459. <p>
  460. Bonjour/Hi!
  461. 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>
  462. 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>).
  463. </p>
  464. <p>
  465. 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>.
  466. </p>
  467. <p>
  468. Voici quelques articles choisis :
  469. <a href="/david/blog/2019/faire-equipe/" title="Accéder à l’article complet">Faire équipe</a>,
  470. <a href="/david/blog/2018/bivouac-automnal/" title="Accéder à l’article complet">Bivouac automnal</a>,
  471. <a href="/david/blog/2018/commodite-effondrement/" title="Accéder à l’article complet">Commodité et effondrement</a>,
  472. <a href="/david/blog/2017/donnees-communs/" title="Accéder à l’article complet">Des données aux communs</a>,
  473. <a href="/david/blog/2016/accompagner-enfant/" title="Accéder à l’article complet">Accompagner un enfant</a>,
  474. <a href="/david/blog/2016/senior-developer/" title="Accéder à l’article complet">Senior developer</a>,
  475. <a href="/david/blog/2016/illusion-sociale/" title="Accéder à l’article complet">L’illusion sociale</a>,
  476. <a href="/david/blog/2016/instantane-scopyleft/" title="Accéder à l’article complet">Instantané Scopyleft</a>,
  477. <a href="/david/blog/2016/enseigner-web/" title="Accéder à l’article complet">Enseigner le Web</a>,
  478. <a href="/david/blog/2016/simplicite-defaut/" title="Accéder à l’article complet">Simplicité par défaut</a>,
  479. <a href="/david/blog/2016/minimalisme-esthetique/" title="Accéder à l’article complet">Minimalisme et esthétique</a>,
  480. <a href="/david/blog/2014/un-web-omni-present/" title="Accéder à l’article complet">Un web omni-présent</a>,
  481. <a href="/david/blog/2014/manifeste-developpeur/" title="Accéder à l’article complet">Manifeste de développeur</a>,
  482. <a href="/david/blog/2013/confort-convivialite/" title="Accéder à l’article complet">Confort et convivialité</a>,
  483. <a href="/david/blog/2013/testament-numerique/" title="Accéder à l’article complet">Testament numérique</a>,
  484. et <a href="/david/blog/" title="Accéder aux archives">bien d’autres…</a>
  485. </p>
  486. <p>
  487. 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>.
  488. </p>
  489. <p>
  490. Je ne traque pas ta navigation mais mon
  491. <abbr title="Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33.184162340">hébergeur</abbr>
  492. conserve des logs d’accès.
  493. </p>
  494. </div>
  495. </footer>
  496. <script type="text/javascript">
  497. ;(_ => {
  498. const jumper = document.getElementById('jumper')
  499. jumper.addEventListener('click', e => {
  500. e.preventDefault()
  501. const anchor = e.target.getAttribute('href')
  502. const targetEl = document.getElementById(anchor.substring(1))
  503. targetEl.scrollIntoView({behavior: 'smooth'})
  504. })
  505. })()
  506. </script>