<?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: Jennie</title>
    <description>The latest articles on DEV Community by Jennie (@jennieji).</description>
    <link>https://dev.to/jennieji</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%2F144179%2Ffb890d83-559f-4233-a3a2-cc006b66bba5.jpeg</url>
      <title>DEV Community: Jennie</title>
      <link>https://dev.to/jennieji</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jennieji"/>
    <language>en</language>
    <item>
      <title>3 facts of Web copy &amp; paste you may not know</title>
      <dc:creator>Jennie</dc:creator>
      <pubDate>Thu, 27 Feb 2025 20:10:07 +0000</pubDate>
      <link>https://dev.to/jennieji/3-facts-of-web-copy-paste-you-may-not-know-2407</link>
      <guid>https://dev.to/jennieji/3-facts-of-web-copy-paste-you-may-not-know-2407</guid>
      <description>&lt;p&gt;One day I accidentally opened a Pandora's box while trying to fix an ancient bug. The issue involved copy &amp;amp; paste functionality for items in our template editor (JSON under the hood) in Safari. My fix solved the original problem but created new ones in Windows and introduced other quirks in Safari. This fascinating journey made me relearn the complexity of copy &amp;amp; paste APIs—something I'd previously dismissed as just a single line of code throughout my career. 🙈&lt;/p&gt;

&lt;p&gt;We know that there are 2 ways to trigger copy &amp;amp; paste diagrammatically now:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;document.execCommand()&lt;/code&gt; - widely known but deprecated for years&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;navigator.clipboard&lt;/code&gt; APIs&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No matter in flexibility, security, and content support, &lt;code&gt;navigator.clipboard&lt;/code&gt; appears to be a better choice now, and the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API#browser_compatibility" rel="noopener noreferrer"&gt;browser support&lt;/a&gt; looks very promising. &lt;/p&gt;

&lt;p&gt;However, there are 3 things you may need to know when you want to work with it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Copy &amp;amp; paste event aren’t triggered
&lt;/h2&gt;

&lt;p&gt;In MDN it confuses us with:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Events are fired as the result of &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Element/cut_event" rel="noopener noreferrer"&gt;&lt;code&gt;cut&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Element/copy_event" rel="noopener noreferrer"&gt;&lt;code&gt;copy&lt;/code&gt;&lt;/a&gt;, and &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Element/paste_event" rel="noopener noreferrer"&gt;&lt;code&gt;paste&lt;/code&gt;&lt;/a&gt; operations modifying the clipboard.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;However, reading and writing content with &lt;code&gt;navigator.clipboard&lt;/code&gt; APIs won’t trigger the copy or paste events. Here is a demo (open console):&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/jennieji/embed/yyLJZzL?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;This makes sense as we are firing the copy &amp;amp; paste programmatically instead of interacting with the UI.&lt;/p&gt;

&lt;h2&gt;
  
  
  The unremovable? “Paste” menu in Safari
&lt;/h2&gt;

&lt;p&gt;Running the above example on Safari, you may find this interesting “Paste” menu popup on top of our “Paste” button if you copy the text with shortcut or context menu, and &lt;strong&gt;ONLY&lt;/strong&gt; trigger the click event if you dismissed this menu:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fgwkjlxais2cpk85pcp1w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fgwkjlxais2cpk85pcp1w.png" alt="Image description" width="800" height="666"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What makes things more fun is that if you copied by my “Copy” button, then click on “Paste” button, this menu won’t appear. ☹️&lt;/p&gt;

&lt;p&gt;Here is an reference of this issue: &lt;a href="https://developer.apple.com/forums/thread/133001" rel="noopener noreferrer"&gt;https://developer.apple.com/forums/thread/133001&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  System will modify your content
&lt;/h2&gt;

&lt;p&gt;Another fun fact was found when I copied content including line break and use as a source for comparison. That is Windows does modify your written text!&lt;/p&gt;

&lt;p&gt;Try this on Mac and Windows Chrome (Does not work on Edge because of security policy):&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/jennieji/embed/jENvaEE?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;If you copy text with &lt;code&gt;\n&lt;/code&gt; symbol on Windows, it will replace them with &lt;code&gt;\r\n&lt;/code&gt;: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F6zdyascu9ozkn8wyd6o6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F6zdyascu9ozkn8wyd6o6.png" alt="Image description" width="123" height="257"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The reason of this behavior was that &lt;code&gt;\r\n&lt;/code&gt; is the valid new line symbol on Windows instead of &lt;code&gt;\n&lt;/code&gt; . I guess the difference made some complains on Windows and then it decided to convert the content when user copy text.&lt;/p&gt;

&lt;p&gt;To be fair, this won’t create a big problem unless you are the unlucky person like me trying to do some comparison.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lastly
&lt;/h2&gt;

&lt;p&gt;Thanks for reading and hope these information make your day better.&lt;/p&gt;

</description>
      <category>clipboard</category>
      <category>javascript</category>
      <category>web</category>
    </item>
    <item>
      <title>Relocate to Australia and re-discover engineering culture</title>
      <dc:creator>Jennie</dc:creator>
      <pubDate>Sat, 08 Feb 2025 04:48:23 +0000</pubDate>
      <link>https://dev.to/jennieji/my-first-year-at-an-aussie-company-from-singapore-and-previously-china-1e0f</link>
      <guid>https://dev.to/jennieji/my-first-year-at-an-aussie-company-from-singapore-and-previously-china-1e0f</guid>
      <description>&lt;p&gt;Time flies—it has been 2.5 years since relocating from Singapore to Australia and 1.5 years at an Australian SaaS company. Though I had experience moving from China to Singapore 10 years ago, this transition took longer to adapt to. It was not only because I became a parent, but also because the cultural differences between Australia and Asia are more significant.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Challenging Time
&lt;/h2&gt;

&lt;p&gt;My husband received a job offer in Sydney just before Covid while I was working at the fast-growing Shopee. Back then, without our little one, everything seemed simpler, and we eagerly decided to embark on our next adventure.&lt;/p&gt;

&lt;p&gt;However, Australian visa processing was suspended for over two years. Like many couples, we welcomed a new family member during this special time.&lt;/p&gt;

&lt;p&gt;When we finally arrived in Sydney, we faced multiple crises: soaring rental prices, doubled petrol costs, and a shortage of childcare spots due to increased demand from the childcare subsidy policy, labor shortages, and higher birth rates. This forced me into an unwanted year as a housewife, watching our savings dwindle each month.&lt;/p&gt;

&lt;p&gt;When I finally secured childcare and started job hunting, both the market and I were at the low time. I had two years of broken sleep and minimal coding practice. Layoffs were still common and opening head counts were still little.&lt;/p&gt;

&lt;p&gt;SafetyCulture offered me an opportunity to excel. Later I learnt that I was their only front-end hire in that year.&lt;/p&gt;

&lt;h2&gt;
  
  
  On-boarding Challenges
&lt;/h2&gt;

&lt;p&gt;Our engineering team is divided into customer-facing teams and platform teams. All the teams are quite lean. Inevitably, a team sometimes lacked dedicated platform engineers when people were sick, on leave, or positions remained unfilled.&lt;/p&gt;

&lt;p&gt;I was replacing an engineer who had returned to Europe. She could only onboard me remotely in my evening hours or asynchronously. Other the engineers were either lacked context about the six-year-old messy front-end code base or on leave.&lt;/p&gt;

&lt;p&gt;Moreover, the outdated documentation and legacy code created unnecessary confusion. After all, the on-boarding documents hadn't needed updates for a year company-wide and over two years in our team.&lt;/p&gt;

&lt;p&gt;Before this, I was in a large web team. On-boarding ran smoothly with well-maintained documentation and plenty of mentor resources.&lt;/p&gt;

&lt;p&gt;Fortunately, a kind engineer from the platform team appeared unexpectedly to help. I soon learned that people here readily assist others, even at the cost of their own time—my first culture shock.&lt;/p&gt;

&lt;p&gt;In Asia, engineers tend to be reserved, especially those who aren't tech leads or managers. Most prefer coding to talking. Cross-team assistance was rare, and sacrificing time to help others wasn't celebrated in the organizational culture—only delivering impressive features was.&lt;/p&gt;

&lt;p&gt;Encouraged by the feedback-welcomed environment here, I took some notes and chat to the platform engineers. Only a few months after, the massive mono-repo documentation was beautifully revamped, earning praise widely from new hires.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Flexible (or Messy) Process
&lt;/h2&gt;

&lt;p&gt;Another aspect that confused me during on-boarding was the process. Each lean engineering team reports to an engineering manager, who reports to a director. The organization gives teams maximum flexibility to establish processes based on their business needs. Everyone is encouraged to shape the team through initiatives that gain team consensus. While this increases ownership and enables quick adaptation, it can disorient newcomers.&lt;/p&gt;

&lt;p&gt;In my first few months, I often felt uncertain about next steps, and no one seemed able to clearly define which situations called for which actions. &lt;/p&gt;

&lt;p&gt;Surprisingly, everything functioned well, with everyone displaying strong self-motivation. This contrasted with my previous experience, where managers or leads would define most processes, refining them over years based on feedback to reduce ambiguity. Most engineers would simply follow established procedures.&lt;/p&gt;

&lt;p&gt;Here, when dealing with ambiguity, we're not only encouraged to discuss with others but expected to contribute our own perspectives. Though decision-making takes longer, it leads to better solutions for various scenarios and ensures all voices are heard. This workplace values active communication over static documentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Re-understanding Engineering Manager and Leadership Roles
&lt;/h2&gt;

&lt;p&gt;While many leading IT companies started to separate engineering and management career tracks years ago, this was my first experience with such a system. &lt;/p&gt;

&lt;p&gt;I happily chose the engineering track due to my exhausting experience of managing technical responsibilities alongside team members without actual managerial authority. &lt;/p&gt;

&lt;p&gt;Yet it wasn’t a simple split.&lt;/p&gt;

&lt;p&gt;Task assignment and workload balancing become shared responsibilities between engineers and managers based on individual interests, particularly when multiple engineers maintain the same platform and team goals aren't assigned to specific individuals.&lt;/p&gt;

&lt;p&gt;Engineers need to put in significant effort to demonstrate the value of technical work when competing for priority against tech debt and product roadmap items. This is especially important since managers, even those with engineering backgrounds, may not fully understand the technical details of our work.&lt;/p&gt;

&lt;p&gt;The glue work is not reduced from our responsibilities. Instead, these essential activities - like mentoring others, improving documentation, or facilitating technical discussions - are actively recognized and celebrated within the organization. This represents a significant shift from environments where such work might go unnoticed, as the company culture here places high value on these contributions that help strengthen team cohesion and knowledge sharing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Re-understanding the Product Manager Role
&lt;/h2&gt;

&lt;p&gt;Product managers here operate differently. They focus on high-level product strategy rather than details— determining the product roadmap, priorities, and scope. &lt;/p&gt;

&lt;p&gt;Instead of PM, engineers will take the lead, carving detailed plans including milestones, tasks, acceptance criteria, edge cases, and handling options.&lt;/p&gt;

&lt;p&gt;Initially, I struggled to identify the right person for discussing issues and solutions. It wasn't a simple if-else scenario: UI/UX for designers, business logic for PMs, resources for project managers, and technical issues for tech leads/managers.&lt;/p&gt;

&lt;p&gt;After all, people in the same role work differently—some PMs focus on high-level strategy and delegate details to designers, while others take a more hands-on approach when the team needs decisions. A crucial skill is identifying the right "team" for efficient collaboration without disturbing others unnecessarily.&lt;/p&gt;

&lt;h2&gt;
  
  
  Re-understanding the Quality Engineer Role
&lt;/h2&gt;

&lt;p&gt;We use a quality engineer assistance model where multiple teams share one QE. This means QEs don't verify everything or conduct team regression testing. Engineers take greater responsibility for testing and rely more on automation. We rely on CI/CD pairing with diff coverage for faster delivery, while QEs help identify essential test cases and establish quality goals and metrics.&lt;/p&gt;

&lt;p&gt;The shift from strict, process-heavy quality control to this trust-based system affected me deeply—like a child's first independent outing. Each PR brought anxiety: Should I request QE testing? When? Are my test cases sufficient? What if I miss critical edge cases and cause production issues?&lt;/p&gt;

&lt;p&gt;Reviewing other engineers' PRs made the responsibility feel even more daunting. And eventually it pushed me to write better tests and accelerate development by removing QA bottlenecks. When issues arise in production, we have clear processes for both urgent incidents and non-urgent customer reports, encouraging quick responses and continuous improvement.&lt;/p&gt;

&lt;h2&gt;
  
  
  Re-understanding Engineering
&lt;/h2&gt;

&lt;p&gt;Back-end developers, SREs, and database engineers regularly create technical design documents with detailed architecture diagrams, data structures, and API designs. However, for the Front-end work, documentation typically appears only for ambitious projects, usually with rough diagrams beforehand and explanatory documents afterwards.&lt;/p&gt;

&lt;p&gt;I had no experience writing Front-end technical designs or RFCs. This stems partly from my Chinese education background, where a notable book criticized the lack of training in research and proposal writing. &lt;/p&gt;

&lt;p&gt;Frontend developers often lack of motivation to craft formal technical design in favor of quick feedback, moving straight from proof of concept to implementation, and expectation of fast delivery.&lt;/p&gt;

&lt;p&gt;After several technical discussions, issue investigations, conversations and observations with our talented Back-end engineers, I've grown to appreciate how technical design and investigation documents help solve complex problems, document decisions, track progress, and support career advancement in cross-functional teams.&lt;/p&gt;

&lt;p&gt;The struggling writing process was time consuming but allowed me to dry-running ideas in all scenarios and refine again and again rather than implementing and refactoring code with double, triple or even more efforts. And I find it pushed me to learn new things everytime, much more than coding.&lt;/p&gt;

&lt;p&gt;Here are some common documentation topics I found easy to start with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;comparing and choosing third party library, tool chain&lt;/li&gt;
&lt;li&gt;naming conventions&lt;/li&gt;
&lt;li&gt;learnings&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And some often skipped and sometimes more difficult to write:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;common utility or component design&lt;/li&gt;
&lt;li&gt;state structure design&lt;/li&gt;
&lt;li&gt;refactor and migration plan&lt;/li&gt;
&lt;li&gt;complex implementation with trade-offs, other options and why&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Afterwords
&lt;/h2&gt;

&lt;p&gt;Fundamentally, all the work remains, but instead of having dedicated project managers, we've distributed those responsibilities across multiple roles. Engineers now occupy a more central position in the organization with expanded capabilities and responsibilities—though this comes with the trade-off of spending less time coding. That updates my understanding of engineering culture.&lt;/p&gt;

&lt;p&gt;This has been a fascinating learning journey, though it took me considerable time to find the right words to describe my observations. After 10 years in the industry, I'm delighted to see that ways of working continue to evolve.&lt;/p&gt;

</description>
      <category>reloacation</category>
      <category>australia</category>
      <category>workplace</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Web component tutorial for React devs</title>
      <dc:creator>Jennie</dc:creator>
      <pubDate>Thu, 25 Apr 2024 02:47:16 +0000</pubDate>
      <link>https://dev.to/jennieji/web-component-tutorial-for-react-devs-4mlc</link>
      <guid>https://dev.to/jennieji/web-component-tutorial-for-react-devs-4mlc</guid>
      <description>&lt;p&gt;Web component was a breaking news but sank over the years. I never paid attention util one day I read about a new open-sourced micro front-end framework is based on web component 🤯. &lt;/p&gt;

&lt;p&gt;Reading MDN and some beginner blogs hardly answered my doubts. Being a React dev for so many years, I knew I would learn better with a React mindset. &lt;/p&gt;

