<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Filip Rakowski</title>
    <description>The latest articles on DEV Community by Filip Rakowski (@filrakowski).</description>
    <link>https://dev.to/filrakowski</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F93872%2F88351a4b-7ae1-4d6f-8265-4d34020ca7a9.png</url>
      <title>DEV Community: Filip Rakowski</title>
      <link>https://dev.to/filrakowski</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/filrakowski"/>
    <language>en</language>
    <item>
      <title>Investing in Developer Experience Could Be The Best Growth Hack for Your Business</title>
      <dc:creator>Filip Rakowski</dc:creator>
      <pubDate>Wed, 19 Apr 2023 11:17:52 +0000</pubDate>
      <link>https://dev.to/vue-storefront/investing-in-developer-experience-could-be-the-best-growth-hack-for-your-business-fnn</link>
      <guid>https://dev.to/vue-storefront/investing-in-developer-experience-could-be-the-best-growth-hack-for-your-business-fnn</guid>
      <description>&lt;p&gt;&lt;strong&gt;It was November 2017 when we pushed the very first version of Vue Storefront to GitHub. At that time, there were only two of us working on it after hours. One year later, we had been invited to almost every Magento event and podcast. Vue Storefront had almost 100 agencies around the world implementing it, and more than 4000 people were on its Slack. New live stores were popping up every week, and SAP had even approached us, asking for help to build its own Enterprise product.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;As crazy as it sounded, this was a real story. What was our secret ingredient to success? Developer Experience.&lt;/strong&gt; Despite having no investment or marketing budget, we managed to leverage Developer Experience to our advantage. The mechanisms that led to our success can be used by other IT companies, both building and consuming developer tools to greatly increase their chances of success.&lt;/p&gt;

&lt;h2&gt;
  
  
  Developer Experience is User Experience
&lt;/h2&gt;

&lt;p&gt;For a company building tools for developers, developers are their users, &lt;strong&gt;so Developer Experience is just User Experience&lt;/strong&gt;. It’s not a secret that improving the experience of your users can be a great way to increase revenue. A &lt;a href="https://www.usertesting.com/blog/customer-experience-roi"&gt;survey by UserTesting&lt;/a&gt; found that &lt;strong&gt;86% of buyers are willing to pay more for a better customer experience&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Some companies, like Amazon, have become completely obsessed with their customers and their experience. It works the same way with developers - we have companies like Vercel, Netlify that are known from their obsession with great DX! By the way - &lt;a href="https://vuestorefront.io/blog/developer-experience-obsession-as-new-cdxo-heres-why-"&gt;we're obsessed about it in Vue Storefront&lt;/a&gt; too!&lt;/p&gt;

&lt;h2&gt;
  
  
  Developer Experience drives web tools adoption
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BuHToCDn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ip5o7d0x5zq4u98hn6bc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BuHToCDn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ip5o7d0x5zq4u98hn6bc.png" alt="Image description" width="800" height="561"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Developers are constantly looking for new and better tools that will allow them to perform tasks more efficiently or decrease the chances of failure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The reason we became so successful with Vue Storefront was quite simple - we identified a pain point of a huge amount of developers and shipped something that improved their experience.&lt;/strong&gt; They lifted it up because our success was in their interest.&lt;/p&gt;

&lt;p&gt;Developers often look for tools that are easy to use, provide the required functionality, have good documentation, and have good community support. If a tool lacks these qualities, developers are more likely to switch to an alternative tool that provides a better experience. &lt;/p&gt;

&lt;p&gt;In our case, we gave Magento 2 frontend developers a way to use a modern frontend framework like Vue, and decouple their work from the PHP world. &lt;strong&gt;It was in their interest to advocate for Vue Storefront and convince the companies they worked for to use it.&lt;/strong&gt; This way, they could learn a new, exciting technology that is much more enjoyable and efficient to work with than what they were used to. &lt;/p&gt;

&lt;p&gt;Let’s take another example from the web dev tooling courtyard. Webpack, a tool that was fueling almost all modern JavaScript projects with 6 000 000 daily downloads was almost completely replaced by Vite in most of the modern frameworks and starters last year. Of course, Webpack won’t die any time soon, with its massive adoption, but Vite is now a go-to for the new projects on Vue, React and Nuxt.&lt;/p&gt;

&lt;p&gt;Speaking of which - Nuxt and Vue also became a market standards because of their amazing DX. They had no marketing budget, but happy developers advocating for their products were all they needed to get where they are now.&lt;/p&gt;

&lt;p&gt;I know what you’re thinking right now - is it really worth investing time in a migration just to make my developers happier? As a leader, or a business owner, should I even listen to their whims?&lt;/p&gt;

&lt;h2&gt;
  
  
  Developer Experience improves the efficiency of your team
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TggwxKZ4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k03rfvntmqw1fu8uhz0i.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TggwxKZ4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k03rfvntmqw1fu8uhz0i.jpeg" alt="Image description" width="800" height="343"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Using a developer tool with great developer experience is like driving on a well-maintained highway, where you can smoothly and efficiently cruise at high speeds towards your destination. &lt;/p&gt;

&lt;p&gt;On the other hand, using a tool with a poor developer experience is like driving on a bumpy and pothole-filled road, where you constantly have to slow down and maneuver around obstacles, causing delays and frustration. Ultimately, the former allows you to get to your destination faster and with less stress, while the latter can slow you down and impede your progress.&lt;/p&gt;

&lt;p&gt;Developers spend most of their time coding, designing, and testing software applications. The experience they have while doing these tasks can have a significant impact on their performance. Good developer experience can make them more productive and more efficient, leading to timely delivery of high-quality software.&lt;/p&gt;

&lt;p&gt;While a bad developer experience can lead to inefficiencies, delays, and ultimately, higher costs. This can also impact the end-user experience, leading to unhappy customers, bad reviews, and lost business opportunities.&lt;/p&gt;

&lt;p&gt;A &lt;a href="https://www.mckinsey.com/industries/technology-media-and-telecommunications/our-insights/developer-velocity-how-software-excellence-fuels-business-performance"&gt;report from McKinsey&lt;/a&gt; shows that &lt;strong&gt;companies prioritizing developer velocity have four to five times the revenue growth of their counterparts&lt;/strong&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  It’s not always worth migrating to the next big thing
&lt;/h2&gt;

&lt;p&gt;Investing in tools that can provide better developer experience is an excellent decision, especially for large teams. However, it's essential to consider that not all migrations are worthwhile. If your current tool is already delivering good developer experience, then it's highly unlikely that migrating to a new tool will bring significant benefits that justify the costs.&lt;/p&gt;

&lt;p&gt;For example, people migrated from webpack to Vite in large numbers because webpack was incredibly slow in larger projects, leading to a significant waste of time.&lt;/p&gt;

&lt;p&gt;However, Vite has addressed this problem by being fast enough, so replacing Vite with an even faster build tool may not provide the same benefits as migrating from webpack to Vite.&lt;/p&gt;

&lt;p&gt;Therefore, it's crucial to make a well-informed decision before making any changes to your current toolset.&lt;/p&gt;

&lt;h2&gt;
  
  
  Developer Experience attracts talent
&lt;/h2&gt;

&lt;p&gt;Good developer experience can also impact employee retention and recruitment. Developers want to work for companies that provide the best tools, technologies, and working environments. Often they switch jobs just to be able to work with the new technology they’re excited about.&lt;/p&gt;

