ÿÿWebpack 動態載入的 Magic Comments | Alex Liu

Webpack 動態載入的 Magic Comments

• 17 min read

動態載入(Dynamic import)å¯ä»¥æœ‰æ•ˆçš„æ¸›å°‘ Webpack 打包後的 bundle size。é©ç•¶åœ°ä½¿ç”¨å‹•態載入,將ä¸ç«‹å³éœ€è¦çš„ code 切分至ç¨ç«‹çš„æ¨¡å¡Šï¼ˆchunk)å¯ä»¥åœ¨åˆæ¬¡è¼‰å…¥æ™‚帶來更好的使用者體驗。此外 webpack 也為動態載入æä¾›äº†ä¸€äº› Magic Comments,讓開發人員能更有彈性的調整å„項細節。本篇將一一介紹這些 Comment 的用法並æ­é…簡易範例來呈ç¾ã€èªªæ˜Žã€‚

å‰è¨€

為什麼想寫這一篇?

åœ¨é€™å€‹éœæ…‹çš„個人網站的首é ï¼Œæˆ‘è¦åŠƒäº†ä¸€å€‹å€åŸŸï¼Œè£¡é¢æ”¾ä¸Šäº†æˆ‘自己比較熟悉的å‰ç«¯æŠ€èƒ½çš„ SVG æ¸…å–®ã€‚è€Œæˆ‘å¸Œæœ›æœªä¾†ä¸€ä½†è¦æ–°å¢žæŠ€èƒ½æ™‚,我åªéœ€åŽ»ä¿®æ”¹ content/skills.ts çš„å…§å®¹ä»¥åŠæ–°å¢ž assets/image/skills 裡é¢çš„ SVG Components 就好,但在整個éŽç¨‹ä¸­æˆ‘é‡åˆ°äº†ä¸€äº›ä»¤æˆ‘苦惱的å•題。

å› ç‚ºæˆ‘ä¸æƒ³è¦ä¸€å€‹ä¸€å€‹æ‰‹å‹•載入,所以我ä¾ç…§ skills.ts æä¾›çš„æŠ€èƒ½è³‡æ–™ï¼Œç”¨ for è¿´åœˆå°‡å…¶å°æ‡‰çš„ SVG Components 動態載入,如下:

<template>
  <!-- ç•¥ -->
    <ul class="skill-list list-none m-0 p-0">
      <li
        v-for="{ svg, name } in skills"
        :key="name"
        :aria-label="name"
        class="skill-list__item skill-tooltip relative inline-block"
      >
        <component
          :is="svg"
          class="skill-list__image md:opacity-75 hover:opacity-100"
        />
      </li>
    </ul>
  <!-- ç•¥ -->
</template>
<script lang="ts">
import { Vue, Component } from 'vue-property-decorator'
import { skills } from '~/content/site'

@Component<TheSkill>({
  beforeCreate () {
    for(let i = 0; i < skills.length; i++) {
      const skill = skills[i].svg
      this.$options.components![skill] = () => import(
        /* webpackChunkName: "svg-skills" */
        `~/assets/images/skills/${skill}.svg?inline`
      )
    }
  }
})
export default class TheSkill extends Vue {
  private skills = Object.freeze(skills)
}
</script>