&lt;p&gt;What is the foundation of a React component?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A custom JSX tag&lt;/li&gt;
&lt;li&gt;Internal state&lt;/li&gt;
&lt;li&gt;Properties and children&lt;/li&gt;
&lt;li&gt;Life cycle&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let’s write some code!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;NOTE that enable to learn progressively, demo code are ignoring good practices. &lt;br&gt;
Full demos are &lt;a href="https://stackblitz.com/@JennieJi/collections/web-component-v-s-react-demos" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Starting with a classic counter
&lt;/h2&gt;

&lt;p&gt;In React, we may create a counter with very little code:&lt;/p&gt;

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

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Counter&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setCount&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&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;increase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&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;span class="nf"&gt;setCount&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&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="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      Current count: &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;count&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;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;increase&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Increase!&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;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;p&gt;The web component version is a bit longer (&lt;a href="https://stackblitz.com/edit/stackblitz-starters-2jknur?file=src%2Fweb-component%2FCounter.js" rel="noopener noreferrer"&gt;try here&lt;/a&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;my-counter&amp;gt;&amp;lt;/my-counter&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Counter&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;HTMLElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&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;shadow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attachShadow&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;open&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nx"&gt;shadow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
      &amp;lt;div&amp;gt;
        Current count: &amp;lt;span class="counter-number"&amp;gt;0&amp;lt;/span&amp;gt;
        &amp;lt;button class="counter-increase"&amp;gt;Increase!&amp;lt;/button&amp;gt;
      &amp;lt;/div&amp;gt;
    `&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&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;countNode&lt;/span&gt; &lt;span class="o"&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;shadowRoot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.counter-number&lt;/span&gt;&lt;span class="dl"&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;shadowRoot&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.counter-increase&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="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;countNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;count&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="nx"&gt;customElements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-counter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Counter&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;To create the web component, we declared a class extends &lt;code&gt;HTMLElement&lt;/code&gt; first, and define a custom HTML element with &lt;code&gt;customElements.define()&lt;/code&gt; API that binds &lt;code&gt;my-counter&lt;/code&gt; HTML tag to the class. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 Note that there are a lot of &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry/define#valid_custom_element_names" rel="noopener noreferrer"&gt;constrains&lt;/a&gt; with the custom HTML tag.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Next, we enriched the component with a &lt;strong&gt;Shadow DOM&lt;/strong&gt; by  &lt;code&gt;this.attachShadow({ mode: ‘open’ })&lt;/code&gt; , and operates the DOM from &lt;code&gt;this.shadowRoot&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Shadow DOM explained
&lt;/h3&gt;

&lt;p&gt;Shadow DOM, just like the &lt;code&gt;document&lt;/code&gt; in iframe, is a HTML fragment that contains a DOM tree named as &lt;strong&gt;shadow tree&lt;/strong&gt;. It is attached to a regular DOM element named as &lt;strong&gt;shadow host&lt;/strong&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%2Fdeveloper.mozilla.org%2Fen-US%2Fdocs%2FWeb%2FAPI%2FWeb_components%2FUsing_shadow_DOM%2Fshadowdom.svg" 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%2Fdeveloper.mozilla.org%2Fen-US%2Fdocs%2FWeb%2FAPI%2FWeb_components%2FUsing_shadow_DOM%2Fshadowdom.svg" alt="https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM/shadowdom.svg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When the &lt;code&gt;mode&lt;/code&gt; of Shadow DOM is set to &lt;code&gt;open&lt;/code&gt;, we can access the shadow tree from the shadow host like this:&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;shadowHostElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shadowRoot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;When the &lt;code&gt;mode&lt;/code&gt; of Shadow DOM is set to &lt;code&gt;closed&lt;/code&gt;,  the shadow tree will get hidden from the document. &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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;shadowHostElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shadowRoot&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// null&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;As it is a separate HTML fragment, the &lt;strong&gt;styles are isolated&lt;/strong&gt;. That means shadow DOM won’t inherit or apply any styles from the &lt;code&gt;document&lt;/code&gt;, and vise versa.&lt;/p&gt;

&lt;p&gt;To style the shadow DOM, we need to place the style tag in the shadow tree like this:&lt;/p&gt;

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

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;shadow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attachShadow&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;open&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;shadow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
&amp;lt;style&amp;gt;
.my-div { background: red; }
&amp;lt;/style&amp;gt;
&amp;lt;div class="my-div"&amp;gt;&amp;lt;/div&amp;gt;
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;The style isolation and the shadow root access created a layer of encapsulation that is very important in sharable components and micro front-end architecture.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Must we use shadow DOM?&lt;/strong&gt; In some cases, but not in this one!  It is generally recommended to use shadow DOM in a web component to have the layer of encapsulation. One of the case that must use shadow DOM will be demonstrated later.&lt;/p&gt;

&lt;h2&gt;
  
  
  State in web component
&lt;/h2&gt;

&lt;p&gt;In the counter demo above, we did tedious DOM operations for the &lt;code&gt;count&lt;/code&gt; state in our web component version. Do we have state management in web component?&lt;/p&gt;

&lt;p&gt;The answer is yes and no. It is quite different (&lt;a href="https://stackblitz.com/edit/stackblitz-starters-cjhwu5?file=src%2Findex.js" rel="noopener noreferrer"&gt;try demo here&lt;/a&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;with-state&amp;gt;&amp;lt;/with-state&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WithState&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;HTMLElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&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;shadowRoot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attachShadow&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;open&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nx"&gt;shadowRoot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
      &amp;lt;div&amp;gt;
        &amp;lt;input type="checkbox" id="check" /&amp;gt;
        &amp;lt;label for="check"&amp;gt;
          Tick and untick me!
        &amp;lt;/label&amp;gt;
      &amp;lt;/div&amp;gt;
      &amp;lt;style&amp;gt;
        // ONLY IN SAFARI WITH PREVIEW ON
        :state(--checked) {
          background: green;
        }
      &amp;lt;/style&amp;gt;
    `&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;states&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attachInternals&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;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;shadowRoot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;label&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;shadowRoot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&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;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;checked&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;states&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&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="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;states&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&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="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;states&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&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="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Untick me!&lt;/span&gt;&lt;span class="dl"&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;Tick me!&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;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;customElements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;with-state&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;WithState&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;In this &lt;code&gt;WithState&lt;/code&gt; component, we enabled states with &lt;code&gt;this.attachInternals()&lt;/code&gt; first. Then add or delete state accordingly. &lt;/p&gt;

&lt;p&gt;There are a few interesting details:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;This state is a &lt;code&gt;Set&lt;/code&gt; - it can only act like a boolean.&lt;/li&gt;
&lt;li&gt;The state require a &lt;strong&gt;ugly double dash&lt;/strong&gt; - if not, browser simply throws error although some of MDN document is not specifying this. 
Well, this is still tolerable considering to avoid conflicts with tag names in CSS.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This “state” is kind of unhelpful. The only game changer is probably the &lt;code&gt;:state()&lt;/code&gt; CSS selector which is only supported in Safari with preview FF on 👀. This selector allows the state work similar to input disabled, focus.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating components with Template
&lt;/h2&gt;

&lt;p&gt;Demos above attach the DOM tree to web component with &lt;code&gt;innerHTML&lt;/code&gt; . In fact, they can be more flexible, scalable and readable with &lt;code&gt;&amp;lt;template/&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;slot/&amp;gt;&lt;/code&gt; instead. &lt;/p&gt;

&lt;p&gt;Let’s look at a simple Card component:&lt;/p&gt;

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

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Card&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;
      &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#ccc&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;borderRadius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;8px&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;14px&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="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&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;div&lt;/span&gt;&lt;span class="p"&gt;&amp;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;p&gt;A card component in React is a container with &lt;code&gt;children&lt;/code&gt; prop. While in web component, this is achievable with &lt;code&gt;&amp;lt;template/&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;slot/&amp;gt;&lt;/code&gt; (&lt;a href="https://stackblitz.com/edit/stackblitz-starters-pbvgbt?file=public%2Findex.html" rel="noopener noreferrer"&gt;try here&lt;/a&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;my-card&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;slot=&lt;/span&gt;&lt;span class="s"&gt;"card-content"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Span slot of the card.&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/my-card&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;template&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"card-template"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"background: #ccc; border-radius: 8px; padding: 14px"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;slot&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"card-content"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/slot&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;style&amp;gt;&lt;/span&gt;
    &lt;span class="nd"&gt;::slotted&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;red&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;/style&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Card&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;HTMLElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&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;template&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;card-template&lt;/span&gt;&lt;span class="dl"&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;shadow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attachShadow&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;open&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="nx"&gt;shadow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cloneNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&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="nx"&gt;customElements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-card&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Card&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;The component class simply attached the cloned DOM tree from a &lt;code&gt;&amp;lt;template/&amp;gt;&lt;/code&gt; node. Styles and the “slot” for content noes were all specified in a template.&lt;/p&gt;

&lt;p&gt;We use a &lt;code&gt;&amp;lt;slot /&amp;gt;&lt;/code&gt; tag with a &lt;code&gt;name&lt;/code&gt; attribute to declare slot, and we may insert any tag with an attribute &lt;code&gt;slot&lt;/code&gt; and the &lt;code&gt;name&lt;/code&gt; we declared.&lt;/p&gt;

&lt;p&gt;Although the HTML markups in &lt;code&gt;&amp;lt;template/&amp;gt;&lt;/code&gt; is not rendered anywhere, it is &lt;strong&gt;accessible and mutable&lt;/strong&gt; like any other regular DOM elements. Hence, we’d better clone the template DOM tree every time instead of using it directly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What happens to any content out of slot?&lt;/strong&gt; I tried with below code and the browser perfectly &lt;strong&gt;ignored&lt;/strong&gt; them 😄.&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;my-card&amp;gt;&lt;/span&gt;
  Card Children
&lt;span class="nt"&gt;&amp;lt;/my-card&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;How about using the same slot multiple times?&lt;/strong&gt; It works! 🤣 The elements were added into card-content slot from top to down.&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;my-card&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;slot=&lt;/span&gt;&lt;span class="s"&gt;"card-content"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Span slot of the card.&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;slot=&lt;/span&gt;&lt;span class="s"&gt;"card-content"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Div slot of the card.&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/my-card&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Remember that we talked about shadow DOM may not be necessary, not here! It turns out the &lt;code&gt;&amp;lt;slot /&amp;gt;&lt;/code&gt; tag &lt;strong&gt;only works in the web component context with shadow DOM&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The life cycle of a web component
&lt;/h2&gt;

&lt;p&gt;To pass in properties and trigger effects when the property changes, we need to work with the life cycle methods. &lt;/p&gt;

&lt;p&gt;There are 4 life cycle methods other than &lt;code&gt;constructor&lt;/code&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;connectedCallback&lt;/li&gt;
&lt;li&gt;disconnectedCallback&lt;/li&gt;
&lt;li&gt;adoptedCallback&lt;/li&gt;
&lt;li&gt;attributeChangedCallback&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let’s &lt;a href="https://stackblitz.com/edit/stackblitz-starters-hrjcya?file=src%2Findex.js" rel="noopener noreferrer"&gt;try this demo&lt;/a&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;button&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"with-attribute-demo-toggle"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Start demo&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"with-attribute-demo-container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;template&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"with-attribute-demo"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt;
    &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"with-attribute-input"&lt;/span&gt;
    &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt;
    &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Change value here"&lt;/span&gt;
  &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;with-attribute&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"Initialized"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/with-attribute&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WithAttribute&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;HTMLElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nx"&gt;observedAttributes&lt;/span&gt; &lt;span class="o"&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;value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Constructor.&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="nf"&gt;connectedCallback&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;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;with-attribute-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;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&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;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Connected.&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="nf"&gt;disconnectedCallback&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Disconnected.&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="nf"&gt;adoptedCallback&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Adopted&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="nf"&gt;attributeChangedCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;oldValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Attribute "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" changed &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;oldValue&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; to &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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="nx"&gt;customElements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;with-attribute&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;WithAttribute&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;toggleBtn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;with-attribute-demo-toggle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;toggleBtn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&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="o"&gt;=&amp;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;container&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;with-attribute-demo-container&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hasChildNodes&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;toggleBtn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Start demo&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="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;toggleBtn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;End demo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;with-attribute-demo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cloneNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&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;Don’t get terrified by the length. The &lt;code&gt;WIthAttribute&lt;/code&gt; component only logs the life cycle methods. Other than that, there are a little input and a button to help mount, unmount the component and change the component attribute. Additionally, a compulsory static &lt;code&gt;observedAttributes&lt;/code&gt; is added for observing the attribute change.&lt;/p&gt;

&lt;p&gt;Click &lt;code&gt;Start demo&lt;/code&gt; button, here is the console log:&lt;/p&gt;

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

Constructor.
Attribute &lt;span class="s2"&gt;"value"&lt;/span&gt; changed null to Initialized.
Connected.


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

&lt;/div&gt;

&lt;p&gt;Something interesting here: Right after the &lt;code&gt;constructor&lt;/code&gt;, the &lt;code&gt;attributeChangedCallback&lt;/code&gt; was triggered to change my &lt;code&gt;value&lt;/code&gt; attribute from &lt;code&gt;undefined&lt;/code&gt; to &lt;code&gt;“Initialized”&lt;/code&gt; that I assigned directly on the HTML markup, and &lt;code&gt;connectedCallback&lt;/code&gt; follows. That is suggesting &lt;code&gt;connectedCallback&lt;/code&gt; is actually a better place for initializing a component.&lt;/p&gt;

&lt;p&gt;When we click the &lt;code&gt;End demo&lt;/code&gt; button, a new log appended:&lt;/p&gt;

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

Disconnected.


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

&lt;/div&gt;

&lt;p&gt;Clicking  &lt;code&gt;Start demo&lt;/code&gt; button again, you will see following log again:&lt;/p&gt;

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

Constructor.
Attribute &lt;span class="s2"&gt;"value"&lt;/span&gt; changed null to Initialized.
Connected.


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

&lt;/div&gt;

&lt;p&gt;I was expecting &lt;code&gt;Constructor&lt;/code&gt; not showing up here but nah…&lt;/p&gt;