&lt;p&gt;According to a survey by Hired, a job search platform for tech talent, outdated technology is one of the top reasons why developers look for new jobs. In their &lt;a href="https://pages.hired.email/rs/289-SIY-439/images/2021%20State%20of%20Software%20Engineering%20Report.pdf"&gt;2021 State of Software Engineers report&lt;/a&gt;, they found that &lt;strong&gt;39% of software engineers surveyed cited outdated technology as a reason for leaving their job&lt;/strong&gt;, making it the second most common reason behind compensation.&lt;/p&gt;

&lt;p&gt;By providing good developer experience, businesses can retain and attract top talent, leading to better results and growth opportunities.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Investing in Developer Experience (DX) can be a game-changer for IT companies building and consuming developer tools. *&lt;em&gt;DX is simply User Experience (UX) for developers, and improving it can increase revenue, drive adoption, and increase team efficiency. *&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;However, not all migrations are worthwhile, and it's crucial to make a well-informed decision before making any changes to your current toolset. Good DX can also impact employee retention and recruitment, leading to better results and growth opportunities. Overall, businesses should consider DX as a growth hack that can drive success and take them to the next level.&lt;/p&gt;




&lt;p&gt;I share how we build best Developer Experience on the market in Vue Storefront every month in my &lt;a href="https://vuestorefront.io/developer-newsletter"&gt;newsletter&lt;/a&gt;! Follow me on &lt;a href="https://twitter.com/filrakowski"&gt;Twitter&lt;/a&gt; or &lt;a href="https://www.linkedin.com/in/filip-rakowski-a43671129/"&gt;Linkedin&lt;/a&gt; to stay in the loop and learn something new!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>react</category>
      <category>vue</category>
    </item>
    <item>
      <title>How to deal with caching and dynamic content in Nuxt?</title>
      <dc:creator>Filip Rakowski</dc:creator>
      <pubDate>Wed, 05 Oct 2022 13:10:19 +0000</pubDate>
      <link>https://dev.to/vue-storefront/how-to-deal-with-caching-and-dynamic-content-2ilk</link>
      <guid>https://dev.to/vue-storefront/how-to-deal-with-caching-and-dynamic-content-2ilk</guid>
      <description>&lt;p&gt;&lt;strong&gt;The cache is one of the most powerful weapons when you want to make your web application fast. Delivering static, pre-rendered pages from the closest location can result in a great performance, but &lt;a href="https://dev.to/jacobandrewsky/leveraging-cache-in-vuejs-and-nuxtjs-4b26"&gt;setting it up on the server&lt;/a&gt; without making your frontend application ready to be cached can lead to unpleasant outcomes. In the best-case scenario, you will annoy your users by breaking the app. Worst-case scenario, you will violate GDPR rules. This is certainly a situation that none of us want to experience! Don't worry - from this article, you will learn everything you need to know to make your Server-Side or Statically generated Single Page Applications ready to be cached (and hopefully distributed through a CDN)!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;⚠️ &lt;strong&gt;Side Note:&lt;/strong&gt; I use Nuxt.js in code examples, but the concepts and solutions are not tied to any framework.&lt;/p&gt;

&lt;h2&gt;
  
  
  We cache HTML templates to improve performance.
&lt;/h2&gt;

&lt;p&gt;Let's learn some theory first and familiarize ourselves with the concept of full-page caching. I found a very good one on Branch CMS documentation:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Full page cache" means that the entire HTML output for the page will be cached. Subsequent requests for the page will return the cached HTML instead of trying to process and re-build the page, thus returning a response to the browser much faster&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When you open a Server-Side Rendered application, the JavaScript code first runs on the server to generate the HTML file containing all your components and data rendered. This static file is then sent to the browser. Single-Page Application framework like Vue takes control over it and makes it dynamic. This process is called hydration. At this stage your application is fully interactive.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fscxm9lm88cn6r25yumyp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fscxm9lm88cn6r25yumyp.png" alt="How Vue hydration works"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Generating the static HTML file on the server can take a few seconds. A few seconds when your user sees a blank screen and potentially leaves your website. &lt;strong&gt;According to &lt;a href="https://think.storage.googleapis.com/docs/mobile-page-speed-new-industry-benchmarks.pdf" rel="noopener noreferrer"&gt;Google's study&lt;/a&gt;, 1-3 seconds of load time increases the bounce rate probability by 32%!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To deal with this significant performance downside of Server-Side Rendering, developers started caching the first generated response and sending it to others. Thanks to that, we perform the time-consuming rendering step only once and send its outcome immediately to all users requesting it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The cached template has to be generic.
&lt;/h2&gt;

&lt;p&gt;When we cache an HTML page generated by a SPA SSR framework like Nuxt.js it contains both the generated HTML &lt;strong&gt;and application state as inlined JavaScript object&lt;/strong&gt; after the server-side code execution.&lt;/p&gt;

&lt;p&gt;Because of that &lt;strong&gt;you need to ensure that there is no session-specific content in both template and application state after server-side code execution.&lt;/strong&gt; Otherwise, it will be cached and served to all users.&lt;/p&gt;

&lt;p&gt;A session-specific content contains data and markup that can be different depending on a user or device - for example: displayed user or device name, conditional rendering statement for specific devices etc.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0eeun5v15n8dsfjccm8m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0eeun5v15n8dsfjccm8m.png" alt="Example of dynamic content"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you don’t remove session-specific content from the cached template, everyone will see a page with the data of the first user.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F75iaube2wvegndc8i37p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F75iaube2wvegndc8i37p.png" alt="Caching dynamic content makes everyone see the content of the first visitor"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Execute session-specific content in the browser
&lt;/h2&gt;

&lt;p&gt;Our applications are rarely entirely generic in the real world, though. There is nothing wrong with it - almost every website has some content that is dynamic and depends on a current user session.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;it’s important to make the dynamic content rendering happen in an environment that is isolated for each user like their browser&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This way the generic content, generated on the server is distributed immediately to all of your users and then, in their browsers, parts that make the experience tailored specifically for them are injected.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to deal with session-specific templates
&lt;/h2&gt;

&lt;p&gt;The first thing that comes to mind when thinking about session-specific content is the one of the currently authenticated user. We obviously don't want other users to receive a page that is filled with someone else's data.&lt;/p&gt;

&lt;p&gt;Let’s see an example of excluding session-specific parts of &lt;code&gt;AppHeader&lt;/code&gt; component from rendering on the server.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwxv1c7mnsj8vxc62fhei.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwxv1c7mnsj8vxc62fhei.png" alt="Dynamic content in AppHeader"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In Nuxt.js you can skip the server-side rendering of components wrapped with a built-in &lt;a href="https://nuxtjs.ir/api/components-client-only" rel="noopener noreferrer"&gt;ClientOnly&lt;/a&gt; component.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;

&lt;span class="c"&gt;&amp;lt;!-- components/AppHeader.vue --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;header&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;AppLogo&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- Keep the elements that are session-specific as client-only --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;ClientOnly&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;v-if=&lt;/span&gt;&lt;span class="s"&gt;"!isLoggedIn"&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;click=&lt;/span&gt;&lt;span class="s"&gt;"LogIn()"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Log in&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;v-else&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;click=&lt;/span&gt;&lt;span class="s"&gt;"logOut()"&lt;/span&gt; &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Log out&lt;span class="nt"&gt;&amp;lt;/butt&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;AppCart&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;AppWishlist&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/ClientOnly&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The server-side-rendered code of the above component will look more or less like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;header&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"./assets/logo.png"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Then in the browser, the rest of the elements are added dynamically.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7801rl4nonvdqfjl41e5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7801rl4nonvdqfjl41e5.png" alt="How static and dynamic content are rendered"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Improving the user experience with loading indicators and placeholders
&lt;/h3&gt;

