Question
Occasionally, you will be forced to create extremely heavy components in creating and rendering, the main reason is that it must perform too complicated logic. I (the author) are no exception.
I created a site that uses StoryBlok , they have an extremely cool feature that is creating a rich-text field
that content managers can use to format text such as text, list. , images, quote blocks, bold, italics …
When you get the content of rich-text
from StoryBlok API, it will have its own structure. To render that data into HTML, you must call the richTextResolver.render(content)
function from storyblok-js-client
.
For example, if you encapsulate this function to a RichText.vue
component, it basically looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <span class="token tag"><span class="token tag"><span class="token punctuation"><</span> template</span> <span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span> div</span> <span class="token attr-name">v-html</span> <span class="token attr-value"><span class="token punctuation">=</span> <span class="token punctuation">"</span> contentHtml <span class="token punctuation">"</span></span> <span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span> div</span> <span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span> template</span> <span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span> script</span> <span class="token punctuation">></span></span> <span class="token script language-javascript"> <span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span> props <span class="token punctuation">:</span> <span class="token punctuation">[</span> <span class="token string">"content"</span> <span class="token punctuation">]</span> <span class="token punctuation">,</span> computed <span class="token punctuation">:</span> <span class="token punctuation">{</span> <span class="token function">contentHtml</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> $storyapi <span class="token punctuation">.</span> richTextResolver <span class="token punctuation">.</span> <span class="token function">render</span> <span class="token punctuation">(</span> content <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">;</span> </span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span> script</span> <span class="token punctuation">></span></span> |
The code looks pretty simple, doesn’t seem strange, but there’s something quite unexpected.
It seems that rendering is very heavy, most noticeable when rendering many components like this with a decent amount of content.
Now imagine the following scenario: You have a list of rich-text
components on the page and a dropdown to filter based on which criteria. When changing on the dropdown filter, you fetch all the content again and the list will be redrawn.
This is where you notice the heavy of richTextResolver.render
, the dropdown lag when closed after you select.
The reason is that by default JavaScript executes commands on the main thread, which is UI-blocking.
The problem is enlightened, So is there any cure?
Problem solving
Easy: Use Web Worker for rendering rich-text.
Web Workers run in a separate thread, more importantly, they are not UI-blocking
, very suitable for our case.
Note: I didn’t go into Web Workers, you can see its documents .
Always remember that web workers have their own context, and by default we cannot access other external contexts, but we need access to storyblok-js-client
module. To do this, Webpack has a worker-loader
.
First, preset:
1 2 3 4 | npm install -D worker-loader # or yarn add worker-loader |
If you use Nuxt.js, we will configure it in nuxt.config.js
as follows:
1 2 3 4 5 6 7 8 9 | build <span class="token punctuation">:</span> <span class="token punctuation">{</span> <span class="token function">extend</span> <span class="token punctuation">(</span> config <span class="token punctuation">,</span> <span class="token punctuation">{</span> isDev <span class="token punctuation">,</span> isClient <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> config <span class="token punctuation">.</span> module <span class="token punctuation">.</span> rules <span class="token punctuation">.</span> <span class="token function">push</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> test <span class="token punctuation">:</span> <span class="token regex">/.worker.js$/</span> <span class="token punctuation">,</span> use <span class="token punctuation">:</span> <span class="token punctuation">{</span> loader <span class="token punctuation">:</span> <span class="token string">"worker-loader"</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
With this configuration, all *.worker.js
files will be processed and loaded by worker-loader
.
Now try to create render-html.worker.js
1 2 3 4 5 6 7 8 9 10 | <span class="token keyword">import</span> StoryblokClient <span class="token keyword">from</span> <span class="token string">"storyblok-js-client"</span> <span class="token punctuation">;</span> <span class="token keyword">let</span> storyClient <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">StoryblokClient</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// When the parent theard requires it, render the HTML</span> self <span class="token punctuation">.</span> <span class="token function">addEventListener</span> <span class="token punctuation">(</span> <span class="token string">"message"</span> <span class="token punctuation">,</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> data <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> result <span class="token operator">=</span> storyClient <span class="token punctuation">.</span> richTextResolver <span class="token punctuation">.</span> <span class="token function">render</span> <span class="token punctuation">(</span> data <span class="token punctuation">)</span> <span class="token punctuation">;</span> self <span class="token punctuation">.</span> <span class="token function">postMessage</span> <span class="token punctuation">(</span> result <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
That is a basic way of doing worker. You need to listen to the message
event – the way the worker communicates with the Vue.js app. From there you can get the data
from the event, render it with storyblok-js-client
and return the result self.postMessage
.
Now we need to fix the RichText.vue
component a bit to use the worker.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | <span class="token tag"><span class="token tag"><span class="token punctuation"><</span> template</span> <span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span> div</span> <span class="token attr-name">v-html</span> <span class="token attr-value"><span class="token punctuation">=</span> <span class="token punctuation">"</span> contentHtml <span class="token punctuation">"</span></span> <span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span> div</span> <span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span> template</span> <span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span> script</span> <span class="token punctuation">></span></span> <span class="token script language-javascript"> <span class="token keyword">import</span> Worker <span class="token keyword">from</span> <span class="token string">"./render-html.worker.js"</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> worker <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Worker</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span> props <span class="token punctuation">:</span> <span class="token punctuation">[</span> <span class="token string">"content"</span> <span class="token punctuation">]</span> <span class="token punctuation">,</span> <span class="token function">data</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token punctuation">{</span> contentHtml <span class="token punctuation">:</span> <span class="token string">""</span> <span class="token punctuation">}</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">,</span> <span class="token function">mounted</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// Update the state with the processed HTML content</span> worker <span class="token punctuation">.</span> <span class="token function-variable function">onmessage</span> <span class="token operator">=</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> data <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> contentHtml <span class="token operator">=</span> data <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">;</span> <span class="token comment">// Call the worker to render the content</span> worker <span class="token punctuation">.</span> <span class="token function">postMessage</span> <span class="token punctuation">(</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> content <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">;</span> </span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span> script</span> <span class="token punctuation">></span></span> |
Result
Surely you are curious about how much the performance will improve after using the worker, for sure. We will try to measure a little to performace more meaning.
In fact, you should read this post: learn and undestand how to meassure performance in Vue.js components to make sure you understand the test below.
I have mmeassure the component with medium-size
(6x throttle) on my mac.
The result: The rendering time is 20.65x
faster and the patch time is 1.39x
faster, the figure is also impressive.
If you don’t know what
render
andpatch
mean, this article explains.
And that is today’s tip.
See you next month =))
The article has been translated from https://vuedose.tips , thank you for reading the article.