&lt;p&gt;The last &lt;code&gt;adoptedCallback&lt;/code&gt; I didn’t figure out with the MDN doc. Then I looked stackoverflow for help and found &lt;a href="https://stackoverflow.com/a/51002629/3322507" rel="noopener noreferrer"&gt;this&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;adoptedCallback(&lt;/code&gt;) method is called when a Custom Element is moved from one HTML document to another one with the &lt;code&gt;[adoptNode()](https://developer.mozilla.org/en-US/docs/Web/API/Document/adoptNode)&lt;/code&gt; method.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;🤯what? You can do that? #TIL.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using web component in React
&lt;/h2&gt;

&lt;p&gt;If you are maintaining a React repo but wondering whether you could introduce web component. The answer is yes! Implementing a web component into a React component is just like any other HTML markups (&lt;a href="https://stackblitz.com/edit/stackblitz-starters-29shew?file=src%2Freact-app%2Findex.js" rel="noopener noreferrer"&gt;try here&lt;/a&gt;):&lt;/p&gt;

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

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Card&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;my&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="na"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;my&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="na"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Card&lt;/span&gt;&lt;span class="p"&gt;&amp;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;p&gt;However, placing a React component into a web component is not that feasible as the React components need to connect with the React app root. &lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping a React app in web component
&lt;/h2&gt;

&lt;p&gt;Though React component in web component is not likely to work (and not necessary?), it is possible to encapsulate the entire React app in a web component (&lt;a href="https://stackblitz.com/edit/stackblitz-starters-29shew?file=src%2Fweb-component%2FReactAppWrapper.js" rel="noopener noreferrer"&gt;try here&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;First, modify the React app to allow passing in the app root:&lt;/p&gt;

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

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;initApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;container&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-app&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createRoot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;StrictMode&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;App&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;StrictMode&lt;/span&gt;&lt;span class="p"&gt;&amp;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;p&gt;Next, create a web component wrapper like this to initialize the React app:&lt;/p&gt;

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

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;initApp&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;../react-app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ReactAppWrapper&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;HTMLElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;connectedCallback&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;shadow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attachShadow&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;closed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nf"&gt;initApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;shadow&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="nx"&gt;customElements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-app-wrapper&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ReactAppWrapper&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;That is exactly what micro front-end needs!&lt;/p&gt;

&lt;h2&gt;
  
  
  Last but not least
&lt;/h2&gt;

&lt;p&gt;Nobody likes to &lt;strong&gt;manually update DOM&lt;/strong&gt; when state updates with the convenience of modern frameworks, but that could be resolved by integrating another framework or library. For example, &lt;a href="https://lit.dev/" rel="noopener noreferrer"&gt;Lit&lt;/a&gt;, &lt;a href="https://stenciljs.com/" rel="noopener noreferrer"&gt;Stencil&lt;/a&gt;, etc. Yep! Another framework!🙈&lt;/p&gt;

</description>
      <category>webcomponents</category>
      <category>tutorial</category>
      <category>react</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Refactoring code reused across multiple teams</title>
      <dc:creator>Jennie</dc:creator>
      <pubDate>Wed, 20 Mar 2024 03:34:10 +0000</pubDate>
      <link>https://dev.to/jennieji/refactoring-code-reused-across-multiple-teams-2d8i</link>
      <guid>https://dev.to/jennieji/refactoring-code-reused-across-multiple-teams-2d8i</guid>
      <description>&lt;h2&gt;
  
  
  The tech debt
&lt;/h2&gt;

&lt;p&gt;I found an interesting ticket concerns about a legacy domain component being reused across 4 teams by importing source code path and coping necessary logic because&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The copied logic lied in other domain is not under my team’s maintenance.&lt;/li&gt;
&lt;li&gt;Applying updates requires more efforts.&lt;/li&gt;
&lt;li&gt;Importing source code path works in the current mono-repo setup temporary. But it may break easily if the package published without source code or changed internal path.&lt;/li&gt;
&lt;li&gt;The code was not designed for reuse at all.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It was not hard to guess the major cause of the ticket is the &lt;strong&gt;limit of resource&lt;/strong&gt; - a norm of a fast developing tech company. It becomes a tech debt and the &lt;strong&gt;complexity grew rapidly&lt;/strong&gt;. Devs started to alter the copied logic for their needs, and more teams follow the efficient copy over way that worked.&lt;/p&gt;

&lt;p&gt;Eventually, we need much more context to ensure the quality than the time it created to pay back the debt.&lt;/p&gt;

&lt;p&gt;My team &lt;strong&gt;never feel the pain&lt;/strong&gt; as before adding any new stuffs there, no extra works for the team. Teams reused these code didn‘t feel the pain as they avoid the long waiting from my team and get things delivered quickly. Util one “lucky” developer feel the pain and decided to cut it off.&lt;/p&gt;

&lt;p&gt;Well, I was lucky enough to become that person and here is some notes and thoughts for such case.&lt;/p&gt;

&lt;h2&gt;
  
  
  The refactor
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Preparations
&lt;/h3&gt;

&lt;p&gt;There were a lot of &lt;strong&gt;challenges&lt;/strong&gt; for a refactor like this, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Limit of resources - Completing the refactor will take long as we have to deal with other higher priority stuff.&lt;/li&gt;
&lt;li&gt;Wide impact range - Every change has risks to cause incident all over the place.&lt;/li&gt;
&lt;li&gt;Lack of code context - No one in the team knows the part of code well.&lt;/li&gt;
&lt;li&gt;Questionable testing coverage - There was decent unit test coverage but almost no e2e coverage. As refactor changes implementation, unit tests often become invalid. Hence no guarantees of things work as expected after refactoring.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Base on these challenges I identified some necessary &lt;strong&gt;preparations&lt;/strong&gt;, like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Break the task down into the granularity that could fit both the development and review works into our tight schedule.&lt;/li&gt;
&lt;li&gt;Instead of changing any exisiting business logic code, create new and migrate one by one.&lt;/li&gt;
&lt;li&gt;Learn the code well before jumping in.&lt;/li&gt;
&lt;li&gt;Fill the test coverage gap before any changes.&lt;/li&gt;
&lt;li&gt;Pair with the teams for the migration.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The plan
&lt;/h3&gt;

&lt;p&gt;Based on all the information and conditions, I tried to create small frequent iterations that could get delivered in a few days and resilient of interruptions from higher priority works.&lt;/p&gt;

&lt;p&gt;I formed a rough plan in my head:&lt;/p&gt;

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

&lt;h3&gt;
  
  
  1. Move the reused files into a shared package
&lt;/h3&gt;

&lt;p&gt;Ideally moving the reused files shall be done at the last step to ensure a smooth and gradually transition like the diagram above. However, the effort of creating the new reusable hook was too large to fit in the schedule, and completing the migration could span across several months. Therefore, I decided to bring this piece of work up front that takes relatively short time and increase the awareness of the reused attribute of this component.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Make small clean ups
&lt;/h3&gt;

&lt;p&gt;Reducing unnecessary properties, states and callbacks are very helpful in the start of the refactoring work. These small clean ups are not only helping to reduce the complexity of refactoring and the following up migration, but also helping me to understand the implementation and usage.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Create a new encapsulation
&lt;/h3&gt;

&lt;p&gt;There were a few duplicated and modified code sitting within the host components. To reduce the redundancy, and to avoid modifying exisiting logic and causing incidents everywhere, creating a new encapsulation was a better choice. The new encapsulation may reuse any existing code and extend the logic while not affecting anyone before fully tested and migrated.&lt;/p&gt;

&lt;p&gt;It ends up to be a new hook allowing the hosts to control the component, and exposing some necessary states.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Migrate usage to the new encapsulation one by one
&lt;/h3&gt;

&lt;p&gt;With the new hook ready and tested. I can finally help the hosts to import the new hook, and clean up the copied and modified logic.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Final clean up
&lt;/h3&gt;

&lt;p&gt;For a smooth migration, I may create some intermediate stuff. After the complete migration, these code would only cause confusion. This last step was to clean up and reduce the possible confusion.&lt;/p&gt;

&lt;h3&gt;
  
  
  The actual work carried out
&lt;/h3&gt;

&lt;p&gt;Before starting touching the code, I only created 4 tickets and added more during the actual work. &lt;/p&gt;

&lt;p&gt;With the rough understanding of the code and plans, I reached out to a Front-end developers from one of the 4 teams. Unsurprisingly, importing our source code from the private space and maintaining the duplicated messy logic was not his concern, but &lt;strong&gt;initializing the extra Redux state&lt;/strong&gt; in their domain was. This was completely out of scope and I promised to take a look later.&lt;/p&gt;

&lt;p&gt;As planned, I moved the related code to a shared package first. To reduce the impact scope, I &lt;strong&gt;relocated the source code but keep the original path and export everything from the new place&lt;/strong&gt;. The change took only few days but the code review took weeks thanks to the long list of the files.&lt;/p&gt;

&lt;p&gt;The next piece of work started at 4 months later. Without writing down the plan, I almost &lt;strong&gt;forgot what I wanted to do&lt;/strong&gt;. Luckily, my brain picked up quickly after scanning through the related code.&lt;/p&gt;

&lt;p&gt;I started with removing a prop that could retrieve from the another prop instead, and making another prop more generic.&lt;/p&gt;

&lt;p&gt;Then I happily discovered &lt;strong&gt;a piece of my work done in this 4 months gap removed a blocker&lt;/strong&gt; of retiring the necessary Redux state. And I did it. The clean up was huge but straightforward enough to demand relatively small efforts from each team to review and merge.&lt;/p&gt;

&lt;p&gt;Next, I created a new encapsulation but went a longer way as the long iteration span made me &lt;strong&gt;forgot my initial plan&lt;/strong&gt; again 🙈 . Furthermore, I found a block of complex logic in the reused code that only required in the inspections list. As a result, I &lt;strong&gt;created an intermediate implementation&lt;/strong&gt; to help the transition.&lt;/p&gt;

&lt;p&gt;I thought I am ready to start the migration with the intermediate hook. Soon I realize it was not good enough when I tried migrating. Therefore, another better hook was born &lt;strong&gt;based on the learnings from the failure trail&lt;/strong&gt;. The new hook required minimal code and provides intuitive and only essential callbacks for the host component to implement.&lt;/p&gt;

&lt;p&gt;When I finally get back on the track of migration, the new hook that I was confident about crashed a page. Interestingly, it was neither a bug in the new hook nor in the host component of that page. It was an implementation in the hook &lt;strong&gt;could not work well with the host component&lt;/strong&gt;. To resolve this, I adjusted the implementation of the hook one more time.&lt;/p&gt;

&lt;p&gt;Eventually, I created 3 more small PRs and migrated everywhere except the most complicated one, and I got pulled into another high priority feature development expecting months of effort.&lt;/p&gt;

&lt;h3&gt;
  
  
  My mistakes
&lt;/h3&gt;

&lt;p&gt;There were various mistakes I made:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Planed in head.&lt;/strong&gt; Since the time span for the project took very long, I kept forgetting my initial plan and spent extra time to recompose one. This could be avoided if I wrote them down.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Created large PRs that was possible to break down.&lt;/strong&gt; The PR I created for relocating files, retiring Redux state was large. These PRs were possible to break down for a safer transition, but I didn’t figure it out.&lt;/p&gt;

&lt;p&gt;Breaking down PRs with little conflicts requires lots of planning ahead as it has to ensure everything works as expected with the small subset of changes. While the large PRs, could contain incomplete changes and polish later util reach the point of perfection.&lt;/p&gt;

&lt;p&gt;Although more planning effort required, I really love the quick feedback loop of the small PRs. The reviewer were happy to review and verify the change in a few hours. Furthermore, my confidence of the deployment was higher as the quality of a small scope were easier to guarantee. Additionally, the maintenance effort before merging is much lower as it gets merge quicker, and the conflict range of each PR is small.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Simple migrations didn’t inform all the stakeholders in advance.&lt;/strong&gt; Engineers love to surprise reviewers with the PRs without having a proper conversation in advance. I am one of them. After the first and only conversation with one team that I found little value, I skip the other conversations. Thankfully everything works well so far. But this has to be corrected as this is how this tech debt started and I tried to avoid.&lt;/p&gt;

&lt;h3&gt;
  
  
  Other take aways
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Pace it slow is not a bad thing.&lt;/strong&gt; As mentioned above, I had a 4 months gap between the first and second piece of work. During this gap, a blocker of the Redux store pain point was removed in a non-related task. What an existing surprise! Sometimes the problem goes away after a while.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Break the tech debts task down to minimal is the key&lt;/strong&gt; of delivering when resource is precious. Other than the fast feedback loop, the higher quality confidence, and the less maintenance overhead mentioned above. It was easy to find a chance between the high priority job since each subtask took up to 3 days of initial work. More over, the small scope of the subtask makes it less overwhelming to drop and pick up after a while.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Complexity of patching tests across domain is very high and may not worth the effort.&lt;/strong&gt; To ensure the quality, it is better to have complete test coverage before refactoring in theory (I did so before and it worked really well). While in reality, the complexity of crossing the domains doubles or even triples the effort that may not worth the value created. Thus, in this refactoring, I chose to rely on the existing tests and doing more manual tests to help speeding up the process. Meanwhile, in the test of the host components, I mocked my component and hook to reduce the testing overhead.&lt;/p&gt;

&lt;h2&gt;
  
  
  After thoughts
&lt;/h2&gt;

&lt;p&gt;Although large refactor was satisfying, I would rather it never happen. However, this is inevitable if an app keeps growing after some time. Then how can we make things better and easier when before reaching that point or avoid it happen?&lt;/p&gt;

&lt;p&gt;First of all, &lt;strong&gt;follow the good practices whenever possible&lt;/strong&gt;. For example, creating components or functions, classes that take care of (little but not too little) logic about the same feature, same category. This practice maximizes the reusability of the code, make them flexible enough for composing in different cases.&lt;/p&gt;

&lt;p&gt;Secondly, &lt;strong&gt;refactor often and small&lt;/strong&gt;. Overtime, good practices change, meanwhile our knowledge grows and the “flawless” design reveals flaws. If we continuously tweaking the code to reduce the flaws, it might never grow into a big refactor.&lt;/p&gt;

&lt;p&gt;Next, remember if we want to &lt;strong&gt;reuse other team's code, discuss with the code owner&lt;/strong&gt; first to gain some context and make short to long term actionable plans together. It seems take more time initially but speeds everything up after.&lt;/p&gt;

&lt;p&gt;Lastly, if we want to prevent tech debts like this and help others to reuse our code, we may consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Open a public channel&lt;/strong&gt; for others reaching out. Sometimes people gave up discussion because they didn’t find a easy way.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Define standards&lt;/strong&gt; for the sharing code so that everyone could follow and make it easy to find. The simplest thing we could do is clarify where to put the shared files especially in a mono-repo structure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Assist proactively&lt;/strong&gt;. Helping the developer to understand the context, review the design and code takes time in a short-term but ensure things go well and causing less headache in a long run.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>react</category>
      <category>refactoring</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Setup VSCode and WSL</title>
      <dc:creator>Jennie</dc:creator>
      <pubDate>Wed, 31 May 2023 06:56:27 +0000</pubDate>
      <link>https://dev.to/jennieji/a-quick-setup-for-front-end-development-with-vscode-in-windows-10-3pok</link>
      <guid>https://dev.to/jennieji/a-quick-setup-for-front-end-development-with-vscode-in-windows-10-3pok</guid>
      <description>&lt;p&gt;Just like most of the Front-end folks, I was a loyal MacBook user for the last 7 years. Only util recent months I started coding on my Windows PC with VSCode.&lt;/p&gt;

&lt;p&gt;Although the development experience is significantly improved since Windows 10 introduced the Linux subsystem(WSL), I still met problems one after one. Eventually, I found that I went in the wrong direction and the quick solution was right in front of me!🤦‍♀️&lt;/p&gt;

&lt;p&gt;In short: &lt;strong&gt;Remote development in WSL with VSCode&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Here are the simple steps and my exploration with other setups. I hope it helps saving your time. &lt;/p&gt;

&lt;h2&gt;
  
  
  The setup in short
&lt;/h2&gt;

&lt;p&gt;Before we start, please ensure that you have upgraded your Windows 10 to at least version 2004 (According to &lt;a href="https://learn.microsoft.com/en-us/windows/wsl/install" rel="noopener noreferrer"&gt;the official WSL tutorial&lt;/a&gt;) for WSL to work.&lt;/p&gt;

&lt;h3&gt;
  
  
  Enable the subsystem
&lt;/h3&gt;

&lt;p&gt;There are 2 ways to enable the subsystem. &lt;/p&gt;

&lt;p&gt;From GUI in the “&lt;strong&gt;Turn Windows Features on and off&lt;/strong&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%2F8ebwr90poftifdx68sy4.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%2F8ebwr90poftifdx68sy4.png" alt="https://code.visualstudio.com/assets/docs/remote/wsl-tutorial/windows-features.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Or in the command line tools with administrator according to &lt;a href="https://learn.microsoft.com/en-us/windows/wsl/install" rel="noopener noreferrer"&gt;the official WSL tutorial&lt;/a&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;wsl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--install&lt;/span&gt;&lt;span class="w"&gt;


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

&lt;/div&gt;

&lt;p&gt;Then you shall be able to find the “WSL” app in the Windows Start Menu.&lt;/p&gt;

&lt;h3&gt;
  
  
  Update Ubuntu version
&lt;/h3&gt;

&lt;p&gt;In my experience, the default Ubuntu installed is v16, which is so outdated that I could only install Node.js v14.&lt;/p&gt;

&lt;p&gt;With command &lt;code&gt;wsl --list --online&lt;/code&gt;, you may find a more updated version like &lt;code&gt;22.04 LTS&lt;/code&gt; in the following screenshot I took few weeks ago:&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%2Fpbs.twimg.com%2Fmedia%2FFuCgS9naYAokowS%3Fformat%3Dpng%26name%3Dsmall" 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%2Fpbs.twimg.com%2Fmedia%2FFuCgS9naYAokowS%3Fformat%3Dpng%26name%3Dsmall" alt="WSL distros"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You may install it with command:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;wsl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Ubuntu-22.04&lt;/span&gt;&lt;span class="w"&gt;


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

&lt;/div&gt;

&lt;p&gt;Then you shall be able to find the “Ubuntu 22.04 LTS” app in the Windows Start Menu.&lt;/p&gt;

&lt;h3&gt;
  
  
  VSCode remote development
&lt;/h3&gt;

&lt;p&gt;Next, install &lt;a href="https://code.visualstudio.com/" rel="noopener noreferrer"&gt;VSCode&lt;/a&gt; and the &lt;a href="https://www.notion.so/The-inspiring-extension-systems-design-overview-Chrome-VSCode-59149ac0fce44b1b878a0d6f71f36898" rel="noopener noreferrer"&gt;WSL extension&lt;/a&gt;, and you will be able to find “Connect to WSL using Distro” in the command palette:&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%2Fcode.visualstudio.com%2Fassets%2Fdocs%2Fremote%2Fwsl-tutorial%2Fwsl-commands.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%2Fcode.visualstudio.com%2Fassets%2Fdocs%2Fremote%2Fwsl-tutorial%2Fwsl-commands.png" alt="https://code.visualstudio.com/assets/docs/remote/wsl-tutorial/wsl-commands.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select the &lt;code&gt;Ubuntu-22.04&lt;/code&gt; you have just installed:&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%2Fi6ode205ezjrdiw8a26o.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%2Fi6ode205ezjrdiw8a26o.png" alt="Choose WSL distro"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A new VSCode window will open in a remote mode working on the Ubuntu file system, and you may happily coding there just like working locally. The only noticeable latency I found so far is in the starting up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why remote development in WSL
&lt;/h2&gt;

&lt;p&gt;I started local development with WSL as a terminal because quite a few things couldn’t work well under Windows. And soon the nightmares came to me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Splitting terminal failed&lt;/li&gt;
&lt;li&gt;Extension like Jest debugger, prettier spoiled&lt;/li&gt;
&lt;li&gt;Watching mode broken&lt;/li&gt;
&lt;li&gt;HMR died&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Maintaining VSCode extensions was still tolerable, while it was nearly impossible for me to work on a web app. &lt;/p&gt;

&lt;p&gt;I noticed the root cause of these malfunctions was &lt;strong&gt;path&lt;/strong&gt;. Afterall, Windows and the subsystem Ubuntu are running on &lt;strong&gt;2 different file systems&lt;/strong&gt; no matter how seamlessly WSL looks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All the Windows drives will be mounted under &lt;code&gt;/mnt/&lt;/code&gt; folder in WSL.&lt;/li&gt;
&lt;li&gt;All the &lt;a href="http://localhost" rel="noopener noreferrer"&gt;localhost&lt;/a&gt; ports will be forwarded without extra configurations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, if we operate the VSCode in Windows, the IDE and their extensions expect working with Windows file system not the Linux’s. As for stuff like HMR not working, I haven’t dived deep into it but I guess something to do with the port forwarding setups.&lt;/p&gt;

&lt;p&gt;I believe most people will choose to code in Linux but play in Windows just like me :) (Or you can just buy a powerful but expensive MacBook).&lt;/p&gt;

&lt;p&gt;You may wonder why not using a docker image for the remote development? Well, docker image is a great choice if you are not planning to run another docker instance in it. In my case, I sometimes setup things such as DB with docker image. &lt;/p&gt;

&lt;p&gt;And I tried to walk away from the environment issue with web IDEs such as GitHub Workspace, Replit, Stackbliz, CodeSandbox before thinking of remote. They are the fast-acting drug worth to take a shot in my mind. However, the web IDE crashed frequently when my experiment grew bigger, and some IDEs demand for a “pro” upgrade to get better experience while the Copilot I paid is not available there. &lt;/p&gt;

&lt;h2&gt;
  
  
  Final thought
&lt;/h2&gt;

&lt;p&gt;I made a mistake of trying to live with the situation and soon it backfires. It is important to get the tools work right enable to get the job done.&lt;/p&gt;

</description>
      <category>windows</category>
      <category>vscode</category>
      <category>wsl</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>The inspiring extension systems design overview (Chrome &amp; VSCode)</title>
      <dc:creator>Jennie</dc:creator>
      <pubDate>Thu, 18 May 2023 02:04:58 +0000</pubDate>
      <link>https://dev.to/jennieji/the-inspiring-extension-systems-design-overview-chrome-vscode-4bco</link>
      <guid>https://dev.to/jennieji/the-inspiring-extension-systems-design-overview-chrome-vscode-4bco</guid>
      <description>&lt;p&gt;A few years ago, I discovered the power of Chrome and VSCode extensions in improving productivity. I created my own extension if my desired feature was not available. The extension systems allow me to experiment my wild ideas with less effort thanks to their graceful design. &lt;/p&gt;

&lt;p&gt;While they have similarities, there are also some significant differences determined by the nature of each app. In this blog post, I will provide an overview of the extension systems of Chrome and VSCode and share what I have learned from them.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
Development

&lt;ul&gt;
&lt;li&gt;Initialization&lt;/li&gt;
&lt;li&gt;APIs&lt;/li&gt;
&lt;li&gt;The script triggers&lt;/li&gt;
&lt;li&gt;Activating extension&lt;/li&gt;
&lt;li&gt;The extension parts and interfaces&lt;/li&gt;
&lt;li&gt;Events&lt;/li&gt;
&lt;li&gt;The communication between parts&lt;/li&gt;
&lt;li&gt;UI/UX&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Testing

&lt;ul&gt;
&lt;li&gt;Testing a Chrome extension&lt;/li&gt;
&lt;li&gt;Testing a VSCode extension&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Distribution

&lt;ul&gt;
&lt;li&gt;Distribution of Chrome extension&lt;/li&gt;
&lt;li&gt;Distribution of VSCode extension&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Summary of what I have learnt&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Development
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Initialization
&lt;/h3&gt;

&lt;p&gt;Both extension systems have an entry point - a JSON file. Chrome extension starts from a &lt;em&gt;manifest.json&lt;/em&gt; file, while VSCode extensions extend the &lt;em&gt;package.json&lt;/em&gt; file, which can sometimes become lengthy and contain too much information. I am worried about conflicts arise from the file. &lt;/p&gt;

&lt;p&gt;In the JSON, developer need to provide essential information such as icon for display and script to initialise with. Each parts of a Chrome extension require a script file or a html file, while VSCode only need an entry script.&lt;/p&gt;

&lt;p&gt;Here is an example of a &lt;code&gt;manifest.json&lt;/code&gt; file for a Chrome extension:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Broken Background Color"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Fix an Extension!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"activeTab"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"scripting"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"storage"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"options_page"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"options.html"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"background"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"service_worker"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"service-worker.js"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"icons"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"16"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"images/icon-16.png"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"manifest_version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;


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

&lt;/div&gt;

&lt;p&gt;And here is an example of a &lt;code&gt;package.json&lt;/code&gt; file for a VSCode extension:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cat-coding"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Cat Coding - A Webview API Sample"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.0.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"publisher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"vscode-samples"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"activationEvents"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./out/extension.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"contributes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"devDependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;


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

&lt;/div&gt;

&lt;p&gt;In addition to these files, Chrome extensions also require careful declaration of browser permissions to enable their APIs. I totally understand the importance and difficulty of guarding browser security, but providing excessive information that can be accessed through automation is unreasonable.&lt;/p&gt;

&lt;h3&gt;
  
  
  APIs
&lt;/h3&gt;

&lt;p&gt;The extension APIs in both Chrome and VSCode are well-categorized based on the functionality of the app. This categorization allows developers to explore the possibilities by following the names of the app modules. For example:&lt;/p&gt;

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

&lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cookies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cookie_name&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="nx"&gt;cookie&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;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cookie&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cookie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&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;p&gt;However, there are possibilities that we have no clues about the name at all 🤦‍♀️ such as the text decoration in VSCode as mentioned in &lt;a href="https://vscode.rocks/decorations/" rel="noopener noreferrer"&gt;this blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Being a Microsoft product, the coding style of VSCode extensions can sometimes be too OOP style. For instance, to implement a tree view, we must declare a class extends the &lt;code&gt;TreeDataProvider&lt;/code&gt; class and provides methods &lt;code&gt;getTreeItem()&lt;/code&gt; and &lt;code&gt;getChildren()&lt;/code&gt;:&lt;/p&gt;

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

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DepNodeProvider&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;vscode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TreeDataProvider&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Dependency&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="na"&gt;_onDidChangeTreeData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;vscode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;EventEmitter&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Dependency&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;vscode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;EventEmitter&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Dependency&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="na"&gt;onDidChangeTreeData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;vscode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Event&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Dependency&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&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;_onDidChangeTreeData&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="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="na"&gt;workspaceRoot&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&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="nf"&gt;refresh&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&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;_onDidChangeTreeData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fire&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;getTreeItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;element&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Dependency&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;vscode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TreeItem&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;element&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;getChildren&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;Dependency&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;Thenable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Dependency&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;span class="c1"&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;p&gt;Plus the “traps” I mentioned above in Chrome, a well structured and clear document with rich example codes become vital. &lt;/p&gt;

&lt;h3&gt;
  
  
  The script triggers
&lt;/h3&gt;

&lt;p&gt;There are three ways to trigger our scripts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;From the extension activation;&lt;/li&gt;
&lt;li&gt;User interaction on the extension interface;&lt;/li&gt;
&lt;li&gt;Internal events.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Activating extension
&lt;/h4&gt;

&lt;p&gt;When a user enables an extension in Chrome, the background script of the extension starts working immediately.&lt;/p&gt;

&lt;p&gt;Chrome has migrated the extension background script to the Service Worker in manifest v3, making the extension work more like a web app. &lt;/p&gt;

&lt;p&gt;In VSCode, the whole extension initializes from one JS file, and the main JS file need to export an &lt;code&gt;activate()&lt;/code&gt; function. Developers shall declare the activation conditions in the &lt;em&gt;package.json&lt;/em&gt; file (see the example above). &lt;/p&gt;

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

&lt;span class="c1"&gt;// out/extension.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;activate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;It seems that Chrome tries to align the pattern with web development, and VSCode adopts another style commonly seen in serverless development. As a web developer, the way of activating script in the Chrome extensions is more natural to me.&lt;/p&gt;

&lt;h4&gt;
  
  
  The extension parts and interfaces
&lt;/h4&gt;

&lt;p&gt;A Chrome extension may contain 4 parts of scripts or HTML pages for different purposes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a popup page for the popup activated from the menu next to the address bar;&lt;/li&gt;
&lt;li&gt;panel pages in the developer tool;&lt;/li&gt;
&lt;li&gt;a content script to interact with the web page content;&lt;/li&gt;
&lt;li&gt;background scripts or page for more interactions.&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%2Fwww.codeinwp.com%2Fwp-content%2Fuploads%2F2021%2F05%2FNotify-Create-Google-Chrome-Extension.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%2Fwww.codeinwp.com%2Fwp-content%2Fuploads%2F2021%2F05%2FNotify-Create-Google-Chrome-Extension.png" alt="Chrome extension popup"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;They are just web pages or scripts allow to access Chrome APIs with some constrains:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We must declare the corresponding permissions in the &lt;em&gt;manifest.json&lt;/em&gt;;&lt;/li&gt;
&lt;li&gt;Some APIs may only work in certain scope (e.g, we can only access user tab &lt;code&gt;document&lt;/code&gt; element from the content script). &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;VSCode defines too many parts to list in a blog (see &lt;a href="https://code.visualstudio.com/api/references/contribution-points" rel="noopener noreferrer"&gt;Contribution Points&lt;/a&gt;). Some parts (or we should call contribution points) are so tiny that all they need are displaying texts and actions. &lt;/p&gt;

&lt;p&gt;As multiple contribution points often result into the same action, VSCode took a &lt;strong&gt;&lt;a href="https://code.visualstudio.com/api/extension-guides/command" rel="noopener noreferrer"&gt;command&lt;/a&gt;&lt;/strong&gt; approach. Command is a scoped key name binding to a registered script.&lt;/p&gt;

&lt;p&gt;We may define our own command in the &lt;em&gt;package.json&lt;/em&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"contributes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"commands"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"myExtension.sayHello"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Say Hello"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;


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

&lt;/div&gt;

&lt;p&gt;And register the command in the &lt;code&gt;activate()&lt;/code&gt; function:&lt;/p&gt;

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

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;activate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&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;command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;myExtension.sayHello&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscriptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;vscode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;commands&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;registerCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello!&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;p&gt;We may trigger either internal command or our customised command from its name:&lt;/p&gt;

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

&lt;span class="nx"&gt;vscode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;commands&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;executeCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;editor.action.addCommentLine&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;Or a Uri (for text editor or webview):&lt;/p&gt;

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

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;commandUri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;vscode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`command:editor.action.addCommentLine`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;When everything relies on name, all the problems we had debated for CSS class come across. It may not cause any issue at all when the extension is small, but it is hard to tell when the extension grows big.&lt;/p&gt;

&lt;p&gt;In fact, more names and complexities come with the command Taking one of the menu contribution point in my extension as example:&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;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;contributes&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;view/title&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;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;command&lt;/span&gt;&lt;span class="dl"&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;gitlabSnippets.addHost&lt;/span&gt;&lt;span class="dl"&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;when&lt;/span&gt;&lt;span class="dl"&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;view =~ /^gitlabSnippetsExplorer-(mine|all)$/&lt;/span&gt;&lt;span class="dl"&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;group&lt;/span&gt;&lt;span class="dl"&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;navigation&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;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;The configuration above adds an icon button to the title of 2 view panels. VSCode requires:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;a href="https://code.visualstudio.com/api/references/when-clause-contexts" rel="noopener noreferrer"&gt;when clause expression&lt;/a&gt; mixing a bit flavor of bash and regular expressions;&lt;/li&gt;
&lt;li&gt;ID (like the view name “&lt;em&gt;gitlabSnippetsExplorer-mine&lt;/em&gt;” above);&lt;/li&gt;
&lt;li&gt;Position name (like the “group” above).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In return, our design needs get minimized. All we need to provide are the condition to display, the icon and the description text. &lt;/p&gt;

&lt;p&gt;In addition, thinking in command forces developers to split part of the workflow and reuse as much as possible. For example, in my GitLab Snippets Explorer extension, there is a &lt;code&gt;addHost&lt;/code&gt; command showing up as a command palette menu item and an icon button in the view panels. &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%2Feeu9dgix7waw1l0wep2b.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%2Feeu9dgix7waw1l0wep2b.png" alt="Command in the command palette"&gt;&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%2Ff5jwkdvdgzzxs8sq6dgu.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%2Ff5jwkdvdgzzxs8sq6dgu.png" alt="Command as a button"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the meantime, another &lt;code&gt;publishSnippet&lt;/code&gt; command simply jump to this adding host action if user select the “Adding new …” option below:&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%2Fhh0213lhbd3kfhe9hzt3.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%2Fhh0213lhbd3kfhe9hzt3.png" alt="Choose host step in GitLab Snippets Extension"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Events
&lt;/h4&gt;

&lt;p&gt;Both Chrome and VSCode provide a set of events to interact with.&lt;/p&gt;

&lt;p&gt;We may subscribe to a Chrome event like following:&lt;/p&gt;

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

&lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;devtools&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;network&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onNavigated&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&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;p&gt;In VSCode, we may listen to an event with a slightly shorter path, like this:&lt;/p&gt;

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

&lt;span class="nx"&gt;vscode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;workspace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onDidChangeConfiguration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;callbackFunction&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;We may fire some event manually. For example, in the VSCode TreeView, it is necessary to inform data changes to with &lt;code&gt;this.onDidChangeTreeData.fire()&lt;/code&gt;in the &lt;code&gt;TreeDataProvider&lt;/code&gt; :&lt;/p&gt;

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

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ExampleTreeProvider&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;vscode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TreeDataProvider&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Dependency&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="na"&gt;_onDidChangeTreeData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;vscode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;EventEmitter&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Dependency&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;vscode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;EventEmitter&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Dependency&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="na"&gt;onDidChangeTreeData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;vscode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Event&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Dependency&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&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;_onDidChangeTreeData&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="nf"&gt;refresh&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&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;_onDidChangeTreeData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fire&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;



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

&lt;/div&gt;

&lt;p&gt;This looks ugly and unbelievably long to me (even worse in Typescript). I thought the tedious method binding in the React class component was the worst thing I knew till I see this.&lt;/p&gt;

&lt;h3&gt;
  
  
  The communication between parts
&lt;/h3&gt;

&lt;p&gt;In Chrome extensions, communication between different parts is achieved through messaging. It is similar to the messaging between browser main thread and workers:&lt;/p&gt;

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

&lt;span class="c1"&gt;// Send a message&lt;/span&gt;
&lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hello&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="c1"&gt;// Receive a message&lt;/span&gt;
&lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onMessage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sendResponse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hello&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="nf"&gt;sendResponse&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hello from background script&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;p&gt;Instead of messaging, VSCode extension take advantage of the command design except for webview. I think it may be more reasonable for webview to execute command instead of posting message if the message type is complex.&lt;/p&gt;

&lt;h3&gt;
  
  
  UI/UX
&lt;/h3&gt;

&lt;p&gt;The command design in VSCode also provides a convenient developer experience and a consistent user experience. Developers only need to provide an icon, label, and description for most scenarios. However, this convenience can sometimes limit creativity and the overall user experience.&lt;/p&gt;

&lt;p&gt;In contrast, Chrome extensions offer more freedom for developers in terms of UI/UX design.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Testing a Chrome extension
&lt;/h3&gt;

&lt;p&gt;Testing a Chrome extension is very similar to testing a web page. There are a few differences:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You will need to manually load the compiled folder in &lt;code&gt;chrome://extensions&lt;/code&gt; and update a background script will bring you back there for a manual update.&lt;/li&gt;
&lt;li&gt;You have to find the inspection window for a background script from the extension detail page or the service worker list.&lt;/li&gt;
&lt;li&gt;You may have no idea of why things do not work due to a wrong manifest configuration such as permission missing 😖.&lt;/li&gt;
&lt;li&gt;You have to remove the developer version to revive the live installation.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Testing a VSCode extension
&lt;/h3&gt;

&lt;p&gt;Testing a VSCode extension requires more learning efforts. &lt;/p&gt;

&lt;p&gt;I confess that I am not motivated in adding &lt;a href="https://code.visualstudio.com/api/working-with-extensions/testing-extension" rel="noopener noreferrer"&gt;automation tests&lt;/a&gt; in my small extensions since the VSCode APIs are everywhere.&lt;/p&gt;

&lt;p&gt;The manual testing of a VSCode extension runs on the &lt;a href="https://code.visualstudio.com/docs/editor/debugging" rel="noopener noreferrer"&gt;debug system&lt;/a&gt; of VSCode. It is my favorite tool of debugging Node.js. Additionally, the test runs on an isolated environment and the marketplace installation settings are not affected. &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%2Fcode.visualstudio.com%2Fassets%2Fdocs%2Feditor%2Fdebugging%2Fdebug-session.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%2Fcode.visualstudio.com%2Fassets%2Fdocs%2Feditor%2Fdebugging%2Fdebug-session.png" alt="VSCode debug system"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, I find it hard to trouble shoot the root cause sometimes as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/Microsoft/vscode/issues/39388" rel="noopener noreferrer"&gt;Details of the network requests are invisible&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Error messages from the message popup or the log panel are not helpful enough, and very little tracing information is offered.&lt;/li&gt;
&lt;li&gt;Passing the local testing does not mean the published one will work perfectly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Distribution
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Distribution of Chrome extension
&lt;/h3&gt;

&lt;p&gt;To distribute a Chrome extension, we need to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Register a Google account and top-up $5 to become an eligible Chrome Web Store Developer.&lt;/li&gt;
&lt;li&gt;Pack the extension to a &lt;code&gt;.zip&lt;/code&gt; manually as &lt;a href="https://developer.chrome.com/docs/webstore/publish/" rel="noopener noreferrer"&gt;required&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Fill out a crazy long form to market the extension and declare the purpose of requesting for the permissions (I mean, each permission).&lt;/li&gt;
&lt;li&gt;Wait for several days to pass or fail the review.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I was horrified by the long form at my first submission. I didn’t expect it would force me to bring out my design tool to get the screenshot size right and the file size small. I thought we have image processing plugins for decades?&lt;/p&gt;

&lt;p&gt;What made things worse was that I waited for a long review time due to a public holiday. The result was an short Email of submission failure. It took me some time to figure out the problem was requesting some unnecessary permission and filling in unclear information.&lt;/p&gt;

&lt;p&gt;Once successfully submitting the extension, we may see the basic download times and user review comments.&lt;/p&gt;

&lt;p&gt;To understand more of the extension usage, it is possible to integrate analytic tools like Google Analytics. I forgot the details but it was not as straightforward as integrating in a normal web app.&lt;/p&gt;

&lt;p&gt;Google allow to publish an extension without showing up in their search engine and the web store. User may only access this kind of extension from a URL. This is a perfect simple work around for internal extensions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Distribution of VSCode extension
&lt;/h3&gt;

&lt;p&gt;In contrast, the process of &lt;a href="https://code.visualstudio.com/api/working-with-extensions/publishing-extension" rel="noopener noreferrer"&gt;publishing a VSCode extension&lt;/a&gt; is incredibly simple and efficient:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Register an Azure account and get a personal token with marketplace access.&lt;/li&gt;
&lt;li&gt;Install the latest version of CLI tool &lt;code&gt;vsce&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;vsce login&lt;/code&gt; with the account info.&lt;/li&gt;
&lt;li&gt;Compile the code and &lt;code&gt;vsce publish&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Usually within few hours will receive an Email of success or fail.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Well, I took one more step to summarise the process, but…&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No form fillings for the extension. All the necessary information could be found in the &lt;em&gt;package.json&lt;/em&gt;, &lt;em&gt;README.md&lt;/em&gt; just like &lt;em&gt;npm&lt;/em&gt; do.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;vsce&lt;/code&gt; does some simple review to fail fast and prevent meaningless waiting.&lt;/li&gt;
&lt;li&gt;The review process was so short that I suspect it is mostly automated as well.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, I did meet a review failure without helpful information due to a “risky” API usage. And I also did find my extension not working as expected after publishing due to packing wrongly.&lt;/p&gt;

&lt;p&gt;Similar to Chrome extension, once successfully publishing a VSCode extension, we may see the basic download times, user review comments, and a handy statics graph of the recent downloads.&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%2Fcode.visualstudio.com%2Fassets%2Fapi%2Fworking-with-extensions%2Fpublishing-extension%2Fextension-report.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%2Fcode.visualstudio.com%2Fassets%2Fapi%2Fworking-with-extensions%2Fpublishing-extension%2Fextension-report.png" alt="Extension report"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Again, it is far from enough to understand user behaviors and figuring out how to improve from these data. Most of the Node.JS monitoring tools are focusing on Node.JS server, not native tools with interfaces like this. It seems to me that I have to setup the metrics and monitoring system by my own.&lt;/p&gt;

&lt;p&gt;For the internal extensions, someone had experimented and created an extension: &lt;a href="https://marketplace.visualstudio.com/items?itemName=garmin.private-extension-manager" rel="noopener noreferrer"&gt;Private Extension Manager&lt;/a&gt;. One of my brilliant teammate did fork and made some small changes to create an internal extension marketplace.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary of what I have learnt
&lt;/h2&gt;

&lt;p&gt;First of all, here are the essentials for an extension system based on my observation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;An entry point.&lt;/strong&gt; JSON is perfect for it, but we need to design wisely. I think it is great to reuse some information like &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;version&lt;/code&gt;, &lt;code&gt;description&lt;/code&gt; from the &lt;em&gt;package.json&lt;/em&gt; but there is conflict risk to extend the file designed for another system.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Super clear document&lt;/strong&gt; about the extension concepts, the APIs, the configuration of extension parts, the distribution guide, etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Isolated runtime environment&lt;/strong&gt; for the whole extension or even for each part of the extension.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A communication channel&lt;/strong&gt; between the parts of the extension. I love the command design from VSCode, but it would be better to avoid trapping us in the naming battle and costing us to learn new expressions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;APIs&lt;/strong&gt; categorised by feature, consistent in style and similar to the existing standards to flatten the learning curve.&lt;/li&gt;
&lt;li&gt;App &lt;strong&gt;events subscription&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complete testing support&lt;/strong&gt;. It is complicated to make a good one, but we may try to use the mature system from browsers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A marketplace&lt;/strong&gt; for user to discover and developer to publish, analyse the extension usage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A review workflow&lt;/strong&gt;. Automate as much as we can, request as little information as we can.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The enhancement I believe is small but significantly improved developer experience is: &lt;strong&gt;publishing tool&lt;/strong&gt; like &lt;code&gt;vsce&lt;/code&gt;. We may fail faster with it.&lt;/p&gt;

&lt;p&gt;It is also nice to have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Helpful and traceable error messages.&lt;/li&gt;
&lt;li&gt;Official solution for internal extensions.&lt;/li&gt;
&lt;li&gt;More informative statistics.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And there are dark and bright sides to choose: &lt;strong&gt;design freedom or programming efficiency&lt;/strong&gt; and complex configuration. No matter which choice we make, extracting actions is a great choice for code reusability.&lt;/p&gt;

&lt;p&gt;Last but not least, I will try my best to &lt;strong&gt;keep away from&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Asking for declaring permission instead of finding out from the code.&lt;/li&gt;
&lt;li&gt;Names, a lot of names.&lt;/li&gt;
&lt;li&gt;Creating new rule expressions.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>chrome</category>
      <category>vscode</category>
      <category>systemdesign</category>
      <category>extension</category>
    </item>
    <item>
      <title>Combining async requests in the background</title>
      <dc:creator>Jennie</dc:creator>
      <pubDate>Tue, 09 May 2023 23:55:42 +0000</pubDate>
      <link>https://dev.to/jennieji/combining-async-requests-in-the-background-4g03</link>
      <guid>https://dev.to/jennieji/combining-async-requests-in-the-background-4g03</guid>
      <description>&lt;p&gt;When I was in Shopee, there was an interesting case about a React component triggering some API requests and a long list of this component often appears in a page. As a result, client-side send many requests in a short time and not only slowed down the interface but also occupied server resource significantly.&lt;/p&gt;

&lt;p&gt;To improve this, my smart teammate created a "batching" solution that I loved so much and used the idea in my interview later. &lt;/p&gt;

&lt;p&gt;How did we achieve this?&lt;/p&gt;

&lt;p&gt;Let me recap the situation with some detailed setups first:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;There is a &lt;code&gt;&amp;lt;ListItem/&amp;gt;&lt;/code&gt; component sends a API &lt;code&gt;GET /item&lt;/code&gt; under certain event (e.g, mouse enter) with compulsory parameters like item ID, like this:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useState&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;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Proper error handling is ignored here...&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchItem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/item?id=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ListItem&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;id&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&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;onMouseEnter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&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;span class="nf"&gt;fetchItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;setItem&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="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;onMouseEnter&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onMouseEnter&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&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;div&lt;/span&gt;&lt;span class="p"&gt;&amp;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;The &lt;code&gt;&amp;lt;ListItem/&amp;gt;&lt;/code&gt; component is used to display a long list, like this:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&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;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&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;ListItem&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;./ListItem&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;List&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;ids&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; 
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ListItem&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;)&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;div&lt;/span&gt;&lt;span class="p"&gt;&amp;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;p&gt;Now you may question that isn't the solution just sending a list request like &lt;code&gt;GET /items?ids=1,2,3&lt;/code&gt; in &lt;code&gt;&amp;lt;List/&amp;gt;&lt;/code&gt; component instead? Well, it is not the case here as &lt;strong&gt;the request is only sent when user triggers a specific event&lt;/strong&gt; on the item. But you are getting closer to the point - instead of &lt;code&gt;GET /item?id=xxx&lt;/code&gt; repeatedly with different sets of parameters, it is better to find a way to send &lt;code&gt;GET /items?ids=1,2,3&lt;/code&gt; instead.&lt;/p&gt;

