<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>JavaScript on Colinx Blog</title>
    
    
    
    <link>/tags/javascript/</link>
    <description>Recent content in JavaScript on Colinx Blog</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <lastBuildDate>Fri, 06 May 2022 00:00:00 +0000</lastBuildDate>
    
	<atom:link href="/tags/javascript/index.xml" rel="self" type="application/rss+xml" />
    
    
    <item>
      <title>前端静态资源加载的一些优化</title>
      <link>/posts/%E5%89%8D%E7%AB%AF%E9%9D%99%E6%80%81%E8%B5%84%E6%BA%90%E5%8A%A0%E8%BD%BD%E7%9A%84%E4%B8%80%E4%BA%9B%E4%BC%98%E5%8C%96/</link>
      <pubDate>Fri, 06 May 2022 00:00:00 +0000</pubDate>
      
      <guid>/posts/%E5%89%8D%E7%AB%AF%E9%9D%99%E6%80%81%E8%B5%84%E6%BA%90%E5%8A%A0%E8%BD%BD%E7%9A%84%E4%B8%80%E4%BA%9B%E4%BC%98%E5%8C%96/</guid>
      <description>
        
          
          
          
        
        
        

&lt;!-- # 前端静态资源加载的一些优化 --&gt;

&lt;p&gt;最近在折腾优化博客，由于全站都是部署在 Netlify、Vercel 的 CDN 上，都是海外节点，国内访问延迟高，在想办法优化下访问速度和体验。线路部分的优化可以看另一篇文章&lt;a href=&#34;https://blog.colinx.one/posts/%E5%9B%BD%E5%A4%96%E9%9D%99%E6%80%81%E7%BD%91%E7%AB%99%E6%89%98%E7%AE%A1%E5%9C%A8%E5%9B%BD%E5%86%85%E9%80%9F%E5%BA%A6%E5%AF%B9%E6%AF%94%E5%8F%8A%E7%BA%BF%E8%B7%AF%E4%BC%98%E5%8C%96/&#34;&gt;国外静态网站托管服务商国内速度对比及线路优化&lt;/a&gt;。这篇文章来探讨下前端 CSS 和 JS 资源的加载。&lt;/p&gt;

&lt;p&gt;优化目标主要就两个：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;尽可能快，但是不希望我引入的辅助性第三方库影响到页面体验，不要阻塞主要内容渲染&lt;/li&gt;
&lt;li&gt;为了快第三方静态资源肯定是上 CDN，但是要有容灾，CDN 挂了要能 fallback 到其他 url&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&#34;静态资源异步加载&#34;&gt;静态资源异步加载&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;prefetch&lt;/code&gt;、&lt;code&gt;preload&lt;/code&gt;这些预加载技术暂且不谈，博客站点没那么多资源。这里主要是用&lt;code&gt;async&lt;/code&gt;和&lt;code&gt;defer&lt;/code&gt;来控制脚本异步加载，以避免阻塞 DOM 解析和页面渲染。&lt;/p&gt;

&lt;p&gt;使用&lt;code&gt;async&lt;/code&gt;异步加载，对于&lt;a href=&#34;https://github.com/vinta/pangu.js/&#34;&gt;pangu&lt;/a&gt;这种会影响到内容呈现的使用&lt;code&gt;async&lt;/code&gt;，对于统计类第三方库使用&lt;code&gt;defer&lt;/code&gt;延迟加载，到 DOM 加载完再执行&lt;/p&gt;

&lt;p&gt;有些第三方库需要手动 init，这样就不能使用&lt;code&gt;defer&lt;/code&gt;、&lt;code&gt;async&lt;/code&gt;关键字加载资源了，&lt;strong&gt;&lt;code&gt;defer&lt;/code&gt;和&lt;code&gt;async&lt;/code&gt;关键字只适用于远程资源&lt;/strong&gt;，本以为把远程资源的&lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;和内嵌在 HTML 里用来 init 的&lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;都加上&lt;code&gt;defer&lt;/code&gt;就可以利用它顺序加载的特性解决这个问题，结果发现不是这样的。。。。异步打乱脚本执行顺序，init 脚本不等待资源下载完毕会先执行。利用好&lt;code&gt;onload&lt;/code&gt;事件可以解决这个问题。&lt;/p&gt;