&lt;p&gt;Usually, the hydration happens in milliseconds, but on some devices, it could take even more than 10 seconds. Seeing empty elements on the template can be misleading and deliver a bad user experience. Users can feel that the website is broken, or they could miss some important elements that are loaded later. We should let them know that some elements are not there yet.&lt;/p&gt;

&lt;p&gt;The most common and effective technique of indicating yet-to-be-loaded content are skeletons.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Skeleton screens &lt;em&gt;**are blank pages that are progressively populated with content, such as text and images, as they become available.&lt;/em&gt;* (from ***&lt;em&gt;uxdesign.cc)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://www.lukew.com/ff/entry.asp?1797" rel="noopener noreferrer"&gt;Skeletons are much better option than spinners&lt;/a&gt; because they give user a hint of what content they can expect.&lt;/p&gt;

&lt;p&gt;Let's take a look at a practical example of Linkedin. When you enter the page, not everything is loaded yet. You immediately receive a cached skeleton home screen, and the data is loaded progressively in the browser:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnnazxgcx1z1s03o7qyxz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnnazxgcx1z1s03o7qyxz.png" alt="Linkedin skeleton screens"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Nuxt.js &lt;code&gt;ClientOnly&lt;/code&gt; component has a placeholder slot we can use to display a skeleton for the content that will gradually load on the client side. I used &lt;a href="https://docs.storefrontui.io/?path=/docs/components-atoms-skeleton--common" rel="noopener noreferrer"&gt;SfSkeleton&lt;/a&gt; component example from &lt;a href="https://vuestorefront.io/storefront-ui" rel="noopener noreferrer"&gt;Storefront UI&lt;/a&gt; - eCommerce UI library we’ve built in Vue Storefront for our users.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;

&lt;span class="c"&gt;&amp;lt;!-- components/AppHeader.vue --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;header&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;AppLogo&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- Keep the elements that are session-specific as client-only --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;ClientOnly&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- Display a placeholder until the page is hydrated --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;template&lt;/span&gt; &lt;span class="na"&gt;#placeholder&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;SfSkeleton&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"paragraph"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;v-if=&lt;/span&gt;&lt;span class="s"&gt;"!isLoggedIn"&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;click=&lt;/span&gt;&lt;span class="s"&gt;"LogIn()"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Log in&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;v-else&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;click=&lt;/span&gt;&lt;span class="s"&gt;"logOut()"&lt;/span&gt; &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Log out&lt;span class="nt"&gt;&amp;lt;/butt&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;AppCart&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;AppWishlist&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/ClientOnly&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl6f53n7zxz2ls8xwdn54.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl6f53n7zxz2ls8xwdn54.png" alt="Skeleton in AppHeader"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;💡 Sometimes it is better to just use the generic state of the component as placeholder&lt;/p&gt;

&lt;h2&gt;
  
  
  What about the data?
&lt;/h2&gt;

&lt;p&gt;Until now we talked only about the session-specific templates but what about data fetched inside components?&lt;/p&gt;

&lt;p&gt;Most of the SSR frameworks like Nuxt or Next send the server-side state at the bottom of &lt;code&gt;index.html&lt;/code&gt; file that comes with the rendered HTML, so you don’t have to fetch the data twice - on the server and then on the client.&lt;/p&gt;

&lt;p&gt;If you inspect your Nuxt or Next SSR response, you will see a similar piece of code injected at the end of the &lt;code&gt;body&lt;/code&gt; tag:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;window.__NUXT__=&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;default&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:[{},{&lt;/span&gt;&lt;span class="na"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;:[{&lt;/span&gt;&lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sunt aut facere repellat provident occaecati excepturi optio reprehenderit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;quia et suscipit&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;suscipit recusandae consequuntur expedita et cum&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;reprehenderit molestiae ut ut quas totam&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;nostrum rerum est autem sunt rem eveniet architecto&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},{&lt;/span&gt;&lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;qui est esse&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;est rerum tempore vitae&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;sequi sint nihil reprehenderit dolor beatae ea dolores neque&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;fugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;qui aperiam non debitis possimus qui neque nisi nulla&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},{&lt;/span&gt;&lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ea molestias quasi exercitationem repellat qui ipsa sit aut&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;et iusto sed quo iure&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;voluptatem occaecati omnis eligendi aut [...]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}]}],&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;serverRendered&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Because of that, we have to ensure that no session-specific data is in the cached HTML just like we did with the templates.&lt;/p&gt;

&lt;p&gt;The data can be fetched from two places:&lt;/p&gt;

&lt;h3&gt;
  
  
  Inside the component
&lt;/h3&gt;

&lt;p&gt;When the data is fetched inside the component, the case is simple. &lt;strong&gt;If the whole component is wrapped with &lt;code&gt;ClientOnly&lt;/code&gt; like in the example above the execution of the component code is completely skipped on the server&lt;/strong&gt;, so the data is fetched for the first time when its executed in the browser.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;

&lt;span class="c"&gt;&amp;lt;!-- components/AppHeader.vue --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;header&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;AppLogo&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- None of the code from components inside ClientOny will execute on the server --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;ClientOnly&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;AppCart&lt;/span&gt; &lt;span class="na"&gt;:items=&lt;/span&gt;&lt;span class="s"&gt;"user.cart.items"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/ClientOnly&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;


&lt;span class="c"&gt;&amp;lt;!-- components/AppCart.vue --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;template&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- cart template --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;setup&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;({})&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;fetchCart&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchCart&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If you’re REST APIs and have multiple requests on your page, executing the fetching logic inside the components is best. It’s much harder to miss the session-specific data if it’s not all around your app.&lt;/p&gt;

&lt;h3&gt;
  
  
  In the parent component
&lt;/h3&gt;

&lt;p&gt;If you follow a smart/dumb components pattern aka. presentational/container (explained well by Dan Abramov &lt;a href="https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0" rel="noopener noreferrer"&gt;here&lt;/a&gt;,  and no longer a go-to approach since the introduction of React Hooks/Composition API) and fetch all the logic in the parent component, you have to take care of the session-specific data separately in the parent component.&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;

&lt;p&gt;&lt;span class="c"&gt;&amp;lt;!-- components/AppHeader.vue --&amp;gt;&lt;/span&gt;&lt;br&gt;
&lt;span class="nt"&gt;&amp;lt;AppHeader&amp;gt;&lt;/span&gt;&lt;br&gt;
  &lt;span class="nt"&gt;&amp;lt;AppLogo&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;&lt;br&gt;
    &lt;span class="c"&gt;&amp;lt;!-- None of the code from components inside ClientOny will execute on the server --&amp;gt;&lt;/span&gt;&lt;br&gt;
  &lt;span class="nt"&gt;&amp;lt;ClientOnly&amp;gt;&lt;/span&gt;&lt;br&gt;
    &lt;span class="c"&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;br&gt;
    &lt;span class="nt"&gt;&amp;lt;AppCart&lt;/span&gt; &lt;span class="na"&gt;:items=&lt;/span&gt;&lt;span class="s"&gt;"user.cart.items"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;&lt;br&gt;
  &lt;span class="nt"&gt;&amp;lt;/ClientOnly&amp;gt;&lt;/span&gt;&lt;br&gt;
&lt;span class="nt"&gt;&amp;lt;/AppHeader&amp;gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;&lt;br&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;({})&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;fetchCart&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;br&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;br&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;br&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="c1"&gt;// onMounted runs only in the browser (client context)&lt;/span&gt;&lt;br&gt;
&lt;span class="nf"&gt;onMounted&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;br&gt;
 &lt;span class="nx"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchCart&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;br&gt;