&lt;p&gt;As these requests are sent at different times, here is an important decision to make: &lt;strong&gt;under what condition shall we combine the requests&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;Here are 2 ideas:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Combine the requests triggered within a certain period;&lt;/li&gt;
&lt;li&gt;Combine the requests when the request queue reaches a certain length.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Timing is the first factor to consider as if the interface is waiting for the response data from the request, we shall not keep user waiting for too long. &lt;/p&gt;

&lt;p&gt;Queuing request length however, depends more on the server side. How many items can they process at one time (considering each item may require extra information to process)? Is there any request URL (for GET requests) or body size limit (load balancer like Nginx has a default setting, and sometimes BE framework sets limit as well)?&lt;/p&gt;

&lt;h2&gt;
  
  
  Combining by time
&lt;/h2&gt;

&lt;p&gt;Speaking of time and requests, there are 2 well-known existing strategies: &lt;a href="https://www.syncfusion.com/blogs/post/javascript-debounce-vs-throttle.aspx"&gt;throttle and debounce&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Debounce delays the function invocation by a defined period of time to avoid unnecessary invocations. It works well with button clicks and key events.&lt;/p&gt;

&lt;p&gt;Throttle invokes the callback function at regular intervals as long as the event trigger is active. It suits more in the continuous events.&lt;/p&gt;