利用動態載入的機制並æ­é… 動態路徑,讓資料決定需è¦è¼‰å…¥çš„ SVG Components,但å»ä¹Ÿè¡ç”Ÿäº†å…©å€‹å°å•題:

  1. 檔案命åå•題
    天真的我,原本以為打包後會是一隻 svg-skills.hash.js,但實際打包後的檔å廿˜¯é€™æ¨£ï¼š
    svg-skills0.hash.js
    svg-skills1.hash.js
    

    這樣當然還是å¯ä»¥æ­£å¸¸è¼‰å…¥ï¼Œä½†é›£æžå¦‚我希望至少å¯ä»¥é•·åƒé€™æ¨£ï¼š
    svg-skills-nuxtjs.hash.js
    svg-skills-gridsome.hash.js
    

    一開始我解決了這個å•題,但後來發ç¾äº†éŽå¤šå‹•態載入導致的å•題。
  2. 動態載入å•題
    解決了檔案命åçš„å•題後一陣å­ç™¼ç¾ï¼Œé¦–é åœ¨ Google çš„ PageSpeed Insights 上最大內容繪製(LCP,Largest Contentful Paint)指標分數並ä¸ç†æƒ³ï¼Œç¶“éŽå„種嘗試後發ç¾ä¸»è¦åŽŸå› å‡ºåœ¨é€™äº›å‹•æ…‹è¼‰å…¥çš„ SVG Components 上。
    由於這些 SVG Components 都是一載入當下立å³å°±éœ€è¦ä½¿ç”¨çš„,而éŽå¤šçš„å‹•æ…‹è¼‰å…¥é˜»å¡žäº†ç•«é¢æ¸²æŸ“的工作進而導致最大內容繪製的時間éŽé•·ã€‚想改善這個å•題,就è¦è¨­æ³•將這些 SVG Components 在打包æˆä¸€éš» js 模塊後動態載入,或是最好能ä¸ç”¢ç”Ÿé¡å¤–çš„ç¶²è·¯è«‹æ±‚ï¼Œåƒæ˜¯éœæ…‹è¼‰å…¥ã€‚

ä½†æ”¹ç”¨éœæ…‹è¼‰å…¥çš„話ä¸å°±æ„味這我終究得一個一個 importï¼ï¼Ÿéœæ…‹ç¶²é æœ€çµ‚è¦è¼‰å…¥é‚£äº› SVG Components 這在打包時就已經確定,難é“ä¸èƒ½åœ¨é€™æ™‚候將這些è¦ç”¨åˆ°çš„æ‰“包在一起嗎?

解決辦法就在 Webpack 動態載入的 Magic Comments 裡é¢ã€‚

