-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathslides_assets_and_webpacker.html
More file actions
306 lines (287 loc) · 19.6 KB
/
slides_assets_and_webpacker.html
File metadata and controls
306 lines (287 loc) · 19.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Slides for
Web Performance, Assets and Webpacker — Ruby on Rails Guides
</title>
<link rel="stylesheet" type="text/css" href="stylesheets/style.css" data-turbo-track="reload">
<link rel="stylesheet" type="text/css" href="stylesheets/print.css" media="print">
<link rel="stylesheet" type="text/css" href="stylesheets/highlight.css" data-turbo-track="reload">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="stylesheets/reset.css">
<link rel="stylesheet" href="stylesheets/reveal.css">
<link rel="stylesheet" href="stylesheets/myslide.css" id="theme">
<link rel="stylesheet" href="stylesheets/code.css">
<script src="javascripts/clipboard.js" data-turbo-track="reload"></script>
<script src="javascripts/slides.js" data-turbo-track="reload"></script>
</head>
<body>
<div class="reveal">
<!-- Any section element inside of this container is displayed as a slide -->
<div class="slides">
<section>
<h1>Web Performance, Assets and Webpacker</h1><p>A web site consist of many more files than just the
HTML documents we have been generating up to now:
css files, javascript files, image files, font files, ...</p><p>Webpacker is Rails' way of preparing
theses files for publication using current
frontend tools.</p><p>By referring to this guide, you will be able to:</p>
<ul>
<li>keep your assets in the right place</li>
<li>have all your assets compiled and minified for production</li>
<li>by using the Rails Asset Pipeline for CSS and image</li>
<li>by using Webpacker for JavaScript</li>
</ul>
<p><small>Slides - use arrow keys to navigate, esc to return to page view, f for fullscreen</small></p>
</section>
<section><a class='slide_break' href='assets_and_webpacker.html#slide-0'>▻</a>
<h2 id="web-performance"><a class="anchorlink" href="#web-performance"><span>1</span> Web Performance</a></h2><p>What do we mean by 'web performance'? From the viewpoint of one user,
the crucial value is the time it takes from requesting a page (by clicking a link
or button, or typing in an URL) to having the page displayed and interactive in your browser.
We will call this the 'response time'.</p><p>From the publishers point of view it might also encompass the question of
how many users you can serve (with acceptable response time) on a given
server. If you look at the question of how to serve more users in case
of more demand you enter the realm of 'scalability'. This is a more advanced
question that goes beyond the scope of this guide.</p></section>
<section><a class='slide_break' href='assets_and_webpacker.html#slide-1'>▻</a>
<h3 id="myths-about-performance"><a class="anchorlink" href="#myths-about-performance"><span>1.1</span> Myths About Performance</a></h3><p>If you have never studied this subject you might still have
an intuition about where performance problems come from.
Many beginners are fascinated by details of their programming
language like: <code>will using more variables make my program slower?</code>
or <code>is string concatenation faster than string interpolation?</code>.</p><p>These 'micro optimizations' are hardly ever necssary with modern
programming languages and computers. Using Rails, Postgres and a modern
hosting service you will have no trouble serving hundreds and thousands
of users a day and achieving adequate performance for all of them.</p></section>
<section><a class='slide_break' href='assets_and_webpacker.html#slide-2'>▻</a>
<h3 id="permature-optimization"><a class="anchorlink" href="#permature-optimization"><span>1.2</span> Permature Optimization</a></h3><p>Trying to 'optimize' you code if there is no problem, or
if you don't know where the problem is,
will make your code worse, not better.</p><p>Donald Knuth stated this quite forcefully:</p><p>"The real problem is that programmers have spent far too much time worrying about efficiency in the wrong places and at the wrong times; <strong>premature optimization is the root of all evil</strong>" -- <a href="https://en.wikiquote.org/wiki/Donald_Knuth#Computer_Programming_as_an_Art_.281974.29">Donald Knuth</a></p><p>Only after you have measured the performance indicators that are
relevant to your project, and only after you have found out
which part of the system is causing theses indicators to go over
the threshold of acceptable values, only then can you truly
start to 'optimize'.</p></section>
<section><a class='slide_break' href='assets_and_webpacker.html#slide-3'>▻</a>
<h3 id="measuring-web-performance"><a class="anchorlink" href="#measuring-web-performance"><span>1.3</span> Measuring Web Performance</a></h3><p>The "exceptional performance" group at Yahoo published the browser addon
<code>yslow</code> in 2007. It first measured performance and displayed the timing
of the different HTTP connections as a "waterfall graph":</p><p><img src="images/network-souders-2008.png" alt="displaying http downloads with yslow"></p><p>(Image from Steve Souders <a href="https://conferences.oreilly.com/web2expo/webexsf2008/public/schedule/detail/3321">talk at Web 2.0 Expo</a> in April 2008)</p><p>Each bar is one resource being retrieved via HTTP, the x-axis
is a common timeline for all. The most striking result you can read from
this graph: the backend is only responsible for 5% of the time in this
example! 95% of time is spent loading and parsing javascript and css files
and loading and displaying images!</p><p>This graph was later integrated into the developer tools
of several browsers, and into the online tool <a href="https://catchpoint.com/webpagetest">webpagetest</a>.</p><p><strong>Firefox</strong></p><p><img src="images/network-view-firefox.png" alt="network view in firefox"></p><p><strong>Chrome</strong></p><p><img src="images/network-view-chrome.png" alt="network view in chrom"></p></section>
<section><a class='slide_break' href='assets_and_webpacker.html#slide-4'>▻</a>
<h3 id="rules"><a class="anchorlink" href="#rules"><span>1.4</span> Rules...</a></h3><p>Yahoo first published 14 rules for web performance in 2007, based
on the measurements back then:</p>
<ul>
<li>Make Less HTTP Requests</li>
<li>Use a Content Delivery Network</li>
<li>Avoid empty src or href</li>
<li>Add an Expires or a Cache-Control Header</li>
<li>Gzip Components</li>
<li>Put StyleSheets at the Top</li>
<li>Put Scripts at the Bottom</li>
<li>Avoid CSS Expressions...</li>
<li>Make JavaScript and CSS External</li>
<li>Reduce DNS Lookups</li>
<li>Minify JavaScript and CSS</li>
<li>Avoid Redirects</li>
<li>Remove Duplicate Scripts</li>
</ul>
<p>Even with changing browsers and improving HTTP 1 to HTTP 2 and now HTTP 3 / QUIC
some of these are still very valid today.
But as a web developer you should always keep an eye on the changing
landscape of web performance! These rules and their priority will change!</p></section>
<section><a class='slide_break' href='assets_and_webpacker.html#slide-5'>▻</a>
<h2 id="how-rails-helps-with-performance"><a class="anchorlink" href="#how-rails-helps-with-performance"><span>2</span> How Rails helps with Performance</a></h2></section>
<section><a class='slide_break' href='assets_and_webpacker.html#slide-6'>▻</a>
<h3 id="static-files-are-fastest"><a class="anchorlink" href="#static-files-are-fastest"><span>2.1</span> Static files are fastest</a></h3><p>The first thing to know, is that assets do not need to be
served through the rails stack, but should be served by the
web server directly. In the following diagram they are
called 'static files':</p><p><img src="images/rails-mvc.svg" alt="MVC in Rails"></p><p>In production these static css, js, svg, gif, webp, ... files are found in the <code>public</code> folder.</p><p>In development we will write other files
that need to be compiled, optimized and / or concatenated to
create the static files.</p>
<ul>
<li>Compile to JavaScript (e.g. typescript, coffeescript,...)</li>
<li>Compile to CSS (e.g. LESS, SASS)</li>
<li>Minify and combine several JavaScript files into one</li>
<li>Minify and combine several CSS files into one</li>
<li>Optimize images</li>
<li>Create several versions of pixel images</li>
<li>Create CSS Sprites</li>
</ul>
</section>
<section><a class='slide_break' href='assets_and_webpacker.html#slide-7'>▻</a>
<h3 id="rails-asset-pipeline"><a class="anchorlink" href="#rails-asset-pipeline"><span>2.2</span> Rails Asset Pipeline</a></h3><p>In a Rails 6 project The Rails Asset Pipeline handles everything except JavaScript.</p>
<ul>
<li>you put CSS (and less, sass, scss) files in <code>app/assets/stylesheets/*</code></li>
<li>you configure which CSS files are built and included in <code>app/assets/stylesheets/application.css</code></li>
<li>you put images in <code>app/assets/images/*</code></li>
<li>files for publishing are automatically created in <code>public/assets/*</code></li>
</ul>
</section>
<section><a class='slide_break' href='assets_and_webpacker.html#slide-8'>▻</a>
<h3 id="webpacker"><a class="anchorlink" href="#webpacker"><span>2.3</span> Webpacker</a></h3><p>Since Rails 6 webpack and yarn are included with Rails.
These frontend tools are used to generate the JavaScript,
and can be used for other assets.</p><p>The gem that handles the setup of webpack is called <code>webpacker</code>,
you can find it in the <code>Gemfile</code>.</p><p>JavaScript packages are installed using <code>yarn install --check-files</code>. Just like
<code>npm</code>, <code>yarn</code> reads the list of packages to install form <code>package.json</code> and
installs them to the folder <code>node_modules</code>. The lockfile for <code>yarn</code> is called <code>yarn.lock</code>.
You should never find a <code>package-lock.json</code> file in your Rails folder.</p></section>
<section><a class='slide_break' href='assets_and_webpacker.html#slide-9'>▻</a>
<h3 id="rails-environments"><a class="anchorlink" href="#rails-environments"><span>2.4</span> Rails Environments</a></h3><p>Building assets works differently in different Rails Environments.
There are three environments that exist by default:</p>
<ul>
<li><code>development</code>
<ul>
<li>this is the environment you have been working in until now,</li>
<li>it is optimized for debugging, shows error messages and the error console.</li>
</ul></li>
<li><code>testing</code>
<ul>
<li>this is used for running the <a href="testing.html">automatic tests</a>.</li>
</ul></li>
<li><code>production</code>
<ul>
<li>this is how the finished app will run after it is published,</li>
<li>it is optimized for speed and stability</li>
</ul></li>
</ul>
<p>How each envirnoments behaves is configured in files in <code>config/environments/*.rb</code>.</p><p>The development environment is used by default on your machine. If you deploy
to heroku or to another hosting server, production will be used there.</p></section>
<section><a class='slide_break' href='assets_and_webpacker.html#slide-10'>▻</a>
<h3 id="rails-environments-and-assets"><a class="anchorlink" href="#rails-environments-and-assets"><span>2.5</span> Rails Environments and Assets</a></h3><p>In <code>development</code> no assets will be written to <code>public/</code>. Instead
these files will be created on the fly by the Asset Pipeline and by <code>webpack-dev-server</code>.</p><p>If you look at the output of <code>rails s</code> or the logfile <code>log/development.log</code>
you will see messages from <code>webpack-dev-server</code> when this happens:</p><div class="interstitial code">
<pre><code class="highlight plaintext">[Webpacker] Compiling...
[Webpacker] Compiled all packs in /Users/bjelline/teach-dev/backend-assign/a4_job_board/public/packs
[Webpacker] Hash: 83233949a2f44e57ae52
Version: webpack 4.44.2
Time: 1345ms
Built at: 08.12.2020 19:36:18
Asset Size Chunks Chunk Names
js/application-cd9baa997ab2a6e5febb.js 70.9 KiB application [emitted] [immutable] application
js/application-cd9baa997ab2a6e5febb.js.map 80.2 KiB application [emitted] [dev] application
manifest.json 364 bytes [emitted]
Entrypoint application = js/application-cd9baa997ab2a6e5febb.js js/application-cd9baa997ab2a6e5febb.js.map
[./app/javascript/channels sync recursive _channel\.js$] ./app/javascript/channels sync _channel\.js$ 160 bytes {application} [built]
[./app/javascript/channels/index.js] 211 bytes {application} [built]
[./app/javascript/packs/application.js] 717 bytes {application} [built]
[./node_modules/webpack/buildin/module.js] (webpack)/buildin/module.js 552 bytes {application} [built]
+ 2 hidden modules
</code></pre>
<button class="clipboard-button" data-clipboard-text="[Webpacker] Compiling...
[Webpacker] Compiled all packs in /Users/bjelline/teach-dev/backend-assign/a4_job_board/public/packs
[Webpacker] Hash: 83233949a2f44e57ae52
Version: webpack 4.44.2
Time: 1345ms
Built at: 08.12.2020 19:36:18
Asset Size Chunks Chunk Names
js/application-cd9baa997ab2a6e5febb.js 70.9 KiB application [emitted] [immutable] application
js/application-cd9baa997ab2a6e5febb.js.map 80.2 KiB application [emitted] [dev] application
manifest.json 364 bytes [emitted]
Entrypoint application = js/application-cd9baa997ab2a6e5febb.js js/application-cd9baa997ab2a6e5febb.js.map
[./app/javascript/channels sync recursive _channel\.js$] ./app/javascript/channels sync _channel\.js$ 160 bytes {application} [built]
[./app/javascript/channels/index.js] 211 bytes {application} [built]
[./app/javascript/packs/application.js] 717 bytes {application} [built]
[./node_modules/webpack/buildin/module.js] (webpack)/buildin/module.js 552 bytes {application} [built]
+ 2 hidden modules
">Copy</button>
</div>
<p>When you deploy to production, the assets will be built and stored in <code>public/</code>.</p><p>If you look at the generated HTML code on the production server,
you will only find two links: in production
the many css files have been concatenated into one <code>application*.css</code>, and
all JavaScript files have been concatenated into one <code>application*.js</code>:</p><div class="interstitial code">
<pre><code class="highlight plaintext"><link rel="stylesheet" media="all" href="/assets/application-1ea07225edcc7e47.css"/>
<script src="/assets/application-58af49959ef0.js"></script>
</code></pre>
<button class="clipboard-button" data-clipboard-text="<link rel="stylesheet" media="all" href="/assets/application-1ea07225edcc7e47.css"/>
<script src="/assets/application-58af49959ef0.js"></script>
">Copy</button>
</div>
<p>You can also try out the production environment on your own machine:</p>
<ul>
<li>start the web server: <code>rails server -e production</code></li>
<li>rails console: <code>rails console -e production</code></li>
<li>other rails commands: add <code>RAILS_ENV=production</code> at the beginning or the end of the command.</li>
</ul>
</section>
<section><a class='slide_break' href='assets_and_webpacker.html#slide-11'>▻</a>
<h3 id="fingerprinting-for-better-expiry"><a class="anchorlink" href="#fingerprinting-for-better-expiry"><span>2.6</span> Fingerprinting for better Expiry</a></h3><p>The filenames mentioned in the last chapter all contain a part that seems random:</p>
<ul>
<li>you named the file <code>slider.css</code></li>
<li>but it shows up as <code>slider-974d585dcb6f5aec673164664a4e49d5.css</code> in development</li>
<li>and is part of <code>application-1ea07225edcc7e47.css</code> in production</li>
</ul>
<p>Where do the extra characters come from and what do they mean?</p><p>These extra characters are the "fingerprint". It is computed from the full
content of the file. If only one byte changes in the file, the fingerprint will
be different.</p><p>This enables a neat trick concerning caching: You can set the expiry time
to infinite, every browser can save the file forever and never try to reload it.
If the contents of the file change, a new file with a new fingerprint in the name will
be generated, and the HTML-page will link to that file.</p><p>This way we avoid one the the <a href="https://twitter.com/codinghorror/status/506010907021828096">two hard problems in computer science</a>: cache invalidation.</p></section>
<section><a class='slide_break' href='assets_and_webpacker.html#slide-12'>▻</a>
<h2 id="user-generated-content"><a class="anchorlink" href="#user-generated-content"><span>3</span> User Generated Content</a></h2><p>The asset pipeline handles assets that are
added by developers during development.
Images uploaded by users in production
are handled by <a href="https://edgeguides.rubyonrails.org/active_storage_overview.html">activestorage</a>.</p><p>When you are using a PAAS to deploy your app there is no simple
way of storing uploaeded data: the <a href="https://12factor.net/build-release-run">release</a> cannot be
changed and should be deposable. You need a <a href="https://12factor.net/backing-services">backing service</a>
for storing files. This can be another cloud service like S3 (storage only) or <a href="https://cloudinary.com/">cloudinary</a> (storage and image processing)</p></section>
<section><a class='slide_break' href='assets_and_webpacker.html#slide-13'>▻</a>
<h2 id="using-webpacker-for-images-and-css"><a class="anchorlink" href="#using-webpacker-for-images-and-css"><span>4</span> Using webpacker for images and css</a></h2>
<ul>
<li><a href="https://stackoverflow.com/questions/57748953/importing-css-with-webpacker-in-rails-6#answer-57750067">CSS with Webpacker</a></li>
<li><a href="https://rossta.net/blog/importing-images-with-webpacker.html">Images with Webpacker</a></li>
</ul>
</section>
<section><a class='slide_break' href='assets_and_webpacker.html#slide-14'>▻</a>
<h2 id="further-reading"><a class="anchorlink" href="#further-reading"><span>5</span> Further Reading</a></h2>
<ul>
<li>Souders(2007): High Performance Web Sites. O'Reilly. ISBN-13: 978-0596529307.</li>
<li>Souders(2009): Even Faster Web Sites. O'Reilly. ISBN-13: 978-0596522308.</li>
<li><a href="https://calendar.perfplanet.com/2018/">The Web Performance (Advent) Calendar</a> new every year</li>
</ul>
</div></section>
</div>
</div>
<!-- End slides. -->
<!-- Required JS files. -->
<script src="javascripts/reveal.js"></script>
<script src="javascripts/search.js"></script>
<script src="javascripts/markdown.js"></script>
<script>
// Also available as an ES module, see:
// https://revealjs.com/initialization/
Reveal.initialize({
controls: false,
progress: true,
center: false,
hash: true,
// The "normal" size of the presentation, aspect ratio will
// be preserved when the presentation is scaled to fit different
// resolutions. Can be specified using percentage units.
width: 1000,
height: 600,
disableLayout: false,
// Factor of the display size that should remain empty around
// the content
margin: 0.05,
// Bounds for smallest/largest possible scale to apply to content
minScale: 0.2,
maxScale: 10.0,
keyboard: {
27: () => {
// do something custom when ESC is pressed
var new_url = window.location.pathname.replace('slides_', '') + window.location.hash.replace('/','slide-');
window.location = new_url;
},
191: 'toggleHelp',
13: 'next', // go to the next slide when the ENTER key is pressed
},
// Learn about plugins: https://revealjs.com/plugins/
plugins: [ RevealSearch, RevealMarkdown ]
});
</script>
</body>
</html>