&lt;p&gt;Here I will demonstrate with a strategy inspired from throttle.&lt;/p&gt;

&lt;p&gt;First of all,  let's add a &lt;code&gt;fetchItems()&lt;/code&gt; helper:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ids&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/item?ids=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&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="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then tweak the original &lt;code&gt;fetchItem()&lt;/code&gt; to store the single set of parameters into a queue:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;queue&lt;/span&gt; &lt;span class="o"&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;fetchItem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;queque&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// TODO: flush the queue&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And handover the parameters to the &lt;code&gt;fetchItems()&lt;/code&gt; helper with a timer delaying &lt;code&gt;200ms&lt;/code&gt;.&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;queue&lt;/span&gt; &lt;span class="o"&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;fetchItem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;setTimeout&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;span class="nf"&gt;fetchItems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;quque&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;200&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;p&gt;You may notice that I only created a timer when the parameters start to queue up, and clear the queue once the batch request &lt;code&gt;fetchItems()&lt;/code&gt; is sent. &lt;/p&gt;

&lt;p&gt;Now it should work for the requests not looking forward to responses (e.g. recording user behaviours). If we would like to process with the responses, we may create a &lt;code&gt;Promise&lt;/code&gt; and cache it for later like this:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;queue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;task&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;fetchItem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&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;span class="nf"&gt;setTimeout&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;span class="nf"&gt;fetchItems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;quque&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;200&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;task&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;p&gt;The promise is wrapping the timer, and each single &lt;code&gt;fetchItem()&lt;/code&gt; "request" is returning this promise. As a result, you may get all the items in the same batch once this promise resolves:&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="nf"&gt;fetchItem&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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="nf"&gt;fetchItem&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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;items&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="nf"&gt;fetchItem&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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;items&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Furthermore, we may abstract this logic for future usage:&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;batchRequests&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;paramMerger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;period&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&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;queue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&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;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&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;span class="nf"&gt;setTimeout&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;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;paramMerger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
          &lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;period&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;task&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchItem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;batchRequests&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;fetchItems&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;ids&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;ids&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the CodeSandbox to try: &lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/combining-async-requests-by-time-8nkfhn"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Combining by count