Magic Comments(inline commentsï¼Œå…§è¯æ³¨é‡‹ï¼‰

我其實比較å好 inline comments 這個詞,所以接下來會盡é‡çµ±ä¸€ä½¿ç”¨ä»–

在上é¢å‡ºç¾çš„ /* webpackChunkName: "svg-skills" */ 就是 inline comments 之一,以下是在 webpack 官網上關於 inline comments 的範例,並接者來說明æ¯ä¸€å€‹ inline comments 的功能與使用時機。

// Single target
// éœæ…‹è·¯å¾‘
import(
  /* webpackChunkName: "my-chunk-name" */
  /* webpackMode: "lazy" */
  /* webpackExports: ["default", "named"] */
  'module'
);

// Multiple possible targets
// 動態路徑
import(
  /* webpackInclude: /\.json$/ */
  /* webpackExclude: /\.noimport\.json$/ */
  /* webpackChunkName: "my-chunk-name" */
  /* webpackMode: "lazy" */
  /* webpackPrefetch: true */
  /* webpackPreload: true */
  `./locale/${language}`
);

webpackChunkName

webpackChunkName å¯ä»¥ç‚ºå‹•態載入的模塊(chunk)指定å稱,例如:

import(
  /* webpackChunkName: "svg-skills" */
  '~/assets/images/skills/gridsome.svg?inline'
)

這樣打包後的檔案å稱會是 svg-skills.hash.jsï¼Œæ˜¯å¦æœ‰ hash ä¾ç…§ webpack 設定會有ä¸åŒã€‚

除此之外,webpackChunkName 在æ­é…動態路徑時æä¾›äº†å…©å€‹ placeholders:[index] åŠ [request],分別表示載入順åºçš„編號,以åŠä¸€å€‹è¢«è§£æžå‡ºä¾†çš„æª”案å稱。以å‰è¨€éƒ¨åˆ†æåˆ°çš„第一個狀æ³ä¾†èªªï¼Œå°±å¯ä»¥ä½¿ç”¨ [request] 讓æ¯å€‹å‹•態載入的模塊有更明確的命å

import { Vue, Component } from 'vue-property-decorator'
import { skills } from '~/content/site'

@Component<TheSkill>({
  beforeCreate () {
    for(let i = 0; i < skills.length; i++) {
      const skill = skills[i].svg
      this.$options.components![skill] = () => import(
        /* webpackChunkName: "svg-skills-[request]" */
        `~/assets/images/skills/${skill}.svg?inline`
      )
    }
  }
})
export default class TheSkill extends Vue {
  private skills = Object.freeze(skills)
}

打包éŽå¾Œå°±æœƒç”¢ç”Ÿä¸‹åˆ—這樣的檔案å稱

svg-skills-nuxtjs-svg.hash.js
svg-skills-gridsome-svg.hash.js

這樣就解決了第一個 檔案命åå•題。這裡å¯ä»¥é€™æ¨£åšæ˜¯å› ç‚ºä¸Šé¢ ~/assets/images/skills/${skill}.svg?inline 是一個動態路徑,å¯å¦‚æžœæ˜¯éœæ…‹è·¯å¾‘的話則會輸出 svg-skills-[request].hash.js 這樣的模塊。

// æœ€å¾Œæœƒæ‰“åŒ…æˆ svg-skills-[request].hash.js
import(
  /*webpackChunkName: "svg-skills-[request]"*/
  '~/assets/images/skills/gridsome.svg?inline'
)

å¦å¤–å¦‚æžœæ˜¯å¤šå€‹éœæ…‹è·¯å¾‘但如果用了相åŒçš„ webpackChunkName 則會全部打包æˆåŒä¸€å€‹æ¨¡å¡Šï¼Œæœ‰ä¸€é»žé»žé¡žä¼¼ä¸‹ä¸€å€‹è¦æåˆ°çš„ webpackMode: "lazy-once" 的效果。

webpackChunkName 應該是相å°å»£ç‚ºäººçŸ¥çš„ inline comments,在 Vue CLI 中é è¨­çš„ router 檔案裡é¢å°±å¯ä»¥è¦‹åˆ°ä»–的身影。

webpackMode

webpackMode ç”¨æ–¼è¨­å®šæ¨¡å¡Šè¢«ä»¥ä»€éº¼å½¢å¼æ‰“åŒ…ï¼Œç›®å‰æœ‰å››ç¨®é¸é …:

  • lazy(é è¨­ï¼‰
    æ¯ä¸€å€‹ä¸åŒçš„ import() 都會打包出一個動態載入的模塊。而 webpackChunkName 的兩個 placeholders ä¹Ÿåªæœ‰åœ¨å‹•態路徑並且 webpackMode 設定為 lazy æ¨¡å¼æ™‚有æ„義。
  • lazy-once
    所有 import() æ­é…å‹•æ…‹è·¯å¾‘åœ¨æ‰“åŒ…å¾Œåªæœƒç”¢ç”Ÿä¸€å€‹å‹•態載入的模塊。
    import { Vue, Component } from 'vue-property-decorator'
    import { skills } from '~/content/site'
    
    @Component<TheSkill>({
      beforeCreate () {
        for(let i = 0; i < skills.length; i++) {
          const skill = skills[i].svg
          this.$options.components![skill] = () => import(
            /* webpackChunkName: "svg-skills" */
            /* webpackMode: "lazy-once" */
            `~/assets/images/skills/${skill}.svg?inline`
          )
        }
      }
    })
    export default class TheSkill extends Vue {
      private skills = Object.freeze(skills)
    }
    

    åƒé€™æ¨£æ‰“包éŽå¾Œç”¢ç”Ÿçš„æ¨¡å¡Šå¦‚下:
    svg-skills.hash.js
    

    體醒一下,lazy-once 模å¼åªæœ‰åœ¨åƒé€™æ¨£çš„動態路徑 import(`~/assets/images/skills/${skill}.svg?inline`) æ‰æœƒæœ‰å¯¦éš›æ„ç¾©ã€‚å¦‚æžœç”¨åœ¨ç´”éœæ…‹è·¯å¾‘則會在打包期間收到警告。
  • eager
    import { Vue, Component } from 'vue-property-decorator'
    import { skills } from '~/content/site'
    
    @Component<TheSkill>({
      beforeCreate () {
        for(let i = 0; i < skills.length; i++) {
          const skill = skills[i].svg
          this.$options.components![skill] = () => import(
            /* webpackMode: "eager" */
            `~/assets/images/skills/${skill}.svg?inline`
          )
        }
      }
    })
    export default class TheSkill extends Vue {
      private skills = Object.freeze(skills)
    }
    

    這個模å¼ä¸æœƒç”¢ç”Ÿä»»ä½•的模塊,而是直接打包到上層模塊裡é¢ï¼Œä¹Ÿå› æ­¤å‘¼å«å‹•態載入 import() çš„æ™‚å€™ä¸æœƒç”¢ç”Ÿé¡å¤–的網路請求,而是以 Promise.resolve() 的方å¼å›žå‚³ã€‚é¡žä¼¼éœæ…‹è¼‰å…¥ï¼Œä½†ä¸åŒçš„æ˜¯è©²æ¨¡å¡Šçš„內容è¦ç›´åˆ°å‘¼å«äº† import() ä¹‹å¾Œæ‰æœƒåŸ·è¡Œã€‚
    這就å¯ä»¥è§£æ±ºå‰è¨€æåˆ°çš„ 動態載入å•題,因為網路請求變少了,原本è¦ç­‰ä¸ƒå…«å€‹è«‹æ±‚回來,先在完全ä¸ç”¨ç™¼å‡ºè«‹æ±‚,能更快完æˆåˆå§‹åŒ–,ç€è¦½å™¨ä¹Ÿèƒ½æ›´å¿«å®Œæˆæœ€å¤§å…§å®¹ç¹ªè£½ã€‚
  • weak
    根據官方文件的說法,他會嘗試請求該模塊,如果該模塊已經在其他部分å–å¾—éŽï¼Œé‚£é€™è£¡å°±æœƒä»¥ Promise.resolve() 回應,但如果沒有被å–å¾—éŽï¼Œå‰‡æœƒæ‹‹å‡ºä¸€æ®µéŒ¯èª¤è¨Šæ¯ã€‚該模å¼å¯ä»¥æ‡‰ç”¨åœ¨ universal rendering(Server-Side Rendering)上。
    å¦ç™½èªªï¼Œç›®å‰æ²’有在任何專案中有用到這個模å¼ï¼Œæ‰€ä»¥ä¹Ÿåªå°±æ•´èƒ½å­—é¢è§£é‡‹ï¼Œå¦‚æžœæœ‰å¤§å¤§å€‘å° /* webpackMode: "weak" */ 有研究的,也歡迎分享一下使用的經驗囉ï¼

webpackPrefetch

webpackPrefetch 接å—一 Boolean 或 Number,他å¯ä»¥è®“ webpack è¼¸å‡ºè³‡æºæç¤ºï¼ˆResource Hint)。例如:

import { Vue, Component } from 'nuxt-property-decorator'

@Component<LoginPage>({})
export default class LoginPage extends Vue {
  // ç•¥
  onLogin() {
    import(
      /* webpackChunkName: "recaptcha-v3" */
      /* webpackPrefetch: true */
      'recaptcha-v3'
    )
      .then(module => {
        // ç•¥
      })
  }
}

這是一個登入é é¢çš„ Vue Component,登入時需è¦é€éŽ reCAPTCHA v3 驗證,並且希望能等到使用者按下登入時æ‰åŽ»å‹•æ…‹è¼‰å…¥ recaptcha-v3 使用。加上 /* webpackPrefetch: true */ 後,在é é¢è¼‰å…¥æ™‚會在 head 裡é¢åŠ ä¸Šè³‡æºæç¤ºã€‚

åƒé€™æ¨£ï¼š

<!-- ç•¥ -->
<link rel="prefetch" as="script" href="/_nuxt/vendorsr.recaptcha-v3.js">

æœ‰äº†è³‡æºæç¤ºï¼Œç€è¦½å™¨å°±æœƒåœ¨æœ‰ç©ºçš„æ™‚候去é å–é€™äº›æª”æ¡ˆï¼Œç­‰çœŸæ­£éœ€è¦æ™‚就能更快載入資æºä¾†ä½¿ç”¨ã€‚

webpackPrefetch 除了 Boolean 之外,後é¢å¦‚果填入 Number 則表示優先順åºã€‚

例如:

// åªæ˜¯ç¯„例,先別管åˆä¸åˆç†
async mounted() {
  const { default: moment } = await import(
    /* webpackPrefetch: 1 */
    /* webpackChunkName: "moment" */
    'moment'
  )
  const { default: _ } = await import(
    /* webpackPrefetch: 3 */
    /* webpackChunkName: "lodash" */
    'lodash'
  )
  const { default: $ } = await import(
    /* webpackPrefetch: 2 */
    /* webpackChunkName: "jquery" */
    'jquery'
  )
}

打包執行後會åƒé€™æ¨£ï¼š

<link rel="prefetch" as="script" href="/js/lodash.js">
<link rel="prefetch" as="script" href="/js/jquery.js">
<link rel="prefetch" as="script" href="/js/moment.js">

è¼¸å‡ºçš„çµæžœæœƒä¾ç…§çµ¦å®šçš„æ•¸å­—ç”±å¤§åˆ°å°æŽ’åºï¼Œä½†å¦‚果這裡的 webpackPrefetch 都為 true ï¼ˆæˆ–éƒ½ç‚ºç›¸åŒæ•¸å­—的話)的話,那順åºå°±æœƒæ˜¯ jqueryã€lodashã€moment。實際上 true å¯ä»¥ç•¶ä½œæ˜¯é †åº 0ï¼Œå¦‚æžœæœ‰å‡ºç¾ 999 è·Ÿ -999 的話,輸出則會是 999ã€trueã€-999ï¼Œé€™å…¶å¯¦å°±åƒæ˜¯ css çš„ z-index 那樣。

