Next.js compiler

Next.js Compiler 介紹

Next.js compiler 是讓 Next.js 效能強大的重要原因之一。在了解 Next.js compiler 之前,先來談談從開發環境到部署的四個重要流程,了解這些流程才能夠知道 Next.js compiler 做了哪些事情。

1. 從開發環境到部署

當我們在開發環境寫完 code 後,還需要將 code 經過一些轉換,最後才將程式碼部署到目標機器上。這些步驟分別為:compile, bundle, minify, code split

1.1 Compile – 編譯

通常在開發環境中,我們不太會直接寫原生的 JavaScript 或是 CSS,以 React 為例,我們會在 component 裡面使用 JSX 的語法,甚至使用 TypeScript 開發來加強 application 的維護性。然而瀏覽器永遠只認識三種程式語言:HTML, CSS, JavaScript,也因此從開發環境到部署的第一個階段,就是要將各式各樣瀏覽器不支援的語法,轉換成瀏覽器看得懂的 code,這個過程稱作 compile。以下舉 JSX 為例:

Next.js compiler
圖片來源:Next.js 官網

1.2 Minify – 極簡化

在開發環境中,寫 code 的原則是基於易讀性及可維護性,然而在真實環境中,某些 code 可能不是那麼必要,ex: 空白、換行、縮排、註解等等。

所謂的 minify 是指在不影響 code 的功能下,將 code 極簡化:

Next.js compiler
圖片來源:Next.js 官網

經過 minify 後的 code 有以下優點:

  • response 的時間減少
  • 傳輸的資料減少,因此可以減少頻寬
  • Code 變得不易讀,增加安全性(不過還是可以透過某些工具把 code 的結構逆推回來)

1.3 Bundle – 打包

在開發環境中,我們可能會將 code 分成 module, components, functions 等等,需要的時候再 import 進檔案以增加可維護性。然而這樣的架構也會增加 code 之間的耦合性

所謂的 bundle 是透過某些工具,減少整個 application 的檔案數量以及 code 的相依性:

Next.js compiler
圖片來源:Webpack 官網

在 web application 中,bundle 最重要的目的是減少 user 造訪網頁時發的 request 數量。

1.4 Code Split

隨著 application 的功能增加,被 bundle 後的檔案也會越來越大,舉例來說,假設是用 Next.js 或是 React.js 來開發 application,隨著 components 越來越多,被 bundle 過後的檔案也會越來越大,如果 user 每次拜訪網站都要等整包被 bundle 過的檔案傳到前端後再執行,就會就會把網站的載入時間給拉長。Code split 這個過程就是將你的 code 給分割,user 造訪某個頁面時,只會載入該頁面用得到的 code,而不是整個網站的 code,藉此降低載入該頁面的時間:

2. Next.js Compiler

Next.js 在 12 版以後引進了全新的 compiler,該 compiler 基本上就是負責處理以上 4 個過程的工具。要注意的是,如果你的專案已經存在 .babelrc 或是 babel.config.js 的話,Next.js 會預先使用 Babel 作為 compiler。

2.1 特性

  • 用 Rust 編寫,以及用 SWC 開發而成
  • Compile 速度比 Babel 快 17 倍
  • Minify 速度比 terser 快 7 倍
  • Refresh 速度比未導入前快了將近 3 倍
  • Build 的速度比未導入前快了將近 5 倍

2.2 主要功能

2.2.1 Styled Components

導入 babel 的 babel-plugin-styled-components,相關設定可以直接參考 babel 套件的 document

// next.config.js
module.exports = {
  compiler: {
    // see https://styled-components.com/docs/tooling#babel-plugin for more info on the options.
    styledComponents: boolean | {
      // Enabled by default in development, disabled in production to reduce file size,
      // setting this will override the default for all environments.
      displayName?: boolean,
      // Enabled by default.
      ssr?: boolean,
      // Enabled by default.
      fileName?: boolean,
      // Empty by default.
      topLevelImportPaths?: string[],
      // Defaults to ["index"].
      meaninglessFileNames?: string[],
      // Enabled by default.
      cssProp?: boolean,
      // Empty by default.
      namespace?: string,
      // Not supported yet.
      minify?: boolean,
      // Not supported yet.
      transpileTemplateLiterals?: boolean,
      // Not supported yet.
      pure?: boolean,
    },
  },
}

2.2.2 Jest

Next.js 12 版以後,內建支援 jest,可以參考官方文件

// jest.config.js
const nextJest = require('next/jest')

const createJestConfig = nextJest({
  // Provide the path to your Next.js app to load next.config.js and .env files in your test environment
  dir: './',
})

// Add any custom config to be passed to Jest
/** @type {import('jest').Config} */
const customJestConfig = {
  // Add more setup options before each test is run
  // setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
  // if using TypeScript with a baseUrl set to the root directory then you need the below for alias' to work
  moduleDirectories: ['node_modules', '<rootDir>/'],
  testEnvironment: 'jest-environment-jsdom',
}

// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async
module.exports = createJestConfig(customJestConfig)

2.2.3 Remove React Properties

用來移除特定的 JSX properties,常用在移除測試用的 properties,ex: Cypress 的官方文件建議使用 data-* 來幫助 E2E 的測試。如果不希望將這些測試用的 properties 上到 production,就可以在 compile 時將這些 properties 移除。

如果將 reactRemoveProperties 設成 true 的話,預設會將 ^data-test 的 properties 全部移除:

// next.config.js
module.exports = {
  compiler: {
    reactRemoveProperties: true,
  },
}

如果希望移除特定格式的 properties,可以直接傳 regular expression:

// next.config.js
module.exports = {
  compiler: {
    // The regexes defined here are processed in Rust so the syntax is different from
    // JavaScript `RegExp`s. See https://docs.rs/regex.
    reactRemoveProperties: { properties: ['^data-custom$'] },
  },
}

2.2.4 Remove Console

類似 babel 的 babel-plugin-transform-remove-console,將 removeConsole 設成 true 的話,預設會將 console.* 全部移除:

// next.config.js
module.exports = {
  compiler: {
    removeConsole: true,
  },
}

如果希望排除某種 console 的話,可以這樣改寫 config:

// next.config.js
module.exports = {
  compiler: {
    removeConsole: {
      exclude: ['error'],
    },
  },
}

2.2.5 Minification

用來 Minify code,比 Terser 快上 7 倍。

// next.config.js

module.exports = {
  swcMinify: true,
}

參考資料

極簡化- 维基百科,自由的百科全書
[鐵人賽 Day05] React 中的 Code splitting(代碼分離)方法
Code splitting – MDN Web Docs Glossary
Why you should use code splitting with higher order components
How Next.js Works | Learn Next.js
Advanced Features: Next.js Compiler

如果覺得我的文章有幫助的話,歡迎幫我的粉專按讚哦~謝謝你!

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top