&lt;/h2&gt;

&lt;p&gt;Combining requests by count alone is quite straightforward. Let's reuse the &lt;code&gt;fetchItems()&lt;/code&gt; created above:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MAX_QUEUE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&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;queue&lt;/span&gt; &lt;span class="o"&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;fetchItem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;MAX_QUEUE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;fetchItems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;quque&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&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;p&gt;However, considering count only is a bad idea as &lt;code&gt;fetchItem()&lt;/code&gt; may never send the request till it collects enough parameters. Therefore, we shall consider with time together, like this:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MAX_QUEUE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&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;queue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;timer&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;flushQueue&lt;/span&gt; &lt;span class="o"&gt;=&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;span class="nf"&gt;clearTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;fetchItems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;quque&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&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;fetchItem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;timer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;flushQueue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;MAX_QUEUE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;flushQueue&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;p&gt;And oops, it gets tricky if we expect the response data from the function:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MAX_QUEUE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&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;queue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;flushQueue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;null&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;fetchItem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;1&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;timer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;flushQueue&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;period&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&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;span class="nx"&gt;flushQueue&lt;/span&gt; &lt;span class="o"&gt;=&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;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;paramMerger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nf"&gt;clearTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&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="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;MAX_QUEUE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;flushQueue&lt;/span&gt;&lt;span class="p"&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;task&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;p&gt;This is because it is trying to resolve the promise for both 2 conditions, but we only create the timer and promise once for each batch. It looks quite messy and risky. We will discuss alternatives later.&lt;/p&gt;

&lt;p&gt;Similar as above, we may extract the logic for future usage:&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;batchRequests&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;paramMerger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;period&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;maxQueue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&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;queue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;flush&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;maxQueue&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Max queuable requests must be more than 1!&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="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&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;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;1&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;timer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;period&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&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;span class="nx"&gt;flush&lt;/span&gt; &lt;span class="o"&gt;=&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;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;paramMerger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
          &lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    &lt;span class="nf"&gt;clearTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&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="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;maxQueue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;flush&lt;/span&gt;&lt;span class="p"&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;task&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;p&gt;Here is the CodeSandbox to try: &lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/combining-async-requests-by-count-we7vy9"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  The alternative
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;When a logic gets tricky, it is highly likely we are doing things wrongly or simple alternative exists.&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Here for getting the &lt;code&gt;fetchItems()&lt;/code&gt; response I believe state manager such as Redux is a better alternative. With the state manager, we just need to update the items state in our modified &lt;code&gt;fetchItem()&lt;/code&gt;. Then the "reactive" framework will notify the change and render the latest data, and resolving promise is no longer expected from the &lt;code&gt;fetchItem()&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The risk
&lt;/h2&gt;

&lt;p&gt;Everything looks pretty nice before user close the tab without sending the request and your important data (e.g. data relates to advertisement charges) get lost! It is necessary to consider moving this part of the logic into &lt;code&gt;ServiceWorker&lt;/code&gt; to avoid losses. &lt;/p&gt;

&lt;h2&gt;
  
  
  Words at last
&lt;/h2&gt;

&lt;p&gt;To wrap up, handling async requests is an essential Front-end skill nowadays, and sometimes it is more than just applying &lt;code&gt;aysnc&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; and &lt;code&gt;Promise&lt;/code&gt; APIs.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>promise</category>
      <category>async</category>
    </item>
    <item>
      <title>How to write a helpful library document</title>
      <dc:creator>Jennie</dc:creator>
      <pubDate>Fri, 01 Jul 2022 08:30:46 +0000</pubDate>
      <link>https://dev.to/jennieji/better-dev-experience-starts-from-a-proper-document-30o5</link>
      <guid>https://dev.to/jennieji/better-dev-experience-starts-from-a-proper-document-30o5</guid>
      <description>&lt;p&gt;When we start to try a new tool, library or framework, what will we first look for? Document!&lt;/p&gt;

&lt;p&gt;When we are implementing a feature and forget how, what will we look for help? Document!&lt;/p&gt;

&lt;p&gt;When different fellows enquiry similar questions and we don't have time to hang around with them, what can we offer them as an alternative? Document!&lt;/p&gt;

&lt;p&gt;Yes, document plays a vital role in our life as a developer. However, writing a document is not an easy job. If the document is not good enough, it may shut our potential users out of the door and bring more hassle to us.&lt;/p&gt;

&lt;p&gt;Several years ago, I created a little formatting library which was widely used in the following years. However, instead of enjoying the happiness of the success, I did suffer from my poor document (as I mentioned in "&lt;a href="https://dev.to/jennieji/sour-and-tears-of-building-libraries-49nj"&gt;Writing library is not cool at all&lt;/a&gt;") until I received very helpful suggestions from a peer.&lt;/p&gt;

&lt;p&gt;Thanks to the poor document, I have to deal with my daily job while hanging on the internal chat app to support my precious users. Their questions mainly were like "How to format xxx?" as if they had never spent time reading my document. But, did they?&lt;/p&gt;

&lt;p&gt;Yes, they did read. There is nothing wrong with our users. When this happens, it must be the document is not doing its job well.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
The structure of a proper document

&lt;ul&gt;
&lt;li&gt;The getting started guide&lt;/li&gt;
&lt;li&gt;Common usage guides and advance guides&lt;/li&gt;
&lt;li&gt;API reference&lt;/li&gt;
&lt;li&gt;Contribution guide&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Write the understandable content&lt;/li&gt;

&lt;li&gt;Improve continuously&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  The structure of a proper document
&lt;/h2&gt;

&lt;p&gt;Like writing anything else, documents require a clear and user-friendly structure to help new or existing users find the information they want.&lt;/p&gt;

&lt;p&gt;You may get some clues from those popular libraries, tools or frameworks.&lt;/p&gt;

&lt;p&gt;The rough document structure of &lt;strong&gt;React&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Key features;&lt;/li&gt;
&lt;li&gt;A list of all the tools can help new users start their journey;&lt;/li&gt;
&lt;li&gt;A getting started tutorial;&lt;/li&gt;
&lt;li&gt;Guides for common usages;&lt;/li&gt;
&lt;li&gt;Advanced usage guides, including a testing guide;&lt;/li&gt;
&lt;li&gt;API reference;&lt;/li&gt;
&lt;li&gt;Contribution guide;&lt;/li&gt;
&lt;li&gt;FAQ.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The rough document structure of &lt;strong&gt;Lodash&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A getting started guide;&lt;/li&gt;
&lt;li&gt;Why Lodash;&lt;/li&gt;
&lt;li&gt;API reference;&lt;/li&gt;
&lt;li&gt;Advanced guide for FP;&lt;/li&gt;
&lt;li&gt;Related links, including contribution guide, etc.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The rough document structure of &lt;strong&gt;Vite&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Key features;&lt;/li&gt;
&lt;li&gt;Why Vite;&lt;/li&gt;
&lt;li&gt;A getting started guide;&lt;/li&gt;
&lt;li&gt;Guides for common usages;&lt;/li&gt;
&lt;li&gt;Similiar tool comparisons;&lt;/li&gt;
&lt;li&gt;Migration guide;&lt;/li&gt;
&lt;li&gt;API reference, config and plugin details;&lt;/li&gt;
&lt;li&gt;Related links.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We may find some patterns in these documents:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There is an attractive landing which is full of &lt;strong&gt;why new users should try this product&lt;/strong&gt;. If similar competitive products are available in the market, compare and show the advantages.&lt;/li&gt;
&lt;li&gt;Followed by a neat and &lt;strong&gt;simple getting started guide&lt;/strong&gt; to try the significant features without knowing many backgrounds.&lt;/li&gt;
&lt;li&gt;Then, another set of guides to &lt;strong&gt;explain the common usages&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;And probably a set of advanced guides folding the complex, less applied use cases.&lt;/li&gt;
&lt;li&gt;Finally, a &lt;strong&gt;searchable place&lt;/strong&gt; to list all the &lt;strong&gt;API details&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Do not forget the contribution guide if you are looking for contributors.&lt;/li&gt;
&lt;li&gt;Also, a migration guide if there are multiple major versions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The getting started guide
&lt;/h3&gt;

&lt;p&gt;Once potential users are attracted by our fantastic landing or others (advertisements, user recommendations, etc.), the getting started guide is the key to open the door to a product.&lt;/p&gt;

&lt;p&gt;Remember, a getting started guide ought to be &lt;strong&gt;as simple as no brainer&lt;/strong&gt;. Users follow the steps, and they will get an express tour of the significant features.&lt;/p&gt;

&lt;p&gt;It simply starts with an installation command with npm/yarn/pnpm or some script tags with the latest CDN links.&lt;/p&gt;

&lt;p&gt;Next, it presents the command to scaffold a new app if the product setup contains multiple steps.&lt;/p&gt;

&lt;p&gt;Lastly, it offers a minimal working example in just few lines of code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Common usage guides and advance guides
&lt;/h3&gt;

&lt;p&gt;A robust product may support various use cases that will confuse new users if we list them together. Therefore, it would be better to group the guides into 2 groups: one for common usages, the other for complex usages, and less applied usages. And place the common usage guides below the getting started guide for users who want to learn more.&lt;/p&gt;

&lt;p&gt;In comparison to the getting started guide, these guides shall be a bit juicier. By slipping in context little by little between the steps, we may gradually demonstrate the concept of this product.&lt;/p&gt;

&lt;p&gt;Furthermore, we may help users sort the guides from what they may require first and next, naming the guide with a minimal title containing keywords.&lt;/p&gt;

&lt;h3&gt;
  
  
  API reference
&lt;/h3&gt;

&lt;p&gt;Apart from the guides, API reference is essential for all the libraries, frameworks and tools opening for custom integrations. It is meant for experienced users to find in-depth information as fast as possible. As a result, the APIs must be searchable in specific ways, such as &lt;code&gt;Ctrl + F&lt;/code&gt; or a built-in search engine.&lt;/p&gt;

&lt;p&gt;To keep the document up to date and minimize the effort for maintenance, I suggest generating the API reference from the code comments (e.g., like JSDoc). Since the comments live closely with the code, developers tend to forget less about updating the document while updating the API.&lt;/p&gt;

&lt;p&gt;There are multiple choices for document generating in the market: &lt;a href="https://storybook.js.org/" rel="noopener noreferrer"&gt;Storybook&lt;/a&gt;, &lt;a href="https://typedoc.org/" rel="noopener noreferrer"&gt;TypeDoc&lt;/a&gt;, &lt;a href="https://jsdoc.app/about-getting-started.html" rel="noopener noreferrer"&gt;JSDoc 3&lt;/a&gt;, etc. Finding one that fits your project and takes time to learn is definitely worth your effort!&lt;/p&gt;

&lt;p&gt;However, a generating tool still requires extra input other than the source code. A description sentence for the constants, functions, classes, etc. and their parameters, properties, and returns would make the document more understandable. It is even better if we provide one or two code examples.&lt;/p&gt;

&lt;h3&gt;
  
  
  Contribution guide
&lt;/h3&gt;

&lt;p&gt;If we would like to attract some contributions, a comprehensive contribution guide is necessary. It should include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How to set up the project from scratch?&lt;/li&gt;
&lt;li&gt;How to run the project from local?&lt;/li&gt;
&lt;li&gt;How to test the project?&lt;/li&gt;
&lt;li&gt;What is the process of fixing a bug and developing a new feature?&lt;/li&gt;
&lt;li&gt;Is there any other ways to contribute? Like providing suggestions and filing bugs.&lt;/li&gt;
&lt;li&gt;How to communicate with the maintenance team?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Additionally, it would be better to provide clues on where to start with the different kinds of tasks for large projects. Here are some great examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/sveltejs/svelte/blob/master/CONTRIBUTING.md" rel="noopener noreferrer"&gt;Svelte&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/mui/material-ui/blob/master/CONTRIBUTING.md" rel="noopener noreferrer"&gt;Material UI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/vitejs/vite/blob/main/CONTRIBUTING.md" rel="noopener noreferrer"&gt;Vite&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Write the understandable content
&lt;/h2&gt;

&lt;p&gt;With a clear structure ready, we have accomplished half of the job. The other half, however, could be tedious yet crucial.&lt;/p&gt;

&lt;p&gt;Just like how we alter our code to optimize its readability, a remarkable document craves our patience to modify words and sentences, even formats, over and over. There is no universal standard but a few well-accepted rules. And here is a great learning resource from Google to recommend: &lt;a href="https://t.co/ioo5hnHMSX" rel="noopener noreferrer"&gt;https://t.co/ioo5hnHMSX&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Except for the wording, it is worthy of mentioning context and repetition.&lt;/p&gt;

&lt;p&gt;No matter how good is a writing expression, people get confused without sufficient context. As a result, we usually suggest avoiding acronyms and other unfamous terms. If we cannot escape the shortened expressions, we may insert links in our document to help. Read more about "Why Is Context Important in Writing?" here: &lt;a href="https://www.masterclass.com/articles/why-is-context-important-in-writing#4-types-of-context-in-writing" rel="noopener noreferrer"&gt;https://www.masterclass.com/articles/why-is-context-important-in-writing#4-types-of-context-in-writing&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And repeat. As a developer, we hate repeating, almost like having OCD. It is about the golden rule - reusability in programming but not the one in writing. To enhance the understandability of the document, we shall repeat some explanations anytime, anywhere, if it is necessary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Improve continuously
&lt;/h2&gt;

&lt;p&gt;Last but not least, a document needs our care along with the application. If this version does not work well, we shall seek feedback and suggestions to improve it.&lt;/p&gt;

&lt;p&gt;Though the effort of maintenance is often hidden, it brings us benefits the other way around, like attracting more users for our creations and avoiding trapping ourselves in the supporting chats, contributing to personal branding, soft skill improvements, etc.&lt;/p&gt;

</description>
      <category>documentation</category>
      <category>writing</category>
      <category>devrel</category>
    </item>
    <item>
      <title>Identifying code with Typescript compiler</title>
      <dc:creator>Jennie</dc:creator>
      <pubDate>Sat, 18 Jun 2022 23:03:07 +0000</pubDate>
      <link>https://dev.to/jennieji/identifying-code-with-typescript-compiler-bl9</link>
      <guid>https://dev.to/jennieji/identifying-code-with-typescript-compiler-bl9</guid>
      <description>&lt;p&gt;Since Typescript gets popular and typed code is easier to be identified, I found it is a good idea sometimes to do code automation with Typescript compiler instead of basing on the AST.&lt;/p&gt;

&lt;p&gt;For example, when I tried to find out all the React function components with AST, I may require help from tool like Babel as following simplified code shows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;transformSync&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Visitor&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="s2"&gt;@babel/core&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&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;Identifier&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="s2"&gt;@babel/types&lt;/span&gt;&lt;span class="dl"&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;funcs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

&lt;span class="nf"&gt;transformSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;parserOpts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;plugins&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;jsx&lt;/span&gt;&lt;span class="dl"&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;typescript&lt;/span&gt;&lt;span class="dl"&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;classProperties&lt;/span&gt;&lt;span class="dl"&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;optionalChaining&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="na"&gt;plugins&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;visitor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;ExportDefaultDeclaration&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;declaration&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="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;declaration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;FunctionDeclaration&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ArrowFuntionExpression&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
              &lt;span class="nx"&gt;funcs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&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="k"&gt;break&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="nc"&gt;ExportNamedDeclaration&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;node&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;declaration&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;declaration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;declaration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;FunctionDeclaration&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;declaration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                  &lt;span class="nx"&gt;funcs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;declaration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
              &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;VariableDeclaration&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="nx"&gt;declaration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;declarations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;d&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;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                  &lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ArrowFunctionExpression&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                      &lt;span class="nx"&gt;funcs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                      &lt;span class="k"&gt;break&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="k"&gt;break&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="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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;funcs&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Here my little VSCode plugin &lt;a href="https://marketplace.visualstudio.com/items?itemName=jyee721.babel-ast-explorer"&gt;Babel AST Explorer&lt;/a&gt; helped me saved a lot of time.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You may found the above &lt;code&gt;Visitors&lt;/code&gt; could only recognize following code:&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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;f&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&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 javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;f&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When they are assigned to variables, it gets even harder to identify. For instance:&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;f2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&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="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This brought me to the Typescript compiler.&lt;/p&gt;

&lt;p&gt;However, the &lt;a href="https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API"&gt;document of Typescript compiler&lt;/a&gt; is kind of unhelpful. So here are some notes of the usage of Typescript compiler that I tested in a small project. Hopefully, it could save you some time.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The basic definitions&lt;/li&gt;
&lt;li&gt;Initiating the type checking utility&lt;/li&gt;
&lt;li&gt;Finding the exports of an ES module&lt;/li&gt;
&lt;li&gt;Printing the closest type name&lt;/li&gt;
&lt;li&gt;Identifying the type syntax with flags&lt;/li&gt;
&lt;li&gt;Identifying the Node type with ts helpers&lt;/li&gt;
&lt;li&gt;Get Type object of a function&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The basic definitions
&lt;/h2&gt;

&lt;p&gt;The Typescript compiler defines several basic objects, and I often worked with these 3: &lt;code&gt;Node&lt;/code&gt;, &lt;code&gt;Symbol&lt;/code&gt;, &lt;code&gt;Type&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In my understanding,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Symbol&lt;/code&gt; is the major data structure that Typescript use to communicate in the compiler.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Node&lt;/code&gt; is the "superset" of AST node, which handles the token, the character locations, etc.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Type&lt;/code&gt; is the Typescript "type" to diagnose type safety.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Therefore, we should always get the Symbol first, and request for Node or Type if necessary.&lt;/p&gt;

&lt;p&gt;For example, we could get Symbol from source code like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;checker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSymbolAtLocation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You may wonder what is the "checker" here, we will talk about it in the next section.&lt;/p&gt;

&lt;p&gt;With the Symbol, we could find the corresponding Nodes according to the node type, like following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;declarations&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;valueDeclaration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we require to work with type then we may get it with Symbol and Node:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;checker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getTypeOfSymbolAtLocation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mySymbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;myNode&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or get from the type Node directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;checker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getTypeFromTypeNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myNode&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Initiating the type checking utility
&lt;/h2&gt;

&lt;p&gt;To figure out the type of a declaration, we will need to initiate a Typescript &lt;strong&gt;type checker&lt;/strong&gt; and parse our source code to the Typescript &lt;strong&gt;Symbol&lt;/strong&gt; first.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;ts&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;typescript&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;assert&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;assert/strict&lt;/span&gt;&lt;span class="dl"&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;program&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createProgram&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;rootNames&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;// A set of root files&lt;/span&gt;
  &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="c1"&gt;// The compiler options&lt;/span&gt;
  &lt;span class="c1"&gt;// There are 3 other options available but I am not sure what they are used for yet&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;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;program&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSourceFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&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;checker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;program&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getTypeChecker&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;moduleSymbol&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;checker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSymbolAtLocation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;moduleSymbol&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Finding the exports of an ES module
&lt;/h2&gt;

&lt;p&gt;With the type checker and symbol we get above, continue analysing from the ES module exports are a lot easier than with Babel:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;checker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getExportsOfModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;moduleSymbol&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;ex&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;span class="nx"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;declarations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;d&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;span class="c1"&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;h2&gt;
  
  
  Printing the closest type name
&lt;/h2&gt;

&lt;p&gt;The type checker could provide the closest type name with helper:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;checker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getTypeOfSymbolAtLocation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;mySymbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;mySymbol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;valueDeclaration&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;checker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;typeToString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myType&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Well, this name is honestly not very helpful in identifying whether this identifier is the right type we are looking for as we often use union or extended types. Unfortunately, I haven't found a better way yet. One possibility I could think of is to create a piece of code and request Typescript to diagnose it with the whole project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Identifying the type syntax with flags
&lt;/h2&gt;

&lt;p&gt;Typescript compiler defined several flags including &lt;code&gt;SymbolFlags&lt;/code&gt;, &lt;code&gt;SyntaxKind&lt;/code&gt;, &lt;code&gt;ModifierFlags&lt;/code&gt;, etc. I found the most helpful one is &lt;code&gt;SyntaxKind&lt;/code&gt;. &lt;code&gt;SyntaxKind&lt;/code&gt; could help to identify the type keywords from a &lt;code&gt;Node&lt;/code&gt; in the following way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kind&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SyntaxKind&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NullKeyword&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// This is a null type, do something&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While some flags like &lt;code&gt;ModifierFlags&lt;/code&gt; require a bit more operations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getCombinedModifierFlags&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myNode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ModifierFlags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Export&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// I am exported, do something&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Identifying the Node type with ts helpers
&lt;/h2&gt;

&lt;p&gt;The compiler provides a set of helpers to identify what kind of code we are dealing with. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isFunctionDeclaration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myNode&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isArrowFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myNode&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isVariableDeclaration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myNode&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isIdentifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myNode&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// ... and you can get more from auto-complete&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These helpers work with &lt;code&gt;Node&lt;/code&gt;, and look quite straightforward from their name if you are familiar with the syntax definitions. However the list of helpers too long to find the right one sometimes as Typescript tried to work with ECMA, JSDoc and its typings.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get Type object of a function
&lt;/h2&gt;

&lt;p&gt;In some cases, we may want to know the parameters and return type of a function or construct. To achieve this, we will start from the &lt;code&gt;Signature&lt;/code&gt; of them which could get from the &lt;code&gt;Type&lt;/code&gt; object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;checker&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getTypeOfSymbolAtLocation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mySymbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mySymbol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;valueDeclaration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getConstructSignatures&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;signature&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;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;paramSymbol&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class="nx"&gt;checker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getTypeOfSymbolAtLocation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;paramSymbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;paramSymbol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;valueDeclaration&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;returnType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getReturnType&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="c1"&gt;// returns Type object&lt;/span&gt;
  &lt;span class="p"&gt;}));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>typescript</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Gain and retain users for your project with user survey</title>
      <dc:creator>Jennie</dc:creator>
      <pubDate>Wed, 25 May 2022 08:55:30 +0000</pubDate>
      <link>https://dev.to/jennieji/gain-and-retain-users-for-your-project-with-user-survey-3ba5</link>
      <guid>https://dev.to/jennieji/gain-and-retain-users-for-your-project-with-user-survey-3ba5</guid>
      <description>&lt;p&gt;Survey was part of our life, but I was not aware of its importance till I read the book "Hacking Growth" and learnt about AARRR model. The book suggests that survey will not only help us to understand what our product should improve, but may also promote the product as a "side effect".&lt;/p&gt;

