Pitayan BlogInspiring stories, thoughts and ideas of software developmentZola2022-09-13T00:00:00+00:00https://pitayan.com/atom.xmlHere're what I've learned after 5 weeks of Leetcode study plan2022-09-13T00:00:00+00:002022-09-13T00:00:00+00:00https://pitayan.com/posts/leetcode-5-weeks-study/<p>I started using <a rel="noopener nofollow noreferrer" target="_blank" href="https://leetcode.com">leetcode.com</a> since 2016 and solved quite a lot of easy problems using Python. There were many solutions on <a href="leetcode.com">leetcode.com</a> with only one line of Python code back then. It felt very cool and I took some time practice a lot mimicking that “one-liner” for solving those problems.</p>
<p>But apparently in the end, it didn’t help me acquire any actual algorithm knowledge. My goal of doing “leetcode” that time was completely out for curiocity. What a waste of time…</p>
<p><img src="images/algo_1.png" alt="Leetcode algorithm study plan" /></p>
<p>Well, just about a few months ago, I restarted my algorithm self-learning with <a rel="noopener nofollow noreferrer" target="_blank" href="https://leetcode.com/study-plan/algorithm/">Leetcode Algorithm Study Plan</a>. I felt very content with what I have been through even the “tight” schedule of this plan had me work my ass off late after work. </p>
<p>As a result, the study plan got me more familiar with some very popular algorithms like backtrack / dynamic programming etc and also data structures like binary tree / linked list which I hardly ever used in my projects.</p>
<p>These weeks of studying really helped me fill the bucket of my wishes. Thus, in this article I’ll share my thoughts and ideas of teaching yourself algorithm online.</p>
<h1 id="what-s-in-the-leetcode-algorithm-study-plan"><a class="zola-anchor" href="#what-s-in-the-leetcode-algorithm-study-plan" aria-label="Anchor link for: what-s-in-the-leetcode-algorithm-study-plan">#</a>
What’s in the “Leetcode Algorithm Study Plan”?</h1>
<p>Well, these days there’re many online courses or leetcode notes that are telling us how to get started with learning algorithm by leetcode quetions. Of course, I did some research and tried their so-called “optimal” study material. They were all great and providing clear solutions to their algorithm problems.</p>
<p>I found these study materials all have one thing in common:</p>
<blockquote>
<p>They list out the algorithm patterns.</p>
</blockquote>
<p>This is also what the <a rel="noopener nofollow noreferrer" target="_blank" href="https://leetcode.com/study-plan/algorithm/">Leetcode Algorithm Study Plan</a> does to their training program. The problems are separated into different sections where each of them is under one algorithm pattern.</p>
<ul>
<li>Binary Search</li>
<li>Two Pointers</li>
<li>Sliding Window</li>
<li>BFS / DFS</li>
<li>Backtracking</li>
<li>Dynamic Programing</li>
<li>etc.</li>
</ul>
<p>Our mission here is to complete the given daily tasks. By the end of the study plan day, all of the problems must have turned into “completed”. Otherwise, we’ll have to start from the first question again (Don’t ask me why I know it… Well, that was a sad story…).</p>
<p>Leetcode offers 3 algorithm study plans ordered by difficuties:</p>
<ul>
<li>Algorithm I (free): 14 days</li>
<li>Algorithm II (free): 21 days</li>
<li>Algorithm III (paid): 28 days</li>
</ul>
<p>The first 2 are free but the 3rd one needs subscription. After 14 + 21 days of study, I was still not sure if I should continue with Leetcode. I dropped it and bought 1 year subscription of Algoexpert.io simply because it has a dark theme. (Leetcode’s experience is also quite good but my eyes now are very picky)</p>
<h1 id="what-i-have-learned"><a class="zola-anchor" href="#what-i-have-learned" aria-label="Anchor link for: what-i-have-learned">#</a>
What I have learned</h1>
<p>The 35 days study was sometimes like riding roller coaster and sometimes among the big waves. There’re averagely 2-3 problems each day. And there’s always one question that’s baking my noodles very badly.</p>
<p>My eago and curiosity always tried to drive me to open the solutions page and look at how other amazing developers solve the problem. There’re so many novel solutions that I think a normal person can never come up with. This made me very anxious during solving the problem. And even sometimes I felt myself being very incredibly stupid.</p>
<p><img src="images/leetcode_problems.png" alt="Leetcode problems" /></p>
<p>So I did quite a lot of search about how other developers approach their algorithm problems. The answers are such a relief and made me calm down quite a lot. In the later days of that study plan, I found myself getting into a rather appropriate rythm so that I could complete those problems peacefully on time.</p>
<p>If you had any concerns while doing the Leetcode problems, simply look at the tips below. Hope you may find them helpful.</p>
<h2 id="are-2-or-3-problems-enough-for-a-day"><a class="zola-anchor" href="#are-2-or-3-problems-enough-for-a-day" aria-label="Anchor link for: are-2-or-3-problems-enough-for-a-day">#</a>
Are 2 or 3 problems enough for a day?</h2>
<p>I think it depends, but usually 3 problems are enough for me because I’m treating them as learning new things. So it’ll take me some more time to ingest and digest the knowledge points.</p>
<p>Don’t worry the speed but instead, worry about whether you get a further understanding over that knowledge point or not.</p>
<p>During these 5 weeks, I also pushed my solutions (using Javascript) to <a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/daiyanze/algo-js">Github repo</a> so that I could always take a look at my solutions any time for a quick knowledge point review.</p>
<h2 id="i-could-only-workout-brute-force-solutions"><a class="zola-anchor" href="#i-could-only-workout-brute-force-solutions" aria-label="Anchor link for: i-could-only-workout-brute-force-solutions">#</a>
I could only workout brute-force solutions</h2>
<p>This is very common thing for those who don’t have a lot of experience on algorithm. It was tough for me to workout a rather optimized solution from beginning. Almost all of the answers I submitted to Leetcode were just some nested for loops which was far behind correct ones.</p>
<p>Here are some answers from Quora, people are event allowed to approach a problem with brute-force during interviews.</p>
<p><a rel="noopener nofollow noreferrer" target="_blank" href="https://www.quora.com/Can-I-use-the-brute-force-approach-to-solve-algorithm-questions-in-the-technical-interview">https://www.quora.com/Can-I-use-the-brute-force-approach-to-solve-algorithm-questions-in-the-technical-interview</a></p>
<p>Brute-force solutions aren’t that bad, they are good references for us to move on for finding the optimal solutions.</p>
<h2 id="how-exactly-should-i-approach-leetcode-problems"><a class="zola-anchor" href="#how-exactly-should-i-approach-leetcode-problems" aria-label="Anchor link for: how-exactly-should-i-approach-leetcode-problems">#</a>
How exactly should I approach Leetcode problems?</h2>
<p>There’re certain patterns for each type of algorithm problems. If you don’t know that pattern, you might not be able to solve it correclty.</p>
<p>Giving an example of “<a rel="noopener nofollow noreferrer" target="_blank" href="https://leetcode.com/problems/two-sum/">Two Sum</a>”, if you don’t clearly know about “binary search”, you may end up with looping all of the elements in an array. This will give you a solution under <code>O(n)</code> time complexity. But with “binary search”, the optimal time complexity is <code>O(log n)</code>. Much faster than your solution.</p>
<p><img src="images/binary_search.png" alt="binary search problems on Leetcode study plan" /></p>
<p>Sometimes, the problem can be really hard as we might not be able to come up with a brute-force solution. Then it’s time to peek the hints or just dive into the answers.</p>
<p>Well, actually I had the same doubt: Should I look at the answers? I don’t even have a solution myself yet.</p>
<p>A lot of developers on the Internet said the same thing:</p>
<blockquote>
<p>Don’t feel bad when looking at the answers</p>
</blockquote>
<p>Yes, our time is precious. Especially nowadays, we have to utilize our personal spare time for self-study. We need to remember that our mission is to know how to use these algorithms instead of finding these algorithm ourselves.</p>
<p>Now I usually set a timer of solving a problem. If 20min passed and I don’t get a clue of how I should approach this problem, then I’ll just take it as a new knowledge point and look for answers directly.</p>
<p>If I have any inspiration of solving this problem, then I’ll extend 20 min for the solution. But in total, it shouldn’t exceed 1 hour for one problem as time is too precious to me.</p>
<h2 id="why-i-kept-forgetting-what-i-have-done-before"><a class="zola-anchor" href="#why-i-kept-forgetting-what-i-have-done-before" aria-label="Anchor link for: why-i-kept-forgetting-what-i-have-done-before">#</a>
Why I kept forgetting what I have done before?</h2>
<p>It was the 3rd week of my Leetcode study plan, I took a look at some my previous problem solutions. I felt I don’t know how I solved the problem.</p>
<p>This actually made me feel very anxious. Nonetheless, it’s all because that I took at the answers and solved the problem with the given solution. I indeed understood how the answers approached the problem. But after all that, I didn’t take a deliberate practice after knowing the solutions.</p>
<p>So, in order to turn it into your own knowledge, let’s better find similar problems and practice until you feel confident about it. In this way, it might turn into a <a rel="noopener nofollow noreferrer" target="_blank" href="https://en.wikipedia.org/wiki/Short-term_memory">short-term memory</a> but you could always stimulate this part of memory by taking practices.</p>
<h1 id="what-s-after-the-leetcode-study-plan-to-me"><a class="zola-anchor" href="#what-s-after-the-leetcode-study-plan-to-me" aria-label="Anchor link for: what-s-after-the-leetcode-study-plan-to-me">#</a>
What’s after the “Leetcode Study Plan” to me?</h1>
<p>Actually, the “Leetcode Study Plan” to me is just a practice for starting the learning. It helped me find my weakness spots regarding the algorithm and data structures. </p>
<p>But indeed with those practices, I feel more confident with those data structures and algorithm than before. And when facing hard algorithm problems, I could now manage to get an approximate possible approaches (even some of them are wrong).</p>
<p><img src="images/algoexpert.png" alt="Algoexpert subscription" /></p>
<p>As for the next following months, I’ll keep on doing algorithm problems using <a rel="noopener nofollow noreferrer" target="_blank" href="https://algoexpert.io">Algoexpert.io</a>. It provides 160 problems at the moment and all of them are those certain types of problems that once you thoroughly understood you’ll be able to handle other similar or harder cases. </p>
<p>There’re of course more or less similar problems to Leetcode on Algoexpert.io. To me, they are quite good problems to let me go over those problems I’ve done earlier.
There’s no easy shortcut of learning algorithm. The only way out is to keep practicing. As I always believe: “no pain, no gain”.</p>
An indepth explanation of the Knuth-Morris-Pratt algorithm2022-06-30T00:00:00+00:002022-06-30T00:00:00+00:00https://pitayan.com/posts/kmp-algorithm-indepth/<p>String matching is a very fundamental task for computer. We’ve seen many programs that allow you to pop out the search box via <kbd>Ctrl + F</kbd> / <kbd>Cmd + F</kbd>.</p>
<p><img src="images/control_f.png" alt="Safari control+f search bar" /></p>
<p>For example, we could check if there’s a string of “search” from “I’m searching on Google”.
There’re many algorithms that could help you achieve this function. Knuth-Morris-Pratt (as known as “KMP”) is one of them (See also Rabin-Karp, Boyer-Moore).</p>
<p>This algorithm is named after the 3 inventors.
I bet you’ve heard of this book
<a rel="noopener nofollow noreferrer" target="_blank" href="https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=&ved=2ahUKEwierP6Fqbz4AhVDMXAKHc6vBFoQFnoECAsQAQ&url=https://en.wikipedia.org/wiki/The_Art_of_Computer_Programming&usg=AOvVaw0ZT6UiIjWJEf_Zv9NHZ_jp">The art of computer programming</a>. </p>
<p><img src="images/the_art_of_computer_programming.jpg" alt="The art of computer programming" /></p>
<p>The author of this book <a rel="noopener nofollow noreferrer" target="_blank" href="https://en.wikipedia.org/wiki/Donald_Knuth">Donald Knuth</a> (The famous comupter scientist) is one of the inventors of this algorithm.
(I haven’t read his book yet but let me pay my best tribute to this great scientist)</p>
<p>This algorithm was so difficult to me to understand until I found these 2 following articles from Google search.</p>
<ul>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://www.w3spot.com/2020/07/kmp-algorithm-explained-in-plain-english.html">https://www.w3spot.com/2020/07/kmp-algorithm-explained-in-plain-english.html</a></li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://www.baeldung.com/cs/knuth-morris-pratt">https://www.baeldung.com/cs/knuth-morris-pratt</a></li>
</ul>
<p>Now I wanna try to explain this amazing algorithm in my own language coz any day in the future I might need to go over this amazing creation.</p>
<h2 id="the-bruteforce-issue"><a class="zola-anchor" href="#the-bruteforce-issue" aria-label="Anchor link for: the-bruteforce-issue">#</a>
The bruteforce issue</h2>
<p>Suppose we have a string of </p>
<blockquote>
<p>“ABCD EFGHABCAGBC” (haystack) </p>
</blockquote>
<p>and a match string of </p>
<blockquote>
<p>“BCAGBC” (needle)</p>
</blockquote>
<p>We might have a bruteforce solution by comparing each of the characters in both haystack and needle as below.</p>
<ol>
<li>First character not match, move to next position</li>
</ol>
<blockquote>
<p>ABCD EFGHABCAGBC</p>
<p>^</p>
<p>BCAGBC</p>
</blockquote>
<ol start="2">
<li>First character “B” matched</li>
</ol>
<blockquote>
<p>ABCD EFGHABCAGBC</p>
<p>..^</p>
<p>..BCAGBC</p>
</blockquote>
<ol start="3">
<li>Second character “C” matched</li>
</ol>
<blockquote>
<p>ABCD EFGHABCAGBC</p>
<p>…..^</p>
<p>..BCAGBC</p>
</blockquote>
<ol start="4">
<li>Thrid character not match, move pointer to next needle character</li>
</ol>
<blockquote>
<p>ABCD EFGHABCAGBC</p>
<p>…….^</p>
<p>..BCAGBC</p>
</blockquote>
<ol start="5">
<li>First character not matched, move needle head to next haystack character</li>
</ol>
<blockquote>
<p>ABCD EFGHABCAGBC</p>
<p>…..^</p>
<p>…..BCAGBC</p>
</blockquote>
<p>Then it goes on with the previous steps… Just take a look at the GIF below, this will make you further understand it intuitively.</p>
<p><img src="images/bruteforce_solution.gif" alt="The bruteforce solution animation" /></p>
<p>Now you see the problem, right? It’s not efficient because we’re trying to move the search position to the already compared position.</p>
<p>When we compared “D” with “A” at step 4, we already know that the current haystack characters are “BCD” and even we move to the next haystack postion character “C” it will obviously fail the attempt.</p>
<p>Thus, we need a good strategy to save us from comparing characters from the already “checked” positions.</p>
<h2 id="how-does-the-kmp-magic-take-effect"><a class="zola-anchor" href="#how-does-the-kmp-magic-take-effect" aria-label="Anchor link for: how-does-the-kmp-magic-take-effect">#</a>
How does the KMP magic take effect?</h2>
<p>Actually KMP does nothing special, apart of checking the next character it’ll just do the following.</p>
<blockquote>
<p>Reuse the known information</p>
</blockquote>
<p>How?</p>
<p>Let’s flashback to step 4 when it tries to compare “D” with “C”. Instead of aligning the head character of “BCC” with next haystack character “C”, KMP will skip comparing the character “C” and move it to a later position.</p>
<p>To achieve this, KMP also has to generate a “Partial Match Table” according to the needle string. In our case, the table may look like this.</p>
<table><thead><tr><th>B</th><th>C</th><th>A</th><th>G</th><th>B</th><th>C</th></tr></thead><tbody>
<tr><td>-1</td><td>0</td><td>0</td><td>0</td><td>1</td><td>2</td></tr>
</tbody></table>
<p>(Note: Usually we set the first value of the “Partial Match Table” to -1 for programming convenience)</p>
<p>To know how this table is generated, we need to understand the following terms first</p>
<ul>
<li><strong>prefix collection</strong>: All combinations of the needle string excluding the last character</li>
<li><strong>suffix collection</strong>: All conbimations of the needle string exlucding the first character</li>
<li><strong>partial match value</strong>: The longest element length of the union of prefix collection and suffix collection</li>
</ul>
<p>Then, we’ll evaluate each “partial match string’s” value</p>
<ul>
<li>
<p>“B”</p>
<ul>
<li>prefix collection: Null</li>
<li>suffix collection: Null</li>
<li>collection union: Null</li>
<li>partial match value: 0</li>
</ul>
</li>
<li>
<p>“BC”</p>
<ul>
<li>prefix collection: [B]</li>
<li>suffix collection: [C]</li>
<li>collection union: null</li>
<li>partial match value: 0</li>
</ul>
</li>
<li>
<p>“BCA”</p>
<ul>
<li>prefix collection: [B, BC]</li>
<li>suffix collection: [CA, A]</li>
<li>collection union: null</li>
<li>partial match value: 0</li>
</ul>
</li>
<li>
<p>“BCAG”</p>
<ul>
<li>prefix collection: [B, BC, BCA]</li>
<li>suffix collection: [CAG, CA, C]</li>
<li>collection union: null</li>
<li>partial match value: 0</li>
</ul>
</li>
<li>
<p>“BCAGB”</p>
<ul>
<li>prefix collection: [B, BC, BCA, BCAG]</li>
<li>suffix collection: [CAGB, AGB, GB, B]</li>
<li>collection union: [B]</li>
<li>partial match value: 1</li>
</ul>
</li>
<li>
<p>“BCAGBC”</p>
<ul>
<li>prefix collection: [B, BC, BCA, BCAG, BCAGB]</li>
<li>suffix collection: [CAGBC, AGBC, GBC, BC, C]</li>
<li>collection union: [BC]</li>
<li>partial match value: 2</li>
</ul>
</li>
</ul>
<p>Okay, let’s continue the case in this article, step 4 -> matching “D” and “A”.</p>
<blockquote>
<p>ABCD EFGHABCAGBC</p>
<p>…….^</p>
<p>..BCAGBC</p>
</blockquote>
<p>Well, the result is false, we will need to move the needle’s position by 2. You will be needing the following formula to calculate the “positions to move”</p>
<blockquote>
<p>positions to move = matched count - partial match value</p>
</blockquote>
<p>In this case, we’ve already matched 2 characters “B” and “C”. The “partial match value” under character “A” is 0. Thus, we have to move 2 characters which aligned “D” and “B” in the same comparing position.</p>
<blockquote>
<p>ABCD EFGHABCAGBC</p>
<p>…….^</p>
<p>…….BCAGBC</p>
</blockquote>
<p>(Skip the ones in the middle “EFGHA”)
We found the head character match, there’ll be another around of comparison until the end.</p>
<blockquote>
<p>ABCD EFGHABCAGBC</p>
<p>…………………..^……….^</p>
<p>…………………..BCAGBC</p>
</blockquote>
<p>Here is the intuitive GIF to help you understand if better.</p>
<p><img src="images/kmp_solution.gif" alt="The KMP solution animation" /></p>
<h2 id="actual-code-bruteforce"><a class="zola-anchor" href="#actual-code-bruteforce" aria-label="Anchor link for: actual-code-bruteforce">#</a>
Actual code (Bruteforce)</h2>
<p>Before we dive into the KMP solution, let’s take a look at how we usually implement it “bruteforcely”.</p>
<pre data-lang="c" style="background-color:#212733;color:#ccc9c2;" class="language-c "><code class="language-c" data-lang="c"><span style="color:#ffa759;">int </span><span style="color:#ffd580;">bruteforce</span><span>(</span><span style="color:#ffa759;">char </span><span style="color:#f29e74;">*</span><span style="color:#ffcc66;">haystack</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffa759;">char </span><span style="color:#f29e74;">*</span><span style="color:#ffcc66;">needle</span><span>)
</span><span>{
</span><span> </span><span style="color:#ffa759;">int</span><span> i </span><span style="color:#f29e74;">= </span><span style="color:#ffcc66;">0</span><span style="color:#ccc9c2cc;">,
</span><span> j </span><span style="color:#f29e74;">= </span><span style="color:#ffcc66;">0</span><span style="color:#ccc9c2cc;">;
</span><span> </span><span style="color:#ffa759;">int</span><span> h_len </span><span style="color:#f29e74;">= </span><span style="color:#f28779;">strlen</span><span>(haystack)</span><span style="color:#ccc9c2cc;">;
</span><span> </span><span style="color:#ffa759;">int</span><span> n_len </span><span style="color:#f29e74;">= </span><span style="color:#f28779;">strlen</span><span>(needle)</span><span style="color:#ccc9c2cc;">;
</span><span>
</span><span> </span><span style="font-style:italic;color:#5c6773;">// When needle is an empty string, always return 0.
</span><span> </span><span style="color:#ffa759;">if </span><span>(n_len </span><span style="color:#f29e74;">== </span><span style="color:#ffcc66;">0</span><span>) </span><span style="color:#ffa759;">return </span><span style="color:#ffcc66;">0</span><span style="color:#ccc9c2cc;">;
</span><span>
</span><span> </span><span style="font-style:italic;color:#5c6773;">// Until i,j reaches the end of each string
</span><span> </span><span style="color:#ffa759;">while </span><span>(h_len </span><span style="color:#f29e74;">></span><span> i </span><span style="color:#f29e74;">&&</span><span> n_len </span><span style="color:#f29e74;">></span><span> j)
</span><span> {
</span><span> </span><span style="font-style:italic;color:#5c6773;">// Aggregate i,j when there's a match
</span><span> </span><span style="color:#ffa759;">if </span><span>(haystack[i] </span><span style="color:#f29e74;">==</span><span> needle[j])
</span><span> {
</span><span> i</span><span style="color:#f29e74;">++</span><span style="color:#ccc9c2cc;">;
</span><span> j</span><span style="color:#f29e74;">++</span><span style="color:#ccc9c2cc;">;
</span><span> }
</span><span> </span><span style="color:#ffa759;">else
</span><span> {
</span><span> </span><span style="font-style:italic;color:#5c6773;">// NOTE: This never efficient
</span><span> </span><span style="font-style:italic;color:#5c6773;">// Jump to the next character of which doesn't match
</span><span> i </span><span style="color:#f29e74;">=</span><span> i </span><span style="color:#f29e74;">-</span><span> j </span><span style="color:#f29e74;">+</span><span style="color:#ffcc66;">1</span><span style="color:#ccc9c2cc;">;
</span><span> </span><span style="font-style:italic;color:#5c6773;">// Jump to needle head
</span><span> j </span><span style="color:#f29e74;">= </span><span style="color:#ffcc66;">0</span><span style="color:#ccc9c2cc;">;
</span><span> }
</span><span> }
</span><span>
</span><span> </span><span style="font-style:italic;color:#5c6773;">// Once the while loop finishes, j should equal to needle length if there's any match
</span><span> </span><span style="color:#ffa759;">if </span><span>(j </span><span style="color:#f29e74;">==</span><span> n_len) {
</span><span> </span><span style="color:#ffa759;">return</span><span> i </span><span style="color:#f29e74;">-</span><span> j</span><span style="color:#ccc9c2cc;">;
</span><span> }
</span><span>
</span><span> </span><span style="color:#ffa759;">return </span><span style="color:#f29e74;">-</span><span style="color:#ffcc66;">1</span><span style="color:#ccc9c2cc;">;
</span><span>}
</span></code></pre>
<h2 id="actual-code-kmp"><a class="zola-anchor" href="#actual-code-kmp" aria-label="Anchor link for: actual-code-kmp">#</a>
Actual code (KMP)</h2>
<p>The coding will be separated into 2 parts:</p>
<ol>
<li>next array (partial match table)</li>
<li>main iteration</li>
</ol>
<h3 id="1-next-array"><a class="zola-anchor" href="#1-next-array" aria-label="Anchor link for: 1-next-array">#</a>
1. Next array</h3>
<p>Next array may appear very different to the explanation of the “prefix and suffix” thing. But indeed, the implementation used some tricks to make the time complexity stay as linear. In a simple word:</p>
<blockquote>
<p>To match the needle string using itself</p>
</blockquote>
<p>Note: Make sure to debug it so that you could fully understand how this amazing code works.</p>
<p>Alternatively, the blow GIF will help you out if you’re lazy for debugging.</p>
<p><img src="images/next_array.gif" alt="KMP Next Array" /></p>
<pre data-lang="c" style="background-color:#212733;color:#ccc9c2;" class="language-c "><code class="language-c" data-lang="c"><span style="color:#ffa759;">int </span><span style="color:#f29e74;">*</span><span style="color:#ffd580;">get_next</span><span>(</span><span style="color:#ffa759;">char </span><span style="color:#f29e74;">*</span><span style="color:#ffcc66;">needle</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffa759;">int </span><span style="color:#ffcc66;">len</span><span>)
</span><span>{
</span><span> </span><span style="color:#ffa759;">int</span><span> i </span><span style="color:#f29e74;">= </span><span style="color:#ffcc66;">0</span><span style="color:#ccc9c2cc;">,
</span><span> j </span><span style="color:#f29e74;">= -</span><span style="color:#ffcc66;">1</span><span style="color:#ccc9c2cc;">;
</span><span> </span><span style="font-style:italic;color:#5c6773;">// Allocate a space of heap memory to store the "partial match table"
</span><span> </span><span style="color:#ffa759;">int </span><span style="color:#f29e74;">*</span><span>next </span><span style="color:#f29e74;">= </span><span>(</span><span style="color:#ffa759;">int </span><span style="color:#f29e74;">*</span><span>)</span><span style="color:#f28779;">calloc</span><span>(len</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffa759;">sizeof</span><span>(</span><span style="color:#ffa759;">int</span><span>))</span><span style="color:#ccc9c2cc;">;
</span><span>
</span><span> </span><span style="font-style:italic;color:#5c6773;">// Make the first element -1 for convenience
</span><span> next[</span><span style="color:#ffcc66;">0</span><span>] </span><span style="color:#f29e74;">= -</span><span style="color:#ffcc66;">1</span><span style="color:#ccc9c2cc;">;
</span><span>
</span><span> </span><span style="font-style:italic;color:#5c6773;">// Until j reaches the end of needle
</span><span> </span><span style="color:#ffa759;">while </span><span>(len </span><span style="color:#f29e74;">></span><span> j) {
</span><span> </span><span style="color:#ffa759;">if </span><span>(j </span><span style="color:#f29e74;">== -</span><span style="color:#ffcc66;">1 </span><span style="color:#f29e74;">||</span><span> needle[i] </span><span style="color:#f29e74;">==</span><span> needle[j])
</span><span> {
</span><span> </span><span style="color:#f29e74;">++</span><span>i</span><span style="color:#ccc9c2cc;">;
</span><span> </span><span style="color:#f29e74;">++</span><span>j</span><span style="color:#ccc9c2cc;">;
</span><span> </span><span style="font-style:italic;color:#5c6773;">// j works as an accumulator. It gives you the number of how many
</span><span> </span><span style="font-style:italic;color:#5c6773;">// characters has matched
</span><span> next[i] </span><span style="color:#f29e74;">=</span><span> j</span><span style="color:#ccc9c2cc;">;
</span><span> }
</span><span> </span><span style="color:#ffa759;">else
</span><span> {
</span><span> j </span><span style="color:#f29e74;">=</span><span> next[j]</span><span style="color:#ccc9c2cc;">;
</span><span> }
</span><span> }
</span><span>
</span><span> </span><span style="font-style:italic;color:#5c6773;">// Caller will free the heap memory
</span><span> </span><span style="color:#ffa759;">return</span><span> next</span><span style="color:#ccc9c2cc;">;
</span><span>}
</span></code></pre>
<h3 id="2-main-iteration"><a class="zola-anchor" href="#2-main-iteration" aria-label="Anchor link for: 2-main-iteration">#</a>
2. Main iteration</h3>
<p>The main part of KMP is almost the same to building the “next array”. As the whole algorithm is actually a format of <a rel="noopener nofollow noreferrer" target="_blank" href="https://en.wikipedia.org/wiki/Finite-state_machine">“finite-state machine”</a>.</p>
<pre data-lang="c" style="background-color:#212733;color:#ccc9c2;" class="language-c "><code class="language-c" data-lang="c"><span style="color:#ffa759;">int </span><span style="color:#ffd580;">kmp</span><span>(</span><span style="color:#ffa759;">char </span><span style="color:#f29e74;">*</span><span style="color:#ffcc66;">haystack</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffa759;">char </span><span style="color:#f29e74;">*</span><span style="color:#ffcc66;">needle</span><span>)
</span><span>{
</span><span> </span><span style="color:#ffa759;">int</span><span> i </span><span style="color:#f29e74;">= </span><span style="color:#ffcc66;">0</span><span style="color:#ccc9c2cc;">,
</span><span> j </span><span style="color:#f29e74;">= </span><span style="color:#ffcc66;">0</span><span style="color:#ccc9c2cc;">;
</span><span> </span><span style="color:#ffa759;">int</span><span> h_len </span><span style="color:#f29e74;">= </span><span style="color:#f28779;">strlen</span><span>(haystack)</span><span style="color:#ccc9c2cc;">;
</span><span> </span><span style="color:#ffa759;">int</span><span> n_len </span><span style="color:#f29e74;">= </span><span style="color:#f28779;">strlen</span><span>(needle)</span><span style="color:#ccc9c2cc;">;
</span><span>
</span><span> </span><span style="font-style:italic;color:#5c6773;">// When needle is an empty string, always return 0.
</span><span> </span><span style="color:#ffa759;">if </span><span>(n_len </span><span style="color:#f29e74;">== </span><span style="color:#ffcc66;">0</span><span>) </span><span style="color:#ffa759;">return </span><span style="color:#ffcc66;">0</span><span style="color:#ccc9c2cc;">;
</span><span>
</span><span> </span><span style="font-style:italic;color:#5c6773;">// get "Partial Match Table" as next
</span><span> </span><span style="color:#ffa759;">int </span><span style="color:#f29e74;">*</span><span>next </span><span style="color:#f29e74;">= </span><span style="color:#ffd580;">get_next</span><span>(needle</span><span style="color:#ccc9c2cc;">,</span><span> n_len)</span><span style="color:#ccc9c2cc;">;
</span><span>
</span><span> </span><span style="font-style:italic;color:#5c6773;">// Until i,j reaches the end of each string
</span><span> </span><span style="color:#ffa759;">while </span><span>(h_len </span><span style="color:#f29e74;">></span><span> i </span><span style="color:#f29e74;">&&</span><span> n_len </span><span style="color:#f29e74;">></span><span> j)
</span><span> {
</span><span> </span><span style="font-style:italic;color:#5c6773;">// Aggregate i,j only when
</span><span> </span><span style="font-style:italic;color:#5c6773;">// 1. j == needle[0] == -1, this means there's no succesful match, need to restart at a new position
</span><span> </span><span style="font-style:italic;color:#5c6773;">// 2. Same character appeared in both haystack and needle
</span><span> </span><span style="color:#ffa759;">if </span><span>(j </span><span style="color:#f29e74;">== -</span><span style="color:#ffcc66;">1 </span><span style="color:#f29e74;">||</span><span> haystack[i] </span><span style="color:#f29e74;">==</span><span> needle[j])
</span><span> {
</span><span> i</span><span style="color:#f29e74;">++</span><span style="color:#ccc9c2cc;">;
</span><span> j</span><span style="color:#f29e74;">++</span><span style="color:#ccc9c2cc;">;
</span><span> }
</span><span> </span><span style="color:#ffa759;">else
</span><span> {
</span><span> </span><span style="font-style:italic;color:#5c6773;">// Assign the "Partial Match Table" value
</span><span> j </span><span style="color:#f29e74;">=</span><span> next[j]</span><span style="color:#ccc9c2cc;">;
</span><span> }
</span><span> }
</span><span>
</span><span> </span><span style="font-style:italic;color:#5c6773;">// (Don't forget to free the heap memory)
</span><span> </span><span style="color:#f28779;">free</span><span>(next)</span><span style="color:#ccc9c2cc;">;
</span><span>
</span><span> </span><span style="font-style:italic;color:#5c6773;">// Once the while loop finishes, j should equal to needle length if there's any match
</span><span> </span><span style="color:#ffa759;">if </span><span>(j </span><span style="color:#f29e74;">==</span><span> n_len) {
</span><span> </span><span style="color:#ffa759;">return</span><span> i </span><span style="color:#f29e74;">-</span><span> j</span><span style="color:#ccc9c2cc;">;
</span><span> }
</span><span>
</span><span> </span><span style="color:#ffa759;">return </span><span style="color:#f29e74;">-</span><span style="color:#ffcc66;">1</span><span style="color:#ccc9c2cc;">;
</span><span>}
</span></code></pre>
<h2 id="in-the-end"><a class="zola-anchor" href="#in-the-end" aria-label="Anchor link for: in-the-end">#</a>
In the end</h2>
<p>Honestly, the KMP algorithm is still very much confusing to me especially the reason why they implemented that way.
But it truly proves an excellent methodology of solving a “patternish” problem.
The KMP algorithm is not the most efficient algorithm compared to “Boyer Moore” and “Rabin-Karp”.
Most of our editors “find” function adopts “Boyer Moore” algorithm. I’ll write another article to explain it.</p>
Capture the flag: A Node.js web app vulnerability practice (part 1)2022-05-20T00:00:00+00:002022-05-20T00:00:00+00:00https://pitayan.com/posts/capture-web-app-vulnerabilities/<p>Last time our team had a small JavaScript workshop together (Check this article here: <a href="/posts/8-javascript-quiz-that-may-confuse-you">8 Javascript quiz that may confuse you</a>).
And we’ve done it quite well. Everyone enjoyed solving those small problems.</p>
<p>Well, this time, my teammate has brought us a practice of finding the Node.js web application vulnerabilities.</p>
<p>It’s not a hard one. There’re <strong>2</strong> vulnerabilities in the web app. The way to “capture the flag” is to succeed in login. If you made it, text of <code>flag</code> will be displayed in the page.</p>
<p><img src="images/login.gif" alt="A successful login process" /></p>
<h2 id="get-started"><a class="zola-anchor" href="#get-started" aria-label="Anchor link for: get-started">#</a>
Get Started</h2>
<p>Follow the instructions and make sure that you’ve already got everything on track. Now, you’re only allowed to look at the repository files of</p>
<ul>
<li>README.md</li>
<li>vuln1/index.js</li>
<li>uploads/*</li>
<li>package*.json</li>
</ul>
<p>Remember not to cheat before you complete the quiz (The God is watching you). Here are some notices before starting it.</p>
<ol>
<li><strong>Please don’t open <code>.env</code> file.</strong></li>
<li><strong>Please complete the quiz without changing the source code (However, debugging is allowed).</strong></li>
<li><strong>Please follow the above advices</strong></li>
</ol>
<h3 id="0-prerequisites"><a class="zola-anchor" href="#0-prerequisites" aria-label="Anchor link for: 0-prerequisites">#</a>
0. Prerequisites</h3>
<p>Make sure that you have Node 16+ install on your system (NPM v8 is required). A recommended way to install Node.js via <a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/nvm-sh/nvm">nvm</a></p>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#ffd580;">$</span><span> nvm install 16
</span><span style="color:#ffd580;">...
</span><span style="color:#ffd580;">$</span><span> nvm use 16
</span></code></pre>
<h3 id="1-clone-the-repo"><a class="zola-anchor" href="#1-clone-the-repo" aria-label="Anchor link for: 1-clone-the-repo">#</a>
1. Clone the repo</h3>
<p>Here is the repository link: https://github.com/daiyanze/vulnerability-code-challenge</p>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#ffd580;">$</span><span> git clone git@github.com:daiyanze/vulnerability-code-challenge.git
</span><span style="color:#ffd580;">$</span><span> cd crack-the-code-challenge </span><span style="color:#f29e74;">& </span><span style="color:#ffd580;">npm</span><span> i
</span></code></pre>
<h3 id="2-start-the-server"><a class="zola-anchor" href="#2-start-the-server" aria-label="Anchor link for: 2-start-the-server">#</a>
2. Start the server</h3>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#ffd580;">$</span><span> npm</span><span style="color:#ffcc66;"> -w</span><span> challenges/vuln1 run start
</span><span>
</span><span style="color:#f29e74;">></span><span> vuln1@1.0.0 </span><span style="color:#ffd580;">start
</span><span style="color:#f29e74;">></span><span> node </span><span style="color:#ffd580;">index.js
</span><span>
</span><span style="color:#ffd580;">Started</span><span> on http://localhost:81
</span></code></pre>
<h3 id="3-open-http-localhost-81"><a class="zola-anchor" href="#3-open-http-localhost-81" aria-label="Anchor link for: 3-open-http-localhost-81">#</a>
3. Open http://localhost:81</h3>
<p>Then the following screen will pop out. </p>
<p><img src="images/admin_panel.png" alt="Your everything about cracking the code starts from here" /></p>
<p>This admin page does 4 things:</p>
<ol>
<li>Modify the JSON file’s name & content</li>
<li>Upload your JSON file</li>
<li>Print out the JSON file content</li>
<li>Login</li>
</ol>
<p>Your mission now is to pass the login step, and let the page display <code>flag</code> text.</p>
<h2 id="hints"><a class="zola-anchor" href="#hints" aria-label="Anchor link for: hints">#</a>
Hints</h2>
<p>If you are still not sure how to do it, pry out the hints below 1 by 1.
But please do remember to read the code in <code>index.js</code> file first.
You’ll find many useful clues in it.</p>
<p>
<details>
<summary>Hint 1</summary>
<blockquote>
<p>What about browsing other files on the server?</p>
</blockquote>
</details>
</p>
<p>
<details>
<summary>Hint 2</summary>
<blockquote>
<p>Take a good look at the messages from your terminal.</p>
</blockquote>
</details>
</p>
<p>
<details>
<summary>Hint 3</summary>
<blockquote>
<p>How about changing the content of the <code>parsed.json</code> file?</p>
</blockquote>
</details>
</p>
<p>
<details>
<summary>Hint 4</summary>
<blockquote>
<p>You know you could mutate <code>prototypes</code> and <code>__proto__</code>, don’t you?</p>
</blockquote>
</details>
</p>
<p>
<details>
<summary>Hint 5</summary>
<blockquote>
<p>Just go to answer section. It’ll tell you how.</p>
</blockquote>
</details>
</p>
<h2 id="when-flag-is-captured"><a class="zola-anchor" href="#when-flag-is-captured" aria-label="Anchor link for: when-flag-is-captured">#</a>
When flag is captured</h2>
<p>Don’t read this section if you haven’t captured the flag. But I really hope you’ve already made it.
Otherwise the following will just lead you to a detailed explanation before you could actually resolve the mystery by yourself.</p>
<h3 id="explanation"><a class="zola-anchor" href="#explanation" aria-label="Anchor link for: explanation">#</a>
Explanation</h3>
<p>In the first few paragraphs of this article, it’s mentioning that we have 2 vulnerabilities. And they are both threatening the login step.</p>
<p>The key points are</p>
<blockquote>
<p>Where do we get the password?</p>
<p>How do we cheat the authentication?</p>
</blockquote>
<p>Once we have a clue of how to handle these things above, then the answer would be right above the surface.</p>
<p>It’s not very hard to associate the password with the mysterious <code>.env</code> file right?
Since you’ve been told not to look at it, then curiosity may have driven you toward the “method” of opening that file as what’s written in the “Hint 1”.</p>
<p>So if you try to open the content of <code>.env</code> file via “Read uploaded JSON”, the password will appear.
Oh, we got a password now. Then that should work? Certainly nope. It’s just a start.</p>
<p>Remember what’s written in the source code about authentication?</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#ffa759;">const </span><span>user </span><span style="color:#f29e74;">= </span><span>{
</span><span> </span><span style="font-style:italic;color:#5c6773;">// just pretend this comes from a db
</span><span> password</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">"$2a$12$3cSdZFdEIG9FizllB9.5E.M8DTQQ185zyITtBTBz7Lz3Va8s0xjSy"</span><span style="color:#ccc9c2cc;">,
</span><span>}</span><span style="color:#ccc9c2cc;">;
</span><span>
</span><span style="color:#ffa759;">switch </span><span>(user</span><span style="color:#f29e74;">.</span><span>auth_method </span><span style="color:#f29e74;">?? </span><span style="color:#bae67e;">"bcrypt"</span><span>) {
</span><span> </span><span style="color:#ffa759;">case </span><span style="color:#bae67e;">"bcrypt"</span><span>:
</span><span> passed </span><span style="color:#f29e74;">= </span><span>bcrypt</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">compareSync</span><span>(req</span><span style="color:#f29e74;">.</span><span>body</span><span style="color:#f29e74;">.</span><span>password</span><span style="color:#ccc9c2cc;">, </span><span>user</span><span style="color:#f29e74;">.</span><span>password)</span><span style="color:#ccc9c2cc;">;
</span><span> </span><span style="color:#ffa759;">break</span><span style="color:#ccc9c2cc;">;
</span><span> </span><span style="color:#ffa759;">case </span><span style="color:#bae67e;">"superadmin"</span><span>:
</span><span> passed </span><span style="color:#f29e74;">= </span><span>req</span><span style="color:#f29e74;">.</span><span>body</span><span style="color:#f29e74;">.</span><span>password </span><span style="color:#f29e74;">=== </span><span>process</span><span style="color:#f29e74;">.</span><span>env</span><span style="color:#f29e74;">.</span><span>SUPER_ADMIN_PASS</span><span style="color:#ccc9c2cc;">;
</span><span> </span><span style="color:#ffa759;">break</span><span style="color:#ccc9c2cc;">;
</span><span> </span><span style="color:#ffa759;">default</span><span>:
</span><span> </span><span style="color:#ffa759;">throw </span><span style="color:#f29e74;">new </span><span style="color:#73d0ff;">Error</span><span>(</span><span style="color:#bae67e;">"invaid auth method"</span><span>)</span><span style="color:#ccc9c2cc;">;
</span><span>}
</span></code></pre>
<p>The encrypted <code>password</code> is already there but you can’t decrypt it easily. So the breakthrough point is <code>ts•user.auth_method</code>.
You need to find a way to turn <code>ts•user.auth_method</code> value into <code>"superadmin"</code>. But how?</p>
<p>Well, here’s one useful information from the terminal when you install the package. There’s one critical severity vulnerability in your package.</p>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#ffd580;">added</span><span> 110 packages, and audited 111 packages in 8s
</span><span>
</span><span style="color:#ffd580;">5</span><span> packages are looking for funding
</span><span> </span><span style="color:#ffd580;">run </span><span>`</span><span style="color:#ffd580;">npm</span><span> fund` for details
</span><span>
</span><span style="color:#ffd580;">1</span><span> critical severity vulnerability
</span><span>
</span><span style="color:#ffd580;">To</span><span> address all issues, run:
</span><span> </span><span style="color:#ffd580;">npm</span><span> audit fix
</span><span>
</span><span style="color:#ffd580;">Run </span><span>`</span><span style="color:#ffd580;">npm</span><span> audit` for details.
</span></code></pre>
<p>When you tried to <code>sh•npm audit fix</code>, the solution would come to your mind easily if you had experience with “prototype pollution”.</p>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#ffd580;">added</span><span> 1 package, removed 1 package, and audited 120 packages in 2s
</span><span>
</span><span style="color:#ffd580;">10</span><span> packages are looking for funding
</span><span> </span><span style="color:#ffd580;">run </span><span>`</span><span style="color:#ffd580;">npm</span><span> fund` for details
</span><span>
</span><span style="font-style:italic;color:#5c6773;"># npm audit report
</span><span>
</span><span style="color:#ffd580;">lodash </span><span style="color:#f29e74;"><</span><span>=4.17.20
</span><span style="color:#ffd580;">Severity:</span><span> critical
</span><span style="color:#ffd580;">Prototype</span><span> Pollution in lodash - https://github.com/advisories/GHSA-jf85-cpcp-j695
</span><span style="color:#ffd580;">Prototype</span><span> Pollution in lodash - https://github.com/advisories/GHSA-fvqr-27wr-82fm
</span><span style="color:#ffd580;">Command</span><span> Injection in lodash - https://github.com/advisories/GHSA-35jh-r3h4-6jhm
</span><span style="color:#ffd580;">Regular</span><span> Expression Denial of Service (ReDoS) </span><span style="color:#ffd580;">in</span><span> lodash - https://github.com/advisories/GHSA-x5rq-j2xg-h7qm
</span><span style="color:#ffd580;">Prototype</span><span> Pollution in lodash - https://github.com/advisories/GHSA-p6mc-m468-83gw
</span><span style="color:#ffd580;">fix</span><span> available via `</span><span style="color:#ffd580;">npm</span><span> audit fix</span><span style="color:#ffcc66;"> --force</span><span>`
</span><span style="color:#ffd580;">Will</span><span> install lodash@4.17.21, which is outside the stated dependency range
</span><span style="color:#ffd580;">node_modules/lodash
</span><span>
</span><span style="color:#ffd580;">1</span><span> critical severity vulnerability
</span><span>
</span><span style="color:#ffd580;">To</span><span> address all issues, run:
</span><span> </span><span style="color:#ffd580;">npm</span><span> audit fix</span><span style="color:#ffcc66;"> --force
</span></code></pre>
<p>Here is a very good article about “prototype pollution”.</p>
<p>https://brightsec.com/blog/prototype-pollution/</p>
<p>In a simple word, “Prototype pollution” is “to extend / modify the global objects”. </p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="font-style:italic;color:#5ccfe6;">Object</span><span style="color:#f29e74;">.</span><span>__proto__</span><span style="color:#f29e74;">.</span><span>auth_method </span><span style="color:#f29e74;">= </span><span style="color:#bae67e;">"superadmin"
</span><span style="font-style:italic;color:#5ccfe6;">Object</span><span style="color:#f29e74;">.</span><span>constructor</span><span style="color:#f29e74;">.</span><span>prototype</span><span style="color:#f29e74;">.</span><span>auth_method </span><span style="color:#f29e74;">= </span><span style="color:#bae67e;">"superadmin"
</span></code></pre>
<p>With the above trick, you could just add some extra seasonings to that JSON content. Now you are good to capture the flag.</p>
<pre data-lang="json" style="background-color:#212733;color:#ccc9c2;" class="language-json "><code class="language-json" data-lang="json"><span>{</span><span style="color:#bae67e;">"__proto__"</span><span style="color:#ccc9c2cc;">:</span><span>{</span><span style="color:#bae67e;">"auth_method"</span><span style="color:#ccc9c2cc;">:</span><span style="color:#bae67e;">"superadmin"</span><span>}}
</span></code></pre>
<p>The vulnerability that helps you “overcome” the password authentication comes from <code>lodash <= 4.17.20</code>. Refer to this <a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/advisories/GHSA-jf85-cpcp-j695">page</a> to get more details.</p>
<h3 id="answer"><a class="zola-anchor" href="#answer" aria-label="Anchor link for: answer">#</a>
Answer</h3>
<p>The solution takes only 3 steps.</p>
<ol>
<li>Input the following to “Secure JSON formatter” <code>html•<textarea></code> and click “Format & Upload” button.</li>
</ol>
<pre data-lang="json" style="background-color:#212733;color:#ccc9c2;" class="language-json "><code class="language-json" data-lang="json"><span>{</span><span style="color:#bae67e;">"__proto__"</span><span style="color:#ccc9c2cc;">:</span><span>{</span><span style="color:#bae67e;">"auth_method"</span><span style="color:#ccc9c2cc;">:</span><span style="color:#bae67e;">"superadmin"</span><span>}}
</span></code></pre>
<ol start="2">
<li>
<p>Input <code>../.env</code> to “Read uploaded JSON” and click “Read” button</p>
</li>
<li>
<p>Input what you get from step 2 to “Login” password</p>
</li>
</ol>
<p>Then you’ll get a <code>flag</code> text in the page.</p>
<h2 id="summary"><a class="zola-anchor" href="#summary" aria-label="Anchor link for: summary">#</a>
Summary</h2>
<p>This practice itself isn’t a very hard one. But at least I think gave us some good warnings of taking care of our project’s securities.</p>
<p>The first important thing in making secure applications is to carry out “user-input validation & sanitization” all the time and never use those inputs directly.</p>
<blockquote>
<p>When shall we trust the end users? When there’s no input at all.</p>
</blockquote>
<p>The second important thing is from our projects. Some of the developers don’t usually pay attention to the information from the <code>npm audit</code>.
There’re so many reasons of “we don’t have to” (you know why :p).
But when it comes to some security concerns, <code>npm audit</code> has already given us enough clues to follow up with.
Thus, constantly check and update our dependencies will probably do us a favor in building a rather secured application.</p>
Interview Officer: "Tell me about Cookies Session and Tokens". Me: "Emm..."2022-05-12T00:00:00+00:002022-05-12T00:00:00+00:00https://pitayan.com/posts/sessions-cookies-and-tokens/<p>In late 2021, I took an interview with a hotshot IT company for a front end engineer position.
During the interview process, the interview officer’s question of “Tell me about cookie session and token” got me into my knowledge blind spot. </p>
<p>I will be explaining some details around “cookies” “sessions” and “tokens”.<br />
Meanwhile, hope this article will remind everyone to strengthen related knowledge points so that it could come to help any day in the future when necessary.</p>
<h2 id="the-interview-briefing"><a class="zola-anchor" href="#the-interview-briefing" aria-label="Anchor link for: the-interview-briefing">#</a>
The interview briefing</h2>
<p>It was an online interview with only interview officer and me in that meeting. The officer was a bit late but he was still very nice and polite. He introduced himself and the interview flow.</p>
<ol>
<li>Self introduction</li>
<li>Recent projects</li>
<li>Fundamental knowledge quiz</li>
<li>Algorithm quiz</li>
</ol>
<p>There’re quite few casual talks among the interview and the whole process took about 50 min (should be 1 hour but he was late, so 10 min was gone…). I did my best to answer each questions as I could. </p>
<p>However, I got stuck when it came to the question below.</p>
<p><em>Interview officer: “How much do you know about ‘Sessions’ ‘Cookies’ and ‘Tokens’? Could you explain by giving some examples?”</em></p>
<p><em>Me: Emm… Sure. Well, cookie is emm… Sorry, I think I can answer it. But could I have 1 minute to prepare the answer?</em></p>
<p>(Actually even with 5 minutes, I think I still can’t get through it.
My brain was simply all blank… Frankly speaking, even I could answer it I think I was only fighting for my ego.)</p>
<p>Then after 1 minute, I began my speech with some stuttering: “Cookie stores on the client side. Session is kept on server side. Token is authorization / authentication credential that helps querying resources. And uh…” (paused for several seconds)</p>
<p><em>Interview Officer: Okay, sounds good. Anything else to add? What about the differences between sessions and tokens?</em></p>
<p><em>Me: Emm… (paused for another several seconds)</em></p>
<p>As a result the interview turned into a failed attempt. And after that day of the interview, I’ve set up my mind to study those fundamental knowledge about “Session” “Cookies” and “Tokens”.</p>
<h2 id="the-evolutionary-history-of-session"><a class="zola-anchor" href="#the-evolutionary-history-of-session" aria-label="Anchor link for: the-evolutionary-history-of-session">#</a>
The evolutionary history of session</h2>
<p>From long ago, our Internet was just a place full of documents for simple browsering purpose.
The server doesn’t need to remember “who” and “what”. Every single HTTP request was just a pure request towards document contents.</p>
<p>Later when users are looking for more interactive functions for example shopping cart and credit card payments, websites started facing a problem which is to memorize and manage the user related information.
Like “who” put “what” product into “which” shopping cart. </p>
<p>This means website needs the ability to distinguish every single user.
It’s a challenging task to the servers since HTTP doesn’t really fit in such “stateful” scenario.</p>
<p>Servers could simply add a “mark” (session id) to each of those who visited / logged-in / purchased.
In another word, this “mark” is a random and unique string that is created and remains in memory of the web server and can be attached to the HTTP request.
This “mark” is also stored on the user’s browser as so-called “Cookies”. When user started a new request, server will then understand who is requesting.</p>
<p>Such solution brought convenience to the users, however the servers were under a huge burden of remembering each user’s session id.
As a result, hundreds and thousands of session ids were kept on the servers. A web service’s backend be a cluster (or several clusters) containing tons of server nodes and each of them has to store user’s session id.</p>
<p>Well, this sounds like a “fair” solution by far, isn’t it? </p>
<p>But what if a user login from “server A” the first time and redirects to another page via “server B” the second time? “server B” doesn’t have the user session id yet… How could “server B” handle such case?</p>
<p>Now, here comes the role of <a rel="noopener nofollow noreferrer" target="_blank" href="https://docs.aws.amazon.com/elasticloadbalancing/latest/application/sticky-sessions.html">sticky-session</a> (also check out this <a rel="noopener nofollow noreferrer" target="_blank" href="https://www.imperva.com/learn/availability/sticky-session-persistence-and-cookies/">article</a>).
The simplest solution is to only allow user to stick with “server A” which maintains user’s session id.
But what if “server A” crashed and cannot respond to user’s request? (the well-known <a rel="noopener nofollow noreferrer" target="_blank" href="https://en.wikipedia.org/wiki/Single_point_of_failure">“single-point-of-failure”</a>)</p>
<p><img src="images/copy_session_id.png" alt="Copy sessions from server A to server B" /></p>
<p>We can copy the session id from “server A” to “server B”. When “server A” crashed, “server B” will still help handle requests.
But it’s not yet optimal because doing “copy-paste” between servers are clearly “cost-effective” especially when there are many many users.</p>
<p>How about we store session ids in another dedicated server as “session server”, away from those “servers” that are providing page routing / rendering.
And such server will only maintain the user session ids. To avoid having “single-point-of-failuire”, let’s group multiple “session servers” together as one cluster.</p>
<p><img src="images/session_cluster.png" alt="Web server cluster and session server cluster" /></p>
<p>Okay, we separated the website servers into “web server cluster” and “session server cluster”.
(<a rel="noopener nofollow noreferrer" target="_blank" href="https://redis.io">Redis</a> and <a rel="noopener nofollow noreferrer" target="_blank" href="https://memcached.org">Memcached</a> are those popular solutions to session management)
Even though the sessions are reliable now, managing this little “session” is yet a big burden to the maintainers no matter what.</p>
<p>So, is there a simple and feasible way to manage those sessions only on the client side to make both users and maintainers happy?
Which can be interpreted as: a user login to the website but the website doesn’t store the user session. </p>
<p>We can let the server return a “Token” string (the most common example is <a rel="noopener nofollow noreferrer" target="_blank" href="https://jwt.io">JWT</a>) to each logged-in users similarly to “Cookies”.
In order to prevent bad guys faking the “Tokens”, we use a hash algorithm (for example HS256, RS256) and a secret key that only we know to generate a “signature”.
This “signature” together with the user id (can also use some other user related information) treated as “Token” is what’s finally stored in the user’s browser.</p>
<p><img src="images/token.png" alt="Token generation" /></p>
<p>When user sends a request together with this token, servers will take some parts (“header” and “payload”) from the token and regenerate the “signature”. If the signature of the given “token” is the same to the regenerated one, then the user can be considered as logged in. Server should fetch and return the user data. Otherwise, authentication should fail.</p>
<p>Note that,</p>
<blockquote>
<p>Signature is not encryption!</p>
</blockquote>
<p>This means we can’t put any sensitive data into the “Token”. Because people will still get what’s written in the “Token” easily.
The below is an example of “JWT” which looks like already encrypted. But actually it’s just a <a rel="noopener nofollow noreferrer" target="_blank" href="https://en.wikipedia.org/wiki/Base64">Base64</a> encoded string.</p>
<pre data-lang="txt" style="background-color:#212733;color:#ccc9c2;" class="language-txt "><code class="language-txt" data-lang="txt"><span>eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJNZSJ9.Ftkmp-5WXYuhgSya_Ah97SBQ1ffR5WlWl_1lLsK3DyM
</span></code></pre>
<p>With any “Token” decoding tool you could find on the Internet, this “Token” can be decoded into:</p>
<pre data-lang="json" style="background-color:#212733;color:#ccc9c2;" class="language-json "><code class="language-json" data-lang="json"><span>{ </span><span style="color:#ff3333;">alg</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">"HS256"</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ff3333;">typ</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">"JWT"</span><span>}.{ </span><span style="color:#ff3333;">userId</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">"Me" </span><span>}.[</span><span style="color:#ff3333;">Signature</span><span>]
</span></code></pre>
<p>Wait… If this can be decoded and seen by anyone else, then it is never safe, isn’t it? </p>
<p>Right, if someone stole your token and pretend he is you then server doesn’t know this “someone” is the real you or not. </p>
<p>In another word, </p>
<blockquote>
<p>One with a “valid” token is a “valid” user.</p>
</blockquote>
<p>But this indeed removes the burden of servers managing sessions for user since servers just need to authenticate whether the “token” is valid without remembering any sessions.
Engineers can now do scalings and maintanences without worrying much about sessions.</p>
<h2 id="more-about-cookies"><a class="zola-anchor" href="#more-about-cookies" aria-label="Anchor link for: more-about-cookies">#</a>
More about “Cookies”</h2>
<p>Cookies are small pieces of data (no more than 4kb) that websites store on your computer as records of your interactions with them.
They’re used by websites to keep track of who you are and to remember things like your indentity information.</p>
<p>This is how websites are able to remember you as you move between pages and return to the same login screen or the same shopping cart.
Nowadays, web apps also utilizes cookies to allow users to save their preferences for instance themes / local settings.</p>
<p>Some key points of “Cookies”.</p>
<ul>
<li>Maximum 4kb of data stored on user’s computer</li>
<li>Not able to cross origin but Top-level domain cookies can be shared to Second-level (and higher levels) domain</li>
<li>Cookies can be expired</li>
<li>Cookies’ number is limited for each domain</li>
</ul>
<h2 id="more-about-sessions"><a class="zola-anchor" href="#more-about-sessions" aria-label="Anchor link for: more-about-sessions">#</a>
More about “Sessions”</h2>
<p>Session is a mechanism to save the clients’ information on the server with a certain data structure.
It represents the process of a session between the server and the client.</p>
<p>For instance, a user session data may contain following information</p>
<ul>
<li>user profile</li>
<li>user authority and authentication</li>
<li>user group</li>
<li>etc</li>
</ul>
<p>In this way, information stored in the session object will not be lost when the user jumps between the application’s web pages, and it will persist throughout the user’s visiting period. </p>
<p>However, session will probably be closed when user proactively closes the session, for example closing pages or logging out. To add more, session will also be closed when it expires.</p>
<p>The server’s session usually needs to collaborate with browser cookies to specify the user’s identity because the browser doesn’t know who this “John Doe” is. </p>
<p>Thus, what’s most commonly seen inside a cookie value is “session_id” which is a unique identification to help server understand the vistor’s identity to proceed to some further actions (e.g. fetch user data).</p>
<p><img src="images/browser_server_session.png" alt="How browser and server handle session" /></p>
<p>Let’s take a look at a real world example.</p>
<p>A user login to a site, the server handles the login request and return the response with a cookie which contains “session_id” in it and gets stored in the user’s browser. </p>
<p>Then the second time the user redirects himself to a page that requires a login. The request brings the cookie to server.
And then the server finds that this user has already logged in, it redirects the user with the target page.</p>
<p>Some key points of “Sessions”</p>
<ul>
<li>“Session” and “Cookies” are under a cooperative relationship</li>
<li>Able store any kinds of data structure as identification information</li>
<li>Stores on server side. Safer than cookies</li>
<li>“Session” can be expired</li>
</ul>
<h2 id="more-about-tokens"><a class="zola-anchor" href="#more-about-tokens" aria-label="Anchor link for: more-about-tokens">#</a>
More about “Tokens”</h2>
<p>(The “Tokens” here we talk about is the “Web Tokens”. The BlockChain related tokens are not in this topic)</p>
<p>“Token” is a series of strings generated by the servers as a token for the user’s request.
When the user visits the server for the first time, the server will generate a “Token” using algorithms (for instance, <a href="RS256%5D(https://auth0.com/blog/rs256-vs-hs256-whats-the-difference/)">HS256 / RS256</a>) and a secret key according to the unique identifier passed in.
Then the “Token” is encoded with <a rel="noopener nofollow noreferrer" target="_blank" href="https://en.wikipedia.org/wiki/Base64">Base64</a> and returned to the user side.
The “Token” can be saved locally as a file or in browser’s memory. In the second time visit, the “Token” will get attached to the requests.
And then server will validate the token via the same algorithms and the secret key.</p>
<p>Some key points of “Tokens”</p>
<ul>
<li>Should be attached to every “resource-fetching” HTTP request’s header when the request requires authentication / validation</li>
<li>No server-side session required</li>
<li>Supports mobile devices naturally</li>
<li>“Tokens” can be expired</li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS">CORS</a> is made available especially when we need to collaborate with other public services or applications or even devices</li>
<li>Able to circumvent <a rel="noopener nofollow noreferrer" target="_blank" href="https://en.wikipedia.org/wiki/Cross-site_request_forgery">CSRF</a> because “Token” is authenticated on the server-side and can be expired</li>
</ul>
<h3 id="access-token"><a class="zola-anchor" href="#access-token" aria-label="Anchor link for: access-token">#</a>
Access token</h3>
<p>An “Access token” is the “Token” mentioned before which is carried together with the requests and as a credential for fetching resources via API endpoints. It has a fixed expire to make user re-authenticate periodically which prevents security issues to a certain extent.</p>
<p>A simple “Access token” can be composed by </p>
<ul>
<li>unique identifier</li>
<li>timestamp</li>
<li>signature (a generated string based on )</li>
</ul>
<p><img src="images/access_token_process_flow.png" alt="How access token works" /></p>
<h3 id="refresh-token"><a class="zola-anchor" href="#refresh-token" aria-label="Anchor link for: refresh-token">#</a>
Refresh token</h3>
<p>A “Refresh token” is a good helper to ask server to regenerate a new “Access token”.
It’s considered as a convenient solution because generating the “Access token” requires user’s id and password everytime.</p>
<p>Same to “Access token”, “Refresh token” also has an expiry date attached to it but “Refresh token” has a longer expiry.
Once “Refresh token” expired, user has to redo the login to get a pair of new “Access token” and “Refresh token”.</p>
<p><img src="images/refresh_token_process_flow.png" alt="How refresh token works" /></p>
<p>Usually, “Refresh token’s” expiry datetime is stored in the database, it’ll be authenticated and validated only when re-issuing the “Access token”.
Thus, it has a very minimal effect to the backend server performance.</p>
<h3 id="json-web-token-jwt"><a class="zola-anchor" href="#json-web-token-jwt" aria-label="Anchor link for: json-web-token-jwt">#</a>
JSON Web Token (JWT)</h3>
<p>A “JWT” string contains</p>
<ul>
<li>Header (hash algo for signature)</li>
<li>Payload (a user data structure containing the unique indentifier)</li>
<li>Signature (a generated string based on <code>Header</code> and <code>Payload</code>)</li>
</ul>
<p>According to <a rel="noopener nofollow noreferrer" target="_blank" href="https://jwt.io/introduction">jwt.io</a>,</p>
<blockquote>
<p>JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object.
This information can be verified and trusted because it is digitally signed.
JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA or ECDSA.</p>
</blockquote>
<p>To make it short, JWT is a solution to provide authenticated info for crossing origins.
The official document has done a very clear and in-depth explanation about JWT’s features.
Allow me skip re-explaining.</p>
<p>“JWT” truly brought us engineers a lot of advatanges. However, its security issue is a nonnegligible topic.
There’s already a good alternative <a rel="noopener nofollow noreferrer" target="_blank" href="https://paseto.io">PASETO</a> to replace “JWT”.</p>
<p>For those who are interested in the “JTW” securities, here’re some good articles:</p>
<ul>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://curity.io/resources/learn/jwt-best-practices/">https://curity.io/resources/learn/jwt-best-practices/</a></li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://paragonie.com/blog/2017/03/jwt-json-web-tokens-is-bad-standard-that-everyone-should-avoid">https://paragonie.com/blog/2017/03/jwt-json-web-tokens-is-bad-standard-that-everyone-should-avoid</a></li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="http://cryto.net/%7Ejoepie91/blog/2016/06/13/stop-using-jwt-for-sessions">http://cryto.net/~joepie91/blog/2016/06/13/stop-using-jwt-for-sessions</a></li>
</ul>
<h2 id="in-the-end"><a class="zola-anchor" href="#in-the-end" aria-label="Anchor link for: in-the-end">#</a>
In the end</h2>
<p>Even this article has already covered quite a few knowledge points of sessions cookies and tokens, but there’re a lot more to investigate.</p>
<p>As for the interview I took that time, I think I was good at interactions and double-confirming questions which contributed to a smooth interview process.</p>
<h2 id="references"><a class="zola-anchor" href="#references" aria-label="Anchor link for: references">#</a>
References</h2>
<ul>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://docs.aws.amazon.com/elasticloadbalancing/latest/application/sticky-sessions.html">https://docs.aws.amazon.com/elasticloadbalancing/latest/application/sticky-sessions.html</a></li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://www.imperva.com/learn/availability/sticky-session-persistence-and-cookies">https://www.imperva.com/learn/availability/sticky-session-persistence-and-cookies</a></li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://en.wikipedia.org/wiki/Single_point_of_failure">https://en.wikipedia.org/wiki/Single_point_of_failure</a></li>
</ul>
8 Javascript quiz that may confuse you2022-05-02T00:00:00+00:002022-05-02T00:00:00+00:00https://pitayan.com/posts/8-javascript-quiz-that-may-confuse-you/<p>These days, I was preparing a small game for our team’s tech workshop. Thought it’d be a good opportunity of introducing the some fundamental and tricky stuffs around JavaScript.
So I made 8 quiz to our team members. And hope they could solve them within 15 min. Eventually, it took all of them over 20 minutes to complete and most of them could solve 4-5 questions correctly.</p>
<p>You can take it as just a small test, each quiz has answer attached to the end of the code. Try to answer them first and then look at the answers. Good luck.</p>
<h2 id="what-do-these-console-log-print-out"><a class="zola-anchor" href="#what-do-these-console-log-print-out" aria-label="Anchor link for: what-do-these-console-log-print-out">#</a>
What do these console.log print out?</h2>
<h3 id="no-1-doctor-pavlov-has-a-dog"><a class="zola-anchor" href="#no-1-doctor-pavlov-has-a-dog" aria-label="Anchor link for: no-1-doctor-pavlov-has-a-dog">#</a>
No. 1 – Doctor Pavlov has a dog</h3>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#ffa759;">function </span><span style="color:#ffd580;">Animal</span><span>(){
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span>type </span><span style="color:#f29e74;">= </span><span style="color:#bae67e;">"animal"
</span><span>}
</span><span>
</span><span style="color:#ffa759;">function </span><span style="color:#ffd580;">Dog</span><span>(){
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span>name </span><span style="color:#f29e74;">= </span><span style="color:#bae67e;">"dog"
</span><span>}
</span><span>
</span><span style="font-style:italic;color:#5ccfe6;">Dog</span><span style="color:#f29e74;">.</span><span>prototype </span><span style="color:#f29e74;">= new </span><span style="color:#73d0ff;">Animal</span><span>()
</span><span>
</span><span style="color:#ffa759;">var </span><span>PavlovPet </span><span style="color:#f29e74;">= new </span><span style="color:#73d0ff;">Dog</span><span>()</span><span style="color:#ccc9c2cc;">;
</span><span>
</span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(PavlovPet</span><span style="color:#f29e74;">.</span><span>__proto__ </span><span style="color:#f29e74;">=== </span><span style="font-style:italic;color:#5ccfe6;">Dog</span><span style="color:#f29e74;">.</span><span>prototype)
</span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(</span><span style="font-style:italic;color:#5ccfe6;">Dog</span><span style="color:#f29e74;">.</span><span>prototype</span><span style="color:#f29e74;">.</span><span>__proto__ </span><span style="color:#f29e74;">=== </span><span style="font-style:italic;color:#5ccfe6;">Animal</span><span style="color:#f29e74;">.</span><span>prototype)
</span></code></pre>
<p>
<details>
<summary>Answer for No. 1</summary>
<p>Very basic stuff about the prototype chain.</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="font-style:italic;color:#5c6773;">// Output
</span><span style="color:#ffcc66;">true
</span><span style="color:#ffcc66;">true
</span></code></pre>
</details>
</p>
<h3 id="no-2-be-careful-with-the-sort"><a class="zola-anchor" href="#no-2-be-careful-with-the-sort" aria-label="Anchor link for: no-2-be-careful-with-the-sort">#</a>
No. 2 – Be careful with the “sort”</h3>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#ffa759;">var </span><span>arr </span><span style="color:#f29e74;">= </span><span>[</span><span style="color:#ffcc66;">5</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">22</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">14</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">9</span><span>]</span><span style="color:#ccc9c2cc;">;
</span><span>
</span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(arr</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">sort</span><span>())</span><span style="color:#ccc9c2cc;">;
</span></code></pre>
<p>
<details>
<summary>Answer for No. 2</summary>
<p>Sorry, not [5, 9, 14, 22]. Each element is converted into string and then comparing the sequence of UTF-16 value.</p>
<p>Check this out: <a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort">https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort</a></p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="font-style:italic;color:#5c6773;">// Output
</span><span>[</span><span style="color:#ffcc66;">14</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">22</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">5</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">9</span><span>]
</span></code></pre>
</details>
</p>
<h3 id="no-3-closure-and-event-loop"><a class="zola-anchor" href="#no-3-closure-and-event-loop" aria-label="Anchor link for: no-3-closure-and-event-loop">#</a>
No. 3 – Closure and event loop</h3>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#ffa759;">for </span><span>(</span><span style="color:#ffa759;">var </span><span>i </span><span style="color:#f29e74;">= </span><span style="color:#ffcc66;">0</span><span style="color:#ccc9c2cc;">; </span><span>i </span><span style="color:#f29e74;">< </span><span style="color:#ffcc66;">3</span><span style="color:#ccc9c2cc;">; </span><span>i</span><span style="color:#f29e74;">++</span><span>) {
</span><span> </span><span style="color:#ffa759;">const </span><span style="color:#ffd580;">log </span><span style="color:#f29e74;">= </span><span>() </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(i)
</span><span> }
</span><span> </span><span style="color:#f28779;">setTimeout</span><span>(log</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">100</span><span>)
</span><span>}
</span></code></pre>
<p>
<details>
<summary>Answer for No. 3</summary>
<p>It prints out 3 thrice since setTimout puts that <code>log</code> function to the next macro task queue</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="font-style:italic;color:#5c6773;">// Output
</span><span style="color:#ffcc66;">3
</span><span style="color:#ffcc66;">3
</span><span style="color:#ffcc66;">3
</span></code></pre>
</details>
</p>
<h3 id="no-4-there-s-indentation"><a class="zola-anchor" href="#no-4-there-s-indentation" aria-label="Anchor link for: no-4-there-s-indentation">#</a>
No. 4 – There’s indentation</h3>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#ffa759;">function </span><span style="color:#ffd580;">createNewArray</span><span>(</span><span style="color:#ffcc66;">item</span><span>) {
</span><span> </span><span style="color:#ffa759;">return
</span><span> [item]
</span><span>}
</span><span>
</span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(</span><span style="color:#ffd580;">createNewArray</span><span>(</span><span style="color:#ffcc66;">0</span><span>))
</span></code></pre>
<p>
<details>
<summary>Answer for No. 4</summary>
<p>This returns <code>js•undefined</code>. The break-line after the <code>return</code> is ignored.
Thus, the argument passed into the function is also ignored.</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#ffcc66;">undefined
</span></code></pre>
</details>
</p>
<h3 id="no-5-what-s-inside-the-numbers"><a class="zola-anchor" href="#no-5-what-s-inside-the-numbers" aria-label="Anchor link for: no-5-what-s-inside-the-numbers">#</a>
No. 5 – What’s inside the “numbers”</h3>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#ffa759;">const </span><span>length </span><span style="color:#f29e74;">= </span><span style="color:#ffcc66;">4
</span><span style="color:#ffa759;">const </span><span>numbers </span><span style="color:#f29e74;">= </span><span>[]
</span><span style="color:#ffa759;">for </span><span>(</span><span style="color:#ffa759;">var </span><span>i </span><span style="color:#f29e74;">= </span><span style="color:#ffcc66;">0</span><span style="color:#ccc9c2cc;">; </span><span>i </span><span style="color:#f29e74;">< </span><span>length</span><span style="color:#ccc9c2cc;">; </span><span>i</span><span style="color:#f29e74;">++</span><span>)</span><span style="color:#ccc9c2cc;">;</span><span>{
</span><span> numbers</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">push</span><span>(i </span><span style="color:#f29e74;">+ </span><span style="color:#ffcc66;">1</span><span>)
</span><span>}
</span><span>
</span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(numbers)
</span></code></pre>
<p>
<details>
<summary>Answer for No. 5</summary>
<p>This needs a pair of sharp eyes. See that semicolon between the parentheses and curly bracket?</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="font-style:italic;color:#5c6773;">// Output
</span><span>[</span><span style="color:#ffcc66;">5</span><span>]
</span></code></pre>
</details>
</p>
<h3 id="no-6-no-length"><a class="zola-anchor" href="#no-6-no-length" aria-label="Anchor link for: no-6-no-length">#</a>
No. 6 – No length</h3>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#ffa759;">const </span><span>clothes </span><span style="color:#f29e74;">= </span><span>[</span><span style="color:#bae67e;">'shirt'</span><span style="color:#ccc9c2cc;">, </span><span style="color:#bae67e;">'socks'</span><span style="color:#ccc9c2cc;">, </span><span style="color:#bae67e;">'jacket'</span><span style="color:#ccc9c2cc;">, </span><span style="color:#bae67e;">'pants'</span><span style="color:#ccc9c2cc;">, </span><span style="color:#bae67e;">'hat'</span><span>]
</span><span>clothes</span><span style="color:#f29e74;">.</span><span>length </span><span style="color:#f29e74;">= </span><span style="color:#ffcc66;">0
</span><span>
</span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(clothes[</span><span style="color:#ffcc66;">3</span><span>])
</span></code></pre>
<p>
<details>
<summary>Answer for No. 6</summary>
<p>This is equivalant to removing all elements from the array</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="font-style:italic;color:#5c6773;">// Output
</span><span style="color:#ffcc66;">undefined
</span></code></pre>
</details>
</p>
<h3 id="no-7-variable-went-crazy"><a class="zola-anchor" href="#no-7-variable-went-crazy" aria-label="Anchor link for: no-7-variable-went-crazy">#</a>
No. 7 – Variable went crazy</h3>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#ffa759;">var </span><span>a </span><span style="color:#f29e74;">= </span><span style="color:#ffcc66;">1
</span><span style="color:#ffa759;">function </span><span style="color:#ffd580;">output </span><span>() {
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(a)
</span><span> </span><span style="color:#ffa759;">var </span><span>a </span><span style="color:#f29e74;">= </span><span style="color:#ffcc66;">2
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(a)
</span><span>}
</span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(a)
</span><span style="color:#ffd580;">output</span><span>()
</span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(a)
</span></code></pre>
<p>
<details>
<summary>Answer for No. 7</summary>
<p>First output: using the global <code>js•var a</code></p>
<p>Second output: the first one in the <code>js•function output()</code> using before declaration should be undefined</p>
<p>Third output: print out after the declaration. Nothing special.</p>
<p>Forth output: <code>js•var a</code> never got changed by <code>js•function output()</code></p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="font-style:italic;color:#5c6773;">// Output
</span><span style="color:#ffcc66;">1
</span><span style="color:#ffcc66;">undefined
</span><span style="color:#ffcc66;">2
</span><span style="color:#ffcc66;">1
</span></code></pre>
</details>
</p>
<h3 id="no-8-there-s-an-accidental-declaration"><a class="zola-anchor" href="#no-8-there-s-an-accidental-declaration" aria-label="Anchor link for: no-8-there-s-an-accidental-declaration">#</a>
No. 8 – There’s an accidental declaration</h3>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#ffa759;">function </span><span style="color:#ffd580;">foo</span><span>() {
</span><span> </span><span style="color:#ffa759;">let </span><span>a </span><span style="color:#f29e74;">= </span><span>b </span><span style="color:#f29e74;">= </span><span style="color:#ffcc66;">0
</span><span> a</span><span style="color:#f29e74;">++
</span><span> </span><span style="color:#ffa759;">return </span><span>a
</span><span>}
</span><span>
</span><span style="color:#ffd580;">foo</span><span>()
</span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(</span><span style="color:#f29e74;">typeof </span><span>a)
</span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(</span><span style="color:#f29e74;">typeof </span><span>b)
</span></code></pre>
<p>
<details>
<summary>Answer for No. 8</summary>
<p><code>js•let a</code> is a local variable. <code>js•typeof a</code> is checking undeclared variable.</p>
<p><code>js•b</code> is a global variable which value is assign in <code>js•function foo()</code>.</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="font-style:italic;color:#5c6773;">// Output
</span><span style="color:#ffcc66;">undefined
</span><span>number
</span></code></pre>
</details>
</p>
<h2 id="in-the-end"><a class="zola-anchor" href="#in-the-end" aria-label="Anchor link for: in-the-end">#</a>
In the end</h2>
<p>Thanks so much for reading! Have you got them all correct?</p>
Learning tmux as a beginner2022-04-30T00:00:00+00:002022-04-30T00:00:00+00:00https://pitayan.com/posts/learning-tmux-as-a-beginner/<p>Nowadays, Tmux has become into the everyday tool in my workdays. I always wanted to summarize the Tmux usage from long ago when I began to use it.
So this article is pretty much what I have started with as a Tmux beginner.</p>
<h2 id="what-consists-of-tmux"><a class="zola-anchor" href="#what-consists-of-tmux" aria-label="Anchor link for: what-consists-of-tmux">#</a>
What consists of Tmux?</h2>
<ul>
<li>Session</li>
<li>Window</li>
<li>Pane</li>
</ul>
<h2 id="what-is-a-tmux-session"><a class="zola-anchor" href="#what-is-a-tmux-session" aria-label="Anchor link for: what-is-a-tmux-session">#</a>
What is a Tmux session?</h2>
<p>The “dialog” that’s been kept by Tmux for running tasks which can be resumed later disconnection.</p>
<h3 id="1-create-a-session"><a class="zola-anchor" href="#1-create-a-session" aria-label="Anchor link for: 1-create-a-session">#</a>
1. Create a session</h3>
<p>Creating a session in Tmux is very semantic:</p>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#ffd580;">$</span><span> tmux new</span><span style="color:#ffcc66;"> -s </span><span style="color:#f29e74;"><</span><span>session_name</span><span style="color:#f29e74;">>
</span><span>
</span><span style="font-style:italic;color:#5c6773;"># e.g.
</span><span style="color:#ffd580;">$</span><span> tmux new</span><span style="color:#ffcc66;"> -s</span><span> helloworld
</span><span style="color:#ffd580;">$</span><span> tmux new</span><span style="color:#ffcc66;"> -s</span><span> great
</span></code></pre>
<h3 id="2-kill-a-session"><a class="zola-anchor" href="#2-kill-a-session" aria-label="Anchor link for: 2-kill-a-session">#</a>
2. Kill a session</h3>
<p>The <strong><code>-t</code></strong> flag here means “target”. Put the target session name after it to delete the session from Tmux</p>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#ffd580;">$</span><span> tmux kill-session</span><span style="color:#ffcc66;"> -t </span><span style="color:#f29e74;"><</span><span>session_name</span><span style="color:#f29e74;">>
</span><span>
</span><span style="font-style:italic;color:#5c6773;"># e.g.
</span><span style="color:#ffd580;">$</span><span> tmux kill-session</span><span style="color:#ffcc66;"> -t</span><span> helloworld
</span></code></pre>
<h3 id="3-kill-all-of-the-sessions"><a class="zola-anchor" href="#3-kill-all-of-the-sessions" aria-label="Anchor link for: 3-kill-all-of-the-sessions">#</a>
3. Kill all of the sessions</h3>
<p>The command <code>kill-server</code> seems a bit confusing. But Tmux does have a server running behind the scenes to be able to keep the located processes when the session is detached. So, killing a Tmux server means deleting all saved sessions.</p>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#ffd580;">$</span><span> tmux kill-server
</span></code></pre>
<h3 id="4-switch-between-sessions"><a class="zola-anchor" href="#4-switch-between-sessions" aria-label="Anchor link for: 4-switch-between-sessions">#</a>
4. Switch between sessions</h3>
<p>It’s useful when we are in a particular session but want to transfer to another session we used.</p>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#ffd580;">$</span><span> tmux switch</span><span style="color:#ffcc66;"> -t </span><span style="color:#f29e74;"><</span><span>session_name</span><span style="color:#f29e74;">>
</span><span>
</span><span style="font-style:italic;color:#5c6773;"># e.g.
</span><span style="color:#ffd580;">$</span><span> tmux switch</span><span style="color:#ffcc66;"> -t</span><span> great
</span></code></pre>
<h3 id="5-rename-session"><a class="zola-anchor" href="#5-rename-session" aria-label="Anchor link for: 5-rename-session">#</a>
5. Rename session</h3>
<p>Edit the session name after we created it.</p>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#ffd580;">$</span><span> tmux switch</span><span style="color:#ffcc66;"> -t </span><span style="color:#f29e74;"><</span><span>from</span><span style="color:#f29e74;">> <</span><span>to</span><span style="color:#f29e74;">>
</span><span>
</span><span style="font-style:italic;color:#5c6773;"># e.g.
</span><span style="color:#ffd580;">$</span><span> tmux rename</span><span style="color:#ffcc66;"> -t</span><span> great hi
</span></code></pre>
<h2 id="what-is-a-tmux-window"><a class="zola-anchor" href="#what-is-a-tmux-window" aria-label="Anchor link for: what-is-a-tmux-window">#</a>
What is a Tmux Window?</h2>
<p>The “child” under a Tmux Session. A session can have multiple windows (“children”) to switch from.</p>
<p>To manipulate the window, we need to use the “Prefix” shortcuts. By default, Tmux uses <code>Ctrl+b</code>.</p>
<h3 id="1-create-a-new-window"><a class="zola-anchor" href="#1-create-a-new-window" aria-label="Anchor link for: 1-create-a-new-window">#</a>
1. Create a new window</h3>
<p>This will create a new window and leave a number of the window we created to the Tmux bottom status line.</p>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#ffd580;">Ctrl</span><span> + b + c
</span></code></pre>
<p><code>0:zsh</code> is the first window, <code>1:zsh</code> is the second window.</p>
<h3 id="2-switch-between-windows"><a class="zola-anchor" href="#2-switch-between-windows" aria-label="Anchor link for: 2-switch-between-windows">#</a>
2. Switch between windows</h3>
<p>We could choose the target window by specifying its number.</p>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#ffd580;">Ctrl</span><span> + b + </span><span style="color:#f29e74;"><</span><span>window_number</span><span style="color:#f29e74;">>
</span><span>
</span><span style="font-style:italic;color:#5c6773;"># e.g.
</span><span style="color:#ffd580;">Ctrl</span><span> + b + 2
</span></code></pre>
<h3 id="3-search-the-windows"><a class="zola-anchor" href="#3-search-the-windows" aria-label="Anchor link for: 3-search-the-windows">#</a>
3. Search the windows</h3>
<p>We could look for the windows we created. The Tmux will go search the output / printout in the terminal.</p>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#ffd580;">Ctrl</span><span> + b + f
</span></code></pre>
<h3 id="4-list-all-of-the-windows-in-this-session"><a class="zola-anchor" href="#4-list-all-of-the-windows-in-this-session" aria-label="Anchor link for: 4-list-all-of-the-windows-in-this-session">#</a>
4. List all of the windows in this session</h3>
<p>The result screen is actually the same to search but with all of the windows we have.</p>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#ffd580;">Ctrl</span><span> + b + w
</span></code></pre>
<h3 id="5-rename-current-window"><a class="zola-anchor" href="#5-rename-current-window" aria-label="Anchor link for: 5-rename-current-window">#</a>
5. Rename current window</h3>
<p>By default, the window we created with <code>Ctrl+b+c</code> will have a name like “0:zsh” “1:shell”. Sometimes we need special names to distinguish those windows (like naming the sessions) in a rather semantic way.</p>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#ffd580;">Ctrl</span><span> + b + ,
</span></code></pre>
<h3 id="6-close-current-window"><a class="zola-anchor" href="#6-close-current-window" aria-label="Anchor link for: 6-close-current-window">#</a>
6. Close current window</h3>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#ffd580;">Ctrl</span><span> + b + </span><span style="color:#f29e74;">&
</span></code></pre>
<h2 id="what-is-a-tmux-pane"><a class="zola-anchor" href="#what-is-a-tmux-pane" aria-label="Anchor link for: what-is-a-tmux-pane">#</a>
What is a Tmux Pane?</h2>
<p>Instead of Tmux windows to switch back and forth, a Tmux pane can help split the window into certain smaller blocks within the same screen. So that we could retain the information on the same screen.</p>
<h3 id="1-split-pane-vertically-horizontally"><a class="zola-anchor" href="#1-split-pane-vertically-horizontally" aria-label="Anchor link for: 1-split-pane-vertically-horizontally">#</a>
1. Split pane vertically / horizontally</h3>
<p>Tmux makes it very easy to understand about pane splitting. The symbol <code>%</code> and <code>"</code> vividly explains how we are gonna split our screen.</p>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="font-style:italic;color:#5c6773;"># vertical split
</span><span style="color:#ffd580;">Ctrl</span><span> + b + </span><span style="color:#bae67e;">"
</span><span style="color:#bae67e;">
</span><span style="color:#bae67e;"># horizontal split
</span><span style="color:#bae67e;">Ctrl + b + %
</span></code></pre>
<h3 id="2-kill-a-pane"><a class="zola-anchor" href="#2-kill-a-pane" aria-label="Anchor link for: 2-kill-a-pane">#</a>
2. Kill a pane</h3>
<p>Usually when we run nothing in the terminal, we could close it immediately with <code>Ctrl+d</code> for most of the terminals. However if we are under some program like <code>Vim</code> or <code>SSH</code> and still hope to kill the pane forcefully, this will come into help.</p>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#ffd580;">Ctrl</span><span> + b + x
</span></code></pre>
<h3 id="3-switch-between-panes"><a class="zola-anchor" href="#3-switch-between-panes" aria-label="Anchor link for: 3-switch-between-panes">#</a>
3. Switch between panes</h3>
<p>Jump around the panes.</p>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#ffd580;">Ctrl</span><span> + b + o
</span></code></pre>
<p>We also have numbers for each panes. Display the number of each pane and type the number to select the target pane.</p>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="font-style:italic;color:#5c6773;"># display pane number
</span><span style="color:#ffd580;">Ctrl</span><span> + b + q
</span><span>
</span><span style="font-style:italic;color:#5c6773;"># e.g.
</span><span style="color:#ffd580;">Ctrl</span><span> + b + q </span><span style="color:#f29e74;">& </span><span style="color:#ffd580;">1
</span></code></pre>
<h3 id="4-move-the-current-pane-to-a-new-window"><a class="zola-anchor" href="#4-move-the-current-pane-to-a-new-window" aria-label="Anchor link for: 4-move-the-current-pane-to-a-new-window">#</a>
4. Move the current pane to a new window</h3>
<p>Sometimes the pane we created is maybe too small to showcase everything in that terminal. We might need to consider moving the pane to a new window.</p>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#ffd580;">Ctrl</span><span> + b + !
</span></code></pre>
<h2 id="summary"><a class="zola-anchor" href="#summary" aria-label="Anchor link for: summary">#</a>
Summary</h2>
<p>I personally think Tmux is a very good looking and helpful tool to allow everyone to control their terminals with only keyboards. Nowadays terminal apps like “iTerm” “cmder” all provided features similar to Tmux’s “window” and “pane”. Sometimes, these features are even more convenient than Tmux.</p>
<p>Tmux is really a powerful tool. But it needs time for us to digest the shortcut keys which is seemingly a “learning” curve to us all so that we could finally master using this powerful tool.</p>
Remake Pitayan Blog (Part 2)2022-04-25T00:00:00+00:002022-04-25T00:00:00+00:00https://pitayan.com/posts/remake-pitayan-blog-part-2/<p>In 2021, I wrote an article of <a href="/posts/remake-pitayan-blog">Remake Pitayan Blog (part 1)</a> sharing an idea of re-constructing my blog with a better design.
And finally at the same timing when this article was published, the project was initialized.</p>
<p>This project went quite a long way for over 10 months. I never thought this whole project would ever spend so much time to complete.
As of this day when this article is written, Pitayan blog is officially updated to this new version.</p>
<h2 id="setting-up-the-objectives"><a class="zola-anchor" href="#setting-up-the-objectives" aria-label="Anchor link for: setting-up-the-objectives">#</a>
Setting up the objectives</h2>
<p>I can only use my spare time on my personal projects. And almost all of my time were spent on function developments and fixing bugs.
As a consequence, those intermittent contributions caused many back and forth due to lacking of appropriate objectives ahead of starting those projects.</p>
<p>
<iframe src="https://giphy.com/embed/yv1ggi3Cbase05a8iS" width="100%" frameBorder="0" class="giphy-embed mark-w-md h-96" allowFullScreen></iframe>
</p>
<p>Thus this time, I setup some practical objectives and list out the necessary “todos” under each of its items.
I know that if I don’t keep a track on what to do and what has been done somewhere,
then resuming the process of this project later after some break may become very problematic.</p>
<p>To begin with creating objectives, I wrote down some notes around the following aspects:</p>
<ul>
<li>The blog new UI design</li>
<li>Implement the blog with Gatsby (as a plugin)</li>
<li>Create necessary pages</li>
<li>Migrate articles from the current blog</li>
</ul>
<p>(And actually in the next following months, the development kept running toward that direction without any turnings.)</p>
<p>For managing objectives, I utilized <a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/orgs/Pitayan/projects/1">Github “Projects”</a> feature to help me manage issues and notes. As you could see there are still quite a few more tasks to do for this project.
Although some of the tasks are not very fine-grained, they are yet helpful every time when I hope to recall the latest progress.</p>
<p><img src="./images/github_projects.png" alt="Github Projects for issues and notes" /></p>
<h2 id="migrating-from-gridsome-to-gatsby"><a class="zola-anchor" href="#migrating-from-gridsome-to-gatsby" aria-label="Anchor link for: migrating-from-gridsome-to-gatsby">#</a>
Migrating from “Gridsome” to “Gatsby”</h2>
<p>The previous <a href="/posts/remake-pitayan-blog">article</a> mentioned why to migrate away from <a rel="noopener nofollow noreferrer" target="_blank" href="https://gridsome.org">Gridsome</a></p>
<blockquote>
<p>I want to migrate to Gatsby for a bigger community. </p>
</blockquote>
<p>It is a very interesting but a very time-consuming step to me. But I believe the outcome of this step will eventually turn out to be the right decision.</p>
<p><a rel="noopener nofollow noreferrer" target="_blank" href="https://gatsbyjs.org">Gatsby</a> provides a solid user guide that let me get started quickly without digging into their source code for solutions.
And meanwhile, <a rel="noopener nofollow noreferrer" target="_blank" href="https://gatsbyjs.org">Gatsby</a>’s rich plugin library helps extending the functions rather easily(even with only official plugins). </p>
<p>Since it’s a complete rewrite, I followed some example repos to scratch my own project. (Want to say thanks to these repo owners and contributors)</p>
<ul>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/JorgeX/gatsby-theme-novela">gatsby-theme-novela</a> (Unfortunately, the original repo was out of maintenance and removed by the owner. This is a fork)</li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/rossettiquette/gatsby-starter-blog-dev-mdx">gatsby-starter-blog-dev-mdx</a></li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/Sridatta19/gatsby-starter-tailwind-mdx-blog">gatsby-starter-tailwind-mdx-blog</a></li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/rwieruch/gatsby-mdx-blog-starter-project">gatsby-mdx-blog-starter-project</a></li>
</ul>
<p>These years, many CSS frameworks claims their helpful features are able to solve developers’ pain points.
At first, frameworks using CSS-in-JS like <a rel="noopener nofollow noreferrer" target="_blank" href="https://styled-components.com">styled-components</a> & <a rel="noopener nofollow noreferrer" target="_blank" href="https://emotion.sh/docs/introduction">emotion</a> were taken into consideration judging their community relativity toward React.js.</p>
<p>I started using <a rel="noopener nofollow noreferrer" target="_blank" href="https://tailwindcss.com">Tailwindcss</a> from long ago, and felt it extremely handy and flexible (maybe I’m just quite used to it already).
So, in this project, I want to try with <a rel="noopener nofollow noreferrer" target="_blank" href="https://tailwindcss.com">Tailwindcss</a> again.</p>
<p>And for the core plugins, the following ones are chosen based upon the official starters. (Guess almost every developer would pick these up no matter what)</p>
<ul>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-plugin-mdx">gatsby-plugin-mdx</a>: need to allow using React components in Markdown source for a quick feature enhancement</li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-plugin-sharp">gatsby-plugin-sharp</a>: Image quality control</li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-plugin-image">gatsby-plugin-image</a>: Provides the images component</li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-remark-images">gatsby-remark-images</a>: Process markdown images</li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-transformer-yaml">gatsby-transformer-yaml</a>: Process Yaml file (used for author profile)</li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-transformer-sharp">gatsby-transformer-sharp</a>: Image quality control for markdown images</li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-source-filesystem">gatsby-source-filesystem</a>: You know, the very necessary plugin…</li>
</ul>
<p>Finally, with some extra “personal-favored” plugins, I created a rough repo outline. Check out to this commit <code>0d23f502cb2538e51e75ea7589b1ca53722c121f</code> to see the beginning setup (<a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/Pitayan/gatsby-theme-pitayan/commit/0d23f502cb2538e51e75ea7589b1ca53722c121f">Github link here</a>).</p>
<h2 id="the-ui-design"><a class="zola-anchor" href="#the-ui-design" aria-label="Anchor link for: the-ui-design">#</a>
The UI design</h2>
<p>The previous Pitayan blog design is very good-looking to me. Back then, it was made to mimic <a rel="noopener nofollow noreferrer" target="_blank" href="https://medium.com">Medium.com</a> and <a rel="noopener nofollow noreferrer" target="_blank" href="https://dev.to">Dev Community</a> to a certain extent (perhaps only the color schemes :p).</p>
<p>But frankly speaking, creating a new design out of the thin air is quite challenging to an engineer without much of design knowledge like me.
Thus this time when I started working on the designs, I almost copied 100% of <a rel="noopener nofollow noreferrer" target="_blank" href="https://novela.narative.co">Novela</a>’s layout but added some personal flavors to make the difference.
Which the “difference” is actually to bring the previous designs like “page footer” & “post meta” etc to the new design.</p>
<p>I now have only one comment for this decision:</p>
<blockquote>
<p>Experience is a reliable beacon.</p>
</blockquote>
<p><img src="./images/novela.png" alt="The Novela blog design, this is really good-looking" /></p>
<p>I wanted to log all of those beautiful designs somewhere like a Github issue. I made one <a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/Pitayan/gatsby-theme-pitayan/issues/2">here</a> but it’s a pity that the progress was never to date…</p>
<h3 id="using-rotala-css"><a class="zola-anchor" href="#using-rotala-css" aria-label="Anchor link for: using-rotala-css">#</a>
Using Rotala.css?</h3>
<p>I still remember my CSS project in 2019: <a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/daiyanze/rotala">Rotala.css</a> which is used by the previous Pitayan blog.
This little framework seems very handy at first because it provides lots of useful CSS definitions to extend upon, especially the typographical designs.</p>
<p>Here is example how I implemented the classes for the components.
Such implementation style is exactly what <a rel="noopener nofollow noreferrer" target="_blank" href="https://rotalacss.com/docs/#why-made-it-this-way">Rotala.css</a> recommends (by using <code>@apply</code> keyword to mix classes).</p>
<pre data-lang="sass" style="background-color:#212733;color:#ccc9c2;" class="language-sass "><code class="language-sass" data-lang="sass"><span style="color:#ffd580;">.site-tag </span><span style="color:#ff3333;">{
</span><span> @apply </span><span style="color:#73d0ff;">rounded-sm</span><span style="color:#ff3333;">;
</span><span> @apply </span><span style="color:#73d0ff;">py-0</span><span style="color:#ccc9c2cc;">.</span><span style="color:#ffcc66;">5 </span><span style="color:#73d0ff;">px-1</span><span style="color:#ff3333;">;
</span><span> @apply </span><span style="color:#73d0ff;">text-sm</span><span style="color:#ff3333;">;
</span><span> @apply </span><span style="color:#73d0ff;">text-gray-800</span><span style="color:#ff3333;">;
</span><span> @apply </span><span style="color:#73d0ff;">bg-gray-200</span><span style="color:#ff3333;">;
</span><span> @apply hover:</span><span style="color:#73d0ff;">bg-gray-300</span><span style="color:#ff3333;">;
</span><span> @apply </span><span style="color:#73d0ff;">transition-colors</span><span style="color:#ff3333;">;
</span><span> @apply </span><span style="color:#73d0ff;">duration-150</span><span style="color:#ff3333;">;
</span><span> @apply </span><span style="color:#73d0ff;">ease-in-out</span><span style="color:#ff3333;">;
</span><span>
</span><span> </span><span style="font-style:italic;color:#5c6773;">/* Dark Mode */
</span><span> @apply dark:hover:</span><span style="color:#73d0ff;">bg-gray-700</span><span style="color:#ff3333;">;
</span><span> @apply dark:</span><span style="color:#73d0ff;">bg-gray-800</span><span style="color:#ff3333;">;
</span><span> @apply dark:</span><span style="color:#5ccfe6;">text</span><span>-</span><span style="color:#73d0ff;">gray-400</span><span style="color:#ff3333;">;
</span><span style="color:#ff3333;">}
</span></code></pre>
<p>Although this is a really cool feature to reply upon, it seems <a rel="noopener nofollow noreferrer" target="_blank" href="https://tailwindcss.com">Tailwindcss</a> prefers users to handle their classes in the HTML template.</p>
<h3 id="color-palette"><a class="zola-anchor" href="#color-palette" aria-label="Anchor link for: color-palette">#</a>
Color Palette</h3>
<p>The color of the new design is just the default setting of <a rel="noopener nofollow noreferrer" target="_blank" href="https://tailwindcss.com">Tailwindcss</a>. The default palette is already very eye-comfortable, thought it’d be not that necessary to change that so early.</p>
<div class="mark-w-md flex flex-wrap space-y-3 text-sm text-center sm:space-y-0 sm:space-x-4 mb-8">
<div class="w-16">
<div class="h-16 w-16 rounded bg-gray-500"></div>
<b>gray</b><br />
<b>500</b>
</div>
<div class="w-16">
<div class="h-16 w-16 rounded bg-red-500"></div>
<b>red</b><br />
<b>500</b>
</div>
<div class="w-16">
<div class="h-16 w-16 rounded bg-yellow-500"></div>
<b>yellow</b><br />
<b>500</b>
</div>
<div class="w-16">
<div class="h-16 w-16 rounded bg-green-500"></div>
<b>green</b><br />
<b>500</b>
</div>
<div class="w-16">
<div class="h-16 w-16 rounded bg-blue-500"></div>
<b>blue</b><br />
<b>500</b>
</div>
<div class="w-16">
<div class="h-16 w-16 rounded bg-indigo-500"></div>
<b>indigo</b><br />
<b>500</b>
</div>
<div class="w-16">
<div class="h-16 w-16 rounded bg-purple-500"></div>
<b>purple</b><br />
<b>500</b>
</div>
<div class="w-16">
<div class="h-16 w-16 rounded bg-pink-500"></div>
<b>pink</b><br />
<b>500</b>
</div>
</div>
<p>By the way, changing the colors with <a rel="noopener nofollow noreferrer" target="_blank" href="https://tailwindcss.com">Tailwindcss</a> is just another piece of cake.
Overriding the theme settings in the <code>tailwind.config.js</code> will do the trick.</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="font-style:italic;color:#5c6773;">// tailwind.config.js
</span><span style="font-style:italic;color:#5ccfe6;">module</span><span style="color:#f29e74;">.</span><span style="font-style:italic;color:#5ccfe6;">exports </span><span style="color:#f29e74;">= </span><span>{
</span><span> </span><span style="color:#f29e74;">...
</span><span> theme: {
</span><span> colors</span><span style="color:#ccc9c2cc;">: </span><span>{
</span><span> primary</span><span style="color:#ccc9c2cc;">: </span><span>{
</span><span> </span><span style="color:#f29e74;">...
</span><span> }</span><span style="color:#ccc9c2cc;">,
</span><span> red</span><span style="color:#ccc9c2cc;">: </span><span>{
</span><span> </span><span style="color:#f29e74;">...
</span><span> }</span><span style="color:#ccc9c2cc;">,
</span><span> green</span><span style="color:#ccc9c2cc;">: </span><span>{
</span><span> </span><span style="color:#f29e74;">...
</span><span> }</span><span style="color:#ccc9c2cc;">,
</span><span> </span><span style="color:#f29e74;">...
</span><span> }
</span><span> }
</span><span>}
</span></code></pre>
<h1 id="some-cool-features"><a class="zola-anchor" href="#some-cool-features" aria-label="Anchor link for: some-cool-features">#</a>
Some cool features</h1>
<h2 id="the-selection-popover-menu"><a class="zola-anchor" href="#the-selection-popover-menu" aria-label="Anchor link for: the-selection-popover-menu">#</a>
The selection popover menu</h2>
<p><a rel="noopener nofollow noreferrer" target="_blank" href="https://medium.com">Medium</a> has a popover menu displaying right above the text content you selected.
When it pops up, you could highlight the text and add comments (and even share it to Twitter). Isn’t it a wonderful feature?</p>
<p><img src="./images/popover_menu.png" alt="The Medium popover menu example" /></p>
<p>Such feature also exists in the <a rel="noopener nofollow noreferrer" target="_blank" href="https://novela.narative.co">Novela</a> blog. It doesn’t support text highlight because it requires backend data.
But instead, you could copy the selected text and share it to Twitter. So I decided to mimic the Novela’s selection popover feature.
In this article, you might already noticed this feature but note that the only available area is this article content itself.</p>
<p>I referred to some community React libraries <a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/mvanlonden/react-selection-popover">react-selection-popover</a> & <a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/juliankrispel/react-text-selection-popover">react-text-selection-popover</a>.
They offered great example of how to detect selection and calculate the positions. Anyhow in the end, I have to create my own plugin / hooks to introduce this feature into the blog theme.</p>
<p>The reason of doing this is pretty simple: neither of those 2 libraries work perfectly in my case.</p>
<p>In order to make it work as expected, I created a hook to meet my demands. </p>
<pre data-lang="typescript" style="background-color:#212733;color:#ccc9c2;" class="language-typescript "><code class="language-typescript" data-lang="typescript"><span style="color:#ffa759;">function </span><span style="color:#ffd580;">useTextSelection </span><span>(
</span><span> </span><span style="color:#ffcc66;">container</span><span style="color:#f29e74;">: </span><span style="color:#73d0ff;">HTMLElement </span><span style="color:#f29e74;">= </span><span>document</span><span style="color:#f29e74;">.</span><span>body</span><span style="color:#ccc9c2cc;">,
</span><span> </span><span style="color:#ffcc66;">offsetWidth </span><span style="color:#f29e74;">= </span><span style="color:#ffcc66;">0</span><span style="color:#ccc9c2cc;">,
</span><span> </span><span style="color:#ffcc66;">offsetHeight </span><span style="color:#f29e74;">= </span><span style="color:#ffcc66;">0</span><span style="color:#ccc9c2cc;">,
</span><span> </span><span style="color:#ffcc66;">lineHeightDelta </span><span style="color:#f29e74;">= </span><span style="color:#ffcc66;">32
</span><span>)</span><span style="color:#f29e74;">: </span><span>{ left</span><span style="color:#f29e74;">: </span><span style="font-style:italic;color:#5ccfe6;">number </span><span style="color:#73d0ff;">top</span><span style="color:#f29e74;">: </span><span style="font-style:italic;color:#5ccfe6;">number </span><span style="color:#73d0ff;">textContent</span><span style="color:#f29e74;">: </span><span style="font-style:italic;color:#5ccfe6;">string </span><span>}
</span></code></pre>
<p>The function returns <code>left</code> <code>top</code> and <code>textContent</code>. To position the popover, just set the popover mask to <code>absolute</code> together with its left and top values.</p>
<pre data-lang="html" style="background-color:#212733;color:#ccc9c2;" class="language-html "><code class="language-html" data-lang="html"><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">div </span><span style="color:#ffd580;">className</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"selection-popover" </span><span style="color:#ffd580;">style</span><span style="color:#ccc9c2cc;">=</span><span style="color:#ffd580;">{{ left, top }}</span><span style="color:#5ccfe690;">>
</span><span> ...
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span></code></pre>
<p>The <code>container</code> is the HTML element target that holds your selection content. It is a relative anchor of the selection popover container.
By default, the <code>html•document.body</code> should be a decent container. However usually, it’s depending on how you gonna define your popover component.</p>
<p>In my case, since the popover component has to stay as descendent of the markdown content(the MDX container), it’s better just to use that content tag as container.</p>
<p><code>offsetWidth</code> and <code>offsetHeight</code> are used for centering the popover container. They don’t have to be static values as you could grab those attributes from the HTML tag <code>typescript• const { width, height } = targetContainer.getBoundingClientRect()</code>.</p>
<p>In the short feature, I may create a new repo to make everyone accessible to this useful plugin. By far, the source code is also a good example I presume for those who wants to have a selection popover component in their page.</p>
<h2 id="medium-like-picture-magnifier"><a class="zola-anchor" href="#medium-like-picture-magnifier" aria-label="Anchor link for: medium-like-picture-magnifier">#</a>
Medium-like picture magnifier</h2>
<p>There’s a ready-to-use library called <a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/francoischalifour/medium-zoom">medium-zoom</a> that reproduced the feature of the Medium.com’s picture zoom-in.
The time when I found it, it made me so happy that I don’t have to create a plugin myself to achieve the feature.</p>
<p>Guess there’s no need to talk more about this feature. Just feel the power of it by click the picture below (or any other pictures in the article page).</p>
<p><img src="./images/medium_zoom.png" alt="This picture will pop out like a modal" /></p>
<p>Amazing, isn’t it?</p>
<h2 id="dark-and-light-mode"><a class="zola-anchor" href="#dark-and-light-mode" aria-label="Anchor link for: dark-and-light-mode">#</a>
Dark and light mode</h2>
<p>Like some document sites such as <a rel="noopener nofollow noreferrer" target="_blank" href="https://vuejs.org">vuejs.org</a>, Pitayan.com now has supported <em>dark-mode</em> with the Tailwind’s builtin feature.
It affects not only the usual components for site layout but also the code highlight.</p>
<p>Remember Tailwind’s <code>dark</code> variable? It is the key to making styles inverted under dark-mode.</p>
<pre data-lang="sass" style="background-color:#212733;color:#ccc9c2;" class="language-sass "><code class="language-sass" data-lang="sass"><span>@apply </span><span style="color:#73d0ff;">text-gray-800</span><span style="color:#ff3333;">;
</span><span>
</span><span style="font-style:italic;color:#5c6773;">/** Dark Mode */
</span><span>@apply dark:</span><span style="color:#5ccfe6;">text</span><span>-</span><span style="color:#73d0ff;">gray-400</span><span style="color:#ff3333;">;
</span></code></pre>
<p>You may have noticed the “sun” icon ☀️ in the top navigation bar, it triggers the theme to toggle between light and dark. (Believe you’ll be enjoying using the dark-mode for protecting eyesight 🤓)</p>
<h1 id="pitayan-gatsby-theme-pitayan"><a class="zola-anchor" href="#pitayan-gatsby-theme-pitayan" aria-label="Anchor link for: pitayan-gatsby-theme-pitayan">#</a>
@pitayan/gatsby-theme-pitayan</h1>
<p>Now the Pitayan.com provides a Gatsby theme plugin so that everyone else can build their own Gatsby blog with our theme. </p>
<p>Check out this Github repo: <a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/Pitayan/gatsby-theme-pitayan">Pitayan/gatsby-theme-pitayan</a>.</p>
<p>Following a quick installation which is also mentioned in the README file. The sad part is that it’s only a plugin rather than a startup template. It still requires some more steps to get it to run locally.</p>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#ffd580;">$</span><span> npm install</span><span style="color:#ffcc66;"> --save-dev</span><span> gatsby @pitayan/gatsby-theme-pitayan react@17 react-dom@17
</span></code></pre>
<p>Besides, as you may noticed, at the moment it supports React 17 instead of 18 because the <code>peerDependency</code> of gatsby-plugin-mdx requires <code>@mdx-js/mdx: ^1.0.0</code>.
Let’s wait until they upgrade their version dependencies. Or maybe we could force using the latest React during NPM modules installation to circumvent the warnings.</p>
<h1 id="what-s-next"><a class="zola-anchor" href="#what-s-next" aria-label="Anchor link for: what-s-next">#</a>
What’s next?</h1>
<p>Finally, the new theme of Pitayan.com is released. Although there’re still some bugs within the theme, they’ll be fixed any day soon.</p>
<p>I think what’s more important to the site now is automate the release process.
Believe any day soon in the future, all I need to do for publishing a new article is just “write content” and “create PR”. And then it should merge the PR automatically by a Github scheduler.
Since the Pitayan.com deploys with <a rel="noopener nofollow noreferrer" target="_blank" href="https://netlify.com">Netlify</a>, any commits to the <code>master</code> branch will trigger a new build.</p>
<p>I’ll be publising a part-3 to introduce this automation as the last step of the “remake”. Hope I could complete this as soon as possible (No more procrastinations 😆).</p>
<p>Thanks for reading!</p>
About2021-07-31T00:00:00+00:002021-07-31T00:00:00+00:00https://pitayan.com/about/<h2 id="hi">Hi!</h2>
<p>Welcome to <a rel="noopener nofollow noreferrer" target="_blank" href="https://pitayan.com">Pitayan.com</a>.</p>
<p>This is a place publishing articles about web techonologies and experience about web developments. Thank you for coming by and reading those articles. Hope Those materials could actually help you improve your skills or gain some knowledge in web development industry.</p>
<h2 id="what-does-pitayan-mean">What does “Pitayan” mean?</h2>
<p>“<strong>Pitayan</strong>” is composed by the following words:</p>
<ul>
<li>pitaya</li>
<li>and</li>
<li>blog</li>
</ul>
<p>Combined as “pitayanblog”. It means “dragon fruit and blog”. “blog” is omitted for brevity. The name was created completely by chance (The founder was eating a dragon fruit and came up with the name of whatsoever is eyes-in-front).</p>
<h2 id="site-introduction">Site introduction</h2>
<p>Pitayan.com is published since 2020 to narrate stories or studies about software engineering. The idea of creating this blog is to simply share knowledge and info to those who hold interest or questions toward certain topics.</p>
<h2 id="system-architecture">System Architecture</h2>
<p>As a static blog, there’s nothing but only static files (html css js) hosted on <a rel="noopener nofollow noreferrer" target="_blank" href="https://netlify.com">Netlify</a>. The front end technologies used for the website is listed below:</p>
<ul>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://gatsbyjs.com">Gatsbyjs</a>: JamStack CMS</li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://tailwindcss.com">Tailwindcss</a>: A utility-first CSS framework for rapidly building custom designs</li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://analytics.google.com/">Google Analytics</a>: Google’s famous tracking and analytics service</li>
</ul>
<h2 id="contribute">Contribute?</h2>
<p>If you are also a developer and want to publish articles to the audience. Why not give it try on Pitayan.com?
Simply send your article to the following email address: <a href="mailto:pitayanblog@gmail.com">pitayanblog@gmail.com</a></p>
<p>Before you do, please take a good look at this page about contribution: <a href="/write-for-us">write-for-us</a></p>
<h2 id="contact">Contact</h2>
<p>Currently Pitayan uses Gmail to receive emails. The address is as below:</p>
<ul>
<li><a href="mailto:pitayanblog@gmail.com">pitayanblog@gmail.com</a></li>
</ul>
<p>Contact Yanze Dai personally via:</p>
<ul>
<li>Email: <a href="mailto:ginoalex8964@yahoo.com">ginoalex8964@yahoo.com</a></li>
<li>Facebook: <a rel="noopener nofollow noreferrer" target="_blank" href="https://facebook.com/yanze.dai">facebook.com/yanze.dai</a></li>
<li>Github: <a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/daiyanze">github.com/daiyanze</a></li>
</ul>
<h2 id="at-last">At Last</h2>
<p>Enjoy reading!</p>
Remake Pitayan Blog (Part 1)2021-06-25T00:00:00+00:002021-06-25T00:00:00+00:00https://pitayan.com/posts/remake-pitayan-blog/<p>Recently I’ve decided to remake this whole blog to improve its workflow and UI/UX. This article will uncover my story of creating this blog and also some thoughts about the “remake” approach.</p>
<h2 id="tldr"><a class="zola-anchor" href="#tldr" aria-label="Anchor link for: tldr">#</a>
TLDR;</h2>
<hr />
<br />
<br />
<br />
<h1 id="the-beginning"><a class="zola-anchor" href="#the-beginning" aria-label="Anchor link for: the-beginning">#</a>
The beginning</h1>
<p>In 2020, I spent some weeks on building a tech blog <a rel="noopener nofollow noreferrer" target="_blank" href="http://pitayan.com">Pitayan.com</a> using some popular technologies — <a rel="noopener nofollow noreferrer" target="_blank" href="https://gridsome.org">Gridsome</a> & <a rel="noopener nofollow noreferrer" target="_blank" href="https://netlify.com">Netlify</a>. At the beginning, it seems that the blog has a natural attraction that urges me to put more efforts onto it. So I kept writing stories to share my recent studies and projects.</p>
<p>This little blog is totally a dream come true for me ever since the first time when I came to know about Wordpress back in 2011. The process of creating a blog using Wordpress was so fun that there’s not a day without imagining how it will look like after the development is finished. In those years when I was with Wordpress, I’ve learned a lot about web development. If you are a frontend engineer, I bet you understand the joy and funs of tuning up the designs and program logics for a web app. </p>
<p><img src="./images/wordpress.png" alt="images/wordpress.png" /></p>
<p>Honestly, that Wordpress blog I made long ago was somehow a “crucial factor” of me quitting my former accounting job and decided to devote myself into software engineering. Now I can feel that it is always what you make out of the imagination brings you the most fantastic memories. So this blog I made today <a rel="noopener nofollow noreferrer" target="_blank" href="http://pitayan.com">Pitayan.com</a> has become into that indivisible part of my life. I’m loving it!</p>
<p>However, it’s never difficult to admit that the whole blog was created by a whim without any architectural designs. I just simply picked up some of the most cutting-edge technologies: <a rel="noopener nofollow noreferrer" target="_blank" href="https://vuejs.org">Vue.js</a> / <a rel="noopener nofollow noreferrer" target="_blank" href="https://gridsome.org">Gridsome</a> / <a rel="noopener nofollow noreferrer" target="_blank" href="https://netlify.com">Netlify</a>. I also attempted to create a CSS framework <a rel="noopener nofollow noreferrer" target="_blank" href="https://rotalacss.com">Rotala.css</a> to power up the blog appearance.</p>
<p>Here is the article of me creating my first ever CSS framework <a rel="noopener nofollow noreferrer" target="_blank" href="https://rotalacss.com">Rotala.css</a> — <a rel="noopener nofollow noreferrer" target="_blank" href="https://pitayan.com/posts/css-framework-attempt/">Attempting to create a CSS framework</a>.</p>
<p>Believe it or not, all of these “cool looking” features went through a lot of “back and forth” which consumed large amount of my time (I could have done something else much more “engineering”). Well, the final result is just in front of you, it looks elegant minimal and fashionable. Hope you find it enjoyable as well.</p>
<p>
<iframe src="https://giphy.com/embed/UqWpaUdQIGueToU7ig" width="100%" frameBorder="0" class="giphy-embed mark-w-md h-96" allowFullScreen></iframe>
</p>
<h1 id="the-free-plans"><a class="zola-anchor" href="#the-free-plans" aria-label="Anchor link for: the-free-plans">#</a>
The “free” plans</h1>
<p>At the time of me writing this article, my blog is hosted on <a rel="noopener nofollow noreferrer" target="_blank" href="https://netlify.com">Netlify</a> under a free plan. All of the articles are saved in a Github private repository. It’s very obvious that I’m taking advantage of “Free of charge”. </p>
<p>But don’t take it wrong, the “free” thing here will actually charge you in a different “format” which is clearly written in the service policies and terms. </p>
<p>And any day in the future, if you hope to continue using the same service but with better functionalities, the “user of inertia” will drive you directly to the port of “subscriptions” or “plans”. This is how the nowaday’s SaaS business converts their “traffics”. (For now, the “free” plan fits me really well thanks to the “open-source spirit” from <a rel="noopener nofollow noreferrer" target="_blank" href="https://netlify.com">Netlify</a> and <a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com">Github</a>)</p>
<p>However I really hope the day of upgrading my account could come earlier because it means this blog has gained decent amount of traffic which becomes into a successful site and brings valuable information to the crowd.</p>
<p>To tell you the truth, maintaining this blog is not totally free of charge. I still have to spend some coins for some extra functions.</p>
<ul>
<li>Domain name on <a rel="noopener nofollow noreferrer" target="_blank" href="https://godaddy.com">Godaddy.com</a> (20 US dollars a year)</li>
<li>SEO tool from <a rel="noopener nofollow noreferrer" target="_blank" href="https://seranking.com">SERanking.com</a> (40 * 12 = 480 US dollars a year)
(Btw, I think SEO tool isn’t quite a necessary thing at the beginning. I rarely use it to help optimize the article keywords)</li>
</ul>
<p>The expense in total is 500 US dollars for a year. Yet I made no profit out of it as of today.</p>
<h1 id="the-turning-point"><a class="zola-anchor" href="#the-turning-point" aria-label="Anchor link for: the-turning-point">#</a>
The turning point</h1>
<p>Most of the tech blogs I’ve seen on the Internet don’t really have ads on it. Maybe they are just blogging for hobbies, thus earnings perhaps don’t really matter to them. </p>
<p>For my part, advertisement is one big reason(target) for me to keep on with it. I consider it as a long-term investment with relatively low cost and high return even this may took much longer than I could imagine. </p>
<p>But I always confidently believe my blog will eventually become into an “oil field” that’s minting dollar bills.</p>
<p>During 2020’s big incident “COVID-19”, <a rel="noopener nofollow noreferrer" target="_blank" href="https://www.google.com/adsense/start">Google Adsense</a> has rejected all of my applications due to the well-known “Review Capacity Reached”. This prevented a lot of blog owners to convert their traffic into some real income.</p>
<p><img src="./images/adsense_review_capacity_reached.png" alt="images/adsense_review_capacity_reached.png" /></p>
<p>I’ve also consulted with Adsense community for help. They told me that it’s maybe not because <a rel="noopener nofollow noreferrer" target="_blank" href="http://pitayan.com">Pitayan.com</a> is not eligible but it’s just that Google don’t really have enough auditing resources for the time being. </p>
<p>After searching the answers on the community forum, I think I’ve found the most reliable solution which is to submit the application once a while. And then I started taking my chances clicking that submit button once a month.</p>
<p>Just around 2 weeks before (May 28th 2021) I started writing this article, my application for <a rel="noopener nofollow noreferrer" target="_blank" href="https://www.google.com/adsense/start">Google Adsense</a> has been approved! It is such great news to my 2020’s effort.</p>
<p><img src="./images/adsense_approval.png" alt="images/adsense_approval.png" /></p>
<blockquote>
<p>Hooray 🎉🎉🎉🎉🎉🎉!</p>
</blockquote>
<p>I am so excited to be able to put some advertisements to the blog pages. However, after a few days of consideration I’ve decided not to do it so quickly.</p>
<p><strong>why</strong>?</p>
<p>Well, here are the reasons not to display ads at the moment:</p>
<ul>
<li>The blog isn’t a well-thought product. Adding ads onto the page will significantly affect the user experience.</li>
<li>There’s not enough amount of traffic and not enough session time. So the ads conversion ratio will lead to a disappointing result.</li>
<li>I want to migrate to <a rel="noopener nofollow noreferrer" target="_blank" href="http://gatsbyjs.com">Gatsby</a> for a bigger community. The <a rel="noopener nofollow noreferrer" target="_blank" href="https://gridsome.org">Gridsome</a> is good enough but the reason why I chose <a rel="noopener nofollow noreferrer" target="_blank" href="https://gridsome.org">Gridsome</a> at the beginning is that I wanted to learn more about <a rel="noopener nofollow noreferrer" target="_blank" href="https://vuejs.org">Vue.js</a>.
Now it’s time to get back to <a rel="noopener nofollow noreferrer" target="_blank" href="http://reactjs.org">React</a> for my personal flavor. (I’ll write an article about why I choose <a rel="noopener nofollow noreferrer" target="_blank" href="http://reactjs.org">React</a> over <a rel="noopener nofollow noreferrer" target="_blank" href="https://vuejs.org">Vue.js</a> in the end)</li>
</ul>
<p>Above all, if I hope to implement Google advertisements I need to remake my blog <a rel="noopener nofollow noreferrer" target="_blank" href="http://pitayan.com">Pitayan.com</a> first.</p>
<h1 id="the-architecture-design"><a class="zola-anchor" href="#the-architecture-design" aria-label="Anchor link for: the-architecture-design">#</a>
The architecture design</h1>
<p>Once you choose to go with static site generators like <a rel="noopener nofollow noreferrer" target="_blank" href="http://gatsbyjs.com">Gatsby</a> or <a rel="noopener nofollow noreferrer" target="_blank" href="https://gridsome.org">Gridsome</a>, the rest of the things to do is to start writing and then deploy the articles to <a rel="noopener nofollow noreferrer" target="_blank" href="https://netlify.com">Netlify</a> with their pre-configured starter template. Everything is so “open-box” that you don’t quite need to modify a lot of things by yourself.</p>
<p>However this time, I hope to bring some solid engineering practices to make it robust and foreseeable. It’d be very hard to picture every single detail for each of the features. So, it’s perhaps a good idea to just initialize a rather intuitive features outline in the first place. And then, I’ll drill down to each of them to fill in the missing details.</p>
<h2 id="features-outline"><a class="zola-anchor" href="#features-outline" aria-label="Anchor link for: features-outline">#</a>
Features outline</h2>
<p>The following items are those essential components to take into consideration. You can call it “minimal requirements”.</p>
<ul>
<li>Github repo 1: Source code</li>
<li>Github repo 2: UI</li>
<li>Github repo 3 (private repo): Posts</li>
<li>Tests</li>
<li>Github Actions (CI/CD)</li>
<li>Netlify setup</li>
<li>Google Tag manager
<ul>
<li>Google Analytics</li>
<li>Google Adsense</li>
</ul>
</li>
</ul>
<h2 id="github-repos"><a class="zola-anchor" href="#github-repos" aria-label="Anchor link for: github-repos">#</a>
Github repos</h2>
<p>There will be 3 major Github repos:</p>
<ul>
<li><strong>Repo 1 (Source code)</strong>: The main repository that follows the Gatsby folder structure. <a rel="noopener nofollow noreferrer" target="_blank" href="https://netlify.com">Netlify</a> will use this repo for builds.</li>
<li><strong>Repo 2 (UI)</strong>: It contains all of the necessary UI components for a blog. And also showcase the collection of components to public under a subdomain.</li>
<li><strong>Repo 3 (Posts)</strong>: It contains all of the blog articles.</li>
</ul>
<p>And the 3 repos have a simple relationship among them:</p>
<p><img src="./images/repo_relationship.png" alt="images/repo_relationship.png" /></p>
<blockquote>
<p>Repo 3 is a Git submodule of Repo 1.
Repo 2 is a JS package dependency of Repo 1.</p>
</blockquote>
<h2 id="tests"><a class="zola-anchor" href="#tests" aria-label="Anchor link for: tests">#</a>
Tests</h2>
<p><img src="./images/tests.png" alt="images/tests.png" /></p>
<p>Repo 1 (Source code) & Repo 2 (UI) will have unit tests and e2e tests. </p>
<p>Repo 3 (Posts) has no tests since it’s only used as a “database”.</p>
<h2 id="github-actions-ci-cd"><a class="zola-anchor" href="#github-actions-ci-cd" aria-label="Anchor link for: github-actions-ci-cd">#</a>
Github Actions (CI/CD)</h2>
<h3 id="repo-1-source-code-repo-2-ui"><a class="zola-anchor" href="#repo-1-source-code-repo-2-ui" aria-label="Anchor link for: repo-1-source-code-repo-2-ui">#</a>
Repo 1 (Source code) & Repo 2 (UI)</h3>
<ul>
<li>Detect PR, run tests</li>
</ul>
<p>(As a personal project, it feels quite redundant to create a PR and merge it by myself… And I’m quite used to merge the changes directly and push them to the remote repo. But I think this disobeys the “engineering spirit” 🤓, right?)</p>
<p><img src="./images/repo_1_actions.png" alt="images/repo_1_actions.png" /></p>
<h3 id="repo-3-posts"><a class="zola-anchor" href="#repo-3-posts" aria-label="Anchor link for: repo-3-posts">#</a>
Repo 3 (Posts)</h3>
<ul>
<li>Detect PR, automatically merge to develop if test passed (Same to Repo 1)</li>
<li>Weekly Crontab, automatically merge develop to master if test passed</li>
</ul>
<p>Both of the CI jobs will finally trigger <a rel="noopener nofollow noreferrer" target="_blank" href="https://netlify.com">Netlify</a> to build the source.</p>
<p>Note that Repo 3 (Posts) has no tests. The CI job has to collaborate with Repo 1 (Source code). The test here behaves like a logic switch to guard against anything wrong with Repo 1 (Source code) so that it won’t proceed to “merge” or “build” step when test cases failed.</p>
<p><img src="./images/repo_3_actions.png" alt="images/repo_3_actions.png" /></p>
<h2 id="netlify"><a class="zola-anchor" href="#netlify" aria-label="Anchor link for: netlify">#</a>
Netlify</h2>
<p>In the new architecture design, most of the settings on <a rel="noopener nofollow noreferrer" target="_blank" href="https://netlify.com">Netlify</a> will probably stay unchanged.</p>
<p>Apart from the current “build & deploy” workflow, there are some “boost-up” features I want to add to <a rel="noopener nofollow noreferrer" target="_blank" href="https://netlify.com">Netlify</a>.</p>
<ul>
<li>Trigger <strong>preview build</strong> on develop branch (like a staging environment)</li>
<li>Trigger <strong>production build</strong> on master branch</li>
<li>Notify subscribers (Email & SNS) on a successful <strong>production build</strong> for the new post <code>tentative</code></li>
</ul>
<p><img src="./images/netlify_builds.png" alt="images/netlify_builds.png" /></p>
<p><a rel="noopener nofollow noreferrer" target="_blank" href="https://netlify.com">Netlify</a> provides this “Branch subdomain” function to allow deploying a branch to a sub-domain. Which will be used as a staging environment for me to confirm the changes.</p>
<p><img src="./images/netlify_branch_subdomains.png" alt="images/netlify_branch_subdomains.png" /></p>
<p>Notifying subscribers will be a very important automation feature. Usually I’ll send out my new article manually to SNS subscribers after the deployment. (Unfortunately, there’s no email subscribers yet 😐)</p>
<p>By the way, I did some research on the “post-build notification” thing but found nearly no articles about how we could send out tweets and emails after the deployment. However, there’s a “Outgoing Webhook” thing for me to coupe with what’s after the deployment.</p>
<p><img src="./images/netlify_outgoing_notifications.png" alt="images/netlify_outgoing_notifications.png" /></p>
<p>Anyway, this feature may consume a lot of time throughout development and researches. Let me mark it as tentative.</p>
<h2 id="google-tag-manager"><a class="zola-anchor" href="#google-tag-manager" aria-label="Anchor link for: google-tag-manager">#</a>
Google Tag Manager</h2>
<p>Now <a rel="noopener nofollow noreferrer" target="_blank" href="http://pitayan.com">Pitayan.com</a> has installed Google Analytics tags for counting page views. The implementation is together with source code. I kinda regretted not implementing the tags with Tag Manager when I noticed how powerful it is now.</p>
<p>With Tag Manager, the Adsense can be installed easily without any coding in the source repo.</p>
<h2 id="put-them-all-together"><a class="zola-anchor" href="#put-them-all-together" aria-label="Anchor link for: put-them-all-together">#</a>
Put them all together</h2>
<p>I’ve created a rough workflow digram to make everything intuitive and also relational. It now looks like a vivid system when put together.</p>
<p><img src="./images/Pitayan_new_architecture.png" alt="images/Pitayan_new_architecture.png" /></p>
<h1 id="lastly"><a class="zola-anchor" href="#lastly" aria-label="Anchor link for: lastly">#</a>
Lastly</h1>
<blockquote>
<p>Is all of this worth it? It’s just a blog.</p>
</blockquote>
<p>Yes. I’m taking a great pain to make all aspects of a blog under control. Anyway, it’s also a good opportunity to practice designing a system even though everything here seems making a mountain out of a molehill. So, in my opinion this is exactly what “engineering” looks like. It visualizes the “known” and “unknown” parts clearly and helps understand how we should approach the solutions.</p>
<p>There are a lot more to do including task management and integrations etc.
I’ll move on to list out all of the necessary tasks for the next step which will be written as a different article <a href="/posts/remake-pitayan-blog-part-2">Remake Pitayan Blog (part 2)</a>.</p>
<p>Can’t wait to write some code and have fun with it!!</p>
A petty trick in front of a builtin function2021-01-19T00:00:00+00:002021-01-19T00:00:00+00:00https://pitayan.com/posts/petty-trick-in-front-of-a-builtin-function/<p>Lately, I was working on a project that’ll heavily concentrating on the server-side. Although those server-side stuffs aren’t really my expertise. The lucky part is that this project is about process the HTTP header and return a proper json as response.</p>
<p>After most of my challenges are completed, there’s one last problem for me to solve:</p>
<blockquote>
<p>“How do I get only the domain name and extension name out of a host name?”</p>
</blockquote>
<p>For instance, you’ve got some string domains as input:</p>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#ffd580;">www.google.com
</span><span style="color:#ffd580;">www.google.co.jp
</span><span style="color:#ffd580;">mail.google.com.hk
</span></code></pre>
<p>The expected output should be:</p>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#ffd580;">google.com
</span><span style="color:#ffd580;">google.co.jp
</span><span style="color:#ffd580;">google.com.hk
</span></code></pre>
<p>After some research, I came to know that there’s a builtin function <code>gethostbyname</code> from a native C library <code>netdb</code> which can be directly adopted into my program. The function is well explained in the <a rel="noopener nofollow noreferrer" target="_blank" href="https://www.gnu.org/software/libc/manual/html_node/Host-Names.html">https://www.gnu.org/software/libc/manual/html_node/Host-Names.html</a>.</p>
<p>Just take a look at the solution out there, it’s easily parsing the hostname into a easy-to-use data structure which completely is the final answer to look for.</p>
<p>Actually, before I investigated this builtin function, I’ve got myself another solution. And here is what I thought.</p>
<h2 id="my-own-approach"><a class="zola-anchor" href="#my-own-approach" aria-label="Anchor link for: my-own-approach">#</a>
My own approach</h2>
<p>Just by taking look at the input and output. It’s obviously an easy problem of processing strings. The first idea I came up with was cutting the first group of strings and return the rest of them.</p>
<p>My idea was obviously naive after running a couple of tests. Because some scenarios like multiple sub-domains weren’t taken into consideration.</p>
<p>For instance, a host name like this one:</p>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#ffd580;">one.two.abc.com
</span></code></pre>
<p>Expected result:</p>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#ffd580;">abc.com
</span></code></pre>
<p>If I only cut the first group of strings. The result is:</p>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#ffd580;">two.abc.com
</span></code></pre>
<p>It’s still far from the correct answer. Then I realized that such “obviously easy” algorithm problem shouldn’t be solved by a brute force. There must be a certain pattern in it.</p>
<p>My ration told me to list all of the elements from the hostnames and then find out some aspects to brainstorm with later.</p>
<p>Alright. What does a hostname consist of?</p>
<blockquote>
<p>Domain name + Delimiter + Extension name</p>
</blockquote>
<p>For <code>google.com</code></p>
<ul>
<li>google as domain name</li>
<li><code>.</code> as delimiter</li>
<li>com as extension name</li>
</ul>
<p>Okay. I think I’ve found something here…</p>
<p>There’s actually a simple formula to balance the numbers of delimiters and sectors</p>
<pre data-lang="txt" style="background-color:#212733;color:#ccc9c2;" class="language-txt "><code class="language-txt" data-lang="txt"><span>delimiters = sectors - 1
</span></code></pre>
<p>Usually the extension is either 1 sector (.com) or 2 sectors (co.cc). So if I could enumerate all of the 2-sector extensions, then the domain name is apparently an easy catch.</p>
<p>Why?</p>
<p>It is quite common to say that species of the 2-sector extensions aren’t many. Most of such extensions starts with either “co” or “com”. For instance, co.jp & com.hk.</p>
<p>So in all, the solution would be doing the following things:</p>
<ol>
<li>
<p>Split the hostname by delimiter of “.”</p>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#ffd580;">e.g.</span><span> www.google.com.hk -</span><span style="color:#f29e74;">> </span><span style="color:#ffa759;">[</span><span>“www”, “google”, “com”, “hk</span><span style="color:#bae67e;">"]
</span></code></pre>
</li>
<li>
<p>Check if the second to last element is “co” or “com”</p>
</li>
<li>
<p>if <code>yes</code>, then take the third to last element as domain name.</p>
</li>
<li>
<p>if <code>no</code>, then take the second to last element as domain name.</p>
</li>
</ol>
<h2 id="implementation"><a class="zola-anchor" href="#implementation" aria-label="Anchor link for: implementation">#</a>
Implementation</h2>
<p>Here is my implementation in C:</p>
<pre data-lang="cpp" style="background-color:#212733;color:#ccc9c2;" class="language-cpp "><code class="language-cpp" data-lang="cpp"><span>function </span><span style="color:#ffd580;">getdomainbyhost</span><span>(</span><span style="color:#ffa759;">char </span><span style="color:#f29e74;">*</span><span style="color:#ffcc66;">http_host</span><span>)
</span><span>{
</span><span> </span><span style="color:#ffa759;">char </span><span style="color:#f29e74;">*</span><span>res </span><span style="color:#f29e74;">= </span><span style="color:#f28779;">calloc</span><span>(</span><span style="color:#ffcc66;">128</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffa759;">sizeof</span><span>(</span><span style="color:#ffa759;">char</span><span>))</span><span style="color:#ccc9c2cc;">;
</span><span>
</span><span> </span><span style="font-style:italic;color:#5c6773;">// return directly if Dev env
</span><span> </span><span style="color:#ffa759;">if </span><span>(</span><span style="color:#f29e74;">!</span><span style="color:#f28779;">strcmp</span><span>(http_host</span><span style="color:#ccc9c2cc;">, </span><span style="color:#bae67e;">"localhost"</span><span>) </span><span style="color:#f29e74;">||
</span><span> </span><span style="color:#f29e74;">!</span><span style="color:#f28779;">strcmp</span><span>(http_host</span><span style="color:#ccc9c2cc;">, </span><span style="color:#bae67e;">"127.0.0.1"</span><span>) </span><span style="color:#f29e74;">||
</span><span> </span><span style="color:#f29e74;">!</span><span style="color:#f28779;">strcmp</span><span>(http_host</span><span style="color:#ccc9c2cc;">, </span><span style="color:#bae67e;">"0.0.0.0"</span><span>))
</span><span> {
</span><span> </span><span style="color:#f28779;">sprintf</span><span>(res</span><span style="color:#ccc9c2cc;">, </span><span style="color:#bae67e;">"</span><span style="color:#ffcc66;">%s</span><span style="color:#bae67e;">"</span><span style="color:#ccc9c2cc;">,</span><span> http_host)</span><span style="color:#ccc9c2cc;">;
</span><span>
</span><span> </span><span style="color:#ffa759;">return</span><span> res</span><span style="color:#ccc9c2cc;">;
</span><span> }
</span><span> </span><span style="font-style:italic;color:#5c6773;">// Get "." occurrence frequency in host
</span><span> </span><span style="font-style:italic;color:#5c6773;">// e.g. a.b.google.com.hk => 4
</span><span> </span><span style="font-style:italic;color:#5c6773;">// e.g. b.google.com.hk => 3
</span><span> </span><span style="font-style:italic;color:#5c6773;">// e.g. google.co.jp => 2
</span><span> </span><span style="font-style:italic;color:#5c6773;">// e.g. google.com.hk => 2
</span><span> </span><span style="font-style:italic;color:#5c6773;">// e.g. google.com => 1
</span><span> </span><span style="color:#ffa759;">int</span><span> occur </span><span style="color:#f29e74;">= </span><span style="color:#ffcc66;">0</span><span style="color:#ccc9c2cc;">;
</span><span> </span><span style="color:#ffa759;">for </span><span>(</span><span style="color:#ffa759;">int</span><span> i </span><span style="color:#f29e74;">= </span><span style="color:#ffcc66;">0</span><span style="color:#ccc9c2cc;">;</span><span> http_host[i] </span><span style="color:#f29e74;">!= </span><span style="color:#bae67e;">'</span><span style="color:#95e6cb;">\0</span><span style="color:#bae67e;">'</span><span style="color:#ccc9c2cc;">; </span><span style="color:#f29e74;">++</span><span>i)
</span><span> {
</span><span> </span><span style="color:#ffa759;">if </span><span>(http_host[i] </span><span style="color:#f29e74;">== </span><span style="color:#bae67e;">'.'</span><span>)
</span><span> {
</span><span> </span><span style="color:#f29e74;">++</span><span>occur</span><span style="color:#ccc9c2cc;">;
</span><span> }
</span><span> }
</span><span>
</span><span> </span><span style="color:#ffa759;">if </span><span>(occur </span><span style="color:#f29e74;">== </span><span style="color:#ffcc66;">1</span><span>)
</span><span> {
</span><span> </span><span style="color:#f28779;">sprintf</span><span>(res</span><span style="color:#ccc9c2cc;">, </span><span style="color:#bae67e;">"</span><span style="color:#ffcc66;">%s</span><span style="color:#bae67e;">"</span><span style="color:#ccc9c2cc;">,</span><span> http_host)</span><span style="color:#ccc9c2cc;">;
</span><span>
</span><span> </span><span style="color:#ffa759;">return</span><span> res</span><span style="color:#ccc9c2cc;">;
</span><span> }
</span><span>
</span><span> </span><span style="color:#ffa759;">char </span><span style="color:#f29e74;">*</span><span>arr[</span><span style="color:#ffcc66;">32</span><span>]</span><span style="color:#ccc9c2cc;">;
</span><span> </span><span style="color:#ffa759;">int</span><span> i </span><span style="color:#f29e74;">= </span><span style="color:#ffcc66;">0</span><span style="color:#ccc9c2cc;">;
</span><span> </span><span style="color:#ffa759;">char</span><span> domain_name[</span><span style="color:#ffcc66;">128</span><span>]</span><span style="color:#ccc9c2cc;">;
</span><span> </span><span style="color:#f28779;">memcpy</span><span>(domain_name</span><span style="color:#ccc9c2cc;">,</span><span> http_host</span><span style="color:#ccc9c2cc;">, </span><span style="color:#f28779;">strlen</span><span>(http_host))</span><span style="color:#ccc9c2cc;">;
</span><span> domain_name[</span><span style="color:#f28779;">strlen</span><span>(http_host)] </span><span style="color:#f29e74;">= </span><span style="color:#bae67e;">'</span><span style="color:#95e6cb;">\0</span><span style="color:#bae67e;">'</span><span style="color:#ccc9c2cc;">;
</span><span> </span><span style="color:#ffa759;">char </span><span style="color:#f29e74;">*</span><span>token </span><span style="color:#f29e74;">= </span><span style="color:#f28779;">strtok</span><span>(domain_name</span><span style="color:#ccc9c2cc;">, </span><span style="color:#bae67e;">"."</span><span>)</span><span style="color:#ccc9c2cc;">;
</span><span>
</span><span> </span><span style="color:#ffa759;">while </span><span>(</span><span style="color:#ffcc66;">NULL </span><span style="color:#f29e74;">!=</span><span> token)
</span><span> {
</span><span> arr[i</span><span style="color:#f29e74;">++</span><span>] </span><span style="color:#f29e74;">=</span><span> token</span><span style="color:#ccc9c2cc;">;
</span><span> token </span><span style="color:#f29e74;">= </span><span style="color:#f28779;">strtok</span><span>(</span><span style="color:#ffcc66;">NULL</span><span style="color:#ccc9c2cc;">, </span><span style="color:#bae67e;">"."</span><span>)</span><span style="color:#ccc9c2cc;">;
</span><span> }
</span><span>
</span><span> </span><span style="font-style:italic;color:#5c6773;">// If it's 2-worded domain extension, check the second to last word
</span><span> </span><span style="font-style:italic;color:#5c6773;">// e.g. google.com.hk => check "com" => google.com
</span><span> </span><span style="font-style:italic;color:#5c6773;">// e.g. some-service.google.com => => check "google" => google.com
</span><span> regex_t regex_domain_ext</span><span style="color:#ccc9c2cc;">;
</span><span> </span><span style="color:#ffd580;">regcomp</span><span>(</span><span style="color:#f29e74;">&</span><span>regex_domain_ext</span><span style="color:#ccc9c2cc;">,</span><span> REGEX_DOMAIN_EXT</span><span style="color:#ccc9c2cc;">,</span><span> REG_EXTENDED </span><span style="color:#f29e74;">|</span><span> REG_NOSUB)</span><span style="color:#ccc9c2cc;">;
</span><span> </span><span style="color:#ffa759;">int</span><span> match_result </span><span style="color:#f29e74;">= </span><span style="color:#ffd580;">regexec</span><span>(</span><span style="color:#f29e74;">&</span><span>regex_domain_ext</span><span style="color:#ccc9c2cc;">,</span><span> arr[occur </span><span style="color:#f29e74;">- </span><span style="color:#ffcc66;">1</span><span>]</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">0</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">NULL</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">0</span><span>)</span><span style="color:#ccc9c2cc;">;
</span><span> </span><span style="color:#ffd580;">regfree</span><span>(</span><span style="color:#f29e74;">&</span><span>regex_domain_ext)</span><span style="color:#ccc9c2cc;">;
</span><span>
</span><span> </span><span style="color:#ffa759;">if </span><span>(match_result </span><span style="color:#f29e74;">!=</span><span> REG_NOMATCH)
</span><span> {
</span><span> </span><span style="color:#f28779;">sprintf</span><span>(res</span><span style="color:#ccc9c2cc;">, </span><span style="color:#bae67e;">"</span><span style="color:#ffcc66;">%s</span><span style="color:#bae67e;">.</span><span style="color:#ffcc66;">%s</span><span style="color:#bae67e;">.</span><span style="color:#ffcc66;">%s</span><span style="color:#bae67e;">"</span><span style="color:#ccc9c2cc;">,</span><span> arr[occur </span><span style="color:#f29e74;">- </span><span style="color:#ffcc66;">2</span><span>]</span><span style="color:#ccc9c2cc;">,</span><span> arr[occur </span><span style="color:#f29e74;">- </span><span style="color:#ffcc66;">1</span><span>]</span><span style="color:#ccc9c2cc;">,</span><span> arr[occur])</span><span style="color:#ccc9c2cc;">;
</span><span> }
</span><span> </span><span style="color:#ffa759;">else
</span><span> {
</span><span> </span><span style="color:#f28779;">sprintf</span><span>(res</span><span style="color:#ccc9c2cc;">, </span><span style="color:#bae67e;">"</span><span style="color:#ffcc66;">%s</span><span style="color:#bae67e;">.</span><span style="color:#ffcc66;">%s</span><span style="color:#bae67e;">"</span><span style="color:#ccc9c2cc;">,</span><span> arr[occur </span><span style="color:#f29e74;">- </span><span style="color:#ffcc66;">1</span><span>]</span><span style="color:#ccc9c2cc;">,</span><span> arr[occur])</span><span style="color:#ccc9c2cc;">;
</span><span> }
</span><span>
</span><span> </span><span style="color:#ffa759;">return</span><span> res</span><span style="color:#ccc9c2cc;">;
</span><span>}
</span></code></pre>
<p>It’s perhaps never a good solution. And it may look a bit buggy and will not pass some of the QA tests. Just as you know, I’ll have to use the builtin function <code>gethostbyname</code>.</p>
<p>But I believe it’s a good practice of solving problems in an unique way. Maybe in the feature days when I take a look back on what I’ve done here, I’ll think myself a super idiot coming out with such dumb solution.</p>
Making a modern JS library in 20202020-11-05T00:00:00+00:002020-11-05T00:00:00+00:00https://pitayan.com/posts/modernest-lib-hello-world/<p>Recently, I was assigned a task of creating a new JS library to replace the obsolete one that’s been released for almost 8 years. This is a quite intruiging task because I’m also permitted to try everything new to make this project much more robust. The first thing came to my mind was to have myself a complex but great “development environment” which explains exactly properly vividly why I’m a DX first developer :D. In this article, I’ll demonstrate how I made it with a little “hello-world” repo.</p>
<p>Why the fuss? Is it worth?</p>
<p>Suppose you are in a war, the battalion chief only gives you the bayonet to battle with the enemies. Do you think you dare to charge forward while your enemies are using machine guns? I bet 99% of us are not brave enough to do so (Please don’t tell me you’d like to die for glory).</p>
<p>So what if the battalion cheif gives you the most lethal weapon that can defeat your enemies with only one click just like Thanos’ snap of fingers? I guess now you got the courage to fight against the enemies :P.</p>
<p>Anyway, I’d like to become that battalion chief providing lethal weapons to my teammates in order to remove the painful part from our development. When our development has become into a joyful experience, I believe the fuss of moving things back and forth is definitely worth of it.</p>
<p>Okay, here is the link to my demo repo:</p>
<p><a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/daiyanze/modern-hello-world">https://github.com/daiyanze/modern-hello-world</a></p>
<h2 id="the-inspiration"><a class="zola-anchor" href="#the-inspiration" aria-label="Anchor link for: the-inspiration">#</a>
The Inspiration</h2>
<p>For making our great new library a real modern one, I’ve been doing some research on varieties of modern JS repos.</p>
<ul>
<li>Angular</li>
<li>Vue-next</li>
<li>Graphql-js</li>
<li>popper-js</li>
<li>next.js</li>
<li>redux</li>
<li>and some other interesting libraries</li>
</ul>
<p>I found that all of these libraries have one thing in common:</p>
<blockquote>
<p>They all happen to conincide having either Jest or Mocha/Chai as their testing suites.</p>
</blockquote>
<p>Actually Jest and Mocha/Chai have been in the market for years, they are pretty solid. Even there are some new comers like Ava.js, but they still cannot replace the ones with larger community at the momment.</p>
<p>It’s already quite a common sense to choose the libraies with larger communities. Because their code is being tested by many other people, and have more bug fixes. In one word: Almost no one is brave enough to use those librarie that are not being tested thoroughly.</p>
<blockquote>
<p>How to know if a library has a large community?</p>
</blockquote>
<p>Simple, just check if they have many Github stars or issues. “Stars” usually means the library is pretty qualified and accepted by developers. “Issues” in some degree refects the community interactivity and library activeness. Those 2 indicators should be very reliable for our technology selection.</p>
<p>Therefore, I will choose those tools as our devDependencies from Github that have lots of stars and issues.</p>
<h2 id="dependency-features"><a class="zola-anchor" href="#dependency-features" aria-label="Anchor link for: dependency-features">#</a>
Dependency Features</h2>
<p>Here are some of the mayjor (“must”) features for our new project. In my opinion, these features have somewhat been the technology selection standard for a new JS library to start up with in 2020.</p>
<h3 id="1-typescript"><a class="zola-anchor" href="#1-typescript" aria-label="Anchor link for: 1-typescript">#</a>
1. Typescript</h3>
<p>Writing code without types was actually a pain in the ass, “TypeError” will surely appear if we don’t think of our data type in advance. So nowadays, since Typescript has become into quite a standard or convention of almost all of the new born JS libraries. Without a doubt, this feature is a “must” to our project.</p>
<h3 id="2-jest"><a class="zola-anchor" href="#2-jest" aria-label="Anchor link for: 2-jest">#</a>
2. Jest</h3>
<p>Test is another thing that a JS project cannot live without. I believe not a team leader will choose a technology that’s not even being tested by itself. So Jest is certainly the utility that we need for tests, as you know they got a big community.</p>
<h3 id="3-prettier"><a class="zola-anchor" href="#3-prettier" aria-label="Anchor link for: 3-prettier">#</a>
3. Prettier</h3>
<p>Unifying the team’s coding style is time-saving. It matters the most when you are visiting your teammates pull request.</p>
<p>The first time when I saw this tool was 2017. Back then, there was almost no JS code formatters in the open market. Well, Prettier made it available. You can format the code the way you hope it should look like.</p>
<p>And what’s more, with the help of ESlinter or TSlinter the editor could become into a really cool stuff for JS developers.</p>
<p>The reason to adopt such tools is simple because:</p>
<blockquote>
<p>They give you hints!</p>
</blockquote>
<p>Just take a look at the <a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/airbnb/javascript">Airbnb’s javascript style guide</a> which was created 7 years ago, you’ll know how important the code style is.</p>
<h3 id="4-husky-conventional-changelog"><a class="zola-anchor" href="#4-husky-conventional-changelog" aria-label="Anchor link for: 4-husky-conventional-changelog">#</a>
4. Husky & Conventional-changelog</h3>
<p>I think everyone has the following insatiable wishes.</p>
<blockquote>
<p>I wish my project can spit out the changelog automatically.
I wish the commit format can be united.
I wish …</p>
</blockquote>
<p>These tools may sound stange to you. But they are actually a great combination to generate stable changelogs automatically based on the git commit messages. Angular project is using this approach to create better changelogs.</p>
<p>Take a look at the Angular’s beatiful changelog:</p>
<pre data-lang="txt" style="background-color:#212733;color:#ccc9c2;" class="language-txt "><code class="language-txt" data-lang="txt"><span>11.0.0-next.3 (2020-09-23)
</span><span>
</span><span>Bug Fixes
</span><span>
</span><span>common: add params and reportProgress options to HttpClient.put() overload (#37873) (dd8d8c8), closes #23600
</span><span>compiler-cli: generate let statements in ES2015+ mode (#38775) (123bff7)
</span><span>core: ensure TestBed is not instantiated before override provider (#38717) (c8f056b)
</span><span>forms: type NG_VALUE_ACCESSOR injection token as array (#29723) (2b1b718), closes #29351
</span><span>Features
</span><span>
</span><span>common: Add ISO week-numbering year formats support to formatDate (#38828) (984ed39)
</span><span>compiler: Parse and recover on incomplete opening HTML tags (#38681) (6ae3b68), closes #38596
</span><span>router: add migration to update calls to navigateByUrl and createUrlTree with invalid parameters (#38825) (7849fdd), closes #38227
</span><span>service-worker: add the option to prefer network for navigation requests (#38565) (a206852), closes #38194
</span><span>BREAKING CHANGES
</span><span>
</span><span>core: If you call TestBed.overrideProvider after TestBed initialization, provider overrides are not applied. This behavior is consistent with other override methods (such as TestBed.overrideDirective, etc) but they throw an error to indicate that, when the check was missing in the TestBed.overrideProvider function. Now calling TestBed.overrideProvider after TestBed initialization also triggers an error, thus there is a chance that some tests (where TestBed.overrideProvider is called after TestBed initialization) will start to fail and require updates to move TestBed.overrideProvider calls before TestBed initialization is completed.
</span><span>
</span></code></pre>
<blockquote>
<p>Just don’t write the changelog yourself. Let the machines handle them.</p>
</blockquote>
<p>Okay, these 4 tools are basically the features I’m really really eager for as a “DX-first” developer. There are of course some other nice features to have, but I think it’s already enough to start with at the moment. After all, new more tools will increase the learning time for each of our members.</p>
<h2 id="the-rollup"><a class="zola-anchor" href="#the-rollup" aria-label="Anchor link for: the-rollup">#</a>
The “Rollup”</h2>
<p>While I was prototyping my repository, I never thought that Rollup would be the biggest challenge to me. Rollup has a great document which you would understand what it hopes you to do immediately just by looking at the examples. But the true problems locate at how I should handle my output files.</p>
<p>Since my output is a library, I need to rollup all my sources into one JS file that can be used within a browser (or maybe Node.js). This can be easily done by Gulp or Grunt with some plugins. I’m pretty new to this magical tool that has enpowered the most famous frameworks like Vue and React.</p>
<p>Frankly speaking, I don’t know much about how I should move next.</p>
<p>In order to save those steps of moving back and forth, I gave up on exploring the Rollup configurations. As you could imagine, there’s no way for a “noob” to create something “great” from completely zero.</p>
<p>Alright, then. Let me try another approach.</p>
<blockquote>
<p>If I don’t know how to do it, then somebody else should know.</p>
</blockquote>
<p>Vue and React have already done the homework, the rest is me copying them :D.
(Very proud of being a copycat~)</p>
<p>I chose <a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/vuejs/vue-next">Vue 3.0</a> to be my targeted repo because it’s quite a new project. And Vue currently has a very high popularity.</p>
<p>Its configuration is a bit complex, but still very easy to understand.</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="font-style:italic;color:#5c6773;">// Part of rollup.config.js in Vue-next repo
</span><span>
</span><span style="color:#ffa759;">import </span><span>path </span><span style="color:#ffa759;">from </span><span style="color:#bae67e;">'path'
</span><span style="color:#ffa759;">import </span><span>ts </span><span style="color:#ffa759;">from </span><span style="color:#bae67e;">'rollup-plugin-typescript2'
</span><span style="color:#ffa759;">import </span><span>replace </span><span style="color:#ffa759;">from </span><span style="color:#bae67e;">'@rollup/plugin-replace'
</span><span style="color:#ffa759;">import </span><span>json </span><span style="color:#ffa759;">from </span><span style="color:#bae67e;">'@rollup/plugin-json'
</span><span>
</span><span style="color:#ffa759;">if </span><span>(</span><span style="color:#f29e74;">!</span><span>process</span><span style="color:#f29e74;">.</span><span>env</span><span style="color:#f29e74;">.</span><span>TARGET) {
</span><span> </span><span style="color:#ffa759;">throw </span><span style="color:#f29e74;">new </span><span style="color:#73d0ff;">Error</span><span>(</span><span style="color:#bae67e;">'TARGET package must be specified via --environment flag.'</span><span>)
</span><span>}
</span><span>
</span><span style="color:#ffa759;">const </span><span>masterVersion </span><span style="color:#f29e74;">= </span><span style="color:#f28779;">require</span><span>(</span><span style="color:#bae67e;">'./package.json'</span><span>)</span><span style="color:#f29e74;">.</span><span>version
</span><span style="color:#ffa759;">const </span><span>packagesDir </span><span style="color:#f29e74;">= </span><span>path</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">resolve</span><span>(__dirname</span><span style="color:#ccc9c2cc;">, </span><span style="color:#bae67e;">'packages'</span><span>)
</span><span style="color:#ffa759;">const </span><span>packageDir </span><span style="color:#f29e74;">= </span><span>path</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">resolve</span><span>(packagesDir</span><span style="color:#ccc9c2cc;">, </span><span>process</span><span style="color:#f29e74;">.</span><span>env</span><span style="color:#f29e74;">.</span><span>TARGET)
</span><span style="color:#ffa759;">const </span><span>name </span><span style="color:#f29e74;">= </span><span>path</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">basename</span><span>(packageDir)
</span><span style="color:#ffa759;">const </span><span style="color:#ffd580;">resolve </span><span style="color:#f29e74;">= </span><span style="color:#ffcc66;">p </span><span style="color:#ffa759;">=> </span><span>path</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">resolve</span><span>(packageDir</span><span style="color:#ccc9c2cc;">, </span><span>p)
</span><span style="color:#ffa759;">const </span><span>pkg </span><span style="color:#f29e74;">= </span><span style="color:#f28779;">require</span><span>(</span><span style="color:#ffd580;">resolve</span><span>(</span><span style="color:#bae67e;">`package.json`</span><span>))
</span><span style="color:#ffa759;">const </span><span>packageOptions </span><span style="color:#f29e74;">= </span><span>pkg</span><span style="color:#f29e74;">.</span><span>buildOptions </span><span style="color:#f29e74;">|| </span><span>{}
</span><span>
</span><span style="font-style:italic;color:#5c6773;">// ensure TS checks only once for each build
</span><span style="color:#ffa759;">let </span><span>hasTSChecked </span><span style="color:#f29e74;">= </span><span style="color:#ffcc66;">false
</span><span>
</span><span style="color:#ffa759;">const </span><span>outputConfigs </span><span style="color:#f29e74;">= </span><span>{
</span><span> </span><span style="color:#bae67e;">'esm-bundler'</span><span style="color:#ccc9c2cc;">: </span><span>{
</span><span> file</span><span style="color:#ccc9c2cc;">: </span><span style="color:#ffd580;">resolve</span><span>(</span><span style="color:#bae67e;">`dist/${</span><span>name</span><span style="color:#bae67e;">}.esm-bundler.js`</span><span>)</span><span style="color:#ccc9c2cc;">,
</span><span> format</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">`es`
</span><span> }</span><span style="color:#ccc9c2cc;">,
</span><span> </span><span style="color:#f29e74;">...
</span><span>}
</span><span style="color:#f29e74;">...
</span><span>
</span></code></pre>
<p>After exploring the <a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/vuejs/vue-next">Vue 3.0</a> configuration file <code>rollup.config.js</code>, I found that it only does 3 things:</p>
<ul>
<li>receive the command line parameters via another script</li>
<li>generate list of configs for different types of builds</li>
<li>export that configs list</li>
</ul>
<p>Just by doing a bit of copy & pasting, I managed to create a custom Rollup configuration file that has the above features. But I replaced one of the Rollup plugins because I personally favor the official packages.</p>
<ul>
<li>Changed <code>rollup-plugin-typescript</code> to the official <code>@rollup/plugin-typescript</code></li>
</ul>
<p>Vue provides various types of builds which I think is a smart move, because the users will have different development purposes and environment.</p>
<p>For now, we could see that Vue offers the following types of build outputs based on the output format of JS code (<code>es</code> & <code>cjs</code> & <code>iife</code>). The ones with a <code>prod</code> in the file name is used for production purposes:</p>
<pre data-lang="bash" style="background-color:#212733;color:#ccc9c2;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="font-style:italic;color:#5c6773;"># Vue dist
</span><span>
</span><span style="color:#ffd580;">vue.cjs.js
</span><span style="color:#ffd580;">vue.cjs.prod.js
</span><span style="color:#ffd580;">vue.d.ts
</span><span style="color:#ffd580;">vue.esm-browser.js
</span><span style="color:#ffd580;">vue.esm-browser.prod.js
</span><span style="color:#ffd580;">vue.esm-bundler.js
</span><span style="color:#ffd580;">vue.global.js
</span><span style="color:#ffd580;">vue.global.prod.js
</span><span style="color:#ffd580;">vue.runtime.esm-browser.js
</span><span style="color:#ffd580;">vue.runtime.esm-browser.prod.js
</span><span style="color:#ffd580;">vue.runtime.esm-bundler.js
</span><span style="color:#ffd580;">vue.runtime.global.js
</span><span style="color:#ffd580;">vue.runtime.global.prod.js
</span></code></pre>
<p>I hope that this approach can be applied in our project. Similarly but differently, the build outputs with a <code>dev</code> in the file name is the ones for development.</p>
<p>And what’s more, we don’t really separate the builds like Vue by judging if it’s the <code>runtime</code> or not. So the following outputs are the final targets.</p>
<pre data-lang="bash" style="background-color:#212733;color:#ccc9c2;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="font-style:italic;color:#5c6773;"># hellowrold dist
</span><span>
</span><span style="color:#ffd580;">helloworld.cjs.js </span><span style="font-style:italic;color:#5c6773;"># for using our library via `require` method
</span><span style="color:#ffd580;">helloworld.cjs.dev.js
</span><span style="color:#ffd580;">helloworld.d.ts
</span><span style="color:#ffd580;">helloworld.esm.js </span><span style="font-style:italic;color:#5c6773;"># for using our library via `import` keyword
</span><span style="color:#ffd580;">helloworld.esm.dev.js
</span><span style="color:#ffd580;">helloworld.js </span><span style="font-style:italic;color:#5c6773;"># for browser
</span><span style="color:#ffd580;">helloworld.dev.js
</span><span style="color:#ffd580;">helloworld.modern.js </span><span style="font-style:italic;color:#5c6773;"># for modern browser like latest Chrome or latest Firefox
</span><span style="color:#ffd580;">helloworld.modern.dev.js
</span></code></pre>
<p>Here is the link to the <code>rollup.config.js</code>: <a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/daiyanze/modern-hello-world/blob/master/rollup.config.js">modern-hello-wrold rollup config</a>.</p>
<p><code>TLDR;</code>… but be patient :P.</p>
<h3 id="some-issues-of-my-rollup-configuration"><a class="zola-anchor" href="#some-issues-of-my-rollup-configuration" aria-label="Anchor link for: some-issues-of-my-rollup-configuration">#</a>
some issues of my rollup configuration</h3>
<h4 id="1-type-checking-issue"><a class="zola-anchor" href="#1-type-checking-issue" aria-label="Anchor link for: 1-type-checking-issue">#</a>
1. Type checking issue</h4>
<p>It seems that even if I hope to build only one package at a time, the Typescript is checking all of the packages within the monorepo no matter they are dependencies to the build target or not.</p>
<p>Besides, the type checking is likely to happen many times while building multiple packages. I could hear my fan is pretty busy during builds. (This is pretty unnecessary)</p>
<p><a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/vuejs/vue-next">Vue 3.0</a> repo used a flag to disable the duplicated type checking while I didn’t. I’m not very sure if this is a good approach or not. But it will surely affect our development or even production builds.</p>
<h4 id="2-declaration-exports-issue"><a class="zola-anchor" href="#2-declaration-exports-issue" aria-label="Anchor link for: 2-declaration-exports-issue">#</a>
2. Declaration exports issue</h4>
<p>My helloworld is using the same tool (API-Extractor) and configurations of Vue for extracting the type declarations from the source code. I’m using a different Typescript plugin. Regrading building declaration outputs, I need to pass the <code>tsconfig.json</code> parameter <code>declaration</code> to that plugin.</p>
<p>Apparently, I didn’t do it. Because I opinionatedly thought building without <code>declaration</code> would be slightly faster. And this could be a wrong idea. Anyhow, I should optimize this part later.</p>
<h2 id="the-build-scripts"><a class="zola-anchor" href="#the-build-scripts" aria-label="Anchor link for: the-build-scripts">#</a>
The “Build” scripts</h2>
<p>I think Vue project is quite smart in the “build” process. They use commands directly together with <a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/sindresorhus/execa">execa</a> to avoid using the programmable APIs.</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#ffd580;">execa</span><span>(
</span><span> </span><span style="color:#bae67e;">'rollup'</span><span style="color:#ccc9c2cc;">,
</span><span> [
</span><span> </span><span style="color:#bae67e;">'-wc'</span><span style="color:#ccc9c2cc;">,
</span><span> </span><span style="color:#bae67e;">'--environment'</span><span style="color:#ccc9c2cc;">,
</span><span> [
</span><span> </span><span style="color:#bae67e;">`NODE_ENV:development`</span><span style="color:#ccc9c2cc;">,
</span><span> </span><span style="color:#f29e74;">...
</span><span> ]
</span><span> </span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">filter</span><span>(</span><span style="font-style:italic;color:#5ccfe6;">Boolean</span><span>)
</span><span> </span><span style="color:#f29e74;">.</span><span style="color:#f28779;">join</span><span>(</span><span style="color:#bae67e;">','</span><span>)</span><span style="color:#ccc9c2cc;">,
</span><span> ]</span><span style="color:#ccc9c2cc;">,
</span><span> {
</span><span> stdio</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">'inherit'</span><span style="color:#ccc9c2cc;">,
</span><span> }
</span><span>)</span><span style="color:#ccc9c2cc;">;
</span></code></pre>
<p><a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/sindresorhus/execa">execa</a> gives us the direct experience of using those farmiliar commands just by regroup the fragments together. This made things a lot simpler IMHO.</p>
<p>I was once thinking about using the Rollup APIs to handle the builds. But after taking a look at the official document, I realised that it is a stupid idea. It made me feel like enforcing a newbie guitar player who can only play 3 chords to beat the rythm in a big concert.</p>
<p>In a brief conclusion: sometimes it’s maybe a good idea to comprimise to ones making things simpler.</p>
<h2 id="the-packages"><a class="zola-anchor" href="#the-packages" aria-label="Anchor link for: the-packages">#</a>
The “packages”</h2>
<p>As I hope to make it a “Monorepo”, the <code>packages/</code> folder contains all of the necessary builtin modules.</p>
<pre data-lang="bash" style="background-color:#212733;color:#ccc9c2;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="font-style:italic;color:#5c6773;"># In the demo repo, we have 2 modules in total
</span><span style="color:#ffd580;">packages/
</span><span> </span><span style="color:#ffd580;">helloworld/
</span><span> </span><span style="color:#ffd580;">src/
</span><span> </span><span style="color:#ffd580;">index.ts
</span><span> </span><span style="color:#ffd580;">index.js
</span><span> </span><span style="color:#ffd580;">package.json
</span><span> </span><span style="color:#ffd580;">shared/
</span><span> </span><span style="color:#ffd580;">src/
</span><span> </span><span style="color:#ffd580;">print.ts
</span><span> </span><span style="color:#ffd580;">index.js
</span><span> </span><span style="color:#ffd580;">package.json
</span></code></pre>
<p>The <code>shared</code> module is like a <strong>helper</strong> or <strong>util</strong> in a normal repo, but it’s used as a package so that I could import it as if I’m using a third party lib.</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#ffa759;">import </span><span>{ print } </span><span style="color:#ffa759;">from </span><span style="color:#bae67e;">'@helloworld/shared'
</span><span>
</span><span style="color:#ffa759;">function </span><span style="color:#ffd580;">helloWorld</span><span>() {
</span><span> </span><span style="color:#ffa759;">if </span><span>(__DEV__) {
</span><span> </span><span style="color:#ffd580;">print</span><span>(</span><span style="color:#bae67e;">"It's under development"</span><span>)
</span><span> }
</span><span> </span><span style="color:#ffd580;">print</span><span>(</span><span style="color:#bae67e;">'hello world'</span><span>)
</span><span>}
</span></code></pre>
<p>I personally favor the naming convention of prefixing an <code>@<global_module_name></code> to the package. This made all of my modules look very united.</p>
<pre data-lang="json" style="background-color:#212733;color:#ccc9c2;" class="language-json "><code class="language-json" data-lang="json"><span>{
</span><span> </span><span style="color:#bae67e;">"name"</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">"@helloworld/shared"
</span><span> </span><span style="color:#ff3333;">...
</span><span>}
</span></code></pre>
<p>I found out that <a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/vuejs/vue-next">Vue 3.0</a> repo uses <code>NODE_ENV</code> to define the target commonjs module (because the <code>require</code> context usually ignores the Node environment). It will help the users to include the correct script accordingly.</p>
<p>Inside the root dir of each module, I copied & pasted how <a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/vuejs/vue-next">Vue 3.0</a> handles its commonjs modules by adding a new entry file.</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="font-style:italic;color:#5c6773;">// packages/helloworld/index.js
</span><span style="color:#bae67e;">'use strict'
</span><span>
</span><span style="color:#ffa759;">if </span><span>(process</span><span style="color:#f29e74;">.</span><span>env</span><span style="color:#f29e74;">.</span><span>NODE_ENV </span><span style="color:#f29e74;">=== </span><span style="color:#bae67e;">'production'</span><span>) {
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">module</span><span style="color:#f29e74;">.</span><span style="font-style:italic;color:#5ccfe6;">exports </span><span style="color:#f29e74;">= </span><span style="color:#f28779;">require</span><span>(</span><span style="color:#bae67e;">'./dist/helloworld.cjs.js'</span><span>)
</span><span>} </span><span style="color:#ffa759;">else </span><span>{
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">module</span><span style="color:#f29e74;">.</span><span style="font-style:italic;color:#5ccfe6;">exports </span><span style="color:#f29e74;">= </span><span style="color:#f28779;">require</span><span>(</span><span style="color:#bae67e;">'./dist/helloworld.cjs.dev.js'</span><span>)
</span><span>}
</span></code></pre>
<p>The difference between <code>helloworld.cjs.js</code> and <code>helloworld.cjs.dev.js</code> in my example is whether it contains the following code block which only serves the script for development. (Have to say that Rollup “treeshaking” is quite an eye opener to me)</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#f29e74;">...
</span><span style="font-style:italic;color:#5c6773;">// "if (__DEV__)" is treeshaked by Rollup
</span><span>
</span><span>{
</span><span> </span><span style="color:#ffd580;">print</span><span>(</span><span style="color:#bae67e;">'It</span><span style="color:#95e6cb;">\'</span><span style="color:#bae67e;">s under development'</span><span>)
</span><span>}
</span><span style="color:#f29e74;">...
</span></code></pre>
<h2 id="the-summary"><a class="zola-anchor" href="#the-summary" aria-label="Anchor link for: the-summary">#</a>
The “summary”</h2>
<p>During these several weeks of investigation over the <a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/vuejs/vue-next">Vue 3.0</a> repository, I think I’ve found enough fresh new stuffs to learn about. My recent task won’t get kicked-off easily without those smart ideas from them.</p>
<p>Now my project was succesfully released. When I saw my teammates having fun with the “well-thought repository”, I feel my effort is really worth it.</p>
Attempting to create a CSS framework2020-08-18T00:00:00+00:002020-08-18T00:00:00+00:00https://pitayan.com/posts/css-framework-attempt/<p>In 2019, I created a CSS framework and named it <a rel="noopener nofollow noreferrer" target="_blank" href="https://rotalacss.com?ref=pitayan">Rotala.css</a>. After some refactorings and modifications, I finally released the “toy” framework in 2020. But still it is under prototyping because I think my solution is not yet elegant.</p>
<h2 id="the-beginning"><a class="zola-anchor" href="#the-beginning" aria-label="Anchor link for: the-beginning">#</a>
The Beginning</h2>
<p>The reason why I built this framework is simple:</p>
<blockquote>
<p>I want a css framework myself</p>
</blockquote>
<p>I knew it will cost me a lot of time to start building it from scratch. So I hope to create such framework by standing on the shoulder of some other powerful tools in order to speed up my development.</p>
<p>At first, I started prototyping with <code>SASS</code>. It is a tool that allows you to combine a lot of unique grammars so that you could experience designing with CSS like programming.</p>
<pre data-lang="scss" style="background-color:#212733;color:#ccc9c2;" class="language-scss "><code class="language-scss" data-lang="scss"><span style="color:#ffa759;">@mixin </span><span style="color:#ffd580;">button-icon </span><span>{
</span><span> </span><span style="color:#5ccfe6;">margin</span><span style="color:#ccc9c2cc;">: </span><span style="color:#ffcc66;">0 2</span><span style="color:#ffa759;">px</span><span style="color:#ccc9c2cc;">;
</span><span>}
</span><span>
</span><span style="color:#ffd580;">.button </span><span>{
</span><span> </span><span style="color:#5ccfe6;">padding</span><span style="color:#ccc9c2cc;">: </span><span style="color:#ffcc66;">2</span><span style="color:#ffa759;">px</span><span style="color:#ccc9c2cc;">;
</span><span>
</span><span> </span><span style="color:#ffa759;">@include</span><span style="color:#ffd580;"> button-icon</span><span style="color:#ccc9c2cc;">;
</span><span>}
</span></code></pre>
<p>Frankly speaking, I learned a lot of good techniques from other famous frameworks like <code>Bootstrap</code> & <code>Bulma</code> & <code>Spectre</code> & <code>Miligram</code>. And I borrowed some good designs from them especially from <code>Spectre.css</code> (There’s no shame about reinventing the wheels by imitating others).</p>
<h2 id="a-remake-attempt"><a class="zola-anchor" href="#a-remake-attempt" aria-label="Anchor link for: a-remake-attempt">#</a>
A Remake Attempt</h2>
<p>CSS was never my expertise. So I didn’t expect any goodies from my initial prototyping. Everything I made the first time was frigile and “copy-cat”. There’s no “I-created-it” in the framework.</p>
<p>Even it was just an unexperienced attempt, how could I undertake such bad result?</p>
<p>Without a doubt, I started it over.</p>
<blockquote>
<p>This time, I’ll make a great one. Great enough to make me smile.</p>
</blockquote>
<p>By a lucky chance, I saw a video talking about a different CSS framework <code>Tailwind.css</code> which made everything nicer. It is perhaps the time to give it a go.</p>
<p><img src="./images/tailwind.png" alt="tailwind.css" /></p>
<p><code>Tailwind.css</code> allows you to build your own framework with their “partical” styling classes. I’m quite into such solution since it is the original usage of styling HTML templates.</p>
<pre data-lang="html" style="background-color:#212733;color:#ccc9c2;" class="language-html "><code class="language-html" data-lang="html"><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">button </span><span style="color:#ffd580;">class</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"mx-4 p-2 text-gray-600 bg-gray-300"</span><span style="color:#5ccfe690;">></span><span>button</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">button</span><span style="color:#5ccfe690;">>
</span></code></pre>
<p>Everything in <code>Tailwind.css</code> is segmented tiny enough so that writing these classes into the element is just like putting the building blocks together.</p>
<p>However, my <a rel="noopener nofollow noreferrer" target="_blank" href="https://rotalacss.com?ref=pitayan">Rotala.css</a> will output stylesheets not templates. So it is a “must” to figure out how I could make it spit out some files on build.</p>
<p>The research proved my worry was redundant. All of the styles in <code>Tailwind.css</code> can be compiled into a small <code>css</code> file with proper configurations just like <code>SASS</code>.</p>
<pre data-lang="scss" style="background-color:#212733;color:#ccc9c2;" class="language-scss "><code class="language-scss" data-lang="scss"><span style="font-style:italic;color:#5c6773;">/* Base */
</span><span style="color:#ffa759;">@import </span><span style="color:#bae67e;">"tailwindcss/base"</span><span style="color:#ccc9c2cc;">;
</span><span style="color:#ffa759;">@import </span><span style="color:#bae67e;">"./base.pcss"</span><span style="color:#ccc9c2cc;">;
</span><span>
</span><span style="font-style:italic;color:#5c6773;">/* Components */
</span><span style="color:#ffa759;">@import </span><span style="color:#bae67e;">"tailwindcss/components"</span><span style="color:#ccc9c2cc;">;
</span><span style="color:#ffa759;">@import </span><span style="color:#bae67e;">"./components.pcss"</span><span style="color:#ccc9c2cc;">;
</span></code></pre>
<p>The compile was made simple with <code>postcss-cli</code></p>
<pre data-lang="bash" style="background-color:#212733;color:#ccc9c2;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#ffd580;">$</span><span> postcss docs/main.pcss</span><span style="color:#ffcc66;"> -o</span><span> docs/assets/css/rotala.css
</span><span style="color:#ffd580;">...
</span></code></pre>
<p>As you can see from the build command, I completely ditched <code>SASS</code> and migrated to <code>Postcss</code>. There’s nothing bad about <code>SASS</code>, but I just hope to stick with only one technology for my framework to avoid some complexities.</p>
<h2 id="construct-the-source-folder"><a class="zola-anchor" href="#construct-the-source-folder" aria-label="Anchor link for: construct-the-source-folder">#</a>
Construct the Source Folder</h2>
<p><img src="./images/github_rotala_repo.png" alt="github rotala css repository" /></p>
<p>Along with restart everything over again and again, I finally found a pattern to keep my code base in a good shape.</p>
<p>The source folder structure looks like this:</p>
<pre data-lang="txt" style="background-color:#212733;color:#ccc9c2;" class="language-txt "><code class="language-txt" data-lang="txt"><span>rotala/
</span><span> docs/
</span><span> style/
</span><span> CHANGELOG.md
</span><span> README.md
</span><span> package.json
</span><span> postcss.config.js
</span></code></pre>
<p><code>docs/</code> folder is meant to hold static files that could help demonstrate the output. This is also an alternative setting for Github pages that could easily help publish static page without an extra route param.</p>
<pre data-lang="txt" style="background-color:#212733;color:#ccc9c2;" class="language-txt "><code class="language-txt" data-lang="txt"><span>docs/
</span><span> assets/
</span><span> base/
</span><span> components/
</span><span> index.html
</span><span> main.pcss
</span></code></pre>
<p><code>style/</code> contains all of the source styles. At the beginning, I made around <strong>20</strong> components because I believe they are quite necessary for building the fundamental parts of a modern website. Those styles were heavily based on <code>Spectre.css</code> and <code>Bulma</code> (I’m fan of those frameworks).</p>
<pre data-lang="txt" style="background-color:#212733;color:#ccc9c2;" class="language-txt "><code class="language-txt" data-lang="txt"><span>style/
</span><span> base/
</span><span> components/
</span><span> Accordion/
</span><span> Typography/
</span><span> Badge/
</span><span> Breadcrumb/
</span><span> Tooltip/
</span><span> Button/
</span><span> Checkbox/
</span><span> Divider/
</span><span> Drawer/
</span><span> Table Group/
</span><span> Form Group/
</span><span> Input/
</span><span> Tab/
</span><span> Avatar/
</span><span> Link/
</span><span> Menu/
</span><span> Modal/
</span><span> Notification/
</span><span> Pagination/
</span><span> Popover/
</span><span> Radio/
</span><span> Select/
</span><span> base.pcss
</span><span> components.pcss
</span><span> main.pcss
</span><span> prefix.pcss
</span></code></pre>
<h2 id="making-difference"><a class="zola-anchor" href="#making-difference" aria-label="Anchor link for: making-difference">#</a>
Making Difference</h2>
<p>When you read till this line, you may ask:</p>
<blockquote>
<p>How is it different to other frameworks since you’ve copied many of their designs?</p>
</blockquote>
<p>I do also have the same question in my head. My intention was to create my own CSS framework. Repolishing others’ work doesn’t smell like a “creating-my-own” spirit. It means this small framework will forever be a toy of mine and has <strong>no value</strong> to other developers.</p>
<p>Actually, I also hope someone else could benefit from what I built. But I’m already tired of recreating everything from start. Is there a simple way to bring the dead project back to life by adding some finishing touch?</p>
<p>Making “different” is truly difficult especially when you don’t have any good inspirations.</p>
<p>What if I take a step back and think about the pros and cons toward <code>Tailwind.css</code>, could I possbily build up a new feature based on the <code>Tailwind.css</code>’s legacy and its “shortcomings”? I think the answer would be a “<strong>YES</strong>”.</p>
<pre data-lang="html" style="background-color:#212733;color:#ccc9c2;" class="language-html "><code class="language-html" data-lang="html"><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">div </span><span style="color:#ffd580;">class</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"md:flex bg-white rounded-lg p-6"</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">img </span><span style="color:#ffd580;">class</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"h-16 w-16 md:h-24 md:w-24 rounded-full mx-auto md:mx-0 md:mr-6" </span><span style="color:#ffd580;">src</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"avatar.jpg" </span><span style="color:#5ccfe690;">/>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">div </span><span style="color:#ffd580;">class</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"text-center md:text-left"</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">h2 </span><span style="color:#ffd580;">class</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"text-lg"</span><span style="color:#5ccfe690;">></span><span>Erin Lindford</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">h2</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">div </span><span style="color:#ffd580;">class</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"text-purple-500"</span><span style="color:#5ccfe690;">></span><span>Customer Support</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">div </span><span style="color:#ffd580;">class</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"text-gray-600"</span><span style="color:#5ccfe690;">></span><span>erinlindford@example.com</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">div </span><span style="color:#ffd580;">class</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"text-gray-600"</span><span style="color:#5ccfe690;">></span><span>(555) 765-4321</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span></code></pre>
<p>Pros <code>Tailwind.css</code>:</p>
<ul>
<li>Non-opinionated styles</li>
<li>Low level utility classes</li>
<li>Design is customizable</li>
<li>Plugin system</li>
<li>Based on <code>Postcss</code> eco-system</li>
</ul>
<p>Cons <code>Tailwind.css</code>:</p>
<ul>
<li>Template may get too “crowded”</li>
<li>The file size is “big”, need to be purged on build</li>
<li>Utilities are less semantic</li>
</ul>
<p>Even there are some down sides of <code>Tailwind.css</code>, I think they can be outweighed by the <code>Pros</code> far easily. So in my framework, I’ll need to figure out plans on dealing with those <code>Cons</code>.</p>
<p>Have to say the 2nd and 3rd <code>Cons</code> are already part of the <code>Tailwind.css</code>’s “feauture” which I cannot get rid of. But the first one “crowded template” seems fairly easy to balance with. Thanks to the <code>Tailwind.css</code>’s powerful function, I could also write my styles in this way:</p>
<pre data-lang="scss" style="background-color:#212733;color:#ccc9c2;" class="language-scss "><code class="language-scss" data-lang="scss"><span style="color:#ffd580;">.container </span><span>{
</span><span> @apply </span><span style="color:#73d0ff;">bg-white rounded-lg p-6</span><span>;
</span><span>
</span><span> @screen md {
</span><span> @apply </span><span style="color:#5ccfe6;">flex</span><span style="color:#ccc9c2cc;">;
</span><span> }
</span><span>}
</span><span>
</span></code></pre>
<p>I believe the blow usage looks much nicer, isn’t it.</p>
<pre data-lang="html" style="background-color:#212733;color:#ccc9c2;" class="language-html "><code class="language-html" data-lang="html"><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">div </span><span style="color:#ffd580;">class</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"container"</span><span style="color:#5ccfe690;">>
</span><span> ...
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span></code></pre>
<p>And if I hope to change the <code>container</code> a little bit, I could also use the “template style” to decorate it directly.</p>
<pre data-lang="html" style="background-color:#212733;color:#ccc9c2;" class="language-html "><code class="language-html" data-lang="html"><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">div </span><span style="color:#ffd580;">class</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"container font-bold mx-2"</span><span style="color:#5ccfe690;">>
</span><span> ...
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span></code></pre>
<p>I clearly understand that I’m not the first one to think in this way, but at least this can be a good feature of my framework to standout among other frameworks.</p>
<h2 id="the-core-feature"><a class="zola-anchor" href="#the-core-feature" aria-label="Anchor link for: the-core-feature">#</a>
The Core Feature</h2>
<p>As I hope to make differences for my framework, I came up with such core feature to accomplish.</p>
<ul>
<li>“Design-less” & “Extensible” components</li>
</ul>
<p>First of all, <code>Tailwind.css</code> is “design-less”. It gives full control of the stylings to us developers. I will follow that and make sure all my components are just skeletons that contains very rudimentry styles. By meaning “rudimentry styles”, components will have fonts text-size color background-color padding margins et cetera if necessary.</p>
<pre data-lang="scss" style="background-color:#212733;color:#ccc9c2;" class="language-scss "><code class="language-scss" data-lang="scss"><span style="color:#ffd580;">.button </span><span>{
</span><span> @apply </span><span style="color:#73d0ff;">appearance-none</span><span>;
</span><span> @apply </span><span style="color:#73d0ff;">select-none</span><span>;
</span><span> @apply </span><span style="color:#73d0ff;">align-middle</span><span>;
</span><span> @apply </span><span style="color:#73d0ff;">font-medium</span><span>;
</span><span> @apply </span><span style="color:#73d0ff;">text-center</span><span>;
</span><span> @apply </span><span style="color:#73d0ff;">text-base</span><span>;
</span><span> @apply </span><span style="color:#73d0ff;">no-underline</span><span>;
</span><span> @apply </span><span style="color:#73d0ff;">leading-normal</span><span>;
</span><span> @apply </span><span style="color:#73d0ff;">whitespace-no-wrap</span><span>;
</span><span> @apply </span><span style="color:#73d0ff;">inline-block</span><span>;
</span><span> @apply </span><span style="color:#73d0ff;">cursor-pointer</span><span>;
</span><span> @apply </span><span style="color:#73d0ff;">rounded-sm</span><span>;
</span><span> @apply </span><span style="color:#73d0ff;">py-1 px-3</span><span>;
</span><span>}
</span></code></pre>
<p>In this way, all of the components can be modified into the desired shape just by adding the new styles to override. It follows the original practice of how we should handle the CSS stylings.</p>
<p>Suppose we are styling the “skeleton button”:</p>
<p>from this:</p>
<p><img src="./images/skeleton_button.png" alt="skeleton button" /></p>
<p>to this:</p>
<p><img src="./images/gray_button.png" alt="gray button" /></p>
<pre data-lang="html" style="background-color:#212733;color:#ccc9c2;" class="language-html "><code class="language-html" data-lang="html"><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">button </span><span style="color:#ffd580;">class</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"button text-gray-700 bg-gray-300 hover:bg-gray-500 transition-colors duration-150"</span><span style="color:#5ccfe690;">>
</span><span> Background Gray
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">button</span><span style="color:#5ccfe690;">>
</span></code></pre>
<p>In a brief expression:</p>
<blockquote>
<p>Class + Utilities = Your stylish component</p>
</blockquote>
<p>It may look too crowded in the template. So the better way to use it is maybe to <strong>extend</strong> the current <code>class</code> instead.</p>
<pre data-lang="scss" style="background-color:#212733;color:#ccc9c2;" class="language-scss "><code class="language-scss" data-lang="scss"><span style="color:#ffd580;">.button </span><span>{
</span><span> @apply </span><span style="color:#73d0ff;">text-gray-700</span><span>;
</span><span> @apply </span><span style="color:#73d0ff;">bg-gray-300</span><span>;
</span><span> @apply </span><span style="color:#73d0ff;">transition-colors
</span><span> @apply </span><span style="color:#73d0ff;">duration-150</span><span>;
</span><span>
</span><span> </span><span style="color:#f29e74;">&</span><span>:hover {
</span><span> @apply </span><span style="color:#73d0ff;">bg-gray-500</span><span>;
</span><span> }
</span><span>}
</span><span>
</span></code></pre>
<h2 id="conclusions"><a class="zola-anchor" href="#conclusions" aria-label="Anchor link for: conclusions">#</a>
Conclusions</h2>
<p>The rest of the tasks will be to implement all other components I hope to have in the framework. It took less time creating each of them than before because I defined how to use the “skeleton” components as a core feature.</p>
<p>Now there are all essential components for building a website. The blog page you are reading is actually utilizing the <a rel="noopener nofollow noreferrer" target="_blank" href="https://rotalacss.com?ref=pitayan">Rotala.css</a> framework. There are still a lot of drawbacks but in my opinion it is such an achievement for me to create something I’m not good at from totally 0.</p>
<p>Anyhow, I’ll continue developing the framework. I’d appreciate that you are also interested in my little work here. Feel free to drop me emails to tell about your opinions of <a rel="noopener nofollow noreferrer" target="_blank" href="https://rotalacss.com?ref=pitayan">Rotala.css</a>. Any PRs or issues are welcome!</p>
<p>About how to use <a rel="noopener nofollow noreferrer" target="_blank" href="https://rotalacss.com?ref=pitayan">Rotala.css</a>, refer to the document by clicking the link below.</p>
<p><a rel="noopener nofollow noreferrer" target="_blank" href="https://rotalacss.com?ref=pitayan">https://rotalacss.com</a></p>
Vue 3 new features summary2020-08-11T00:00:00+00:002020-08-11T00:00:00+00:00https://pitayan.com/posts/vue-next-features/<p>Vue-next (Vue 3) has been out for a while. It is now under release candidate stage which means there won’t be big changes on the open APIs. Good to see that Vue has already been stabilized and ready to waltz into our projects.</p>
<p>I have to say that Vue 2 is already amazing enough. But with Vue 3’s new features, it’s likely to upgrade our projects to an upper level. I guess the most thrilling feature in Vue 3 would be the composition APIs. Evan You himself mentioned that the composition APIs are inspired by the React hooks. Even though the two APIs hooks and compositions are a lot alike, but from the code base they are completely different. Let’s not discuss which is better or promising because I don’t really think either framework outraces another.</p>
<p>In all, it’s so happy to see that Vue can also do what React does. Let’s have a close look at the new features.</p>
<p><strong>TLDR;</strong></p>
<h2 id="1-vite"><a class="zola-anchor" href="#1-vite" aria-label="Anchor link for: 1-vite">#</a>
1. Vite</h2>
<p>This is another work of art by Evan You which is aim at replacing <a rel="noopener nofollow noreferrer" target="_blank" href="https://webpack.js.org">Webpack</a> in Vue development (Currently only works for Vue). It is designed to be <strong>fast</strong> just as its French name implies.</p>
<h3 id="getting-started-with-vite"><a class="zola-anchor" href="#getting-started-with-vite" aria-label="Anchor link for: getting-started-with-vite">#</a>
Getting started with Vite</h3>
<p>The official repo offers us a simple way to create a Vue 3 app via <a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/vitejs/vite">Vite</a>.</p>
<h4 id="npm"><a class="zola-anchor" href="#npm" aria-label="Anchor link for: npm">#</a>
Npm</h4>
<pre data-lang="bash" style="background-color:#212733;color:#ccc9c2;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#ffd580;">$</span><span> npm init vite-app </span><span style="color:#f29e74;"><</span><span>project-name</span><span style="color:#f29e74;">>
</span><span style="color:#ffd580;">$</span><span> cd </span><span style="color:#f29e74;"><</span><span>project-name</span><span style="color:#f29e74;">>
</span><span style="color:#ffd580;">$</span><span> npm install
</span><span style="color:#ffd580;">$</span><span> npm run dev
</span></code></pre>
<h4 id="yarn"><a class="zola-anchor" href="#yarn" aria-label="Anchor link for: yarn">#</a>
Yarn</h4>
<pre data-lang="bash" style="background-color:#212733;color:#ccc9c2;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#ffd580;">$</span><span> yarn create vite-app </span><span style="color:#f29e74;"><</span><span>project-name</span><span style="color:#f29e74;">>
</span><span style="color:#ffd580;">$</span><span> cd </span><span style="color:#f29e74;"><</span><span>project-name</span><span style="color:#f29e74;">>
</span><span style="color:#ffd580;">$</span><span> yarn
</span><span style="color:#ffd580;">$</span><span> yarn dev
</span></code></pre>
<h4 id="start-dev-server"><a class="zola-anchor" href="#start-dev-server" aria-label="Anchor link for: start-dev-server">#</a>
Start Dev Server</h4>
<p>It all happened in a blink of eyes.</p>
<pre data-lang="bash" style="background-color:#212733;color:#ccc9c2;" class="language-bash "><code class="language-bash" data-lang="bash"><span> </span><span style="color:#ffd580;">❯</span><span> yarn dev
</span><span style="color:#ffd580;">yarn</span><span> run v1.22.4
</span><span style="color:#ffd580;">$</span><span> vite
</span><span style="color:#ffd580;">vite</span><span> v1.0.0-rc.4
</span><span>
</span><span> </span><span style="color:#ffd580;">Dev</span><span> server running at:
</span><span> </span><span style="color:#f29e74;">></span><span> Local: </span><span style="color:#ffd580;">http://localhost:3000/
</span><span> </span><span style="color:#f29e74;">></span><span> Network: </span><span style="color:#ffd580;">http://192.168.3.2:3000/
</span><span> </span><span style="color:#f29e74;">></span><span> Network: </span><span style="color:#ffd580;">http://10.80.67.216:3000/
</span></code></pre>
<p>Open http://localhost:3000/</p>
<p><img src="./images/dev_page.png" alt="dev page" /></p>
<h3 id="vue-next-features"><a class="zola-anchor" href="#vue-next-features" aria-label="Anchor link for: vue-next-features">#</a>
vue-next-features</h3>
<ul>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/daiyanze/vue-next-features">repository link</a></li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://daiyanze.com/vue-next-features/dist/">demo link</a></li>
</ul>
<p>I created a small app to <a rel="noopener nofollow noreferrer" target="_blank" href="https://daiyanze.com/vue-next-features/dist/">demo</a> the new features of Vue 3. If you take a look at the projects’ <code>package.json</code>, the simplicity of <a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/daiyanze/vue-next-features">vue-next-features</a> dependencies will make you fond of <a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/vitejs/vite">Vite</a> immediately. (I mean, who doesn’t want a simpler <code>package.json</code> to start with?)</p>
<p>There is another Vue 3 “Hello World” repo (<a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/vuejs/vue-next-webpack-preview">vue-next-webpack-preview</a>) bundled with <a rel="noopener nofollow noreferrer" target="_blank" href="https://webpack.js.org">Webpack</a>. It is also a good playground.</p>
<p><a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/daiyanze/vue-next-features"><strong>vue-next-features</strong></a></p>
<pre data-lang="json" style="background-color:#212733;color:#ccc9c2;" class="language-json "><code class="language-json" data-lang="json"><span>{
</span><span> </span><span style="color:#ff3333;">...,
</span><span> </span><span style="color:#bae67e;">"dependencies"</span><span style="color:#ccc9c2cc;">: </span><span>{
</span><span> </span><span style="color:#bae67e;">"vite"</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">"^1.0.0-rc.4"</span><span style="color:#ccc9c2cc;">,
</span><span> </span><span style="color:#bae67e;">"vue"</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">"^3.0.0-rc.5"
</span><span> }</span><span style="color:#ccc9c2cc;">,
</span><span> </span><span style="color:#bae67e;">"devDependencies"</span><span style="color:#ccc9c2cc;">: </span><span>{
</span><span> </span><span style="color:#bae67e;">"@vue/compiler-sfc"</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">"^3.0.0-rc.5"
</span><span> }
</span><span>}
</span></code></pre>
<p><a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/vuejs/vue-next-webpack-preview"><strong>vue-next-webpack-preview</strong></a></p>
<pre data-lang="json" style="background-color:#212733;color:#ccc9c2;" class="language-json "><code class="language-json" data-lang="json"><span>{
</span><span> </span><span style="color:#ff3333;">...,
</span><span> </span><span style="color:#bae67e;">"dependencies"</span><span style="color:#ccc9c2cc;">: </span><span>{
</span><span> </span><span style="color:#bae67e;">"vue"</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">"^3.0.0-beta.2"
</span><span> }</span><span style="color:#ccc9c2cc;">,
</span><span> </span><span style="color:#bae67e;">"devDependencies"</span><span style="color:#ccc9c2cc;">: </span><span>{
</span><span> </span><span style="color:#bae67e;">"@vue/compiler-sfc"</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">"^3.0.0-beta.2"</span><span style="color:#ccc9c2cc;">,
</span><span> </span><span style="color:#bae67e;">"css-loader"</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">"^3.4.2"</span><span style="color:#ccc9c2cc;">,
</span><span> </span><span style="color:#bae67e;">"file-loader"</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">"^6.0.0"</span><span style="color:#ccc9c2cc;">,
</span><span> </span><span style="color:#bae67e;">"mini-css-extract-plugin"</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">"^0.9.0"</span><span style="color:#ccc9c2cc;">,
</span><span> </span><span style="color:#bae67e;">"url-loader"</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">"^4.0.0"</span><span style="color:#ccc9c2cc;">,
</span><span> </span><span style="color:#bae67e;">"vue-loader"</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">"^16.0.0-alpha.3"</span><span style="color:#ccc9c2cc;">,
</span><span> </span><span style="color:#bae67e;">"webpack"</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">"^4.42.1"</span><span style="color:#ccc9c2cc;">,
</span><span> </span><span style="color:#bae67e;">"webpack-cli"</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">"^3.3.11"</span><span style="color:#ccc9c2cc;">,</span><span style="color:#ff3333;">¥
</span><span> </span><span style="color:#bae67e;">"webpack-dev-server"</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">"^3.10.3"
</span><span> }
</span><span>}
</span></code></pre>
<h2 id="2-composition-api"><a class="zola-anchor" href="#2-composition-api" aria-label="Anchor link for: 2-composition-api">#</a>
2. Composition API</h2>
<p>As the biggest the change of <a rel="noopener nofollow noreferrer" target="_blank" href="https://vuejs.org">Vue.js</a>, the composition API would become your next most frequently and commonly used feature. Just like <a rel="noopener nofollow noreferrer" target="_blank" href="https://reactjs.org/docs/hooks-reference.html#">React hooks</a>, with the Vue composition API will help gain more customizibilities.</p>
<p>Here is a list of the Vue 3 composition APIs. (There are actually more…)</p>
<ul>
<li>
<p>Reactivity</p>
<ul>
<li><code>computed</code> <code>reactive</code> <code>ref</code> <code>readonly</code></li>
<li><code>watch</code> <code>watchEffect</code> <code>unref</code> <code>toRefs</code></li>
<li><code>isRef</code> <code>isProxy</code> <code>isReactive</code> <code>isReadonly</code></li>
<li><code>customRef</code> <code>markRaw</code> <code>shallowReactive</code></li>
<li><code>shallowReadonly</code> <code>shallowRef</code> <code>toRaw</code></li>
</ul>
</li>
<li>
<p>Lifecycle Hooks</p>
<ul>
<li><code>onBeforeMount</code> <code>onBeforeUnmount</code> <code>onBeforeUpdate</code> </li>
<li><code>onMounted</code> <code>onUpdated</code> <code>onErrorCaptured</code> </li>
<li><code>onRenderTracked</code> <code>onRenderTriggered</code> <code>onUnmounted</code> </li>
<li><code>onActivated</code> <code>onDeactivated</code> </li>
</ul>
</li>
</ul>
<p>Visit Vue 3 official doc to know more about these APIs.
<a rel="noopener nofollow noreferrer" target="_blank" href="https://v3.vuejs.org/api/composition-api.html">https://v3.vuejs.org/api/composition-api.html</a></p>
<h3 id="component-styles"><a class="zola-anchor" href="#component-styles" aria-label="Anchor link for: component-styles">#</a>
Component Styles</h3>
<h4 id="in-vue-2"><a class="zola-anchor" href="#in-vue-2" aria-label="Anchor link for: in-vue-2">#</a>
In Vue 2</h4>
<p>Use configuration template to define the component contents. In Vue 3, this legacy usage is still available. If you’d prefer this style, you can continue using it.</p>
<pre data-lang="html" style="background-color:#212733;color:#ccc9c2;" class="language-html "><code class="language-html" data-lang="html"><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">template</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">button </span><span style="color:#ffd580;">@click</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"count++"</span><span style="color:#5ccfe690;">></span><span>count: {{ count }}</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">button</span><span style="color:#5ccfe690;">>
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">template</span><span style="color:#5ccfe690;">>
</span><span>
</span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">script</span><span style="color:#5ccfe690;">>
</span><span style="color:#ffa759;">const </span><span>multiplier </span><span style="color:#f29e74;">= </span><span style="color:#ffcc66;">2
</span><span>
</span><span style="color:#ffa759;">export default </span><span>{
</span><span> </span><span style="color:#ffd580;">data </span><span>() {
</span><span> </span><span style="color:#ffa759;">return </span><span>{
</span><span> count</span><span style="color:#ccc9c2cc;">: </span><span style="color:#ffcc66;">0
</span><span> }
</span><span> }</span><span style="color:#ccc9c2cc;">,
</span><span> computed</span><span style="color:#ccc9c2cc;">: </span><span>{
</span><span> </span><span style="color:#ffd580;">result </span><span>() {
</span><span> </span><span style="color:#ffa759;">return </span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span>count </span><span style="color:#f29e74;">* </span><span>multiplier
</span><span> }
</span><span> }</span><span style="color:#ccc9c2cc;">,
</span><span> </span><span style="color:#ffd580;">mounted </span><span>() {
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(</span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span>count)
</span><span> }</span><span style="color:#ccc9c2cc;">,
</span><span> watch</span><span style="color:#ccc9c2cc;">: </span><span>{
</span><span> </span><span style="color:#ffd580;">count </span><span>(</span><span style="color:#ffcc66;">val</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">oldVal</span><span>) {
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(val</span><span style="color:#ccc9c2cc;">, </span><span>oldVal)
</span><span> }
</span><span> }
</span><span>}
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">script</span><span style="color:#5ccfe690;">>
</span></code></pre>
<h4 id="in-vue-3"><a class="zola-anchor" href="#in-vue-3" aria-label="Anchor link for: in-vue-3">#</a>
In Vue 3</h4>
<p>To use the composition API, you’ll need to add a <code>setup</code> property in to the default export. The below code is completely equivalent to the <a href="https://pitayan.com/posts/vue-next-features/#in-vue-2">code above</a>.</p>
<pre data-lang="html" style="background-color:#212733;color:#ccc9c2;" class="language-html "><code class="language-html" data-lang="html"><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">template</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">button </span><span style="color:#ffd580;">@click</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"count++"</span><span style="color:#5ccfe690;">></span><span>count: {{ count }}</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">button</span><span style="color:#5ccfe690;">>
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">template</span><span style="color:#5ccfe690;">>
</span><span>
</span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">script</span><span style="color:#5ccfe690;">>
</span><span style="color:#ffa759;">import </span><span>{ computed</span><span style="color:#ccc9c2cc;">, </span><span>reactive</span><span style="color:#ccc9c2cc;">, </span><span>toRefs</span><span style="color:#ccc9c2cc;">, </span><span>onMounted</span><span style="color:#ccc9c2cc;">, </span><span>watch } </span><span style="color:#ffa759;">from </span><span style="color:#bae67e;">'vue'
</span><span>
</span><span style="color:#ffa759;">export default </span><span>{
</span><span> </span><span style="color:#ffd580;">setup </span><span>() {
</span><span> </span><span style="color:#ffa759;">const </span><span>multiplier </span><span style="color:#f29e74;">= </span><span style="color:#ffcc66;">2
</span><span>
</span><span> </span><span style="color:#ffa759;">const </span><span>state </span><span style="color:#f29e74;">= </span><span style="color:#ffd580;">reactive</span><span>({
</span><span> count</span><span style="color:#ccc9c2cc;">: </span><span style="color:#ffcc66;">0
</span><span> })
</span><span>
</span><span> </span><span style="color:#ffa759;">const </span><span>result </span><span style="color:#f29e74;">= </span><span style="color:#ffd580;">computed</span><span>(() </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="color:#ffa759;">return </span><span>state</span><span style="color:#f29e74;">.</span><span>count </span><span style="color:#f29e74;">* </span><span>multiplier
</span><span> })
</span><span>
</span><span> </span><span style="color:#ffd580;">onMounted</span><span>(() </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(state</span><span style="color:#f29e74;">.</span><span>count)
</span><span> })
</span><span>
</span><span> </span><span style="color:#ffd580;">watch</span><span>(state</span><span style="color:#f29e74;">.</span><span>count</span><span style="color:#ccc9c2cc;">, </span><span>(</span><span style="color:#ffcc66;">val</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">oldVal</span><span>) </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(val</span><span style="color:#ccc9c2cc;">, </span><span>oldVal)
</span><span> })
</span><span>
</span><span> </span><span style="color:#ffa759;">return </span><span>{
</span><span> </span><span style="color:#f29e74;">...</span><span style="color:#ffd580;">toRefs</span><span>(state)
</span><span> }
</span><span> }
</span><span>}
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">script</span><span style="color:#5ccfe690;">>
</span></code></pre>
<h4 id="go-ahead-with-the-new-api"><a class="zola-anchor" href="#go-ahead-with-the-new-api" aria-label="Anchor link for: go-ahead-with-the-new-api">#</a>
Go Ahead with the new API</h4>
<p>There are 4 reasons why you should use composition API over the default Vue 2 config template:</p>
<ul>
<li>To increase readability of source code</li>
<li>To avoid duplicated or redundant logics</li>
<li>To group up similar logics</li>
<li><strong>To reuse the logics</strong></li>
</ul>
<p>Compared to the Vue 2 configuration style, the logics are precisely broken down into smaller particals so that you could group the similar logics together easily. In this way, it also reduces chances jumping around from irrelavant logics. This will help increase the productivity without a doubt.</p>
<h2 id="2-advanced-reactivity-api"><a class="zola-anchor" href="#2-advanced-reactivity-api" aria-label="Anchor link for: 2-advanced-reactivity-api">#</a>
2. Advanced Reactivity API</h2>
<p>Personally, I think this is nothing different to the other reactivity APIs. But it indeed offers those abilities of handling edge cases like <strong>custom hooks</strong> and <strong>shallow layer modification</strong>. It is now part of the <a rel="noopener nofollow noreferrer" target="_blank" href="https://v3.vuejs.org/api/basic-reactivity.html">basic reactivity API</a> according to the Vue 3 official doc.</p>
<p>In the <a rel="noopener nofollow noreferrer" target="_blank" href="https://composition-api.vuejs.org/">Vue composition api</a> doc (Yes, there’s a doc only for the composition APIs), the following APIs are listed as <strong>advanced reactivity apis</strong>.</p>
<ul>
<li>customRef: custom hook</li>
<li>markRaw: not able to be a <code>reactive</code></li>
<li>shallowReactive: Object’s first layer <code>reactive</code></li>
<li>shallowReadonly: Object’s first layer <code>readonly</code></li>
<li>shallowRef: Object’s value not <code>reactive</code></li>
<li>toRaw: restore a <code>reactive</code> to normal Object</li>
</ul>
<p>Are you farmiliar with <code>Debounce</code>? Here is an official demo of <code>customRef</code>:</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#ffa759;">import </span><span>{ customRef } </span><span style="color:#ffa759;">from </span><span style="color:#bae67e;">'vue'
</span><span>
</span><span style="color:#ffa759;">const </span><span style="color:#ffd580;">useDebouncedRef </span><span style="color:#f29e74;">= </span><span>(</span><span style="color:#ffcc66;">value</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">delay </span><span style="color:#f29e74;">= </span><span style="color:#ffcc66;">200</span><span>) </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="color:#ffa759;">let </span><span>timeout
</span><span> </span><span style="color:#ffa759;">return </span><span style="color:#ffd580;">customRef</span><span>((</span><span style="color:#ffcc66;">track</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">trigger</span><span>) </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="color:#ffa759;">return </span><span>{
</span><span> </span><span style="color:#ffd580;">get</span><span>() {
</span><span> </span><span style="color:#ffd580;">track</span><span>()
</span><span> </span><span style="color:#ffa759;">return </span><span>value
</span><span> }</span><span style="color:#ccc9c2cc;">,
</span><span> </span><span style="color:#ffd580;">set</span><span>(</span><span style="color:#ffcc66;">newValue</span><span>) {
</span><span> </span><span style="color:#f28779;">clearTimeout</span><span>(timeout)
</span><span> timeout </span><span style="color:#f29e74;">= </span><span style="color:#f28779;">setTimeout</span><span>(() </span><span style="color:#ffa759;">=> </span><span>{
</span><span> value </span><span style="color:#f29e74;">= </span><span>newValue
</span><span> </span><span style="color:#ffd580;">trigger</span><span>()
</span><span> }</span><span style="color:#ccc9c2cc;">, </span><span>delay)
</span><span> }</span><span style="color:#ccc9c2cc;">,
</span><span> }
</span><span> })
</span><span>}
</span><span>
</span><span style="color:#ffa759;">export default </span><span>{
</span><span> </span><span style="color:#ffd580;">setup </span><span>() {
</span><span> </span><span style="color:#ffa759;">return </span><span>{
</span><span> text</span><span style="color:#ccc9c2cc;">: </span><span style="color:#ffd580;">useDebouncedRef</span><span>(</span><span style="color:#bae67e;">'some text'</span><span>)
</span><span> }
</span><span> }
</span><span>}
</span></code></pre>
<h2 id="3-v-enter-from-v-leave-from"><a class="zola-anchor" href="#3-v-enter-from-v-leave-from" aria-label="Anchor link for: 3-v-enter-from-v-leave-from">#</a>
3. v-enter-from / v-leave-from</h2>
<p>In Vue 2, the <code><Transition></code> component helps handle the component <code>animation</code> / <code>transition</code>. But the component property <code>v-enter-active</code> <code>v-enter</code> <code>v-enter-to</code> were quite ambiguous to me. Sometimes I’m confused which happens first.</p>
<p>Now in Vue 3, those transition property names became more unified and intuitive.</p>
<ul>
<li><code>v-enter</code> => <code>v-enter-from</code></li>
<li><code>v-leave</code> => <code>v-leave-from</code></li>
</ul>
<pre data-lang="html" style="background-color:#212733;color:#ccc9c2;" class="language-html "><code class="language-html" data-lang="html"><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">template</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">transition </span><span style="color:#ffd580;">name</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"fade"</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">div </span><span style="color:#ffd580;">v-show</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"show"</span><span style="color:#5ccfe690;">></span><span>fade transition</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">transition</span><span style="color:#5ccfe690;">>
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">template</span><span style="color:#5ccfe690;">>
</span><span>
</span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">script</span><span style="color:#5ccfe690;">>
</span><span style="color:#ffa759;">import </span><span>{ reactive</span><span style="color:#ccc9c2cc;">, </span><span>toRefs } </span><span style="color:#ffa759;">from </span><span style="color:#bae67e;">'vue'
</span><span style="color:#ffa759;">export default </span><span>{
</span><span> </span><span style="color:#ffd580;">setup </span><span>() {
</span><span> </span><span style="color:#ffa759;">const </span><span>state </span><span style="color:#f29e74;">= </span><span style="color:#ffd580;">reactive</span><span>({
</span><span> show</span><span style="color:#ccc9c2cc;">: </span><span style="color:#ffcc66;">true
</span><span> })
</span><span>
</span><span> </span><span style="color:#f28779;">setTimeout</span><span>(() </span><span style="color:#ffa759;">=> </span><span>{
</span><span> state</span><span style="color:#f29e74;">.</span><span>show </span><span style="color:#f29e74;">= </span><span style="color:#ffcc66;">false
</span><span> }</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">1000</span><span>)
</span><span>
</span><span> </span><span style="color:#f28779;">setTimeout</span><span>(() </span><span style="color:#ffa759;">=> </span><span>{
</span><span> state</span><span style="color:#f29e74;">.</span><span>show </span><span style="color:#f29e74;">= </span><span style="color:#ffcc66;">true
</span><span> }</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">2000</span><span>)
</span><span>
</span><span> </span><span style="color:#ffa759;">return </span><span>{
</span><span> </span><span style="color:#f29e74;">...</span><span style="color:#ffd580;">toRefs</span><span>(state)
</span><span> }
</span><span> }
</span><span>}
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">script</span><span style="color:#5ccfe690;">>
</span><span>
</span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">style</span><span style="color:#5ccfe690;">>
</span><span style="color:#ffd580;">.fade-enter-from</span><span style="color:#ccc9c2cc;">,
</span><span style="color:#ffd580;">.fade-leave-to </span><span>{
</span><span> </span><span style="color:#5ccfe6;">opacity</span><span style="color:#ccc9c2cc;">: </span><span style="color:#ffcc66;">0</span><span style="color:#ccc9c2cc;">;
</span><span>}
</span><span>
</span><span style="color:#ffd580;">.fade-enter-to</span><span style="color:#ccc9c2cc;">,
</span><span style="color:#ffd580;">.fade-leave-from </span><span>{
</span><span> </span><span style="color:#5ccfe6;">opacity</span><span style="color:#ccc9c2cc;">: </span><span style="color:#ffcc66;">1</span><span style="color:#ccc9c2cc;">;
</span><span>}
</span><span>
</span><span style="color:#ffd580;">.fade-enter-active</span><span style="color:#ccc9c2cc;">,
</span><span style="color:#ffd580;">.fade-leave-active </span><span>{
</span><span> </span><span style="color:#5ccfe6;">transition</span><span style="color:#ccc9c2cc;">: </span><span style="font-style:italic;color:#f29e74;">opacity </span><span style="color:#ffcc66;">2000</span><span style="color:#ffa759;">ms</span><span style="color:#ccc9c2cc;">;
</span><span>}
</span><span>
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">style</span><span style="color:#5ccfe690;">>
</span></code></pre>
<p>The transition order:</p>
<ol>
<li><code>v-enter-from</code> (v-enter)</li>
<li><code>v-enter-active</code></li>
<li><code>v-enter-to</code></li>
<li><code>v-leave-from</code> (v-leave)</li>
<li><code>v-leave-active</code></li>
<li><code>v-leave-to</code></li>
</ol>
<p>I believe this is much easier to understand, isn’t it?</p>
<h2 id="4-allow-multiple-root-element"><a class="zola-anchor" href="#4-allow-multiple-root-element" aria-label="Anchor link for: 4-allow-multiple-root-element">#</a>
4. Allow Multiple Root Element</h2>
<p>Vue 2 throws errors on multiple root element. All elements must be nested within one root element in the template.</p>
<pre data-lang="html" style="background-color:#212733;color:#ccc9c2;" class="language-html "><code class="language-html" data-lang="html"><span style="font-style:italic;color:#5c6773;"><!-- Error -->
</span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">template</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">></span><span>pitayan</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">></span><span>blog</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">template</span><span style="color:#5ccfe690;">>
</span><span>
</span><span style="font-style:italic;color:#5c6773;"><!-- One Root Element only -->
</span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">template</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">></span><span>pitayan</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">></span><span>blog</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">template</span><span style="color:#5ccfe690;">>
</span><span>
</span></code></pre>
<p>Vue 3 removed this annoying usage. I think this is extremely helpful when you really don’t want to nest your elements within a “container” parent. Sometimes all you need is maybe just to insert those bare elements into the right place.</p>
<p>This works similarly to the <a rel="noopener nofollow noreferrer" target="_blank" href="https://reactjs.org/docs/fragments.html">React Fragments</a> which helps mitigate the nesting issues.</p>
<pre data-lang="html" style="background-color:#212733;color:#ccc9c2;" class="language-html "><code class="language-html" data-lang="html"><span style="font-style:italic;color:#5c6773;"><!-- Vue 3 Multiple Root Element -->
</span><span style="font-style:italic;color:#5c6773;"><!-- Okay -->
</span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">template</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">></span><span>pitayan</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">></span><span>blog</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">template</span><span style="color:#5ccfe690;">>
</span></code></pre>
<h2 id="5-filters-is-deprecated-removed"><a class="zola-anchor" href="#5-filters-is-deprecated-removed" aria-label="Anchor link for: 5-filters-is-deprecated-removed">#</a>
5. “Filters” is Deprecated(Removed)</h2>
<p>I think a lot of people think that <code>filters</code> is maybe an awesome feature of Vue.js. It indeed works well in Vue’s template engine. (For example, data formatting / calculation etc).</p>
<p>Let’s see how Vue 3 doc explains why <code>filters</code> is removed:</p>
<blockquote>
<p>While this seems like a convenience, it requires a custom syntax that breaks the assumption of expressions inside of curly braces being “just JavaScript,” which has both learning and implementation costs.</p>
</blockquote>
<p>I believe it’s nothing bad for development without the <code>filters</code>, even though it may cost you extra time on migrating to Vue 3. In my projects, the appearance of <code>filters</code> is pretty a rare case since I could replace such functionality with a <code>method</code> or <code>computed</code> easily. Because in my opinion, <code>method</code> / <code>computed</code> has higher readability than the <code>filters</code>.</p>
<pre data-lang="html" style="background-color:#212733;color:#ccc9c2;" class="language-html "><code class="language-html" data-lang="html"><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">template</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="font-style:italic;color:#5c6773;"><!-- Deprecated (removed) & Error -->
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">span</span><span style="color:#5ccfe690;">></span><span>{{ count | double }}</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">span</span><span style="color:#5ccfe690;">>
</span><span>
</span><span> </span><span style="font-style:italic;color:#5c6773;"><!-- If you have to use fiter, make it a function -->
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">span</span><span style="color:#5ccfe690;">></span><span>{{ double(count) }}</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">span</span><span style="color:#5ccfe690;">>
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">template</span><span style="color:#5ccfe690;">>
</span><span>
</span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">script</span><span style="color:#5ccfe690;">>
</span><span style="color:#ffa759;">import </span><span>{ ref } </span><span style="color:#ffa759;">from </span><span style="color:#bae67e;">'vue'
</span><span>
</span><span style="color:#ffa759;">export default </span><span>{
</span><span> </span><span style="font-style:italic;color:#5c6773;">// Not working
</span><span> filters</span><span style="color:#ccc9c2cc;">: </span><span>{
</span><span> </span><span style="color:#ffd580;">double </span><span>(</span><span style="color:#ffcc66;">val</span><span>) {
</span><span> </span><span style="color:#ffa759;">return </span><span>val </span><span style="color:#f29e74;">* </span><span style="color:#ffcc66;">2
</span><span> }
</span><span> }</span><span style="color:#ccc9c2cc;">,
</span><span> </span><span style="color:#ffd580;">setup </span><span>() {
</span><span> </span><span style="color:#ffa759;">const </span><span>count </span><span style="color:#f29e74;">= </span><span style="color:#ffd580;">ref</span><span>(</span><span style="color:#ffcc66;">1</span><span>)
</span><span> </span><span style="color:#ffa759;">return </span><span>{
</span><span> count</span><span style="color:#ccc9c2cc;">,
</span><span> </span><span style="color:#ffd580;">double</span><span style="color:#ccc9c2cc;">: </span><span style="color:#ffcc66;">val </span><span style="color:#ffa759;">=> </span><span>val </span><span style="color:#f29e74;">* </span><span style="color:#ffcc66;">2
</span><span> }
</span><span> }
</span><span>}
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">script</span><span style="color:#5ccfe690;">>
</span></code></pre>
<h2 id="6-new-async-component-suspense"><a class="zola-anchor" href="#6-new-async-component-suspense" aria-label="Anchor link for: 6-new-async-component-suspense">#</a>
6. New Async Component: Suspense</h2>
<p>This is perhaps the only new feature of Vue 3 that may be changed even after official release. The inspiration is also from <a rel="noopener nofollow noreferrer" target="_blank" href="https://reactjs.org/docs/concurrent-mode-suspense.html">React Suspense</a>. So the usage scenario would be the same in my opinion.</p>
<p><img src="./images/suspense_exp.png" alt="suspense experimental feature" /></p>
<p>Do you remember how you render the asynchronous data previously in Vue 2? I think <code>v-if</code> / <code>v-else</code> should be the answer.</p>
<pre data-lang="html" style="background-color:#212733;color:#ccc9c2;" class="language-html "><code class="language-html" data-lang="html"><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">template</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">div </span><span style="color:#ffd580;">v-for</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"i in items" </span><span style="color:#ffd580;">:key</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"i"</span><span style="color:#5ccfe690;">></span><span>{{ i }}</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">div </span><span style="color:#ffd580;">v-else</span><span style="color:#5ccfe690;">></span><span>loading...</span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">template</span><span style="color:#5ccfe690;">>
</span><span>
</span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">script</span><span style="color:#5ccfe690;">>
</span><span style="color:#ffa759;">export default </span><span>{
</span><span> </span><span style="color:#ffd580;">data </span><span>() {
</span><span> </span><span style="color:#ffa759;">return </span><span>{
</span><span> items</span><span style="color:#ccc9c2cc;">: </span><span style="color:#ffcc66;">null
</span><span> }
</span><span> }</span><span style="color:#ccc9c2cc;">,
</span><span> </span><span style="color:#ffd580;">mounted </span><span>() {
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span>items </span><span style="color:#f29e74;">= </span><span style="color:#ffa759;">await new </span><span style="font-style:italic;color:#5ccfe6;">Promise</span><span>(</span><span style="color:#ffcc66;">resolve </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="color:#f28779;">setTimeout</span><span>(() </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="color:#ffa759;">return </span><span style="color:#ffd580;">resolve</span><span>([</span><span style="color:#bae67e;">'one'</span><span style="color:#ccc9c2cc;">, </span><span style="color:#bae67e;">'two'</span><span>])
</span><span> }</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">3000</span><span>)
</span><span> })
</span><span> }
</span><span>}
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">script</span><span style="color:#5ccfe690;">>
</span></code></pre>
<p>With <code>Suspense</code> component, you can do it without handling conditions yourself. By setting up the <code>default</code> and <code>fallback</code> slot, the <code>Suspense</code> component will handle the async event automatically.</p>
<pre data-lang="html" style="background-color:#212733;color:#ccc9c2;" class="language-html "><code class="language-html" data-lang="html"><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">template</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">suspense</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">template </span><span style="color:#ffd580;">#default</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">div </span><span style="color:#ffd580;">v-for</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"i in items" </span><span style="color:#ffd580;">:key</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"i"</span><span style="color:#5ccfe690;">></span><span>{{ i }}</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">template</span><span style="color:#5ccfe690;">>
</span><span>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">template </span><span style="color:#ffd580;">#fallback</span><span style="color:#5ccfe690;">>
</span><span> Loading...
</span><span> </span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">template</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">suspense</span><span style="color:#5ccfe690;">>
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">template</span><span style="color:#5ccfe690;">>
</span><span>
</span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">script</span><span style="color:#5ccfe690;">>
</span><span style="color:#ffa759;">export default </span><span>{
</span><span> </span><span style="color:#ffa759;">async </span><span style="color:#ffd580;">setup </span><span>() {
</span><span> </span><span style="color:#ffa759;">const </span><span>items </span><span style="color:#f29e74;">= </span><span style="color:#ffa759;">await new </span><span style="font-style:italic;color:#5ccfe6;">Promise</span><span>(</span><span style="color:#ffcc66;">resolve </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="color:#f28779;">setTimeout</span><span>(() </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="color:#ffa759;">return </span><span style="color:#ffd580;">resolve</span><span>([</span><span style="color:#bae67e;">'one'</span><span style="color:#ccc9c2cc;">, </span><span style="color:#bae67e;">'two'</span><span>])
</span><span> }</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">3000</span><span>)
</span><span> })
</span><span>
</span><span> </span><span style="color:#ffa759;">return </span><span>{
</span><span> items
</span><span> }
</span><span> }
</span><span>}
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">script</span><span style="color:#5ccfe690;">>
</span></code></pre>
<h2 id="7-display-it-elsewhere-teleport"><a class="zola-anchor" href="#7-display-it-elsewhere-teleport" aria-label="Anchor link for: 7-display-it-elsewhere-teleport">#</a>
7. Display it elsewhere: Teleport</h2>
<p>It is another cool stuff based on <a rel="noopener nofollow noreferrer" target="_blank" href="https://reactjs.org/docs/portals.html">React Portals</a>. It provides the ability to insert the component to a target DOM Node.</p>
<p>What we do in Vue 2 to insert a custom component in <code><body></code> (Of course there is a Vue 3rd party plugin <a rel="noopener nofollow noreferrer" target="_blank" href="https://portal-vue.linusb.org">PortalVue</a> providing such functionality):</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#ffa759;">import </span><span>Vue </span><span style="color:#ffa759;">from </span><span style="color:#bae67e;">'vue'
</span><span>
</span><span style="color:#ffa759;">const </span><span>Ctor </span><span style="color:#f29e74;">= </span><span>Vue</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">extends</span><span>({
</span><span> template</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">`<div>hello world</div>`
</span><span>})
</span><span>
</span><span style="color:#ffa759;">const </span><span>vm </span><span style="color:#f29e74;">= new </span><span style="color:#73d0ff;">Ctor</span><span>({ </span><span style="color:#f29e74;">... </span><span>})</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">$mount</span><span>()
</span><span>
</span><span>document</span><span style="color:#f29e74;">.</span><span>body</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">appendChild</span><span>(vm</span><span style="color:#f29e74;">.</span><span>$el)
</span><span>
</span></code></pre>
<p>To use such feature in Vue 3, wrap your target component within <code><Teleport></code> and define the destination Node (querySelector) in <code>to</code> property.</p>
<pre data-lang="html" style="background-color:#212733;color:#ccc9c2;" class="language-html "><code class="language-html" data-lang="html"><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">template</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">Teleport </span><span style="color:#ffd580;">to</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"body"</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">></span><span>Pitayan</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">Teleport</span><span style="color:#5ccfe690;">>
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">template</span><span style="color:#5ccfe690;">>
</span></code></pre>
<h2 id="8-allow-multiple-v-model"><a class="zola-anchor" href="#8-allow-multiple-v-model" aria-label="Anchor link for: 8-allow-multiple-v-model">#</a>
8. Allow Multiple v-model</h2>
<p><code>v-model</code> is used for data two-way bindings in form elements or even custom components. In Vue 2, a custom component can only have one <code>v-model</code> in the tag.</p>
<pre data-lang="html" style="background-color:#212733;color:#ccc9c2;" class="language-html "><code class="language-html" data-lang="html"><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">template</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">my-input-form </span><span style="color:#ffd580;">v-model</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"input" </span><span style="color:#5ccfe690;">/>
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">template</span><span style="color:#5ccfe690;">>
</span></code></pre>
<p>Vue 3 removed the limitation and allows you to have multiple <code>v-model</code> so that you could specify the bindings separately for more input elements.</p>
<pre data-lang="html" style="background-color:#212733;color:#ccc9c2;" class="language-html "><code class="language-html" data-lang="html"><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">template</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">my-input-form
</span><span> </span><span style="color:#ffd580;">v-model:first</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"inputFirst"
</span><span> </span><span style="color:#ffd580;">v-model:second</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"inputSecond"
</span><span> </span><span style="color:#5ccfe690;">/>
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">template</span><span style="color:#5ccfe690;">>
</span></code></pre>
<h2 id="9-global-apis"><a class="zola-anchor" href="#9-global-apis" aria-label="Anchor link for: 9-global-apis">#</a>
9. Global APIs</h2>
<p>Vue 3 offers some new APIs to help us control the components and instances better.</p>
<h3 id="createapp"><a class="zola-anchor" href="#createapp" aria-label="Anchor link for: createapp">#</a>
createApp</h3>
<p>In Vue 2, <code>Vue</code> can be used as constructor to return an instance Object. In Vue 3, you could use <code>createApp</code> function instead. The behavior is actually the same.</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="font-style:italic;color:#5c6773;">// Vue 2
</span><span style="color:#ffa759;">import </span><span>Vue </span><span style="color:#ffa759;">from </span><span style="color:#bae67e;">'vue'
</span><span style="color:#ffa759;">import </span><span>App </span><span style="color:#ffa759;">from </span><span style="color:#bae67e;">'@/src/App'
</span><span>
</span><span style="color:#f29e74;">new </span><span style="color:#73d0ff;">Vue</span><span>({
</span><span> el</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">'#app'</span><span style="color:#ccc9c2cc;">,
</span><span> components</span><span style="color:#ccc9c2cc;">: </span><span>{
</span><span> App
</span><span> }
</span><span>})
</span></code></pre>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="font-style:italic;color:#5c6773;">// Vue 3
</span><span style="color:#ffa759;">import </span><span>{ createApp } </span><span style="color:#ffa759;">from </span><span style="color:#bae67e;">'vue'
</span><span style="color:#ffa759;">import </span><span>App </span><span style="color:#ffa759;">from </span><span style="color:#bae67e;">'@/src/App'
</span><span>
</span><span style="color:#ffa759;">const </span><span>app </span><span style="color:#f29e74;">= </span><span style="color:#ffd580;">createApp</span><span>(App)
</span></code></pre>
<p>What about those global methods like <code>extend</code> <code>component</code>&nbsp<code>mixin</code> and <code>directive</code>?</p>
<p>Same, but you need to use the instance method instead.</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="font-style:italic;color:#5c6773;">// Global methods
</span><span>app</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">extend</span><span>()
</span><span>app</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">component</span><span>()
</span><span>app</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">mixin</span><span>()
</span><span>app</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">directive</span><span>()
</span></code></pre>
<h3 id="nexttick"><a class="zola-anchor" href="#nexttick" aria-label="Anchor link for: nexttick">#</a>
nextTick</h3>
<p>I think <code>nextTick</code> is a frequently used API since a lot of the logics are actually asynchronous and they need to be arraged to the next DOM update cycle.</p>
<p>In Vue 2, <code>nextTick</code> is an instance method.</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#ffa759;">export default </span><span>{
</span><span> </span><span style="color:#f29e74;">...</span><span style="color:#ccc9c2cc;">,
</span><span> </span><span style="color:#ffd580;">mounted </span><span>() {
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">$nextTick</span><span>(() </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(</span><span style="color:#bae67e;">'pitayan'</span><span>)
</span><span> })
</span><span> }
</span><span>}
</span></code></pre>
<p>Vue 3 allows to you use <code>nextTick</code> as an independent function.</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="font-style:italic;color:#5c6773;">// nextTick function type
</span><span style="color:#ffa759;">export declare function </span><span style="color:#ffd580;">nextTick</span><span>(</span><span style="color:#ffd580;">fn</span><span style="color:#f29e74;">?: </span><span>() </span><span style="color:#ffa759;">=> </span><span style="font-style:italic;color:#5ccfe6;">void</span><span>)</span><span style="color:#f29e74;">: </span><span style="color:#73d0ff;">Promise</span><span><</span><span style="font-style:italic;color:#5ccfe6;">void</span><span>></span><span style="color:#ccc9c2cc;">;
</span></code></pre>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="font-style:italic;color:#5c6773;">// An official doc Example
</span><span style="color:#ffa759;">import </span><span>{ nextTick } </span><span style="color:#ffa759;">from </span><span style="color:#bae67e;">'vue'
</span><span>
</span><span style="color:#ffa759;">export default </span><span>{
</span><span> </span><span style="color:#ffd580;">setup </span><span>() {
</span><span> </span><span style="color:#ffa759;">const </span><span>message </span><span style="color:#f29e74;">= </span><span style="color:#ffd580;">ref</span><span>(</span><span style="color:#bae67e;">'Hello, Pitayan!'</span><span>)
</span><span>
</span><span> </span><span style="color:#ffa759;">const </span><span style="color:#ffd580;">changeMessage </span><span style="color:#f29e74;">= </span><span style="color:#ffa759;">async </span><span style="color:#ffcc66;">newMessage </span><span style="color:#ffa759;">=> </span><span>{
</span><span> message</span><span style="color:#f29e74;">.</span><span>value </span><span style="color:#f29e74;">= </span><span>newMessage
</span><span> </span><span style="color:#ffa759;">await </span><span style="color:#ffd580;">nextTick</span><span>()
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(</span><span style="color:#bae67e;">'Now DOM is updated'</span><span>)
</span><span> }
</span><span> }
</span><span>}
</span></code></pre>
<h3 id="other-helper-functions"><a class="zola-anchor" href="#other-helper-functions" aria-label="Anchor link for: other-helper-functions">#</a>
Other Helper Functions</h3>
<p>These new APIs will be extremely helpful when you need extra controls for much more abstracted scenarios. I personally think that they can be frequently used in the 3rd party libraries.</p>
<ul>
<li><strong>h:</strong> return virtual node</li>
<li><strong>createRenderer:</strong> custom renderer that can be used for cross-environment purposes</li>
<li><strong>defineComponent:</strong> type the Object passed in</li>
<li><strong>defineAsyncComponent:</strong> load async component when necessary</li>
<li><strong>resolveComponent:</strong> resolve a component within the current instance scope</li>
<li><strong>resolveDynamicComponent:</strong> resolve a dynamic component within the current instance scope</li>
<li><strong>resolveDirective:</strong> get a <code>directive</code> from the current instance scope</li>
<li><strong>withDirectives:</strong> applies <code>directive</code> to a <code>VNode</code></li>
</ul>
<h2 id="conclusions"><a class="zola-anchor" href="#conclusions" aria-label="Anchor link for: conclusions">#</a>
Conclusions</h2>
<p>I’m very happy and honored to vitness the growth of Vue.js 2.x => 3.x. Vue team concludes what was not possible natively in Vue 2 and made them possible in Vue 3. As I could see that there are many familar stuffs from Vue 3’s code base.</p>
<p>It’s not hard to tell that Vue 3 is a much more solid framework. It provides a new and simpler way to organize your source code, meanwhile smaller and faster. And under the help of <code>Typescript</code> and their new features for instance composition API, projects’ structure can become very much different than before. Which I believe is a positive impact to the front end community.</p>
<p>That’s all for the Vue 3’s new feature.</p>
<p>If you think this article is great. Please share it to the social networks. Thanks for reading.</p>
<h2 id="references"><a class="zola-anchor" href="#references" aria-label="Anchor link for: references">#</a>
References</h2>
<ul>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://v3.vuejs.org/">https://v3.vuejs.org</a></li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://vuejs.org/v2/">https://vuejs.org/v2/</a></li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://composition-api.vuejs.org/">https://composition-api.vuejs.org/</a></li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://reactjs.org/docs/">https://reactjs.org/docs/</a></li>
</ul>
Learn Functional Progamming Design from Redux2020-08-04T00:00:00+00:002020-08-04T00:00:00+00:00https://pitayan.com/posts/redux-fp-design/<p>Before I set my eyes on the Redux source code, I naively thought OOP is superior than FP(Functional Programming) as a programming paradigm. But this is not right. As we know that FP is dedicated to forming a easy to understand and clear workflow without those obscure abstracted objects and relations. It’s much closer to human’s procedural mode of thinking.</p>
<p>Now <code>React</code> has already got hooks which can handle the “states” properly event without <a rel="noopener nofollow noreferrer" target="_blank" href="https://redux.js.org">Redux</a>. The demand for <a rel="noopener nofollow noreferrer" target="_blank" href="https://redux.js.org">Redux</a> could be declining but its code base is still worth learning. Especially for those who wants to enlighten themselves in functional programming. So, I guess it’s never a bad idea to learn from a good example even though it is “obsolete” (not at all).</p>
<p>When I started reading the <a rel="noopener nofollow noreferrer" target="_blank" href="https://redux.js.org">Redux</a> source code, I immediately felt the power of this unfamiliar usage of my familiar programming language. It feels like exploring an acient cave with a torch lighting up the paintings and found the great secret.</p>
<p>In order to know more about what Redux benefits from FP, I researched the <a rel="noopener nofollow noreferrer" target="_blank" href="https://redux.js.org">Redux</a> source code and created a mini version of it.</p>
<blockquote>
<p>Never be afraid of reinventing the wheel.</p>
</blockquote>
<h2 id="recap-how-redux-works"><a class="zola-anchor" href="#recap-how-redux-works" aria-label="Anchor link for: recap-how-redux-works">#</a>
Recap How Redux Works</h2>
<p>There are 4 basic key points for <a rel="noopener nofollow noreferrer" target="_blank" href="https://redux.js.org">Redux</a>:</p>
<ol>
<li>Create a store for data and let the view subscribe to it</li>
<li>The view dispatches an action to submit the changs</li>
<li>The reducer changes the state based on the action type</li>
<li>Finally return the new state and triggers the view to change</li>
</ol>
<p>This is the classic diagram explaining how <a rel="noopener nofollow noreferrer" target="_blank" href="https://redux.js.org">Redux</a> works:</p>
<p><img src="./images/Redux.jpg" alt="redux diagram" /></p>
<p>From the diagram above, it’s easy to find the keywords: <code>action</code> <code>store</code> <code>reducer</code> <code>view</code> <code>subscribe</code> and <code>dispatch</code>. And the next is to handle the relations among these keywords.</p>
<h2 id="redux-approach-comparison-fp-vs-oop"><a class="zola-anchor" href="#redux-approach-comparison-fp-vs-oop" aria-label="Anchor link for: redux-approach-comparison-fp-vs-oop">#</a>
Redux Approach Comparison: FP vs OOP</h2>
<p>Example usage of <a rel="noopener nofollow noreferrer" target="_blank" href="https://redux.js.org">Redux</a></p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#ffa759;">const </span><span>store </span><span style="color:#f29e74;">= </span><span style="color:#ffd580;">createStore</span><span>(
</span><span> </span><span style="color:#ffd580;">combineReducers</span><span>({
</span><span> one</span><span style="color:#ccc9c2cc;">: </span><span>oneReducer</span><span style="color:#ccc9c2cc;">,
</span><span> two</span><span style="color:#ccc9c2cc;">: </span><span>twoReducer
</span><span> })</span><span style="color:#ccc9c2cc;">,
</span><span> </span><span style="color:#ffd580;">applyMiddleware</span><span>(ReduxThunk</span><span style="color:#ccc9c2cc;">, </span><span>ReduxLogger)
</span><span>)</span><span style="color:#ccc9c2cc;">;
</span></code></pre>
<p>Imagine if we do this in OOP, it may look like this:</p>
<p>(The following is just my imagination. Not how older <a rel="noopener nofollow noreferrer" target="_blank" href="https://redux.js.org">Redux</a> behaves)</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#ffa759;">const </span><span>store </span><span style="color:#f29e74;">= new </span><span style="color:#73d0ff;">Store</span><span>()
</span><span>store</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">setReducers</span><span>({
</span><span> one</span><span style="color:#ccc9c2cc;">: </span><span>oneReducer</span><span style="color:#ccc9c2cc;">,
</span><span> two</span><span style="color:#ccc9c2cc;">: </span><span>twoReducer
</span><span>})
</span><span>store</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">setMiddlewares</span><span>({
</span><span> ReduxThunk</span><span style="color:#ccc9c2cc;">,
</span><span> ReduxLogger
</span><span>})
</span></code></pre>
<p>So, what are the differences? Both are good approaches IMO.</p>
<p>FP does a good job on combining the functions together without side-effects. The return value is consistent which made the program returnings foreseeable during or after the execution.</p>
<p>OOP made a solid structure which defined all the attributes a data model should contain. It makes it easy to modify or configure the data model.</p>
<p>In <a rel="noopener nofollow noreferrer" target="_blank" href="https://redux.js.org">Redux</a>, the <code>reduers</code> and <code>middlewares</code> are usually defined only once. It means, we don’t need the ability to update these properties and we don’t hope them to be altered during the runtime. As for FP approach, it utilizes the <code>closure</code> technique that kills the possbility of exposing the internal properties. With some fantastic FP techniques (curry, compose, pipe), it’s even making the program much more human-readable than OOP.</p>
<p>I would say FP should be the best fit for such scenario. Of course, the FP I’m talking about here is far from the real functional programming like Haskell. But at least the idea of utilizing FP techniques in Javascript is something to follow.</p>
<p><img src="./images/haskell.jpg" alt="haskell functional programming" /></p>
<h2 id="wonderful-redux-fp-design"><a class="zola-anchor" href="#wonderful-redux-fp-design" aria-label="Anchor link for: wonderful-redux-fp-design">#</a>
Wonderful Redux FP Design</h2>
<p>In Redux, there is no class at all (In the earlier versions, it was once based on <code>Class</code>). All of its core APIs return either value or function (function factory). And this is exactly what FP expects a function to behave:</p>
<blockquote>
<p>Pure with no side effects.</p>
</blockquote>
<ul>
<li><strong>createStore</strong>: returns new <code>Object</code> { getState, dispatch, subscribe }</li>
<li><strong>combineReducers</strong>: returns new <code>Function</code></li>
<li><strong>applyMiddleware</strong>: returns new <code>Function</code></li>
</ul>
<p>To explain the <a rel="noopener nofollow noreferrer" target="_blank" href="https://redux.js.org">Redux</a> design in an easy way, I implemented only the very core part of the APIs above. Since the latest version’s core concept hasn’t changed much, I wrote the source code based on very primitive version of <a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/reduxjs/redux/tree/v1.0.1/src">Redux v1.0.1</a>. Because I believe the very first related version would be the most comprehensive one to look at.</p>
<p>Let’s have a look.</p>
<h4 id="createstore"><a class="zola-anchor" href="#createstore" aria-label="Anchor link for: createstore">#</a>
createStore</h4>
<p><code>createStore</code> defines those APIs that can be used within components. It’s more like <code>setter</code> and <code>getter</code></p>
<ul>
<li>getState</li>
<li>dispatch</li>
<li>subscribe</li>
</ul>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#ffa759;">export default function </span><span style="color:#ffd580;">createStore </span><span>(</span><span style="color:#ffcc66;">reducer</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">enhancer</span><span>) {
</span><span> </span><span style="color:#ffa759;">if </span><span>(enhancer) {
</span><span> </span><span style="color:#ffa759;">return </span><span style="color:#ffd580;">enhancer</span><span>(createStore)(reducer)</span><span style="color:#ccc9c2cc;">;
</span><span> }
</span><span>
</span><span> </span><span style="color:#ffa759;">let </span><span>currentState</span><span style="color:#ccc9c2cc;">;
</span><span> </span><span style="font-style:italic;color:#5c6773;">// Redux now uses a shallow copy `nextListeners` via `ensureCanMutateNextListeners()`
</span><span> </span><span style="font-style:italic;color:#5c6773;">// to prevent bugs in the middle of `dispatch`
</span><span> </span><span style="color:#ffa759;">let </span><span>currentListeners </span><span style="color:#f29e74;">= </span><span>[]</span><span style="color:#ccc9c2cc;">;
</span><span>
</span><span> </span><span style="color:#ffa759;">function </span><span style="color:#ffd580;">getState </span><span>() {
</span><span> </span><span style="color:#ffa759;">return </span><span>currentState</span><span style="color:#ccc9c2cc;">;
</span><span> }
</span><span>
</span><span> </span><span style="font-style:italic;color:#5c6773;">// Register callbacks to execute after changes
</span><span> </span><span style="color:#ffa759;">function </span><span style="color:#ffd580;">subscribe </span><span>(</span><span style="color:#ffcc66;">listener</span><span>) {
</span><span> currentListeners</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">push</span><span>(listener)</span><span style="color:#ccc9c2cc;">;
</span><span>
</span><span> </span><span style="color:#ffa759;">return </span><span>() </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="font-style:italic;color:#5c6773;">// empty listeners
</span><span> </span><span style="color:#ffa759;">const </span><span>index </span><span style="color:#f29e74;">= </span><span>currentListeners</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">indexOf</span><span>(listener)</span><span style="color:#ccc9c2cc;">;
</span><span> currentListeners</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">splice</span><span>(index</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">1</span><span>)</span><span style="color:#ccc9c2cc;">;
</span><span> }</span><span style="color:#ccc9c2cc;">;
</span><span> }
</span><span>
</span><span> </span><span style="color:#ffa759;">function </span><span style="color:#ffd580;">dispatch </span><span>(</span><span style="color:#ffcc66;">action</span><span>) {
</span><span> currentState </span><span style="color:#f29e74;">= </span><span style="color:#ffd580;">reducer</span><span>(currentState</span><span style="color:#ccc9c2cc;">, </span><span>action)</span><span style="color:#ccc9c2cc;">;
</span><span> </span><span style="font-style:italic;color:#5c6773;">// state changes, notify to invoke callbacks
</span><span> currentListeners</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">forEach</span><span>(</span><span style="color:#ffcc66;">listener </span><span style="color:#ffa759;">=> </span><span style="color:#ffd580;">listener</span><span>())</span><span style="color:#ccc9c2cc;">;
</span><span> }
</span><span>
</span><span> </span><span style="font-style:italic;color:#5c6773;">// Initialize Redux by calling a virtual reducer
</span><span> </span><span style="color:#ffd580;">dispatch</span><span>({ type</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">"MY-MINI-REDUX" </span><span>})</span><span style="color:#ccc9c2cc;">;
</span><span>
</span><span> </span><span style="color:#ffa759;">return </span><span>{
</span><span> getState</span><span style="color:#ccc9c2cc;">,
</span><span> dispatch</span><span style="color:#ccc9c2cc;">,
</span><span> subscribe
</span><span> }</span><span style="color:#ccc9c2cc;">;
</span><span>}
</span></code></pre>
<h4 id="combinereducers"><a class="zola-anchor" href="#combinereducers" aria-label="Anchor link for: combinereducers">#</a>
combineReducers</h4>
<p>Returns a new function that can return the new state. Can’t be any purer.</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="font-style:italic;color:#5c6773;">// This is just a helper function to map through the Object
</span><span style="color:#ffa759;">function </span><span style="color:#ffd580;">mapValues</span><span>(</span><span style="color:#ffcc66;">obj</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">fn</span><span>) {
</span><span> </span><span style="color:#ffa759;">return </span><span style="font-style:italic;color:#5ccfe6;">Object</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">keys</span><span>(obj)</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">reduce</span><span>((</span><span style="color:#ffcc66;">result</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">key</span><span>) </span><span style="color:#ffa759;">=> </span><span>{
</span><span> result[key] </span><span style="color:#f29e74;">= </span><span style="color:#ffd580;">fn</span><span>(obj[key]</span><span style="color:#ccc9c2cc;">, </span><span>key)</span><span style="color:#ccc9c2cc;">;
</span><span> </span><span style="color:#ffa759;">return </span><span>result</span><span style="color:#ccc9c2cc;">;
</span><span> }</span><span style="color:#ccc9c2cc;">, </span><span>{})</span><span style="color:#ccc9c2cc;">;
</span><span>}
</span><span>
</span><span style="color:#ffa759;">export default function </span><span style="color:#ffd580;">combineReducers </span><span>(</span><span style="color:#ffcc66;">reducers</span><span>) {
</span><span> </span><span style="color:#ffa759;">return function </span><span style="color:#ffd580;">combination </span><span>(</span><span style="color:#ffcc66;">state </span><span style="color:#f29e74;">= </span><span>{}</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">action</span><span>) {
</span><span> </span><span style="font-style:italic;color:#5c6773;">// Official Redux uses `pick` on filtering reducers.
</span><span> </span><span style="font-style:italic;color:#5c6773;">// Let's trust reducers are functions here
</span><span> </span><span style="color:#ffa759;">return </span><span style="color:#ffd580;">mapValues</span><span>(reducers</span><span style="color:#ccc9c2cc;">, </span><span>(</span><span style="color:#ffcc66;">reducer</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">key</span><span>) </span><span style="color:#ffa759;">=> </span><span style="color:#ffd580;">reducer</span><span>(state[key]</span><span style="color:#ccc9c2cc;">, </span><span>action))
</span><span> }</span><span style="color:#ccc9c2cc;">;
</span><span>}
</span><span>
</span></code></pre>
<h4 id="applymiddleware"><a class="zola-anchor" href="#applymiddleware" aria-label="Anchor link for: applymiddleware">#</a>
applyMiddleware</h4>
<p>I personally think the <code>applyMiddleware</code> API is the most amazing part of Redux. It provides an optimal solution to apply 3rd party plugins.</p>
<p>The FP <code>compose</code> in the source code is corresponding to Math’s <a rel="noopener nofollow noreferrer" target="_blank" href="https://en.wikipedia.org/wiki/Associative_property">associative law</a> in my understanding.</p>
<blockquote>
<p>( <em>x</em> ∗ ( <em>y</em> ∗ <em>z</em> ) ) = <em>x</em> ∗ <em>y</em> ∗ <em>z</em></p>
</blockquote>
<p>The usage of <code>applyMiddleware</code> is actually a form of a <code>pipe</code> that allows us to inject enhancement functions that returns the store Object. It’s pretty similar to <code>Aspect Oriented Programming</code> which the most typical example is the annotation / decorator.</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="font-style:italic;color:#5c6773;">// Combine the functions
</span><span style="font-style:italic;color:#5c6773;">// a(b(c())) => compose(a, b, c)
</span><span style="color:#ffa759;">function </span><span style="color:#ffd580;">compose</span><span>(</span><span style="color:#f29e74;">...</span><span style="color:#ffcc66;">funcs</span><span>) {
</span><span> </span><span style="color:#ffa759;">return </span><span>funcs</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">reduceRight</span><span>((</span><span style="color:#ffcc66;">composed</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">f</span><span>) </span><span style="color:#ffa759;">=> </span><span style="color:#ffd580;">f</span><span>(composed))</span><span style="color:#ccc9c2cc;">;
</span><span>}
</span><span>
</span><span style="color:#ffa759;">export default function </span><span style="color:#ffd580;">applyMiddleware</span><span>(</span><span style="color:#f29e74;">...</span><span style="color:#ffcc66;">middlewares</span><span>) {
</span><span> </span><span style="color:#ffa759;">return </span><span style="color:#ffcc66;">next </span><span style="color:#ffa759;">=> </span><span>(</span><span style="color:#ffcc66;">reducer</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">initialState</span><span>) </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="color:#ffa759;">const </span><span>store </span><span style="color:#f29e74;">= </span><span style="color:#ffd580;">next</span><span>(reducer</span><span style="color:#ccc9c2cc;">, </span><span>initialState)</span><span style="color:#ccc9c2cc;">;
</span><span> </span><span style="color:#ffa759;">let </span><span>dispatch </span><span style="color:#f29e74;">= </span><span>store</span><span style="color:#f29e74;">.</span><span>dispatch</span><span style="color:#ccc9c2cc;">;
</span><span> </span><span style="color:#ffa759;">const </span><span>middlewareAPI </span><span style="color:#f29e74;">= </span><span>{
</span><span> getState</span><span style="color:#ccc9c2cc;">: </span><span>store</span><span style="color:#f29e74;">.</span><span>getState</span><span style="color:#ccc9c2cc;">,
</span><span> </span><span style="color:#ffd580;">dispatch</span><span style="color:#ccc9c2cc;">: </span><span style="color:#ffcc66;">action </span><span style="color:#ffa759;">=> </span><span style="color:#ffd580;">dispatch</span><span>(action)
</span><span> }</span><span style="color:#ccc9c2cc;">;
</span><span> </span><span style="color:#ffa759;">const </span><span>chain </span><span style="color:#f29e74;">= </span><span>middlewares</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">map</span><span>(</span><span style="color:#ffcc66;">middleware </span><span style="color:#ffa759;">=> </span><span style="color:#ffd580;">middleware</span><span>(middlewareAPI))</span><span style="color:#ccc9c2cc;">;
</span><span>
</span><span> </span><span style="font-style:italic;color:#5c6773;">// Enhance the `dispatchers` by applying middlewares to each of them
</span><span> dispatch </span><span style="color:#f29e74;">= </span><span style="color:#ffd580;">compose</span><span>(</span><span style="color:#f29e74;">...</span><span>chain</span><span style="color:#ccc9c2cc;">, </span><span>store</span><span style="color:#f29e74;">.</span><span>dispatch)</span><span style="color:#ccc9c2cc;">;
</span><span>
</span><span> </span><span style="color:#ffa759;">return </span><span>{
</span><span> </span><span style="color:#f29e74;">...</span><span>store</span><span style="color:#ccc9c2cc;">,
</span><span> dispatch
</span><span> }</span><span style="color:#ccc9c2cc;">;
</span><span> }</span><span style="color:#ccc9c2cc;">;
</span><span>}
</span><span>
</span></code></pre>
<h2 id="redux-middlewares"><a class="zola-anchor" href="#redux-middlewares" aria-label="Anchor link for: redux-middlewares">#</a>
Redux Middlewares</h2>
<p>There are some famous middlewares for <a rel="noopener nofollow noreferrer" target="_blank" href="https://redux.js.org">Redux</a> like <a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/reduxjs/redux-thunk">redux-thunk</a> and [redux-logger(https://github.com/LogRocket/redux-logger). These are the good examples using <code>applyMiddleware</code> API to enhance the functionalities. Furthermore, their code base is astonishingly small. The core part has only a few lines of code.</p>
<p>All of the middlewares are <code>curry</code> functions.</p>
<blockquote>
<p>funcA => funcB => funcC</p>
<p>funcB = funcA()</p>
<p>funcC = funcB()</p>
</blockquote>
<p>This is extremly helpful when I need other contexts to use within the code block. As of the examples, it’s easy to find that <code>next</code> and <code>action</code> are passed in as context to help handle some complex cases.</p>
<h4 id="redux-thunk"><a class="zola-anchor" href="#redux-thunk" aria-label="Anchor link for: redux-thunk">#</a>
Redux Thunk</h4>
<p><code>redux-thunk</code> allows to use function as <code>dispatch</code> parameter so that I could do something right before “dispatching”.</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="font-style:italic;color:#5c6773;">// without redux-thunk
</span><span style="color:#ffd580;">dispatch</span><span>({ type</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">'action'</span><span style="color:#ccc9c2cc;">, </span><span>payload</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">'value' </span><span>})
</span><span>
</span><span style="font-style:italic;color:#5c6773;">// with redux-thunk
</span><span style="font-style:italic;color:#5c6773;">// the dispatch is wrapped up by a new function
</span><span style="color:#ffd580;">dispatch</span><span>(</span><span style="color:#ffa759;">function </span><span>(</span><span style="color:#ffcc66;">dispatch</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">getState</span><span>) {
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(</span><span style="color:#bae67e;">'redux-thunk'</span><span>)
</span><span> </span><span style="color:#ffd580;">dispatch</span><span>({ type</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">'action'</span><span style="color:#ccc9c2cc;">, </span><span>payload</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">'value' </span><span>})
</span><span>})
</span></code></pre>
<p>Here is the core:</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="font-style:italic;color:#5c6773;">// Allow passing function to dispatch
</span><span style="color:#ffa759;">export default function </span><span style="color:#ffd580;">thunk</span><span>({ </span><span style="color:#ffcc66;">dispatch</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">getState </span><span>}) {
</span><span> </span><span style="color:#ffa759;">return </span><span style="color:#ffcc66;">next </span><span style="color:#ffa759;">=> </span><span style="color:#ffcc66;">action </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="color:#ffa759;">if </span><span>(</span><span style="color:#f29e74;">typeof </span><span>action </span><span style="color:#f29e74;">=== </span><span style="color:#bae67e;">"function"</span><span>) {
</span><span> </span><span style="color:#ffa759;">return </span><span style="color:#ffd580;">action</span><span>(dispatch</span><span style="color:#ccc9c2cc;">, </span><span>getState)</span><span style="color:#ccc9c2cc;">;
</span><span> }
</span><span>
</span><span> </span><span style="color:#ffa759;">return </span><span style="color:#ffd580;">next</span><span>(action)</span><span style="color:#ccc9c2cc;">;
</span><span> }</span><span style="color:#ccc9c2cc;">;
</span><span>}
</span></code></pre>
<h4 id="redux-logger"><a class="zola-anchor" href="#redux-logger" aria-label="Anchor link for: redux-logger">#</a>
Redux Logger</h4>
<p>It’s easy to guess what this middleware does. It simply outputs the state changes.</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="font-style:italic;color:#5c6773;">// Output the previous and current state in console
</span><span style="color:#ffa759;">export default function </span><span style="color:#ffd580;">logger</span><span>({ </span><span style="color:#ffcc66;">getState </span><span>}) {
</span><span> </span><span style="color:#ffa759;">return </span><span style="color:#ffcc66;">next </span><span style="color:#ffa759;">=> </span><span style="color:#ffcc66;">action </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(</span><span style="color:#bae67e;">"======== Redux Logger ========"</span><span>)</span><span style="color:#ccc9c2cc;">;
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(</span><span style="color:#bae67e;">"Action Type: "</span><span style="color:#ccc9c2cc;">, </span><span>action</span><span style="color:#f29e74;">.</span><span>type)</span><span style="color:#ccc9c2cc;">;
</span><span> </span><span style="color:#ffa759;">const </span><span>prevState </span><span style="color:#f29e74;">= </span><span style="color:#ffd580;">getState</span><span>()</span><span style="color:#ccc9c2cc;">;
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(</span><span style="color:#bae67e;">"Prev: "</span><span style="color:#ccc9c2cc;">, </span><span>prevState)</span><span style="color:#ccc9c2cc;">;
</span><span>
</span><span> </span><span style="color:#ffa759;">const </span><span>returnValue </span><span style="color:#f29e74;">= </span><span style="color:#ffd580;">next</span><span>(action)</span><span style="color:#ccc9c2cc;">;
</span><span>
</span><span> </span><span style="color:#ffa759;">const </span><span>nextState </span><span style="color:#f29e74;">= </span><span style="color:#ffd580;">getState</span><span>()</span><span style="color:#ccc9c2cc;">;
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(</span><span style="color:#bae67e;">"Next: "</span><span style="color:#ccc9c2cc;">, </span><span>nextState)</span><span style="color:#ccc9c2cc;">;
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(</span><span style="color:#bae67e;">"=============================="</span><span>)</span><span style="color:#ccc9c2cc;">;
</span><span> </span><span style="color:#ffa759;">return </span><span>returnValue</span><span style="color:#ccc9c2cc;">;
</span><span> }</span><span style="color:#ccc9c2cc;">;
</span><span>}
</span></code></pre>
<h2 id="a-demo-app"><a class="zola-anchor" href="#a-demo-app" aria-label="Anchor link for: a-demo-app">#</a>
A demo app</h2>
<p>I implemented mini version of redux and a small counter application to demostrate the functions. The application will do four arithmetic operations: <strong>plus</strong>, <strong>minus</strong>, <strong>multiply</strong> and <strong>divide</strong>. The number will change after clicking the operation button. Meanwhile, <code>multiply</code> and <code>divide</code> will have 300ms’ delay which is enabled by a custom middleware (a mini redux-thunk).</p>
<p><strong>Repository link of “mini-redux”:</strong></p>
<p>https://github.com/daiyanze/mini-redux</p>
<p><strong>Demo App link:</strong></p>
<p>https://daiyanze.com/mini-redux/build/index.html</p>
<p><img src="./images/app.png" alt="app" /></p>
<p>The app has one child component: <code>MiniReduxComp</code>. In my mini-redux, I didn’t create a context provider to trigger updates. Instead, I subscribe to the store changes within the component and do <code>forceUpdate</code> to react to changes.</p>
<p>I also applied the custom middlewares <code>redux-thunk</code> and <code>redux-logger</code> to enrich the functions.</p>
<pre data-lang="tsx" style="background-color:#212733;color:#ccc9c2;" class="language-tsx "><code class="language-tsx" data-lang="tsx"><span style="color:#ffa759;">import </span><span>React</span><span style="color:#ccc9c2cc;">, </span><span>{ Component } </span><span style="color:#ffa759;">from </span><span style="color:#bae67e;">'react'</span><span style="color:#ccc9c2cc;">;
</span><span style="color:#ffa759;">import </span><span>store </span><span style="color:#ffa759;">from </span><span style="color:#bae67e;">'../store'
</span><span>
</span><span style="color:#ffa759;">export default class </span><span style="color:#73d0ff;">MiniReduxComp </span><span style="color:#ffa759;">extends </span><span style="text-decoration:underline;color:#73d0ff;">Component </span><span>{
</span><span>
</span><span> </span><span style="color:#ffd580;">componentDidMount</span><span>() {
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span>unsubscribe </span><span style="color:#f29e74;">= </span><span>store</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">subscribe</span><span>(() </span><span style="color:#ffa759;">=> </span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">forceUpdate</span><span>())</span><span style="color:#ccc9c2cc;">;
</span><span> }
</span><span>
</span><span> </span><span style="color:#ffd580;">componentWillUnmount</span><span>() {
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span>unsubscribe </span><span style="color:#f29e74;">&& </span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">unsubscribe</span><span>()</span><span style="color:#ccc9c2cc;">;
</span><span> }
</span><span>
</span><span> </span><span style="color:#ffd580;">plus </span><span style="color:#f29e74;">= </span><span>() </span><span style="color:#ffa759;">=> </span><span>store</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">dispatch</span><span>({ type</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">"PLUS" </span><span>})
</span><span>
</span><span> </span><span style="color:#ffd580;">minus </span><span style="color:#f29e74;">= </span><span>() </span><span style="color:#ffa759;">=> </span><span>store</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">dispatch</span><span>({ type</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">'MINUS' </span><span>})
</span><span>
</span><span> </span><span style="color:#ffd580;">multiply </span><span style="color:#f29e74;">= </span><span>() </span><span style="color:#ffa759;">=> </span><span>store</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">dispatch</span><span>((</span><span style="color:#ffcc66;">dispatch</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">getState</span><span>) </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="color:#f28779;">setTimeout</span><span>(() </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="color:#ffd580;">dispatch</span><span>({ type</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">'MULTIPLY' </span><span>})
</span><span> }</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">300</span><span>)
</span><span> })
</span><span>
</span><span> </span><span style="color:#ffd580;">divide </span><span style="color:#f29e74;">= </span><span>() </span><span style="color:#ffa759;">=> </span><span>store</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">dispatch</span><span>((</span><span style="color:#ffcc66;">dispatch</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">getState</span><span>) </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="color:#f28779;">setTimeout</span><span>(() </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="color:#ffd580;">dispatch</span><span>({ type</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">'DIVIDE' </span><span>})
</span><span> }</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">300</span><span>)
</span><span> })
</span><span>
</span><span> </span><span style="color:#ffd580;">render</span><span>() {
</span><span> </span><span style="color:#ffa759;">return </span><span>(
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">h4</span><span style="color:#5ccfe690;">></span><span>Plus / Minus 1</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">h4</span><span style="color:#5ccfe690;">>
</span><span>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">p</span><span style="color:#5ccfe690;">></span><span>{store</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">getState</span><span>()</span><span style="color:#f29e74;">.</span><span>count}</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">p</span><span style="color:#5ccfe690;">>
</span><span>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">button </span><span style="color:#ffd580;">onClick</span><span style="color:#f29e74;">=</span><span>{</span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span>plus}</span><span style="color:#5ccfe690;">></span><span>+1</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">button</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">button </span><span style="color:#ffd580;">onClick</span><span style="color:#f29e74;">=</span><span>{</span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span>minus}</span><span style="color:#5ccfe690;">></span><span>-1</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">button</span><span style="color:#5ccfe690;">>
</span><span>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">br </span><span style="color:#5ccfe690;">/>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">br </span><span style="color:#5ccfe690;">/>
</span><span>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">h4</span><span style="color:#5ccfe690;">></span><span>Multiply / Divide 2 (0.3s delay)</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">h4</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">p</span><span style="color:#5ccfe690;">></span><span>{store</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">getState</span><span>()</span><span style="color:#f29e74;">.</span><span>double}</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">p</span><span style="color:#5ccfe690;">>
</span><span>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">button </span><span style="color:#ffd580;">onClick</span><span style="color:#f29e74;">=</span><span>{</span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span>multiply}</span><span style="color:#5ccfe690;">></span><span>x2</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">button</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">button </span><span style="color:#ffd580;">onClick</span><span style="color:#f29e74;">=</span><span>{</span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span>divide}</span><span style="color:#5ccfe690;">></span><span>/2</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">button</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span><span> )</span><span style="color:#ccc9c2cc;">;
</span><span> }
</span><span>}
</span></code></pre>
<h2 id="conclusion"><a class="zola-anchor" href="#conclusion" aria-label="Anchor link for: conclusion">#</a>
Conclusion</h2>
<p>I think in modern web development, OOP is still the mainstream. Yet we could see that there are some open source projects mix the programming paradigms and deliver very qualified frameworks (e.g. <a rel="noopener nofollow noreferrer" target="_blank" href="https://nestjs.com">nest.js</a>). Thanks to <code>React</code> communities, FP is part of development necessities now.</p>
<p>Okay, that’s all for the Redux drill-down. Hope you also get a good understanding about the FP designs in <a rel="noopener nofollow noreferrer" target="_blank" href="https://redux.js.org">Redux</a>. If you think this article is great, please share it on social networks.</p>
<p>Thank you reading!</p>
<h2 id="references"><a class="zola-anchor" href="#references" aria-label="Anchor link for: references">#</a>
References</h2>
<ul>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://redux.js.org">https://redux.js.org</a></li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/reduxjs/redu">https://github.com/reduxjs/redux</a></li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://en.wikipedia.org/wiki/Distributive_property">https://en.wikipedia.org/wiki/Distributive_property</a></li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://en.wikipedia.org/wiki/Associative_property">https://en.wikipedia.org/wiki/Associative_property</a></li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://medium.com/javascript-scene/10-tips-for-better-redux-architecture-69250425af44">https://medium.com/javascript-scene/10-tips-for-better-redux-architecture-69250425af44</a></li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://code-cartoons.com/a-cartoon-intro-to-redux-3afb775501a6?source=search_post---------3">https://code-cartoons.com/a-cartoon-intro-to-redux-3afb775501a6?source=search_post———3</a></li>
</ul>
JSON.stringify accepts 2 other parameters2020-07-30T00:00:00+00:002020-07-30T00:00:00+00:00https://pitayan.com/posts/json-stringify-params/<p>Do you know that <code>JSON.stringify</code> can actually take 2 other parameters? I didn’t know this before I laid my eyes on my colleage’s pull request.(I’m such a noob) But it’s nothing difficult. Those 2 parameters will help optimize the result in a good format.</p>
<p>In my opinion, the usage of <code>JSON.stringify</code> parameters is never a rare case. Well, let’s take a look at those “I-didn’t-know” features that “educated” me.</p>
<h2 id="1-replacer-filtering-your-properties"><a class="zola-anchor" href="#1-replacer-filtering-your-properties" aria-label="Anchor link for: 1-replacer-filtering-your-properties">#</a>
1. Replacer: Filtering your properties</h2>
<p>This parameter is of course <code>optional</code> by default. By assigning <code>Array</code> of <code>Number</code> or <code>String</code>, the output JSON will return the stringified properties given in the <code>Array</code>.</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#ffa759;">const </span><span>obj </span><span style="color:#f29e74;">= </span><span>{
</span><span> </span><span style="color:#bae67e;">"site"</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">"Pitayan"</span><span style="color:#ccc9c2cc;">,
</span><span> </span><span style="color:#bae67e;">"url"</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">"https://pitayan.com"</span><span style="color:#ccc9c2cc;">,
</span><span> </span><span style="color:#ffcc66;">100</span><span style="color:#ccc9c2cc;">: </span><span style="color:#ffcc66;">100
</span><span>}
</span><span>
</span><span style="font-style:italic;color:#f29e74;">JSON</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">stringify</span><span>(obj</span><span style="color:#ccc9c2cc;">, </span><span>[</span><span style="color:#bae67e;">'site'</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">100</span><span>])
</span><span style="font-style:italic;color:#5c6773;">// "{\"site\":\"Pitayan\",\"100\":100}"
</span></code></pre>
<p>This is extremely helpful when I only some of the properties inside the <code>Object</code>.</p>
<p>But it comes to negations, the 2nd parameter will not provide any help. Well, I suppose this is how this API is designed initially. In such case, it’s better to handle the <code>Object</code> properties before <code>JSON.stringify</code>.</p>
<h2 id="2-space-formatting-the-string-json"><a class="zola-anchor" href="#2-space-formatting-the-string-json" aria-label="Anchor link for: 2-space-formatting-the-string-json">#</a>
2. Space: Formatting the string JSON</h2>
<p><code>JSON.stringify</code> offers another useful parameter which allows use to format the string output with whitespaces.</p>
<p>Frankly speaking, I don’t quite need it, since I could print out the Object in the browser console directly.
But it is truely helpful when the JSON data is big enough, and I’m printing out data in the terminal.</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="font-style:italic;color:#5c6773;">// Without formatter
</span><span style="font-style:italic;color:#f29e74;">JSON</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">stringify</span><span>(obj)
</span><span style="font-style:italic;color:#5c6773;">// "{\"site\":\"Pitayan\","url\":\"https://pitayan.com\",\"100\":100}"
</span></code></pre>
<p>This looks prettier, isn’t it?</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="font-style:italic;color:#5c6773;">// With formatter
</span><span style="font-style:italic;color:#f29e74;">JSON</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">stringify</span><span>(obj</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">null</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">2</span><span>)
</span><span style="font-style:italic;color:#5c6773;">// "{
</span><span style="font-style:italic;color:#5c6773;">// \"100\": 100,
</span><span style="font-style:italic;color:#5c6773;">// \"site\": \"Pitayan\",
</span><span style="font-style:italic;color:#5c6773;">// \"url\": \"https://pitayan.com\"
</span><span style="font-style:italic;color:#5c6773;">// }"
</span></code></pre>
<h2 id="3-tojson"><a class="zola-anchor" href="#3-tojson" aria-label="Anchor link for: 3-tojson">#</a>
3. toJSON</h2>
<p>After I realised that there are 2 parameters for <code>JSON.stringify</code>, I decided to take a look at the official document. Then I found that an <code>Object</code> can define a method to control the behavior of <code>JSON.stringify</code>.</p>
<p>It’s intercepting the <code>stringify</code> process and a proper <code>String</code> value must be returned from <code>toJSON</code> method. Otherwise, the output is <code>undefined</code>.</p>
<p><code>toJSON</code> receives an argument which is the key of the target <code>Object</code> if it’s nested within another one.</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#ffa759;">const </span><span>objToJSON </span><span style="color:#f29e74;">= </span><span>{
</span><span> </span><span style="color:#bae67e;">"site"</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">"pitayan"</span><span style="color:#ccc9c2cc;">,
</span><span> </span><span style="color:#bae67e;">"url"</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">"https://pitayan.com"</span><span style="color:#ccc9c2cc;">,
</span><span> </span><span style="color:#ffd580;">toJSON</span><span style="color:#ccc9c2cc;">: </span><span style="color:#ffa759;">function </span><span>(</span><span style="color:#ffcc66;">key</span><span>) {
</span><span> </span><span style="color:#ffa759;">if </span><span>(key) {
</span><span> </span><span style="color:#ffa759;">return </span><span style="color:#bae67e;">`[${</span><span>key</span><span style="color:#bae67e;">}]: ${</span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span>site</span><span style="color:#bae67e;">} -- ${</span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span>url</span><span style="color:#bae67e;">}`
</span><span> } </span><span style="color:#ffa759;">else </span><span>{
</span><span> </span><span style="color:#ffa759;">return </span><span style="color:#bae67e;">`${</span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span>site</span><span style="color:#bae67e;">} -- ${</span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span>url</span><span style="color:#bae67e;">}`
</span><span> }
</span><span> }
</span><span>}
</span><span>
</span><span>
</span><span style="font-style:italic;color:#f29e74;">JSON</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">stringify</span><span>(objToJSON)
</span><span style="font-style:italic;color:#5c6773;">// "\"pitayan -- https://pitayan.com\""
</span><span>
</span><span style="font-style:italic;color:#f29e74;">JSON</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">stringify</span><span>({ objToJSON })
</span><span style="font-style:italic;color:#5c6773;">// "{\"objToJSON\":\"[objToJSON]: pitayan -- https://pitayan.com\"}"
</span></code></pre>
<p>Okay, this is all for <code>JSON.stringify</code>. Hope this article will help everyone gain some knowledge about this usefull API.</p>
<p>If you think this article is great, please do share it on the social network. Thanks for reading!</p>
<h2 id="references"><a class="zola-anchor" href="#references" aria-label="Anchor link for: references">#</a>
References</h2>
<ul>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify">https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify</a></li>
</ul>
I Have Very Good Feelings about ES2020 features2020-07-28T00:00:00+00:002020-07-28T00:00:00+00:00https://pitayan.com/posts/es2020-features/<p>ES2020 has been out for a while. I guess a lot of <code>Node</code> developers have already adopted these features. Some even started using them when these features were still under proposal stages. Yep. My team started using some stage 3 features for quite a while.</p>
<p>In this article, I’m going to talk about my feelings of using these ES2020 features. As a result, I think those features are great and essential.</p>
<h2 id="1-operator-nullish-coalescing"><a class="zola-anchor" href="#1-operator-nullish-coalescing" aria-label="Anchor link for: 1-operator-nullish-coalescing">#</a>
1. Operator: Nullish Coalescing</h2>
<p>At the beginning, my idea of “providing exlicit explanation of your code” denies such improvement. I think since a value of <code>nullish</code> or <code>falsy</code> should be given a thorough “explanation” under an <code>if condition</code> or some other strategic <code>function</code>. In this way, I could also add some extra logics without refactoring the shorthand expression later.</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#ffa759;">let </span><span>website </span><span style="color:#f29e74;">= </span><span>{}
</span><span>
</span><span style="color:#ffa759;">let </span><span>url </span><span style="color:#f29e74;">= </span><span style="color:#bae67e;">'https://pitayan.com'
</span><span style="color:#ffa759;">if </span><span>(website</span><span style="color:#f29e74;">.</span><span>url </span><span style="color:#f29e74;">!== </span><span style="color:#ffcc66;">undefined </span><span style="color:#f29e74;">&& typeof </span><span>website</span><span style="color:#f29e74;">.</span><span>url </span><span style="color:#f29e74;">=== </span><span style="font-style:italic;color:#5ccfe6;">String</span><span>) {
</span><span> url </span><span style="color:#f29e74;">= </span><span>website</span><span style="color:#f29e74;">.</span><span>url
</span><span>}
</span></code></pre>
<p>But I made compromise quickly after trying “nullish coalescing” everywhere in the project. My concern was proved unnecessary. Because what I want is to make sure the target variable has concrete value in most of the scenarios.</p>
<p>In Typescript, operating a <code>nullish</code> value will probably be given errors or warnings. This means my code can be optimized easily by following the expostulation.</p>
<pre data-lang="ts" style="background-color:#212733;color:#ccc9c2;" class="language-ts "><code class="language-ts" data-lang="ts"><span style="color:#ffa759;">let </span><span>url</span><span style="color:#f29e74;">: </span><span style="font-style:italic;color:#5ccfe6;">string </span><span style="color:#f29e74;">= </span><span>website</span><span style="color:#f29e74;">.</span><span>url </span><span style="color:#f29e74;">?? </span><span style="color:#bae67e;">'https://pitayan.com'
</span></code></pre>
<p>In brief, my feeling toward <code>nullish coalescing</code> is quite supportive. It’ going to be very helpful while assigning non-nullable value to a variable.</p>
<h2 id="2-asychronous-modules-dynamic-import"><a class="zola-anchor" href="#2-asychronous-modules-dynamic-import" aria-label="Anchor link for: 2-asychronous-modules-dynamic-import">#</a>
2. Asychronous Modules: Dynamic import</h2>
<p>I’ve been using this feature since stage 2. You know, our apps needs that ability of “Just in Time”.</p>
<p>It helps me import Javascript / JSON files as modules in my application. And now it can just show up anywhere in my project (mostly for front end. Haven’t experience it on server side). Have to say this feature is indispensible.</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#ffa759;">let </span><span>answer </span><span style="color:#f29e74;">= </span><span style="color:#ffa759;">await </span><span style="color:#f29e74;">import</span><span>(</span><span style="color:#bae67e;">'./myanswer.json'</span><span>)
</span><span>
</span><span style="color:#f29e74;">import</span><span>(</span><span style="color:#bae67e;">'./component.js'</span><span>)
</span><span> </span><span style="color:#f29e74;">.</span><span style="color:#f28779;">then</span><span>(</span><span style="color:#ffcc66;">module </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">module</span><span style="color:#f29e74;">.</span><span>default</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">mount</span><span>(answer)
</span><span> })
</span></code></pre>
<h2 id="3-promise-allsettled"><a class="zola-anchor" href="#3-promise-allsettled" aria-label="Anchor link for: 3-promise-allsettled">#</a>
3. Promise.allSettled</h2>
<p><code>Promise.all</code> has brought us a useful solution to the famous “callback hell”. Nesting functions is truly nasty.</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="font-style:italic;color:#5c6773;">// Before
</span><span style="color:#ffd580;">getUp</span><span>()</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">then</span><span>(() </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="color:#ffd580;">washFace</span><span>()</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">then</span><span>(() </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="color:#ffd580;">brushTeeth</span><span>()</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">then</span><span>(() </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="color:#ffd580;">eatBreakfast</span><span>()
</span><span> })
</span><span> })
</span><span>})
</span></code></pre>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="font-style:italic;color:#5c6773;">// After
</span><span style="font-style:italic;color:#5ccfe6;">Promise</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">all</span><span>([
</span><span> getUp</span><span style="color:#ccc9c2cc;">,
</span><span> watchFace</span><span style="color:#ccc9c2cc;">,
</span><span> brushTeeth
</span><span>])</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">then</span><span>(() </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="color:#ffd580;">eatBreakfast</span><span>()
</span><span>})
</span><span>
</span></code></pre>
<p>As you know, <code>Promise.all</code> will throw errors once one of the tasks encounters exceptions. I never hope that I can’t eat breakfast without washing my face.</p>
<p>Of course, I can add <code>finally</code> to the <code>Promise</code> chain to make sure eating breakfast. But <code>finally</code> doesn’t provide the correct context I wanted. Don’t even have to mention using <code>catch</code> to eat breakfast, that’s a bad practice.</p>
<p>Finally, <code>allSettled</code> permits us to set a callback whenever I wash my face or brush my teeth. I just want that breakfast! It feels like you grew up and move out of parents’ home. So mom’s scolding about washing face and brushing teeth are gone.</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="font-style:italic;color:#5c6773;">// Bad
</span><span style="font-style:italic;color:#5ccfe6;">Promise</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">all</span><span>([
</span><span> getUp</span><span style="color:#ccc9c2cc;">,
</span><span> watchFace</span><span style="color:#ccc9c2cc;">,
</span><span> brushTeeth
</span><span>])</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">then</span><span>(() </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="color:#ffd580;">eatBreakfast</span><span>()
</span><span>})</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">catch</span><span>(() </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="color:#ffd580;">eatBreakfast</span><span>()
</span><span>})
</span></code></pre>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="font-style:italic;color:#5c6773;">// Good
</span><span style="font-style:italic;color:#5ccfe6;">Promise</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">allSettled</span><span>([
</span><span> getUp</span><span style="color:#ccc9c2cc;">,
</span><span> watchFace</span><span style="color:#ccc9c2cc;">,
</span><span> brushTeeth
</span><span>])</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">then</span><span>(</span><span style="color:#ffcc66;">results </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="color:#ffd580;">eatBreakfast</span><span>()
</span><span>})
</span></code></pre>
<h2 id="4-very-large-number-bigint"><a class="zola-anchor" href="#4-very-large-number-bigint" aria-label="Anchor link for: 4-very-large-number-bigint">#</a>
4. Very Large Number: BigInt</h2>
<p>The Javascript <code>Number</code> type used to range from <strong>-(2<sup>53</sup>-1)</strong> to <strong>2<sup>53</sup>-1</strong> (Number.MIN_SAFE_INTEGER ~ Number.MAX_SAFE_INTEGER).</p>
<p>With this new API, any large number can be process properly in browser without losing any precision.</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#ffa759;">let </span><span>bitInteger </span><span style="color:#f29e74;">= </span><span style="color:#ffd580;">BitInt</span><span>(</span><span style="color:#ffcc66;">9007199254740995</span><span>)
</span><span>
</span><span style="font-style:italic;color:#5c6773;">// Or
</span><span>
</span><span style="color:#ffa759;">let </span><span>bitInteger </span><span style="color:#f29e74;">= </span><span style="color:#ffcc66;">9007199254740995</span><span style="color:#ffa759;">n
</span></code></pre>
<p>In my case, big integers are converted into <code>String</code> to avoid precision issues before they are carried out to the front. It is indeed a rare case to use <code>BitInt</code> at the moment in my project. I believe there are other general demands around this features in other projects.</p>
<p>A simple example I can come up with is: If a database’s model ID is numberic and fairly long (like a shopping order ID), then when it is passed onto the frontend the <code>BigInt</code> can come into help.</p>
<h2 id="5-regex-string-prototype-matchall"><a class="zola-anchor" href="#5-regex-string-prototype-matchall" aria-label="Anchor link for: 5-regex-string-prototype-matchall">#</a>
5. Regex: String.prototype.matchAll</h2>
<p>Actually <code>matchAll</code> has been proposed for a long time. It returns an <code>Array</code> containing all matched characters. Compared to <code>match</code>, the return type <code>RegExpStringIterator</code> gives us a consistant result whenever the string matches or not. By using tools like <code>Array.from</code> I could finally pull all results from the <code>iterator</code>.</p>
<p>This after all is a good improvement in my humble opinion. Because the returned data type is always the same.</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#ffa759;">let </span><span>url </span><span style="color:#f29e74;">= </span><span style="color:#bae67e;">'https://pitayan.com'
</span><span style="color:#ffa759;">let </span><span>blank </span><span style="color:#f29e74;">= </span><span style="color:#bae67e;">''
</span><span style="color:#ffa759;">let </span><span>reg </span><span style="color:#f29e74;">= </span><span style="color:#95e6cb;">/pitayan</span><span style="color:#ffcc66;">.</span><span style="color:#95e6cb;">com/</span><span style="color:#ffa759;">g
</span><span>
</span><span style="font-style:italic;color:#5c6773;">// match
</span><span>url</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">match</span><span>(reg) </span><span style="font-style:italic;color:#5c6773;">// ["pitayan.com"]
</span><span>blank</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">match</span><span>(reg) </span><span style="font-style:italic;color:#5c6773;">// null
</span><span>
</span><span style="font-style:italic;color:#5c6773;">// matchAll
</span><span style="font-style:italic;color:#5ccfe6;">Array</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">from</span><span>(url</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">matchAll</span><span>(reg)) </span><span style="font-style:italic;color:#5c6773;">// [["pitayan.com", index: 8, input: "https://pitayan.com", groups: undefined]]
</span><span style="font-style:italic;color:#5ccfe6;">Array</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">from</span><span>(blank</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">match</span><span>(reg)) </span><span style="font-style:italic;color:#5c6773;">// []
</span></code></pre>
<h2 id="6-a-standardized-global-object-globalthis"><a class="zola-anchor" href="#6-a-standardized-global-object-globalthis" aria-label="Anchor link for: 6-a-standardized-global-object-globalthis">#</a>
6. A Standardized Global Object: GlobalThis</h2>
<p>Sometimes the JS code needs to cross platform, but Node.js uses <code>global</code> which is different to browser’s <code>window</code> (web worker uses <code>self</code>). So before starting everything, I need to handle the environment compatibility first.</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#ffa759;">const </span><span>globalThis </span><span style="color:#f29e74;">= </span><span>((</span><span style="color:#ffcc66;">global</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">window</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">self</span><span>) </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="color:#ffa759;">if </span><span>(global) </span><span style="color:#ffa759;">return </span><span>global
</span><span> </span><span style="color:#ffa759;">if </span><span>(window) </span><span style="color:#ffa759;">return </span><span>window
</span><span> </span><span style="color:#ffa759;">if </span><span>(self) </span><span style="color:#ffa759;">return </span><span>self
</span><span> </span><span style="color:#ffa759;">throw </span><span style="color:#f29e74;">new </span><span style="color:#73d0ff;">Error</span><span>(</span><span style="color:#bae67e;">'...'</span><span>)
</span><span>})(global</span><span style="color:#ccc9c2cc;">, </span><span>window</span><span style="color:#ccc9c2cc;">, </span><span>self)
</span></code></pre>
<p>I personally think that identifying the environment is the duty of the lanuague system. So the <code>globalThis</code> is something that filled the nasty gap. Really really appreciated this feature’s release.</p>
<h2 id="7-chain-with-elegance-optional-chaining"><a class="zola-anchor" href="#7-chain-with-elegance-optional-chaining" aria-label="Anchor link for: 7-chain-with-elegance-optional-chaining">#</a>
7. Chain with Elegance: Optional Chaining</h2>
<p>I’ve used this feature since stage 2. It helps reduce a lot of <code>if conditions</code> or <code>ternary operators</code> which made my code look much simpler.</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#ffa759;">let </span><span>food </span><span style="color:#f29e74;">= </span><span>restuarant </span><span style="color:#f29e74;">&& </span><span>restuarant</span><span style="color:#f29e74;">.</span><span>cooking </span><span style="color:#f29e74;">&& </span><span>restuarant</span><span style="color:#f29e74;">.</span><span>cooking</span><span style="color:#f29e74;">.</span><span>food
</span><span>
</span><span style="font-style:italic;color:#5c6773;">// after
</span><span style="color:#ffa759;">let </span><span>food </span><span style="color:#f29e74;">= </span><span>restuarant</span><span style="color:#f29e74;">?.</span><span>cooking</span><span style="color:#f29e74;">?.</span><span>food
</span></code></pre>
<p>Other than accessing the properties, I can also use it on methods.</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#ffa759;">let </span><span>food </span><span style="color:#f29e74;">= </span><span>restuarant</span><span style="color:#f29e74;">?.</span><span>cooking</span><span style="color:#f29e74;">?</span><span>()</span><span style="color:#f29e74;">.</span><span>food
</span></code></pre>
<p>Isn’t this looking good?</p>
<h2 id="8-module-namespace-exports-export-as"><a class="zola-anchor" href="#8-module-namespace-exports-export-as" aria-label="Anchor link for: 8-module-namespace-exports-export-as">#</a>
8. Module Namespace Exports: export * as</h2>
<p>This an amazing API for Javascript. I used to export some modules in this way.</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#ffa759;">import </span><span>A </span><span style="color:#ffa759;">from </span><span style="color:#bae67e;">'./A.js'
</span><span style="color:#ffa759;">import </span><span>B </span><span style="color:#ffa759;">from </span><span style="color:#bae67e;">'./B.js'
</span><span style="color:#ffa759;">import </span><span>C </span><span style="color:#ffa759;">from </span><span style="color:#bae67e;">'./C.js'
</span><span>
</span><span style="color:#ffa759;">export default </span><span>{ A</span><span style="color:#ccc9c2cc;">, </span><span>B</span><span style="color:#ccc9c2cc;">, </span><span>C }
</span></code></pre>
<p>Now I can do this instead.</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#ffa759;">export </span><span style="color:#ffcc66;">* </span><span style="color:#ffa759;">as </span><span>A </span><span style="color:#ffa759;">from </span><span style="color:#bae67e;">'./A.js'
</span><span style="color:#ffa759;">export </span><span style="color:#ffcc66;">* </span><span style="color:#ffa759;">as </span><span>B </span><span style="color:#ffa759;">from </span><span style="color:#bae67e;">'./B.js'
</span><span style="color:#ffa759;">export </span><span style="color:#ffcc66;">* </span><span style="color:#ffa759;">as </span><span>C </span><span style="color:#ffa759;">from </span><span style="color:#bae67e;">'./C.js'
</span></code></pre>
<p>And the usage of import these modules stays the same.</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#ffa759;">import </span><span>{ A</span><span style="color:#ccc9c2cc;">, </span><span>B</span><span style="color:#ccc9c2cc;">, </span><span>C } </span><span style="color:#ffa759;">from </span><span style="color:#bae67e;">'./index.js'
</span></code></pre>
<p>Fancy but very practical!</p>
<h2 id="9-other-features"><a class="zola-anchor" href="#9-other-features" aria-label="Anchor link for: 9-other-features">#</a>
9. Other features</h2>
<p>There are some other features I haven’t experienced well enough to make conclusions. Their definition is clear enough to speculate the changes. I believe they are quite useful otherwise it’s impossible to introduce them into the new standard.</p>
<h4 id="for-in-loop-order"><a class="zola-anchor" href="#for-in-loop-order" aria-label="Anchor link for: for-in-loop-order">#</a>
for … in loop order</h4>
<p>The <code>for in</code> loop order wasn’t specified by ECMAScript at the beginning. Each browsers has different behiviors but now they are unified aligning with the ECMA standard.</p>
<h4 id="import-meta"><a class="zola-anchor" href="#import-meta" aria-label="Anchor link for: import-meta">#</a>
import meta</h4>
<p>Now you can access the information from an imported module.</p>
<pre data-lang="html" style="background-color:#212733;color:#ccc9c2;" class="language-html "><code class="language-html" data-lang="html"><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">script </span><span style="color:#ffd580;">src</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"script.js"</span><span style="color:#5ccfe690;">></</span><span style="color:#73d0ff;">script</span><span style="color:#5ccfe690;">>
</span></code></pre>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">oog</span><span>(</span><span style="color:#ffa759;">import</span><span style="color:#f29e74;">.</span><span>meta) </span><span style="font-style:italic;color:#5c6773;">// { url: "https://pitayan.com/script.js" }
</span></code></pre>
<h2 id="conclusion"><a class="zola-anchor" href="#conclusion" aria-label="Anchor link for: conclusion">#</a>
Conclusion</h2>
<p>Javascript has brought us many convenient and powerful APIs these years. Our development has been improved ever since the new standards coming out continuously. And they are proved to be the life saver for us engineers. I wish there will be more powerful features in the future so that maybe one day I don’t have to type any code to build a wonderful application.</p>
<p>Alright, that’s all about some humble opitions toward the ES2020 features. I hope you also have the same feelings.</p>
<p>If you think this article is great, then please share it to the social network to let more people get involved.</p>
<p>Thank you for reading!</p>
<h2 id="references"><a class="zola-anchor" href="#references" aria-label="Anchor link for: references">#</a>
References</h2>
<ul>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://www.freecodecamp.org/news/javascript-new-features-es2020/">https://www.freecodecamp.org/news/javascript-new-features-es2020/</a></li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://www.jianshu.com/p/416a0931e96c">https://www.jianshu.com/p/416a0931e96c</a></li>
</ul>
The optimal solution to shuffle an Array in Javascript2020-07-27T00:00:00+00:002020-07-27T00:00:00+00:00https://pitayan.com/posts/javascript-shuffle-array/<p>I recently met a small issue on creating a new randomly ordered array based on an old one. To speak shortly, the final goal is to get a shuffled array.</p>
<p>The following is my solution after a few moment’s experiment before I search the web. (I thought I could do it myself :p)</p>
<pre data-lang="javascript" style="background-color:#212733;color:#ccc9c2;" class="language-javascript "><code class="language-javascript" data-lang="javascript"><span style="color:#ffa759;">var </span><span>arr </span><span style="color:#f29e74;">= </span><span>[</span><span style="color:#ffcc66;">1</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">2</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">3</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">4</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">5</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">6</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">7</span><span>]
</span><span>
</span><span style="color:#ffa759;">function </span><span style="color:#ffd580;">shuffle </span><span>(</span><span style="color:#ffcc66;">arr</span><span>) {
</span><span> </span><span style="color:#ffa759;">let </span><span>i </span><span style="color:#f29e74;">= </span><span style="color:#ffcc66;">0</span><span style="color:#ccc9c2cc;">,
</span><span> res </span><span style="color:#f29e74;">= </span><span>[]</span><span style="color:#ccc9c2cc;">,
</span><span> index
</span><span>
</span><span> </span><span style="color:#ffa759;">while </span><span>(i </span><span style="color:#f29e74;"><= </span><span>arr</span><span style="color:#f29e74;">.</span><span>length </span><span style="color:#f29e74;">- </span><span style="color:#ffcc66;">1</span><span>) {
</span><span> index </span><span style="color:#f29e74;">= </span><span style="font-style:italic;color:#f29e74;">Math</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">floor</span><span>(</span><span style="font-style:italic;color:#f29e74;">Math</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">random</span><span>() </span><span style="color:#f29e74;">* </span><span>arr</span><span style="color:#f29e74;">.</span><span>length)
</span><span>
</span><span> </span><span style="color:#ffa759;">if </span><span>(</span><span style="color:#f29e74;">!</span><span>res</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">includes</span><span>(arr[index])) {
</span><span> res</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">push</span><span>(arr[index])
</span><span> i</span><span style="color:#f29e74;">++
</span><span> }
</span><span> }
</span><span>
</span><span> </span><span style="color:#ffa759;">return </span><span>res
</span><span>}
</span><span>
</span><span style="font-style:italic;color:#5c6773;">// expected
</span><span>arr </span><span style="color:#f29e74;">= </span><span style="color:#ffd580;">shuffle</span><span>(arr)
</span><span style="font-style:italic;color:#5c6773;">// [6, 3, 4, 1, 7, 2, 5]
</span></code></pre>
<p>As you can see that this is not a good way handle shuffling, so I decide to do some researches over it.</p>
<p>After looking for some answers on google and stackoverflow, I found a most satisfying <a rel="noopener nofollow noreferrer" target="_blank" href="https://stackoverflow.com/questions/2450954/how-to-randomize-shuffle-a-javascript-array">solution</a> to shuffle an array. (The answer has been there since 2010… But, very qualified indeed.)</p>
<p>First things first, let’s take a look at the answer. It’s quite simple but fast enough.</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#ffa759;">function </span><span style="color:#ffd580;">shuffle</span><span>(</span><span style="color:#ffcc66;">array</span><span>) {
</span><span> </span><span style="color:#ffa759;">var </span><span>currentIndex </span><span style="color:#f29e74;">= </span><span>array</span><span style="color:#f29e74;">.</span><span>length</span><span style="color:#ccc9c2cc;">, </span><span>temporaryValue</span><span style="color:#ccc9c2cc;">, </span><span>randomIndex</span><span style="color:#ccc9c2cc;">;
</span><span>
</span><span> </span><span style="font-style:italic;color:#5c6773;">// While there remain elements to shuffle...
</span><span> </span><span style="color:#ffa759;">while </span><span>(</span><span style="color:#ffcc66;">0 </span><span style="color:#f29e74;">!== </span><span>currentIndex) {
</span><span>
</span><span> </span><span style="font-style:italic;color:#5c6773;">// Pick a remaining element...
</span><span> randomIndex </span><span style="color:#f29e74;">= </span><span style="font-style:italic;color:#f29e74;">Math</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">floor</span><span>(</span><span style="font-style:italic;color:#f29e74;">Math</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">random</span><span>() </span><span style="color:#f29e74;">* </span><span>currentIndex)</span><span style="color:#ccc9c2cc;">;
</span><span> currentIndex </span><span style="color:#f29e74;">-= </span><span style="color:#ffcc66;">1</span><span style="color:#ccc9c2cc;">;
</span><span>
</span><span> </span><span style="font-style:italic;color:#5c6773;">// And swap it with the current element.
</span><span> temporaryValue </span><span style="color:#f29e74;">= </span><span>array[currentIndex]</span><span style="color:#ccc9c2cc;">;
</span><span> array[currentIndex] </span><span style="color:#f29e74;">= </span><span>array[randomIndex]</span><span style="color:#ccc9c2cc;">;
</span><span> array[randomIndex] </span><span style="color:#f29e74;">= </span><span>temporaryValue</span><span style="color:#ccc9c2cc;">;
</span><span> }
</span><span>
</span><span> </span><span style="color:#ffa759;">return </span><span>array</span><span style="color:#ccc9c2cc;">;
</span><span>}
</span></code></pre>
<h2 id="why-my-solution-is-bad"><a class="zola-anchor" href="#why-my-solution-is-bad" aria-label="Anchor link for: why-my-solution-is-bad">#</a>
Why My Solution is Bad</h2>
<p>At the beginning, I was just thinking about creating new random indexes within a <code>while</code> loop and push the old array element to a new array as return.</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#ffa759;">while </span><span>(i </span><span style="color:#f29e74;"><= </span><span>arr</span><span style="color:#f29e74;">.</span><span>length </span><span style="color:#f29e74;">- </span><span style="color:#ffcc66;">1</span><span>) {
</span><span> </span><span style="font-style:italic;color:#5c6773;">// create random index
</span><span> index </span><span style="color:#f29e74;">= </span><span style="font-style:italic;color:#f29e74;">Math</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">floor</span><span>(</span><span style="font-style:italic;color:#f29e74;">Math</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">random</span><span>() </span><span style="color:#f29e74;">* </span><span>arr</span><span style="color:#f29e74;">.</span><span>length)
</span><span>
</span><span> </span><span style="font-style:italic;color:#5c6773;">// insert the element to new array
</span><span> </span><span style="color:#ffa759;">if </span><span>(</span><span style="color:#f29e74;">!</span><span>res</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">includes</span><span>(arr[index])) {
</span><span> res</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">push</span><span>(arr[index])
</span><span> i</span><span style="color:#f29e74;">++
</span><span> }
</span><span>}
</span></code></pre>
<p>It works well with very satisfying returnings. But the time complexity was pretty bad. In the <code>while</code> loop, it checks if the element to be inserted exists in the new array for each of the loop round. This results in <em><strong>O(n<sup>2</sup>)</strong></em>.</p>
<p>If an array isn’t that big, then my function was just fine. But the truth is, my project needs to generate a list with more than <strong>1000</strong> elements. So it’s better to optimize the algorithm. (I think it’s always better to do such optimization. Don’t be afraid to mean to computers :D)</p>
<h2 id="the-fisher-yates-shuffle"><a class="zola-anchor" href="#the-fisher-yates-shuffle" aria-label="Anchor link for: the-fisher-yates-shuffle">#</a>
The Fisher–Yates Shuffle</h2>
<p>The stackoverflow’s answer seems quite simple, however in fact it uses an algorithm invented by <a rel="noopener nofollow noreferrer" target="_blank" href="https://en.wikipedia.org/wiki/Ronald_Fisher">Ronald Fisher</a> and <a rel="noopener nofollow noreferrer" target="_blank" href="https://en.wikipedia.org/wiki/Frank_Yates">Frank Yates</a>.</p>
<blockquote>
<p>The Fisher–Yates shuffle is an algorithm for generating a random permutation of a finite sequence—in plain terms, the algorithm shuffles the sequence.</p>
<p>…and is also known as the Knuth shuffle after <a rel="noopener nofollow noreferrer" target="_blank" href="https://en.wikipedia.org/wiki/Donald_Knuth">Donald Knuth</a>.</p>
<p>– <cite><strong>From <a rel="noopener nofollow noreferrer" target="_blank" href="https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle">Wikipedia</a></strong></cite></p>
</blockquote>
<p>There’s an old blog article that visualizes the shuffle algorithm. <a rel="noopener nofollow noreferrer" target="_blank" href="https://bost.ocks.org/mike/shuffle/">https://bost.ocks.org/mike/shuffle/</a></p>
<p>The <code>shuffle</code> function is a description of the algorithm.</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#ffa759;">function </span><span style="color:#ffd580;">shuffle</span><span>(</span><span style="color:#ffcc66;">array</span><span>) {
</span><span> </span><span style="color:#ffa759;">var </span><span>currentIndex </span><span style="color:#f29e74;">= </span><span>array</span><span style="color:#f29e74;">.</span><span>length</span><span style="color:#ccc9c2cc;">, </span><span>temporaryValue</span><span style="color:#ccc9c2cc;">, </span><span>randomIndex</span><span style="color:#ccc9c2cc;">;
</span><span>
</span><span> </span><span style="font-style:italic;color:#5c6773;">// While there remain elements to shuffle...
</span><span> </span><span style="color:#ffa759;">while </span><span>(</span><span style="color:#ffcc66;">0 </span><span style="color:#f29e74;">!== </span><span>currentIndex) {
</span><span>
</span><span> </span><span style="font-style:italic;color:#5c6773;">// Create a random index to pick from the original array
</span><span> randomIndex </span><span style="color:#f29e74;">= </span><span style="font-style:italic;color:#f29e74;">Math</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">floor</span><span>(</span><span style="font-style:italic;color:#f29e74;">Math</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">random</span><span>() </span><span style="color:#f29e74;">* </span><span>currentIndex)</span><span style="color:#ccc9c2cc;">;
</span><span> currentIndex </span><span style="color:#f29e74;">-= </span><span style="color:#ffcc66;">1</span><span style="color:#ccc9c2cc;">;
</span><span>
</span><span> </span><span style="font-style:italic;color:#5c6773;">// Cache the value, and swap it with the current element
</span><span> temporaryValue </span><span style="color:#f29e74;">= </span><span>array[currentIndex]</span><span style="color:#ccc9c2cc;">;
</span><span> array[currentIndex] </span><span style="color:#f29e74;">= </span><span>array[randomIndex]</span><span style="color:#ccc9c2cc;">;
</span><span> array[randomIndex] </span><span style="color:#f29e74;">= </span><span>temporaryValue</span><span style="color:#ccc9c2cc;">;
</span><span> }
</span><span>
</span><span> </span><span style="color:#ffa759;">return </span><span>array</span><span style="color:#ccc9c2cc;">;
</span><span>}
</span></code></pre>
<p>The solution is very good, but it still has some improving potentials. I believe making a pure function here makes more sense. So I’d rather return a new array than modifying the original argument as a side effect.</p>
<p>To avoid modifying the original data, I can also create a clone while passing the arugment.</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#ffd580;">shuffle</span><span>(arr</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">slice</span><span>(</span><span style="color:#ffcc66;">0</span><span>))
</span></code></pre>
<h2 id="other-variations"><a class="zola-anchor" href="#other-variations" aria-label="Anchor link for: other-variations">#</a>
Other Variations</h2>
<p>There are some honorable alternatives to the solution I found on <a rel="noopener nofollow noreferrer" target="_blank" href="https://stackoverflow.com/questions/2450954/how-to-randomize-shuffle-a-javascript-array">stackoverflow</a> which I think is properly optimized.</p>
<h4 id="the-durstenfeld-shuffle"><a class="zola-anchor" href="#the-durstenfeld-shuffle" aria-label="Anchor link for: the-durstenfeld-shuffle">#</a>
The Durstenfeld Shuffle</h4>
<p>This solution appears on the <a rel="noopener nofollow noreferrer" target="_blank" href="https://stackoverflow.com/questions/2450954/how-to-randomize-shuffle-a-javascript-array">stackoverflow</a> page. I found a gist memo in the end.</p>
<p><a rel="noopener nofollow noreferrer" target="_blank" href="https://gist.github.com/webbower/8d19b714ded3ec53d1d7ed32b79fdbac">https://gist.github.com/webbower/8d19b714ded3ec53d1d7ed32b79fdbac</a></p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="font-style:italic;color:#5c6773;">// Pre-ES6
</span><span style="color:#ffa759;">function </span><span style="color:#ffd580;">shuffleArray</span><span>(</span><span style="color:#ffcc66;">array</span><span>) {
</span><span> </span><span style="color:#ffa759;">for </span><span>(</span><span style="color:#ffa759;">var </span><span>i </span><span style="color:#f29e74;">= </span><span>array</span><span style="color:#f29e74;">.</span><span>length </span><span style="color:#f29e74;">- </span><span style="color:#ffcc66;">1</span><span style="color:#ccc9c2cc;">; </span><span>i </span><span style="color:#f29e74;">> </span><span style="color:#ffcc66;">0</span><span style="color:#ccc9c2cc;">; </span><span>i</span><span style="color:#f29e74;">--</span><span>) {
</span><span> </span><span style="color:#ffa759;">var </span><span>j </span><span style="color:#f29e74;">= </span><span style="font-style:italic;color:#f29e74;">Math</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">floor</span><span>(</span><span style="font-style:italic;color:#f29e74;">Math</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">random</span><span>() </span><span style="color:#f29e74;">* </span><span>(i </span><span style="color:#f29e74;">+ </span><span style="color:#ffcc66;">1</span><span>))</span><span style="color:#ccc9c2cc;">;
</span><span> </span><span style="color:#ffa759;">var </span><span>temp </span><span style="color:#f29e74;">= </span><span>array[i]</span><span style="color:#ccc9c2cc;">;
</span><span> array[i] </span><span style="color:#f29e74;">= </span><span>array[j]</span><span style="color:#ccc9c2cc;">;
</span><span> array[j] </span><span style="color:#f29e74;">= </span><span>temp</span><span style="color:#ccc9c2cc;">;
</span><span> }
</span><span>}
</span><span>
</span><span style="font-style:italic;color:#5c6773;">// ES6+
</span><span style="color:#ffa759;">function </span><span style="color:#ffd580;">shuffleArray</span><span>(</span><span style="color:#ffcc66;">array</span><span>) {
</span><span> </span><span style="color:#ffa759;">for </span><span>(</span><span style="color:#ffa759;">let </span><span>i </span><span style="color:#f29e74;">= </span><span>array</span><span style="color:#f29e74;">.</span><span>length </span><span style="color:#f29e74;">- </span><span style="color:#ffcc66;">1</span><span style="color:#ccc9c2cc;">; </span><span>i </span><span style="color:#f29e74;">> </span><span style="color:#ffcc66;">0</span><span style="color:#ccc9c2cc;">; </span><span>i</span><span style="color:#f29e74;">--</span><span>) {
</span><span> </span><span style="color:#ffa759;">let </span><span>j </span><span style="color:#f29e74;">= </span><span style="font-style:italic;color:#f29e74;">Math</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">floor</span><span>(</span><span style="font-style:italic;color:#f29e74;">Math</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">random</span><span>() </span><span style="color:#f29e74;">* </span><span>(i </span><span style="color:#f29e74;">+ </span><span style="color:#ffcc66;">1</span><span>))</span><span style="color:#ccc9c2cc;">;
</span><span> [array[i]</span><span style="color:#ccc9c2cc;">, </span><span>array[j]] </span><span style="color:#f29e74;">= </span><span>[array[j]</span><span style="color:#ccc9c2cc;">, </span><span>array[i]]</span><span style="color:#ccc9c2cc;">;
</span><span> }
</span><span>}
</span></code></pre>
<h4 id="array-extension-method"><a class="zola-anchor" href="#array-extension-method" aria-label="Anchor link for: array-extension-method">#</a>
Array extension method</h4>
<p>Actually, I’d prefer this one due to its simplicity and a small trick of round numbers. The trick here is to use <code>>>></code> (<a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Unsigned_right_shift">unsigned right shift operator</a>) instead of <code>Math.floor</code>.</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="font-style:italic;color:#5ccfe6;">Array</span><span style="color:#f29e74;">.</span><span>prototype</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">shuffle </span><span style="color:#f29e74;">= </span><span style="color:#ffa759;">function</span><span>() {
</span><span> </span><span style="color:#ffa759;">let </span><span>m </span><span style="color:#f29e74;">= </span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span>length</span><span style="color:#ccc9c2cc;">, </span><span>i</span><span style="color:#ccc9c2cc;">;
</span><span> </span><span style="color:#ffa759;">while </span><span>(m) {
</span><span> i </span><span style="color:#f29e74;">= </span><span>(</span><span style="font-style:italic;color:#f29e74;">Math</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">random</span><span>() </span><span style="color:#f29e74;">* </span><span>m</span><span style="color:#f29e74;">--</span><span>) </span><span style="color:#f29e74;">>>> </span><span style="color:#ffcc66;">0</span><span style="color:#ccc9c2cc;">;
</span><span> [</span><span style="font-style:italic;color:#5ccfe6;">this</span><span>[m]</span><span style="color:#ccc9c2cc;">, </span><span style="font-style:italic;color:#5ccfe6;">this</span><span>[i]] </span><span style="color:#f29e74;">= </span><span>[</span><span style="font-style:italic;color:#5ccfe6;">this</span><span>[i]</span><span style="color:#ccc9c2cc;">, </span><span style="font-style:italic;color:#5ccfe6;">this</span><span>[m]]
</span><span> }
</span><span> </span><span style="color:#ffa759;">return </span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#ccc9c2cc;">;
</span><span>}
</span></code></pre>
<p>Okay, that’s all for the research. Hope you also get a good understanding of the <code>shuffle</code> algorithm from this article.
If you think this article is great, please share it on social networks.</p>
<p>Thank you reading!</p>
<h2 id="references"><a class="zola-anchor" href="#references" aria-label="Anchor link for: references">#</a>
References</h2>
<ul>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Unsigned_right_shift">https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Unsigned_right_shift</a></li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://en.wikipedia.org/wiki/Fisher%E2%80%9A%C3%84%C3%ACYates_shuffle">https://en.wikipedia.org/wiki/Fisher–Yates_shuffle</a></li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://stackoverflow.com/questions/2450954/how-to-randomize-shuffle-a-javascript-array">https://stackoverflow.com/questions/2450954/how-to-randomize-shuffle-a-javascript-array</a></li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://gist.github.com/webbower/8d19b714ded3ec53d1d7ed32b79fdbac">https://gist.github.com/webbower/8d19b714ded3ec53d1d7ed32b79fdbac</a></li>
</ul>
Have You Mastered These 9 Vue Techniques?2020-07-24T00:00:00+00:002020-07-24T00:00:00+00:00https://pitayan.com/posts/vue-techniques/<p>Now <a rel="noopener nofollow noreferrer" target="_blank" href="https://vuejs.org">Vue.js</a> has become a hot framework for front end development. There are a lot of engineers utilizing the convenience and powerful features of <a rel="noopener nofollow noreferrer" target="_blank" href="https://vuejs.org">Vue.js</a>. Yet, some of the solutions we’ve done might not follow the best practice. Well, let’s take a look at those must-know <a rel="noopener nofollow noreferrer" target="_blank" href="https://vuejs.org">Vue</a> techniques.</p>
<h2 id="1-functional-component"><a class="zola-anchor" href="#1-functional-component" aria-label="Anchor link for: 1-functional-component">#</a>
1. Functional Component</h2>
<p>A <a rel="noopener nofollow noreferrer" target="_blank" href="https://vuejs.org/v2/guide/render-function.html#Functional-Components">functional component</a> is <strong>stateless</strong> and has not <code>lifecycle</code> or <code>methods</code>. So it cannot be instantiated</p>
<p>It’s very easy to create a functional component, all you need to do is to add a <code>functional: true</code> property to the SFC or adding <code>functional</code> to the template. Since it’s as light as a function and has no instance reference, the rendering performance is quite improved.</p>
<p>Functional component relys on the <code>context</code> and mutates along with the given data within it.</p>
<pre data-lang="html" style="background-color:#212733;color:#ccc9c2;" class="language-html "><code class="language-html" data-lang="html"><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">template </span><span style="color:#ffd580;">functional</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">div </span><span style="color:#ffd580;">class</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"book"</span><span style="color:#5ccfe690;">>
</span><span> {{book.name}} {{book.price}}
</span><span> </span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">template</span><span style="color:#5ccfe690;">>
</span></code></pre>
<pre data-lang="html" style="background-color:#212733;color:#ccc9c2;" class="language-html "><code class="language-html" data-lang="html"><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">script</span><span style="color:#5ccfe690;">>
</span><span style="font-style:italic;color:#5ccfe6;">Vue</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">component</span><span>(</span><span style="color:#bae67e;">'book'</span><span style="color:#ccc9c2cc;">, </span><span>{
</span><span> functional</span><span style="color:#ccc9c2cc;">: </span><span style="color:#ffcc66;">true</span><span style="color:#ccc9c2cc;">,
</span><span> props</span><span style="color:#ccc9c2cc;">: </span><span>{
</span><span> books</span><span style="color:#ccc9c2cc;">: </span><span>{
</span><span> </span><span style="color:#ffd580;">type</span><span style="color:#ccc9c2cc;">: </span><span>() </span><span style="color:#ffa759;">=> </span><span>({})</span><span style="color:#ccc9c2cc;">,
</span><span> required</span><span style="color:#ccc9c2cc;">: </span><span style="color:#ffcc66;">true
</span><span> }
</span><span> }</span><span style="color:#ccc9c2cc;">,
</span><span> </span><span style="color:#ffd580;">render</span><span style="color:#ccc9c2cc;">: </span><span style="color:#ffa759;">function </span><span>(</span><span style="color:#ffcc66;">createElement</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">context</span><span>) {
</span><span> </span><span style="color:#ffa759;">return </span><span style="color:#ffd580;">createElement</span><span>(
</span><span> </span><span style="color:#bae67e;">'div'</span><span style="color:#ccc9c2cc;">,
</span><span> {
</span><span> attrs</span><span style="color:#ccc9c2cc;">: </span><span>{
</span><span> class</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">'book'
</span><span> }
</span><span> }</span><span style="color:#ccc9c2cc;">,
</span><span> [context</span><span style="color:#f29e74;">.</span><span>props</span><span style="color:#f29e74;">.</span><span>book]
</span><span> )
</span><span> }
</span><span>})
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">script</span><span style="color:#5ccfe690;">>
</span></code></pre>
<h2 id="2-deep-selectors"><a class="zola-anchor" href="#2-deep-selectors" aria-label="Anchor link for: 2-deep-selectors">#</a>
2. Deep Selectors</h2>
<p>Sometimes you even need to cha the third party components CSS which are <code>scoped</code> styles. It’ impossbile to remove the <code>sc ope</code> or open a new style.</p>
<p>Now the <a rel="noopener nofollow noreferrer" target="_blank" href="https://vue-loader.vuejs.org/guide/scoped-css.html#child-component-root-elements">deep selectors</a> <code>>>></code> <code>/deep/</code> <code>::v-deep</code> come into the right place for help.</p>
<pre data-lang="html" style="background-color:#212733;color:#ccc9c2;" class="language-html "><code class="language-html" data-lang="html"><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">style </span><span style="color:#ffd580;">scoped</span><span style="color:#5ccfe690;">>
</span><span>>>> </span><span style="color:#ffd580;">.scoped-third-party-class </span><span>{
</span><span> </span><span style="color:#5ccfe6;">color</span><span style="color:#ccc9c2cc;">: </span><span style="font-style:italic;color:#f29e74;">gray</span><span style="color:#ccc9c2cc;">;
</span><span>}
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">style</span><span style="color:#5ccfe690;">>
</span></code></pre>
<pre data-lang="html" style="background-color:#212733;color:#ccc9c2;" class="language-html "><code class="language-html" data-lang="html"><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">style </span><span style="color:#ffd580;">scoped</span><span style="color:#5ccfe690;">>
</span><span>/deep/ </span><span style="color:#ffd580;">.scoped-third-party-class </span><span>{
</span><span> </span><span style="color:#5ccfe6;">color</span><span style="color:#ccc9c2cc;">: </span><span style="font-style:italic;color:#f29e74;">gray</span><span style="color:#ccc9c2cc;">;
</span><span>}
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">style</span><span style="color:#5ccfe690;">>
</span></code></pre>
<pre data-lang="html" style="background-color:#212733;color:#ccc9c2;" class="language-html "><code class="language-html" data-lang="html"><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">style </span><span style="color:#ffd580;">scoped</span><span style="color:#5ccfe690;">>
</span><span>::v-deep </span><span style="color:#ffd580;">.scoped-third-party-class </span><span>{
</span><span> </span><span style="color:#5ccfe6;">color</span><span style="color:#ccc9c2cc;">: </span><span style="font-style:italic;color:#f29e74;">gray</span><span style="color:#ccc9c2cc;">;
</span><span>}
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">style</span><span style="color:#5ccfe690;">>
</span></code></pre>
<h2 id="3-advanced-watcher"><a class="zola-anchor" href="#3-advanced-watcher" aria-label="Anchor link for: 3-advanced-watcher">#</a>
3. Advanced “watcher”</h2>
<h4 id="execute-immedately"><a class="zola-anchor" href="#execute-immedately" aria-label="Anchor link for: execute-immedately">#</a>
execute immedately</h4>
<p><code>watch</code> handler triggers when the monitered prop mutates. But sometimes, it’s expected right after the component icreated.</p>
<p>Yea, there’s a simple solution: invoke the handler in the <code>created</code> hook. But that doesn’t look elegant and meanwhile levels up the complexity.</p>
<p>Or you could add an <code>immediate</code> property to watcher:</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#73d0ff;">watch</span><span style="color:#ccc9c2cc;">: </span><span>{
</span><span> </span><span style="color:#73d0ff;">value</span><span style="color:#ccc9c2cc;">: </span><span>{
</span><span> </span><span style="color:#73d0ff;">handler</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">'printValue'</span><span style="color:#ccc9c2cc;">,
</span><span> </span><span style="color:#73d0ff;">immediate</span><span style="color:#ccc9c2cc;">: </span><span style="color:#ffcc66;">true
</span><span> }
</span><span>}</span><span style="color:#ccc9c2cc;">,
</span><span style="color:#73d0ff;">methods </span><span style="color:#ccc9c2cc;">: </span><span>{
</span><span> </span><span style="color:#ffd580;">printValue </span><span>() {
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(</span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span>value)
</span><span> }
</span><span>}
</span></code></pre>
<h4 id="deep-listening"><a class="zola-anchor" href="#deep-listening" aria-label="Anchor link for: deep-listening">#</a>
deep listening</h4>
<p>Sometimes the watcher prop is an <code>Object</code>. But its properties mutation cannot trigger the watcher handler. In this case, adding <code>deep: true</code> to watcher can make its properties’ mutation detectable.</p>
<p><strong>Note</strong> that <code>deep</code> may cause some serious performance issues when your <code>Object</code> has many layers. It’s better to think about using a rather flatten data structure instead.</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#ffd580;">data </span><span>() {
</span><span> </span><span style="color:#ffa759;">return </span><span>{
</span><span> value</span><span style="color:#ccc9c2cc;">: </span><span>{
</span><span> one</span><span style="color:#ccc9c2cc;">: </span><span>{
</span><span> two</span><span style="color:#ccc9c2cc;">: </span><span>{
</span><span> three</span><span style="color:#ccc9c2cc;">: </span><span style="color:#ffcc66;">3
</span><span> }
</span><span> }
</span><span> }
</span><span> }
</span><span>}</span><span style="color:#ccc9c2cc;">,
</span><span style="color:#73d0ff;">watch</span><span style="color:#ccc9c2cc;">: </span><span>{
</span><span> </span><span style="color:#73d0ff;">value</span><span style="color:#ccc9c2cc;">: </span><span>{
</span><span> </span><span style="color:#73d0ff;">handler</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">'printValue'</span><span style="color:#ccc9c2cc;">,
</span><span> </span><span style="color:#73d0ff;">deep</span><span style="color:#ccc9c2cc;">: </span><span style="color:#ffcc66;">true
</span><span> }
</span><span>}</span><span style="color:#ccc9c2cc;">,
</span><span style="color:#73d0ff;">methods </span><span style="color:#ccc9c2cc;">: </span><span>{
</span><span> </span><span style="color:#ffd580;">printValue </span><span>() {
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(</span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span>value)
</span><span> }
</span><span>}
</span></code></pre>
<h4 id="multiple-handlers"><a class="zola-anchor" href="#multiple-handlers" aria-label="Anchor link for: multiple-handlers">#</a>
multiple handlers</h4>
<p>Actually watcher can be set as an <code>Array</code>. Supported types are <code>String</code> | <code>Function</code> | <code>Object</code>. The registered watcher handlers will be invoked one by one when triggered.</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#73d0ff;">watch</span><span style="color:#ccc9c2cc;">: </span><span>{
</span><span> </span><span style="color:#73d0ff;">value</span><span style="color:#ccc9c2cc;">: </span><span>[
</span><span> </span><span style="color:#bae67e;">'printValue'</span><span style="color:#ccc9c2cc;">,
</span><span> </span><span style="color:#ffa759;">function </span><span>(</span><span style="color:#ffcc66;">val</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">oldVal</span><span>) {
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(val)
</span><span> }</span><span style="color:#ccc9c2cc;">,
</span><span> {
</span><span> handler</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">'printValue'</span><span style="color:#ccc9c2cc;">,
</span><span> deep</span><span style="color:#ccc9c2cc;">: </span><span style="color:#ffcc66;">true
</span><span> }
</span><span> ]
</span><span>}</span><span style="color:#ccc9c2cc;">,
</span><span style="color:#73d0ff;">methods </span><span style="color:#ccc9c2cc;">: </span><span>{
</span><span> </span><span style="color:#ffd580;">printValue </span><span>() {
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(</span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span>value)
</span><span> }
</span><span>}
</span></code></pre>
<h4 id="subscribe-to-multiple-variables-mutation"><a class="zola-anchor" href="#subscribe-to-multiple-variables-mutation" aria-label="Anchor link for: subscribe-to-multiple-variables-mutation">#</a>
subscribe to multiple variables mutation</h4>
<p><code>watcher</code> cannot listen to multiple variables , but we could combine the targets together as a new <code>computed</code> and watch this new “variable”.</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#73d0ff;">computed</span><span style="color:#ccc9c2cc;">: </span><span>{
</span><span> </span><span style="color:#ffd580;">multipleValues </span><span>() {
</span><span> </span><span style="color:#ffa759;">return </span><span>{
</span><span> value1</span><span style="color:#ccc9c2cc;">: </span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span>value1</span><span style="color:#ccc9c2cc;">,
</span><span> value2</span><span style="color:#ccc9c2cc;">: </span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span>value2</span><span style="color:#ccc9c2cc;">,
</span><span> }
</span><span> }
</span><span>}</span><span style="color:#ccc9c2cc;">,
</span><span style="color:#73d0ff;">watch</span><span style="color:#ccc9c2cc;">: </span><span>{
</span><span> </span><span style="color:#ffd580;">multipleValues </span><span>(val</span><span style="color:#ccc9c2cc;">, </span><span>oldVal) {
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(val)
</span><span> }
</span><span>}
</span></code></pre>
<h2 id="4-event-argument-event"><a class="zola-anchor" href="#4-event-argument-event" aria-label="Anchor link for: 4-event-argument-event">#</a>
4. Event argument: $event</h2>
<p><code>$event</code> is a special variable of Event Object. It provides more optional arugment in some scenarios for the complex functionalities.</p>
<h4 id="native-events"><a class="zola-anchor" href="#native-events" aria-label="Anchor link for: native-events">#</a>
native events</h4>
<p>In native events, the value is the same to the default event (DOM event or window event).</p>
<pre data-lang="html" style="background-color:#212733;color:#ccc9c2;" class="language-html "><code class="language-html" data-lang="html"><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">template</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">input </span><span style="color:#ffd580;">type</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"text" </span><span style="color:#ffd580;">@input</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"handleInput('hello', $event)" </span><span style="color:#5ccfe690;">/>
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">template</span><span style="color:#5ccfe690;">>
</span><span>
</span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">script</span><span style="color:#5ccfe690;">>
</span><span style="color:#ffa759;">export default </span><span>{
</span><span> methods</span><span style="color:#ccc9c2cc;">: </span><span>{
</span><span> </span><span style="color:#ffd580;">handleInput </span><span>(</span><span style="color:#ffcc66;">val</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">e</span><span>) {
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(e</span><span style="color:#f29e74;">.</span><span>target</span><span style="color:#f29e74;">.</span><span>value) </span><span style="font-style:italic;color:#5c6773;">// hello
</span><span> }
</span><span> }
</span><span>}
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">script</span><span style="color:#5ccfe690;">>
</span></code></pre>
<h4 id="custom-events"><a class="zola-anchor" href="#custom-events" aria-label="Anchor link for: custom-events">#</a>
custom events</h4>
<p>In custom events, the value is what’s captured from its child component.</p>
<pre data-lang="html" style="background-color:#212733;color:#ccc9c2;" class="language-html "><code class="language-html" data-lang="html"><span style="font-style:italic;color:#5c6773;"><!-- Child -->
</span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">template</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">input </span><span style="color:#ffd580;">type</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"text" </span><span style="color:#ffd580;">@input</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"$emit('custom-event', 'hello')" </span><span style="color:#5ccfe690;">/>
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">template</span><span style="color:#5ccfe690;">>
</span></code></pre>
<pre data-lang="html" style="background-color:#212733;color:#ccc9c2;" class="language-html "><code class="language-html" data-lang="html"><span style="font-style:italic;color:#5c6773;"><!-- Parent -->
</span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">template</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">Child </span><span style="color:#ffd580;">@custom-event</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"handleCustomevent" </span><span style="color:#5ccfe690;">/>
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">template</span><span style="color:#5ccfe690;">>
</span><span>
</span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">script</span><span style="color:#5ccfe690;">>
</span><span style="color:#ffa759;">export default </span><span>{
</span><span> methods</span><span style="color:#ccc9c2cc;">: </span><span>{
</span><span> </span><span style="color:#ffd580;">handleCustomevent </span><span>(</span><span style="color:#ffcc66;">value</span><span>) {
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(value) </span><span style="font-style:italic;color:#5c6773;">// hello
</span><span> }
</span><span> }
</span><span>}
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">script</span><span style="color:#5ccfe690;">>
</span></code></pre>
<h2 id="5-router-parameter-decoupling"><a class="zola-anchor" href="#5-router-parameter-decoupling" aria-label="Anchor link for: 5-router-parameter-decoupling">#</a>
5. Router Parameter Decoupling</h2>
<p>I believe this is how most people handle the router parameters in a component:</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#ffa759;">export default </span><span>{
</span><span> methods</span><span style="color:#ccc9c2cc;">: </span><span>{
</span><span> </span><span style="color:#ffd580;">getRouteParamsId</span><span>() {
</span><span> </span><span style="color:#ffa759;">return </span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span>$route</span><span style="color:#f29e74;">.</span><span>params</span><span style="color:#f29e74;">.</span><span>id
</span><span> }
</span><span> }
</span><span>}
</span></code></pre>
<p>Using <code>$route</code> inside a component will generate a strong coupling for the certain URL. This limited the flexibility of a component.</p>
<p>The correct solution is to add <code>props</code> to the <code>Router</code>.</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#ffa759;">const </span><span>router </span><span style="color:#f29e74;">= new </span><span style="color:#73d0ff;">VueRouter</span><span>({
</span><span> routes</span><span style="color:#ccc9c2cc;">: </span><span>[{
</span><span> path</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">'/:id'</span><span style="color:#ccc9c2cc;">,
</span><span> component</span><span style="color:#ccc9c2cc;">: </span><span>Component</span><span style="color:#ccc9c2cc;">,
</span><span> props</span><span style="color:#ccc9c2cc;">: </span><span style="color:#ffcc66;">true
</span><span> }]
</span><span>})
</span></code></pre>
<p>In this way, component can get <code>params</code> directly from props.</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#ffa759;">export default </span><span>{
</span><span> props</span><span style="color:#ccc9c2cc;">: </span><span>[</span><span style="color:#bae67e;">'id'</span><span>]</span><span style="color:#ccc9c2cc;">,
</span><span> methods</span><span style="color:#ccc9c2cc;">: </span><span>{
</span><span> </span><span style="color:#ffd580;">getParamsId</span><span>() {
</span><span> </span><span style="color:#ffa759;">return </span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span>id
</span><span> }
</span><span> }
</span><span>}
</span></code></pre>
<p>In addition, you can also pass in a function to return the <code>props</code> for customization purposes.</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#ffa759;">const </span><span>router </span><span style="color:#f29e74;">= new </span><span style="color:#73d0ff;">VueRouter</span><span>({
</span><span> routes</span><span style="color:#ccc9c2cc;">: </span><span>[{
</span><span> path</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">'/:id'</span><span style="color:#ccc9c2cc;">,
</span><span> component</span><span style="color:#ccc9c2cc;">: </span><span>Component</span><span style="color:#ccc9c2cc;">,
</span><span> </span><span style="color:#ffd580;">props</span><span style="color:#ccc9c2cc;">: </span><span style="color:#ffcc66;">router </span><span style="color:#ffa759;">=> </span><span>({ id</span><span style="color:#ccc9c2cc;">: </span><span>route</span><span style="color:#f29e74;">.</span><span>query</span><span style="color:#f29e74;">.</span><span>id })
</span><span> }]
</span><span>})
</span></code></pre>
<h2 id="6-two-way-binding-for-custom-components"><a class="zola-anchor" href="#6-two-way-binding-for-custom-components" aria-label="Anchor link for: 6-two-way-binding-for-custom-components">#</a>
6. Two-way Binding for Custom Components</h2>
<blockquote>
<p>Allows a custom component to customize the prop and event used when it’s used with v-model. By default, v-model on a component uses value as the prop and input as the event, but some input types such as checkboxes and radio buttons may want to use the value prop for a different purpose. Using the model option can avoid the conflict in such cases.</p>
</blockquote>
<p><code>v-model</code> is well-known for two-way binding. <code>input</code> is the default update event. The value can be updated via <code>$emit</code>. The only limitation is that the component needs an <code><input></code> tag to bind with the <code>value</code> prop.</p>
<pre data-lang="html" style="background-color:#212733;color:#ccc9c2;" class="language-html "><code class="language-html" data-lang="html"><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">my-checkbox </span><span style="color:#ffd580;">v-model</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"val"</span><span style="color:#5ccfe690;">></</span><span style="color:#73d0ff;">my-checkbox</span><span style="color:#5ccfe690;">>
</span></code></pre>
<pre data-lang="html" style="background-color:#212733;color:#ccc9c2;" class="language-html "><code class="language-html" data-lang="html"><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">template</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">input </span><span style="color:#ffd580;">type</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"checkbox" </span><span style="color:#ffd580;">:value</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"value" </span><span style="color:#ffd580;">@input</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"handleInputChange(value)" </span><span style="color:#5ccfe690;">/>
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">template</span><span style="color:#5ccfe690;">>
</span><span>
</span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">script</span><span style="color:#5ccfe690;">>
</span><span style="color:#ffa759;">export default </span><span>{
</span><span> props</span><span style="color:#ccc9c2cc;">: </span><span>{
</span><span> value</span><span style="color:#ccc9c2cc;">: </span><span>{
</span><span> type</span><span style="color:#ccc9c2cc;">: </span><span style="font-style:italic;color:#5ccfe6;">Boolean</span><span style="color:#ccc9c2cc;">,
</span><span> default</span><span style="color:#ccc9c2cc;">: </span><span style="color:#ffcc66;">false
</span><span> }
</span><span> }</span><span style="color:#ccc9c2cc;">,
</span><span> methods</span><span style="color:#ccc9c2cc;">: </span><span>{
</span><span> </span><span style="color:#ffd580;">handleInputChange </span><span>(</span><span style="color:#ffcc66;">val</span><span>) {
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(val)
</span><span> }
</span><span> }
</span><span>}
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">script</span><span style="color:#5ccfe690;">>
</span></code></pre>
<p>There’s another solution to two-way binding which is <code>sync</code> modifier. Different to <code>v-model</code>, it doesn’t require your component to have an <code><input></code> tag and bind the value to it. It only triggers <code>update:<your_prop></code> to mutate the prop via event system.</p>
<pre data-lang="html" style="background-color:#212733;color:#ccc9c2;" class="language-html "><code class="language-html" data-lang="html"><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">custom-component </span><span style="color:#ffd580;">:value.sync</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"value" </span><span style="color:#5ccfe690;">/>
</span></code></pre>
<h2 id="7-component-lifecycle-hook"><a class="zola-anchor" href="#7-component-lifecycle-hook" aria-label="Anchor link for: 7-component-lifecycle-hook">#</a>
7. Component Lifecycle Hook</h2>
<p>Normally, you can listen to child component lifecycle (e.g <code>mounted</code>) like this</p>
<pre data-lang="html" style="background-color:#212733;color:#ccc9c2;" class="language-html "><code class="language-html" data-lang="html"><span style="font-style:italic;color:#5c6773;"><!-- Child -->
</span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">script</span><span style="color:#5ccfe690;">>
</span><span style="color:#ffa759;">export default </span><span>{
</span><span> </span><span style="color:#ffd580;">mounted </span><span>() {
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">$emit</span><span>(</span><span style="color:#bae67e;">'onMounted'</span><span>)
</span><span> }
</span><span>}
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">script</span><span style="color:#5ccfe690;">>
</span></code></pre>
<pre data-lang="html" style="background-color:#212733;color:#ccc9c2;" class="language-html "><code class="language-html" data-lang="html"><span style="font-style:italic;color:#5c6773;"><!-- Parent -->
</span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">template</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">Child </span><span style="color:#ffd580;">@onMounted</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"handleOnMounted" </span><span style="color:#5ccfe690;">/>
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">template</span><span style="color:#5ccfe690;">>
</span></code></pre>
<p>There’s another simple solution to this. You can use the <code>@hook:mounted</code> instead. It’s used within the Vue internal system.</p>
<pre data-lang="html" style="background-color:#212733;color:#ccc9c2;" class="language-html "><code class="language-html" data-lang="html"><span style="font-style:italic;color:#5c6773;"><!-- Parent -->
</span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">template</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">Child </span><span style="color:#ffd580;">@hook:mounted</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"handleOnMounted" </span><span style="color:#5ccfe690;">/>
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">template</span><span style="color:#5ccfe690;">>
</span></code></pre>
<h2 id="8-event-listener-apis"><a class="zola-anchor" href="#8-event-listener-apis" aria-label="Anchor link for: 8-event-listener-apis">#</a>
8. Event Listener APIs</h2>
<p>For instance, adding a timer when the page mounted but the timer needs to be cleared when destroyed. This looks good.</p>
<p>Frankly speaking, <code>this.timer</code> only makes sense whenused within <code>beforeDestroy</code> to get the timer id. Not being mean, but fewer reative variables you have the better performance you’ll have.</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#ffa759;">export default </span><span>{
</span><span> </span><span style="color:#ffd580;">data </span><span>() {
</span><span> </span><span style="color:#ffa759;">return </span><span>{
</span><span> timer</span><span style="color:#ccc9c2cc;">: </span><span style="color:#ffcc66;">null
</span><span> }
</span><span> }</span><span style="color:#ccc9c2cc;">,
</span><span> </span><span style="color:#ffd580;">mounted </span><span>() {
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span>timer </span><span style="color:#f29e74;">= </span><span style="color:#f28779;">setInterval</span><span>(() </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(</span><span style="font-style:italic;color:#5ccfe6;">Date</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">now</span><span>())
</span><span> }</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">1000</span><span>)
</span><span> }</span><span style="color:#ccc9c2cc;">,
</span><span> </span><span style="color:#ffd580;">beforeDestroy </span><span>() {
</span><span> </span><span style="color:#f28779;">clearInterval</span><span>(</span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span>timer)
</span><span> }
</span><span>}
</span></code></pre>
<p>Make it accessable only within the lifecycle hook. Using <code>$once</code> to let go of the unnecessary stuffs.</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#ffa759;">export default </span><span>{
</span><span> </span><span style="color:#ffd580;">mounted </span><span>() {
</span><span> </span><span style="color:#ffa759;">let </span><span>timer </span><span style="color:#f29e74;">= </span><span style="color:#ffcc66;">null
</span><span>
</span><span> timer </span><span style="color:#f29e74;">= </span><span style="color:#f28779;">setInterval</span><span>(() </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(</span><span style="font-style:italic;color:#5ccfe6;">Date</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">now</span><span>())
</span><span> }</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">1000</span><span>)
</span><span>
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">$once</span><span>(</span><span style="color:#bae67e;">'hook:beforeDestroy'</span><span style="color:#ccc9c2cc;">, </span><span>() </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="color:#f28779;">clearInterval</span><span>(timer)
</span><span> })
</span><span> }
</span><span>}
</span></code></pre>
<h2 id="9-mount-components-programmatically"><a class="zola-anchor" href="#9-mount-components-programmatically" aria-label="Anchor link for: 9-mount-components-programmatically">#</a>
9. Mount Components Programmatically</h2>
<p>In some scenarios, it’s much more elegant to load a component programmatically. For instance, a popup window or modal can be open up via a global context <code>$popup()</code> or <code>$modal.open()</code>.</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#ffa759;">import </span><span>Vue </span><span style="color:#ffa759;">from </span><span style="color:#bae67e;">'vue'
</span><span style="color:#ffa759;">import </span><span>Popup </span><span style="color:#ffa759;">from </span><span style="color:#bae67e;">'./popup'
</span><span>
</span><span style="color:#ffa759;">const </span><span>PopupCtor </span><span style="color:#f29e74;">= </span><span>Vue</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">extend</span><span>(Popup)
</span><span>
</span><span style="color:#ffa759;">const </span><span>PopupIns </span><span style="color:#f29e74;">= new </span><span style="color:#73d0ff;">PopupCtr</span><span>()
</span><span>
</span><span>PopupIns</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">$mount</span><span>()
</span><span>
</span><span>document</span><span style="color:#f29e74;">.</span><span>body</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">append</span><span>(PopupIns</span><span style="color:#f29e74;">.</span><span>$el)
</span><span>
</span><span style="font-style:italic;color:#5ccfe6;">Vue</span><span style="color:#f29e74;">.</span><span>prototype</span><span style="color:#f29e74;">.</span><span>$popup </span><span style="color:#f29e74;">= </span><span>Vue</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">$popup </span><span style="color:#f29e74;">= </span><span style="color:#ffa759;">function </span><span>() {
</span><span> PopupIns</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">open</span><span>()
</span><span>}
</span></code></pre>
<p><a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/ElemeFE/element/blob/dev/packages/message-box/src/main.js">Element UI</a> implemented a well structured modal component which allows to use custom APIs to control the lifecycle of the instance. The theory is pretty much the same to what I demo above.</p>
<p>These are the 9 techniques about Vue 2.x. Hope throughout this artcile you can have a better vision of utilizing the framework.
If you think this article is great, please share it on other social networks.</p>
<p>Thank you reading!</p>
<h2 id="references"><a class="zola-anchor" href="#references" aria-label="Anchor link for: references">#</a>
References</h2>
<ul>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://vuejs.org">https://vuejs.org</a></li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://www.digitalocean.com/community/tutorials/vuejs-add-v-model-support">https://www.digitalocean.com/community/tutorials/vuejs-add-v-model-support</a></li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://vue-loader.vuejs.org/guide/scoped-css.html#child-component-root-elements">https://vue-loader.vuejs.org/guide/scoped-css.html#child-component-root-elements</a></li>
</ul>
16 CSS Pseudo Selectors Worth Bookmarking2020-07-23T00:00:00+00:002020-07-23T00:00:00+00:00https://pitayan.com/posts/css-pseudo-selectors/<link rel="stylesheet" href="PseudoStyle.css" />
<p>This article suggests using more CSS and less JS to construct the web UI. To realize this target, it’s preferrable to get farmiliar with as many as possible of CSS features. But it’s quite difficult to know all them. Another solution instead is to follow the best pratices and reduce the code quantity.</p>
<h2 id="1-first-line"><a class="zola-anchor" href="#1-first-line" aria-label="Anchor link for: 1-first-line">#</a>
1. :first-line</h2>
<p>It represents the first line of text as its name implies.</p>
<p>Browser Compatibility: https://caniuse.com/#search=%3Afirst-line</p>
<pre data-lang="css" style="background-color:#212733;color:#ccc9c2;" class="language-css "><code class="language-css" data-lang="css"><span style="color:#73d0ff;">pre</span><span>:first-line {
</span><span> </span><span style="color:#5ccfe6;">font-size</span><span style="color:#ccc9c2cc;">: </span><span style="color:#ffcc66;">24</span><span style="color:#ffa759;">px</span><span style="color:#ccc9c2cc;">;
</span><span> </span><span style="color:#5ccfe6;">color</span><span style="color:#ccc9c2cc;">: </span><span style="font-style:italic;color:#f29e74;">green</span><span style="color:#ccc9c2cc;">;
</span><span>}
</span></code></pre>
<p class="demo first-line">
::first-line
selector
</p>
<br />
<br />
<h2 id="2-first-letter"><a class="zola-anchor" href="#2-first-letter" aria-label="Anchor link for: 2-first-letter">#</a>
2. :first-letter</h2>
<p>Like <code>first-line</code>, it represents the first letter of the text.</p>
<p>Browser Compatibility: https://caniuse.com/#search=%3Afirst-line</p>
<pre data-lang="css" style="background-color:#212733;color:#ccc9c2;" class="language-css "><code class="language-css" data-lang="css"><span style="color:#73d0ff;">p</span><span>:first-letter {
</span><span> </span><span style="color:#5ccfe6;">font-size</span><span style="color:#ccc9c2cc;">: </span><span style="color:#ffcc66;">36</span><span style="color:#ffa759;">px</span><span style="color:#ccc9c2cc;">;
</span><span> </span><span style="color:#5ccfe6;">color</span><span style="color:#ccc9c2cc;">: </span><span style="font-style:italic;color:#f29e74;">green</span><span style="color:#ccc9c2cc;">;
</span><span>}
</span></code></pre>
<p class="demo first-letter">
::first-letter selector
</p>
<h2 id="3-selection"><a class="zola-anchor" href="#3-selection" aria-label="Anchor link for: 3-selection">#</a>
3. ::selection</h2>
<p>The <code>selection</code> selector means those text you selected and highlighted. The color is <b class="bg-blue-300">blue</b> by default for most of the browsers.</p>
<p>Browser Compatibility: https://caniuse.com/#search=%3Aselection</p>
<pre data-lang="css" style="background-color:#212733;color:#ccc9c2;" class="language-css "><code class="language-css" data-lang="css"><span style="color:#73d0ff;">p</span><span>::selection {
</span><span> </span><span style="color:#5ccfe6;">background</span><span style="color:#ccc9c2cc;">: </span><span style="font-style:italic;color:#f29e74;">orange</span><span style="color:#ccc9c2cc;">;
</span><span>}
</span></code></pre>
<p class="demo selection">
::selection selector
</p>
<h2 id="4-root"><a class="zola-anchor" href="#4-root" aria-label="Anchor link for: 4-root">#</a>
4. :root</h2>
<p>The <code>root</code> selector represents the root element of a document. In <code>HTML</code>, the root element is <code><html></code> element. In <code>RSS</code>, the root element is <code><rss></code> element.</p>
<p>In most of the modern browsers, it’s used for storing custom style properties. Use <code>var()</code> as a getter for the stored values.</p>
<p>Browser Compatibility: https://caniuse.com/#search=%3Aroot</p>
<pre data-lang="css" style="background-color:#212733;color:#ccc9c2;" class="language-css "><code class="language-css" data-lang="css"><span>:root {
</span><span> </span><span style="font-style:italic;color:#5c6773;">--</span><span style="font-style:italic;color:#5ccfe6;">bg-color</span><span style="color:#ccc9c2cc;">: </span><span style="font-style:italic;color:#f29e74;">lightgray</span><span style="color:#ccc9c2cc;">;
</span><span> </span><span style="font-style:italic;color:#5c6773;">--</span><span style="font-style:italic;color:#5ccfe6;">text-color</span><span style="color:#ccc9c2cc;">: </span><span style="font-style:italic;color:#f29e74;">green</span><span style="color:#ccc9c2cc;">;
</span><span>}
</span><span>
</span><span style="color:#73d0ff;">p </span><span>{
</span><span> </span><span style="color:#5ccfe6;">background</span><span style="color:#ccc9c2cc;">: </span><span style="color:#f28779;">var</span><span>(</span><span style="font-style:italic;color:#5c6773;">--</span><span style="font-style:italic;color:#5ccfe6;">bg-color</span><span>)</span><span style="color:#ccc9c2cc;">;
</span><span> </span><span style="color:#5ccfe6;">color</span><span style="color:#ccc9c2cc;">: </span><span style="color:#f28779;">var</span><span>(</span><span style="font-style:italic;color:#5c6773;">--</span><span style="font-style:italic;color:#5ccfe6;">text-color</span><span>)</span><span style="color:#ccc9c2cc;">;
</span><span>}
</span></code></pre>
<p class="demo root">
::root selector
</p>
<h2 id="5-empty"><a class="zola-anchor" href="#5-empty" aria-label="Anchor link for: 5-empty">#</a>
5. :empty</h2>
<p>The <code>empty</code> represents an empy element. An element without <code>space</code> <code>visible content</code> or <code>children nodes</code> is an <code>empty</code> element.</p>
<p>Browser Compatibility: https://caniuse.com/#search=%3Aempty</p>
<pre data-lang="css" style="background-color:#212733;color:#ccc9c2;" class="language-css "><code class="language-css" data-lang="css"><span style="color:#73d0ff;">p</span><span>:empty {
</span><span> </span><span style="color:#5ccfe6;">border</span><span style="color:#ccc9c2cc;">: </span><span style="color:#ffcc66;">1</span><span style="color:#ffa759;">px </span><span style="font-style:italic;color:#f29e74;">solid black</span><span style="color:#ccc9c2cc;">;
</span><span> </span><span style="color:#5ccfe6;">height</span><span style="color:#ccc9c2cc;">: </span><span style="color:#ffcc66;">16</span><span style="color:#ffa759;">px</span><span style="color:#ccc9c2cc;">;
</span><span>}
</span></code></pre>
<pre data-lang="html" style="background-color:#212733;color:#ccc9c2;" class="language-html "><code class="language-html" data-lang="html"><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">p</span><span style="color:#5ccfe690;">></</span><span style="color:#73d0ff;">p</span><span style="color:#5ccfe690;">>
</span><span>
</span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">p</span><span style="color:#5ccfe690;">> </</span><span style="color:#73d0ff;">p</span><span style="color:#5ccfe690;">>
</span><span>
</span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">p</span><span style="color:#5ccfe690;">><</span><span style="color:#73d0ff;">div </span><span style="color:#ffd580;">style</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"</span><span style="color:#5ccfe6;">display</span><span style="color:#ccc9c2cc;">:</span><span style="font-style:italic;color:#f29e74;">hidden</span><span style="color:#ccc9c2cc;">;</span><span style="color:#bae67e;">"</span><span style="color:#5ccfe690;">></</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">></</span><span style="color:#73d0ff;">p</span><span style="color:#5ccfe690;">>
</span></code></pre>
<p class="demo empty"></p>
<h2 id="6-only-child"><a class="zola-anchor" href="#6-only-child" aria-label="Anchor link for: 6-only-child">#</a>
6. :only-child</h2>
<p>The <code>only-child</code> represents the child node which the parent has only one child node.</p>
<p>Browser Compatibility: https://caniuse.com/#search=%3Aonly-child</p>
<pre data-lang="css" style="background-color:#212733;color:#ccc9c2;" class="language-css "><code class="language-css" data-lang="css"><span style="color:#73d0ff;">div</span><span>:only-child {
</span><span> </span><span style="color:#5ccfe6;">background</span><span style="color:#ccc9c2cc;">: </span><span style="font-style:italic;color:#f29e74;">lightgray</span><span style="color:#ccc9c2cc;">;
</span><span>}
</span></code></pre>
<pre data-lang="html" style="background-color:#212733;color:#ccc9c2;" class="language-html "><code class="language-html" data-lang="html"><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">p</span><span style="color:#5ccfe690;">></span><span>only child</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">p</span><span style="color:#5ccfe690;">>
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span></code></pre>
<div>
<p class="demo only-child">only child</p>
</div>
<h2 id="7-first-of-type"><a class="zola-anchor" href="#7-first-of-type" aria-label="Anchor link for: 7-first-of-type">#</a>
7. :first-of-type</h2>
<p>The <code>first-of-type</code> represents the node that is the first sibling of its type in the list of children of its parent element.</p>
<p>Browser Compatibility: https://caniuse.com/#search=%3Afirst-of-type</p>
<pre data-lang="css" style="background-color:#212733;color:#ccc9c2;" class="language-css "><code class="language-css" data-lang="css"><span style="color:#73d0ff;">p</span><span>:first-of-type {
</span><span> </span><span style="color:#5ccfe6;">background</span><span style="color:#ccc9c2cc;">: </span><span style="font-style:italic;color:#f29e74;">lightgray</span><span style="color:#ccc9c2cc;">;
</span><span>}
</span></code></pre>
<pre data-lang="html" style="background-color:#212733;color:#ccc9c2;" class="language-html "><code class="language-html" data-lang="html"><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">></span><span>1</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">p</span><span style="color:#5ccfe690;">></span><span>2</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">p</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">p</span><span style="color:#5ccfe690;">></span><span>3</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">p</span><span style="color:#5ccfe690;">>
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span></code></pre>
<div class="mark-w-md">
<div>1</div>
<p class="demo first-of-type">2</p>
<p>3</p>
</div>
<h2 id="8-last-of-type"><a class="zola-anchor" href="#8-last-of-type" aria-label="Anchor link for: 8-last-of-type">#</a>
8. :last-of-type</h2>
<p>But of <code>first-of-type</code>, <code>last-of-type</code> represents the last.</p>
<p>Browser Compatibility: https://caniuse.com/#search=%3Alast-of-type</p>
<pre data-lang="css" style="background-color:#212733;color:#ccc9c2;" class="language-css "><code class="language-css" data-lang="css"><span style="color:#73d0ff;">p</span><span>:last-of-type {
</span><span> </span><span style="color:#5ccfe6;">background</span><span style="color:#ccc9c2cc;">: </span><span style="font-style:italic;color:#f29e74;">lightgray</span><span style="color:#ccc9c2cc;">;
</span><span>}
</span></code></pre>
<pre data-lang="html" style="background-color:#212733;color:#ccc9c2;" class="language-html "><code class="language-html" data-lang="html"><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">></span><span>1</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">p</span><span style="color:#5ccfe690;">></span><span>2</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">p</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">p</span><span style="color:#5ccfe690;">></span><span>3</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">p</span><span style="color:#5ccfe690;">>
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span></code></pre>
<div class="mark-w-md">
<div>1</div>
<p>2</p>
<p class="demo last-of-type">3</p>
</div>
<h2 id="9-nth-of-type"><a class="zola-anchor" href="#9-nth-of-type" aria-label="Anchor link for: 9-nth-of-type">#</a>
9. :nth-of-type()</h2>
<p><code>first-of-type</code> and <code>last-of-type</code> only represents the first or last element. With <code>nth-of-type</code>, you can select the node using its index. Remember CSS indexes start from <strong>1</strong>.</p>
<p>Browser Compatibility: https://caniuse.com/#search=%3Anth-of-type</p>
<pre data-lang="css" style="background-color:#212733;color:#ccc9c2;" class="language-css "><code class="language-css" data-lang="css"><span>P:nth-of-type(</span><span style="color:#ffcc66;">2</span><span>) {
</span><span> </span><span style="color:#5ccfe6;">background</span><span style="color:#ccc9c2cc;">: </span><span style="font-style:italic;color:#f29e74;">lightgray</span><span style="color:#ccc9c2cc;">;
</span><span>}
</span></code></pre>
<pre data-lang="html" style="background-color:#212733;color:#ccc9c2;" class="language-html "><code class="language-html" data-lang="html"><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">></span><span>1</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">p</span><span style="color:#5ccfe690;">></span><span>2</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">p</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">p</span><span style="color:#5ccfe690;">></span><span>3</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">p</span><span style="color:#5ccfe690;">></span><span style="font-style:italic;color:#5c6773;"><!-- this one -->
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span></code></pre>
<div class="mark-w-md">
<div>1</div>
<p>2</p>
<p class="demo nth-of-type">3</p>
</div>
<h2 id="10-nth-last-of-type"><a class="zola-anchor" href="#10-nth-last-of-type" aria-label="Anchor link for: 10-nth-last-of-type">#</a>
10. :nth-last-of-type()</h2>
<p>Different form <code>nth-of-type</code>, <code>nth-last-of-type</code> counts from last one in the children list.</p>
<p>Browser Compatibility: https://caniuse.com/#search=%3Alast-nth-of-type</p>
<pre data-lang="css" style="background-color:#212733;color:#ccc9c2;" class="language-css "><code class="language-css" data-lang="css"><span>P:nth-last-of-type(</span><span style="color:#ffcc66;">2</span><span>) {
</span><span> </span><span style="color:#5ccfe6;">background</span><span style="color:#ccc9c2cc;">: </span><span style="font-style:italic;color:#f29e74;">lightgray</span><span style="color:#ccc9c2cc;">;
</span><span>}
</span></code></pre>
<pre data-lang="html" style="background-color:#212733;color:#ccc9c2;" class="language-html "><code class="language-html" data-lang="html"><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">></span><span>1</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">p</span><span style="color:#5ccfe690;">></span><span>2</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">p</span><span style="color:#5ccfe690;">></span><span style="font-style:italic;color:#5c6773;"><!-- this one -->
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">p</span><span style="color:#5ccfe690;">></span><span>3</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">p</span><span style="color:#5ccfe690;">>
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span></code></pre>
<div class="mark-w-md">
<div>1</div>
<p class="demo nth-last-of-type">2</p>
<p>3</p>
</div>
<h2 id="11-link"><a class="zola-anchor" href="#11-link" aria-label="Anchor link for: 11-link">#</a>
11. :link</h2>
<p>The <code>link</code> represents the unvisited <code><a></code> tag with href.</p>
<p>Browser Compatibility: https://caniuse.com/#search=%3Alink</p>
<pre data-lang="css" style="background-color:#212733;color:#ccc9c2;" class="language-css "><code class="language-css" data-lang="css"><span style="color:#73d0ff;">a</span><span>:link {
</span><span> </span><span style="color:#5ccfe6;">color</span><span style="color:#ccc9c2cc;">: </span><span style="font-style:italic;color:#f29e74;">green</span><span style="color:#ccc9c2cc;">;
</span><span>}
</span></code></pre>
<p>
<a class="demo unvisited-link" href="#11-link">:link</a>
</p>
<h2 id="12-valid"><a class="zola-anchor" href="#12-valid" aria-label="Anchor link for: 12-valid">#</a>
12. :valid</h2>
<p>It’s used in a form with validations. The <code>valid</code> represents the node that passed the validation.</p>
<p>Browser Compatibility: https://caniuse.com/#search=%3Avalid</p>
<pre data-lang="css" style="background-color:#212733;color:#ccc9c2;" class="language-css "><code class="language-css" data-lang="css"><span style="color:#73d0ff;">input</span><span>:valid {
</span><span> </span><span style="color:#5ccfe6;">border</span><span style="color:#ccc9c2cc;">: </span><span style="color:#ffcc66;">1</span><span style="color:#ffa759;">px </span><span style="font-style:italic;color:#f29e74;">solid green</span><span style="color:#ccc9c2cc;">;
</span><span>}
</span></code></pre>
<div class="mark-w-md">
<input class="demo valid mark-w-md" valid type="text" value=":valid" />
</div>
<br />
<br />
<br />
<h2 id="13-invalid"><a class="zola-anchor" href="#13-invalid" aria-label="Anchor link for: 13-invalid">#</a>
13. :invalid</h2>
<p>So to <code>valid</code>, <code>invalid</code> represents the node that didn’t pass the validation.</p>
<p>Browser Compatibility: https://caniuse.com/#search=%3Ainvalid</p>
<pre data-lang="css" style="background-color:#212733;color:#ccc9c2;" class="language-css "><code class="language-css" data-lang="css"><span style="color:#73d0ff;">input</span><span>:invalid {
</span><span> </span><span style="color:#5ccfe6;">border</span><span style="color:#ccc9c2cc;">: </span><span style="color:#ffcc66;">1</span><span style="color:#ffa759;">px </span><span style="font-style:italic;color:#f29e74;">solid red</span><span style="color:#ccc9c2cc;">;
</span><span>}
</span></code></pre>
<div class="mark-w-md">
<input class="demo invalid mark-w-md" type="email" value=":invalid" />
</div>
<br />
<br />
<br />
<h2 id="14-lang"><a class="zola-anchor" href="#14-lang" aria-label="Anchor link for: 14-lang">#</a>
14. :lang()</h2>
<p>The <code>lang</code> represents the node with specified language.</p>
<p>Browser Compatibility: https://caniuse.com/#search=%3Alang</p>
<pre data-lang="css" style="background-color:#212733;color:#ccc9c2;" class="language-css "><code class="language-css" data-lang="css"><span style="color:#73d0ff;">p</span><span>:lang(</span><span style="color:#bae67e;">ja</span><span>) {
</span><span> </span><span style="color:#5ccfe6;">color</span><span style="color:#ccc9c2cc;">: </span><span style="font-style:italic;color:#f29e74;">green</span><span style="color:#ccc9c2cc;">;
</span><span>}
</span><span>
</span><span style="font-style:italic;color:#5c6773;">/* or */
</span><span>
</span><span style="color:#73d0ff;">p</span><span>[</span><span style="color:#ffd580;">lang</span><span style="color:#f29e74;">|=</span><span style="color:#bae67e;">"ja"</span><span>] {
</span><span> </span><span style="color:#5ccfe6;">color</span><span style="color:#ccc9c2cc;">: </span><span style="font-style:italic;color:#f29e74;">green</span><span style="color:#ccc9c2cc;">;
</span><span>}
</span></code></pre>
<p class="demo lang" lang="ja">こんにちは</p>
<h2 id="15-not"><a class="zola-anchor" href="#15-not" aria-label="Anchor link for: 15-not">#</a>
15. :not()</h2>
<p>The <code>not</code> takes a simple selector as an argument. It represents an element that is not represented by its argument.</p>
<p>Browser Compatibility: https://caniuse.com/#search=%3Anot</p>
<pre data-lang="css" style="background-color:#212733;color:#ccc9c2;" class="language-css "><code class="language-css" data-lang="css"><span style="color:#73d0ff;">div </span><span>:not(</span><span style="color:#73d0ff;">p</span><span>) {
</span><span> </span><span style="color:#5ccfe6;">background</span><span style="color:#ccc9c2cc;">: </span><span style="font-style:italic;color:#f29e74;">lightgray</span><span style="color:#ccc9c2cc;">;
</span><span>}
</span></code></pre>
<pre data-lang="html" style="background-color:#212733;color:#ccc9c2;" class="language-html "><code class="language-html" data-lang="html"><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">></span><span>1</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">p</span><span style="color:#5ccfe690;">></span><span>2</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">p</span><span style="color:#5ccfe690;">></span><span style="font-style:italic;color:#5c6773;"><!-- p tag is not taking effect -->
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">></span><span>3</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span></code></pre>
<div class="demo not mark-w-md">
<div>1</div>
<p>2</p>
<div>3</div>
</div>
<p>These are the 16 pseudo selectors. Hope you’ve already got farmiliar with these selectors. Actually, there are a lot more pseudo selectors that are non-standards. So I neglected them. If you think this article is great, please share it on other social networks.</p>
<p>Thank you reading!</p>
<h2 id="references"><a class="zola-anchor" href="#references" aria-label="Anchor link for: references">#</a>
References</h2>
<ul>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://www.w3schools.com/cssref/css_selectors.asp">https://www.w3schools.com/cssref/css_selectors.asp</a></li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors">https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors</a></li>
</ul>
I created a sexy voice assistant in 180 lines of code2020-07-22T00:00:00+00:002020-07-22T00:00:00+00:00https://pitayan.com/posts/voice-assistant/<p>Just a few days ago, I saw someone (<a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/nhudinhtuan">nhudinhtuan</a>) made a voice recognition helper with Chrome experimental <a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Speech_API">WebSpeechAPI</a>. The helper app is going to listen to human voice and turn it into plain text. After all that it will open up a new tab page of Google Search.</p>
<p>Give it try via the link <a rel="noopener nofollow noreferrer" target="_blank" href="https://nhudinhtuan.github.io/mysiri/">here</a>. (Only supports Chrome / new Edge browser)</p>
<p><img src="./images/my_siri.png" alt="webspeechapi siri" /></p>
<p>I think this is quite fun to make some interesting project with API that is hardly ever used. So I decided to make an upgraded version of this voice assistant. My version is nothing special but it’s implemented with <a rel="noopener nofollow noreferrer" target="_blank" href="https://vuejs.org/">Vue.js</a> and its new <a rel="noopener nofollow noreferrer" target="_blank" href="https://composition-api.vuejs.org/">composition api</a>.</p>
<p>In this article, I’ll walk you through the process of creating this sexy voice assistant.</p>
<p><strong>TLDR;</strong></p>
<h2 id="demo-my-voice-assistant"><a class="zola-anchor" href="#demo-my-voice-assistant" aria-label="Anchor link for: demo-my-voice-assistant">#</a>
Demo My Voice Assistant</h2>
<blockquote>
<p>Talk is cheap, let me show you my work</p>
</blockquote>
<p>Hold the button “Hold to Listen” and try with some of the following sentenses (Only works on Chrome/Edge79+)</p>
<ul>
<li><em>Hello!</em></li>
<li><em>Hi!</em></li>
<li><em>What is your name?</em></li>
<li><em>I love you</em></li>
<li><em>Google search: <something_you_want_to_query></em></li>
<li><em>…naked…</em></li>
</ul>
<div class="gatsby-resp-iframe-wrapper mark-w-2l w-full mb-8" style="padding-bottom: 56.5%; position: relative; height: 0px; overflow: hidden;">
<iframe
style="position: absolute; top: 0px; left: 0px; width: 100%; height: 100%;"
scrolling="no"
title="Voice Assistant"
src="https://codepen.io/daiyanze/embed/zYpJwmN?default-tab=result"
frameborder="no"
loading="lazy"
allowtransparency="true"
allowfullscreen="true"
allow="microphone *">
See the Pen <a href="https://codepen.io/daiyanze/pen/zYpJwmN">
Voice Assistant</a> by Yanze Dai (<a href="https://codepen.io/daiyanze">@daiyanze</a>)
on <a href="https://codepen.io">CodePen</a>.
</iframe>
</div>
<p>You can also change the speaker voice from the select list on the right side. Currently, there are <strong>60</strong> more voices supported on my Mac. Personally, I think <strong>Karen</strong> was one of the Smoothest robot English speaker.</p>
<p>I guess that you must have tried speaking some other stuffs to the voice assistant. But I’m really sorry to tell you that it could only reply you properly when your speech matches the above examples. Otherwise, it cannot understand. (It’s a prototype though)</p>
<h2 id="how-it-works"><a class="zola-anchor" href="#how-it-works" aria-label="Anchor link for: how-it-works">#</a>
How it works</h2>
<p>Let’s take a quick look at the voice handling APIs used in the voice assistant. These are the key figures of making the voice assistant “sexy”.</p>
<h4 id="apis"><a class="zola-anchor" href="#apis" aria-label="Anchor link for: apis">#</a>
APIs</h4>
<h5 id="speechrecognition-recognize-your-voice-and-turn-it-into-text"><a class="zola-anchor" href="#speechrecognition-recognize-your-voice-and-turn-it-into-text" aria-label="Anchor link for: speechrecognition-recognize-your-voice-and-turn-it-into-text">#</a>
<a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition">SpeechRecognition</a>: Recognize your voice and turn it into text</h5>
<pre data-lang="javascript" style="background-color:#212733;color:#ccc9c2;" class="language-javascript "><code class="language-javascript" data-lang="javascript"><span style="font-style:italic;color:#5c6773;">// Needs the `webkit` prefix due to lacking of browsers support
</span><span style="color:#ffa759;">var </span><span>recognition </span><span style="color:#f29e74;">= </span><span style="color:#ffd580;">webkitSpeechRecognition</span><span>()
</span><span>
</span><span style="font-style:italic;color:#5c6773;">// Set to `true` to listen to voices
</span><span style="font-style:italic;color:#5c6773;">// continuously and return multiple results.
</span><span>recognition</span><span style="color:#f29e74;">.</span><span>continuous </span><span style="color:#f29e74;">= </span><span style="color:#ffcc66;">true</span><span style="color:#ccc9c2cc;">;
</span><span>
</span><span style="font-style:italic;color:#5c6773;">// Start recognition
</span><span>recognition</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">start</span><span>()
</span><span>
</span><span style="font-style:italic;color:#5c6773;">// Stop recognition
</span><span>recognition</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">stop</span><span>()
</span><span>
</span><span style="font-style:italic;color:#5c6773;">// Handle the result
</span><span>recognition</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">onresult </span><span style="color:#f29e74;">= </span><span style="color:#ffa759;">function </span><span>(</span><span style="color:#ffcc66;">event</span><span>) {
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(event)
</span><span>}
</span></code></pre>
<h5 id="speechsynthesis-get-all-supported-voices-and-speak-out-loud"><a class="zola-anchor" href="#speechsynthesis-get-all-supported-voices-and-speak-out-loud" aria-label="Anchor link for: speechsynthesis-get-all-supported-voices-and-speak-out-loud">#</a>
<a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesis">SpeechSynthesis</a>: Get all supported voices and speak out loud</h5>
<pre data-lang="javascript" style="background-color:#212733;color:#ccc9c2;" class="language-javascript "><code class="language-javascript" data-lang="javascript"><span style="color:#ffa759;">var </span><span>Synth </span><span style="color:#f29e74;">= </span><span>window</span><span style="color:#f29e74;">.</span><span>speechSynthesis
</span><span>
</span><span style="font-style:italic;color:#5c6773;">// Speak out loud
</span><span>Synth</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">speak</span><span>(utterThis)
</span><span>
</span><span style="font-style:italic;color:#5c6773;">// Cancel speaking
</span><span>Synth</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">cancel</span><span>()
</span><span>
</span><span style="font-style:italic;color:#5c6773;">// Pause speaking
</span><span>Synth</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">pause</span><span>()
</span><span>
</span><span style="font-style:italic;color:#5c6773;">// Resume speaking
</span><span>Synth</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">resume</span><span>()
</span><span>
</span><span style="font-style:italic;color:#5c6773;">// List out voices supported by the OS
</span><span>Synth</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">getVoices</span><span>()
</span></code></pre>
<h5 id="speechsynthesisutterance-speech-request-object"><a class="zola-anchor" href="#speechsynthesisutterance-speech-request-object" aria-label="Anchor link for: speechsynthesisutterance-speech-request-object">#</a>
<a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesisUtterance">SpeechSynthesisUtterance</a>: Speech request Object</h5>
<pre data-lang="javascript" style="background-color:#212733;color:#ccc9c2;" class="language-javascript "><code class="language-javascript" data-lang="javascript"><span style="color:#ffa759;">var </span><span>utterThis </span><span style="color:#f29e74;">= new </span><span style="color:#73d0ff;">SpeechSynthesisUtterance</span><span>()
</span><span>
</span><span style="font-style:italic;color:#5c6773;">// Utterance only has properties
</span><span style="font-style:italic;color:#5c6773;">// The spoken language
</span><span>utterThis</span><span style="color:#f29e74;">.</span><span>lang
</span><span style="font-style:italic;color:#5c6773;">// The tone or the pitch or voice, ranges from 0 to 2.
</span><span style="font-style:italic;color:#5c6773;">// The higher the younger :D
</span><span>utterThis</span><span style="color:#f29e74;">.</span><span>pitch
</span><span style="font-style:italic;color:#5c6773;">// Speaking speed, ranges from 0.1 to 10
</span><span>utterThis</span><span style="color:#f29e74;">.</span><span>rate
</span><span style="font-style:italic;color:#5c6773;">// The content to be spoken
</span><span>utterThis</span><span style="color:#f29e74;">.</span><span>text
</span><span style="font-style:italic;color:#5c6773;">// Speaker's voice. It's an Object
</span><span>utterThis</span><span style="color:#f29e74;">.</span><span>voice
</span><span style="font-style:italic;color:#5c6773;">// Speaker's volume
</span><span>utterThis</span><span style="color:#f29e74;">.</span><span>volume
</span></code></pre>
<p>Actually these APIs are created for people who need to use voices to control web pages other than providing as a chat bot. In another word, when your team has completed mosted of the robust features. Then it’d be a good timing to level up the accessibility of your web app to help those people who need voice controls.</p>
<h4 id="usage-examples"><a class="zola-anchor" href="#usage-examples" aria-label="Anchor link for: usage-examples">#</a>
Usage Examples</h4>
<p>You can give it a try in the Chrome console by <strong>copying</strong> & <strong>pasting</strong> the code below.</p>
<ol>
<li>
<p>Voice speaker: The following code will say “hello world” out loud.</p>
<pre data-lang="javascript" style="background-color:#212733;color:#ccc9c2;" class="language-javascript "><code class="language-javascript" data-lang="javascript"><span style="color:#ffa759;">const </span><span>utterThis </span><span style="color:#f29e74;">= new </span><span style="color:#73d0ff;">SpeechSynthesisUtterance</span><span>(</span><span style="color:#bae67e;">"Hello world"</span><span>)
</span><span>
</span><span>speechSynthesis</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">speak</span><span>(utterThis)
</span></code></pre>
</li>
<li>
<p>Voice to text: The following code will turn your voice into text.</p>
<pre data-lang="javascript" style="background-color:#212733;color:#ccc9c2;" class="language-javascript "><code class="language-javascript" data-lang="javascript"><span style="color:#ffa759;">const </span><span>SpeechRecognition </span><span style="color:#f29e74;">= new </span><span style="color:#73d0ff;">webkitSpeechRecognition</span><span>()
</span><span>
</span><span style="font-style:italic;color:#5c6773;">// record the voice for 5 seconds
</span><span>SpeechRecognition</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">start</span><span>()
</span><span style="color:#f28779;">setTimeout</span><span>(() </span><span style="color:#ffa759;">=> </span><span>{
</span><span> SpeechRecognition</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">stop</span><span>()
</span><span>}</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">5000</span><span>)
</span><span>
</span><span>SpeechRecognition</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">onresult </span><span style="color:#f29e74;">= </span><span style="color:#ffcc66;">event </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="color:#ffa759;">const </span><span>result </span><span style="color:#f29e74;">= </span><span>event</span><span style="color:#f29e74;">.</span><span>results[event</span><span style="color:#f29e74;">.</span><span>results</span><span style="color:#f29e74;">.</span><span>length </span><span style="color:#f29e74;">- </span><span style="color:#ffcc66;">1</span><span>]
</span><span> </span><span style="color:#ffa759;">if </span><span>(result</span><span style="color:#f29e74;">.</span><span>isFinal) {
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(result[</span><span style="color:#ffcc66;">0</span><span>]</span><span style="color:#f29e74;">.</span><span>transcript)
</span><span> }
</span><span>}
</span></code></pre>
<p>(Here is the result)
<img src="./images/recognition.png" alt="voice recognition webkitSpeechRecognition speechSynthesis" /></p>
</li>
</ol>
<h2 id="the-design"><a class="zola-anchor" href="#the-design" aria-label="Anchor link for: the-design">#</a>
The Design</h2>
<p>After some practices over these <a href="https://pitayan.com/posts/voice-assistant/#apis">APIs</a>, I was able to create a voice repeater with the <a href="https://pitayan.com/posts/voice-assistant/#usage-examples">example codes</a> above. But we need another text processor to receive my messages and return the proper response.</p>
<h4 id="workflow"><a class="zola-anchor" href="#workflow" aria-label="Anchor link for: workflow">#</a>
Workflow</h4>
<p><img src="./images/voice_assistant_design.jpg" alt="voice assistant design workflow" /></p>
<p>Quite simple, isn’t it?</p>
<p>Workflow in a brief word, “<strong>speak</strong>” “<strong>process</strong>” and “<strong>reply</strong>”.</p>
<h4 id="controls"><a class="zola-anchor" href="#controls" aria-label="Anchor link for: controls">#</a>
Controls</h4>
<p>The design was simple and easy to start with. Now we need some triggers that enables/disables listening to voices. So here comes with two “buttons”:</p>
<ul>
<li><button class="text-sm font-semibold text-white px-4 py-px bg-gray-600 rounded-sm">Hold to Listen</button> Hold this button to record voices</li>
<li><button class="text-sm font-semibold text-white px-4 py-px bg-gray-600 rounded-sm">Reset</button> Click this button to cancel everything and reset all states</li>
</ul>
<p>I hope that I could also make the voices “visible” in order to have an intuitive history of my chats. This means I need a <strong>message-box</strong> like this:</p>
<pre class="border border-solid border-gray-300 p-2 leading-6 rounded-md">
Me: Hello!
You: Hi!
</pre>
<p>Okay… I’ve done all of the imaginations for my voice assistant. Now we can start coding.</p>
<h2 id="dive-into-coding"><a class="zola-anchor" href="#dive-into-coding" aria-label="Anchor link for: dive-into-coding">#</a>
Dive Into Coding…</h2>
<h4 id="html-template"><a class="zola-anchor" href="#html-template" aria-label="Anchor link for: html-template">#</a>
HTML Template</h4>
<p>I’m using <a rel="noopener nofollow noreferrer" target="_blank" href="https://vuejs.org/">Vue.js</a> as my frontend framework so that the appearance of the voice assitant can be prototyped very easily. In terms with the design I made, I figured out a very rudimentary template like the following.</p>
<pre data-lang="html" style="background-color:#212733;color:#ccc9c2;" class="language-html "><code class="language-html" data-lang="html"><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">template</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">pre</span><span style="color:#5ccfe690;">></</span><span style="color:#73d0ff;">pre</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">button</span><span style="color:#5ccfe690;">></span><span>Hold to Listen</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">button</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">button</span><span style="color:#5ccfe690;">></span><span>Reset</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">button</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">template</span><span style="color:#5ccfe690;">>
</span></code></pre>
<h4 id="controller"><a class="zola-anchor" href="#controller" aria-label="Anchor link for: controller">#</a>
Controller</h4>
<p>In order to enable the controls, I added some functions and props to the template. Now the template looks like:</p>
<pre data-lang="html" style="background-color:#212733;color:#ccc9c2;" class="language-html "><code class="language-html" data-lang="html"><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">template</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">pre </span><span style="color:#ffd580;">class</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"message-box" </span><span style="color:#ffd580;">v-html</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"context"</span><span style="color:#5ccfe690;">></</span><span style="color:#73d0ff;">pre</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">button
</span><span> </span><span style="color:#ffd580;">class</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"button-control"
</span><span> </span><span style="color:#ffd580;">@mousedown</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"listen(true)"
</span><span> </span><span style="color:#ffd580;">@mouseup</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"listen(false)"</span><span style="color:#5ccfe690;">>
</span><span> Hold to Listen
</span><span> </span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">button</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">button </span><span style="color:#ffd580;">class</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"button-control" </span><span style="color:#ffd580;">@click</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"reset"</span><span style="color:#5ccfe690;">></span><span>Reset</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">button</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">template</span><span style="color:#5ccfe690;">>
</span></code></pre>
<p>As for the logics part, I used the <a rel="noopener nofollow noreferrer" target="_blank" href="https://composition-api.vuejs.org/">vue-composition-api</a> to return the properties for the template. These codes below are enough to let go of the errors meanwhile enable the buttons. Right now, it’s just a skeleton.</p>
<pre data-lang="html" style="background-color:#212733;color:#ccc9c2;" class="language-html "><code class="language-html" data-lang="html"><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">script</span><span style="color:#5ccfe690;">>
</span><span style="color:#ffa759;">import </span><span>{ toRefs</span><span style="color:#ccc9c2cc;">, </span><span>reactive } </span><span style="color:#ffa759;">from </span><span style="color:#bae67e;">'vue-composition-api'
</span><span>
</span><span style="color:#ffa759;">export default </span><span>{
</span><span> </span><span style="color:#ffd580;">setup </span><span>() {
</span><span> </span><span style="color:#ffa759;">const </span><span>state </span><span style="color:#f29e74;">= </span><span style="color:#ffd580;">reactive</span><span>({
</span><span> context</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">'Hello!'
</span><span> })
</span><span>
</span><span> </span><span style="color:#ffa759;">const </span><span>listen </span><span style="color:#f29e74;">= </span><span style="color:#ffcc66;">start </span><span style="color:#ffa759;">=> </span><span>{}
</span><span>
</span><span> </span><span style="color:#ffa759;">const </span><span>reset </span><span style="color:#f29e74;">= </span><span>() </span><span style="color:#ffa759;">=> </span><span>{}
</span><span>
</span><span> </span><span style="color:#ffa759;">return </span><span>{
</span><span> </span><span style="color:#f29e74;">...</span><span style="color:#ffd580;">toRefs</span><span>(state)</span><span style="color:#ccc9c2cc;">,
</span><span> listen
</span><span> }
</span><span> }
</span><span>}
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">script</span><span style="color:#5ccfe690;">>
</span></code></pre>
<h4 id="recognize-the-voice"><a class="zola-anchor" href="#recognize-the-voice" aria-label="Anchor link for: recognize-the-voice">#</a>
Recognize the Voice</h4>
<p>The target is quite clear:</p>
<blockquote>
<p>To let browser convert my voice to text.</p>
</blockquote>
<p>I’ll use the <code>listen</code> function to control the voice recongnition. And setup a callback function to handle the recognition result.</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#ffa759;">const </span><span>SpeechRecognition </span><span style="color:#f29e74;">= </span><span>window</span><span style="color:#f29e74;">.</span><span>webkitSpeechRecognition </span><span style="color:#f29e74;">&& new </span><span style="color:#73d0ff;">window</span><span style="color:#f29e74;">.</span><span style="color:#73d0ff;">webkitSpeechRecognition</span><span>()
</span><span>SpeechRecognition </span><span style="color:#f29e74;">&& </span><span>(SpeechRecognition</span><span style="color:#f29e74;">.</span><span>interimResults </span><span style="color:#f29e74;">= </span><span style="color:#ffcc66;">true</span><span>)
</span><span>
</span><span style="color:#ffa759;">const </span><span style="color:#ffd580;">listen </span><span style="color:#f29e74;">= </span><span style="color:#ffcc66;">start </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="color:#ffa759;">if </span><span>(</span><span style="color:#f29e74;">!</span><span>SpeechRecognition) </span><span style="color:#ffa759;">return
</span><span>
</span><span> </span><span style="color:#ffa759;">if </span><span>(start) {
</span><span> SpeechRecognition</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">start</span><span>()
</span><span> } </span><span style="color:#ffa759;">else </span><span>{
</span><span> SpeechRecognition</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">stop</span><span>()
</span><span> }
</span><span>}
</span><span>
</span><span style="color:#ffa759;">const </span><span style="color:#ffd580;">reset </span><span style="color:#f29e74;">= </span><span>() </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="color:#ffa759;">if </span><span>(</span><span style="color:#f29e74;">!</span><span>SpeechRecognition) </span><span style="color:#ffa759;">return
</span><span>
</span><span> state</span><span style="color:#f29e74;">.</span><span>context </span><span style="color:#f29e74;">= </span><span style="color:#bae67e;">'<b>' </span><span style="color:#f29e74;">+ </span><span>state</span><span style="color:#f29e74;">.</span><span>name </span><span style="color:#f29e74;">+ </span><span style="color:#bae67e;">'</b>' </span><span style="color:#f29e74;">+ </span><span style="color:#bae67e;">': Hi, my hero!'
</span><span> speechSynthesis</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">cancel</span><span>()
</span><span>}
</span><span>
</span><span>SpeechRecognition</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">onresult </span><span style="color:#f29e74;">= </span><span style="color:#ffcc66;">event </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="color:#ffa759;">const </span><span>result </span><span style="color:#f29e74;">= </span><span>event</span><span style="color:#f29e74;">.</span><span>results[event</span><span style="color:#f29e74;">.</span><span>results</span><span style="color:#f29e74;">.</span><span>length </span><span style="color:#f29e74;">- </span><span style="color:#ffcc66;">1</span><span>]
</span><span>
</span><span> </span><span style="color:#ffa759;">if </span><span>(result</span><span style="color:#f29e74;">.</span><span>isFinal) {
</span><span> state</span><span style="color:#f29e74;">.</span><span>context </span><span style="color:#f29e74;">= </span><span>result[</span><span style="color:#ffcc66;">0</span><span>]</span><span style="color:#f29e74;">.</span><span>transcript
</span><span> }
</span><span>}
</span></code></pre>
<h4 id="create-reply-text"><a class="zola-anchor" href="#create-reply-text" aria-label="Anchor link for: create-reply-text">#</a>
Create reply text</h4>
<p>I’m not creating an almighty AI chatbot (That’s too difficult for me) but something enough to deal with text. In short, my idea is to create a processor which will generate reply text based on what I spoke.</p>
<p>I’ll just make the function name <code>process</code>. With some <code>if else</code> statement, we are done with it.</p>
<pre data-lang="javascript" style="background-color:#212733;color:#ccc9c2;" class="language-javascript "><code class="language-javascript" data-lang="javascript"><span style="color:#ffa759;">const </span><span style="color:#ffd580;">process </span><span style="color:#f29e74;">= </span><span>(</span><span style="color:#ffcc66;">msg</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">voice</span><span>) </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="color:#ffa759;">const </span><span>content </span><span style="color:#f29e74;">= </span><span>msg</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">toLowerCase</span><span>()
</span><span>
</span><span> </span><span style="color:#ffa759;">if </span><span>(</span><span style="color:#95e6cb;">/</span><span style="color:#ffa759;">^</span><span style="color:#95e6cb;">google search/</span><span style="color:#ffa759;">g</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">test</span><span>(content)) {
</span><span> </span><span style="color:#ffa759;">const </span><span>url </span><span style="color:#f29e74;">= </span><span style="color:#bae67e;">`https://google.com/search?q=${</span><span>msg</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">replace</span><span style="color:#bae67e;">('Google search '</span><span style="color:#ccc9c2cc;">, </span><span style="color:#bae67e;">'')}`
</span><span> window</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">open</span><span>(url</span><span style="color:#ccc9c2cc;">, </span><span style="color:#bae67e;">'_blank'</span><span>)
</span><span> </span><span style="color:#ffa759;">return </span><span style="color:#bae67e;">`Base on your query, I found some search results on Google`
</span><span> } </span><span style="color:#ffa759;">else if </span><span>(</span><span style="color:#95e6cb;">/your name/</span><span style="color:#ffa759;">g</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">test</span><span>(content)) {
</span><span> </span><span style="color:#ffa759;">return </span><span style="color:#bae67e;">`My name is ${</span><span>voice</span><span style="color:#f29e74;">.</span><span style="color:#bae67e;">name}.`
</span><span> } </span><span style="color:#ffa759;">else if </span><span>(</span><span style="color:#95e6cb;">/(hello</span><span style="color:#f29e74;">|</span><span style="color:#95e6cb;">hi)/</span><span style="color:#ffa759;">g</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">test</span><span>(content)) {
</span><span> </span><span style="color:#ffa759;">return </span><span style="color:#bae67e;">'Hi! Nice to meet you!'
</span><span> } </span><span style="color:#ffa759;">else if </span><span>(</span><span style="color:#95e6cb;">/love you/</span><span style="color:#ffa759;">g</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">test</span><span>(content)) {
</span><span> </span><span style="color:#ffa759;">return </span><span style="color:#bae67e;">'I love you too. And I</span><span style="color:#95e6cb;">\'</span><span style="color:#bae67e;">ll love you forever!'
</span><span> } </span><span style="color:#ffa759;">else if </span><span>(</span><span style="color:#95e6cb;">/(naked</span><span style="color:#f29e74;">|</span><span style="color:#95e6cb;">nude</span><span style="color:#f29e74;">|</span><span style="color:#95e6cb;">tits</span><span style="color:#f29e74;">|</span><span style="color:#95e6cb;">breast</span><span style="color:#f29e74;">|</span><span style="color:#95e6cb;">butt</span><span style="color:#f29e74;">|</span><span style="color:#95e6cb;">ass</span><span style="color:#f29e74;">|</span><span style="color:#95e6cb;">shit</span><span style="color:#f29e74;">|</span><span style="color:#95e6cb;">dick</span><span style="color:#f29e74;">|</span><span style="color:#95e6cb;">pussy</span><span style="color:#f29e74;">|</span><span style="color:#95e6cb;">asshole)/</span><span style="color:#ffa759;">g</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">test</span><span>(content)) {
</span><span> </span><span style="color:#ffa759;">return </span><span style="color:#bae67e;">'I know I love you but can you show some politeness in front of a lady?'
</span><span> }
</span><span>
</span><span> </span><span style="color:#ffa759;">return </span><span style="color:#bae67e;">'Sorry, my sweetheart. I don</span><span style="color:#95e6cb;">\'</span><span style="color:#bae67e;">t understand. Could you try something else?'
</span><span>}
</span></code></pre>
<p>Give it a small test: when I say “I love you”.</p>
<pre data-lang="javascript" style="background-color:#212733;color:#ccc9c2;" class="language-javascript "><code class="language-javascript" data-lang="javascript"><span>process(</span><span style="color:#bae67e;">'I love you'</span><span>)
</span><span style="font-style:italic;color:#5c6773;">// I love you too. And I'll love you forever!
</span></code></pre>
<h4 id="set-up-a-sexy-voice"><a class="zola-anchor" href="#set-up-a-sexy-voice" aria-label="Anchor link for: set-up-a-sexy-voice">#</a>
Set up a “sexy” voice</h4>
<p>There are actually a lot of different speaking voices stashed in our OS. <a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesis">SpeechSynthesis</a> API allows us to use these voices to replace the default.</p>
<pre data-lang="javascript" style="background-color:#212733;color:#ccc9c2;" class="language-javascript "><code class="language-javascript" data-lang="javascript"><span style="color:#ffa759;">var </span><span>voices </span><span style="color:#f29e74;">= </span><span>window</span><span style="color:#f29e74;">.</span><span>speechSynthesis</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">getVoices</span><span>()
</span></code></pre>
<p>Take my Mac as an example, there are <strong>66</strong> voices to choose from.</p>
<p><img src="./images/voices.png" alt="voices speechSynthesis getVoices" /></p>
<p><code>speechSynthesis.getVoices</code> is unpredictable, have to make sure it returns an non-empty list. So I made another async function to resolve the list by check it insistantly.</p>
<pre data-lang="javascript" style="background-color:#212733;color:#ccc9c2;" class="language-javascript "><code class="language-javascript" data-lang="javascript"><span style="color:#ffa759;">const </span><span style="color:#ffd580;">getVoices </span><span style="color:#f29e74;">= </span><span style="color:#ffa759;">async </span><span>(</span><span style="color:#ffcc66;">window</span><span>) </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="color:#ffa759;">let </span><span>id</span><span style="color:#ccc9c2cc;">, </span><span>res
</span><span>
</span><span> </span><span style="color:#ffa759;">await </span><span style="color:#f29e74;">new </span><span style="color:#73d0ff;">Promise</span><span>((</span><span style="color:#ffcc66;">resolve</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">reject</span><span>) </span><span style="color:#ffa759;">=> </span><span>{
</span><span> id </span><span style="color:#f29e74;">= </span><span style="color:#f28779;">setInterval</span><span>(() </span><span style="color:#ffa759;">=> </span><span>{
</span><span> res </span><span style="color:#f29e74;">= </span><span>window</span><span style="color:#f29e74;">.</span><span>speechSynthesis</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">getVoices</span><span>()
</span><span> </span><span style="color:#ffa759;">if </span><span>(res</span><span style="color:#f29e74;">.</span><span>length </span><span style="color:#f29e74;">!== </span><span style="color:#ffcc66;">0</span><span>) {
</span><span> </span><span style="color:#ffd580;">resolve</span><span>(res)
</span><span> </span><span style="color:#f28779;">clearInterval</span><span>(id)
</span><span> }
</span><span> }</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">10</span><span>)
</span><span> })
</span><span>
</span><span> </span><span style="color:#ffa759;">return </span><span>res
</span><span>}
</span></code></pre>
<p>To make sure the voice is “sexy” enough, I’ve tested all of the voices. For a female voice, I choose <strong>“Karen”</strong>.</p>
<p><img src="./images/voice_karen.png" alt="voices speechSynthesis getVoices Karen" /></p>
<p>2 upvotes for <strong>“Karen”</strong>:</p>
<ul>
<li>She speaks smooth enough</li>
<li>Tuning up a bit of the pitch will make her voice cuter</li>
</ul>
<pre data-lang="javascript" style="background-color:#212733;color:#ccc9c2;" class="language-javascript "><code class="language-javascript" data-lang="javascript"><span style="color:#ffd580;">onMounted</span><span>(</span><span style="color:#ffa759;">async </span><span>() </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="color:#ffa759;">const </span><span>voices </span><span style="color:#f29e74;">= </span><span style="color:#ffa759;">await </span><span style="color:#ffd580;">getVoices</span><span>()
</span><span> </span><span style="color:#ffa759;">const </span><span>voiceKaren </span><span style="color:#f29e74;">= </span><span>voices[</span><span style="color:#ffcc66;">17</span><span>]
</span><span>})
</span></code></pre>
<h4 id="speak-out-loud"><a class="zola-anchor" href="#speak-out-loud" aria-label="Anchor link for: speak-out-loud">#</a>
Speak out loud</h4>
<p>I’ve got all the resources I need. Then the next step would be to use the response processor to create text, and allow the speechSpeaker to display the audio. The timing of speaking is right after the voice recognition.</p>
<pre data-lang="javascript" style="background-color:#212733;color:#ccc9c2;" class="language-javascript "><code class="language-javascript" data-lang="javascript"><span style="color:#ffd580;">onMounted</span><span>(</span><span style="color:#ffa759;">async </span><span>() </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="color:#ffa759;">const </span><span>voices </span><span style="color:#f29e74;">= </span><span style="color:#ffa759;">await </span><span style="color:#ffd580;">getVoices</span><span>()
</span><span> </span><span style="color:#ffa759;">const </span><span>voiceKaren </span><span style="color:#f29e74;">= </span><span>voices[</span><span style="color:#ffcc66;">17</span><span>]
</span><span>
</span><span> </span><span style="font-style:italic;color:#5c6773;">// The final function to speak out the response text
</span><span> </span><span style="color:#ffa759;">const </span><span style="color:#ffd580;">speechSpeaker </span><span style="color:#f29e74;">= </span><span>(</span><span style="color:#ffcc66;">text</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">voice</span><span>) </span><span style="color:#ffa759;">=> </span><span>{
</span><span> utterThis</span><span style="color:#f29e74;">.</span><span>text </span><span style="color:#f29e74;">= </span><span>text
</span><span> utterThis</span><span style="color:#f29e74;">.</span><span>voice </span><span style="color:#f29e74;">= </span><span>voice
</span><span> utterThis</span><span style="color:#f29e74;">.</span><span>pitch </span><span style="color:#f29e74;">= </span><span style="color:#ffcc66;">1.4 </span><span style="font-style:italic;color:#5c6773;">// This sounds better
</span><span> utterThis</span><span style="color:#f29e74;">.</span><span>lang </span><span style="color:#f29e74;">= </span><span>voice</span><span style="color:#f29e74;">.</span><span>lang
</span><span> window</span><span style="color:#f29e74;">.</span><span>speechSynthesis</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">speak</span><span>(utterThis)
</span><span> }
</span><span>
</span><span> SpeechRecognition</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">onresult </span><span style="color:#f29e74;">= </span><span style="color:#ffcc66;">event </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="color:#ffa759;">const </span><span>result </span><span style="color:#f29e74;">= </span><span>event</span><span style="color:#f29e74;">.</span><span>results[event</span><span style="color:#f29e74;">.</span><span>results</span><span style="color:#f29e74;">.</span><span>length </span><span style="color:#f29e74;">- </span><span style="color:#ffcc66;">1</span><span>]
</span><span>
</span><span> </span><span style="color:#ffa759;">if </span><span>(result</span><span style="color:#f29e74;">.</span><span>isFinal) {
</span><span> state</span><span style="color:#f29e74;">.</span><span>context </span><span style="color:#f29e74;">= </span><span style="color:#bae67e;">'</span><span style="color:#95e6cb;">\n</span><span style="color:#bae67e;"><b>Me:</b> ' </span><span style="color:#f29e74;">+ </span><span>result[</span><span style="color:#ffcc66;">0</span><span>]</span><span style="color:#f29e74;">.</span><span>transcript
</span><span>
</span><span> </span><span style="color:#ffa759;">const </span><span>reply </span><span style="color:#f29e74;">= </span><span>process(result[</span><span style="color:#ffcc66;">0</span><span>]</span><span style="color:#f29e74;">.</span><span>transcript</span><span style="color:#ccc9c2cc;">, </span><span>voice)
</span><span>
</span><span> state</span><span style="color:#f29e74;">.</span><span>context </span><span style="color:#f29e74;">+= </span><span style="color:#bae67e;">'</span><span style="color:#95e6cb;">\n</span><span style="color:#bae67e;"><b>' </span><span style="color:#f29e74;">+ </span><span>voice</span><span style="color:#f29e74;">.</span><span>name </span><span style="color:#f29e74;">+ </span><span style="color:#bae67e;">'</b>: ' </span><span style="color:#f29e74;">+ </span><span>reply
</span><span>
</span><span> </span><span style="color:#ffd580;">speechSpeaker</span><span>(reply</span><span style="color:#ccc9c2cc;">, </span><span>voiceKaren)
</span><span> }
</span><span> }
</span><span>})
</span></code></pre>
<p>After a few tests, it feels weird to hear the response voice immediately after I speak. So it’d be more natural to delay the response a little bit by 600ms.</p>
<pre data-lang="javascript" style="background-color:#212733;color:#ccc9c2;" class="language-javascript "><code class="language-javascript" data-lang="javascript"><span>SpeechRecognition</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">onresult </span><span style="color:#f29e74;">= </span><span style="color:#ffcc66;">event </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="color:#ffa759;">const </span><span>result </span><span style="color:#f29e74;">= </span><span>event</span><span style="color:#f29e74;">.</span><span>results[event</span><span style="color:#f29e74;">.</span><span>results</span><span style="color:#f29e74;">.</span><span>length </span><span style="color:#f29e74;">- </span><span style="color:#ffcc66;">1</span><span>]
</span><span>
</span><span> </span><span style="color:#ffa759;">if </span><span>(result</span><span style="color:#f29e74;">.</span><span>isFinal) {
</span><span> state</span><span style="color:#f29e74;">.</span><span>context </span><span style="color:#f29e74;">= </span><span style="color:#bae67e;">'</span><span style="color:#95e6cb;">\n</span><span style="color:#bae67e;"><b>Me:</b> ' </span><span style="color:#f29e74;">+ </span><span>result[</span><span style="color:#ffcc66;">0</span><span>]</span><span style="color:#f29e74;">.</span><span>transcript
</span><span>
</span><span> </span><span style="color:#ffa759;">const </span><span>reply </span><span style="color:#f29e74;">= </span><span>process(result[</span><span style="color:#ffcc66;">0</span><span>]</span><span style="color:#f29e74;">.</span><span>transcript</span><span style="color:#ccc9c2cc;">, </span><span>voice)
</span><span>
</span><span> </span><span style="color:#f28779;">setTimeout</span><span>(() </span><span style="color:#ffa759;">=> </span><span>{
</span><span> state</span><span style="color:#f29e74;">.</span><span>context </span><span style="color:#f29e74;">+= </span><span style="color:#bae67e;">'</span><span style="color:#95e6cb;">\n</span><span style="color:#bae67e;"><b>' </span><span style="color:#f29e74;">+ </span><span>voice</span><span style="color:#f29e74;">.</span><span>name </span><span style="color:#f29e74;">+ </span><span style="color:#bae67e;">'</b>: ' </span><span style="color:#f29e74;">+ </span><span>reply
</span><span>
</span><span> </span><span style="color:#ffd580;">speechSpeaker</span><span>(reply</span><span style="color:#ccc9c2cc;">, </span><span>voiceKaren)
</span><span> }</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">600</span><span>)
</span><span> }
</span><span> }
</span></code></pre>
<h4 id="styling"><a class="zola-anchor" href="#styling" aria-label="Anchor link for: styling">#</a>
Styling</h4>
<p>I used <a rel="noopener nofollow noreferrer" target="_blank" href="https://tailwindcss.com/">Tailwindcss</a> to stylish my voice assistant because it offers more customizability.</p>
<p>The <code><style></style></code> looks like this:</p>
<pre data-lang="sass" style="background-color:#212733;color:#ccc9c2;" class="language-sass "><code class="language-sass" data-lang="sass"><span style="color:#ffd580;">.button-control </span><span style="color:#ff3333;">{
</span><span> @apply </span><span style="color:#73d0ff;">font-semibold</span><span style="color:#ff3333;">;
</span><span> @apply </span><span style="color:#73d0ff;">px-4 py-2</span><span style="color:#ff3333;">;
</span><span> @apply </span><span style="color:#73d0ff;">mr-2 mb-2</span><span style="color:#ff3333;">;
</span><span> @apply </span><span style="color:#73d0ff;">bg-gray-600</span><span style="color:#ff3333;">;
</span><span> @apply </span><span style="color:#73d0ff;">rounded-sm</span><span style="color:#ff3333;">;
</span><span> @apply </span><span style="color:#73d0ff;">shadow-2xl</span><span style="color:#ff3333;">;
</span><span> @apply </span><span style="color:#73d0ff;">text-white</span><span style="color:#ff3333;">;
</span><span>
</span><span> </span><span style="color:#f29e74;">&</span><span>:focus </span><span style="color:#ff3333;">{
</span><span> @apply </span><span style="color:#73d0ff;">outline-none</span><span style="color:#ff3333;">;
</span><span> </span><span style="color:#ff3333;">}
</span><span>
</span><span> </span><span style="color:#f29e74;">&</span><span>:active </span><span style="color:#ff3333;">{
</span><span> @apply </span><span style="color:#73d0ff;">bg-gray-700</span><span style="color:#ff3333;">;
</span><span> @apply </span><span style="color:#73d0ff;">shadow-none</span><span style="color:#ff3333;">;
</span><span> </span><span style="color:#ff3333;">}
</span><span style="color:#ff3333;">}
</span><span>
</span><span style="color:#ffd580;">.message-box </span><span style="color:#ff3333;">{
</span><span> @apply relative</span><span style="color:#ff3333;">;
</span><span> @apply </span><span style="color:#73d0ff;">h-48</span><span style="color:#ff3333;">;
</span><span> @apply </span><span style="color:#73d0ff;">overflow-y-scroll</span><span style="color:#ff3333;">;
</span><span> @apply </span><span style="color:#73d0ff;">rounded-sm</span><span style="color:#ff3333;">;
</span><span> @apply </span><span style="color:#5ccfe6;">border </span><span style="color:#73d0ff;">border-solid </span><span style="color:#5ccfe6;">border</span><span>-</span><span style="color:#73d0ff;">gray-300</span><span style="color:#ff3333;">;
</span><span> @apply </span><span style="color:#73d0ff;">p-2</span><span style="color:#ff3333;">;
</span><span> @apply </span><span style="color:#73d0ff;">mb-2</span><span style="color:#ff3333;">;
</span><span> @apply </span><span style="color:#73d0ff;">leading-6</span><span style="color:#ff3333;">;
</span><span style="color:#ff3333;">}
</span><span>
</span><span style="color:#ffd580;">.voice-options </span><span style="color:#ff3333;">{
</span><span> @apply </span><span style="color:#73d0ff;">font-semibold</span><span style="color:#ff3333;">;
</span><span> @apply </span><span style="color:#73d0ff;">bg-gray-100</span><span style="color:#ff3333;">;
</span><span> @apply </span><span style="color:#73d0ff;">px-2 py-1</span><span style="color:#ff3333;">;
</span><span> @apply </span><span style="color:#73d0ff;">mr-2 mb-2</span><span style="color:#ff3333;">;
</span><span> @apply </span><span style="color:#73d0ff;">rounded-sm</span><span style="color:#ff3333;">;
</span><span> @apply </span><span style="color:#73d0ff;">border-2 </span><span style="color:#5ccfe6;">border</span><span>-solid </span><span style="color:#73d0ff;">border-gray-100</span><span style="color:#ff3333;">;
</span><span> @apply </span><span style="color:#73d0ff;">shadow-md</span><span style="color:#ff3333;">;
</span><span>
</span><span> </span><span style="color:#f29e74;">&</span><span>:focus </span><span style="color:#ff3333;">{
</span><span> @apply </span><span style="color:#73d0ff;">outline-none</span><span style="color:#ff3333;">;
</span><span> </span><span style="color:#ff3333;">}
</span><span style="color:#ff3333;">}
</span></code></pre>
<h4 id="ux-optimization"><a class="zola-anchor" href="#ux-optimization" aria-label="Anchor link for: ux-optimization">#</a>
UX optimization</h4>
<p>Now I got 2 good ideas to make it better:</p>
<ul>
<li>Allow selecting voice from a dropdown list</li>
<li>Let the message-box scrollbar follow the latest message</li>
</ul>
<p>With these 2 ideas, my final prototype of “sexy” voice assistant came out.</p>
<p>Let me just show you the full source code of it:</p>
<pre data-lang="html" style="background-color:#212733;color:#ccc9c2;" class="language-html "><code class="language-html" data-lang="html"><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">template</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">div </span><span style="color:#ffd580;">class</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"mx-auto w-full"</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">pre </span><span style="color:#ffd580;">class</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"message-box" </span><span style="color:#ffd580;">v-html</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"context" </span><span style="color:#ffd580;">ref</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"messageBox"</span><span style="color:#5ccfe690;">></</span><span style="color:#73d0ff;">pre</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">button </span><span style="color:#ffd580;">class</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"button-control" </span><span style="color:#ffd580;">@mousedown</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"listen(true)" </span><span style="color:#ffd580;">@mouseup</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"listen(false)"</span><span style="color:#5ccfe690;">></span><span>Hold to Listen</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">button</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">button </span><span style="color:#ffd580;">class</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"button-control" </span><span style="color:#ffd580;">@click</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"reset"</span><span style="color:#5ccfe690;">></span><span>Reset</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">button</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">select </span><span style="color:#ffd580;">class</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"voice-options" </span><span style="color:#ffd580;">v-model</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"activeVoiceIdx"</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">option </span><span style="color:#ffd580;">:key</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"key" </span><span style="color:#ffd580;">:value</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"key" </span><span style="color:#ffd580;">:selected</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"key === activeVoiceIdx" </span><span style="color:#ffd580;">v-for</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"(val, key) in voices"</span><span style="color:#5ccfe690;">>
</span><span> {{ val.name }} ({{ val.lang }})
</span><span> </span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">option</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">select</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">template</span><span style="color:#5ccfe690;">>
</span></code></pre>
<pre data-lang="javascript" style="background-color:#212733;color:#ccc9c2;" class="language-javascript "><code class="language-javascript" data-lang="javascript"><span><</span><span style="color:#73d0ff;">script</span><span>>
</span><span style="color:#ffa759;">import </span><span>{ onMounted</span><span style="color:#ccc9c2cc;">, </span><span>reactive</span><span style="color:#ccc9c2cc;">, </span><span>toRefs</span><span style="color:#ccc9c2cc;">, </span><span>ref } </span><span style="color:#ffa759;">from </span><span style="color:#bae67e;">'@vue/composition-api'
</span><span>
</span><span style="color:#ffa759;">export default </span><span>{
</span><span> </span><span style="color:#ffd580;">setup </span><span>(</span><span style="color:#ffcc66;">_</span><span style="color:#ccc9c2cc;">, </span><span>{ </span><span style="color:#ffcc66;">root </span><span>}) {
</span><span> </span><span style="color:#ffa759;">const </span><span style="color:#ffd580;">getVoices </span><span style="color:#f29e74;">= </span><span style="color:#ffa759;">async </span><span>(</span><span style="color:#ffcc66;">window</span><span>) </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="color:#ffa759;">let </span><span>id</span><span style="color:#ccc9c2cc;">, </span><span>res
</span><span>
</span><span> </span><span style="color:#ffa759;">await </span><span style="color:#f29e74;">new </span><span style="color:#73d0ff;">Promise</span><span>((</span><span style="color:#ffcc66;">resolve</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">reject</span><span>) </span><span style="color:#ffa759;">=> </span><span>{
</span><span> id </span><span style="color:#f29e74;">= </span><span style="color:#f28779;">setInterval</span><span>(() </span><span style="color:#ffa759;">=> </span><span>{
</span><span> res </span><span style="color:#f29e74;">= </span><span>window</span><span style="color:#f29e74;">.</span><span>speechSynthesis</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">getVoices</span><span>()
</span><span> </span><span style="color:#ffa759;">if </span><span>(res</span><span style="color:#f29e74;">.</span><span>length </span><span style="color:#f29e74;">!== </span><span style="color:#ffcc66;">0</span><span>) {
</span><span> </span><span style="color:#ffd580;">resolve</span><span>(res)
</span><span> </span><span style="color:#f28779;">clearInterval</span><span>(id)
</span><span> }
</span><span> }</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">10</span><span>)
</span><span> })
</span><span>
</span><span> </span><span style="color:#ffa759;">return </span><span>res
</span><span> }
</span><span>
</span><span> </span><span style="color:#ffa759;">const </span><span style="color:#ffd580;">process </span><span style="color:#f29e74;">= </span><span>(</span><span style="color:#ffcc66;">msg</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">voice</span><span>) </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="color:#ffa759;">const </span><span>content </span><span style="color:#f29e74;">= </span><span>msg</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">toLowerCase</span><span>()
</span><span>
</span><span> </span><span style="color:#ffa759;">if </span><span>(</span><span style="color:#95e6cb;">/</span><span style="color:#ffa759;">^</span><span style="color:#95e6cb;">google search/</span><span style="color:#ffa759;">g</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">test</span><span>(content)) {
</span><span> </span><span style="color:#ffa759;">const </span><span>url </span><span style="color:#f29e74;">= </span><span style="color:#bae67e;">`https://google.com/search?q=${</span><span>msg</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">replace</span><span style="color:#bae67e;">('Google search '</span><span style="color:#ccc9c2cc;">, </span><span style="color:#bae67e;">'')}`
</span><span> window</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">open</span><span>(url</span><span style="color:#ccc9c2cc;">, </span><span style="color:#bae67e;">'_blank'</span><span>)
</span><span> </span><span style="color:#ffa759;">return </span><span style="color:#bae67e;">`Base on your query, I found some search results on Google`
</span><span> } </span><span style="color:#ffa759;">else if </span><span>(</span><span style="color:#95e6cb;">/your name/</span><span style="color:#ffa759;">g</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">test</span><span>(content)) {
</span><span> </span><span style="color:#ffa759;">return </span><span style="color:#bae67e;">`My name is ${</span><span>voice</span><span style="color:#f29e74;">.</span><span style="color:#bae67e;">name}.`
</span><span> } </span><span style="color:#ffa759;">else if </span><span>(</span><span style="color:#95e6cb;">/(hello</span><span style="color:#f29e74;">|</span><span style="color:#95e6cb;">hi)/</span><span style="color:#ffa759;">g</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">test</span><span>(content)) {
</span><span> </span><span style="color:#ffa759;">return </span><span style="color:#bae67e;">'Hi! Nice to meet you!'
</span><span> } </span><span style="color:#ffa759;">else if </span><span>(</span><span style="color:#95e6cb;">/love you/</span><span style="color:#ffa759;">g</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">test</span><span>(content)) {
</span><span> </span><span style="color:#ffa759;">return </span><span style="color:#bae67e;">'I love you too. And I</span><span style="color:#95e6cb;">\'</span><span style="color:#bae67e;">ll love you forever!'
</span><span> } </span><span style="color:#ffa759;">else if </span><span>(</span><span style="color:#95e6cb;">/(naked</span><span style="color:#f29e74;">|</span><span style="color:#95e6cb;">nude</span><span style="color:#f29e74;">|</span><span style="color:#95e6cb;">tits</span><span style="color:#f29e74;">|</span><span style="color:#95e6cb;">breast</span><span style="color:#f29e74;">|</span><span style="color:#95e6cb;">butt</span><span style="color:#f29e74;">|</span><span style="color:#95e6cb;">ass</span><span style="color:#f29e74;">|</span><span style="color:#95e6cb;">shit</span><span style="color:#f29e74;">|</span><span style="color:#95e6cb;">dick</span><span style="color:#f29e74;">|</span><span style="color:#95e6cb;">pussy</span><span style="color:#f29e74;">|</span><span style="color:#95e6cb;">asshole)/</span><span style="color:#ffa759;">g</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">test</span><span>(content)) {
</span><span> </span><span style="color:#ffa759;">return </span><span style="color:#bae67e;">'I know I love you but can you show some politeness in front of a lady?'
</span><span> }
</span><span>
</span><span> </span><span style="color:#ffa759;">return </span><span style="color:#bae67e;">'Sorry, my sweetheart. I don</span><span style="color:#95e6cb;">\'</span><span style="color:#bae67e;">t understand. Could you try something else?'
</span><span> }
</span><span>
</span><span> </span><span style="color:#ffa759;">const </span><span>state </span><span style="color:#f29e74;">= </span><span style="color:#ffd580;">reactive</span><span>({
</span><span> name</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">''</span><span style="color:#ccc9c2cc;">,
</span><span> context</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">'Sorry, your browser doesn</span><span style="color:#95e6cb;">\'</span><span style="color:#bae67e;">t support SpeechRecognition. Please use Chrome / Edge79+ instead.'</span><span style="color:#ccc9c2cc;">,
</span><span> voices</span><span style="color:#ccc9c2cc;">: </span><span>[]</span><span style="color:#ccc9c2cc;">,
</span><span> activeVoiceIdx</span><span style="color:#ccc9c2cc;">: </span><span style="color:#ffcc66;">17
</span><span> })
</span><span>
</span><span> </span><span style="color:#ffa759;">const </span><span>messageBox </span><span style="color:#f29e74;">= </span><span style="color:#ffd580;">ref</span><span>(</span><span style="color:#ffcc66;">null</span><span>)
</span><span>
</span><span> </span><span style="color:#ffa759;">const </span><span>SpeechRecognition </span><span style="color:#f29e74;">= </span><span>window</span><span style="color:#f29e74;">.</span><span>webkitSpeechRecognition </span><span style="color:#f29e74;">&& new </span><span style="color:#73d0ff;">window</span><span style="color:#f29e74;">.</span><span style="color:#73d0ff;">webkitSpeechRecognition</span><span>()
</span><span> SpeechRecognition </span><span style="color:#f29e74;">&& </span><span>(SpeechRecognition</span><span style="color:#f29e74;">.</span><span>interimResults </span><span style="color:#f29e74;">= </span><span style="color:#ffcc66;">true</span><span>)
</span><span>
</span><span> </span><span style="color:#ffa759;">const </span><span style="color:#ffd580;">listen </span><span style="color:#f29e74;">= </span><span style="color:#ffcc66;">start </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="color:#ffa759;">if </span><span>(</span><span style="color:#f29e74;">!</span><span>SpeechRecognition) </span><span style="color:#ffa759;">return
</span><span>
</span><span> </span><span style="color:#ffa759;">if </span><span>(start) {
</span><span> SpeechRecognition</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">start</span><span>()
</span><span> } </span><span style="color:#ffa759;">else </span><span>{
</span><span> SpeechRecognition</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">stop</span><span>()
</span><span> }
</span><span> }
</span><span>
</span><span> </span><span style="color:#ffa759;">const </span><span style="color:#ffd580;">reset </span><span style="color:#f29e74;">= </span><span>() </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="color:#ffa759;">if </span><span>(</span><span style="color:#f29e74;">!</span><span>SpeechRecognition) </span><span style="color:#ffa759;">return
</span><span>
</span><span> state</span><span style="color:#f29e74;">.</span><span>context </span><span style="color:#f29e74;">= </span><span style="color:#bae67e;">'<b>' </span><span style="color:#f29e74;">+ </span><span>state</span><span style="color:#f29e74;">.</span><span>name </span><span style="color:#f29e74;">+ </span><span style="color:#bae67e;">'</b>' </span><span style="color:#f29e74;">+ </span><span style="color:#bae67e;">': Hi, my hero!'
</span><span> speechSynthesis</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">cancel</span><span>()
</span><span> }
</span><span>
</span><span> </span><span style="color:#ffd580;">onMounted</span><span>(</span><span style="color:#ffa759;">async </span><span>() </span><span style="color:#ffa759;">=> </span><span>{
</span><span>
</span><span> </span><span style="color:#ffa759;">if </span><span>(</span><span style="color:#f29e74;">!</span><span>window</span><span style="color:#f29e74;">.</span><span>webkitSpeechRecognition) </span><span style="color:#ffa759;">return
</span><span>
</span><span> state</span><span style="color:#f29e74;">.</span><span>voices </span><span style="color:#f29e74;">= </span><span style="color:#ffa759;">await </span><span style="color:#ffd580;">getVoices</span><span>(window)
</span><span> state</span><span style="color:#f29e74;">.</span><span>name </span><span style="color:#f29e74;">= </span><span>state</span><span style="color:#f29e74;">.</span><span>voices[state</span><span style="color:#f29e74;">.</span><span>activeVoiceIdx]</span><span style="color:#f29e74;">.</span><span>name
</span><span> state</span><span style="color:#f29e74;">.</span><span>context </span><span style="color:#f29e74;">= </span><span style="color:#bae67e;">'<b>' </span><span style="color:#f29e74;">+ </span><span>state</span><span style="color:#f29e74;">.</span><span>name </span><span style="color:#f29e74;">+ </span><span style="color:#bae67e;">'</b>' </span><span style="color:#f29e74;">+ </span><span style="color:#bae67e;">': Hi, my hero!'
</span><span>
</span><span> </span><span style="color:#ffa759;">const </span><span>utterThis </span><span style="color:#f29e74;">= new </span><span style="color:#73d0ff;">window</span><span style="color:#f29e74;">.</span><span style="color:#73d0ff;">SpeechSynthesisUtterance</span><span>()
</span><span>
</span><span> </span><span style="color:#ffa759;">const </span><span style="color:#ffd580;">speechSpeaker </span><span style="color:#f29e74;">= </span><span>(</span><span style="color:#ffcc66;">text</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">voice</span><span>) </span><span style="color:#ffa759;">=> </span><span>{
</span><span> utterThis</span><span style="color:#f29e74;">.</span><span>text </span><span style="color:#f29e74;">= </span><span>text
</span><span> utterThis</span><span style="color:#f29e74;">.</span><span>voice </span><span style="color:#f29e74;">= </span><span>voice
</span><span> utterThis</span><span style="color:#f29e74;">.</span><span>pitch </span><span style="color:#f29e74;">= </span><span style="color:#ffcc66;">1.4
</span><span> utterThis</span><span style="color:#f29e74;">.</span><span>lang </span><span style="color:#f29e74;">= </span><span>voice</span><span style="color:#f29e74;">.</span><span>lang
</span><span> window</span><span style="color:#f29e74;">.</span><span>speechSynthesis</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">speak</span><span>(utterThis)
</span><span> }
</span><span>
</span><span> SpeechRecognition</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">onresult </span><span style="color:#f29e74;">= </span><span style="color:#ffcc66;">event </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="color:#ffa759;">const </span><span>result </span><span style="color:#f29e74;">= </span><span>event</span><span style="color:#f29e74;">.</span><span>results[event</span><span style="color:#f29e74;">.</span><span>results</span><span style="color:#f29e74;">.</span><span>length </span><span style="color:#f29e74;">- </span><span style="color:#ffcc66;">1</span><span>]
</span><span>
</span><span> </span><span style="color:#ffa759;">if </span><span>(result</span><span style="color:#f29e74;">.</span><span>isFinal) {
</span><span> state</span><span style="color:#f29e74;">.</span><span>context </span><span style="color:#f29e74;">+= </span><span style="color:#bae67e;">'</span><span style="color:#95e6cb;">\n</span><span style="color:#bae67e;"><b>Me:</b> ' </span><span style="color:#f29e74;">+ </span><span>result[</span><span style="color:#ffcc66;">0</span><span>]</span><span style="color:#f29e74;">.</span><span>transcript
</span><span>
</span><span> </span><span style="color:#ffa759;">const </span><span>voice </span><span style="color:#f29e74;">= </span><span>state</span><span style="color:#f29e74;">.</span><span>voices[state</span><span style="color:#f29e74;">.</span><span>activeVoiceIdx]
</span><span> </span><span style="color:#ffa759;">const </span><span>reply </span><span style="color:#f29e74;">= </span><span>process(result[</span><span style="color:#ffcc66;">0</span><span>]</span><span style="color:#f29e74;">.</span><span>transcript</span><span style="color:#ccc9c2cc;">, </span><span>voice)
</span><span> root</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">$nextTick</span><span>(() </span><span style="color:#ffa759;">=> </span><span>{
</span><span> messageBox</span><span style="color:#f29e74;">.</span><span>value</span><span style="color:#f29e74;">.</span><span>scrollTop </span><span style="color:#f29e74;">= </span><span>messageBox</span><span style="color:#f29e74;">.</span><span>value</span><span style="color:#f29e74;">.</span><span>scrollHeight
</span><span> })
</span><span>
</span><span> </span><span style="color:#f28779;">setTimeout</span><span>(() </span><span style="color:#ffa759;">=> </span><span>{
</span><span> state</span><span style="color:#f29e74;">.</span><span>context </span><span style="color:#f29e74;">+= </span><span style="color:#bae67e;">'</span><span style="color:#95e6cb;">\n</span><span style="color:#bae67e;"><b>' </span><span style="color:#f29e74;">+ </span><span>voice</span><span style="color:#f29e74;">.</span><span>name </span><span style="color:#f29e74;">+ </span><span style="color:#bae67e;">'</b>: ' </span><span style="color:#f29e74;">+ </span><span>reply
</span><span>
</span><span> root</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">$nextTick</span><span>(() </span><span style="color:#ffa759;">=> </span><span>{
</span><span> messageBox</span><span style="color:#f29e74;">.</span><span>value</span><span style="color:#f29e74;">.</span><span>scrollTop </span><span style="color:#f29e74;">= </span><span>messageBox</span><span style="color:#f29e74;">.</span><span>value</span><span style="color:#f29e74;">.</span><span>scrollHeight
</span><span> })
</span><span>
</span><span> </span><span style="color:#ffd580;">speechSpeaker</span><span>(reply</span><span style="color:#ccc9c2cc;">, </span><span>voice)
</span><span> }</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">600</span><span>)
</span><span> }
</span><span> }
</span><span>
</span><span> })
</span><span>
</span><span> </span><span style="color:#ffa759;">return </span><span>{
</span><span> </span><span style="color:#f29e74;">...</span><span style="color:#ffd580;">toRefs</span><span>(state)</span><span style="color:#ccc9c2cc;">,
</span><span> listen</span><span style="color:#ccc9c2cc;">,
</span><span> reset</span><span style="color:#ccc9c2cc;">,
</span><span> messageBox
</span><span> }
</span><span> }
</span><span>}
</span><span style="color:#f29e74;"></</span><span>script</span><span style="color:#f29e74;">>
</span></code></pre>
<pre data-lang="sass" style="background-color:#212733;color:#ccc9c2;" class="language-sass "><code class="language-sass" data-lang="sass"><span><</span><span style="color:#73d0ff;">style </span><span>lang="postcss" scoped>
</span><span style="color:#ffd580;">.button-control </span><span style="color:#ff3333;">{
</span><span> @apply </span><span style="color:#73d0ff;">font-semibold</span><span style="color:#ff3333;">;
</span><span> @apply </span><span style="color:#73d0ff;">px-4 py-2</span><span style="color:#ff3333;">;
</span><span> @apply </span><span style="color:#73d0ff;">mr-2 mb-2</span><span style="color:#ff3333;">;
</span><span> @apply </span><span style="color:#73d0ff;">bg-gray-600</span><span style="color:#ff3333;">;
</span><span> @apply </span><span style="color:#73d0ff;">rounded-sm</span><span style="color:#ff3333;">;
</span><span> @apply </span><span style="color:#73d0ff;">shadow-2xl</span><span style="color:#ff3333;">;
</span><span> @apply </span><span style="color:#73d0ff;">text-white</span><span style="color:#ff3333;">;
</span><span>
</span><span> </span><span style="color:#f29e74;">&</span><span>:focus </span><span style="color:#ff3333;">{
</span><span> @apply </span><span style="color:#73d0ff;">outline-none</span><span style="color:#ff3333;">;
</span><span> </span><span style="color:#ff3333;">}
</span><span>
</span><span> </span><span style="color:#f29e74;">&</span><span>:active </span><span style="color:#ff3333;">{
</span><span> @apply </span><span style="color:#73d0ff;">bg-gray-700</span><span style="color:#ff3333;">;
</span><span> @apply </span><span style="color:#73d0ff;">shadow-none</span><span style="color:#ff3333;">;
</span><span> </span><span style="color:#ff3333;">}
</span><span style="color:#ff3333;">}
</span><span>
</span><span style="color:#ffd580;">.message-box </span><span style="color:#ff3333;">{
</span><span> @apply relative</span><span style="color:#ff3333;">;
</span><span> @apply </span><span style="color:#73d0ff;">h-48</span><span style="color:#ff3333;">;
</span><span> @apply </span><span style="color:#73d0ff;">overflow-y-scroll</span><span style="color:#ff3333;">;
</span><span> @apply </span><span style="color:#73d0ff;">rounded-sm</span><span style="color:#ff3333;">;
</span><span> @apply </span><span style="color:#5ccfe6;">border </span><span style="color:#73d0ff;">border-solid </span><span style="color:#5ccfe6;">border</span><span>-</span><span style="color:#73d0ff;">gray-300</span><span style="color:#ff3333;">;
</span><span> @apply </span><span style="color:#73d0ff;">p-2</span><span style="color:#ff3333;">;
</span><span> @apply </span><span style="color:#73d0ff;">mb-2</span><span style="color:#ff3333;">;
</span><span> @apply </span><span style="color:#73d0ff;">leading-6</span><span style="color:#ff3333;">;
</span><span style="color:#ff3333;">}
</span><span>
</span><span style="color:#ffd580;">.voice-options </span><span style="color:#ff3333;">{
</span><span> @apply </span><span style="color:#73d0ff;">font-semibold</span><span style="color:#ff3333;">;
</span><span> @apply </span><span style="color:#73d0ff;">bg-gray-100</span><span style="color:#ff3333;">;
</span><span> @apply </span><span style="color:#73d0ff;">px-2 py-1</span><span style="color:#ff3333;">;
</span><span> @apply </span><span style="color:#73d0ff;">mr-2 mb-2</span><span style="color:#ff3333;">;
</span><span> @apply </span><span style="color:#73d0ff;">rounded-sm</span><span style="color:#ff3333;">;
</span><span> @apply </span><span style="color:#73d0ff;">border-2 </span><span style="color:#5ccfe6;">border</span><span>-solid </span><span style="color:#73d0ff;">border-gray-100</span><span style="color:#ff3333;">;
</span><span> @apply </span><span style="color:#73d0ff;">shadow-md</span><span style="color:#ff3333;">;
</span><span>
</span><span> </span><span style="color:#f29e74;">&</span><span>:focus </span><span style="color:#ff3333;">{
</span><span> @apply </span><span style="color:#73d0ff;">outline-none</span><span style="color:#ff3333;">;
</span><span> </span><span style="color:#ff3333;">}
</span><span style="color:#ff3333;">}
</span><span></</span><span style="color:#73d0ff;">style</span><span style="color:#ccc9c2cc;">>
</span><span>
</span></code></pre>
<p>You can also find the source on my Github:
<a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/daiyanze/sexy-voice-assistant">https://github.com/daiyanze/sexy-voice-assistant</a></p>
<h2 id="reference"><a class="zola-anchor" href="#reference" aria-label="Anchor link for: reference">#</a>
Reference</h2>
<ul>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesisUtterance">https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesisUtterance</a></li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesis">https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesis</a></li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition">https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition</a></li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/nhudinhtuan">https://github.com/nhudinhtuan</a></li>
</ul>
A brief talk about technical debt2020-07-16T00:00:00+00:002020-07-16T00:00:00+00:00https://pitayan.com/posts/technical-debt/<h2 id="what-is-technical-debt"><a class="zola-anchor" href="#what-is-technical-debt" aria-label="Anchor link for: what-is-technical-debt">#</a>
What is technical debt?</h2>
<p>“Techinical Debt” comes from the mouth of <a rel="noopener nofollow noreferrer" target="_blank" href="https://en.wikipedia.org/wiki/Ward_Cunningham">Ward Cunningham</a>, he first used the technical complexity ratio as a liability, referred to as “technical debt”.</p>
<p>Software development is a very complicated project, so many people consider “software development” as “software engineering”. Software is aimed at serving various industries (finance, medical, shopping, etc.). Thus our programmers may not fully understand that certain field in order to control it under our expertise properly. In the end, software architecture will inevitably result in having lots of “technical debts”.</p>
<p>Although technical debt is inevitable, but it is a problem of quantity as a matter of fact.</p>
<h2 id="how-did-technical-debt-arise"><a class="zola-anchor" href="#how-did-technical-debt-arise" aria-label="Anchor link for: how-did-technical-debt-arise">#</a>
How did technical debt arise?</h2>
<p>I think technical debt has mainly three categories.</p>
<ul>
<li>Doucment Debts
<ol>
<li><a href="https://pitayan.com/posts/technical-debt/#1-requirements-debts">Requirements Debts</a></li>
<li><a href="https://pitayan.com/posts/technical-debt/#2-development-document-debts">Development Document Debts</a></li>
<li><a href="https://pitayan.com/posts/technical-debt/#3-test-document-debts">Test Document Debts</a></li>
</ol>
</li>
<li>Program Debts
<ol>
<li><a href="https://pitayan.com/posts/technical-debt/#1-structural-debts">Structural Debts</a></li>
<li><a href="https://pitayan.com/posts/technical-debt/#2-coding-debts">Coding Debts</a></li>
<li><a href="https://pitayan.com/posts/technical-debt/#3-business-logic-debts">Business Logic Debts</a></li>
</ol>
</li>
<li>Management Debts
<ol>
<li><a href="https://pitayan.com/posts/technical-debt/#1-deadline-debts">Deadline Debts</a></li>
<li><a href="https://pitayan.com/posts/technical-debt/#2-turnover-debts">Turnover Debts</a></li>
<li><a href="https://pitayan.com/posts/technical-debt/#3-coordinated-debts">Coordinated debts</a></li>
<li><a href="https://pitayan.com/posts/technical-debt/#4-cost-debts">Cost Debts</a></li>
</ol>
</li>
</ul>
<h2 id="doucment-debts"><a class="zola-anchor" href="#doucment-debts" aria-label="Anchor link for: doucment-debts">#</a>
Doucment Debts</h2>
<h4 id="1-requirements-debts"><a class="zola-anchor" href="#1-requirements-debts" aria-label="Anchor link for: 1-requirements-debts">#</a>
1. Requirements Debts</h4>
<blockquote>
<p>A software developer who does not understand requirements analysis is not a good software developer.</p>
</blockquote>
<p>In case of problems, a developer must provide prompt feedback and communication with superiors or customers. If Some requirements cannot be done then he/she must stay skeptical toward those requirements and deny them properly.</p>
<p>Other than just do complainings privately, it’s always better to find the right way to feedback problems and know how to communicate effectively. So that we could let customers or leaders understand the technology difficulties.</p>
<p>In another way around, if the developer does not understand the project requirements but develops the project blindly. It will lead to a mismatch between the business spec and the development, and this will cost more time or even money due to the mistakes.</p>
<h4 id="2-development-document-debts"><a class="zola-anchor" href="#2-development-document-debts" aria-label="Anchor link for: 2-development-document-debts">#</a>
2. Development Document Debts</h4>
<p>This usually happends when the development documentation is incomplete, or the documented function is inconsistent with the source code under development.</p>
<p>There are two following reasons:</p>
<ol>
<li>(<em><strong>Rare</strong></em>) The project has no development documents. There are no relevant technical documents such as coding regulation documents and interface documents. It’s hard enough to just look at the code without looking at the documentation, even if the semantic variable names and function names are easy to understand quickly.</li>
<li>(<em><strong>Common</strong></em>) The project has not updated the development documentation. There were documents in the early stage, but later the documents haven’t been updated ever since, because some requirements are almost newly added temporarily. This results in a large amount of redundant source code when the project is iterated later.</li>
</ol>
<p>The development document is the most systematic and comprehensive reflection of the project. It is easier to understand the project’s functional modules than to see the source code directly. Therefore, it’s very very important to keep the development document being updated in order to stay helpful to other team members.</p>
<h4 id="3-test-document-debts"><a class="zola-anchor" href="#3-test-document-debts" aria-label="Anchor link for: 3-test-document-debts">#</a>
3. Test Document Debts</h4>
<p>It happens when the project has low test coverage and insufficient test cases.</p>
<p>In most companies, in order to control labor costs, software testing is done by software development engineers rather than professional test engineers. This often overlooks some software vulnerabilities and berried some technical debts ahead.</p>
<blockquote>
<p>Lookers-on see most of the game.</p>
</blockquote>
<p>If a company does not attach importance to testing, then the result is definitely an unqualified product.</p>
<h2 id="program-debts"><a class="zola-anchor" href="#program-debts" aria-label="Anchor link for: program-debts">#</a>
Program Debts</h2>
<h4 id="1-structural-debts"><a class="zola-anchor" href="#1-structural-debts" aria-label="Anchor link for: 1-structural-debts">#</a>
1. Structural Debts</h4>
<p>Inadequate assessment of the project structure in the early stage resulted in irrational project organization and high coupling. This makes it difficult to expand and maintain in the later stage.</p>
<p>With business requirements constantly increasing, projects are difficult to move on. Bugs and leaks are prone to occur if things are not taken carefully. Later, it was found that the source was extremely difficult to change, and we will have to restart everything over.</p>
<h4 id="2-coding-debts"><a class="zola-anchor" href="#2-coding-debts" aria-label="Anchor link for: 2-coding-debts">#</a>
2. Coding Debts</h4>
<p>The low coding quality makes it difficult for the development team to work together. When software product iterates, there will be a pile of technical debts. And software products are full of bugs and difficult to maintain.</p>
<p>Here are some common cases:</p>
<ol>
<li>Naming Convention: No naming convention but loosely named.</li>
<li>Code complexity: too many conditional statements / too complicated flow control / too much code nesting (or callback hells)</li>
<li>Code coupling: The parameters, classes, and interfaces are highly coupled</li>
<li>Code lines: There are a lot of unused codes.</li>
</ol>
<p>A Good, unified coding specification brings a lot of advantages to project iteration and maintenance. And also conducive to refactoring and reducing technical debts to a certain extent. Of course each team has its own standards. The above are only reference indicators, not the only indicators.</p>
<h4 id="3-business-logic-debts"><a class="zola-anchor" href="#3-business-logic-debts" aria-label="Anchor link for: 3-business-logic-debts">#</a>
3. Business Logic Debts</h4>
<p>Such “debts” is likely to happen if we patch the bugs or leaks in an opportunistic method every time without in-depth thinking about our business logic or a thorough understanding of the cause of the vulnerability.</p>
<p>To make things easy, we fix the code vulnerability through simple excessive condition judgment, or force the modification of user data in the database.</p>
<p>This kind of unsuccessful one-time plan, is undesirable and meanwhile causes more technical debts.</p>
<h2 id="management-debts"><a class="zola-anchor" href="#management-debts" aria-label="Anchor link for: management-debts">#</a>
Management Debts</h2>
<h4 id="1-deadline-debts"><a class="zola-anchor" href="#1-deadline-debts" aria-label="Anchor link for: 1-deadline-debts">#</a>
1. Deadline Debts</h4>
<p>The deadline of the project is also one of the reasons for technical debt. The current project is mostly aiming at taking the money quickly after the project is done.</p>
<p>In order to seize market share, companies want to produce products in the short term, so software developers must only use some old solutions to speed up the development.</p>
<p>The quality of the developed product is not much different from the previous one, so the software life cycle is as short as the previous one.</p>
<h4 id="2-turnover-debts"><a class="zola-anchor" href="#2-turnover-debts" aria-label="Anchor link for: 2-turnover-debts">#</a>
2. Turnover Debts</h4>
<p>A high-mobility team makes project development difficult or slow.
What’s more, the different capabilities of the developers in the team have their own style. Even though the code style is standardized, but everyone programs in the way they are already used to.</p>
<p>Technical debt has to increase because of the team members’ turnovers.</p>
<h4 id="3-coordinated-debts"><a class="zola-anchor" href="#3-coordinated-debts" aria-label="Anchor link for: 3-coordinated-debts">#</a>
3. Coordinated debts</h4>
<p>We need to let the people on the development team know: “Who am I, where am I, and what am I doing.”</p>
<p>There are 2 notable things:</p>
<ol>
<li>The manager must fully understand his/her own position and do whatever he/she should do, not to interfere too much with the work of the team members, but to monitor the quality of the team members’ work.</li>
<li>The manager must assign the development tasks of the team members seriously and divide tasks and duties clearly. Avoid task duplications.</li>
</ol>
<p>Good cooperation can avoid some repetitive tasks and reduce software redundant code. In another aspect, it’ll promote project development efficiency with less effort, and ensure that it is carried out on schedule.</p>
<h4 id="4-cost-debts"><a class="zola-anchor" href="#4-cost-debts" aria-label="Anchor link for: 4-cost-debts">#</a>
4. Cost Debts</h4>
<p>The hardware and software environment determine the cost debts of the project. Only by controlling the costs can we get more benefits. Smart managers will never be greedy for small advantages. Looking at the immediate benefits will cause greater cost issues later.</p>
<p>Spend the money when we need it, don’t be mean at it.</p>
<h2 id="do-technical-debts-need-to-be-repaid"><a class="zola-anchor" href="#do-technical-debts-need-to-be-repaid" aria-label="Anchor link for: do-technical-debts-need-to-be-repaid">#</a>
Do technical debts need to be repaid?</h2>
<blockquote>
<p>Yes. Certainly.</p>
</blockquote>
<p>We can’t avoid technical debts. But the cost of not paying technical debt is higher.</p>
<p>By paying back the tenchnical debts, we can avoid software vulnerabilities and improve software functionalities. And what’s more, we don’t need to let our teammates work over time too much.</p>
<p>If we don’t, we certainly cannot support large-scale project requirements. To refactor the source code based on the current business logics will have some techinical risks.</p>
<p>It’s up to you whether to pay back the debts.</p>
<p>In short, an experienced and excellent software engineer will never easily bear excessive technical debts. Even if you encounter technical debts, no matter how much, you can still pay back technical debts. Only in this way will it become a veritable software engineer who will not be eliminated by companies or the era.</p>
<h2 id="references"><a class="zola-anchor" href="#references" aria-label="Anchor link for: references">#</a>
References</h2>
<ul>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://www.cnblogs.com/Sroot/p/9110835.html">https://www.cnblogs.com/Sroot/p/9110835.html</a></li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://www.freepik.com/free-photos-vectors/business">Business vector created by pikisuperstar - www.freepik.com</a>: cover image</li>
</ul>
A drill down of React Router: can't be any simpler2020-07-08T00:00:00+00:002020-07-08T00:00:00+00:00https://pitayan.com/posts/react-router-mvp/<h2 id="drill-down"><a class="zola-anchor" href="#drill-down" aria-label="Anchor link for: drill-down">#</a>
Drill down</h2>
<p>Recently, I started drilling down to the bottom of those tools and frameworks’ source code in order to enlighten me on software/system design (as a pretentious developer).</p>
<p>I think you’ve seen that there are many libraries which kernel are with only a few lines of code on Github (e.g. Reselect), and they are starred 1000+. (Actually, react-router isn’t one from them though) Thus, I started to rethink myself: Isn’t it too simple? I could even make one myself (not being self-delusional but arrogant :D). But after reviewing their source code, the fact proves me wrong: I’m young and foolish. Simple things are difficult to make.</p>
<p>After drilling down the <a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/ReactTraining/react-router">react-router</a> repository, I’d like to share some of the key points of how they made the router possible. So in this article, I’ll demonstrate creating a sweet homemade react-router from scratch.</p>
<h2 id="basic-concepts"><a class="zola-anchor" href="#basic-concepts" aria-label="Anchor link for: basic-concepts">#</a>
Basic Concepts</h2>
<p>A very rudimentary router behavior would be:</p>
<ul>
<li>When a user clicks on the <code><a></code> tag, the browser will redirect to the target page without reload</li>
</ul>
<p>In terms with the requirement above, let’s imagine one simple solution to react to the URL change.</p>
<ul>
<li>Use <a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en/docs/Web/API/History_API">history API</a> to handle page redirects programmatically</li>
<li>Use a “link” component (<code><a></code> tag) to intercept redirection click</li>
<li>Use a wrapper component to conditionally render contents by judging the URL</li>
</ul>
<p>(Can’t be any simpler)</p>
<p><img src="./images/workflow.png" alt="workflow" /></p>
<p>In <a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/ReactTraining/react-router">react-router</a>, there are 5 basic APIs:</p>
<ul>
<li><strong>BrowserRouter</strong>: exposes <a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en/docs/Web/API/History_API">history API</a> to its children components</li>
<li><strong>Router</strong>: child component of <strong>BrowserRouter</strong> (and other types of router) which will provide</li>
<li><strong>Link</strong>: redirects without reload</li>
<li><strong>Route</strong>: decides wether to render</li>
<li><strong>RouterContext</strong>: provides URL related context including <a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en/docs/Web/API/History_API">history API</a></li>
</ul>
<p>With these components above, I will make a mini project: hello world.</p>
<pre data-lang="tsx" style="background-color:#212733;color:#ccc9c2;" class="language-tsx "><code class="language-tsx" data-lang="tsx"><span style="font-style:italic;color:#5c6773;">// It will jump between /hello and /world
</span><span style="color:#5ccfe690;"><</span><span style="font-style:italic;color:#5ccfe6;">BrowserRouter</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="font-style:italic;color:#5ccfe6;">Link </span><span style="color:#ffd580;">to</span><span style="color:#f29e74;">=</span><span style="color:#bae67e;">"/hello"</span><span style="color:#5ccfe690;">></span><span>hello</span><span style="color:#5ccfe690;"></</span><span style="font-style:italic;color:#5ccfe6;">Link</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="font-style:italic;color:#5ccfe6;">Link </span><span style="color:#ffd580;">to</span><span style="color:#f29e74;">=</span><span style="color:#bae67e;">"/world"</span><span style="color:#5ccfe690;">></span><span>world</span><span style="color:#5ccfe690;"></</span><span style="font-style:italic;color:#5ccfe6;">Link</span><span style="color:#5ccfe690;">>
</span><span>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="font-style:italic;color:#5ccfe6;">Route </span><span style="color:#ffd580;">path</span><span style="color:#f29e74;">=</span><span style="color:#bae67e;">"/hello" </span><span style="color:#ffd580;">render</span><span style="color:#f29e74;">=</span><span>{() </span><span style="color:#ffa759;">=> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">></span><span>hello</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">></span><span>} </span><span style="color:#5ccfe690;">/>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="font-style:italic;color:#5ccfe6;">Route </span><span style="color:#ffd580;">path</span><span style="color:#f29e74;">=</span><span style="color:#bae67e;">"/world" </span><span style="color:#ffd580;">render</span><span style="color:#f29e74;">=</span><span>{() </span><span style="color:#ffa759;">=> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">></span><span>world</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">></span><span>} </span><span style="color:#5ccfe690;">/>
</span><span style="color:#5ccfe690;"></</span><span style="font-style:italic;color:#5ccfe6;">BrowserRouter</span><span style="color:#5ccfe690;">>
</span></code></pre>
<h2 id="example-repository"><a class="zola-anchor" href="#example-repository" aria-label="Anchor link for: example-repository">#</a>
Example repository</h2>
<p>I created a repo with <a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/facebook/create-react-app">create-react-app</a>.</p>
<p>Repo url: https://github.com/daiyanze/react-router-tear-down</p>
<p>The folder structure looks like this</p>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#ffd580;">-</span><span> public
</span><span style="color:#ffd580;">-</span><span> src
</span><span> </span><span style="color:#ffd580;">-</span><span> router/ </span><span style="font-style:italic;color:#5c6773;"># sweet homemade router
</span><span> </span><span style="color:#ffd580;">...
</span><span> </span><span style="color:#ffd580;">-</span><span> App.js
</span><span> </span><span style="color:#ffd580;">-</span><span> index.js
</span><span style="color:#ffd580;">-</span><span> .gitignore
</span><span style="color:#ffd580;">-</span><span> LICENSE
</span><span style="color:#ffd580;">-</span><span> package.json
</span><span style="color:#ffd580;">-</span><span> README.md
</span></code></pre>
<h4 id="1-clone"><a class="zola-anchor" href="#1-clone" aria-label="Anchor link for: 1-clone">#</a>
1.Clone</h4>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#ffd580;">$</span><span> git clone https://github.com/daiyanze/react-router-tear-down
</span><span style="color:#ffd580;">$</span><span> cd react-router-tear-down
</span></code></pre>
<h4 id="2-start-the-server"><a class="zola-anchor" href="#2-start-the-server" aria-label="Anchor link for: 2-start-the-server">#</a>
2. Start the server</h4>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="font-style:italic;color:#5c6773;"># start the server
</span><span style="color:#ffd580;">$</span><span> yarn </span><span style="color:#f29e74;">& </span><span style="color:#ffd580;">yarn</span><span> start
</span><span>
</span><span style="font-style:italic;color:#5c6773;"># use npm if you prefer
</span><span style="color:#ffd580;">$</span><span> npm i </span><span style="color:#f29e74;">& </span><span style="color:#ffd580;">npm</span><span> run start
</span></code></pre>
<p><img src="./images/tada.png" alt="tada" /></p>
<p>🎉 My React is alive!</p>
<h4 id="3-simplify"><a class="zola-anchor" href="#3-simplify" aria-label="Anchor link for: 3-simplify">#</a>
3. Simplify</h4>
<p>I think I don’t quite need styling sheets and service worker. So I’ll remove these fancy stuff.</p>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#ffd580;">rm</span><span style="color:#ffcc66;"> -f</span><span> src/App.css
</span><span style="color:#ffd580;">rm</span><span style="color:#ffcc66;"> -f</span><span> src/index.css
</span><span style="color:#ffd580;">rm</span><span style="color:#ffcc66;"> -f</span><span> src/logo.svg
</span><span style="color:#ffd580;">rm</span><span style="color:#ffcc66;"> -f</span><span> src/serviceWorker.js
</span></code></pre>
<p>And stuff in my little “hello world”.</p>
<p><code>./src/App.js</code></p>
<pre data-lang="tsx" style="background-color:#212733;color:#ccc9c2;" class="language-tsx "><code class="language-tsx" data-lang="tsx"><span style="color:#ffa759;">import </span><span>React </span><span style="color:#ffa759;">from </span><span style="color:#bae67e;">'react'
</span><span>
</span><span style="color:#ffa759;">function </span><span style="color:#ffd580;">App</span><span>() {
</span><span> </span><span style="color:#ffa759;">return </span><span>(
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">></</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span><span> )</span><span style="color:#ccc9c2cc;">;
</span><span>}
</span><span>
</span><span style="color:#ffa759;">export default </span><span>App
</span></code></pre>
<p><code>./src/index.js</code></p>
<pre data-lang="tsx" style="background-color:#212733;color:#ccc9c2;" class="language-tsx "><code class="language-tsx" data-lang="tsx"><span style="color:#ffa759;">import </span><span>React </span><span style="color:#ffa759;">from </span><span style="color:#bae67e;">'react'
</span><span style="color:#ffa759;">import </span><span>ReactDOM </span><span style="color:#ffa759;">from </span><span style="color:#bae67e;">'react-dom'
</span><span style="color:#ffa759;">import </span><span>App </span><span style="color:#ffa759;">from </span><span style="color:#bae67e;">'./App'
</span><span>
</span><span>ReactDOM</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">render</span><span>(
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="font-style:italic;color:#5ccfe6;">React.StrictMode</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="font-style:italic;color:#5ccfe6;">App </span><span style="color:#5ccfe690;">/>
</span><span> </span><span style="color:#5ccfe690;"></</span><span style="font-style:italic;color:#5ccfe6;">React.StrictMode</span><span style="color:#5ccfe690;">></span><span style="color:#ccc9c2cc;">,
</span><span> document</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">getElementById</span><span>(</span><span style="color:#bae67e;">'root'</span><span>)
</span><span>)</span><span style="color:#ccc9c2cc;">;
</span><span>
</span></code></pre>
<p>Now the page is completely blank. Let me create each of the components to bring it back to life.</p>
<h2 id="start-scratching"><a class="zola-anchor" href="#start-scratching" aria-label="Anchor link for: start-scratching">#</a>
Start scratching</h2>
<p>Now I will start implementing these files under <code>./src/router</code> folder.</p>
<pre style="background-color:#212733;color:#ccc9c2;"><code><span>src/router/
</span><span> RouterContext.js
</span><span> Route.js
</span><span> Router.js
</span><span> BrowserRouter.js
</span><span> Link.js
</span></code></pre>
<h4 id="routercontext"><a class="zola-anchor" href="#routercontext" aria-label="Anchor link for: routercontext">#</a>
RouterContext</h4>
<p>In order to let all of the components access some properties, I need to create a context to provide the URL related objects for the decendant components.</p>
<p><code>./src/router/RouterContext.js</code></p>
<pre data-lang="tsx" style="background-color:#212733;color:#ccc9c2;" class="language-tsx "><code class="language-tsx" data-lang="tsx"><span style="color:#ffa759;">import </span><span>React </span><span style="color:#ffa759;">from </span><span style="color:#bae67e;">"react"
</span><span>
</span><span style="color:#ffa759;">export const </span><span>RouterContext </span><span style="color:#f29e74;">= </span><span>React</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">createContext</span><span>()
</span></code></pre>
<p>As simple as that!</p>
<h4 id="route"><a class="zola-anchor" href="#route" aria-label="Anchor link for: route">#</a>
Route</h4>
<p>The <strong>Route</strong> will render the content once the <code>path</code> matches the current URL.</p>
<p>Properties:</p>
<ul>
<li><strong>path</strong> (string): The matching URL</li>
<li><strong>children</strong> (Function): The children component that ignores the <code>path</code> matching</li>
<li><strong>render</strong> (Function): Similar to <strong>children</strong> but considers <code>path</code> matching</li>
<li><strong>component</strong> (Component): Invokes <code>React.cloneElement</code></li>
</ul>
<p>For the component properties, there’s a rendering order.</p>
<pre data-lang="txt" style="background-color:#212733;color:#ccc9c2;" class="language-txt "><code class="language-txt" data-lang="txt"><span>children > component > render
</span></code></pre>
<p>This means when all of the above props come up in the same component, only <code>children</code> will render. If <code>children</code> doesn’t exist, then <code>component</code> will take effect. Else then <code>render</code>…</p>
<pre data-lang="tsx" style="background-color:#212733;color:#ccc9c2;" class="language-tsx "><code class="language-tsx" data-lang="tsx"><span style="color:#ffd580;">render </span><span>() {
</span><span> </span><span style="color:#ffa759;">return </span><span>(
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="font-style:italic;color:#5ccfe6;">Route
</span><span> </span><span style="color:#ffd580;">children</span><span style="color:#f29e74;">=</span><span>{ChildrenComponents} </span><span style="font-style:italic;color:#5c6773;">// This will be rendered
</span><span> </span><span style="color:#ffd580;">component</span><span style="color:#f29e74;">=</span><span>{Component} </span><span style="font-style:italic;color:#5c6773;">// Nope
</span><span> </span><span style="color:#ffd580;">render</span><span style="color:#f29e74;">=</span><span>{() </span><span style="color:#ffa759;">=> </span><span>Component} </span><span style="font-style:italic;color:#5c6773;">// Nope
</span><span> </span><span style="color:#5ccfe690;">/>
</span><span> )
</span><span>}
</span></code></pre>
<p><code>./src/router/Route.js</code></p>
<pre data-lang="tsx" style="background-color:#212733;color:#ccc9c2;" class="language-tsx "><code class="language-tsx" data-lang="tsx"><span style="color:#ffa759;">import </span><span>React</span><span style="color:#ccc9c2cc;">, </span><span>{ Component } </span><span style="color:#ffa759;">from </span><span style="color:#bae67e;">"react"
</span><span style="color:#ffa759;">import </span><span>{ RouterContext } </span><span style="color:#ffa759;">from </span><span style="color:#bae67e;">"./RouterContext"
</span><span style="color:#ffa759;">import </span><span>{ matchPath } </span><span style="color:#ffa759;">from </span><span style="color:#bae67e;">"react-router" </span><span style="font-style:italic;color:#5c6773;">// allow me to borrow its beautiful function
</span><span>
</span><span style="color:#ffa759;">export default class </span><span style="color:#73d0ff;">Route </span><span style="color:#ffa759;">extends </span><span style="text-decoration:underline;color:#73d0ff;">Component </span><span>{
</span><span> </span><span style="color:#ffd580;">render</span><span>() {
</span><span> </span><span style="color:#ffa759;">return </span><span>(
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="font-style:italic;color:#5ccfe6;">RouterContext.Consumer</span><span style="color:#5ccfe690;">>
</span><span> {
</span><span> </span><span style="color:#ffcc66;">context </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="color:#ffa759;">const </span><span>{ location } </span><span style="color:#f29e74;">= </span><span>context
</span><span>
</span><span> </span><span style="color:#ffa759;">const </span><span>{ path</span><span style="color:#ccc9c2cc;">, </span><span>children</span><span style="color:#ccc9c2cc;">, </span><span>component</span><span style="color:#ccc9c2cc;">, </span><span>render</span><span style="color:#ccc9c2cc;">, </span><span>computedMatch } </span><span style="color:#f29e74;">= </span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span>props
</span><span>
</span><span> </span><span style="color:#ffa759;">const </span><span>match </span><span style="color:#f29e74;">= </span><span>computedMatch
</span><span> </span><span style="color:#f29e74;">? </span><span>computedMatch
</span><span> </span><span style="color:#f29e74;">: </span><span>path </span><span style="color:#f29e74;">? </span><span style="color:#ffd580;">matchPath</span><span>(location</span><span style="color:#f29e74;">.</span><span>pathname</span><span style="color:#ccc9c2cc;">, </span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span>props) </span><span style="color:#f29e74;">: </span><span>context</span><span style="color:#f29e74;">.</span><span>match
</span><span>
</span><span> </span><span style="color:#ffa759;">const </span><span>props </span><span style="color:#f29e74;">= </span><span>{
</span><span> </span><span style="color:#f29e74;">...</span><span>context</span><span style="color:#ccc9c2cc;">,
</span><span> location</span><span style="color:#ccc9c2cc;">,
</span><span> match
</span><span> }
</span><span>
</span><span> </span><span style="font-style:italic;color:#5c6773;">// If match
</span><span> </span><span style="font-style:italic;color:#5c6773;">// then children > component > render
</span><span> </span><span style="font-style:italic;color:#5c6773;">// else
</span><span> </span><span style="font-style:italic;color:#5c6773;">// children | null
</span><span> </span><span style="color:#ffa759;">return </span><span>(
</span><span> </span><span style="font-style:italic;color:#5c6773;">// Need to use context again to ensure the provider passes the updated props
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="font-style:italic;color:#5ccfe6;">RouterContext.Provider </span><span style="color:#ffd580;">value</span><span style="color:#f29e74;">=</span><span>{props}</span><span style="color:#5ccfe690;">>
</span><span> {
</span><span> match
</span><span> </span><span style="color:#f29e74;">? </span><span>children </span><span style="font-style:italic;color:#5c6773;">// Children comes first
</span><span> </span><span style="color:#f29e74;">? </span><span>(</span><span style="color:#f29e74;">typeof </span><span>children </span><span style="color:#f29e74;">=== </span><span style="color:#bae67e;">"function" </span><span style="color:#f29e74;">? </span><span style="color:#ffd580;">children</span><span>(props) </span><span style="color:#f29e74;">: </span><span>children)
</span><span> </span><span style="color:#f29e74;">: </span><span>(component </span><span style="font-style:italic;color:#5c6773;">// Component comes second
</span><span> </span><span style="color:#f29e74;">? </span><span>React</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">createElement</span><span>(component</span><span style="color:#ccc9c2cc;">, </span><span>props)
</span><span> </span><span style="color:#f29e74;">: </span><span>(render </span><span style="color:#f29e74;">? </span><span style="color:#ffd580;">render</span><span>(props) </span><span style="color:#f29e74;">: </span><span style="color:#ffcc66;">null</span><span>)) </span><span style="font-style:italic;color:#5c6773;">// Render comes last
</span><span> </span><span style="color:#f29e74;">: </span><span>(</span><span style="color:#f29e74;">typeof </span><span>children </span><span style="color:#f29e74;">=== </span><span style="color:#bae67e;">"function" </span><span style="color:#f29e74;">? </span><span style="color:#ffd580;">children</span><span>(props) </span><span style="color:#f29e74;">: </span><span style="color:#ffcc66;">null</span><span>)
</span><span> }
</span><span> </span><span style="color:#5ccfe690;"></</span><span style="font-style:italic;color:#5ccfe6;">RouterContext.Provider</span><span style="color:#5ccfe690;">>
</span><span> )
</span><span> }
</span><span> }
</span><span> </span><span style="color:#5ccfe690;"></</span><span style="font-style:italic;color:#5ccfe6;">RouterContext.Consumer</span><span style="color:#5ccfe690;">>
</span><span> )
</span><span> }
</span><span>}
</span></code></pre>
<h4 id="browserrouter"><a class="zola-anchor" href="#browserrouter" aria-label="Anchor link for: browserrouter">#</a>
BrowserRouter</h4>
<p>The <strong>BrowserRouter</strong> uses HTML5 <a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en/docs/Web/API/History_API">history API</a> (<code>pushState</code> <code>replaceState</code> <code>popState</code> etc.) to sync up the UI and URL.</p>
<p>All of those components that needs the <a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en/docs/Web/API/History_API">history</a> context have to stay as <strong>BrowserRouter</strong> children component. Otherwise, it will throw errors.</p>
<p>Properties:</p>
<ul>
<li>
<p><strong>basename</strong> (string): The base of your app’s entire URL</p>
<pre data-lang="html" style="background-color:#212733;color:#ccc9c2;" class="language-html "><code class="language-html" data-lang="html"><span style="font-style:italic;color:#5c6773;"><!-- For instance, if "/app" is our URL base, "/app" will be prepended to all URLs -->
</span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">BrowserRouter </span><span style="color:#ffd580;">basename</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"/app"</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">Link </span><span style="color:#ffd580;">to</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"/somewhere"</span><span style="color:#5ccfe690;">>
</span><span> Take me to somewhere
</span><span> </span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">Link</span><span style="color:#5ccfe690;">>
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">BrowserRouter</span><span style="color:#5ccfe690;">>
</span><span>
</span><span style="font-style:italic;color:#5c6773;"><!-- The above will be compiled to -->
</span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">a </span><span style="color:#ffd580;">href</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"/app/somewhere"</span><span style="color:#5ccfe690;">></span><span>Take me to somewhere</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">a</span><span style="color:#5ccfe690;">>
</span></code></pre>
</li>
</ul>
<p><code>./src/router/BrowserRouter.js</code></p>
<pre data-lang="tsx" style="background-color:#212733;color:#ccc9c2;" class="language-tsx "><code class="language-tsx" data-lang="tsx"><span style="color:#ffa759;">import </span><span>React</span><span style="color:#ccc9c2cc;">, </span><span>{ Component } </span><span style="color:#ffa759;">from </span><span style="color:#bae67e;">"react"
</span><span style="color:#ffa759;">import </span><span>{ createBrowserHistory } </span><span style="color:#ffa759;">from </span><span style="color:#bae67e;">"history"
</span><span style="color:#ffa759;">import </span><span>Router </span><span style="color:#ffa759;">from </span><span style="color:#bae67e;">"./Router"
</span><span>
</span><span style="color:#ffa759;">export default class </span><span style="color:#73d0ff;">BrowserRouter </span><span style="color:#ffa759;">extends </span><span style="text-decoration:underline;color:#73d0ff;">Component </span><span>{
</span><span> </span><span style="color:#ffa759;">constructor</span><span>(</span><span style="color:#ffcc66;">props</span><span>) {
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">super</span><span>(props)
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span>history </span><span style="color:#f29e74;">= </span><span style="color:#ffd580;">createBrowserHistory</span><span>()
</span><span> }
</span><span>
</span><span> </span><span style="color:#ffd580;">render</span><span>() {
</span><span> </span><span style="color:#ffa759;">return </span><span style="color:#5ccfe690;"><</span><span style="font-style:italic;color:#5ccfe6;">Router </span><span style="color:#ffd580;">children</span><span style="color:#f29e74;">=</span><span>{</span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span>props</span><span style="color:#f29e74;">.</span><span>children} </span><span style="color:#ffd580;">history</span><span style="color:#f29e74;">=</span><span>{</span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span>history} </span><span style="color:#5ccfe690;">/>
</span><span> }
</span><span>}
</span></code></pre>
<h4 id="router"><a class="zola-anchor" href="#router" aria-label="Anchor link for: router">#</a>
Router</h4>
<p>In <a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/ReactTraining/react-router">react-router</a>, <strong>Router</strong> component is just a context provider which will listen to <code>location</code> changes. It absorbs the <a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en/docs/Web/API/History_API">history</a> passed from <strong>BrowserRouter</strong> as its state.</p>
<p><code>./src/router/Router.js</code></p>
<pre data-lang="tsx" style="background-color:#212733;color:#ccc9c2;" class="language-tsx "><code class="language-tsx" data-lang="tsx"><span style="color:#ffa759;">import </span><span>React</span><span style="color:#ccc9c2cc;">, </span><span>{ Component } </span><span style="color:#ffa759;">from </span><span style="color:#bae67e;">"react"
</span><span style="color:#ffa759;">import </span><span>{ RouterContext } </span><span style="color:#ffa759;">from </span><span style="color:#bae67e;">"./RouterContext"
</span><span>
</span><span style="color:#ffa759;">export default class </span><span style="color:#73d0ff;">Router </span><span style="color:#ffa759;">extends </span><span style="text-decoration:underline;color:#73d0ff;">Component </span><span>{
</span><span> </span><span style="font-style:italic;color:#5c6773;">// Check root path
</span><span> </span><span style="color:#ffa759;">static </span><span style="color:#ffd580;">computeRootMatch </span><span>(</span><span style="color:#ffcc66;">pathname</span><span>) {
</span><span> </span><span style="color:#ffa759;">return </span><span>{
</span><span> path</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">"/"</span><span style="color:#ccc9c2cc;">,
</span><span> url</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">"/"</span><span style="color:#ccc9c2cc;">,
</span><span> params</span><span style="color:#ccc9c2cc;">: </span><span>{}</span><span style="color:#ccc9c2cc;">,
</span><span> isExact</span><span style="color:#ccc9c2cc;">: </span><span>pathname </span><span style="color:#f29e74;">=== </span><span style="color:#bae67e;">"/"
</span><span> }
</span><span> }
</span><span>
</span><span> </span><span style="color:#ffa759;">constructor </span><span>(</span><span style="color:#ffcc66;">props</span><span>) {
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">super</span><span>(props)
</span><span>
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span>state </span><span style="color:#f29e74;">= </span><span>{
</span><span> location</span><span style="color:#ccc9c2cc;">: </span><span>props</span><span style="color:#f29e74;">.</span><span>history</span><span style="color:#f29e74;">.</span><span>location
</span><span> }
</span><span>
</span><span> }
</span><span>
</span><span> </span><span style="color:#ffd580;">componentDidMount </span><span>() {
</span><span> </span><span style="font-style:italic;color:#5c6773;">// update location when url changes
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span>props</span><span style="color:#f29e74;">.</span><span>history</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">listen</span><span>(({ </span><span style="color:#ffcc66;">location </span><span>}) </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">setState</span><span>({ location })
</span><span> })
</span><span> }
</span><span>
</span><span> </span><span style="color:#ffd580;">render </span><span>() {
</span><span> </span><span style="color:#ffa759;">const </span><span>{ history</span><span style="color:#ccc9c2cc;">, </span><span>children } </span><span style="color:#f29e74;">= </span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span>props
</span><span>
</span><span> </span><span style="color:#ffa759;">return </span><span>(
</span><span> </span><span style="font-style:italic;color:#5c6773;">// Provide `history` `location` `match` to children components
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="font-style:italic;color:#5ccfe6;">RouterContext.Provider
</span><span> </span><span style="color:#ffd580;">value</span><span style="color:#f29e74;">=</span><span>{{
</span><span> history</span><span style="color:#ccc9c2cc;">,
</span><span> location</span><span style="color:#ccc9c2cc;">: </span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span>state</span><span style="color:#f29e74;">.</span><span>location</span><span style="color:#ccc9c2cc;">,
</span><span> match</span><span style="color:#ccc9c2cc;">: </span><span>Router</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">computeRootMatch</span><span>(</span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span>state</span><span style="color:#f29e74;">.</span><span>location</span><span style="color:#f29e74;">.</span><span>pathname)
</span><span> }}</span><span style="color:#5ccfe690;">>
</span><span> { children }
</span><span> </span><span style="color:#5ccfe690;"></</span><span style="font-style:italic;color:#5ccfe6;">RouterContext.Provider</span><span style="color:#5ccfe690;">>
</span><span> )
</span><span> }
</span><span>}
</span></code></pre>
<h4 id="link"><a class="zola-anchor" href="#link" aria-label="Anchor link for: link">#</a>
Link</h4>
<p>All I need to do is to intercept the click event and use the <a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en/docs/Web/API/History_API">history</a> API to change the url.</p>
<p><code>./src/router/Link.js</code></p>
<pre data-lang="tsx" style="background-color:#212733;color:#ccc9c2;" class="language-tsx "><code class="language-tsx" data-lang="tsx"><span style="color:#ffa759;">import </span><span>React</span><span style="color:#ccc9c2cc;">, </span><span>{ Component } </span><span style="color:#ffa759;">from </span><span style="color:#bae67e;">"react"
</span><span style="color:#ffa759;">import </span><span>{ RouterContext } </span><span style="color:#ffa759;">from </span><span style="color:#bae67e;">"./RouterContext"
</span><span>
</span><span style="color:#ffa759;">export default class </span><span style="color:#73d0ff;">Link </span><span style="color:#ffa759;">extends </span><span style="text-decoration:underline;color:#73d0ff;">Component </span><span>{
</span><span> </span><span style="color:#ffa759;">static </span><span>contextType </span><span style="color:#f29e74;">= </span><span>RouterContext
</span><span>
</span><span> </span><span style="color:#ffd580;">handleClick </span><span style="color:#f29e74;">= </span><span style="color:#ffcc66;">e </span><span style="color:#ffa759;">=> </span><span>{
</span><span> e</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">preventDefault</span><span>()
</span><span> </span><span style="font-style:italic;color:#5c6773;">// Push the target location to history
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span>context</span><span style="color:#f29e74;">.</span><span>history</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">push</span><span>(</span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span>props</span><span style="color:#f29e74;">.</span><span>to)
</span><span> }
</span><span>
</span><span> </span><span style="color:#ffd580;">render</span><span>() {
</span><span> </span><span style="color:#ffa759;">const </span><span>{ to</span><span style="color:#ccc9c2cc;">, </span><span>children</span><span style="color:#ccc9c2cc;">, </span><span style="color:#f29e74;">...</span><span>others } </span><span style="color:#f29e74;">= </span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span>props
</span><span> </span><span style="color:#ffa759;">return </span><span>(
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">a </span><span style="color:#ffd580;">onClick</span><span style="color:#f29e74;">=</span><span>{</span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span>handleClick} </span><span style="color:#ffd580;">href</span><span style="color:#f29e74;">=</span><span>{to} {</span><span style="color:#f29e74;">...</span><span>others}</span><span style="color:#5ccfe690;">>
</span><span> { children }
</span><span> </span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">a</span><span style="color:#5ccfe690;">>
</span><span> )
</span><span> }
</span><span>}
</span></code></pre>
<h2 id="all-components-are-done"><a class="zola-anchor" href="#all-components-are-done" aria-label="Anchor link for: all-components-are-done">#</a>
All components are done</h2>
<p>Em… still nothing come out.</p>
<p>I see. I need to add routes and links to <code>App.js</code>.</p>
<p><code>./src/App.js</code></p>
<pre data-lang="tsx" style="background-color:#212733;color:#ccc9c2;" class="language-tsx "><code class="language-tsx" data-lang="tsx"><span style="color:#ffa759;">import </span><span>React </span><span style="color:#ffa759;">from </span><span style="color:#bae67e;">'react'
</span><span>
</span><span style="color:#ffa759;">function </span><span style="color:#ffd580;">App</span><span>() {
</span><span> </span><span style="color:#ffa759;">return </span><span>(
</span><span> </span><span style="font-style:italic;color:#5c6773;">// Center the elements
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">div </span><span style="color:#ffd580;">style</span><span style="color:#f29e74;">=</span><span>{{ textAlign</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">"center"</span><span style="color:#ccc9c2cc;">, </span><span>width</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">"100%" </span><span>}}</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="font-style:italic;color:#5ccfe6;">BrowserRouter</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="font-style:italic;color:#5ccfe6;">Link </span><span style="color:#ffd580;">to</span><span style="color:#f29e74;">=</span><span style="color:#bae67e;">"/hello"</span><span style="color:#5ccfe690;">></span><span>hello</span><span style="color:#5ccfe690;"></</span><span style="font-style:italic;color:#5ccfe6;">Link</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="font-style:italic;color:#5ccfe6;">Link </span><span style="color:#ffd580;">to</span><span style="color:#f29e74;">=</span><span style="color:#bae67e;">"/world"</span><span style="color:#5ccfe690;">></span><span>world</span><span style="color:#5ccfe690;"></</span><span style="font-style:italic;color:#5ccfe6;">Link</span><span style="color:#5ccfe690;">>
</span><span>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="font-style:italic;color:#5ccfe6;">Route </span><span style="color:#ffd580;">path</span><span style="color:#f29e74;">=</span><span style="color:#bae67e;">"/hello" </span><span style="color:#ffd580;">render</span><span style="color:#f29e74;">=</span><span>{() </span><span style="color:#ffa759;">=> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">></span><span>hello</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">></span><span>} </span><span style="color:#5ccfe690;">/>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="font-style:italic;color:#5ccfe6;">Route </span><span style="color:#ffd580;">path</span><span style="color:#f29e74;">=</span><span style="color:#bae67e;">"/world" </span><span style="color:#ffd580;">render</span><span style="color:#f29e74;">=</span><span>{() </span><span style="color:#ffa759;">=> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">></span><span>world</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">></span><span>} </span><span style="color:#5ccfe690;">/>
</span><span> </span><span style="color:#5ccfe690;"></</span><span style="font-style:italic;color:#5ccfe6;">BrowserRouter</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span><span> )</span><span style="color:#ccc9c2cc;">;
</span><span>}
</span><span>
</span><span style="color:#ffa759;">export default </span><span>App
</span></code></pre>
<p>Now my little “hello world” is done. Let’s see the result.</p>
<p><img src="./images/success.gif" alt="success" /></p>
<p>Yay! It works!</p>
<h2 id="conclusion"><a class="zola-anchor" href="#conclusion" aria-label="Anchor link for: conclusion">#</a>
Conclusion</h2>
<p><a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/ReactTraining/react-router">react-router</a> indeed is a very very solid middleware for <a rel="noopener nofollow noreferrer" target="_blank" href="https://reactjs.org">React</a>. After exploring the whole project, I can see that there are a lot of great efforts to make it as simple as possible. What I created myself was just a very basic stuff which is based on <a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/ReactTraining/react-router/tree/v1.0.0">react-router v1.0.0</a> and it is only an example to help me understand how router works. Gotta keeps study and exploring so that I could improve and become a better engineer.</p>
<h2 id="references"><a class="zola-anchor" href="#references" aria-label="Anchor link for: references">#</a>
References</h2>
<ul>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://reactrouter.com/">https://reactrouter.com/</a></li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/ReactTraining/react-router">https://github.com/ReactTraining/react-router</a></li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://knowbody.github.io/react-router-docs/">https://knowbody.github.io/react-router-docs/</a></li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://stackoverflow.com/questions/42123261/programmatically-navigate-using-react-router-v4">https://stackoverflow.com/questions/42123261/programmatically-navigate-using-react-router-v4</a></li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://www.kirupa.com/react/creating_single_page_app_react_using_react_router.htm">https://www.kirupa.com/react/creating_single_page_app_react_using_react_router.htm</a></li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://reactjs.net/features/react-router.html">https://reactjs.net/features/react-router.html</a></li>
</ul>
Javascript key-value store: understand some cool built-in Objects2020-07-02T00:00:00+00:002020-07-02T00:00:00+00:00https://pitayan.com/posts/javascript-key-value-array/<p>In this article, I’ll show you how to handle Javascript “key-value” data type with its special “Array”.</p>
<h2 id="can-you-use-array-as-a-key-value-store"><a class="zola-anchor" href="#can-you-use-array-as-a-key-value-store" aria-label="Anchor link for: can-you-use-array-as-a-key-value-store">#</a>
Can you use “Array” as a “key-value” store?</h2>
<p>Technically, <strong>No</strong>…</p>
<p>An <a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en-US/docs/Glossary/array">Array</a> is used for storing ordered list of data. This means that the key to each of its value is actually a number(string number).</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#ffa759;">let </span><span>arr </span><span style="color:#f29e74;">= </span><span>[</span><span style="color:#ffcc66;">0</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">1</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">2</span><span>]
</span><span>
</span><span>arr[</span><span style="color:#ffcc66;">0</span><span>] </span><span style="font-style:italic;color:#5c6773;">// 0
</span><span>arr[</span><span style="color:#ffcc66;">1</span><span>] </span><span style="font-style:italic;color:#5c6773;">// 1
</span><span>arr[</span><span style="color:#bae67e;">'2'</span><span>] </span><span style="font-style:italic;color:#5c6773;">// 2
</span><span>arr[</span><span style="color:#ffcc66;">2</span><span>] </span><span style="color:#f29e74;">=== </span><span>arr[</span><span style="color:#bae67e;">'2'</span><span>] </span><span style="font-style:italic;color:#5c6773;">// true
</span></code></pre>
<p>As we all know, <a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en-US/docs/Glossary/array">Array</a> is also an <a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object">Object</a> in Javascript. Even though assigning a new property to an <a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en-US/docs/Glossary/array">Array</a> made it work. The methods within <a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en-US/docs/Glossary/array">Array</a> is not going work in the way you expected.</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#ffa759;">let </span><span>arr </span><span style="color:#f29e74;">= </span><span>[]
</span><span>
</span><span>arr</span><span style="color:#f29e74;">.</span><span>article </span><span style="color:#f29e74;">= </span><span style="color:#bae67e;">'Javascript key-value store: is this unique among all programming languages?'
</span><span>arr</span><span style="color:#f29e74;">.</span><span>url </span><span style="color:#f29e74;">= </span><span style="color:#bae67e;">'https://pitayan.com/posts/javascript-key-value-array/'
</span><span>
</span><span style="font-style:italic;color:#5c6773;">// Now there should be an record in the arr but...
</span><span>arr[</span><span style="color:#ffcc66;">0</span><span>] </span><span style="font-style:italic;color:#5c6773;">// undefined
</span><span>arr</span><span style="color:#f29e74;">.</span><span>length </span><span style="font-style:italic;color:#5c6773;">// 0
</span><span>
</span><span style="font-style:italic;color:#5c6773;">// aritcle is one property other than a record
</span><span>arr[</span><span style="color:#bae67e;">'article'</span><span>] </span><span style="font-style:italic;color:#5c6773;">// Javascript key-value store: is this unique among all programming languages?
</span><span>arr[</span><span style="color:#bae67e;">'url'</span><span>] </span><span style="font-style:italic;color:#5c6773;">// https://pitayan.com/posts/javascript-key-value-array/
</span></code></pre>
<h2 id="object-is-all-you-want"><a class="zola-anchor" href="#object-is-all-you-want" aria-label="Anchor link for: object-is-all-you-want">#</a>
“Object” is all you want</h2>
<p>There’s no key value array in Javascript. Basically what you want with Javascript on storing the “key-value” is a data structure called <a rel="noopener nofollow noreferrer" target="_blank" href="https://www.w3schools.com/java/java_hashmap.asp">hashmap</a>. But Javascript doesn’t have <a rel="noopener nofollow noreferrer" target="_blank" href="https://www.w3schools.com/java/java_hashmap.asp">hashmap</a> itself. It uses a different data type <a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object">Object</a> which has the almost similar ability of a <a rel="noopener nofollow noreferrer" target="_blank" href="https://www.w3schools.com/java/java_hashmap.asp">hashmap</a>.</p>
<p>When you open up a console in Chrome browser and declare an <code>Object</code>, you’ll get the following result: A variable defind by a pair of lovely curly brackets.
<img src="./images/object.png" alt="object" /></p>
<p>Of course you could assign some properties (key-value) in it. It’s very simple. Just define the key and the value, you’ll have your data store in a few seconds.</p>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="font-style:italic;color:#5c6773;">// Define the key-value directly in Object
</span><span style="color:#ffa759;">let </span><span>obj </span><span style="color:#f29e74;">= </span><span>{
</span><span> url</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">'https://pitayan.com/posts/javascript-key-value-array/'
</span><span>}
</span><span>
</span><span style="font-style:italic;color:#5c6773;">// Additionally assign the value to a new key
</span><span>obj</span><span style="color:#f29e74;">.</span><span>article: </span><span style="color:#bae67e;">'Javascript key-value store: is this unique among all programming languages?'
</span></code></pre>
<p><a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object">Object</a> is quite a common and frequently used built-in Object. In order to meet some functionality demands, you may need some other built-in Objects like Map and Set (WeakMap WeakSet). They are used for data recombiniation and storage. Let’s take a look.</p>
<h2 id="other-key-value-store-objects-in-javascript"><a class="zola-anchor" href="#other-key-value-store-objects-in-javascript" aria-label="Anchor link for: other-key-value-store-objects-in-javascript">#</a>
Other “key-value” store Objects in Javascript</h2>
<p>Other than just using <a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object">Object</a> as a key-value store, you could also utilize the following Javascript standard built-in Objects.</p>
<ul>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map"><em><strong>Map</strong></em></a>:
<ul>
<li>a key-value pair collection</li>
<li>iterable</li>
</ul>
</li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap"><em><strong>WeakMap</strong></em></a>:
<ul>
<li>a reverted Map that stores key-value pairs but the “key” is the Object</li>
<li>All entries are Objects</li>
<li>Entries are weak references and retrievable by GC. No memory leak.</li>
<li>non-iterable</li>
</ul>
</li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set"><em><strong>Set</strong></em></a>:
<ul>
<li>Unique, unordered</li>
<li>Key equals value. Or there’s only value but no key</li>
<li>Iterable</li>
</ul>
</li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakSet"><em><strong>WeakSet</strong></em></a>:
<ul>
<li>All entries are Objects</li>
<li>Entries are weak references and retrievable by GC. Key can be any type.</li>
<li>non-iterable</li>
</ul>
</li>
</ul>
<h3 id="map"><a class="zola-anchor" href="#map" aria-label="Anchor link for: map">#</a>
Map</h3>
<p>Here are the methods/properties for manipulating the entries of a <a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map">Map</a></p>
<ul>
<li>set(key, value): add a new element to the collection</li>
<li>get(key, value): get an element from the collection</li>
<li>size: the amount of the included elements</li>
<li>delete(key): remove an element from the collection</li>
<li>has(key): check if an element exists</li>
<li>keys(): get all keys</li>
<li>values(): get all values, same to keys()</li>
<li>entries(): get all iterators of all key-value pairs</li>
<li>forEach(): loop through all elements</li>
<li>clear(): remove all elements from collection</li>
</ul>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#ffa759;">let </span><span>map </span><span style="color:#f29e74;">= new </span><span style="color:#73d0ff;">Map</span><span>()
</span><span>
</span><span>map</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">set</span><span>(</span><span style="color:#ffcc66;">1</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">1</span><span>) </span><span style="font-style:italic;color:#5c6773;">// Map { 1 }
</span><span>map</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">set</span><span>(</span><span style="color:#ffcc66;">2</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">2</span><span>) </span><span style="font-style:italic;color:#5c6773;">// Map { 1, 2 }
</span><span>map</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">get</span><span>(</span><span style="color:#ffcc66;">1</span><span>) </span><span style="font-style:italic;color:#5c6773;">// 1
</span><span>map</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">get</span><span>(</span><span style="color:#ffcc66;">2</span><span>) </span><span style="font-style:italic;color:#5c6773;">// 2
</span><span>map</span><span style="color:#f29e74;">.</span><span>size </span><span style="font-style:italic;color:#5c6773;">// 2
</span><span>map</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">has</span><span>(</span><span style="color:#ffcc66;">1</span><span>) </span><span style="font-style:italic;color:#5c6773;">// true
</span><span>map</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">delete</span><span>(</span><span style="color:#ffcc66;">1</span><span>) </span><span style="font-style:italic;color:#5c6773;">// true
</span><span>map</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">keys</span><span>() </span><span style="font-style:italic;color:#5c6773;">// Map Iterator [2]
</span><span>map</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">values</span><span>() </span><span style="font-style:italic;color:#5c6773;">// Map Iterator [2]
</span><span>map</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">entries</span><span>() </span><span style="font-style:italic;color:#5c6773;">// Map Iterator [[2, 2]]
</span><span>map</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">forEach</span><span>((</span><span style="color:#ffcc66;">d</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">k</span><span>) </span><span style="color:#ffa759;">=> </span><span>{ </span><span style="color:#f29e74;">... </span><span>})
</span><span>map</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">clear</span><span>() </span><span style="font-style:italic;color:#5c6773;">// undefined
</span></code></pre>
<h3 id="weakmap"><a class="zola-anchor" href="#weakmap" aria-label="Anchor link for: weakmap">#</a>
WeakMap</h3>
<p>Here are the methods/properties for manipulating the entries of a <a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap">WeakMap</a></p>
<ul>
<li>set(key): add a new element to the collection</li>
<li>get(key): get an element</li>
<li>has(value): check if an element exists</li>
<li>delete(value): remove an element from the collection</li>
</ul>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#ffa759;">let </span><span>weakmap </span><span style="color:#f29e74;">= new </span><span style="color:#73d0ff;">WeakMap</span><span>()
</span><span style="color:#ffa759;">let </span><span>article </span><span style="color:#f29e74;">= </span><span style="color:#bae67e;">'Javascript key-value store: is this unique among all programming languages?'
</span><span style="color:#ffa759;">let </span><span>url </span><span style="color:#f29e74;">= </span><span style="color:#bae67e;">'https://pitayan.com/posts/javascript-key-value-array/'
</span><span>
</span><span>weakmap</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">set</span><span>(article</span><span style="color:#ccc9c2cc;">, </span><span style="color:#bae67e;">'article'</span><span>) </span><span style="font-style:italic;color:#5c6773;">// WeakMap {{...} => "article"}
</span><span>weakmap</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">set</span><span>(url</span><span style="color:#ccc9c2cc;">, </span><span style="color:#bae67e;">'url'</span><span>) </span><span style="font-style:italic;color:#5c6773;">// WeakMap {{...} => "url"}
</span><span>weakmap</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">has</span><span>(article) </span><span style="font-style:italic;color:#5c6773;">// true
</span><span>weakmap</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">has</span><span>(url) </span><span style="font-style:italic;color:#5c6773;">// true
</span><span>
</span><span>weakmap</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">delete</span><span>(article) </span><span style="font-style:italic;color:#5c6773;">// true
</span><span>
</span></code></pre>
<h3 id="set"><a class="zola-anchor" href="#set" aria-label="Anchor link for: set">#</a>
Set</h3>
<p>Here are the methods/properties for manipulating the entries of a <a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set">Set</a></p>
<ul>
<li>size: the amount of the included elements</li>
<li>add(value): add a new element to the collection</li>
<li>delete(value): remove an element from the collection</li>
<li>has(value): check if an element exists</li>
<li>clear(): remove all elements from collection</li>
<li>keys(): get all keys</li>
<li>values(): get all values, same to keys() since <a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set">Set</a> only has values</li>
<li>entries(): get all iterators of all key-value pairs</li>
<li>forEach(): loop through all elements</li>
</ul>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#ffa759;">let </span><span>set </span><span style="color:#f29e74;">= new </span><span style="color:#73d0ff;">Set</span><span>()
</span><span>
</span><span>set</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">add</span><span>(</span><span style="color:#ffcc66;">1</span><span>) </span><span style="font-style:italic;color:#5c6773;">// Set { 1 }
</span><span>set</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">add</span><span>(</span><span style="color:#ffcc66;">2</span><span>) </span><span style="font-style:italic;color:#5c6773;">// Set { 1, 2 }
</span><span>set</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">size</span><span>() </span><span style="font-style:italic;color:#5c6773;">// 1
</span><span>set</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">has</span><span>(</span><span style="color:#ffcc66;">1</span><span>) </span><span style="font-style:italic;color:#5c6773;">// true
</span><span>set</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">delete</span><span>(</span><span style="color:#ffcc66;">1</span><span>) </span><span style="font-style:italic;color:#5c6773;">// true
</span><span>set</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">keys</span><span>() </span><span style="font-style:italic;color:#5c6773;">// Set Iterator [2]
</span><span>set</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">values</span><span>() </span><span style="font-style:italic;color:#5c6773;">// Set Iterator [2]
</span><span>set</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">entries</span><span>() </span><span style="font-style:italic;color:#5c6773;">// Set Iterator [[2, 2]]
</span><span>set</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">forEach</span><span>((</span><span style="color:#ffcc66;">d</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">k</span><span>) </span><span style="color:#ffa759;">=> </span><span>{ </span><span style="color:#f29e74;">... </span><span>})
</span><span>set</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">clear</span><span>() </span><span style="font-style:italic;color:#5c6773;">// undefined
</span></code></pre>
<h3 id="weakset"><a class="zola-anchor" href="#weakset" aria-label="Anchor link for: weakset">#</a>
WeakSet</h3>
<p>Here are the methods/properties for manipulating the entries of a <a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakSet">WeakSet</a></p>
<ul>
<li>add(value): add a new element to the collection</li>
<li>delete(value): remove an element from the collection</li>
<li>has(value): check if an element exists</li>
</ul>
<pre data-lang="js" style="background-color:#212733;color:#ccc9c2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#ffa759;">let </span><span>weakset </span><span style="color:#f29e74;">= new </span><span style="color:#73d0ff;">WeakSet</span><span>()
</span><span style="color:#ffa759;">let </span><span>article </span><span style="color:#f29e74;">= </span><span>{
</span><span> name</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">'Javascript key-value store: is this unique among all programming languages?'
</span><span>}
</span><span style="color:#ffa759;">let </span><span>url </span><span style="color:#f29e74;">= </span><span>{
</span><span> address</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">'https://pitayan.com/posts/javascript-key-value-array/'
</span><span>}
</span><span>
</span><span>weakset</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">add</span><span>(article) </span><span style="font-style:italic;color:#5c6773;">// WeakSet {{...}}
</span><span>weakset</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">add</span><span>(url) </span><span style="font-style:italic;color:#5c6773;">// WeakSet {{...}, {...}}
</span><span>weakset</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">has</span><span>(url) </span><span style="font-style:italic;color:#5c6773;">// true
</span><span>weakset</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">delete</span><span>(article) </span><span style="font-style:italic;color:#5c6773;">// true
</span><span>weakset</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">has</span><span>(article) </span><span style="font-style:italic;color:#5c6773;">// false
</span></code></pre>
<h2 id="references"><a class="zola-anchor" href="#references" aria-label="Anchor link for: references">#</a>
References</h2>
<ul>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects">https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects</a></li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map">https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map</a></li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap">https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap</a></li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set">https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set</a></li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakSet">https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakSet</a></li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://juejin.im/post/5d39d14c518825625337f84e">https://juejin.im/post/5d39d14c518825625337f84e</a></li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://juejin.im/post/5e1d2717e51d4557e97b109b">https://juejin.im/post/5e1d2717e51d4557e97b109b</a></li>
</ul>
Well explained: Javascript in operator2020-07-01T00:00:00+00:002020-07-01T00:00:00+00:00https://pitayan.com/posts/javascript-in-operator/<p>This article is going to uncover the usage of Javascript <code>in</code> operator. <code>in</code> operator is one of the keywords in Javascript. We use it very often in loops or if conditions.</p>
<blockquote>
<p><code>in</code> operator can check if something exists in the Object. It returns <code>true</code> when the first operand is in the Object as a property or exists in the prototype chain.</p>
</blockquote>
<h2 id="1-simple-basic-usage"><a class="zola-anchor" href="#1-simple-basic-usage" aria-label="Anchor link for: 1-simple-basic-usage">#</a>
1. Simple basic usage</h2>
<h3 id="object-property-check"><a class="zola-anchor" href="#object-property-check" aria-label="Anchor link for: object-property-check">#</a>
Object property check</h3>
<p>The follow example returns <code>true</code> because variable <code>obj</code> has all of them.</p>
<pre data-lang="javascript" style="background-color:#212733;color:#ccc9c2;" class="language-javascript "><code class="language-javascript" data-lang="javascript"><span style="color:#ffa759;">let </span><span>obj </span><span style="color:#f29e74;">= </span><span>{
</span><span> javascript</span><span style="color:#ccc9c2cc;">: </span><span style="color:#ffcc66;">1</span><span style="color:#ccc9c2cc;">,
</span><span> html</span><span style="color:#ccc9c2cc;">: </span><span style="color:#ffcc66;">2</span><span style="color:#ccc9c2cc;">,
</span><span> css</span><span style="color:#ccc9c2cc;">: </span><span style="color:#ffcc66;">3
</span><span>}
</span><span>
</span><span style="color:#bae67e;">'javascript' </span><span style="color:#f29e74;">in </span><span>obj </span><span style="font-style:italic;color:#5c6773;">// true
</span><span style="color:#bae67e;">'html' </span><span style="color:#f29e74;">in </span><span>obj </span><span style="font-style:italic;color:#5c6773;">// true
</span><span style="color:#bae67e;">'css' </span><span style="color:#f29e74;">in </span><span>obj </span><span style="font-style:italic;color:#5c6773;">// true
</span></code></pre>
<h3 id="prorotype-chain"><a class="zola-anchor" href="#prorotype-chain" aria-label="Anchor link for: prorotype-chain">#</a>
Prorotype chain</h3>
<p>It is <code>false</code> because <code>python</code> does not exist in <code>obj</code> but if we could add it to the prototype chain the result will turn to <code>true</code></p>
<pre data-lang="javascript" style="background-color:#212733;color:#ccc9c2;" class="language-javascript "><code class="language-javascript" data-lang="javascript"><span style="color:#bae67e;">'python' </span><span style="color:#f29e74;">in </span><span>obj </span><span style="font-style:italic;color:#5c6773;">// false
</span><span>
</span><span style="font-style:italic;color:#5ccfe6;">obj</span><span style="color:#f29e74;">.</span><span>prototype</span><span style="color:#f29e74;">.</span><span>python </span><span style="color:#f29e74;">= </span><span style="color:#ffcc66;">4
</span><span>
</span><span style="color:#bae67e;">'python' </span><span style="color:#f29e74;">in </span><span>obj </span><span style="font-style:italic;color:#5c6773;">// true
</span></code></pre>
<h3 id="deleted-undefined-property"><a class="zola-anchor" href="#deleted-undefined-property" aria-label="Anchor link for: deleted-undefined-property">#</a>
Deleted/Undefined Property</h3>
<p>When a property is deleted from <code>obj</code>, it returns <code>false</code> because we remove the property completely.</p>
<pre data-lang="javascript" style="background-color:#212733;color:#ccc9c2;" class="language-javascript "><code class="language-javascript" data-lang="javascript"><span style="color:#f29e74;">delete </span><span>obj</span><span style="color:#f29e74;">.</span><span>javascript
</span><span>
</span><span style="color:#bae67e;">'javascript' </span><span style="color:#f29e74;">in </span><span>obj </span><span style="font-style:italic;color:#5c6773;">// false
</span></code></pre>
<p>But if we sett the property <code>undefined</code>, the <code>in</code> operator returns true because the property exists it just doesn’t have any value.</p>
<pre data-lang="javascript" style="background-color:#212733;color:#ccc9c2;" class="language-javascript "><code class="language-javascript" data-lang="javascript"><span>obj</span><span style="color:#f29e74;">.</span><span>javascript </span><span style="color:#f29e74;">= </span><span style="color:#ffcc66;">undefined
</span><span>
</span><span style="color:#bae67e;">'javascript' </span><span style="color:#f29e74;">in </span><span>obj </span><span style="font-style:italic;color:#5c6773;">// true
</span></code></pre>
<h2 id="2-other-usages"><a class="zola-anchor" href="#2-other-usages" aria-label="Anchor link for: 2-other-usages">#</a>
2. Other usages</h2>
<p>Other than using <code>in</code> operator in an hashmap Object, we could also apply it to other situations like <code>String</code> <code>Array</code> and some other supported behaviors in Javascript.</p>
<h3 id="if-string-is-in-another-string"><a class="zola-anchor" href="#if-string-is-in-another-string" aria-label="Anchor link for: if-string-is-in-another-string">#</a>
If String is in another String</h3>
<pre data-lang="javascript" style="background-color:#212733;color:#ccc9c2;" class="language-javascript "><code class="language-javascript" data-lang="javascript"><span style="color:#ffa759;">let </span><span>str </span><span style="color:#f29e74;">= </span><span style="color:#bae67e;">'string'
</span><span>
</span><span style="color:#bae67e;">'string' </span><span style="color:#f29e74;">in </span><span>str </span><span style="font-style:italic;color:#5c6773;">// true
</span><span>
</span><span style="color:#bae67e;">'str' </span><span style="color:#f29e74;">in </span><span>str </span><span style="font-style:italic;color:#5c6773;">// true
</span><span>
</span><span style="color:#bae67e;">'ing' </span><span style="color:#f29e74;">in </span><span>str </span><span style="font-style:italic;color:#5c6773;">// true
</span><span>
</span><span style="color:#bae67e;">'something else' </span><span style="color:#f29e74;">in </span><span>str </span><span style="font-style:italic;color:#5c6773;">// false
</span></code></pre>
<h3 id="if-an-index-is-in-an-array"><a class="zola-anchor" href="#if-an-index-is-in-an-array" aria-label="Anchor link for: if-an-index-is-in-an-array">#</a>
If an index is in an Array</h3>
<p>There are only 3 elements in <code>arr</code>, so the fourth element witn index <code>3</code> is out of range.</p>
<pre data-lang="javascript" style="background-color:#212733;color:#ccc9c2;" class="language-javascript "><code class="language-javascript" data-lang="javascript"><span style="color:#ffa759;">let </span><span>arr </span><span style="color:#f29e74;">= </span><span>[</span><span style="color:#bae67e;">'javascript'</span><span style="color:#ccc9c2cc;">, </span><span style="color:#bae67e;">'in'</span><span style="color:#ccc9c2cc;">, </span><span style="color:#bae67e;">'operator'</span><span>]
</span><span>
</span><span style="color:#ffcc66;">0 </span><span style="color:#f29e74;">in </span><span>arr </span><span style="font-style:italic;color:#5c6773;">// true
</span><span style="color:#ffcc66;">1 </span><span style="color:#f29e74;">in </span><span>arr </span><span style="font-style:italic;color:#5c6773;">// true
</span><span style="color:#ffcc66;">2 </span><span style="color:#f29e74;">in </span><span>arr </span><span style="font-style:italic;color:#5c6773;">// true
</span><span>
</span><span style="color:#ffcc66;">3 </span><span style="color:#f29e74;">in </span><span>arr </span><span style="font-style:italic;color:#5c6773;">// false
</span></code></pre>
<h3 id="non-inherited-property-is-in-object"><a class="zola-anchor" href="#non-inherited-property-is-in-object" aria-label="Anchor link for: non-inherited-property-is-in-object">#</a>
Non-inherited property is in Object</h3>
<p><code>obj</code> is inheriting <code>Object</code>. But the native function of <code>toString</code> is an inherited property. To Check if non-inherited property is in Object we need to use <a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty">Object.property.hasOwnProperty()</a> method.</p>
<pre data-lang="javascript" style="background-color:#212733;color:#ccc9c2;" class="language-javascript "><code class="language-javascript" data-lang="javascript"><span style="color:#ffa759;">let </span><span>obj </span><span style="color:#f29e74;">= </span><span>{}
</span><span>
</span><span style="color:#bae67e;">'toString' </span><span style="color:#f29e74;">in </span><span>obj </span><span style="font-style:italic;color:#5c6773;">// true
</span><span>
</span><span>obj</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">hasOwnProperty</span><span>(</span><span style="color:#bae67e;">'toString'</span><span>) </span><span style="font-style:italic;color:#5c6773;">// false
</span></code></pre>
<h2 id="references"><a class="zola-anchor" href="#references" aria-label="Anchor link for: references">#</a>
References:</h2>
<ul>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty">https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty</a></li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/in">https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/in</a></li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://dmitripavlutin.com/own-and-inherited-properties-in-javascript/">https://dmitripavlutin.com/own-and-inherited-properties-in-javascript/</a></li>
</ul>
Javascript this Object: still confused with this helpful keyword?2020-06-24T00:00:00+00:002020-06-24T00:00:00+00:00https://pitayan.com/posts/javascript-this-object-still-confused-with-this-helpful-keyword/<p>This article will help you fully understand the keyword <code>this</code> in javascript. With some “pain-in-the-ass” examples, I’ll show you how to turn <code>this</code> from a “headache” into a real helper.</p>
<h2 id="this-is-a-changeful-object"><a class="zola-anchor" href="#this-is-a-changeful-object" aria-label="Anchor link for: this-is-a-changeful-object">#</a>
“this” is a changeful object</h2>
<p>Maybe you misunderstood <code>this</code> keyword with a <code>class</code> <code>this</code>. But javascript <code>this</code> keyword behaves a bit different than some other programming languages like Python or Java. The way to use it is pretty similar and even behaves the same in some scenarios.</p>
<p>But in javascript, <code>this</code> keyword’s action scope can actually change. Right, no joking, it changes. And this is probably the reason why it brings us a lot of headaches while using it in our project.</p>
<p>According to <a rel="noopener nofollow noreferrer" target="_blank" href="https://www.w3schools.com/Js/js_this.asp">w3schools</a> and <a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this">MDN Web Docs</a>,</p>
<blockquote>
<p>“this” is a property of an execution context. It refers to the object it belongs to.</p>
</blockquote>
<p>It sounds a little ambiguous.</p>
<p>Yet, they can be interperted as:</p>
<blockquote>
<p><code>this</code> is a camera-monitor. Put it in your house, you can monitor everything via <code>this</code>. If you put it elsewhere, the picture certainly changes.</p>
</blockquote>
<p>I guess now you can see the good adaptiveness from the keyword <code>this</code>.</p>
<p>Okay, let’s take a look at how <code>this</code> varies in our real world programs.</p>
<h2 id="some-confusing-examples"><a class="zola-anchor" href="#some-confusing-examples" aria-label="Anchor link for: some-confusing-examples">#</a>
Some Confusing Examples</h2>
<p>Open your browser console, and input some of the following examples. You’ll personally understand the confusing part about <code>this</code> keyword in different scenarios.</p>
<h4 id="1-this-is-window"><a class="zola-anchor" href="#1-this-is-window" aria-label="Anchor link for: 1-this-is-window">#</a>
1. “this” is window</h4>
<pre data-lang="javascript" style="background-color:#212733;color:#ccc9c2;" class="language-javascript "><code class="language-javascript" data-lang="javascript"><span style="font-style:italic;color:#5c6773;">// 1. global console
</span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(</span><span style="font-style:italic;color:#5ccfe6;">this</span><span>) </span><span style="font-style:italic;color:#5c6773;">// window
</span><span>
</span><span style="font-style:italic;color:#5c6773;">// 2. arrow function
</span><span style="color:#ffa759;">var </span><span style="color:#ffd580;">thisIsWindow </span><span style="color:#f29e74;">= </span><span>() </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(</span><span style="font-style:italic;color:#5ccfe6;">this</span><span>) </span><span style="font-style:italic;color:#5c6773;">// window
</span><span>}
</span><span>
</span><span style="font-style:italic;color:#5c6773;">// 3. normal function
</span><span style="color:#ffa759;">function </span><span style="color:#ffd580;">thisIsWindow </span><span>() {
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(</span><span style="font-style:italic;color:#5ccfe6;">this</span><span>) </span><span style="font-style:italic;color:#5c6773;">// window
</span><span>}
</span><span>
</span><span style="font-style:italic;color:#5c6773;">// 4. immediate function
</span><span>(</span><span style="color:#ffa759;">function </span><span>() {
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(</span><span style="font-style:italic;color:#5ccfe6;">this</span><span>) </span><span style="font-style:italic;color:#5c6773;">// window
</span><span>})()
</span><span>
</span><span style="color:#f29e74;">~</span><span style="color:#ffa759;">function </span><span>() {
</span><span> </span><span style="color:#bae67e;">'use strict'
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(</span><span style="font-style:italic;color:#5ccfe6;">this</span><span>) </span><span style="font-style:italic;color:#5c6773;">//window
</span><span>}()
</span><span>
</span><span style="font-style:italic;color:#5c6773;">// 5. function call inside another function
</span><span style="color:#ffa759;">function </span><span style="color:#ffd580;">another </span><span>() {
</span><span> </span><span style="color:#ffd580;">thisIsWindow</span><span>() </span><span style="font-style:italic;color:#5c6773;">// window
</span><span>}
</span><span>
</span><span style="font-style:italic;color:#5c6773;">// 6. arrow function call inside an object
</span><span style="color:#ffa759;">var </span><span>obj </span><span style="color:#f29e74;">= </span><span>{
</span><span> </span><span style="color:#ffd580;">func</span><span style="color:#ccc9c2cc;">: </span><span>() </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(</span><span style="font-style:italic;color:#5ccfe6;">this</span><span>) </span><span style="font-style:italic;color:#5c6773;">// window
</span><span> }
</span><span>}
</span><span>
</span><span style="font-style:italic;color:#5c6773;">// 7. normal function call inside an object function
</span><span style="color:#ffa759;">var </span><span>obj </span><span style="color:#f29e74;">= </span><span>{
</span><span> </span><span style="color:#ffd580;">func</span><span style="color:#ccc9c2cc;">: </span><span style="color:#ffa759;">function </span><span>() {
</span><span> </span><span style="color:#ffd580;">thisIsWindow</span><span>() </span><span style="font-style:italic;color:#5c6773;">// window
</span><span> }
</span><span>}
</span></code></pre>
<h4 id="2-this-is-not-window"><a class="zola-anchor" href="#2-this-is-not-window" aria-label="Anchor link for: 2-this-is-not-window">#</a>
2. “this” is <strong>NOT</strong> window</h4>
<pre data-lang="javascript" style="background-color:#212733;color:#ccc9c2;" class="language-javascript "><code class="language-javascript" data-lang="javascript"><span style="font-style:italic;color:#5c6773;">// 1. normal object function
</span><span style="color:#ffa759;">var </span><span>obj </span><span style="color:#f29e74;">= </span><span>{
</span><span> </span><span style="color:#ffd580;">func</span><span style="color:#ccc9c2cc;">: </span><span style="color:#ffa759;">function </span><span>() {
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(</span><span style="font-style:italic;color:#5ccfe6;">this</span><span>) </span><span style="font-style:italic;color:#5c6773;">// obj
</span><span> }
</span><span>}
</span><span>
</span><span style="font-style:italic;color:#5c6773;">// 2. immediate function under strict mode
</span><span>(</span><span style="color:#ffa759;">function </span><span>() {
</span><span> </span><span style="color:#bae67e;">'use strict'
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(</span><span style="font-style:italic;color:#5ccfe6;">this</span><span>) </span><span style="font-style:italic;color:#5c6773;">// undefined
</span><span>})()
</span><span>
</span><span style="color:#f29e74;">~</span><span style="color:#ffa759;">function </span><span>() {
</span><span> </span><span style="color:#bae67e;">'use strict'
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(</span><span style="font-style:italic;color:#5ccfe6;">this</span><span>) </span><span style="font-style:italic;color:#5c6773;">// undefined
</span><span>}() </span><span style="font-style:italic;color:#5c6773;">// undefined
</span><span>
</span><span style="font-style:italic;color:#5c6773;">// 3. bind DOM event to a function
</span><span>document</span><span style="color:#f29e74;">.</span><span>body</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">onclick </span><span style="color:#f29e74;">= </span><span style="color:#ffa759;">function </span><span>() {
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(</span><span style="font-style:italic;color:#5ccfe6;">this</span><span>) </span><span style="font-style:italic;color:#5c6773;">// document.body
</span><span>}
</span><span>
</span><span>document</span><span style="color:#f29e74;">.</span><span>body</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">addEventListener</span><span>(</span><span style="color:#bae67e;">"click"</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffa759;">function </span><span>() {
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(</span><span style="font-style:italic;color:#5ccfe6;">this</span><span>) </span><span style="font-style:italic;color:#5c6773;">// document.body
</span><span>})
</span></code></pre>
<p>There are more complex examples that will lead to an unexpected context of <code>this</code> keyword. I won’t be listing all of them out here. I think you’ve already felt the painful part of it and start to perceive <code>this</code> keyword’s as a significant knowledge point since it may confuse you anytime at the beginning.</p>
<p>Don’t worry, let me explain you the key points that needs special attentions so that you won’t make mistakes with <code>this</code> during the development.</p>
<h2 id="matter-of-this-fact"><a class="zola-anchor" href="#matter-of-this-fact" aria-label="Anchor link for: matter-of-this-fact">#</a>
Matter of “this” fact</h2>
<h4 id="1-as-for-functions-if-a-function-is-chained-by-another-object-this-refers-to-the-owner-object-if-function-is-not-chained-this-refers-to-window-object"><a class="zola-anchor" href="#1-as-for-functions-if-a-function-is-chained-by-another-object-this-refers-to-the-owner-object-if-function-is-not-chained-this-refers-to-window-object" aria-label="Anchor link for: 1-as-for-functions-if-a-function-is-chained-by-another-object-this-refers-to-the-owner-object-if-function-is-not-chained-this-refers-to-window-object">#</a>
1. As for functions, if a function is chained by another object. <code>this</code> refers to the owner object. If function is not chained, <code>this</code> refers to window object</h4>
<pre data-lang="javascript" style="background-color:#212733;color:#ccc9c2;" class="language-javascript "><code class="language-javascript" data-lang="javascript"><span style="color:#ffa759;">function </span><span style="color:#ffd580;">func </span><span>() {
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(</span><span style="color:#bae67e;">"this: "</span><span style="color:#ccc9c2cc;">, </span><span style="font-style:italic;color:#5ccfe6;">this</span><span>)
</span><span>}
</span><span>
</span><span style="color:#ffa759;">var </span><span>obj </span><span style="color:#f29e74;">= </span><span>{
</span><span> func</span><span style="color:#ccc9c2cc;">: </span><span>func
</span><span>}
</span><span>
</span><span>obj</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">func</span><span>() </span><span style="font-style:italic;color:#5c6773;">// this: {func: function}
</span></code></pre>
<h4 id="2-this-within-an-immediate-function-always-refers-to-window-object"><a class="zola-anchor" href="#2-this-within-an-immediate-function-always-refers-to-window-object" aria-label="Anchor link for: 2-this-within-an-immediate-function-always-refers-to-window-object">#</a>
2. <code>this</code> within an immediate function always refers to window object</h4>
<pre data-lang="javascript" style="background-color:#212733;color:#ccc9c2;" class="language-javascript "><code class="language-javascript" data-lang="javascript"><span>(</span><span style="color:#ffa759;">function</span><span>(){
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(</span><span style="color:#bae67e;">"this: "</span><span style="color:#ccc9c2cc;">, </span><span style="font-style:italic;color:#5ccfe6;">this</span><span>) </span><span style="font-style:italic;color:#5c6773;">// this: Window {...}
</span><span>})()
</span><span>
</span><span style="color:#f29e74;">~</span><span style="color:#ffa759;">function</span><span>(){
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(</span><span style="color:#bae67e;">"this: "</span><span style="color:#ccc9c2cc;">, </span><span style="font-style:italic;color:#5ccfe6;">this</span><span>) </span><span style="font-style:italic;color:#5c6773;">// this: Window {...}
</span><span>}()
</span></code></pre>
<h4 id="3-while-binding-an-event-to-a-dom-element-this-refers-to-the-current-element"><a class="zola-anchor" href="#3-while-binding-an-event-to-a-dom-element-this-refers-to-the-current-element" aria-label="Anchor link for: 3-while-binding-an-event-to-a-dom-element-this-refers-to-the-current-element">#</a>
3. While binding an event to a DOM element, <code>this</code> refers to the current element</h4>
<pre data-lang="javascript" style="background-color:#212733;color:#ccc9c2;" class="language-javascript "><code class="language-javascript" data-lang="javascript"><span>document</span><span style="color:#f29e74;">.</span><span>body</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">onclick </span><span style="color:#f29e74;">= </span><span style="color:#ffa759;">function </span><span>() {
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(</span><span style="color:#bae67e;">"this: "</span><span style="color:#ccc9c2cc;">, </span><span style="font-style:italic;color:#5ccfe6;">this</span><span>) </span><span style="font-style:italic;color:#5c6773;">// this: <body>...</body>
</span><span>}
</span></code></pre>
<h4 id="4-in-a-constructor-function-this-refers-to-the-function-class-instance"><a class="zola-anchor" href="#4-in-a-constructor-function-this-refers-to-the-function-class-instance" aria-label="Anchor link for: 4-in-a-constructor-function-this-refers-to-the-function-class-instance">#</a>
4. In a constructor function, <code>this</code> refers to the function/class instance</h4>
<pre data-lang="javascript" style="background-color:#212733;color:#ccc9c2;" class="language-javascript "><code class="language-javascript" data-lang="javascript"><span style="font-style:italic;color:#5c6773;">// Function
</span><span style="color:#ffa759;">function </span><span style="color:#ffd580;">Website </span><span>(</span><span style="color:#ffcc66;">name</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">url</span><span>) {
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span>name </span><span style="color:#f29e74;">= </span><span>name
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span>url </span><span style="color:#f29e74;">= </span><span>url
</span><span>
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">print </span><span style="color:#f29e74;">= </span><span style="color:#ffa759;">function </span><span>() {
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(</span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span>name </span><span style="color:#f29e74;">+ </span><span style="color:#bae67e;">' -- ' </span><span style="color:#f29e74;">+ </span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span>url)
</span><span> }
</span><span>}
</span><span>
</span><span style="font-style:italic;color:#5c6773;">// Class
</span><span style="color:#ffa759;">class </span><span style="color:#73d0ff;">Website </span><span>{
</span><span> </span><span style="color:#ffa759;">constructor </span><span>(</span><span style="color:#ffcc66;">name</span><span style="color:#ccc9c2cc;">, </span><span style="color:#ffcc66;">url</span><span>) {
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span>name </span><span style="color:#f29e74;">= </span><span>name
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span>url </span><span style="color:#f29e74;">= </span><span>url
</span><span> }
</span><span>
</span><span> </span><span style="color:#ffd580;">print </span><span>() {
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(</span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span>name </span><span style="color:#f29e74;">+ </span><span style="color:#bae67e;">' -- ' </span><span style="color:#f29e74;">+ </span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span>url)
</span><span> }
</span><span>}
</span><span>
</span><span style="color:#ffa759;">var </span><span>pitayanBlog </span><span style="color:#f29e74;">= new </span><span style="color:#73d0ff;">Website</span><span>(</span><span style="color:#bae67e;">'Pitayan Blog'</span><span style="color:#ccc9c2cc;">, </span><span style="color:#bae67e;">'https://pitayan.com'</span><span>)
</span><span>
</span><span>pitayanBlog</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">print</span><span>() </span><span style="font-style:italic;color:#5c6773;">// PitayanBlog -- https://pitayan.com
</span></code></pre>
<p><strong>Note:</strong> The <code>console</code> output will change its value in terms with the caller’s context.</p>
<pre data-lang="javascript" style="background-color:#212733;color:#ccc9c2;" class="language-javascript "><code class="language-javascript" data-lang="javascript"><span style="color:#ffa759;">var </span><span>test </span><span style="color:#f29e74;">= </span><span>pitayanBlog</span><span style="color:#f29e74;">.</span><span>print
</span><span>
</span><span style="color:#ffd580;">test</span><span>() </span><span style="font-style:italic;color:#5c6773;">// undefined -- undefined
</span></code></pre>
<h4 id="5-change-this-context-scope-with-bind"><a class="zola-anchor" href="#5-change-this-context-scope-with-bind" aria-label="Anchor link for: 5-change-this-context-scope-with-bind">#</a>
5. Change <code>this</code> context scope with <code>bind</code></h4>
<p><code>bind</code> will help return a new function containing the specified context. Execute the returned new function will output the result.</p>
<pre data-lang="javascript" style="background-color:#212733;color:#ccc9c2;" class="language-javascript "><code class="language-javascript" data-lang="javascript"><span style="color:#ffa759;">var </span><span>website </span><span style="color:#f29e74;">= </span><span>{
</span><span> url</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">'https://pitayan.com'
</span><span>}
</span><span>
</span><span style="color:#ffa759;">function </span><span style="color:#ffd580;">func </span><span>() {
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(</span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span>url)
</span><span>}
</span><span>
</span><span style="color:#ffa759;">var </span><span>newFunc </span><span style="color:#f29e74;">= </span><span>func</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">bind</span><span>(website)
</span><span>
</span><span style="color:#ffd580;">newFunc</span><span>() </span><span style="font-style:italic;color:#5c6773;">// https://pitayan.com
</span></code></pre>
<h4 id="6-change-this-context-scope-with-apply-and-call"><a class="zola-anchor" href="#6-change-this-context-scope-with-apply-and-call" aria-label="Anchor link for: 6-change-this-context-scope-with-apply-and-call">#</a>
6. Change <code>this</code> context scope with <code>apply</code> and <code>call</code></h4>
<p>In the following example, if you execute <code>print</code> function directly it will output <code>undefined</code>. But if you utilize <a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call"><code>apply</code></a> and <a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call"><code>call</code></a> to change the context scope of <code>print</code> function, it will output “https://pitayan.com”.</p>
<pre data-lang="javascript" style="background-color:#212733;color:#ccc9c2;" class="language-javascript "><code class="language-javascript" data-lang="javascript"><span style="color:#ffa759;">var </span><span>website </span><span style="color:#f29e74;">= </span><span>{
</span><span> url</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">'https://pitayan.com'
</span><span>}
</span><span>
</span><span style="color:#ffa759;">function </span><span style="color:#ffd580;">print </span><span>() {
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(</span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span>url)
</span><span>}
</span><span>
</span><span style="color:#ffd580;">print</span><span>() </span><span style="font-style:italic;color:#5c6773;">// undefined
</span><span>
</span><span>print</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">apply</span><span>(website)
</span><span style="font-style:italic;color:#5c6773;">// or
</span><span>print</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">call</span><span>(website)
</span></code></pre>
<h4 id="7-the-strict-mode-apply-call-behave-differently-than-none-strict-mode"><a class="zola-anchor" href="#7-the-strict-mode-apply-call-behave-differently-than-none-strict-mode" aria-label="Anchor link for: 7-the-strict-mode-apply-call-behave-differently-than-none-strict-mode">#</a>
7. The “strict mode” <code>apply</code> /<code>call</code> behave differently than “none strict mode”</h4>
<pre data-lang="javascript" style="background-color:#212733;color:#ccc9c2;" class="language-javascript "><code class="language-javascript" data-lang="javascript"><span style="color:#ffa759;">function </span><span style="color:#ffd580;">func </span><span>() {
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(</span><span style="color:#bae67e;">"this: "</span><span style="color:#ccc9c2cc;">, </span><span style="font-style:italic;color:#5ccfe6;">this</span><span>)
</span><span>}
</span><span>
</span><span style="font-style:italic;color:#5c6773;">// none strict mode
</span><span>func</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">call</span><span>() </span><span style="font-style:italic;color:#5c6773;">// this: Window {...}
</span><span>func</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">call</span><span>(</span><span style="color:#ffcc66;">null</span><span>) </span><span style="font-style:italic;color:#5c6773;">// this: Window {...}
</span><span>func</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">call</span><span>(</span><span style="color:#ffcc66;">undefined</span><span>) </span><span style="font-style:italic;color:#5c6773;">// this: window {...}
</span><span>
</span><span style="font-style:italic;color:#5c6773;">// strict mode
</span><span>func</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">call</span><span>() </span><span style="font-style:italic;color:#5c6773;">// this: undefined
</span><span>func</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">call</span><span>(</span><span style="color:#ffcc66;">null</span><span>) </span><span style="font-style:italic;color:#5c6773;">// this: null
</span><span>func</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">call</span><span>(</span><span style="color:#ffcc66;">undefined</span><span>) </span><span style="font-style:italic;color:#5c6773;">// this: undefined
</span></code></pre>
<p><strong>Note:</strong> <code>apply </code> behaves the same to <code>call</code> in the above situation.</p>
<h4 id="8-this-used-within-an-arrow-function-always-refers-to-the-object-where-it-s-defined"><a class="zola-anchor" href="#8-this-used-within-an-arrow-function-always-refers-to-the-object-where-it-s-defined" aria-label="Anchor link for: 8-this-used-within-an-arrow-function-always-refers-to-the-object-where-it-s-defined">#</a>
8. <code>this</code> used within an arrow function always refers to the object where it’s defined</h4>
<pre data-lang="javascript" style="background-color:#212733;color:#ccc9c2;" class="language-javascript "><code class="language-javascript" data-lang="javascript"><span style="color:#ffa759;">var </span><span>obj </span><span style="color:#f29e74;">= </span><span>{
</span><span> </span><span style="color:#ffd580;">func</span><span style="color:#ccc9c2cc;">: </span><span style="color:#ffa759;">function </span><span>() {
</span><span> document</span><span style="color:#f29e74;">.</span><span>body</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">onclick </span><span style="color:#f29e74;">= </span><span>() </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(</span><span style="color:#bae67e;">"this: "</span><span style="color:#ccc9c2cc;">, </span><span style="font-style:italic;color:#5ccfe6;">this</span><span>) </span><span style="font-style:italic;color:#5c6773;">// this: {func: Function}
</span><span> }
</span><span> }
</span><span>}
</span></code></pre>
<p>Here is something intersting about arrow function. Arrow function has no action scope for <code>this</code> keyword, so if you use <code>this</code> keyword within the arrow function <code>this</code> refers to some object way up to the top layer.</p>
<pre data-lang="javascript" style="background-color:#212733;color:#ccc9c2;" class="language-javascript "><code class="language-javascript" data-lang="javascript"><span style="color:#ffa759;">var </span><span>obj </span><span style="color:#f29e74;">= </span><span>{
</span><span> </span><span style="color:#ffd580;">func</span><span style="color:#ccc9c2cc;">: </span><span style="color:#ffa759;">function </span><span>() {
</span><span> </span><span style="color:#ffa759;">return </span><span>() </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="color:#ffa759;">return </span><span>() </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(</span><span style="color:#bae67e;">"this: "</span><span style="color:#ccc9c2cc;">, </span><span style="font-style:italic;color:#5ccfe6;">this</span><span>)
</span><span> }
</span><span> }
</span><span> }
</span><span>}
</span><span>
</span><span>obj</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">func</span><span>()()() </span><span style="font-style:italic;color:#5c6773;">// this: {func: Function}
</span></code></pre>
<p><strong>Note:</strong> arrow function cannot use <code>this</code> context, so it will ignore the first argument while invoking with <code>apply</code> or <code>call</code>.</p>
<pre data-lang="javascript" style="background-color:#212733;color:#ccc9c2;" class="language-javascript "><code class="language-javascript" data-lang="javascript"><span style="color:#ffa759;">var </span><span>obj </span><span style="color:#f29e74;">= </span><span>{
</span><span> name</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">'obj'</span><span style="color:#ccc9c2cc;">,
</span><span> </span><span style="color:#ffd580;">func</span><span style="color:#ccc9c2cc;">: </span><span style="color:#ffa759;">function </span><span>() {
</span><span> </span><span style="color:#ffa759;">var </span><span style="color:#ffd580;">fn </span><span style="color:#f29e74;">= </span><span>() </span><span style="color:#ffa759;">=> </span><span>{
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">console</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">log</span><span>(</span><span style="font-style:italic;color:#5ccfe6;">this</span><span style="color:#f29e74;">.</span><span>name)
</span><span> }
</span><span>
</span><span> fn</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">call</span><span>({ name</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">"something else" </span><span>})
</span><span> }
</span><span>}
</span><span>
</span><span>obj</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">func</span><span>() </span><span style="font-style:italic;color:#5c6773;">// obj
</span></code></pre>
<p>Well, this is pretty much all of what you need to pay attention to while using javascript this object. Hope you’ve understood its usage and felt no longer confused using it.</p>
<p>Thanks for reading!</p>
<p>Here are some reference links:</p>
<ul>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://medium.com/better-programming/understanding-the-this-keyword-in-javascript-cb76d4c7c5e8">https://medium.com/better-programming/understanding-the-this-keyword-in-javascript-cb76d4c7c5e8</a></li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/ljianshu/Blog/issues/7">https://github.com/ljianshu/Blog/issues/7</a></li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://dmitripavlutin.com/gentle-explanation-of-this-in-javascript/">https://dmitripavlutin.com/gentle-explanation-of-this-in-javascript/</a></li>
</ul>
Javascript Get URL info with document.location2020-06-12T00:00:00+00:002020-06-12T00:00:00+00:00https://pitayan.com/posts/document-location-javascript/<p>In this article, you will learn about the usage of document location object in javascript.</p>
<h2 id="1-basic-usage"><a class="zola-anchor" href="#1-basic-usage" aria-label="Anchor link for: 1-basic-usage">#</a>
1. Basic Usage</h2>
<h3 id="href"><a class="zola-anchor" href="#href" aria-label="Anchor link for: href">#</a>
href</h3>
<p>Using the <code>location</code> api is quite easy. What we use most frequently is perhaps the <code>href</code> property. This returns us the string url of the current working page.</p>
<pre data-lang="javascript" style="background-color:#212733;color:#ccc9c2;" class="language-javascript "><code class="language-javascript" data-lang="javascript"><span style="color:#ffa759;">function </span><span style="color:#ffd580;">getURL </span><span>() {
</span><span> </span><span style="color:#ffa759;">return </span><span>window</span><span style="color:#f29e74;">.</span><span>location</span><span style="color:#f29e74;">.</span><span>href </span><span style="font-style:italic;color:#5c6773;">// https://pitayan.com/posts/document-location-javascript
</span><span>}
</span></code></pre>
<p><code>href</code> can also be altered in order to let browser redirect itself to a different page</p>
<pre data-lang="javascript" style="background-color:#212733;color:#ccc9c2;" class="language-javascript "><code class="language-javascript" data-lang="javascript"><span>window</span><span style="color:#f29e74;">.</span><span>location</span><span style="color:#f29e74;">.</span><span>href </span><span style="color:#f29e74;">= </span><span style="color:#bae67e;">'google.com' </span><span style="font-style:italic;color:#5c6773;">// redirect to google.com
</span></code></pre>
<h3 id="reload-page-dynamically"><a class="zola-anchor" href="#reload-page-dynamically" aria-label="Anchor link for: reload-page-dynamically">#</a>
Reload page dynamically</h3>
<p>With <code>reload</code> method, we could reload the current working page on demand.</p>
<pre data-lang="html" style="background-color:#212733;color:#ccc9c2;" class="language-html "><code class="language-html" data-lang="html"><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">button </span><span style="color:#ffd580;">onclick</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"</span><span style="color:#ffd580;">reloadPage</span><span>()</span><span style="color:#bae67e;">"</span><span style="color:#5ccfe690;">></span><span>reload page</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">button</span><span style="color:#5ccfe690;">>
</span><span>
</span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">script</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#ffa759;">function </span><span style="color:#ffd580;">reloadPage </span><span>() {
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">window</span><span style="color:#f29e74;">.</span><span>location</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">reload</span><span>(</span><span style="color:#ffcc66;">true</span><span>)
</span><span> }
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">script</span><span style="color:#5ccfe690;">>
</span></code></pre>
<h3 id="redirect-page-dynamically"><a class="zola-anchor" href="#redirect-page-dynamically" aria-label="Anchor link for: redirect-page-dynamically">#</a>
Redirect page dynamically</h3>
<p>With <code>assign</code> or <code>replace</code> method, we could go to another page effortlessly. If we use <code>replace</code> method, we cannot use browser back button to navigate to the previous page because the history record is overrided.</p>
<pre data-lang="html" style="background-color:#212733;color:#ccc9c2;" class="language-html "><code class="language-html" data-lang="html"><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">button </span><span style="color:#ffd580;">onclick</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"</span><span style="color:#ffd580;">goToGoogle</span><span>()</span><span style="color:#bae67e;">"</span><span style="color:#5ccfe690;">></span><span>go to google.com</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">button</span><span style="color:#5ccfe690;">>
</span><span>
</span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">script</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#ffa759;">function </span><span style="color:#ffd580;">goToGoogle </span><span>() {
</span><span> </span><span style="font-style:italic;color:#5ccfe6;">window</span><span style="color:#f29e74;">.</span><span>location</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">assign</span><span>(</span><span style="color:#bae67e;">"https://google.com"</span><span>)
</span><span> </span><span style="font-style:italic;color:#5c6773;">// using replace
</span><span> </span><span style="font-style:italic;color:#5c6773;">// window.location.replace("https://google.com")
</span><span> }
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">script</span><span style="color:#5ccfe690;">>
</span><span>
</span></code></pre>
<h2 id="2-window-location-vs-document-location"><a class="zola-anchor" href="#2-window-location-vs-document-location" aria-label="Anchor link for: 2-window-location-vs-document-location">#</a>
2. window.location <em>vs</em> document.location</h2>
<p>You may noticed that both <code>window</code> and <code>document</code> have the <code>location</code> property. According to <a rel="noopener nofollow noreferrer" target="_blank" href="https://html.spec.whatwg.org/multipage/browsers.html#dom-location">W3C</a>, these two properties are bascically the same.</p>
<p>They both reference to the <a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Document/location">Location</a> Object. But in order to ensure the cross-browser compatibility, it’s always preferrable to use <code>window.location</code> other than <code>document.location</code>.</p>
<p>If you open up <a rel="noopener nofollow noreferrer" target="_blank" href="https://developers.google.com/web/tools/chrome-devtools">Chrome browser devtools</a> on a blank page, you can easily find out that the two properties <code>window.location</code> and <code>document.location</code> are equal.</p>
<p><img src="./images/window-and-document-location.png" alt="window-and-document-location" /></p>
<h2 id="3-location-properties"><a class="zola-anchor" href="#3-location-properties" aria-label="Anchor link for: 3-location-properties">#</a>
3. Location Properties</h2>
<h3 id="example"><a class="zola-anchor" href="#example" aria-label="Anchor link for: example">#</a>
Example</h3>
<p>If we take the <a href="/">Pitayan top page</a> as an example, the <code>Location</code> Object will look like this.</p>
<p><img src="./images/top-page-example.png" alt="top-page-example" /></p>
<h3 id="brief-introduction"><a class="zola-anchor" href="#brief-introduction" aria-label="Anchor link for: brief-introduction">#</a>
Brief introduction</h3>
<p>In a <code>location</code> object, the current properties / apis are as follows.</p>
<ul>
<li><em><strong>ancestorOrigins:</strong></em> for iframe usage</li>
<li><em><strong>hash:</strong></em> URL fragment includes leading “#” e.g. <a href="https://pitayan.com/posts/document-location-javascript/#3-location-properties">#3. Location Properties</a></li>
<li><em><strong>host:</strong></em> URL & Port e.g. localhost:8000</li>
<li><em><strong>hostname:</strong></em> URL e.g. www.pitayan.com</li>
<li><em><strong>href:</strong></em> complete URL <a rel="noopener nofollow noreferrer" target="_blank" href="https://pitayan.com/posts/document-location-javascript">https://pitayan.com/posts/document-location-javascript</a></li>
<li><em><strong>origin:</strong></em> URL scheme + URL e.g. https://www.pitayan.com</li>
<li><em><strong>pathname:</strong></em> path come after the host e.g. /posts/document-location-javascript</li>
<li><em><strong>port:</strong></em> server port e.g. 8000</li>
<li><em><strong>protocol:</strong></em> URL scheme e.g. <code>https</code> or <code>http</code></li>
<li><em><strong>search:</strong></em> query string e.g. ?item=macbook</li>
</ul>
<h2 id="references"><a class="zola-anchor" href="#references" aria-label="Anchor link for: references">#</a>
References</h2>
<ul>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://www.tutorialrepublic.com/javascript-tutorial/javascript-window-location.php">https://www.tutorialrepublic.com/javascript-tutorial/javascript-window-location.php</a></li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Document/location">https://developer.mozilla.org/en-US/docs/Web/API/Document/location</a></li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Location">https://developer.mozilla.org/en-US/docs/Web/API/Location</a></li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://stackoverflow.com/questions/2430936/whats-the-difference-between-window-location-and-document-location-in-javascrip">https://stackoverflow.com/questions/2430936/whats-the-difference-between-window-location-and-document-location-in-javascrip</a></li>
</ul>
Server-side rendering (SSR) for an SPA project2020-06-12T00:00:00+00:002020-06-12T00:00:00+00:00https://pitayan.com/posts/server-side-rendering-ssr-for-an-spa-project/<blockquote>
<p>SSR stands for Server-Side Rendering. It is a technique used to improve the perceived page load times.</p>
</blockquote>
<p>You may heard of SSR (server-side rendering) or have already been using it in your project. I believe that there are still many questions about when we need it and how we are going to implement it in our project. In this article, we are going to have a peak over this interesting topic.</p>
<h2 id="1-what-can-ssr-do-for-us"><a class="zola-anchor" href="#1-what-can-ssr-do-for-us" aria-label="Anchor link for: 1-what-can-ssr-do-for-us">#</a>
1. What can SSR do for us</h2>
<p>First, let’s have a look-back on our history. Many years ago when there was no “SPA” at all. All of the web pages were rendered by servers and sent to our browsers for presentation. Technically, this is an SSR behavior and quite common among that period of time. But this “behavior” takes quite a lot of resouces from our server and may frequently cause server-side issues. Later when “SPA” came out to provide solutions to release servers from such burdens and became into the standard of how a web app should look like, we started worrying about some new questions as below.</p>
<ul>
<li><strong>SEO for SPA</strong></li>
<li><strong>Optimization of “above the fold”</strong></li>
</ul>
<p>As we know, most of our SPA frameworks have only one root entrance which means our app DOM tree needs to be mounted in one root HTML element to be activated.</p>
<pre data-lang="tsx" style="background-color:#212733;color:#ccc9c2;" class="language-tsx "><code class="language-tsx" data-lang="tsx"><span style="font-style:italic;color:#5c6773;">// Template
</span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">div </span><span style="color:#ffd580;">id</span><span style="color:#f29e74;">=</span><span style="color:#bae67e;">"app"</span><span style="color:#5ccfe690;">></</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span><span>
</span><span style="font-style:italic;color:#5c6773;">// Vue
</span><span style="color:#f29e74;">new </span><span style="color:#73d0ff;">Vue</span><span>({
</span><span> el</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">"#app"</span><span style="color:#ccc9c2cc;">,
</span><span> data</span><span style="color:#ccc9c2cc;">: </span><span>{
</span><span> message</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">"Hello, world!"
</span><span> }
</span><span>})
</span><span>
</span><span style="font-style:italic;color:#5c6773;">// React
</span><span>ReactDOM</span><span style="color:#f29e74;">.</span><span style="color:#ffd580;">render</span><span>(
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">h1</span><span style="color:#5ccfe690;">></span><span>Hello, world!</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">h1</span><span style="color:#5ccfe690;">></span><span style="color:#ccc9c2cc;">,
</span><span> document</span><span style="color:#f29e74;">.</span><span style="color:#f28779;">getElementById</span><span>(</span><span style="color:#bae67e;">"app"</span><span>)
</span><span>)
</span></code></pre>
<p>With the above SPA solution, our app HTML document may look like this.</p>
<pre data-lang="html" style="background-color:#212733;color:#ccc9c2;" class="language-html "><code class="language-html" data-lang="html"><span style="color:#5ccfe690;"><!</span><span style="color:#ffa759;">DOCTYPE </span><span style="color:#ffcc66;">html</span><span style="color:#5ccfe690;">>
</span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">html </span><span style="color:#ffd580;">lang</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"en"</span><span style="color:#5ccfe690;">>
</span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">head</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">meta </span><span style="color:#ffd580;">charset</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"UTF-8"</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">meta </span><span style="color:#ffd580;">name</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"viewport" </span><span style="color:#ffd580;">content</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"width=device-width, initial-scale=1.0"</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">title</span><span style="color:#5ccfe690;">></span><span>Hello World App</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">title</span><span style="color:#5ccfe690;">>
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">head</span><span style="color:#5ccfe690;">>
</span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">body</span><span style="color:#5ccfe690;">>
</span><span> </span><span style="color:#5ccfe690;"><</span><span style="color:#73d0ff;">div </span><span style="color:#ffd580;">id</span><span style="color:#ccc9c2cc;">=</span><span style="color:#bae67e;">"app"</span><span style="color:#5ccfe690;">></</span><span style="color:#73d0ff;">div</span><span style="color:#5ccfe690;">>
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">body</span><span style="color:#5ccfe690;">>
</span><span style="color:#5ccfe690;"></</span><span style="color:#73d0ff;">html</span><span style="color:#5ccfe690;">>
</span></code></pre>
<p>It looks a lot simpler right? It does, but the issues when mentioned above came right out. It’s not friendly to our searching engines. When the crawler came to our site, there’s nothing to crawl about. So when we want to search some content on Google, nothing useful or related to our site will appear.</p>
<p>Besides the searching engine, when our app gets big enough and takes a long time to get to the first paint of the page, this may result in a performance issue. Well, how long will your users give up on opening up your web after loading? <strong>3 seconds</strong>. In the 90s, we didn’t quite have a good network condition for websites. We actually waited longer than that. (But we were quite patient :D.)</p>
<p>We want our SPA to be SEO-friendly enough and fast enough. Here comes with the solution to all our annoyances: <strong>SSR</strong>.</p>
<p>With SSR, we can let server compute the page HTML content and return us the complete structure of our SPA (HTML JS CSS). After receiving these resouces, the page will be activated and function as an SPA. This is how SSR works. Pretty simple, isn’t it? Same to Springboot Cakephp Ruby-on-rails and other MVC frameworks.</p>
<h2 id="2-downsides-for-ssr"><a class="zola-anchor" href="#2-downsides-for-ssr" aria-label="Anchor link for: 2-downsides-for-ssr">#</a>
2. Downsides for SSR</h2>
<p>Before including SSR into our project, we should try to evaluate those figures that could cause a lot of troubles during development and maintenance.</p>
<p>Here are some of the demerits of SSR.</p>
<ul>
<li><strong>Higher Architecture Complexity</strong></li>
<li><strong>Higher Maintenance Complexity</strong></li>
<li><strong>Extra Cost for SSR Clusters</strong></li>
<li><strong>Performance Issues on Rendering Pages</strong></li>
<li><strong>Third-party Library Compatibility and Support</strong></li>
</ul>
<p>Adopting SSR means that we are adding a new service layer into front end clusters. The position could be right after the proxy server and in front of our REST API servers. This made the architecture a bit more complex and the maintenance a bit time-consuming.</p>
<p>Usually, SSR use Node.js as their server app which lacks of computing performance for some complicated scenarios. The different pages performance may vary a lot in terms with their business logics. So SSR may increase some extra cost to add more SSR nodes for the project if we want to ensure a fair user experience.</p>
<p>Even if we accept these shortcomings, there is still a technical question left for us: handling 3rd party libraries. This could be the devil sometimes during development. Any mistake we made can deal a big damage to UX or even our traffics. A firm 3rd party library and rapid support are very essential. (You know how “good” JS 3rd party libraries are, right :P?)</p>
<p>In all, SSR mainly helps us with the SEO and firt paint performance, we actually don’t have to implement it if our purposes are none of the above. However, there are some best scenarios for SSR to take place.</p>
<h2 id="3-other-solution-alternatives-to-mitigate-ssr-downsides"><a class="zola-anchor" href="#3-other-solution-alternatives-to-mitigate-ssr-downsides" aria-label="Anchor link for: 3-other-solution-alternatives-to-mitigate-ssr-downsides">#</a>
3. Other Solution Alternatives to mitigate SSR downsides</h2>
<p>Actually, there are some of the cases that we don’t really need SSR. But we could instead do a pitching-in towards the issue directly. The following solutions may have already fit our scenarios.</p>
<p>For first paint optimization</p>
<ul>
<li><strong>Staticize</strong>: We could use SSR libraries to turn those pages that don’t require much of the interactions into static files and host them on NFS. In this way, we can avoid maintenance cost on the SSR clusters.</li>
</ul>
<p>For SEO solutions</p>
<ul>
<li><strong>Pre-render</strong>: Utilize some crawlers or headless browsers like <a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/puppeteer/puppeteer">puppeteer</a> to crawl the whole site and generate pre-rendered content and cache them somewhere like NFS. This is similar to “Staticize”.</li>
</ul>
<h2 id="4-ssr-frameworks"><a class="zola-anchor" href="#4-ssr-frameworks" aria-label="Anchor link for: 4-ssr-frameworks">#</a>
4. SSR Frameworks</h2>
<p>We can utilize the official libraries (vue-ssr-renderer & react-dom/server) provided by <strong>Vue</strong> and <strong>React</strong> to implement SSR functions. However we could also directly introduce the “wheels” into our system to avoid extra development cost.</p>
<p>Now, let’s take a look at the <strong>Vue</strong> and <strong>React</strong> community libraries for SSR purposes.</p>
<p>For <strong>Vue</strong>, you have</p>
<ul>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://nuxtjs.org">Nuxt.js</a>: Quite a famous full stack framework. Similar to React Next.js. Provides isomorphic architecture for client-side and server-side. Recommended for using in production.</li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://vapperjs.org">Vapper.js</a>: Focusing on Server-side Rendering</li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://ream.dev">Ream</a>: Could be an alternative to Nuxt.js but with more customizability.</li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/rlindskog/vueniverse">Vueniverse</a>: Yet another fullstack framework.</li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://www.yuque.com/easy-team/ves/introduction">Ves</a>: A fullstack framework. The server-side is based on <a rel="noopener nofollow noreferrer" target="_blank" href="https://eggjs.org">egg.js</a>.</li>
</ul>
<p>For <strong>React</strong>, you have</p>
<ul>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://nextjs.org">Next.js</a>: Backed by <a rel="noopener nofollow noreferrer" target="_blank" href="https://vercel.com">Vercel</a>. Might be the most powerful isomorphic framework in React community.</li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://react-server.io">react-server</a>: Just another framework to handle React SSR. (Development seems stopped)</li>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/jaredpalmer/razzle">Razzle</a>: Robust abstracted SSR framework that supports many SPA frameworks.</li>
</ul>
<p><strong>Plus:</strong> In my personal opinion. If you start a new project with SSR function, go with Nuxt.js or Next.js coz they have large community and have been developed for a long time. Documentations are fairly well, this saves you a lot of pains at the beginning.</p>
<p>In the end, SSR brings us the solutions to handle the SPA shortcomings. Before you adopt SSR into your project, take a thorough consideration over it. Whether adopt or not, let’s give our appreciations to those who worked and are working on making SPA a better tool.</p>
Deploy Springboot application with Travis and Heroku2020-06-01T00:00:00+00:002020-06-01T00:00:00+00:00https://pitayan.com/posts/deploy-springboot-application-with-travis-and-heroku/<p>Our target in this article here is to let you know how to deploy the application to Heroku cloud with Dockerhub and Travis CI.
The code example is uploaded to Github repository <a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/daiyanze/my-springboot-helloworld">my-springboot-helloworld</a>.
TL DR;
Please skip to step 3 if you are ready with a Github repository and a simple Spring Boot application. You also need to have Travis Dockerhub and Heroku accounts before start. They are all free to register.</p>
<h2 id="1-github"><a class="zola-anchor" href="#1-github" aria-label="Anchor link for: 1-github">#</a>
1. Github</h2>
<p>First things first, we need a project repository on Github. I created one named my-springboot-helloworld. And ignore the target/ folder to avoid adding build files into git repo.
You may either create one of your own or clone my repo for convenience.</p>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#ffd580;">$</span><span> git clone https://github.com/daiyanze/my-springboot-helloworld.git
</span><span style="color:#ffd580;">$</span><span> cd my-springboot-helloworld
</span></code></pre>
<h2 id="2-spring-boot"><a class="zola-anchor" href="#2-spring-boot" aria-label="Anchor link for: 2-spring-boot">#</a>
2. Spring Boot</h2>
<p>It would be easier to go with the official project repo gs-spring-boot.Yet, the Spring Boot official website provides the tutorial for creating a “Hello World” application.
I only copied src/ folder and pom.xml file from gs-spring-boot repo into my project folder my-springboot-helloworld/ as following.</p>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#ffd580;">my-springboot-helloworld/
</span><span style="color:#ffd580;">--</span><span> src/
</span><span style="color:#ffd580;">--</span><span> pom.xml
</span></code></pre>
<p>My HelloController looks like this after modification. The endpoint “/” will return a string of “<h2>Hello World!</h2>” .</p>
<pre data-lang="java" style="background-color:#212733;color:#ccc9c2;" class="language-java "><code class="language-java" data-lang="java"><span style="font-style:italic;color:#5c6773;">// my-springboot-helloworld/src/main/java/hello/HelloController.java
</span><span style="color:#ffa759;">package </span><span style="color:#73d0ff;">hello</span><span style="color:#ccc9c2cc;">;
</span><span>
</span><span style="color:#ffa759;">import </span><span style="font-style:italic;color:#5ccfe6;">org</span><span style="color:#f29e74;">.</span><span style="font-style:italic;color:#5ccfe6;">springframework</span><span style="color:#f29e74;">.</span><span style="font-style:italic;color:#5ccfe6;">web</span><span style="color:#f29e74;">.</span><span style="font-style:italic;color:#5ccfe6;">bind</span><span style="color:#f29e74;">.</span><span style="font-style:italic;color:#5ccfe6;">annotation</span><span style="color:#f29e74;">.</span><span style="font-style:italic;color:#5ccfe6;">RestController</span><span style="color:#ccc9c2cc;">;
</span><span style="color:#ffa759;">import </span><span style="font-style:italic;color:#5ccfe6;">org</span><span style="color:#f29e74;">.</span><span style="font-style:italic;color:#5ccfe6;">springframework</span><span style="color:#f29e74;">.</span><span style="font-style:italic;color:#5ccfe6;">web</span><span style="color:#f29e74;">.</span><span style="font-style:italic;color:#5ccfe6;">bind</span><span style="color:#f29e74;">.</span><span style="font-style:italic;color:#5ccfe6;">annotation</span><span style="color:#f29e74;">.</span><span style="font-style:italic;color:#5ccfe6;">RequestMapping</span><span style="color:#ccc9c2cc;">;
</span><span>
</span><span style="color:#ccc9c2cc;">@</span><span style="color:#ffd580;">RestController
</span><span style="color:#ffa759;">public class </span><span style="color:#73d0ff;">HelloController </span><span>{
</span><span>
</span><span> </span><span style="color:#ccc9c2cc;">@</span><span style="color:#ffd580;">RequestMapping</span><span>(</span><span style="color:#bae67e;">"/"</span><span>)
</span><span> </span><span style="color:#ffa759;">public </span><span style="font-style:italic;color:#5ccfe6;">String </span><span style="color:#ffd580;">index</span><span>() {
</span><span> </span><span style="color:#ffa759;">return </span><span style="color:#bae67e;">"<h2>Hello World!</h2>"</span><span style="color:#ccc9c2cc;">;
</span><span> }
</span><span>
</span><span>}
</span></code></pre>
<p>I want to use a different port other than the SpringBoot default port 8080 , 9908 (a random number) for instance. Create a new file named application.yml under config/.</p>
<pre data-lang="yml" style="background-color:#212733;color:#ccc9c2;" class="language-yml "><code class="language-yml" data-lang="yml"><span style="font-style:italic;color:#5c6773;"># config/application.yml
</span><span style="color:#73d0ff;">server</span><span style="color:#ccc9c2cc;">:
</span><span> </span><span style="color:#73d0ff;">port</span><span style="color:#ccc9c2cc;">: </span><span style="color:#ffcc66;">9908
</span></code></pre>
<p>Alright, it seems everything is Okay! We could build our .jar file now with the following command.</p>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#ffd580;">$</span><span> mvn package
</span></code></pre>
<p>It will generate a new folder target/ (git-ignored) where your .jar file is inside.</p>
<pre data-lang="txt" style="background-color:#212733;color:#ccc9c2;" class="language-txt "><code class="language-txt" data-lang="txt"><span>my-springboot-helloworld/
</span><span>-- src/
</span><span>-- target/
</span><span> ...
</span><span> gs-spring-boot-0.1.0.jar
</span><span>-- pom.xml
</span></code></pre>
<p>Test if it runs properly.</p>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#ffd580;">$</span><span> mvn spring-boot:run
</span><span style="color:#ffd580;">or
</span><span style="color:#ffd580;">$</span><span> java</span><span style="color:#ffcc66;"> -jar</span><span> target/</span><span style="color:#f29e74;">*</span><span>.jar
</span></code></pre>
<p>Access localhost:9908
LGTM!</p>
<p><img src="./images/hello_world.png" alt="Image" /></p>
<h2 id="3-docker"><a class="zola-anchor" href="#3-docker" aria-label="Anchor link for: 3-docker">#</a>
3. Docker</h2>
<p>Use Docker to create an image and push it to your Dockerhub repository (Create one here on https://hub.docker.com/). It is recommended to name your Dockerhub repo same with your Github repo. Mines are the same: daiyanze/my-springboot-helloworld.
After Dockerhub repo gets ready, we need to create a Dockerfile for the project.</p>
<pre data-lang="dockerfile" style="background-color:#212733;color:#ccc9c2;" class="language-dockerfile "><code class="language-dockerfile" data-lang="dockerfile"><span style="font-style:italic;color:#5c6773;"># my-springboot-helloworld/Dockerfile
</span><span style="color:#ffa759;">FROM</span><span> openjdk:</span><span style="color:#73d0ff;">8-jre-alpine
</span><span style="color:#ffa759;">ENV </span><span>APP_ROOT /app
</span><span style="color:#ffa759;">RUN </span><span>mkdir ${APP_ROOT}
</span><span style="color:#ffa759;">WORKDIR </span><span>${APP_ROOT}
</span><span style="color:#ffa759;">COPY</span><span> target/*.jar ${APP_ROOT}/run.jar
</span><span style="color:#f29e74;">ENTRYPOINT </span><span>[</span><span style="color:#bae67e;">"java"</span><span>, </span><span style="color:#bae67e;">"-jar"</span><span>, </span><span style="color:#bae67e;">"run.jar"</span><span>]
</span></code></pre>
<p>It’s also good to use docker-compose for building the image. Here is the docker-compose.yml.</p>
<pre data-lang="yml" style="background-color:#212733;color:#ccc9c2;" class="language-yml "><code class="language-yml" data-lang="yml"><span style="color:#73d0ff;">version</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">"3"
</span><span style="color:#73d0ff;">services</span><span style="color:#ccc9c2cc;">:
</span><span> </span><span style="color:#73d0ff;">app</span><span style="color:#ccc9c2cc;">:
</span><span> </span><span style="color:#73d0ff;">build</span><span style="color:#ccc9c2cc;">: .
</span><span> </span><span style="color:#73d0ff;">image</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">daiyanze/my-springboot-helloworld:latest
</span><span> </span><span style="color:#73d0ff;">container_name</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">hello-world
</span></code></pre>
<p>We’ve already built the .jar file on step 2. Now we could test building a Docker image for our app.</p>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#ffd580;">$</span><span> docker-compose build
</span><span style="color:#ffd580;">...
</span><span style="color:#ffd580;">$</span><span> docker image ls
</span><span style="color:#ffd580;">REPOSITORY</span><span> TAG IMAGE ID CREATED SIZE
</span><span style="color:#ffd580;">my-springboot-helloworld_app</span><span> latest b73ca880fe7c 1 min ago 101MB
</span><span style="color:#ffd580;">$</span><span> docker-compose up
</span><span style="color:#ffd580;">...
</span></code></pre>
<p>Access localhost:9908
LGTM!
<img src="./images/hello_world.png" alt="Springboot: hello world" /></p>
<h2 id="4-heroku"><a class="zola-anchor" href="#4-heroku" aria-label="Anchor link for: 4-heroku">#</a>
4. Heroku</h2>
<p>Create an app named my-springboot-helloworld first.
<img src="./images/create_new_app.png" alt="Heroku: create new app" /></p>
<p>Copy and paste your API key from Account Settings to a safe place. We are going to use the API key for Travis later.
Done! Settings about Heroku is just this simple.
<img src="./images/api_key.png" alt="Heroku: api key" /></p>
<h2 id="5-teams-incoming-hook"><a class="zola-anchor" href="#5-teams-incoming-hook" aria-label="Anchor link for: 5-teams-incoming-hook">#</a>
5. Teams Incoming Hook</h2>
<p>Create a team if necessary. We will use the General channel to receive messages from Travis.<br /></p>
<p>Add a connector to our channel
<img src="./images/channel_connector.png" alt="Teams: channel connector" /></p>
<p>Select “Incoming Webhook”
<img src="./images/incoming_webhook.png" alt="Teams: incoming webhook" /></p>
<p>Pick a cool icon for Travis.
<img src="./images/travis_icon.png" alt="Teams: webhook icon of travis" /></p>
<p>Save the channel url. We will use it later on Travis settings.
<img src="./images/channel_url.png" alt="Teams: channel url" /></p>
<h2 id="6-travis"><a class="zola-anchor" href="#6-travis" aria-label="Anchor link for: 6-travis">#</a>
6. Travis</h2>
<p>Setting up Travis is perhaps an easy part if you have experience on using CI/CD. With Travis CI, we are going to automate the following processes that “should” be done by the server.</p>
<ol>
<li>Test & build our “Hello World” project</li>
<li>Create Docker image and push to our Dockerhub repository</li>
<li>Deploy the app to Heroku Cloud</li>
<li>Send build/deploy messages to Teams channel</li>
</ol>
<p>To start using Travis, we need a setup file for Travis which defines what to do after we triggered the job. Let’s create a file named .travis.yml. And put in some basic settings in it. The following settings mean that our application needs openjdk8 which Travis will help install it for us. And if we want to use Docker commands in our job, then we’ll have to add docker to services property.</p>
<pre data-lang="yml" style="background-color:#212733;color:#ccc9c2;" class="language-yml "><code class="language-yml" data-lang="yml"><span style="font-style:italic;color:#5c6773;"># .travis.yml
</span><span style="color:#73d0ff;">language</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">java
</span><span style="color:#73d0ff;">jdk</span><span style="color:#ccc9c2cc;">:
</span><span> - </span><span style="color:#bae67e;">openjdk8
</span><span style="color:#73d0ff;">services</span><span style="color:#ccc9c2cc;">:
</span><span> - </span><span style="color:#bae67e;">docker
</span></code></pre>
<p>We’re gonna need some global variables for other scripts to reuse later. And of course those global variables can be encrypted for security purposes since those settings are open to public in Github. In our case, we need the following variables:</p>
<pre data-lang="yml" style="background-color:#212733;color:#ccc9c2;" class="language-yml "><code class="language-yml" data-lang="yml"><span style="font-style:italic;color:#5c6773;"># env:
</span><span style="font-style:italic;color:#5c6773;"># global:
</span><span style="font-style:italic;color:#5c6773;"># secure: IMAGE_NAME
</span><span style="font-style:italic;color:#5c6773;"># secure: HEROKU_API_KEY
</span><span style="font-style:italic;color:#5c6773;"># secure: DOCKER_USER
</span><span style="font-style:italic;color:#5c6773;"># secure: DOCKER_PASS
</span><span style="font-style:italic;color:#5c6773;"># secure: TEAMS_CHANNEL_URL
</span></code></pre>
<p>My repo and image name are the same: “my-springboot-helloworld”.<br /></p>
<p>As I mentioned above, we will use Dockerhub and Heroku to help us deploy the application. So the authorization of our identity on both sites is apparently inevitable.<br /></p>
<p>Let’s encrypt the Heroku API key we saved from step 4 (Heroku).</p>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#ffd580;">$</span><span> travis encrypt HEROKU_API_KEY=</span><span style="color:#bae67e;">"******"
</span><span style="color:#ffd580;">Please</span><span> add the following to your .travis.yml file:
</span><span> </span><span style="color:#ffd580;">secure: </span><span style="color:#bae67e;">"<encrypted_HEROKU_API_KEY>"
</span></code></pre>
<p>Copy the <encrypted_HEROKU_API_KEY> and paste it in our .travis.yml file. (Don’t need the double quote mark)</p>
<pre data-lang="yml" style="background-color:#212733;color:#ccc9c2;" class="language-yml "><code class="language-yml" data-lang="yml"><span style="font-style:italic;color:#5c6773;"># .travis.yml
</span><span style="color:#73d0ff;">language</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">java
</span><span style="color:#73d0ff;">jdk</span><span style="color:#ccc9c2cc;">:
</span><span> - </span><span style="color:#bae67e;">openjdk8
</span><span style="color:#73d0ff;">services</span><span style="color:#ccc9c2cc;">:
</span><span> - </span><span style="color:#bae67e;">docker
</span><span style="color:#73d0ff;">env</span><span style="color:#ccc9c2cc;">:
</span><span> </span><span style="color:#73d0ff;">global</span><span style="color:#ccc9c2cc;">:
</span><span> - </span><span style="color:#73d0ff;">secure</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;"><encrypted_HEROKU_API_KEY>
</span></code></pre>
<p>Repeat the encryption for your Dockerhub account and Teams channel url.</p>
<pre data-lang="yml" style="background-color:#212733;color:#ccc9c2;" class="language-yml "><code class="language-yml" data-lang="yml"><span style="color:#bae67e;">$ travis encrypt DOCKER_USER="******"
</span><span style="color:#73d0ff;">Please add the following to your .travis.yml file</span><span style="color:#ccc9c2cc;">:
</span><span> </span><span style="color:#73d0ff;">secure</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">"<encrypted_DOCKER_USER>"
</span><span style="color:#bae67e;">$ travis encrypt DOCKER_PASSWORD="******"
</span><span style="color:#73d0ff;">Please add the following to your .travis.yml file</span><span style="color:#ccc9c2cc;">:
</span><span> </span><span style="color:#73d0ff;">secure</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">"<encrypted_DOCKER_PASSWORD>"
</span><span style="color:#bae67e;">$ travis encrypt IMAGE_NAME="******"
</span><span style="color:#73d0ff;">Please add the following to your .travis.yml file</span><span style="color:#ccc9c2cc;">:
</span><span> </span><span style="color:#73d0ff;">secure</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">"<encrypted_IMAGE_NAME>"
</span><span style="color:#bae67e;">$ travis encrypt TEAM_CHANNEL_URL="******"
</span><span style="color:#73d0ff;">Please add the following to your .travis.yml file</span><span style="color:#ccc9c2cc;">:
</span><span> </span><span style="color:#73d0ff;">secure</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">"<encrypted_TEAM_CHANNEL_URL>"
</span></code></pre>
<p>Copy and Paste…</p>
<pre data-lang="yml" style="background-color:#212733;color:#ccc9c2;" class="language-yml "><code class="language-yml" data-lang="yml"><span style="font-style:italic;color:#5c6773;"># .travis.yml
</span><span style="color:#73d0ff;">language</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">java
</span><span style="color:#73d0ff;">jdk</span><span style="color:#ccc9c2cc;">:
</span><span> - </span><span style="color:#bae67e;">openjdk8
</span><span style="color:#73d0ff;">services</span><span style="color:#ccc9c2cc;">:
</span><span> - </span><span style="color:#bae67e;">docker
</span><span style="color:#73d0ff;">env</span><span style="color:#ccc9c2cc;">:
</span><span> </span><span style="color:#73d0ff;">global</span><span style="color:#ccc9c2cc;">:
</span><span> - </span><span style="color:#73d0ff;">secure</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;"><encrypted_IMAGE_NAME>
</span><span> - </span><span style="color:#73d0ff;">secure</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;"><encrypted_HEROKU_API_KEY>
</span><span> - </span><span style="color:#73d0ff;">secure</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;"><encrypted_DOCKER_USER>
</span><span> - </span><span style="color:#73d0ff;">secure</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;"><encrypted_DOCKER_PASSWORD>
</span><span> - </span><span style="color:#73d0ff;">secure</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;"><encrypted_TEAM_CHANNEL_URL>
</span></code></pre>
<p>Now we continue with the Travis job. There are 12 job life-circles in Travis CI. We are going to use only some of them for build & deploy.<br /></p>
<p>First, we pull the openjdk alpine image from Dockerhub for later Docker image build.</p>
<pre data-lang="yml" style="background-color:#212733;color:#ccc9c2;" class="language-yml "><code class="language-yml" data-lang="yml"><span style="color:#73d0ff;">before_install</span><span style="color:#ccc9c2cc;">:
</span><span> - </span><span style="color:#bae67e;">docker pull openjdk:8-jdk-alpine
</span></code></pre>
<p>Okay. Let’s go on with building .jar file and Docker image.</p>
<p><strong>Stage 1:</strong></p>
<ol>
<li>Test & build our “Hello World” project</li>
<li>Create Docker image and push to our Dockerhub repository</li>
</ol>
<pre data-lang="yml" style="background-color:#212733;color:#ccc9c2;" class="language-yml "><code class="language-yml" data-lang="yml"><span style="font-style:italic;color:#5c6773;"># Test & Build SpringBoot app
</span><span style="color:#73d0ff;">script</span><span style="color:#ccc9c2cc;">:
</span><span> - </span><span style="color:#bae67e;">mvn test
</span><span> - </span><span style="color:#bae67e;">mvn clean package
</span><span style="font-style:italic;color:#5c6773;"># Create Docker image for our app and push it to Dockerhub repository and send messages to the Teams channel after the `script` job is successfully executed
</span><span style="color:#73d0ff;">after_success</span><span style="color:#ccc9c2cc;">:
</span><span> - </span><span style="color:#bae67e;">export COMMIT=${TRAVIS_COMMIT::7}
</span><span> - </span><span style="color:#bae67e;">export TAG=`if [ ! -z "$TRAVIS_TAG" ]; then echo "$TRAVIS_TAG"; else echo "$TRAVIS_BRANCH--$COMMIT"; fi`
</span><span> - </span><span style="color:#bae67e;">docker login -u $DOCKER_USER -p $DOCKER_PASS
</span><span> - </span><span style="color:#bae67e;">docker-compose build
</span><span> - </span><span style="color:#bae67e;">docker tag $IMAGE_NAME:latest $IMAGE_NAME:$TAG
</span><span> - </span><span style="color:#bae67e;">docker push $IMAGE_NAME:$TAG
</span><span> - </span><span style="color:#bae67e;">export TITLE="$IMAGE_NAME:$TAG is built properly and pushed to Dockerhub"
</span><span> - </span><span style="color:#bae67e;">export TIMESTAMP=`date`
</span><span> - </span><span style="color:#bae67e;">export GIT_LOG=`git log -1 --pretty=%B $COMMIT`
</span><span> - </span><span style="color:#bae67e;">export TEXT="[build version] $TAG<br />[datetime] $TIMESTAMP<br />[changelog] $GIT_LOG<br />"
</span><span> - </span><span style="color:#bae67e;">chmod +x send_to_teams.sh && ./send_to_teams.sh
</span><span style="font-style:italic;color:#5c6773;"># If the `script` job fails, it will send a failure message to Teams channel
</span><span style="color:#73d0ff;">after_failure</span><span style="color:#ccc9c2cc;">:
</span><span> - </span><span style="color:#bae67e;">export TITLE="Travis:$TRAVIS_JOB_ID -- build job is failed"
</span><span> - </span><span style="color:#bae67e;">export TEXT=[datetime]:`date`
</span></code></pre>
<p><strong>Stage 2:</strong>
3. Deploy the app to Heroku Cloud
4. Send build/deploy messages to Teams channel</p>
<pre data-lang="yml" style="background-color:#212733;color:#ccc9c2;" class="language-yml "><code class="language-yml" data-lang="yml"><span style="font-style:italic;color:#5c6773;"># Allow Travis to help deploy the app on Heroku
</span><span style="color:#73d0ff;">deploy</span><span style="color:#ccc9c2cc;">:
</span><span> </span><span style="color:#73d0ff;">provider</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">heroku
</span><span> </span><span style="color:#73d0ff;">api-key</span><span style="color:#ccc9c2cc;">:
</span><span> </span><span style="color:#73d0ff;">secure</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">"$HEROKU_API_KEY"
</span><span> </span><span style="color:#73d0ff;">app</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">my-springboot-helloworld
</span><span> </span><span style="color:#ffcc66;">on</span><span style="color:#ccc9c2cc;">:
</span><span> </span><span style="color:#73d0ff;">tags</span><span style="color:#ccc9c2cc;">: </span><span style="color:#ffcc66;">true
</span><span> </span><span style="color:#73d0ff;">repo</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">"$IMAGE_NAME"
</span><span style="font-style:italic;color:#5c6773;"># Send the message of success after the deploy is done properly
</span><span style="color:#73d0ff;">after_deploy</span><span style="color:#ccc9c2cc;">:
</span><span> - </span><span style="color:#bae67e;">export TITLE="🎉🎉🎉New version $TAG is deployed to Heroku production🎉🎉🎉"
</span><span> - </span><span style="color:#bae67e;">export TEXT=[datetime]:`date`
</span><span> - </span><span style="color:#bae67e;">./send_to_teams.sh
</span></code></pre>
<p>Now the full Travis settings should look like this.</p>
<pre data-lang="yml" style="background-color:#212733;color:#ccc9c2;" class="language-yml "><code class="language-yml" data-lang="yml"><span style="color:#73d0ff;">language</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">java
</span><span style="color:#73d0ff;">jdk</span><span style="color:#ccc9c2cc;">:
</span><span> - </span><span style="color:#bae67e;">openjdk8
</span><span style="color:#73d0ff;">services</span><span style="color:#ccc9c2cc;">:
</span><span> - </span><span style="color:#bae67e;">docker
</span><span style="font-style:italic;color:#5c6773;"># env:
</span><span style="font-style:italic;color:#5c6773;"># global:
</span><span style="font-style:italic;color:#5c6773;"># secure: IMAGE_NAME
</span><span style="font-style:italic;color:#5c6773;"># secure: HEROKU_API_KEY
</span><span style="font-style:italic;color:#5c6773;"># secure: DOCKER_USER
</span><span style="font-style:italic;color:#5c6773;"># secure: DOCKER_PASS
</span><span style="font-style:italic;color:#5c6773;"># secure: TEAMS_CHANNEL_URL
</span><span style="font-style:italic;color:#5c6773;"># replace the following env configurations with your own
</span><span style="color:#73d0ff;">env</span><span style="color:#ccc9c2cc;">:
</span><span> </span><span style="color:#73d0ff;">global</span><span style="color:#ccc9c2cc;">:
</span><span> - </span><span style="color:#73d0ff;">secure</span><span style="color:#ccc9c2cc;">: </span><span style="color:#ffa759;">*</span><span>**
</span><span> - </span><span style="color:#bae67e;">... (your global variables)
</span><span style="color:#73d0ff;">before_install</span><span style="color:#ccc9c2cc;">:
</span><span> - </span><span style="color:#bae67e;">docker pull openjdk:8-jdk-alpine
</span><span style="font-style:italic;color:#5c6773;"># Test & Build SpringBoot app
</span><span style="color:#73d0ff;">script</span><span style="color:#ccc9c2cc;">:
</span><span> - </span><span style="color:#bae67e;">mvn test
</span><span> - </span><span style="color:#bae67e;">mvn clean package
</span><span style="font-style:italic;color:#5c6773;"># Create Docker image for our app and push it to Dockerhub repository and send messages to the Teams channel after the `script` job is successfully executed
</span><span style="color:#73d0ff;">after_success</span><span style="color:#ccc9c2cc;">:
</span><span> - </span><span style="color:#bae67e;">export COMMIT=${TRAVIS_COMMIT::7}
</span><span> - </span><span style="color:#bae67e;">export TAG=`if [ ! -z "$TRAVIS_TAG" ]; then echo "$TRAVIS_TAG"; else echo "$TRAVIS_BRANCH--$COMMIT"; fi`
</span><span> - </span><span style="color:#bae67e;">docker login -u $DOCKER_USER -p $DOCKER_PASS
</span><span> - </span><span style="color:#bae67e;">docker-compose build
</span><span> - </span><span style="color:#bae67e;">docker tag $IMAGE_NAME:latest $IMAGE_NAME:$TAG
</span><span> - </span><span style="color:#bae67e;">docker push $IMAGE_NAME:$TAG
</span><span> - </span><span style="color:#bae67e;">export TITLE="$IMAGE_NAME:$TAG is built properly and pushed to Dockerhub"
</span><span> - </span><span style="color:#bae67e;">export TIMESTAMP=`date`
</span><span> - </span><span style="color:#bae67e;">export GIT_LOG=`git log -1 --pretty=%B $COMMIT`
</span><span> - </span><span style="color:#bae67e;">export TEXT="[build version] $TAG<br />[datetime] $TIMESTAMP<br />[changelog] $GIT_LOG<br />"
</span><span> - </span><span style="color:#bae67e;">chmod +x send_to_teams.sh && ./send_to_teams.sh
</span><span style="font-style:italic;color:#5c6773;"># If the `script` job fails, it will send a failure message to Teams channel
</span><span style="color:#73d0ff;">after_failure</span><span style="color:#ccc9c2cc;">:
</span><span> - </span><span style="color:#bae67e;">export TITLE="Travis:$TRAVIS_JOB_ID -- build job is failed"
</span><span> - </span><span style="color:#bae67e;">export TEXT=[datetime]:`date`
</span><span style="font-style:italic;color:#5c6773;"># Allow Travis to help deploy the app on Heroku
</span><span style="color:#73d0ff;">deploy</span><span style="color:#ccc9c2cc;">:
</span><span> </span><span style="color:#73d0ff;">provider</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">heroku
</span><span> </span><span style="color:#73d0ff;">api-key</span><span style="color:#ccc9c2cc;">:
</span><span> </span><span style="color:#73d0ff;">secure</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">"$HEROKU_API_KEY"
</span><span> </span><span style="color:#73d0ff;">app</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">my-springboot-helloworld
</span><span> </span><span style="color:#ffcc66;">on</span><span style="color:#ccc9c2cc;">:
</span><span> </span><span style="color:#73d0ff;">tags</span><span style="color:#ccc9c2cc;">: </span><span style="color:#ffcc66;">true
</span><span> </span><span style="color:#73d0ff;">repo</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">"$IMAGE_NAME"
</span><span style="font-style:italic;color:#5c6773;"># Send the message of success after the deploy is done properly
</span><span style="color:#73d0ff;">after_deploy</span><span style="color:#ccc9c2cc;">:
</span><span> - </span><span style="color:#bae67e;">export TITLE="🎉🎉🎉New version $TAG is deployed to Heroku production🎉🎉🎉"
</span><span> - </span><span style="color:#bae67e;">export TEXT=[datetime]:`date`
</span><span> - </span><span style="color:#bae67e;">./send_to_teams.sh
</span></code></pre>
<h2 id="7-we-could-give-it-a-go-for-deployment"><a class="zola-anchor" href="#7-we-could-give-it-a-go-for-deployment" aria-label="Anchor link for: 7-we-could-give-it-a-go-for-deployment">#</a>
7. We could give it a go for deployment</h2>
<p>Since Travis will listen to Github commits. Once we create a new commit it will help test and build our application. If there’s a tag, then our application will be deployed to Heroku cloud.</p>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#ffd580;">$</span><span> git add . </span><span style="color:#f29e74;">&& </span><span style="color:#ffd580;">git</span><span> commit</span><span style="color:#ffcc66;"> -m </span><span style="color:#bae67e;">"made some changes"
</span><span style="color:#ffd580;">$</span><span> git push
</span></code></pre>
<p>Here comes with the Travis info in Teams.
<img src="./images/teams_travis_info.png" alt="Teams: Travis build message 1" /></p>
<p>Now, let’s release our new version.</p>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#ffd580;">$</span><span> git tag v0.1.3
</span><span style="color:#ffd580;">$</span><span> git push</span><span style="color:#ffcc66;"> --tags
</span></code></pre>
<p>Master with tag v0.1.3’s build message come.
<img src="./images/teams_travis_message.png" alt="Teams: Travis build message 2" /></p>
<p>Yes! Deployment is very successful!
<img src="./images/teams_travis_successful.png" alt="Treams: Travis deploy message successful" /></p>
<p>Thanks for reading. Hope you already know about how to deploy the application with Travis and Heroku cloud.</p>
Git commands: 8 very commonly used magics2020-06-01T00:00:00+00:002020-06-01T00:00:00+00:00https://pitayan.com/posts/git-commands-8-very-commonly-used-magics/<p>The article is going to cover the following Git commands that will likely make you more efficient on using Git. Hopefully you’d like to put them into your daily practice.</p>
<ol>
<li>git show</li>
<li>git rev-parse</li>
<li>git rebase</li>
<li>git cherry</li>
<li>git cherry-pick</li>
<li>git diff</li>
<li>git stash</li>
<li>git checkout</li>
</ol>
<p>TLDR;</p>
<h2 id="1-get-me-the-given-commit-message"><a class="zola-anchor" href="#1-get-me-the-given-commit-message" aria-label="Anchor link for: 1-get-me-the-given-commit-message">#</a>
1. Get me the given commit message</h2>
<p>Sometimes you need the message of one commit. Of course it’ possible to trace back the log with <code>git log</code> and navigate by button “d”. Now here is an easier way to do it.</p>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="font-style:italic;color:#5c6773;"># show the first commit message
</span><span style="color:#ffd580;">$</span><span> git show HEAD
</span><span style="font-style:italic;color:#5c6773;"># show the message of commit 72ee5fae5a
</span><span style="color:#ffd580;">$</span><span> git show 72ee5fae5a
</span><span style="color:#ffd580;">commit </span><span style="color:#f29e74;">****
</span><span style="color:#ffd580;">Author: </span><span style="color:#f29e74;">****
</span><span style="color:#ffd580;">Date: </span><span style="color:#f29e74;">****
</span><span style="color:#ffd580;">Change</span><span> version
</span><span style="color:#ffd580;">diff</span><span style="color:#ffcc66;"> --git</span><span> a/pom.xml b/pom.xml
</span><span style="color:#ffd580;">index</span><span> 641928aa..ae19a0e0 100644
</span><span style="color:#ffd580;">---</span><span> a/pom.xml
</span><span style="color:#ffd580;">+++</span><span> b/pom.xml
</span><span style="color:#ffd580;">@@</span><span style="color:#ffcc66;"> -5</span><span>,7 +5,7 @@
</span><span style="color:#f29e74;"><</span><span>groupId</span><span style="color:#f29e74;">></span><span>com.demo.someApp</span><span style="color:#f29e74;"><</span><span>/groupId</span><span style="color:#f29e74;">>
</span><span> </span><span style="color:#f29e74;"><</span><span>artifactId</span><span style="color:#f29e74;">></span><span>module-someApp</span><span style="color:#f29e74;"><</span><span>/artifactId</span><span style="color:#f29e74;">>
</span><span style="color:#ffd580;">- </span><span style="color:#f29e74;"><</span><span>version</span><span style="color:#f29e74;">></span><span style="color:#ffcc66;">1</span><span>.0.</span><span style="color:#ffcc66;">0</span><span style="color:#f29e74;"><</span><span>/version</span><span style="color:#f29e74;">>
</span><span style="color:#ffd580;">+ </span><span style="color:#f29e74;"><</span><span>version</span><span style="color:#f29e74;">></span><span style="color:#ffcc66;">1</span><span>.0.1-SNAPSHOT</span><span style="color:#f29e74;"><</span><span>/version</span><span style="color:#f29e74;">></span><span>^M
</span><span> </span><span style="color:#f29e74;"><</span><span>packaging</span><span style="color:#f29e74;">></span><span>jar</span><span style="color:#f29e74;"><</span><span>/packaging</span><span style="color:#f29e74;">>
</span><span style="color:#f29e74;"><</span><span>name</span><span style="color:#f29e74;">></span><span>module-someApp</span><span style="color:#f29e74;"><</span><span>/name</span><span style="color:#f29e74;">>
</span><span style="font-style:italic;color:#5c6773;"># show the message of tag v1.0.1
</span><span style="color:#ffd580;">$</span><span> git show v1.0.1
</span><span style="color:#ffd580;">commit </span><span style="color:#f29e74;">****
</span><span style="color:#ffd580;">merge</span><span> 102n9fsl 9eb22873
</span><span style="color:#ffd580;">Author: </span><span style="color:#f29e74;">****
</span><span style="color:#ffd580;">Date: </span><span style="color:#f29e74;">****
</span><span>
</span><span> </span><span style="color:#ffd580;">Merge</span><span> pull request </span><span style="font-style:italic;color:#5c6773;">#444 in Project/module-someApp from feature/12345 to master
</span><span style="color:#ffd580;">*</span><span> commit </span><span style="color:#bae67e;">'9eb228733ee22730344353e2179a1c91532f82a6'</span><span>:
</span><span> </span><span style="color:#ffd580;">[App-1][module-someApp]</span><span> feature 1
</span><span> </span><span style="color:#ffd580;">[App-2][module-someApp]</span><span> feature 2
</span><span> </span><span style="color:#ffd580;">[App-3][module-someApp]</span><span> feature 3
</span></code></pre>
<h2 id="2-get-me-the-first-commit-hash-string"><a class="zola-anchor" href="#2-get-me-the-first-commit-hash-string" aria-label="Anchor link for: 2-get-me-the-first-commit-hash-string">#</a>
2. Get me the first commit hash string</h2>
<p>You know what to do with a commit hash string, don’t you.</p>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="font-style:italic;color:#5c6773;"># show the head commit hash
</span><span style="color:#ffd580;">$</span><span> git rev-parse HEAD
</span><span style="color:#ffd580;">c5b841f343135e7a5fea7c04ba74e595261ae330
</span><span style="font-style:italic;color:#5c6773;"># show the head commit hash of master branch
</span><span style="color:#ffd580;">$</span><span> git rev-parse master
</span><span style="color:#ffd580;">6b8e232b323724737720c7fa970864c28355e638
</span><span style="font-style:italic;color:#5c6773;"># show the head commit hash of tag v1.0.0
</span><span style="color:#ffd580;">$</span><span> git rev-parse v1.0.0
</span><span style="color:#ffd580;">d8110b6b396c394bc0c33c6d76c9ab442df19fd6
</span><span style="font-style:italic;color:#5c6773;"># this also works but a bit slower than `git rev-parse`
</span><span style="color:#ffd580;">$</span><span> git log</span><span style="color:#ffcc66;"> -n1 --format</span><span style="color:#f29e74;">=</span><span>format:</span><span style="color:#bae67e;">"%H"</span><span> v1.0.0
</span><span style="color:#ffd580;">d8110b6b396c394bc0c33c6d76c9ab442df19fd6
</span></code></pre>
<h2 id="3-don-t-always-merge-rebase-on-your-own-work-branch"><a class="zola-anchor" href="#3-don-t-always-merge-rebase-on-your-own-work-branch" aria-label="Anchor link for: 3-don-t-always-merge-rebase-on-your-own-work-branch">#</a>
3. Don’t always “merge”, “rebase” on your own work branch</h2>
<p>As we all know that it is very common to keep our work branch up to date by using <code>git merge master</code>. But here is the thing, all your commits history for the current changes will be spaced by the “up-to-date” merges.
I believe the following example might be similar to the working branch you have right now.</p>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#ffd580;">$</span><span> git log
</span><span style="color:#ffd580;">commit</span><span> 72ee5fae5a46c39c859f1ab69216556f2c1af0e9
</span><span style="color:#ffd580;">Author: </span><span style="color:#f29e74;">****
</span><span style="color:#ffd580;">Date: </span><span style="color:#f29e74;">****
</span><span style="color:#ffd580;">added</span><span> some feature
</span><span style="color:#ffd580;">commit</span><span> addc8b47f03fb51eabde618e46b4cc4dc722b823
</span><span style="color:#ffd580;">Author: </span><span style="color:#f29e74;">****
</span><span style="color:#ffd580;">Date: </span><span style="color:#f29e74;">****
</span><span> </span><span style="color:#ffd580;">Merge</span><span> pull request </span><span style="font-style:italic;color:#5c6773;">#123 in Project/module-someApp from hotfix/123 to master
</span><span> </span><span style="color:#ffd580;">*</span><span> commit </span><span style="color:#bae67e;">'f0286b65d82c43940945da965f0f72d4c9a90a96'</span><span>:
</span><span> </span><span style="color:#ffd580;">[feat]</span><span> some changes ccc
</span><span style="color:#ffd580;">commit</span><span> aaefad32bcd081bbb45e9fb19b040asdf3ece0ad
</span><span style="color:#ffd580;">Author: </span><span style="color:#f29e74;">****
</span><span style="color:#ffd580;">Date: </span><span style="color:#f29e74;">****
</span><span style="color:#ffd580;">added</span><span> some logic
</span><span style="color:#ffd580;">commit</span><span> bd0c1f0c981bc24e29d15f96e80b0f587c8d3508
</span><span style="color:#ffd580;">Author: </span><span style="color:#f29e74;">****
</span><span style="color:#ffd580;">Date: </span><span style="color:#f29e74;">****
</span><span> </span><span style="color:#ffd580;">Merge</span><span> pull request </span><span style="font-style:italic;color:#5c6773;">#100 in Project/module-someApp from feature/666 to master
</span><span> </span><span style="color:#ffd580;">*</span><span> commit </span><span style="color:#bae67e;">'9865d5c666a3b75aaf298353aeaf0dfefd7d7dc2'</span><span>:
</span><span> </span><span style="color:#ffd580;">[feat]</span><span> some changes bbb
</span><span style="color:#ffd580;">Please</span><span> do use `</span><span style="color:#ffd580;">rebase</span><span>` instead of `</span><span style="color:#ffd580;">merge</span><span>`.
</span><span style="font-style:italic;color:#5c6773;"># get updates for your work branch
</span><span style="color:#ffd580;">$</span><span> git pull</span><span style="color:#ffcc66;"> --rebase
</span><span style="font-style:italic;color:#5c6773;"># rebase interactively to develop
</span><span style="color:#ffd580;">$</span><span> git rebase</span><span style="color:#ffcc66;"> -i</span><span> develop
</span><span style="font-style:italic;color:#5c6773;"># migrate your work branch to develop
</span><span style="color:#ffd580;">$</span><span> git rebase</span><span style="color:#ffcc66;"> --onto</span><span> develop
</span></code></pre>
<h2 id="4-rearrange-some-commits-from-the-first-few-commits"><a class="zola-anchor" href="#4-rearrange-some-commits-from-the-first-few-commits" aria-label="Anchor link for: 4-rearrange-some-commits-from-the-first-few-commits">#</a>
4. Rearrange some commits from the first few commits</h2>
<p>The commit logs can be very dirty during development. It may look like this.</p>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#ffd580;">$</span><span> git log
</span><span style="color:#ffd580;">commit</span><span> 72ee5fae5a46c39c859f1ab69216556f2c1af0e9
</span><span style="color:#ffd580;">Author: </span><span style="color:#f29e74;">****
</span><span style="color:#ffd580;">Date: </span><span style="color:#f29e74;">****
</span><span style="color:#ffd580;">fixed</span><span> previous commit due to typo
</span><span style="color:#ffd580;">commit</span><span> addc8b47f03fb51eabde618e46b4cc4dc722b823
</span><span style="color:#ffd580;">Author: </span><span style="color:#f29e74;">****
</span><span style="color:#ffd580;">Date: </span><span style="color:#f29e74;">****
</span><span style="color:#ffd580;">add</span><span> new feature
</span><span style="color:#ffd580;">commit</span><span> aaefad32bcd081bbb45e9fb19b040asdf3ece0ad
</span><span style="color:#ffd580;">Author: </span><span style="color:#f29e74;">****
</span><span style="color:#ffd580;">Date: </span><span style="color:#f29e74;">****
</span><span style="color:#ffd580;">fixed</span><span> the previous commit
</span><span style="color:#ffd580;">commit</span><span> bd0c1f0c981bc24e29d15f96e80b0f587c8d3508
</span><span style="color:#ffd580;">Author: </span><span style="color:#f29e74;">****
</span><span style="color:#ffd580;">Date: </span><span style="color:#f29e74;">****
</span><span style="color:#ffd580;">changed</span><span> some logic
</span></code></pre>
<p>*Note that it is not recommended to use this command when there are merge commits mixed within. But it is a good custom to constantly keep your work branch away from merging other branches.</p>
<p>Simply squashing them together will result in a clean and neat branch which will be loved by your teammates.</p>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="font-style:italic;color:#5c6773;"># squash the commits
</span><span style="color:#ffd580;">$</span><span> git rebase</span><span style="color:#ffcc66;"> -i</span><span> HEAD</span><span style="font-style:italic;color:#5ccfe6;">~</span><span>4
</span><span style="color:#ffd580;">pick</span><span> bd0c1f0c changed some logic
</span><span style="color:#ffd580;">pick</span><span> aaefad32 fixed the previous commit
</span><span style="color:#ffd580;">pick</span><span> addc8b47 add new feature
</span><span style="color:#ffd580;">pick</span><span> 72ee5fae fixed previous commit due to typo
</span><span style="font-style:italic;color:#5c6773;"># squash the fixed sth. commits into their previous commits
</span><span style="color:#ffd580;">pick</span><span> bd0c1f0c changed some logic
</span><span style="color:#ffd580;">squash</span><span> aaefad32 fixed the previous commit
</span><span style="color:#ffd580;">pick</span><span> addc8b47 add new feature
</span><span style="color:#ffd580;">squash</span><span> 72ee5fae fixed previous commit due to typo
</span><span style="font-style:italic;color:#5c6773;"># After squashing, redundant commits are gone
</span><span style="color:#ffd580;">$</span><span> git log
</span><span style="color:#ffd580;">commit</span><span> addc8b47f03fb51eabde618e46b4cc4dc722b823
</span><span style="color:#ffd580;">Author: </span><span style="color:#f29e74;">****
</span><span style="color:#ffd580;">Date: </span><span style="color:#f29e74;">****
</span><span style="color:#ffd580;">add</span><span> new feature
</span><span style="color:#ffd580;">commit</span><span> bd0c1f0c981bc24e29d15f96e80b0f587c8d3508
</span><span style="color:#ffd580;">Author: </span><span style="color:#f29e74;">****
</span><span style="color:#ffd580;">Date: </span><span style="color:#f29e74;">****
</span><span style="color:#ffd580;">changed</span><span> some logic
</span></code></pre>
<h2 id="5-modify-the-head-commit-message"><a class="zola-anchor" href="#5-modify-the-head-commit-message" aria-label="Anchor link for: 5-modify-the-head-commit-message">#</a>
5. Modify the HEAD commit message</h2>
<p>What if we typed something wrong in the commit message? We could reset the commit and redo it with the correct message like the following example.</p>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#ffd580;">$</span><span> git add</span><span style="color:#ffcc66;"> -u </span><span style="color:#f29e74;">&& </span><span style="color:#ffd580;">git</span><span> commit</span><span style="color:#ffcc66;"> -m </span><span style="color:#bae67e;">"a wrong message xP"
</span><span style="color:#ffd580;">[master</span><span> f917771] a wrong message xP
</span><span> </span><span style="color:#ffd580;">2</span><span> files changed, 2 insertions(+)</span><span style="color:#ffd580;">,</span><span> 3 deletions(-)
</span><span style="color:#ffd580;">$</span><span> git log</span><span style="color:#ffcc66;"> -1
</span><span style="color:#ffd580;">commit</span><span> f91777109c6520e207af099407181d2d0fbaac97 (HEAD -</span><span style="color:#f29e74;">></span><span> master)
</span><span style="color:#ffd580;">Author: </span><span style="color:#f29e74;">****
</span><span style="color:#ffd580;">Date: </span><span style="color:#f29e74;">****
</span><span style="color:#ffd580;">a</span><span> wrong message xP
</span></code></pre>
<p>Okay. Let’s rollback the local commit, and commit it with the correct message</p>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#ffd580;">$</span><span> git reset</span><span style="color:#ffcc66;"> --soft</span><span> HEAD</span><span style="font-style:italic;color:#5ccfe6;">~ </span><span style="color:#f29e74;">&& </span><span style="color:#ffd580;">git</span><span> status </span><span style="color:#f29e74;">&& </span><span style="color:#ffd580;">git</span><span> commit</span><span style="color:#ffcc66;"> -m </span><span style="color:#bae67e;">"the right message :D"
</span><span style="color:#ffd580;">On</span><span> branch master
</span><span style="color:#ffd580;">Your</span><span> branch is up to date with </span><span style="color:#bae67e;">'origin/master'</span><span>.
</span><span style="color:#ffd580;">Changes</span><span> to be committed:
</span><span> (</span><span style="color:#ffd580;">use </span><span style="color:#bae67e;">"git reset HEAD <file>..."</span><span> to unstage)
</span><span style="color:#ffd580;">modified:</span><span> file1.yml
</span><span style="color:#ffd580;">modified:</span><span> file2.java
</span><span style="color:#ffd580;">[master</span><span> 3f04b5e] the right commit message
</span><span> </span><span style="color:#ffd580;">2</span><span> files changed, 2 insertions(+)</span><span style="color:#ffd580;">,</span><span> 3 deletions(-)
</span></code></pre>
<p>There’s another simpler way to avoid all of these steps just by using one command.</p>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#ffd580;">$</span><span> git commit</span><span style="color:#ffcc66;"> --amend -m </span><span style="color:#bae67e;">"the right message :D"
</span><span style="color:#ffd580;">[master</span><span> 71da00b] the right message :D
</span><span> </span><span style="color:#ffd580;">Date: </span><span style="color:#f29e74;">****
</span><span> </span><span style="color:#ffd580;">2</span><span> files changed, 2 insertions(+)</span><span style="color:#ffd580;">,</span><span> 3 deletions(-)
</span><span style="font-style:italic;color:#5c6773;"># If you want to use an editor for the commit message, do it without the "-m"
</span><span style="color:#ffd580;">$</span><span> git commit</span><span style="color:#ffcc66;"> --amend
</span><span style="color:#ffd580;">the</span><span> right message
</span><span style="font-style:italic;color:#5c6773;"># Please enter the commit message for your changes. Lines starting
</span><span style="font-style:italic;color:#5c6773;"># with '#' will be ignored, and an empty message aborts the commit.
</span><span style="font-style:italic;color:#5c6773;"># ...
</span><span style="color:#ffd580;">[master</span><span> cb5a929] the right message :D
</span><span> </span><span style="color:#ffd580;">Date: </span><span style="color:#f29e74;">****
</span><span> </span><span style="color:#ffd580;">2</span><span> files changed, 2 insertions(+)</span><span style="color:#ffd580;">,</span><span> 3 deletions(-)
</span></code></pre>
<h2 id="6-compare-the-two-branches-and-get-different-commits-git-cherry"><a class="zola-anchor" href="#6-compare-the-two-branches-and-get-different-commits-git-cherry" aria-label="Anchor link for: 6-compare-the-two-branches-and-get-different-commits-git-cherry">#</a>
6. Compare the two branches and get different commits - $ git cherry</h2>
<p>You may want to know the commit differences between the two branches. Here is a very simple way to make it.</p>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#ffd580;">$</span><span> git status
</span><span style="color:#ffd580;">On</span><span> branch master
</span><span style="color:#ffd580;">Your</span><span> branch is up to date with </span><span style="color:#bae67e;">'origin/master'</span><span>.
</span><span style="color:#ffd580;">nothing</span><span> to commit, working tree clean
</span><span style="font-style:italic;color:#5c6773;"># show the difference with develop branch verbosely
</span><span style="color:#ffd580;">$</span><span> git cherry</span><span style="color:#ffcc66;"> -v</span><span> develop
</span><span style="color:#ffd580;">+</span><span> ea90ece5b6283f57e96ad1d0839fc82a86c5c821 some small fix
</span></code></pre>
<h2 id="7-pick-some-commits-to-another-branch"><a class="zola-anchor" href="#7-pick-some-commits-to-another-branch" aria-label="Anchor link for: 7-pick-some-commits-to-another-branch">#</a>
7. Pick some commits to another branch</h2>
<p>There’s one commit from another branch which we need in order to resolve some stupid issue that blocks our development progress. Using “git merge” might bring other changes we might not need into our work branch. So this is when the “cherry-pick” should show up.</p>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#ffd580;">$</span><span> git status
</span><span style="color:#ffd580;">On</span><span> branch feature/111
</span><span style="color:#ffd580;">nothing</span><span> to commit, working tree clean
</span><span style="color:#ffd580;">$</span><span> git log</span><span style="color:#ffcc66;"> -2
</span><span style="color:#ffd580;">commit</span><span> 2513e31f732794e54f62edaf3fde9fe5ab129dfe (HEAD -</span><span style="color:#f29e74;">></span><span> feature/111)
</span><span style="color:#ffd580;">Author: </span><span style="color:#f29e74;">****
</span><span style="color:#ffd580;">Date: </span><span style="color:#f29e74;">****
</span><span style="color:#ffd580;">Merge</span><span> pull request </span><span style="font-style:italic;color:#5c6773;">#551 from picturepan2/0.5.8
</span><span style="color:#ffd580;">0.5.8
</span><span style="color:#ffd580;">commit</span><span> 807ae1aff5090226d55b0134c48de06bfb8737f9
</span><span style="color:#ffd580;">Author: </span><span style="color:#f29e74;">****
</span><span style="color:#ffd580;">Date: </span><span style="color:#f29e74;">****
</span><span style="color:#ffd580;">some</span><span> feature resolves a stupid issue
</span><span style="color:#ffd580;">$</span><span> git checkout feature/222
</span><span style="font-style:italic;color:#5c6773;"># We need the second commit from branch feature/111
</span><span style="color:#ffd580;">$</span><span> git cherry-pick 807ae1aff5090226d55b0134c48de06bfb8737f9
</span><span style="color:#ffd580;">[develop</span><span> 807ae1a] some feature resolves a stupid issue
</span><span> </span><span style="color:#ffd580;">Author: </span><span style="color:#f29e74;">****
</span><span> </span><span style="color:#ffd580;">Date: </span><span style="color:#f29e74;">****
</span><span> </span><span style="color:#ffd580;">2</span><span> files changed, 1 insertions(+)</span><span style="color:#ffd580;">,</span><span> 5 deletions(-)
</span><span> </span><span style="color:#ffd580;">rewrite</span><span> file1.txt (1%)
</span><span> </span><span style="color:#ffd580;">rewrite</span><span> file2.java (2%)
</span><span style="font-style:italic;color:#5c6773;"># Now you have the target commit as your branch's HEAD commit
</span><span style="color:#ffd580;">$</span><span> git log</span><span style="color:#ffcc66;"> -1
</span><span style="color:#ffd580;">commit</span><span> 807ae1aff5090226d55b0134c48de06bfb8737f9
</span><span style="color:#ffd580;">Author: </span><span style="color:#f29e74;">****
</span><span style="color:#ffd580;">Date: </span><span style="color:#f29e74;">****
</span><span style="color:#ffd580;">some</span><span> feature resolves a stupid issue
</span></code></pre>
<h2 id="8-show-me-the-changed-files"><a class="zola-anchor" href="#8-show-me-the-changed-files" aria-label="Anchor link for: 8-show-me-the-changed-files">#</a>
8. Show me the changed files</h2>
<p>I use this command very often. It will help list up all the changed files with their current status (added or deleted or modified).</p>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#ffd580;">$</span><span> git diff</span><span style="color:#ffcc66;"> --name-status
</span><span style="color:#ffd580;">M</span><span> CHANGELOG.md
</span><span style="color:#ffd580;">M</span><span> README.md
</span><span style="color:#ffd580;">M</span><span> file1.txt
</span><span style="color:#ffd580;">A</span><span> file2.java
</span><span style="color:#ffd580;">D</span><span> file3.java
</span></code></pre>
<h2 id="9-stash-the-changes-before-going-to-a-different-work-branch-git-stash"><a class="zola-anchor" href="#9-stash-the-changes-before-going-to-a-different-work-branch-git-stash" aria-label="Anchor link for: 9-stash-the-changes-before-going-to-a-different-work-branch-git-stash">#</a>
9. Stash the changes before going to a different work branch - $ git stash</h2>
<p>We might need this command a lot in order to keep the changes during switching or synchronizing our work branch.</p>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#ffd580;">$</span><span> git status
</span><span style="color:#ffd580;">On</span><span> branch develop
</span><span style="color:#ffd580;">Changes</span><span> not staged for commit:
</span><span> (</span><span style="color:#ffd580;">use </span><span style="color:#bae67e;">"git add/rm <file>..."</span><span> to update what will be committed)
</span><span> (</span><span style="color:#ffd580;">use </span><span style="color:#bae67e;">"git checkout -- <file>..."</span><span> to discard changes in working directory)
</span><span> </span><span style="color:#ffd580;">modified:</span><span> file1.txt
</span><span> </span><span style="color:#ffd580;">deleted:</span><span> file2.java
</span><span style="color:#ffd580;">no</span><span> changes added to commit (use </span><span style="color:#bae67e;">"git add"</span><span> and/or </span><span style="color:#bae67e;">"git commit -a"</span><span>)
</span><span>
</span><span>
</span><span style="font-style:italic;color:#5c6773;"># You need to handle the changes first to switch to another branch
</span><span style="color:#ffd580;">$</span><span> git checkout master
</span><span style="color:#ffd580;">error:</span><span> Your local changes to the following files would be overwritten by checkout:
</span><span> </span><span style="color:#ffd580;">modified:</span><span> file1.txt
</span><span> </span><span style="color:#ffd580;">deleted:</span><span> file2.java
</span><span style="color:#ffd580;">Please</span><span> commit your changes or stash them before you switch branches.
</span><span style="color:#ffd580;">Aborting
</span><span style="color:#ffd580;">$</span><span> git stash
</span><span style="color:#ffd580;">Saved</span><span> working directory and index state WIP on develop: 8b2cec0 fixed typo
</span></code></pre>
<p>There are some other useful commands based on “git stash”.</p>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="font-style:italic;color:#5c6773;"># List up the stashes
</span><span style="color:#ffd580;">$</span><span> git stash list
</span><span style="color:#ffd580;">stash@{0</span><span>}</span><span style="color:#f28779;">:</span><span> WIP on develop: 8b2cec0 fixed typo
</span><span style="font-style:italic;color:#5c6773;"># Recover from your stashes (the HEAD stash)
</span><span style="color:#ffd580;">$</span><span> git stash pop
</span><span style="color:#ffd580;">On</span><span> branch develop
</span><span style="color:#ffd580;">Your</span><span> branch is up to date with </span><span style="color:#bae67e;">'origin/develop'</span><span>.
</span><span style="color:#ffd580;">Changes</span><span> not staged for commit:
</span><span> (</span><span style="color:#ffd580;">use </span><span style="color:#bae67e;">"git add <file>..."</span><span> to update what will be committed)
</span><span> (</span><span style="color:#ffd580;">use </span><span style="color:#bae67e;">"git checkout -- <file>..."</span><span> to discard changes in working directory)
</span><span> </span><span style="color:#ffd580;">modified:</span><span> file1.txt
</span><span> </span><span style="color:#ffd580;">deleted:</span><span> file2.java
</span><span style="color:#ffd580;">no</span><span> changes added to commit (use </span><span style="color:#bae67e;">"git add"</span><span> and/or </span><span style="color:#bae67e;">"git commit -a"</span><span>)
</span><span style="color:#ffd580;">Dropped</span><span> refs/stash@{0} (0f14acba974e2c11e33880db9bcc7b4012151e6f)
</span><span style="font-style:italic;color:#5c6773;"># Clear all the stashes of the current branch
</span><span style="color:#ffd580;">$</span><span> git stash clear
</span><span style="font-style:italic;color:#5c6773;"># Peek the changes of the HEAD stash
</span><span style="color:#ffd580;">$</span><span> git stash show
</span><span style="color:#ffd580;">file1.txt </span><span style="color:#f29e74;">| </span><span style="color:#ffd580;">1</span><span> +-
</span><span style="color:#ffd580;">file2.java </span><span style="color:#f29e74;">| </span><span style="color:#ffd580;">1</span><span> +-
</span><span style="color:#ffd580;">2</span><span> file changed, 2 insertion(+)</span><span style="color:#ffd580;">,</span><span> 2 deletion(-)
</span></code></pre>
<h2 id="10-get-the-file-from-another-branch-without-switching-branch-git-checkout-develop-file1-txt"><a class="zola-anchor" href="#10-get-the-file-from-another-branch-without-switching-branch-git-checkout-develop-file1-txt" aria-label="Anchor link for: 10-get-the-file-from-another-branch-without-switching-branch-git-checkout-develop-file1-txt">#</a>
10. Get the file from another branch without switching branch – $ git checkout develop – file1.txt</h2>
<p>It’s quite common that we actually need some changes from a different branch. A stupid way to do this is to switch to the target branch and copy the changes. Then switch back to the work branch and paste your code somewhere. With this very common command “git checkout”, you can easily achieve it without switching the branch.</p>
<pre data-lang="sh" style="background-color:#212733;color:#ccc9c2;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#ffd580;">$</span><span> git status
</span><span style="color:#ffd580;">On</span><span> branch master
</span><span style="color:#ffd580;">Your</span><span> branch is up to date with </span><span style="color:#bae67e;">'origin/master'</span><span>.
</span><span style="color:#ffd580;">nothing</span><span> to commit, working tree clean
</span><span style="font-style:italic;color:#5c6773;"># checkout the file from develop
</span><span style="color:#ffd580;">$</span><span> git checkout develop</span><span style="color:#f29e74;"> --</span><span> file1.txt
</span><span style="font-style:italic;color:#5c6773;"># the changes are ready to be committed after "checkout"
</span><span style="color:#ffd580;">$</span><span> git status
</span><span style="color:#ffd580;">On</span><span> branch master
</span><span style="color:#ffd580;">Your</span><span> branch is up to date with </span><span style="color:#bae67e;">'origin/master'</span><span>.
</span><span style="color:#ffd580;">Changes</span><span> to be committed:
</span><span> (</span><span style="color:#ffd580;">use </span><span style="color:#bae67e;">"git reset HEAD <file>..."</span><span> to unstage)
</span><span> </span><span style="color:#ffd580;">modified:</span><span> file1.txt
</span></code></pre>
Privacy And Policies2020-06-01T00:00:00+00:002020-06-01T00:00:00+00:00https://pitayan.com/privacy-policy/<p>At Pitayan, accessible from <a rel="noopener nofollow noreferrer" target="_blank" href="https://pitayan.com">https://pitayan.com</a>, one of our main priorities is the privacy of our visitors. This Privacy Policy document contains types of information that is collected and recorded by Pitayan and how we use it.</p>
<p>If you have additional questions or require more information about our Privacy Policy, do not hesitate to contact us.</p>
<h2 id="1-log-files">1. Log Files</h2>
<p>Pitayan follows a standard procedure of using log files. These files log visitors when they visit websites. All hosting companies do this and a part of hosting services’ analytics. The information collected by log files include internet protocol (IP) addresses, browser type, Internet Service Provider (ISP), date and time stamp, referring/exit pages, and possibly the number of clicks. These are not linked to any information that is personally identifiable. The purpose of the information is for analyzing trends, administering the site, tracking users’ movement on the website, and gathering demographic information.</p>
<h2 id="2-cookies-and-web-beacons">2. Cookies and Web Beacons</h2>
<p>Like any other website, Pitayan uses ‘cookies’. These cookies are used to store information including visitors’ preferences, and the pages on the website that the visitor accessed or visited. The information is used to optimize the users’ experience by customizing our web page content based on visitors’ browser type and/or other information.</p>
<p>For more general information on cookies, please read the “What Are Cookies” article on</p>
<ul>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://www.cookieconsent.com/what-are-cookies/">Cookie Consent website</a>.</li>
</ul>
<h2 id="3-google-doubleclick-dart-cookie">3. Google DoubleClick DART Cookie</h2>
<p>Google is one of a third-party vendor on our site. It also uses cookies, known as DART cookies, to serve ads to our site visitors based upon their visit to www.website.com and other sites on the internet. However, visitors may choose to decline the use of DART cookies by visiting the Google ad and content network Privacy Policy at the following URL</p>
<ul>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://policies.google.com/technologies/ads">Google ads policies</a></li>
</ul>
<h2 id="4-our-advertising-partners">4. Our Advertising Partners</h2>
<p>Some of advertisers on our site may use cookies and web beacons. Our advertising partners are listed below. Each of our advertising partners has their own Privacy Policy for their policies on user data. For easier access, we hyperlinked to their Privacy Policies below</p>
<ul>
<li><a rel="noopener nofollow noreferrer" target="_blank" href="https://policies.google.com/technologies/ads">Google Adsense</a></li>
</ul>
<h2 id="5-privacy-policies">5. Privacy Policies</h2>
<p>You may consult this list to find the Privacy Policy for each of the advertising partners of Pitayan. Our Privacy Policy was created with the help of the <a rel="noopener nofollow noreferrer" target="_blank" href="https://www.privacypolicygenerator.org">Free Privacy Policy Generator</a> and the <a rel="noopener nofollow noreferrer" target="_blank" href="https://www.privacypolicyonline.com/privacy-policy-generator/">Privacy Policy Generator Online</a>.</p>
<p>Third-party ad servers or ad networks uses technologies like cookies, JavaScript, or Web Beacons that are used in their respective advertisements and links that appear on Pitayan, which are sent directly to users’ browser. They automatically receive your IP address when this occurs. These technologies are used to measure the effectiveness of their advertising campaigns and/or to personalize the advertising content that you see on websites that you visit.</p>
<p>Note that Pitayan has no access to or control over these cookies that are used by third-party advertisers.</p>
<h2 id="6-third-party-privacy-policies">6. Third Party Privacy Policies</h2>
<p>Pitayan’s Privacy Policy does not apply to other advertisers or websites. Thus, we are advising you to consult the respective Privacy Policies of these third-party ad servers for more detailed information. It may include their practices and instructions about how to opt-out of certain options.</p>
<p>You can choose to disable cookies through your individual browser options. To know more detailed information about cookie management with specific web browsers, it can be found at the browsers’ respective websites. What Are Cookies?</p>
<h2 id="7-children-s-information">7. Children’s Information</h2>
<p>Another part of our priority is adding protection for children while using the internet. We encourage parents and guardians to observe, participate in, and/or monitor and guide their online activity.</p>
<p>Pitayan does not knowingly collect any Personal Identifiable Information from children under the age of 13. If you think that your child provided this kind of information on our website, we strongly encourage you to contact us immediately and we will do our best efforts to promptly remove such information from our records.</p>
<h2 id="8-online-privacy-policy-only">8. Online Privacy Policy Only</h2>
<p>This Privacy Policy applies only to our online activities and is valid for visitors to our website with regards to the information that they shared and/or collect in Pitayan. This policy is not applicable to any information collected offline or via channels other than this website.</p>
<h2 id="9-consent">9. Consent</h2>
<p>By using our website, you hereby consent to our Privacy Policy and agree to its <a href="/terms-and-conditions">Terms and Conditions</a>.</p>
Terms and Conditions2020-06-01T00:00:00+00:002020-06-01T00:00:00+00:00https://pitayan.com/terms-and-conditions/<p>These terms and conditions outline the rules and regulations for the use of Pitayan’s Website, located at <a rel="noopener nofollow noreferrer" target="_blank" href="https://pitayan.com">https://pitayan.com</a>.</p>
<p>By accessing this website we assume you accept these terms and conditions. Do not continue to use Pitayan if you do not agree to take all of the terms and conditions stated on this page. Our Terms and Conditions were created with the help of the <a rel="noopener nofollow noreferrer" target="_blank" href="https://www.privacypolicyonline.com/terms-conditions-generator/">Terms & Conditions Generator Online</a> and the <a rel="noopener nofollow noreferrer" target="_blank" href="https://www.termsconditionsgenerator.com">Free Terms & Conditions Generator</a>.</p>
<p>The following terminology applies to these Terms and Conditions, Privacy Statement and Disclaimer Notice and all Agreements: “Client”, “You” and “Your” refers to you, the person log on this website and compliant to our terms and conditions. “The Pitayan”, “Ourselves”, “We”, “Our” and “Us”, refers to our website. “Party”, “Parties”, or “Us”, refers to both the Client and ourselves. All terms refer to the offer, acceptance and consideration of payment necessary to undertake the process of our assistance to the Client in the most appropriate manner for the express purpose of meeting the Client’s needs in respect of provision of the Pitayan’s stated services, in accordance with and subject to, prevailing law of Netherlands. Any use of the above terminology or other words in the singular, plural, capitalization and/or he/she or they, are taken as interchangeable and therefore as referring to same.</p>
<h2 id="1-cookies">1. Cookies</h2>
<p>We employ the use of cookies. By accessing Pitayan, you agreed to use cookies in agreement with the Pitayan’s Privacy Policy.</p>
<p>Most interactive websites use cookies to let us retrieve the user’s details for each visit. Cookies are used by our website to enable the functionality of certain areas to make it easier for people visiting our website. Some of our affiliate/advertising partners may also use cookies.</p>
<h2 id="2-license">2. License</h2>
<p>Unless otherwise stated, Pitayan and/or its licensors own the intellectual property rights for all material on Pitayan. All intellectual property rights are reserved. You may access this from Pitayan for your own personal use subjected to restrictions set in these terms and conditions.</p>
<p>You must not:</p>
<ul>
<li>Republish material from Pitayan</li>
<li>Sell, rent or sub-license material from Pitayan</li>
<li>Reproduce, duplicate or copy material from Pitayan</li>
<li>Redistribute content from Pitayan</li>
</ul>
<p>This Agreement shall begin on the date hereof Jun.1 2020.</p>
<p>Parts of this website offer an opportunity for users to post and exchange opinions and information in certain areas of the website. Pitayan does not filter, edit, publish or review Comments prior to their presence on the website. Comments do not reflect the views and opinions of Pitayan,its agents and/or affiliates. Comments reflect the views and opinions of the person who post their views and opinions. To the extent permitted by applicable laws, Pitayan shall not be liable for the Comments or for any liability, damages or expenses caused and/or suffered as a result of any use of and/or posting of and/or appearance of the Comments on this website.</p>
<p>Pitayan reserves the right to monitor all Comments and to remove any Comments which can be considered inappropriate, offensive or causes breach of these Terms and Conditions.</p>
<p>You warrant and represent that:</p>
<ul>
<li>You are entitled to post the Comments on our website and have all necessary licenses and consents to do so;</li>
<li>The Comments do not invade any intellectual property right, including without limitation copyright, patent or trademark of any third party;</li>
<li>The Comments do not contain any defamatory, libelous, offensive, indecent or otherwise unlawful material which is an invasion of privacy</li>
<li>The Comments will not be used to solicit or promote business or custom or present commercial activities or unlawful activity.</li>
</ul>
<p>You hereby grant Pitayan a non-exclusive license to use, reproduce, edit and authorize others to use, reproduce and edit any of your Comments in any and all forms, formats or media.</p>
<h2 id="3-hyperlinking-to-our-content">3. Hyperlinking to our Content</h2>
<p>The following organizations may link to our Website without prior written approval:</p>
<ul>
<li>Government agencies;</li>
<li>Search engines;</li>
<li>News organizations;</li>
<li>Online directory distributors may link to our Website in the same manner as they hyperlink to the Websites of other listed businesses; and</li>
<li>System wide Accredited Businesses except soliciting non-profit organizations, charity shopping malls, and charity fundraising groups which may not hyperlink to our Web site.</li>
</ul>
<p>These organizations may link to our home page, to publications or to other Website information so long as the link: (a) is not in any way deceptive; (b) does not falsely imply sponsorship, endorsement or approval of the linking party and its products and/or services; and (c) fits within the context of the linking party’s site.</p>
<p>We may consider and approve other link requests from the following types of organizations:</p>
<ul>
<li>commonly-known consumer and/or business information sources;</li>
<li>dot.com community sites;</li>
<li>associations or other groups representing charities;</li>
<li>online directory distributors;</li>
<li>internet portals;</li>
<li>accounting, law and consulting firms; and</li>
<li>educational institutions and trade associations.</li>
</ul>
<p>We will approve link requests from these organizations if we decide that: (a) the link would not make us look unfavorably to ourselves or to our accredited businesses; (b) the organization does not have any negative records with us; (c) the benefit to us from the visibility of the hyperlink compensates the absence of Pitayan; and (d) the link is in the context of general resource information.</p>
<p>These organizations may link to our home page so long as the link: (a) is not in any way deceptive; (b) does not falsely imply sponsorship, endorsement or approval of the linking party and its products or services; and (c) fits within the context of the linking party’s site.</p>
<p>If you are one of the organizations listed in paragraph 2 above and are interested in linking to our website, you must inform us by sending an e-mail to Pitayan. Please include your name, your organization name, contact information as well as the URL of your site, a list of any URLs from which you intend to link to our Website, and a list of the URLs on our site to which you would like to link. Wait 2-3 weeks for a response.</p>
<p>Approved organizations may hyperlink to our Website as follows:</p>
<ul>
<li>By use of our corporate name; or</li>
<li>By use of the uniform resource locator being linked to; or</li>
<li>By use of any other description of our Website being linked to that makes sense within the context and format of content on the linking party’s site.</li>
</ul>
<p>No use of Pitayan’s logo or other artwork will be allowed for linking absent a trademark license agreement.</p>
<h2 id="4-iframes">4. iFrames</h2>
<p>Without prior approval and written permission, you may not create frames around our Webpages that alter in any way the visual presentation or appearance of our Website.</p>
<h2 id="5-content-liability">5. Content Liability</h2>
<p>We shall not be hold responsible for any content that appears on your Website. You agree to protect and defend us against all claims that is rising on your Website. No link(s) should appear on any Website that may be interpreted as libelous, obscene or criminal, or which infringes, otherwise violates, or advocates the infringement or other violation of, any third party rights.</p>
<h2 id="6-reservation-of-rights">6. Reservation of Rights</h2>
<p>We reserve the right to request that you remove all links or any particular link to our Website. You approve to immediately remove all links to our Website upon request. We also reserve the right to amen these terms and conditions and it’s linking policy at any time. By continuously linking to our Website, you agree to be bound to and follow these linking terms and conditions.</p>
<h2 id="7-removal-of-links-from-our-website">7. Removal of links from our website</h2>
<p>If you find any link on our Website that is offensive for any reason, you are free to contact and inform us any moment. We will consider requests to remove links but we are not obligated to or so or to respond to you directly.</p>
<p>We do not ensure that the information on this website is correct, we do not warrant its completeness or accuracy; nor do we promise to ensure that the website remains available or that the material on the website is kept up to date.</p>
<h2 id="8-disclaimer">8. Disclaimer</h2>
<p>To the maximum extent permitted by applicable law, we exclude all representations, warranties and conditions relating to our website and the use of this website. Nothing in this disclaimer will:</p>
<ul>
<li>limit or exclude our or your liability for death or personal injury;</li>
<li>limit or exclude our or your liability for fraud or fraudulent misrepresentation;</li>
<li>limit any of our or your liabilities in any way that is not permitted under applicable law; or</li>
<li>exclude any of our or your liabilities that may not be excluded under applicable law.</li>
</ul>
<p>The limitations and prohibitions of liability set in this Section and elsewhere in this disclaimer: (a) are subject to the preceding paragraph; and (b) govern all liabilities arising under the disclaimer, including liabilities arising in contract, in tort and for breach of statutory duty.</p>
<p>As long as the website and the information and services on the website are provided free of charge, we will not be liable for any loss or damage of any nature.</p>
Write For Us2020-06-01T00:00:00+00:002020-06-01T00:00:00+00:00https://pitayan.com/write-for-us/<p>Hello friend! Thought you must be interested in submitting your own writings to this site. <a rel="noopener nofollow noreferrer" target="_blank" href="https://pitayan.com">Pitayan.com</a> welcome people who are also fan of software development.
If you are also one of them, why not read the following guide and begin your writings?</p>
<h2 id="commitments">Commitments</h2>
<p>For those who wants to contribute to Pitayan, please feel free to submit articles. However, there are several
commitments for an author to undertake and obey.</p>
<ol>
<li>A contributor will undertake the legit responsibility of the content of his/her articles. Which means all articles need to be at least original. No copy/paste allowed. However, referencing other articles is permitted.</li>
<li>The content of articles must relate to web development. Which should contain the following example topics:
<ul>
<li>computers</li>
<li>software</li>
<li>technologies</li>
<li>soft-skills</li>
<li>team-management</li>
<li>development & testing</li>
<li>tools</li>
<li>other topics that engineers are keen on</li>
</ul>
</li>
<li>Obey the laws and regulations. Promise to not submit any inappropriate contents.</li>
</ol>
<h2 id="know-the-system">Know the system</h2>
<p>This site is built with <a rel="noopener nofollow noreferrer" target="_blank" href="https://getzola.org">Zola</a>. Which means, our article adopts <a rel="noopener nofollow noreferrer" target="_blank" href="https://en.wikipedia.org/wiki/Markdown">Markdown</a> syntax. Even if you are not familiar with this mark up language, it’d be a easy tool for you to grasp within short time.</p>
<h2 id="submit-your-work">Submit your work</h2>
<p>If you wrote your article in <a rel="noopener nofollow noreferrer" target="_blank" href="https://en.wikipedia.org/wiki/Markdown">Markdown</a> language, then with only a few more steps you’ll be ready to submit.</p>
<h3 id="frontmatter">Frontmatter</h3>
<p>“Frontmatter” contains a custom attributes at the top of the Markdown file. The valid ones for our side are as follows:</p>
<ul>
<li>title</li>
<li>date</li>
<li>description: A short brief about the whole article</li>
<li>slug (optional): The article path in our site</li>
<li>authors: Who wrote this article, welcome collaborators</li>
<li>categories: What’s this article about?</li>
</ul>
<p>A good example here:</p>
<pre data-lang="markdown" style="background-color:#212733;color:#ccc9c2;" class="language-markdown "><code class="language-markdown" data-lang="markdown"><span style="background-color:#ccc9c210;font-weight:bold;color:#5c6773;">---
</span><span>title: "I created a sexy voice assistant in 180 lines of code"
</span><span>slug: "posts/voice-assistant"
</span><span>description: "The helper app is going to listen to your voice and turn it into plain text. After all that it will open up a new tab page of Google Search with the text recorded."
</span><span>date: 2020-07-22
</span><span>taxonomies:
</span><span> authors:
</span><span> - Yanze Dai
</span><span> categories:
</span><span> - Javascript
</span><span> - Vue
</span><span style="font-weight:bold;color:#ffa759;">---
</span></code></pre>
<h3 id="your-info">Your Info</h3>
<p>Don’t worry, you’ll only need to provide public information of yourself. These information will be used in <a href="/authors">/authors</a> and article pages to tell other readers who wrote this amazing article.</p>
<ul>
<li>name</li>
<li>social (required)</li>
<li>bio (required)</li>
</ul>
<p>If you have an profile picture, please do attach it together with your public info.</p>
<p>Another good example here:</p>
<pre data-lang="yaml" style="background-color:#212733;color:#ccc9c2;" class="language-yaml "><code class="language-yaml" data-lang="yaml"><span>---
</span><span style="color:#73d0ff;">name</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">Yanze Dai
</span><span style="color:#73d0ff;">extra</span><span style="color:#ccc9c2cc;">:
</span><span> </span><span style="color:#73d0ff;">image</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">profile.jpg
</span><span> </span><span style="color:#73d0ff;">social</span><span style="color:#ccc9c2cc;">:
</span><span> </span><span style="color:#73d0ff;">stackoverflow</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">7831025/daiyanze
</span><span> </span><span style="color:#73d0ff;">facebook</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">yanze.dai
</span><span> </span><span style="color:#73d0ff;">twitter</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">daiyanze
</span><span> </span><span style="color:#73d0ff;">github</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">daiyanze
</span><span> </span><span style="color:#73d0ff;">medium</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">"@daiyanze"
</span><span> </span><span style="color:#73d0ff;">mailto</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">mailto:ginoalex8964@yahoo.com
</span><span> </span><span style="color:#73d0ff;">url</span><span style="color:#ccc9c2cc;">: </span><span style="color:#bae67e;">https://daiyanze.com
</span><span>---
</span><span style="color:#bae67e;">An editor of Pitayan.com (Yes. Please write your bio here.)
</span></code></pre>
<h2 id="hand-over">Hand Over</h2>
<p>When you are ready, please send an email with your article folder (zipped) to the following address:
<a href="mailto:pitayanblog@mail.com">pitayanblog@gmail.com</a></p>
<p>Your articles will be reviewed ASAP. You will be reply when the article is adopted.</p>
<h2 id="join-us-as-contributors">Join us as contributors</h2>
<p>Are you also into web development? Join Pitayan organization and you’ll know the rest.</p>
<p>Please follow <a rel="noopener nofollow noreferrer" target="_blank" href="https://github.com/Pitayan">Pitayan</a> on Github and contact <a href="mailto:pitayanblog@mail.com">pitayanblog@gmail.com</a>.</p>