&lt;span class="p"&gt;})&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="c1"&gt;// ...other logic&lt;/span&gt;&lt;br&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;&lt;/p&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Summary&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;To make your app cacheable and CDN-ready you have to avoid session-specific content in the cached content rendered on the server.&lt;/p&gt;

&lt;p&gt;You have to render your application in a few steps - a generic one that renders most of the page and a session-specific one that injects dynamic parts.&lt;/p&gt;




&lt;p&gt;Liked the article? Follow me on &lt;a href="https://twitter.com/filrakowski" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; to get daily tips about web development.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>vue</category>
      <category>react</category>
    </item>
    <item>
      <title>Everything you need to know about Web Performance (in 5 Minutes)</title>
      <dc:creator>Filip Rakowski</dc:creator>
      <pubDate>Thu, 23 Jun 2022 15:00:53 +0000</pubDate>
      <link>https://dev.to/vue-storefront/everything-you-need-to-know-about-web-performance-as-a-dev-in-5-minutes-450l</link>
      <guid>https://dev.to/vue-storefront/everything-you-need-to-know-about-web-performance-as-a-dev-in-5-minutes-450l</guid>
      <description>&lt;p&gt;&lt;strong&gt;I hear a lot of people saying that web performance is hard. Honestly, I don't think that's true. It could feel complex and intimidating at first glance because there is a lot of domain-specific naming, metrics, etc but to build a fast website you don't need to know them. You only need a basic understanding of what influences the speed of your website the most and make sure you have it under control. Believe me or not, you can learn this in about 5 minutes. Let's see if I'm right!&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What influences your app performance?
&lt;/h2&gt;

&lt;p&gt;Let's start with identifying all the aspects that influence your app performance. I find this mental model most useful when thinking about web performance:&lt;/p&gt;

&lt;p&gt;There are essentially three "steps" that sum up the overall loading performance of your app&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Server-side execution&lt;/strong&gt; - First the HTML document has to be generated on the server. In some cases, this step costs us nothing because it's already generated (static sites).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Network&lt;/strong&gt; - The generated HTML document has to travel through wires and routers to arrive in the user's browser.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Client-side execution&lt;/strong&gt; - The document needs to be parsed, and dependencies (CSS, JavaScript) have to be downloaded and executed. Once it's all done our page is fully loaded.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzejj3z1nnu6rrxwg9i7y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzejj3z1nnu6rrxwg9i7y.png" alt="Response Journey"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Optimizing server-side execution
&lt;/h2&gt;

&lt;p&gt;If you're building a SPA (&lt;strong&gt;Single Page Application&lt;/strong&gt;) there is a high chance you're also adopting SSR (&lt;strong&gt;Server-Side Rendering&lt;/strong&gt;). In that case, the same code will run both on the server and the client sides.&lt;/p&gt;

&lt;p&gt;The best code is the one that never has to run so you should first consider SSG (Static Site Generation). If it's not an option and you're sticking to SSR, make heavy use of full-page caching and distribute cached content through CDN.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgk250d9agmxwpd6g6cbs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgk250d9agmxwpd6g6cbs.png" alt="The fastest code is the one you don't need to run"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Some pages will have to be generated on the server during runtime and just cannot be cached. Of those, make sure to fetch only fast, essential data on the server and make less important, and slower API calls on the client-side. This way you will significantly improve your Time to First Byte.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optimizing Network
&lt;/h2&gt;

&lt;p&gt;Optimizing the networking part boils down to 4 main rules:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Ship the smallest possible assets.&lt;/strong&gt; The bigger they are, the longer it takes to download them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Avoid chaining network requests&lt;/strong&gt; (making one request depending on another) and try to download them in parallel.Avoid using multiple external domains in the critical path. Establishing a connection with all of them will take more time than downloading everything from one source.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache static assets&lt;/strong&gt; (HTML, CSS JS) through a Service Worker.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9va5rci6lghh7iy2hlzd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9va5rci6lghh7iy2hlzd.png"&gt;&lt;/a&gt;&lt;br&gt;Source: &lt;a href="https://developer.chrome.com/blog/app-shell/" rel="noopener noreferrer"&gt;https://developer.chrome.com/blog/app-shell/&lt;/a&gt;
  &lt;/p&gt;

&lt;p&gt;If you take care of that there is a much smaller chance you will run into performance bottlenecks on the network part.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optimizing client execution
&lt;/h2&gt;

&lt;p&gt;This is where we, frontend developers, have the most power and where we also make a lot of mistakes! From my experience, 90% of frontend performance bottlenecks are around &lt;strong&gt;rendering time&lt;/strong&gt; (that one can be easily solved with cached SSR and SSG output) and &lt;strong&gt;interactivity&lt;/strong&gt; (that one can be solved by reducing amount of JavaScript in so-called critical rendering path through code splitting, lazy loading and being cautious with adding new libs to the clients).&lt;/p&gt;

&lt;p&gt;The thing that usually leads to the biggest number of performance bottlenecks is JavaScript. In SPAs it's very easy to lose control over your JS bundle size. Here's what you can do to prevent it from growing into a Brontosaurus:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If you're using SSR/SSG it means that many of your components are already rendered on the server and they don't need interactivity on the frontend. You can drastically increase the speed of your hydration by hydrating only the components that need to be interactive and only when they need to become ones. You can use Astro.build or vue-lazy-hydration plugin if you're using Nuxt to control the hydration process and exclude the components that don't need it.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw1c8yucbd4mdr8dgjlgx.png" alt="Hydrate on Scroll"&gt;
&lt;/li&gt;
&lt;li&gt;Split your app into multiple lazy-loaded chunks (start with routes!). Every sidebar, modal or expensive widget can be loaded lazily on interaction.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7yim098yr3gxy8npaa1p.png" alt="Code Split"&gt;
&lt;/li&gt;
&lt;li&gt;Your website could seem fast when you're building it but once the marketing team puts all the analytics there I guarantee it will slow down. You can use web workers to run the non-critical code asynchronously. I strongly recommend &lt;a href="https://github.com/nuxt-community/partytown-module" rel="noopener noreferrer"&gt;Partytown&lt;/a&gt; - it's integrated with all major frameworks from the Vue ecosystem.

&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvz29r5xd99k9sqdyub00.png"&gt;Source: &lt;a href="https://partytown.builder.io/" rel="noopener noreferrer"&gt;https://partytown.builder.io/&lt;/a&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Optimizing images
&lt;/h2&gt;

&lt;p&gt;Surprising amount of developers does a rookie mistake of not optimizing their images. To make sure images aren't the bottleneck simply adjust their size to the screen and use next-gen formats like webp. You can automatically resize and optimize your images using &lt;a href="https://image.nuxtjs.org/" rel="noopener noreferrer"&gt;&lt;/a&gt; and/or &lt;a href="https://cloudinary.com/" rel="noopener noreferrer"&gt;Cloudinary&lt;/a&gt;. Also, load your below-the-fold images last. You can use native &lt;code&gt;&amp;lt;img loading="lazy" /&amp;gt;&lt;/code&gt; property for that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measuring performance
&lt;/h2&gt;

&lt;p&gt;If you can't measure – you can't say if there was any improvement. Measuring your performance constantly is as important as optimizing it regularly.&lt;/p&gt;