&lt;p&gt;In comparison to user experimentation(that I mentioned in "&lt;a href="https://dev.to/jennieji/the-2-ways-to-gain-and-retain-users-for-your-creations-1-user-experimentation-ek7"&gt;Gain and retain users for your project with user experimentation&lt;/a&gt;"), user survey could get more people involved, more sets of data, yet not certainly helpful information.&lt;/p&gt;

&lt;p&gt;One of my first few trials was surveying both our front-end developers and the backend developers about the backend API integration experiences. Our team was facing the pain of working with multiple backend teams reporting to different managers and not applying the same practices.&lt;/p&gt;

&lt;p&gt;The survey aimed to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;collecting the data and expectations of API integration from both sides;&lt;/li&gt;
&lt;li&gt;providing actionable and convincible suggestions based on data;&lt;/li&gt;
&lt;li&gt;finding out the common patterns and providing tools base on them;&lt;/li&gt;
&lt;li&gt;advertise some existing practices and benefits of adopting them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With above goals in mind, I carefully designed the questions and options, distributed with the helps from each sub-team PICs. As a result, I gain over 70% response rates from FEs, and responses from all the BE teams we were working with.&lt;/p&gt;

&lt;p&gt;The data I collected and summarized helped me to clarify the situation, and helped others to make some technical decisions(like what kind of API schema should internal tools relates to API integration adopt). However, the result of "side effect" was not obvious in a short term, as it was designed to be conducted periodically to reflect the situation.&lt;/p&gt;

&lt;p&gt;The other interesting survey I tried was altering a regular feedback form for a team sharing activity. I found the feedback collected from that form was not helpful in improving my sharing or the project I shared. Therefore, I carefully chose questions to keep the form short, provided options to reduce effort on answering open questions, and altered words to make audience think of more helpful information. Yet I was still too greedy and asked too much. And I only got less answers. The trial was a failure.&lt;/p&gt;

&lt;p&gt;Then I further simplified the form, and saved some engagement.🙈&lt;/p&gt;

&lt;p&gt;Here is the lesson I have learnt along my try-error journey.&lt;/p&gt;

&lt;p&gt;When designing a survey:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Be clear about the goal of the survey, so you won't ask unhelpful questions;&lt;/li&gt;
&lt;li&gt;Target to single group of people, and only ask related questions;&lt;/li&gt;
&lt;li&gt;Keep the survey as short as possible, so you may get more answers.&lt;/li&gt;
&lt;li&gt;Choose words carefully so you could get more helpful information. One example here is changing question like "What do you think xxx need to improve?" to "What made you want to leave xxx?".&lt;/li&gt;
&lt;li&gt;Less open questions, more selections, and the questions become easier to answer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When conducting a survey:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Choose the way to request answers. You may expect only few answers if you request for answers by volunteer, though the message could be received by more people. Or you may get more answers if you request each survey target.&lt;/li&gt;
&lt;li&gt;Set deadline and keep sending reminders. People may miss your message or simply forgot the survey.&lt;/li&gt;
&lt;li&gt;Analyze result and summarize to simple conclusions for an easier understanding.&lt;/li&gt;
&lt;li&gt;Share and discuss the results with the related parties, plan for actionable.&lt;/li&gt;
&lt;li&gt;Follow up the actionable regularly to ensure your effort is not wasted.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>survey</category>
      <category>product</category>
      <category>leadership</category>
      <category>startup</category>
    </item>
    <item>
      <title>Gain and retain users for your project with user experimentation</title>
      <dc:creator>Jennie</dc:creator>
      <pubDate>Thu, 12 May 2022 08:34:53 +0000</pubDate>
      <link>https://dev.to/jennieji/the-2-ways-to-gain-and-retain-users-for-your-creations-1-user-experimentation-ek7</link>
      <guid>https://dev.to/jennieji/the-2-ways-to-gain-and-retain-users-for-your-creations-1-user-experimentation-ek7</guid>
      <description>&lt;p&gt;As a developer, my work is highly depending on tools, as if keep finding better tools to be more efficient is part of my job. When I cannot find a tool that works for me, I create one. And I share it as I hope my hard work could help others.&lt;/p&gt;

&lt;p&gt;However, I found that sharing my creation to other people even other team members were not as simple as imagined. There are various reasons to refuse my kind offer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;They don't understand how this tool could help their work.&lt;/li&gt;
&lt;li&gt;They don't have time to learn and try out new stuff that looks not promising because it is made by an infamous person.&lt;/li&gt;
&lt;li&gt;They have no idea how to start.&lt;/li&gt;
&lt;li&gt;They worry that the tool would not be maintained well.&lt;/li&gt;
&lt;li&gt;They don't like its UI/UX etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fair enough🤷🏻‍♀️. At this point, I require skills more than coding to "sell my product". Here are 2 ways that I found really helpful for resolving most of the refuse reasons mentioned above:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;F2F user experimentation&lt;/li&gt;
&lt;li&gt;User survey&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The primary way: F2F user experimentation
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Eyewitnesses can provide very compelling legal testimony, but rather than recording experiences flawlessly, their memories are susceptible to a variety of errors and biases.&lt;br&gt;
from &lt;a href="http://noba.to/uy49tm37"&gt;Eyewitness Testimony and Memory Biases&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Observing how a user use your product by yourself can provide you a lot of unexpected information, even much more than interviewing in-person.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I got this conclusion from my first time user experimentation, and verified more times in the following experimentations I tried. I organized my wonderful first session for a tool that I would like to integrate, but it was not ready for quite some time.&lt;/p&gt;

&lt;p&gt;What I did observe was that the project owner as well as the contributors wish to receive feedback passively from their target users. Yet they only received little feedback as the tool was not ready, and they were not that helpful as who provided those feedback were in fact not using the tool. As a result, the developer team figured out "the improvements" by their instincts and struggled in gaining users.&lt;/p&gt;

&lt;p&gt;The session invited 2 major developers and 2 target users from a team who were eager to find such kind of solution to resolve their pain points.&lt;/p&gt;

&lt;p&gt;The experimentation took around 1.5 hours and went on in the following flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Firstly, introduce the experimentation, why and how;&lt;/li&gt;
&lt;li&gt;Then target user follow the instruction of doing specific things with the tool from scratch;&lt;/li&gt;
&lt;li&gt;In the meantime, developer sit next to the target user, observe their behaviour, note down the issues user has met and provide guidance if necessary;&lt;/li&gt;
&lt;li&gt;Lastly, 2 groups brainstorm together about the issues and possible improvements.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The tool developers were surprised at how buggy was the tool which they believed at least the main flow were well tested. And they also witnessed user could not easily understand how to even start from the UI/UX and the documents they provided.&lt;/p&gt;

&lt;p&gt;If these were not seen by their own eyes, you may imagine how many rounds of communications required to make them understand what was going on and convince them to "waste" more effort on writing better documents.&lt;/p&gt;

&lt;p&gt;As I expected, the developers found several points to improve, but still missed quite a few as they were busy on putting down the fire on the scene. In the end, me as the coordinator and a 3rd party helped noting down 50% more issues since I caught more details in my unique aspect.&lt;/p&gt;

&lt;p&gt;It's effective to some point, while the downside is obvious as well - it takes precious coding time to design, prepare and follow up, yet each session only covers a small group. Since the sample size is small, the opinions they provide and the issues you found may not be the right thing to care about.&lt;/p&gt;

&lt;p&gt;To obtain a larger size of sample, a well designed user survey and user behaviour tracking is more favourable, but we may not enjoy the effective observation and communication advantages from user experimentation.&lt;/p&gt;

&lt;p&gt;Here I summarized some keys from my success and failure in the user experimentation.&lt;/p&gt;

&lt;p&gt;When designing an experimentation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Select a specific feature or behaviour. Focusing on depth not breadth.&lt;/li&gt;
&lt;li&gt;Let user experience a complete process without disturbing if possible. And you may witness more.&lt;/li&gt;
&lt;li&gt;Plan for following up. By another experimentation? An interview? Or a survey? To close the feedback loop.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When conducting the experimentation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Involve the main devs, the decision makers. Witness is more convincing than being told.&lt;/li&gt;
&lt;li&gt;Devs and users should be in pairs, just like peer programming do. No one can observe so many objects at the same time.&lt;/li&gt;
&lt;li&gt;Note down observations immediately as much as possible. Or you will forgot at least half of them soon.&lt;/li&gt;
&lt;li&gt;Ask users' thoughts, discuss the possible improvements. Engage and open your mind.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>product</category>
      <category>startup</category>
      <category>tooling</category>
      <category>leadership</category>
    </item>
    <item>
      <title>Introduction to workers and why we should use them</title>
      <dc:creator>Jennie</dc:creator>
      <pubDate>Fri, 12 Nov 2021 03:21:21 +0000</pubDate>
      <link>https://dev.to/jennieji/introduction-to-workers-and-why-we-should-use-them-3mn3</link>
      <guid>https://dev.to/jennieji/introduction-to-workers-and-why-we-should-use-them-3mn3</guid>
      <description>&lt;p&gt;As we know that browser runs all the Javascript of a web page in a single thread - the main thread, any excessive Javascript code may block the main thread and makes the page look laggy or even unresponsive.&lt;/p&gt;

&lt;p&gt;We may improve this by off-loading some tasks to workers, and turns the single-threaded app into a multi-threaded, higher-performance, and potentially safer app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction of workers
&lt;/h2&gt;

&lt;p&gt;There are 3 kinds of workers in the browser context:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Web worker&lt;/strong&gt; - including &lt;strong&gt;dedicated worker&lt;/strong&gt;, and &lt;strong&gt;shared worker&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Service worker&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Worklet" rel="noopener noreferrer"&gt;Worklet&lt;/a&gt;- including &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/PaintWorklet" rel="noopener noreferrer"&gt;PaintWorklet&lt;/a&gt;, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/AudioWorklet" rel="noopener noreferrer"&gt;AudioWorklet&lt;/a&gt;, AnimationWorklet, LayoutWorklet.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I will focus on the web worker and service worker in this post, as worklet is still experimental and designed for more specific usages.&lt;/p&gt;

&lt;h3&gt;
  
  
  Web worker
&lt;/h3&gt;

&lt;p&gt;Web worker is designed as &lt;strong&gt;the background thread for running scripts&lt;/strong&gt;. Web pages are &lt;strong&gt;allow to spawn multiple workers&lt;/strong&gt; to do different tasks in an &lt;strong&gt;isolated context&lt;/strong&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%2Fweb-dev.imgix.net%2Fimage%2FtcFciHGuF3MxnTr1y5ue01OGLBn2%2FZCC24V8uqi6HfFjRzuPq.png%3Fauto%3Dformat%26w%3D1000" 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%2Fweb-dev.imgix.net%2Fimage%2FtcFciHGuF3MxnTr1y5ue01OGLBn2%2FZCC24V8uqi6HfFjRzuPq.png%3Fauto%3Dformat%26w%3D1000"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(Image from &lt;a href="https://web.dev/workers-overview/" rel="noopener noreferrer"&gt;Workers Overview&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;We may create such thread dedicated to a web page via the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Worker/Worker" rel="noopener noreferrer"&gt;Worker&lt;/a&gt; API:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myWorker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;worker.js&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;Or sharing the thread among the same origin via the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker" rel="noopener noreferrer"&gt;SharedWorker&lt;/a&gt; API.&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mySharedWorker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SharedWorker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sharedWorker.js&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;Their usage is almost the same but &lt;strong&gt;the connection between the web page and the shared worker is assigned ports&lt;/strong&gt;. To communicate with the shared worker, the web page need to go through the port assigned.&lt;/p&gt;

&lt;p&gt;In the main thread:&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;mySharedWorker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Test message to worker.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;mySharedWorker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Receive data from shared worker:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&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="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the shared worker:&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;onconnect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hi, I received your message "${e.data}"!`);
  };
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While communicating with the dedicated worker is more straightforward.&lt;/p&gt;

&lt;p&gt;In the main thread:&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;myWorker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Test message to worker.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;myWorker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onMessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Receive data from dedicated worker:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&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="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the dedicated worker:&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;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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;span class="nf"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hi, I received your message "${e.data}"!`);
};
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another difference you may notice is that the dedicated worker is coupled with the web page, hence &lt;strong&gt;it will be terminated once user close the page&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Service worker
&lt;/h3&gt;