å¦å¤–,填入 false æˆ–æ˜¯å­—ä¸²çš„è©±åŸ·è¡Œæ™‚å°±ä¸æœƒç”¢ç”Ÿè³‡æºæç¤ºã€‚

在 Vue CLI 中é è¨­æœƒå°‡é é¢ä¸­æ‰€æœ‰å‹•態載入的 Componentsã€æ¨¡å¡Š...ç­‰å…¨éƒ½åŠ ä¸Šè³‡æºæç¤ºã€‚å¦‚æžœå°ˆæ¡ˆæ¯”è¼ƒé¾å¤§ï¼Œå®˜æ–¹ä¹Ÿå»ºè­°å¾ž vue.config.js 中將該é å–功能關閉,並用 webpackPrefetch 這個 inline comments 手動é¸é¸æ“‡é‚£äº›éƒ¨åˆ†éœ€è¦åŠ ä¸Šè³‡æºæç¤ºã€‚

webpackPreload

webpackPreload 使用方å¼å¤§è‡´è·Ÿ webpackPrefetch ç„¡ç•°ã€‚ä¸€æ¨£æœƒç”¢ç”Ÿè³‡æºæç¤ºï¼Œè€Œé€™è£¡ç”¢çš„æç¤ºç‚º <link rel="preload" ...>。