&lt;p&gt;before&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-html&#34;&gt;&amp;lt;script src=&amp;quot;https://cdn.jsdelivr.net/npm/pangu@4/dist/browser/pangu.min.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script&amp;gt;
    pangu.spacingElementByClassName(&#39;post&#39;);
    pangu.spacingElementByTagName(&#39;p&#39;);

    document.addEventListener(&#39;DOMContentLoaded&#39;, () =&amp;gt; {
        pangu.autoSpacingPage();
    });
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;after&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-html&#34;&gt;&amp;lt;script&amp;gt;
    function panguSpaing() {
        pangu.spacingElementByClassName(&#39;post&#39;);
        pangu.spacingElementByTagName(&#39;p&#39;);

        document.addEventListener(&#39;DOMContentLoaded&#39;, () =&amp;gt; {
            // listen to any DOM change and automatically perform spacing via MutationObserver()
            pangu.autoSpacingPage();
        });
    }
&amp;lt;/script&amp;gt;
&amp;lt;script async onload=&amp;quot;panguSpaing()&amp;quot; src=&amp;quot;https://cdn.jsdelivr.net/npm/pangu@4/dist/browser/pangu.min.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;除了&lt;code&gt;onload&lt;/code&gt;还有&lt;code&gt;onerror&lt;/code&gt;，还可以利用&lt;code&gt;onerror&lt;/code&gt;事件在资源加载失败时 fallback 到其他 CDN，详见下文&lt;/p&gt;

&lt;p&gt;这里跑了个测试看下优化前后的差距&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;优化前&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1301127393.file.myqcloud.com/BlogImgs/202205062117479.png&#34; alt=&#34;image-20220506193639029&#34; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1301127393.file.myqcloud.com/BlogImgs/202205062117127.png&#34; alt=&#34;image-20220506193800266&#34; /&gt;&lt;/p&gt;

&lt;p&gt;First Contentful Pain Time 在 1.5s，整个页面到 1.5s 才能可见，DOM 加载总时间达到了 2.5s&lt;/p&gt;

&lt;p&gt;本来我都已经把首页不需要加载的三方库都拿走了，但是还剩下一个，这个用于前端性能监控的 js 阻塞了渲染，挂的是腾讯云的 CDN，国内快到飞起，国外就卡到不行（作为对比上图里从 jsdelivr 加载的 js 也是快到飞起，墙内就慢得一批）&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;优化后&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1301127393.file.myqcloud.com/BlogImgs/202205062117498.png&#34; alt=&#34;image-20220506193652188&#34; /&gt;&lt;/p&gt;

&lt;p&gt;优化后页面没有再阻塞，0.8s 页面就可用了，总 DOM 加载时间只有 1.5s，相较之前简直直接起飞。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1301127393.file.myqcloud.com/BlogImgs/202205071426038.png&#34; alt=&#34;cn&#34; /&gt;&lt;/p&gt;

&lt;p&gt;国内某检测平台的结果也显示优化效果明显&lt;/p&gt;

&lt;h2 id=&#34;静态资源-fallback-加载&#34;&gt;静态资源 Fallback 加载&lt;/h2&gt;

&lt;p&gt;国内好用的静态资源 CDN 还真没有，新浪那种只提供那么几个热门的库完全不符合需求，BootCDN 炸了不是一次两次，字节 CDN 的资源 URL 有够 ugly，URL 里面还带节点信息多半后面会出问题。360 奇舞 CDN 之前也换过域名、备案出问题，感觉每一个能省心用。七牛的目前看上去还行，也不知道后面会不会出什么幺蛾子。。。为了稳妥起见 + 照顾全球的访客，还是优先使用的 cloudflare 和 jsdelivr 的 CDN&lt;/p&gt;

&lt;p&gt;网站主要三方库都是通过 jsdelivr 和 cloudflare 引入的，但是毕竟是海外的节点不知道哪一天就被墙了，所以还要为墙内的访客准备一个 fallback 方案。这里利用到&lt;code&gt;script&lt;/code&gt;标签的&lt;code&gt;onerror&lt;/code&gt;事件&lt;/p&gt;

&lt;p&gt;现代浏览器都支持&lt;code&gt;script&lt;/code&gt;和&lt;code&gt;link&lt;/code&gt;标签上的&lt;code&gt;onload&lt;/code&gt;和&lt;code&gt;onerror&lt;/code&gt;事件，详细支持情况可以查看这里&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://pie.gd/test/script-link-events/&#34;&gt;https://pie.gd/test/script-link-events/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;关于&lt;code&gt;onerror&lt;/code&gt;，中文互联网上有好几篇文章说他会捕获&lt;code&gt;script&lt;/code&gt;标签里的代码的执行错误。。。简直离谱。稍微查下 MDN 之类的文档就可以发现这玩意本来就是只捕获资源加载错误的&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The onerror event is triggered if an error occurs while loading an external file (e.g. a document or an image).&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://www.w3schools.com/jsref/event_onerror.asp&#34;&gt;https://www.w3schools.com/jsref/event_onerror.asp&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;利用好这个特性，就可以实现在资源加载失败时 fallback 从另一个 url 加载资源。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-html&#34;&gt;&amp;lt;script&amp;gt;
    function fallbackJSloader(url, loadedEvent) {
        console.log(&#39;Error loading asset, using fallback url&#39;);
        console.log(&#39;load asset from&#39;, url);

        let script = document.createElement(&amp;quot;script&amp;quot;);
        script.type = &amp;quot;text/javascript&amp;quot;;
        script.src = url;
        script.async = true;
        if (loadedEvent)
            script.setAttribute(&#39;onload&#39;, `${loadedEvent}()`)
        document.body.appendChild(script);
    }
&amp;lt;/script&amp;gt;

&amp;lt;script defer onload=&amp;quot;init_gitalk()&amp;quot;
onerror=&amp;quot;fallbackJSloader(&#39;https://cdn.staticfile.org/gitalk/1.7.2/gitalk.min.js&#39;,&#39;init_gitalk&#39;)&amp;quot;
src=&amp;quot;https://cdn.jsdelivr.net/npm/gitalk@1/dist/gitalk.min.js&amp;quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;CSS 的 fallback 加载同理，此处不再赘述&lt;/p&gt;

&lt;p&gt;优化后如图，在开发者工具中手动 block 资源网站，可以自动 fallback 从另一个 url 加载资源，这下网站的可用性又提升了一点点🤏哈哈哈&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1301127393.file.myqcloud.com/BlogImgs/202205062133393.png&#34; alt=&#34;66230832-3581-42DF-9F2F-B817E88DAF4D&#34; /&gt;&lt;/p&gt;

&lt;h2 id=&#34;扩展阅读&#34;&gt;扩展阅读&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.w3cplus.com/performance/remove-block-rendering.html&#34;&gt;消除阻塞页面渲染的资源&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://segmentfault.com/q/1010000000640869&#34;&gt;defer 和 async 的区别&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://lengyun.github.io/js/3-2-2dynamicAddCSS.html#%E4%B8%8E%E5%8A%A8%E6%80%81%E5%8A%A0%E8%BD%BDjs%E7%9A%84%E5%8C%BA%E5%88%AB&#34;&gt;动态加载 css&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;网站 profile&lt;/strong&gt;工具&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.webpagetest.org&#34;&gt;https://www.webpagetest.org&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

        
        </description>
    </item>
    
  </channel>
</rss>