&lt;p&gt;Service worker is designed as an &lt;strong&gt;extra layer working between the web page and the server&lt;/strong&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%2Fweb-dev.imgix.net%2Fimage%2FtcFciHGuF3MxnTr1y5ue01OGLBn2%2FGmGcVnb2y1yNc4ZIFFQ8.png%3Fauto%3Dformat%26w%3D1248" 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%2Fweb-dev.imgix.net%2Fimage%2FtcFciHGuF3MxnTr1y5ue01OGLBn2%2FGmGcVnb2y1yNc4ZIFFQ8.png%3Fauto%3Dformat%26w%3D1248"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(Image from &lt;a href="https://web.dev/workers-overview/" rel="noopener noreferrer"&gt;Workers Overview&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Unlike the web worker, it has a more complicated lifecycle:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Register in main thread via &lt;code&gt;navigator.serviceWorker.register()&lt;/code&gt;, and download the script for the service worker;&lt;/li&gt;
&lt;li&gt;Install if the service worker is newly registered, or the script is updated;&lt;/li&gt;
&lt;li&gt;Activate when no "old" service worker is controlling the clients under its scope;&lt;/li&gt;
&lt;li&gt;Redundant when it is out-dated and being replaced.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It is given &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API#interfaces" rel="noopener noreferrer"&gt;a set of APIs and events&lt;/a&gt; to achieve things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;caching data&lt;/li&gt;
&lt;li&gt;intercepting requests&lt;/li&gt;
&lt;li&gt;managing browser push notifications&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On top of these capabilities, we do a lot more interesting things. We will discuss these later.&lt;/p&gt;

&lt;h3&gt;
  
  
  Their limitations
&lt;/h3&gt;

&lt;p&gt;Both web worker and service worker are quite powerful, but meanwhile they have various limitations. Including:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No access to DOM, window object and some other APIs like localStorage&lt;/strong&gt;. This restriction keeps main thread and worker isolated and not disturbing each other. However, this also means if you would like off-load some heavy jobs like DOM operations, it's not possible with worker.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Messages between the main thread and the worker are cloned&lt;/strong&gt; via &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm" rel="noopener noreferrer"&gt;the structured clone algorithm&lt;/a&gt; which does not work with various data types. And unlike &lt;code&gt;JSON.stringify()&lt;/code&gt; simply strip off the data it does not support, passing a data type the structured clone does not support will simply throw an exception.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Experimental and &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules" rel="noopener noreferrer"&gt;limited support&lt;/a&gt; for ES modules.&lt;/strong&gt; As mordern browsers support ES module better, we could lower down quite some overhead on compiling our scripts, and run ES module directly in the browser. But, that is not quite the case in the workers. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;As of Mar 1, 2019, only &lt;a href="https://web.dev/module-workers/" rel="noopener noreferrer"&gt;Chrome 80+&lt;/a&gt; supports this feature, while &lt;a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1247687" rel="noopener noreferrer"&gt;Firefox has an open feature request&lt;/a&gt;. No other browsers are known to have support for production usage of worker scripts written as modules.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In these browsers, you may spawn a worker with ES module via:&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;worker.js&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="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;module&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;However, the ES module syntax &lt;code&gt;import&lt;/code&gt; and &lt;code&gt;export&lt;/code&gt; may not work well as you expected.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Spawning a worker from a worker almost cannot work&lt;/strong&gt;, although some browser claims that we can spawn a sub worker from a dedicated worker. This could be a bit frastrating sometimes as some library (e.g. esbuild-wasm) has built-in worker logics, which prevent us from organizing the tasks properly according to our needs, and have to communicate between the worker in the library and our own worker through the main thread message channel.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance improvement with workers
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Off-loading heavy tasks
&lt;/h3&gt;

&lt;p&gt;As introduced previously in "Introduction of workers", we may assign some heavy tasks to workers to avoid blocking the main thread and affecting user experiences.&lt;/p&gt;

&lt;p&gt;For example, when we have to group or process the raw data from BE for display, we may spawn a web worker to do this while continuing to render other things.&lt;/p&gt;

&lt;p&gt;In the main thread, we spawn a web worker, then instruct the task and wait for the result:&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getData&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;myWorker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;processData.js&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&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;span class="nx"&gt;myWorker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onMessage&lt;/span&gt; &lt;span class="o"&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;resolve&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="nx"&gt;myWorker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;terminate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;myWorker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onError&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;myWorker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;terminate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;myWorker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;sortBy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;update_time&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;span class="nf"&gt;getData&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the &lt;code&gt;processData.js&lt;/code&gt;:&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rawData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&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;processedData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rawData&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;// Do the processing&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;processedData&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;options&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;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;/get_raw_data&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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rawData&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rawData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;postMessage&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="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another classic use case is to zip/unzip files in the browser. The workers are often integrated within the library, like &lt;a href="https://www.npmjs.com/package/unzipit" rel="noopener noreferrer"&gt;unzipit&lt;/a&gt;, &lt;a href="https://www.npmjs.com/package/js-untar" rel="noopener noreferrer"&gt;js-untar&lt;/a&gt;, etc.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prefetching data
&lt;/h3&gt;

&lt;p&gt;Service worker provides APIs for caching data, and intercepting requests in the browser. This provide us an opportunity to get some data ready in advance.&lt;/p&gt;

&lt;p&gt;In the main thread, we simply register the service worker script:&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;serviceWorker&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// do make sure the service worker script is under the right folder as it will only control the requests from the scripts under the same folder&lt;/span&gt;
  &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;serviceWorker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/sw.js&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And fetch data as normal:&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="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;/get_data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// If the service worker is successfully activated and the prefetched data is ready, you shall get the response immediately&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the &lt;code&gt;sw.js&lt;/code&gt;, we load data ahead and put into cache:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;preFetchUrls&lt;/span&gt; &lt;span class="o"&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;/get_data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="nx"&gt;caches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;prefetchedData&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prefetchedData&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;preFetchUrls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;requestUrl&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;requestUrl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;prefetchedData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;requestUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;res&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;p&gt;And intercept the request to replace the response with the cache if it exists:&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="nf"&gt;addEventlistener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fetch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;preFetchUrls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respondWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &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;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prefetchedData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;caches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;prefetchedData&lt;/span&gt;&lt;span class="dl"&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;cachedResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&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="nx"&gt;request&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;cachedResponse&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nf"&gt;fetch&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="nx"&gt;request&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;h3&gt;
  
  
  Caching for loading faster next time with Workbox
&lt;/h3&gt;

&lt;p&gt;In the use case above, we made use of the caching and request intercepting ability of service worker to load some data ahead. We may also use these abilities to help our return user to load page faster by caching the necessary files.&lt;/p&gt;

&lt;p&gt;Here I would like to recommend using &lt;a href="https://developers.google.com/web/tools/workbox" rel="noopener noreferrer"&gt;Workbox&lt;/a&gt;, which will largely simplify the setups and apply different strategies easily.&lt;/p&gt;

&lt;p&gt;In the main thread, we will need to register the service worker script as before:&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;serviceWorker&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;serviceWorker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/sw.js&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While in the &lt;code&gt;sw.js&lt;/code&gt;, we will import the Workbox libraries this time, which means you will require to bundle this file before use.&lt;/p&gt;

&lt;p&gt;The example code from Workbox website is sufficient for most of the use cases:&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;registerRoute&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;workbox-routing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&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;NetworkFirst&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;StaleWhileRevalidate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;CacheFirst&lt;/span&gt;&lt;span class="p"&gt;,&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;workbox-strategies&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&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;CacheableResponsePlugin&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;workbox-cacheable-response&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&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;ExpirationPlugin&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;workbox-expiration&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Cache page navigations (html) with a Network First strategy&lt;/span&gt;
&lt;span class="nf"&gt;registerRoute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;navigate&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;NetworkFirst&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;cacheName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pages&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="c1"&gt;// Ensure that only requests that result in a 200 status are cached&lt;/span&gt;
      &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CacheableResponsePlugin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;statuses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;200&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="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Cache CSS, JS, and Web Worker requests with a Stale While Revalidate strategy&lt;/span&gt;
&lt;span class="nf"&gt;registerRoute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;destination&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;style&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
    &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;destination&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;script&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
    &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;destination&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;worker&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;StaleWhileRevalidate&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;cacheName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;assets&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CacheableResponsePlugin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;statuses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;200&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="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Cache images with a Cache First strategy&lt;/span&gt;
&lt;span class="nf"&gt;registerRoute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;destination&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CacheFirst&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;cacheName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;images&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CacheableResponsePlugin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;statuses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="c1"&gt;// Don't cache more than 50 items, and expire them after 30 days&lt;/span&gt;
      &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ExpirationPlugin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;maxEntries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;maxAgeSeconds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 30 Days&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If these are the only logics you needed in the service worker, you may also simply use a bundler plugin instead of copy paste and configure the bundler. &lt;/p&gt;

&lt;p&gt;For Webpack users, check this doc: &lt;a href="https://developers.google.com/web/tools/workbox/modules/workbox-webpack-plugin" rel="noopener noreferrer"&gt;Workbox Webpack plugin&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For Rollup users, you may try the plugin &lt;a href="https://www.npmjs.com/package/rollup-plugin-workbox" rel="noopener noreferrer"&gt;rollup-plugin-workbox&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other usages with workers
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Mocking API without Node.js server
&lt;/h3&gt;

&lt;p&gt;Once we have the ability to intercept a request, we may achieve some interesting things like mocking API. &lt;a href="https://mswjs.io/" rel="noopener noreferrer"&gt;MSW&lt;/a&gt; is an perfect example.&lt;/p&gt;

&lt;p&gt;Basically we could:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;listen to the "fetch" event;&lt;/li&gt;
&lt;li&gt;check the request matches our rules;&lt;/li&gt;
&lt;li&gt;if it matches, replace with a cached response.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Just like what I demonstrated in the "Prefetching data" use case.&lt;/p&gt;

&lt;h3&gt;
  
  
  As a data processing layer
&lt;/h3&gt;

&lt;p&gt;Another interesting use case is separating a data processing layer from our UI layer. &lt;/p&gt;

&lt;p&gt;For example, when we are tracking the usage of an app, the app may throw all the information of a component to the worker. Then the worker selects the required data, and transforms the data into the right format before finally sending them to the server.&lt;/p&gt;

&lt;p&gt;The layer isolation keeps the relatively non-critical but probably heavy tracking logics away from affecting the user experience, and crashing the app when there's something wrong.&lt;/p&gt;

&lt;h3&gt;
  
  
  Syncing in the background
&lt;/h3&gt;

&lt;p&gt;Have you encounter any super slow data operations such as uploading multiple large files, or simply the BE server respond slowly? In these cases, the user is likely have to wait for the server response before exiting it.&lt;/p&gt;

&lt;p&gt;Service worker does not have this limitation! It keeps running as long as the browser is still active. Therefore, we could cache these data somewhere in the browser first (like indexDB), and let the service worker to communicate with server instead to allow the user doing other things while waiting.&lt;/p&gt;

&lt;h3&gt;
  
  
  And more
&lt;/h3&gt;

&lt;p&gt;There are a lot more possibilities with workers besides to the stuff mentioned above, like the well-known Progressive Web App, push notifications, code sandbox (without DOM access), etc.  &lt;/p&gt;

&lt;p&gt;Let's be creative and bring the web app to the next level!&lt;/p&gt;

</description>
      <category>worker</category>
      <category>performance</category>
      <category>browser</category>
    </item>
  </channel>
</rss>