ä¸éŽé—œæ–¼ <link rel="preload" ...> è·Ÿ <link rel="prefetch" ...> 是有一些差異的:

  • preload 資æºä¸‹è¼‰çš„æ™‚間點與上層並行,但 prefetch æœƒç­‰ä¸Šå±¤ä¸‹è¼‰å®Œç•¢å¾Œæ‰æœƒé–‹å§‹ã€‚
  • preload 的優優先權為中等(Medium/High),會立刻下載,但 prefetch è¦ç­‰ç€è¦½å™¨æœ‰ç©ºé–’æ‰æœƒé å–。
  • preload çš„è³‡æºæ‡‰è©²ç›¡å¿«ä½¿ç”¨ï¼Œè€Œ prefetch 的資æºå‰‡è¡¨ç¤ºæœªä¾†æœ‰å¯èƒ½æœƒç”¨åˆ°ã€‚

preload ç›¸å°æ¯”較耗資æºï¼Œæ‰€ä»¥è¦è¬¹æ…Žä½¿ç”¨ã€‚

比較一下兩者在 Chrome Devtools çš„ Network 颿¿è£¡é¢çš„差異å§ï¼ï¼ˆé †åºè·Ÿä¸Šé¢ç¯„例無關)

使用 webpackPrefetch 時 Chrome Devtools çš„ Network 颿¿