&lt;p&gt;The performance metrics that have the biggest impact on user experience are called &lt;strong&gt;Core Web Vitals (CVV)&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Largest Contentful Paint (LCP)&lt;/strong&gt;: measures loading performance. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;First Input Delay (FID)&lt;/strong&gt;: measures interactivity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cumulative Layout Shift (CLS)&lt;/strong&gt;: measures visual stability. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs74t6f06szzlr2qcncgw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs74t6f06szzlr2qcncgw.png"&gt;&lt;/a&gt;&lt;br&gt;Source: &lt;a href="https://web.dev/vitals/?gclid=Cj0KCQjw2MWVBhCQARIsAIjbwoN567MYvlge9gXipQmZGvQG-9WZddKuQXRto_NiuIaHaTXStrjNFx0aApY9EALw_wcB" rel="noopener noreferrer"&gt;https://web.dev/vitals/?gclid=Cj0KCQjw2MWVBhCQARIsAIjbwoN567MYvlge9gXipQmZGvQG-9WZddKuQXRto_NiuIaHaTXStrjNFx0aApY9EALw_wcB&lt;/a&gt;
  &lt;/p&gt;

&lt;p&gt;If you want to quickly check how your website is performing, try &lt;a href="https://pagespeed.web.dev/" rel="noopener noreferrer"&gt;Page Speed Insights&lt;/a&gt;. It will run a Lighthouse audit on your website using the closest Google Data Center.&lt;/p&gt;

&lt;p&gt;You should also incorporate performance checks into your CI/CD pipeline. Use &lt;a href="https://github.com/GoogleChrome/lighthouse-ci" rel="noopener noreferrer"&gt;Lighthouse CI&lt;/a&gt; to run a synthetic Lighthouse test on each PR (PS: &lt;a href="https://dev.to/vue-storefront/youre-probably-using-lighthouse-wrong-how-we-got-tricked-by-a-single-magic-number-1laj"&gt;Learn why&lt;/a&gt; you shouldn't believe the Lighthouse score alone) and &lt;a href="https://www.npmjs.com/package/bundlesize" rel="noopener noreferrer"&gt;bundlesize&lt;/a&gt; package to raise alerts if your bundle size exceeds a certain threshold. For more nuanced data you should use &lt;a href="https://www.webpagetest.org/" rel="noopener noreferrer"&gt;WebPageTest&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7x1ss0t9uynul0gr7f9a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7x1ss0t9uynul0gr7f9a.png" alt="Bundle Analysis"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Believe me or not but that's all you need to know to have your performance under control!&lt;/p&gt;




&lt;p&gt;If you liked the article and want to learn more about web performance through articles and tips you can follow Vue Storefront profile here or &lt;a href="https://twitter.com/filrakowski" rel="noopener noreferrer"&gt;follow me on Twitter&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>performance</category>
      <category>javascript</category>
      <category>react</category>
    </item>
    <item>
      <title>You’re probably using Lighthouse wrong: How we got tricked by a single magic number</title>
      <dc:creator>Filip Rakowski</dc:creator>
      <pubDate>Wed, 13 Apr 2022 11:59:51 +0000</pubDate>
      <link>https://dev.to/vue-storefront/youre-probably-using-lighthouse-wrong-how-we-got-tricked-by-a-single-magic-number-1laj</link>
      <guid>https://dev.to/vue-storefront/youre-probably-using-lighthouse-wrong-how-we-got-tricked-by-a-single-magic-number-1laj</guid>
      <description>&lt;p&gt;&lt;strong&gt;These days web performance is one of the most important things everyone wants to optimize on their apps, and it's clear to everyone how dramatic the impact of a poorly optimized website is on business. Yet we as an industry completely fail in recognizing its complexity and widely misuse the most common tool to measure it — Google Lighthouse. If you’re one of those people thinking that good performance equals a good Lighthouse score, you’ve also fallen into this trap and this article is for you.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You may not believe me, but five years ago, when we were writing the first lines of code for &lt;a href="https://github.com/vuestorefront/vue-storefront" rel="noopener noreferrer"&gt;Vue Storefront&lt;/a&gt;, the topic of frontend performance was almost non-existent in the web-dev space.&lt;/p&gt;

&lt;p&gt;At that time, JavaScript SPA frameworks were gaining huge traction. AngularJS and React were already well-established tools gaining in popularity every day. Evan You just released the second major version of Vue.js that only a few years later became one of the three most popular SPA frameworks in the world.&lt;/p&gt;

&lt;p&gt;At that time almost no one cared how fast the websites built with those tools were. The world was simple and if you wanted to dig into frontend performance topics, you ended up on websites with retro designs made by old computer geeks. &lt;strong&gt;The mainstream didn’t care, but this was about to change very soon because of the little friends in our pockets.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As long as we were using PCs and laptops as primary machines to consume the web no one seemed to be concerned with the growing size of websites. Both CPU and internet bandwidth were progressing faster than websites were growing their size.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftykxz1ylid9gbwfkqgwk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftykxz1ylid9gbwfkqgwk.png" alt="Timeseries of Total Kilobytes"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This all changed when mobile started to become the preferred way of consuming the web.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;According to Google Research in 2017, it took on average 15.3 seconds to fully load a web page on a mobile phone. The impact of poor mobile performance on the business started to become clear to everyone, but we were still lacking an easy way to link those two components.&lt;/p&gt;

&lt;p&gt;Everything changed when Google Lighthouse started gaining popularity. I remember when it was introduced and became rapidly adopted in the eCommerce space following Progressive Web Apps hype around 2018. Everyone was obsessed with PWAs, everyone was obsessed with Web Performance, and almost no one knew anything about both. Unfortunately, not much has changed since then.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What makes Lighthouse so widely adopted is its simplicity.&lt;/strong&gt; You run a test, and get a number between 1 and 100 that tells you how good or bad the web performance of your website is. Everyone, even those without a technical background, can understand that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The reality is not that simple, though, and web performance or user experience cannot be represented by a single number.&lt;/strong&gt; In addition, there are a lot of nuances around how a Lighthouse audit works, and to use it as a reliable source of knowledge, you have to be aware of them. You won’t read about these things in the audit summary but don’t worry. In this article, I’ll dig into the most important ones.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting obsessed with one metric
&lt;/h2&gt;

&lt;p&gt;Let’s start with a simple question. &lt;strong&gt;What does Google Lighthouse measure, and how can we use this information?&lt;/strong&gt; I have a feeling that many people don’t try to answer it and just blindly assume that the score has to be high to be right.&lt;/p&gt;

&lt;p&gt;As we can read on its website, &lt;strong&gt;the goal of Google Lighthouse is to measure “page quality”.&lt;/strong&gt; The audit divides the quality into performance, accessibility, best practices, and SEO. All of those combined should give a good perspective on the quality of the website and, by that, try to accurately predict real end-user experience. “Try to predict” is accurate here because the audit will not give you any definitive answers about your users' experience. Some tools will, though, and I will talk about them in the end.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Google has always promoted the performance score as the most important one.&lt;/strong&gt; In the heads of the general audience, “Lighthouse score” equals “performance score”, so “quality page” means “page with a high-performance score”.&lt;/p&gt;

&lt;p&gt;Don’t get me wrong here. Performance is definitely a major factor influencing page quality and end-user experience, but the fact that we got so obsessed with just one of the four metrics gives the false impression that the only thing that matters to the end-user is performance, and if we get it right, our users will have a delightful experience. &lt;strong&gt;In reality, there are so many factors influencing good user experience that even all four metrics of Lighthouse can’t give you a definitive answer if it's good or bad.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Do you even know where this number comes from?
&lt;/h2&gt;

&lt;p&gt;Making business decisions based on a raw number without broader context usually leads to bad decisions, but you can make even worse ones if you don’t even know where this number comes from.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Let me quickly explain how the Lighthouse score is calculated&lt;/strong&gt; to make sure we are all on the same page.&lt;/p&gt;

&lt;p&gt;The Lighthouse score is calculated from a bunch of other metrics’ scores. Each of them has its own weight - some of them are more important, some of them less. You can check how individual metrics contribute to the score on &lt;a href="https://googlechrome.github.io/lighthouse/scorecalc/" rel="noopener noreferrer"&gt;this website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F26b97u21l3sryb4l9y1f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F26b97u21l3sryb4l9y1f.png" alt="Lighthouse Scoring Calculator"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The algorithm changes with each version. This is nothing unusual - the more Google explores the impact of individual metrics on the end-user experience, the more accurate the weights are. &lt;strong&gt;This is why it makes no sense to compare your current Lighthouse score to the one from a year or two ago.&lt;/strong&gt;  Most likely, the scoring system has changed during that time. It could’ve improved or decreased because the algorithm changed, not because you changed something.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbmhd4tpekakj26cu67wg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbmhd4tpekakj26cu67wg.png" alt="Lighthouse Algorithm changes"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I’ve seen many people who made that mistake and were convinced that their website was slower than before (even though it wasn’t). When I asked them why they think it is, they showed me a one-year-old Lighthouse score and compared it to the most recent one.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem with local testing
&lt;/h2&gt;

&lt;p&gt;Let’s dig deeper into understanding where the magical performance score comes from. We already know half of the truth behind the number, but we still don’t know about the second most important thing — the environment the test is performed in.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Most people use Chrome Devtools to run their Lighthouse audits, and this is probably the least reliable way of doing it.&lt;/strong&gt; There are multiple external factors that influence the score. The download and rendering speed depends on your CPU and network. The developers will usually run the test on their shiny Macbooks with 5G internet and in most cases have better results than real users. In addition, your browser extensions are also treated as part of the website you’re auditing.&lt;/p&gt;

&lt;p&gt;We can decrease the impact of these factors by running the test in Incognito mode, which will exclude Chrome extensions, and by applying CPU and network throttling, but we will still see different results on different devices.&lt;/p&gt;

&lt;p&gt;Running Lighthouse locally is not reliable if you want to compare the results with anyone else and is definitely not reliable if you want to tell if the supposed end-user experience will be good or bad. &lt;strong&gt;Everyone in the company can run the benchmark, and everyone will have a different result.&lt;/strong&gt; It’s hard to tell if there was an improvement or if the opposite happened after releasing the new version if the tests were performed in different environments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You should always test your website in the same environment and limit the external factors&lt;/strong&gt; that may influence the score as much as possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Page Speed Insights
&lt;/h2&gt;

&lt;p&gt;You can have more consistent results if you set up &lt;a href="https://github.com/GoogleChrome/lighthouse-ci" rel="noopener noreferrer"&gt;Lighthouse CI&lt;/a&gt; in an external environment to test your page or use tools like &lt;a href="https://www.speedcurve.com/" rel="noopener noreferrer"&gt;SpeedCurve&lt;/a&gt;, but if you need to quickly inspect a website, I suggest taking a look at &lt;a href="https://pagespeed.web.dev/" rel="noopener noreferrer"&gt;Page Speed Insights&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It’s a website made by Google itself, and you can use it to perform quick Lighthouse tests on any website using the closest unoccupied Google Data Center instead of your local machine.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F62j76mc7sqnlolpbjidq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F62j76mc7sqnlolpbjidq.png" alt="Earth map with PinPoints in Europe, North America, South America and Asia"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Page Speed Insights usually uses the data center that is the closest one to your location&lt;/strong&gt;, but sometimes it could use another one if the closest is under heavy load. This is why you could sometimes get slightly different results on the same page in tests made one after another but it’s still much better than using your own laptop.&lt;/p&gt;

&lt;p&gt;Even though the PSI score will be more consistent, it’s still far from being an accurate measurement of real user experience. Page Speed Insights will run a mobile Lighthouse test on emulated Moto G4, which is a typical mid-range smartphone. Unless all of your users have it, they’re gonna experience the website differently.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lighthouses can be easily tricked
&lt;/h2&gt;

&lt;p&gt;Another problem with Lighthouse is the fact that it is just an algorithm. It gets input, processes the data, and gives output. &lt;strong&gt;If we know how it works, we can easily cheat it, and that happens more often than you think!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can easily find a lot of articles like &lt;a href="https://www.matuzo.at/blog/building-the-most-inaccessible-site-possible-with-a-perfect-lighthouse-score/" rel="noopener noreferrer"&gt;this one&lt;/a&gt; showing how to build a website that is getting a perfect Lighthouse score in a particular category while delivering a horrible experience.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6xwtphj5or34xjcemhq9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6xwtphj5or34xjcemhq9.png" alt="Screen Shot of the article Building the most inaccessible site possible with a perfect Lighthouse Score"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It’s equally easy to trick the performance score.&lt;/strong&gt; You can detect the Lighthouse user agent and serve a different version of your website for auditing tools.&lt;/p&gt;

&lt;p&gt;In fact, there are companies out there doing exactly that! If the score is suspiciously high compared to the website's complexity and real loading experience, there is a high chance someone is trying to trick you. We’ve seen that not that long ago in the eCommerce space where one of the PWA frontend solutions - ScandiPWA &lt;a href="https://twitter.com/Igloczek/status/1416056228095082499" rel="noopener noreferrer"&gt;tried to cheat their clients&lt;/a&gt; with a fake Lighthouse score.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1416056228095082499-295" src="https://platform.twitter.com/embed/Tweet.html?id=1416056228095082499"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1416056228095082499-295');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1416056228095082499&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;As a side note, things like that wouldn’t happen if all of us didn’t so blindly believe that this score is so important.&lt;/p&gt;