使用 webpackPrefetch 時 Chrome Devtools çš„ Network 颿¿

使用 webpackPreload 時 Chrome Devtools çš„ Network 颿¿

使用 webpackPreload 時 Chrome Devtools çš„ Network 颿¿

webpackInclude

webpackInclude å¯ä»¥æŽ¥ä¸€å€‹æ­£è¦è¡¨é”å¼ï¼Œç”¨ä¾†éŽæ¿¾æŽ‰å‹•態路徑中ä¸ç¬¦åˆè©²æ­£è¦è¡¨é”試的檔案。

舉個方便說明的範例

import { Vue, Component } from 'vue-property-decorator'
import { skills } from '~/content/site'

@Component<TheSkill>({
  beforeCreate () {
    for(let i = 0; i < skills.length; i++) {
      const skill = skills[i].svg
      const fileName = `skill-${skill}.svg?inline`
      this.$options.components![skill] = () => import(
        /* webpackChunkName: "skill-svg" */
        /* webpackMode: "lazy-once" */
        `~/assets/images/svg/${fileName}`
      )
    }
  }
})
export default class TheSkill extends Vue {
  private skills = Object.freeze(skills)
}

ç¾åœ¨å°Žå…¥çš„動態路徑為 ~/assets/images/svg/${fileName},這表示在所有 ~/assets/images/svg/ 之下的檔案都會被打包起來,裡é¢å¯èƒ½é‚„嫿œ‰å…¶ä»–çš„ SVG,例如 facebook.svgã€twitter.svg ... 之類的。ä¸éŽå¥½åœ¨ä¸€é–‹å§‹æˆ‘有將 skill 相關的 SVG 開頭都以「skill-ã€å€éš”開來,這時我就å¯ä»¥åŠ ä¸Š /* webpackInclude: /^skill-/ */ é€™æ¨£æ‰“åŒ…å‡ºä¾†çš„æ¨¡å¡Šå°±ä¸æœƒå«æœ‰ä¸æ˜¯ä»¥ã€Œskill-ã€ç‚ºé–‹é ­çš„當案了ï¼

å‰é¢æœƒåŒ…嫿‰€æœ‰æª”案的原因是因為 webpack 沒辦法é çŸ¥é‚£äº›æ˜¯ä½¿ç”¨è€…真正需è¦çš„ï¼Œå› æ­¤ä»–åªæœƒä¾ç…§ä»–能分æžçš„æœ€å¤§é™åº¦åŽ»æ‰“åŒ…ã€‚ä»¥é€™å€‹ç¯„ä¾‹ä¾†èªªï¼Œä»–æœ€å¤šå¯ä»¥è¾¨è­˜åˆ° ~/assets/images/svg/ 這個路徑下的檔案,如果沒有æä¾› webpackInclude 或 webpackExclude 幫助篩é¸ï¼Œé‚£ä»–就會把這些全部包起來。

但其實這個範例路徑åªè¦è®Šæˆ ~/assets/images/svg/skill-${skill}.svg?inline, webpack 就能知é“ä»–éœ€è¦æ‰“包的是 skill- 開頭,且副檔å為 svg çš„æª”æ¡ˆäº†ã€‚ï¼ˆæ‰€ä»¥èªªé€™åªæ˜¯æ–¹ä¾¿èªªæ˜Žçš„範例)

webpackExclude

webpackExclude 一樣接收一個正è¦è¡¨é”å¼ï¼Œä½†é€™è£¡çš„çš„è¡Œç‚ºæ˜¯ï¼šå¦‚æžœç¬¦åˆæ­£è¦è¡¨é”å¼ï¼Œå°±æŽ’除這個檔案。例如å¯ä»¥æŽ’é™¤æŽ‰æ‰€æœ‰ä¸æ˜¯ svg çš„æª”æ¡ˆï¼Œæˆ–æ˜¯æ‰€æœ‰ä¸æ˜¯ JSON æ ¼å¼çš„æª”案。webpackExclude è·Ÿ webpackInclude 兩者在專案中å¯ä¾ç…§è‡ªå·±çš„需求æ­é…使用。