&lt;h2&gt;
  
  
  So when does it make sense to use Lighthouse?
&lt;/h2&gt;

&lt;p&gt;By reading all of that you can get the impression that in my opinion, Lighthouse is useless. This is definitely not my point here! I think it’s a wonderful tool and the fact that Google is trying to find an easy way to help developers identify potential performance bottlenecks impacting user experience is worth supporting. &lt;strong&gt;The problem is not in the tool itself but in the way how we use it or, to be more precise, misuse it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I have no doubts that Google Lighthouse has contributed to a faster web more than any other tool before, but the name “Lighthouse” has its purpose. Its goal is to guide on improving the page quality, not give definitive answers if it's good or bad. I’ve seen websites with a great user experience and low Lighthouse scores and vice versa. Good performance is a tradeoff. You always have to sacrifice something to make it better. Sometimes it’s an analytics script, sometimes it's a feature. It’s not always a good business decision to get rid of some of them to have better performance.&lt;/p&gt;

&lt;p&gt;To me, Lighthouse shines the most when we want to quickly compare different versions of our website to see if there was an improvement or decrease in its performance. It’s definitely worth implementing Lighthouse checks in your CI/CD pipelines with Lighthouse CI.&lt;/p&gt;

&lt;p&gt;You should also audit websites with similar complexity from your industry to get a sense of the realistic score in your case that you should be aiming for. &lt;strong&gt;An eCommerce website rarely scores above 60 while a blog often hits 100.&lt;/strong&gt; It’s important to know what is a “good” score in your case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measuring real user performance
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Lighthouse is not a good tool to measure actual user experiences.&lt;/strong&gt; Synthetic data will never tell you anything about that. To understand how users are experiencing your website, you have to get the data from… your users.&lt;/p&gt;

&lt;p&gt;You don’t have to set up any additional monitoring tools to check that! If you audit your page on Page Speed Insights at the very top you will see how it is scoring against the four most important performance metrics (and that will also affect your SEO results).&lt;/p&gt;

&lt;p&gt;This data is collected from the past 30 days on devices using Chrome among real users of your website so keep that in mind when comparing the score before and after the update. The only requirement for collecting this data is to make your website crawlable.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fesajrxsdd2m8wcu0zc9v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fesajrxsdd2m8wcu0zc9v.png" alt="Screenshot of PageSpeed Insights Mobile"&gt;&lt;/a&gt;&lt;br&gt;
 &lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw0jrrj9tq3lg8xp0ueoi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw0jrrj9tq3lg8xp0ueoi.png" alt="Dashboard showing Core Web Vitals"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This real-world data comes from a &lt;strong&gt;Chrome User Experience Report (CrUX)&lt;/strong&gt; which collects performance metrics from real user devices using Google Chrome (by default it’s turned on on every Chrome browser). You can easily get access to the history of your metric in &lt;a href="https://web.dev/chrome-ux-report-data-studio-dashboard/" rel="noopener noreferrer"&gt;CrUX Dashboard in Google Data Studio&lt;/a&gt;. (edit: Piotr Grzywa posted a &lt;a href="https://rviscomi.github.io/crux-dash-launcher/" rel="noopener noreferrer"&gt;link&lt;/a&gt; in the comment to generate the CrUX Dashboard for your website automatically)&lt;/p&gt;

&lt;p&gt;If you do a PSI benchmark on a few websites, you will quickly notice that in many cases, there is no relation between a Lighthouse score and performance data from real users. For example, below, you can find a screenshot from an audit of &lt;a href="https://www.adidas.pl/" rel="noopener noreferrer"&gt;adidas.pl&lt;/a&gt; (performed some time ago). Even though the Lighthouse score is rather high, the real-world performance is terrible.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhsyh6nemi5fq0qjtlj1h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhsyh6nemi5fq0qjtlj1h.png" alt="Adidas.pl lighthouse score"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is why you shouldn’t blindly optimize the Lighthouse score and always look for real user data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Lighthouse is a great tool if it is used in the right way. You can (and should) use it to get a general sense of the performance of your website. No other tool will give you so much ready-to-process information with just a few clicks.&lt;/p&gt;

&lt;p&gt;You just need to be aware of where this data comes from and what exactly it represents so you can make conscious decisions based on it. Not everything that Lighthouse will identify as a bottleneck is an actual bottleneck and it’s not always worth optimizing your performance at all costs.&lt;/p&gt;

&lt;p&gt;Lighthouse is best at identifying if there was an improvement or decrease in the performance of a specific website, but the actual score could have nothing to do with actual performance experienced by real-world users. To learn about this, you need to collect real user data.&lt;/p&gt;

&lt;p&gt;If you liked this post you can follow me here or on &lt;a href="https://twitter.com/home" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; to stay up to date with new articles! &lt;/p&gt;

</description>
      <category>webdev</category>
      <category>vue</category>
      <category>performance</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Vue during coffee break - Transform any Vue application into offline-ready PWA in 5 minutes.</title>
      <dc:creator>Filip Rakowski</dc:creator>
      <pubDate>Thu, 18 Apr 2019 09:49:33 +0000</pubDate>
      <link>https://dev.to/vue-storefront/vue-during-coffee-break-transform-any-vue-application-into-offline-ready-pwa-in-5-minutes-31dm</link>
      <guid>https://dev.to/vue-storefront/vue-during-coffee-break-transform-any-vue-application-into-offline-ready-pwa-in-5-minutes-31dm</guid>
      <description>&lt;p&gt;The purpose of this series is to post small tips about advanced Vue concepts that can be quickly applied into every application and give you a new weapon to approach problems.&lt;/p&gt;

&lt;p&gt;In this short article I will explain how to transform existing Vue application into PWA or how to set up new one.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are Progressive Web Apps?
&lt;/h2&gt;

&lt;p&gt;In short words Progressive Web App (PWA) is a web application that works and behaves like a native one. &lt;/p&gt;

&lt;p&gt;Some of PWA capabilities are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ability to work offline&lt;/li&gt;
&lt;li&gt;homescreen installation&lt;/li&gt;
&lt;li&gt;support for push notifications&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want to know more i strongly suggest reading &lt;a href="https://developers.google.com/web/progressive-web-apps/" rel="noopener noreferrer"&gt;this&lt;/a&gt; document. Now let's write some code.&lt;/p&gt;

&lt;h2&gt;
  
  
  PWA Module for Nuxt
&lt;/h2&gt;