webpackExports

這個部分是 webpack 5 æŽ¨å‡ºçš„æ–°åŠŸèƒ½ï¼Œåœ¨ç›®å‰ webpack 4 的環境中沒有辦法實際驗證。所以以下內容是看完 webpack çš„ releases 後的ç†è§£ï¼Œå°šæœªå¯¦æ¸¬ã€‚

webpackExports 是 webpack v5.0.0-beta.18 後的新功能,按照 releases 裡é¢çš„說明它å¯ä»¥æ›´ç²¾æº–的切分程å¼ç¢¼ã€‚例如我åªéœ€è¦æ•´ä»½å‹•態載入的檔案的æŸå€‹ export,例如å«åš theme 但ï¼ä¸€èˆ¬æƒ…形下會整包阿哩阿雜的都被打包起來,這時就å¯ä»¥åˆ©ç”¨ /* webpackExports: "default" */ 或 /* webpackExports: ["default", "theme"] */ 等方å¼å°‡ä¸éœ€è¦çš„部分切分開來。

補充

  1. /* webpackIgnore: true */ 表示ç¦ç”¨è©²å‹•æ…‹è¼‰å…¥ï¼Œæˆ‘ç›®å‰æš«æ™‚想ä¸åˆ°æœ‰ä»€éº¼éœ€æ±‚會使用到他。
  2. å‹•æ…‹è¼‰å…¥ç„¡æ³•ä½¿ç”¨æ–¼å®Œå…¨å‹•æ…‹çš„è·¯å¾‘ï¼Œåƒæ˜¯ï%Ú༚import(path),因為這表示檔案å¯èƒ½ä¾†è‡ªæ•´åŒ…å°ˆæ¡ˆçš„ä»»ä½•è·¯å¾‘ï¼Œå¾ˆå¯æ€•。
  3. 上é¢çš„範例都是這樣寫:
import(
  /* webpackChunkName: "svg-skills" */
  /* webpackMode: "lazy-once" */
  `~/assets/images/skills/${skill}.svg?inline`
)

但其實也å¯ä»¥é€™æ¨£å¯«ï¼š

import(
  /*
    webpackChunkName: "svg-skills",
    webpackMode: "lazy-once"
  */
  `~/assets/images/skills/${skill}.svg?inline`
)

效果一樣,就看自己或團隊比較å好哪一種風格了。

çµèªž

在å‰è¨€ä¸­æåˆ°çš„ PageSpeed Insights 的最大內容繪製效能議題,最後在 Skill å€å¡Šå‹•態載入的 svg components 部分加上 /* webpackMode: "eager" */ 後ç²å¾—了éžå¸¸æ˜Žé¡¯çš„æ”¹å–„。最大內容繪製時間從 1.9s 下é™åˆ°ç´„ 1.2s,總分從 92 上å‡åˆ° 97 å·¦å³ï¼Œä¸¦ä¸”未來如果需è¦èª¿æ•´æŠ€èƒ½æ¸…單時,åªè¦ä¿®æ”¹æ¸…單內容,新增或刪除 svg 圖檔,就ä¸éœ€è¦ä¿®æ”¹åˆ°é€™ä¸€ä»½ components 內容了。

在éŽåŽ»åœ¨å°ˆæ¡ˆä¸­ï¼Œé™¤äº† webpackChunkName 外其他 Comments 還真的沒有用éŽã€‚ä½†åƒæ˜¯ webpackPrefetch 這個 Comments å°±éžå¸¸æœ‰ç”¨ã€‚åƒæ˜¯ Nuxt.js é è¨­ä¸¦ä¸æœƒå°å‹•態載入的模塊加上 prefetch çš„è³‡æºæç¤ºï¼Œé©ç•¶çš„在動態導入的模塊加上 webpackPrefetch,多少能為使用者體驗帶來一些改進。

åƒè€ƒè³‡æ–™

è«‹æˆ‘å–æ¯å’–å•¡

如果這裡的內容有幫助到你的話,一æ¯å’–å•¡å°±æ˜¯å°æˆ‘最大的鼓勵。

è«‹æˆ‘å–æ¯å’–å•¡
ÿÿÿÿ