&lt;p&gt;If you're using Nuxt adding PWA features works the same for new and existing apps.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;(optional) If it's a new project set it up with &lt;code&gt;create-nuxt-app&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx create-nuxt-app &amp;lt;project-name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Install @nuxtjs/pwa module
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm i @nuxtjs/pwa
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Register module in &lt;code&gt;nuxt.config.js&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;modules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nuxtjs/pwa&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;(optional) Create &lt;code&gt;static/icon.png&lt;/code&gt; (recommended 512x512px) which will be used as a homescreen icon for your app.&lt;/li&gt;
&lt;li&gt;(optional) Add &lt;code&gt;sw.*&lt;/code&gt; rule to &lt;code&gt;.gitignore&lt;/code&gt; file to avoid commiting files generated by Nuxt module.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Nuxt PWA module is in fact a set of smaller PWA submodules. Let's give them a look after we're done with the installation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Workbox&lt;/strong&gt; - under the hood Nuxt PWA module is using Workbox in &lt;code&gt;generateSW&lt;/code&gt; mode (You can find configuration options &lt;a href="https://developers.google.com/web/tools/workbox/modules/workbox-cli#generateSW" rel="noopener noreferrer"&gt;here&lt;/a&gt;) which means it will automatically generate us a service Worker file that will take care of caching our static assets. Every file from your dist directory will be cached for offline usage. This module works out of the box&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manifest&lt;/strong&gt; - Automatically generates manifest.json file. This module works out of the box but can be configured via &lt;code&gt;manifest&lt;/code&gt; property of your &lt;code&gt;nuxt.config.js&lt;/code&gt; (&lt;a href="https://pwa.nuxtjs.org/modules/manifest.html" rel="noopener noreferrer"&gt;read more&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Meta&lt;/strong&gt; - Automatically adds SEO friendly meta data with manifest integration. (&lt;a href="https://pwa.nuxtjs.org/modules/meta.html" rel="noopener noreferrer"&gt;read more&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Icon&lt;/strong&gt; - Automatically generates app icons with different sizes. (&lt;a href="https://pwa.nuxtjs.org/modules/icon.html" rel="noopener noreferrer"&gt;read more&lt;/a&gt;). This module works out of the box but can be configured via &lt;code&gt;icon&lt;/code&gt; property of your &lt;code&gt;nuxt.config.js&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OneSignal&lt;/strong&gt; - Free background push notifications using OneSignal. OneSignal is a platform that allows to easily send push notifications to the user. You can read how to configure this module &lt;a href="https://pwa.nuxtjs.org/modules/onesignal.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Vue-cli PWA plugin
&lt;/h1&gt;

&lt;p&gt;If you're using vue-cli 3.x installation is even easier.&lt;/p&gt;

&lt;p&gt;For new projects after running&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vue create &amp;lt;project_name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;select &lt;code&gt;Manually select features&lt;/code&gt; on the first step and then check &lt;code&gt;Progressive Web Apps&lt;/code&gt; with spacebar. &lt;/p&gt;

&lt;p&gt;After finishing the installation process along with standard files generated by vue-cli you will find &lt;code&gt;registerServiceWorker.js&lt;/code&gt; and &lt;code&gt;manifest.json&lt;/code&gt;. You can customize behavior of the plugin under &lt;code&gt;pwa&lt;/code&gt; property of your &lt;code&gt;vue.config.js&lt;/code&gt; and under &lt;code&gt;pwa.workboxOptions&lt;/code&gt; you can customize underlying Workbox plugin in &lt;code&gt;generateSW&lt;/code&gt; mode (same that we have seen in Nuxt).&lt;/p&gt;

&lt;p&gt;For already existing projects on vue-cli 3 installation of &lt;code&gt;@vue/pwa&lt;/code&gt; plugin will have exactly the same effect. You can add PWA capabilities to your app by simply typing&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vue add @vue/pwa
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Other Projects
&lt;/h1&gt;

&lt;p&gt;If you're not using Nuxt or vue-cli 3.x you can still transform your application into offline-ready PWA with just a few commands by using &lt;a href="https://developers.google.com/web/tools/workbox/modules/workbox-cli" rel="noopener noreferrer"&gt;Workbox CLI&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;First you need to install the cli:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install workbox-cli --global
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next in the root of your project we should make use of a wizard that will generate Service Worker for us:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;workbox wizard
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After answering prompted questions wizard will generate a &lt;code&gt;workbox-config.js&lt;/code&gt; file that will be used to generate a Service Worker! &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdevelopers.google.com%2Fweb%2Ftools%2Fworkbox%2Fimages%2Fmodules%2Fworkbox-cli%2Fcli-wizard.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdevelopers.google.com%2Fweb%2Ftools%2Fworkbox%2Fimages%2Fmodules%2Fworkbox-cli%2Fcli-wizard.png"&gt;&lt;/a&gt;&lt;br&gt;
You can generate your service worker with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;workbox generateSW workbox-config.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those simple steps can significantely boost your application performance so they're certainly worth a try ;)&lt;/p&gt;

&lt;p&gt;Stay tuned for the next parts of the series!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>vue</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Vue during coffee break - using v-model with custom components.</title>
      <dc:creator>Filip Rakowski</dc:creator>
      <pubDate>Mon, 08 Apr 2019 13:13:00 +0000</pubDate>
      <link>https://dev.to/vue-storefront/vue-during-coffee-break-using-v-model-with-custom-components-3bo9</link>
      <guid>https://dev.to/vue-storefront/vue-during-coffee-break-using-v-model-with-custom-components-3bo9</guid>
      <description>&lt;p&gt;The purpose of this series is to post tips &amp;amp; tricks about advanced Vue concepts that can be quickly applied to every application and give you a new weapon to approach problems.&lt;/p&gt;

&lt;p&gt;In this short article, I will explain how &lt;code&gt;v-model&lt;/code&gt; works and how it can be applied to every Vue component.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding v-model
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;v-model&lt;/code&gt; is a common directive used in almost every Vue application. It's typically used to enable two-way data binding on form elements and works perfectly with &lt;code&gt;input&lt;/code&gt;, &lt;code&gt;checkbox&lt;/code&gt;, &lt;code&gt;select&lt;/code&gt;, &lt;code&gt;textarea&lt;/code&gt; and &lt;code&gt;radio&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In below example, &lt;code&gt;v-model&lt;/code&gt; applied on the &lt;code&gt;input&lt;/code&gt; element binds &lt;code&gt;someVal&lt;/code&gt; variable with native &lt;code&gt;value&lt;/code&gt; property of the input.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;v-model=&lt;/span&gt;&lt;span class="s"&gt;"someVal"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the directive listens for native &lt;code&gt;input&lt;/code&gt; event and updates &lt;code&gt;someVal&lt;/code&gt; every time it's emitted.&lt;/p&gt;

&lt;p&gt;So it turns out that we can rewrite the above code to well-known events and props with the same effect:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt;
  &lt;span class="na"&gt;v-bind:value=&lt;/span&gt;&lt;span class="s"&gt;"someVal"&lt;/span&gt;
  &lt;span class="na"&gt;v-on:input=&lt;/span&gt;&lt;span class="s"&gt;"someVal = $event.target.value"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is how &lt;code&gt;v-model&lt;/code&gt; applied to regular input works under the hood.&lt;/p&gt;

&lt;p&gt;Knowing this we can use &lt;code&gt;v-model&lt;/code&gt; on every component that will emit &lt;code&gt;input&lt;/code&gt; event and accept a &lt;code&gt;value&lt;/code&gt; prop.&lt;/p&gt;

&lt;p&gt;Take a look at this &lt;code&gt;MagicCounter&lt;/code&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;template&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;click=&lt;/span&gt;&lt;span class="s"&gt;"changeValue(value-1)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;-&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;{{ value }}&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;click=&lt;/span&gt;&lt;span class="s"&gt;"changeValue(value+1)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;+&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;changeValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newVal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;input&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newVal&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since we are emitting &lt;code&gt;input&lt;/code&gt; event with a new value each time it's changed and accepting the &lt;code&gt;value&lt;/code&gt; prop we can safely use &lt;code&gt;v-model&lt;/code&gt; directive on this component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;MagicCounter&lt;/span&gt; &lt;span class="na"&gt;v-model=&lt;/span&gt;&lt;span class="s"&gt;"count"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using v-model with custom components
&lt;/h2&gt;

&lt;p&gt;Event thought &lt;code&gt;input&lt;/code&gt; and &lt;code&gt;value&lt;/code&gt; pair is the default setup for &lt;code&gt;v-model&lt;/code&gt; depending on the input type, those bindings can be different (I strongly suggest checking &lt;a href="https://github.com/vuejs/vue/blob/6fe07ebf5ab3fea1860c59fe7cdd2ec1b760f9b0/src/platforms/web/compiler/directives/model.js"&gt;it's source code&lt;/a&gt; for details). For example in &lt;code&gt;checkbox&lt;/code&gt; element &lt;code&gt;checked&lt;/code&gt; property and &lt;code&gt;change&lt;/code&gt; event are used instead of default ones.&lt;/p&gt;

&lt;p&gt;It turns out that we customize the event/prop pair accepted by &lt;code&gt;v-model&lt;/code&gt; directive through a &lt;code&gt;model&lt;/code&gt; property. For example, this is how it could look like for &lt;code&gt;checkbox&lt;/code&gt; element:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;checked&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;change&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You might want to change the name of the event emitted by our &lt;code&gt;MagicCounter&lt;/code&gt; to be more descriptive (for example &lt;code&gt;modified&lt;/code&gt;). &lt;/p&gt;

&lt;p&gt;Let’s see how we can make this custom event work with &lt;code&gt;v-model&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;template&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;click=&lt;/span&gt;&lt;span class="s"&gt;"changeValue(value-1)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;-&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;{{ value }}&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;click=&lt;/span&gt;&lt;span class="s"&gt;"changeValue(value+1)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;+&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`modified`&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;changeValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newVal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;modified&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newVal&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;..and voilà! Now you know how to use &lt;code&gt;v-model&lt;/code&gt; with every Vue component. I hope you'll find a way to use this knowledge very soon .&lt;/p&gt;

&lt;p&gt;&lt;a href="https://codesandbox.io/s/0yo61k5zrl?fontsize=14"&gt;Here&lt;/a&gt; you can find a working example with a code from the post to play with.&lt;/p&gt;

&lt;p&gt;Stay tuned for the next parts of the series!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>vue</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
