Back返回部落格

Next.js 5:通用 Webpack、CSS 導入、插件與區域功能

Next.js 5 著重於提升大型應用的擴展性、組合性與效能表現

我們非常高興向全球開發者推出 Next.js 5.0 版本,該版本已立即在 npm 上提供。升級方式如下:

終端機
npm i next@latest react@latest react-dom@latest

除了升級 Next.js 外,我們也同時更新了 peer dependencies 的 reactreact-dom

Next.js 是一個用於建構通用型、伺服器渲染(或靜態預渲染)React.js 應用的工具包。無論應用規模大小,只需執行 next 即可開始開發。(了解更多

在每個新版本中,我們都致力於保持向後兼容性,提供簡單的升級路徑,並僅在絕對必要時才進行 API 變更。Next.js 5.0 也不例外。

然而在底層,Next.js 經歷了根本性的變革,以實現強大的新用例和擴展性。我們首先讓 Next.js 共享相同的 Webpack 管線來處理伺服器端與客戶端程式碼。

通用 Webpack 與 Next 插件

Next.js 整合了 Webpack、Babel 和 Uglify 等強大工具,並為終端使用者提供極簡介面:next(開發用)、next build(生產環境準備)、next start(啟動服務)或 next export(預渲染為靜態檔案)。

我們早期的一個重要決策是為這些工具的配置提供高度可擴展的接入點。我們不僅追求易用性,更希望讓您能靈活擴展工具鏈。

例如,您可以透過在 next.config.js 中設置 webpack 屬性來擴展 Next.js 的 webpack 配置

由於 webpack 在生產與開發環境下的執行方式不同,我們當時決定將其設計為函數形式來修飾預設配置:

next.config.js
module.exports = {
  webpack(config, { dev }) {
    // 修改配置
    return config;
  },
};

這是可選的 next.config.js 檔案範例

然而,Webpack 過去僅在客戶端(瀏覽器)套件中執行,您無法在伺服器渲染時使用這套強大工具鏈。

我們很高興宣布,我們已全面重構程式碼庫,實現了 Webpack 的通用化執行

從使用者角度來看,唯一變化是上述修飾函數會多接收一個 isServer 屬性。但這意味著您現在可以使用豐富的 Webpack loader 生態系統。

CSS、LESS、SASS、SCSS 與 CSS 模組

我們最常被要求的功能之一就是支援 CSS 檔案導入與 Webpack loader 的使用:

import './index.css';
 
export default function Index() {
  return (
    <div>
      <p>我愛 CSS!</p>
    </div>
  );
}

透過通用 Webpack 實現 CSS 導入的頁面範例(pages/index.js

要啟用此功能,您需要將所需 loader 安裝為 peer dependencies:

終端機
npm i --save css-loader style-loader postcss-loader

Next.js 讓您可以自由選擇所需 loader 並隨時升級至不同版本

然後在 next.config.js 中擴展配置來設定您的 loader:

next.config.js
module.exports = {
  webpack(config, options) {
    const { dev, isServer } = options;
    const extractCSSPlugin = new ExtractTextPlugin({
      filename: 'static/style.css',
      disable: dev,
    });
    config.module.rules.push({
      test: /\\.css$/,
      use: cssLoaderConfig(extractCSSPlugin, {
        cssModules,
        dev,
        isServer,
      }),
    });
    return config;
  },
};

直接擴展 webpack 配置能提供極大的靈活性與控制權

雖然我們一般推薦使用元件局部樣式解決方案(如內建的 styled-jsx babel 插件),但我們認為 CSS loader 有許多重要優勢,例如易於重用現有 CSS 程式碼庫,並大幅簡化舊程式碼遷移至 Next.js 的過程。

與其預設啟用所有可能的功能和 loader,我們推出了 Next.js 插件,這些是_用來修飾配置的簡單函數_。無需像上面那樣手動擴展配置,您只需:

const withCss = require('next-css');
module.exports = withCss({
  /* 額外的可選配置 */
});

只需引入 next-css 即可啟用 .css 檔案導入功能

了解更多關於 CSS Loader 在 Next.JS 中的使用方式,或參考我們已為您準備好的套件:

Loader套件
CSSnext-css
LESSnext-less
SASSnext-sass

我們的目標是讓社群能夠發展出實用簡單的擴展生態系統。為此,我們開放 next-plugins monorepo 供 Next.js 社群共同維護。歡迎所有 PR 貢獻!

TypeScript 支援

TypeScript 是 JavaScript 生態系中成長最快的技術之一,甚至已獲得 Babel 7 官方支援,這意味著只需自訂 .babelrc 就能在 Next.js 中自然支援。

同時,得益於新的通用 Webpack 支援,您現在就能獲得完整的 TypeScript 支援!

您可以這樣擴展 webpack 配置:

next.config.js
module.exports = {
  webpack(config, options) {
    const { dir, defaultLoaders } = options;
    config.resolve.extensions.push('.ts', '.tsx');
    config.module.rules.push({
      test: /\\.+(ts|tsx)$/,
      include: [dir],
      exclude: /node_modules/,
      use: [
        defaultLoaders.babel,
        { loader: 'ts-loader', options: { transpileOnly: true } },
      ],
    });
    return config;
  },
};

我們只需啟用 ts-loader 即可

與 CSS loader 和預處理器一樣,TypeScript 也是最常被要求的功能之一。為了讓它像其他 loader 一樣容易整合到專案中,我們現在提供了一個 next-typescript 插件

next.config.js
const withTs = require('next-typescript');
module.exports = withTs({
  /* 額外配置 */
});

插件可以輕鬆組合:它們只是函數

更好的 React 替代庫與模組覆寫支援

隨著時間推移,出現了許多 React 的替代實現方案。其中值得注意的有 preactnervjsinferno

其他庫則專注於替換 DOM 渲染器,如 react-dom-lite,它透過在瀏覽器兼容性上做些微妥協來實現更小的 React 構建。

通用 Webpack 支援讓將這些庫作為替代方案整合變得更加容易。與其他插件類似,要在 Next.js 中使用 preact 只需:

終端機
npm i @zeit/next-preact preact preact-compat

我們安裝 preact 插件與必要的 peer dependencies

const withPreact = require('@zeit/next-preact');
module.exports = withPreact();

我們新的 next.config.js 已準備好使用 preact

查看非常簡單的 @zeit/next-preact 模組或創建您自己的插件!

可選的生產環境外部 Sourcemap

現在 Next.js 同時使用 webpack 處理客戶端和伺服器端程式碼,在生產環境建置中啟用 source-map 只需對其配置進行小幅調整。

在開發環境中 source map 會自動啟用,因此我們針對生產環境進行了不同的配置:

next.config.js
module.exports = {
  webpack(config, { dev }) {
    if (!dev) {
      config.devtool = 'source-map';
    }
    return config;
  },
};

我們只需在非開發環境時對 devtool 選項進行不同的配置

區域 (Zones)

Next.js 從一開始就明確目標要回歸並保持網路的簡潔性。

伺服器渲染 (Server-rendering)、簡單且無關框架的資料獲取方式,以及基於檔案系統結構的宣告式頁面,都是我們依循此理念推出的功能。

網路服務和網站經常被忽視的一個特性是它們_天然的組合性與可擴展性_。

例如,mydomain.com/settingsmydomain.com/ 可以是兩個完全獨立的應用程式,各自獨立部署、獨立擴展,甚至運行相同軟體的不同版本。

要將它們「黏合」成終端用戶的統一體驗,只需對後端路由層或向外界暴露的負載平衡器進行簡單配置。我們很高興現在能提供組合多個使用 Next.js 建置的應用程式的能力,透過普通的 <Link> 元件相互連接。我們稱此功能為區域 (Zones)

舉例來說,考慮這兩個部署在 Vercel 上的獨立 Next.js 應用程式:

我們的兩個頁面擁有無縫體驗,但它們屬於不同的應用程式

我們的兩個頁面擁有無縫體驗,但它們屬於不同的應用程式

當我們改版文件時,希望讓社群貢獻的接納過程盡可能簡單。

我們決定將文件「迷你網站」拆分到自己的儲存庫。此外,每當有拉取請求提交並提出變更時,我們會自動將其隔離部署:

每當 PR 內發生變更時,我們的機器人會自動部署它

每當 PR 內發生變更時,我們的機器人會自動部署它

最終我們得到_兩個區域_,透過我們的路徑別名功能整合到父網域 https://vercel.com 下。配置大致如下:

{
  "rules": [
    { "pathname": "/docs", "dest": "our-docs.vercel.app" },
    { "pathname": "/api", "dest": "our-docs.vercel.app" },
    { "dest": "my-main-website.vercel.app" }
  ]
}

這些簡單規則讓你可以組合微服務和區域

剩下的只需執行 now alias 指令:

Terminal
now alias -r rules.json my-domain.com

我們的使命是讓部署盡可能普及和開放。為了輔助本地開發,我們最近開源了 micro-proxy,這是一個與上述配置格式相容的工具。

你也可以使用其他解決方案如 Nginx、HAProxy 或 API Gateway 來連接區域。

更快的生產環境建置時間

我們認為開發者體驗與用戶體驗相輔相成。變更的撰寫、測試和部署效率越高,新功能添加速度就越快,錯誤修復也更迅速,整體用戶體驗自然提升。

因此,我們持續專注於迭代系統最基本構建塊的性能表現。

在 Next.js 5.0 中,我們有機會重新審視 next build 指令,這是你在部署到生產環境或將 Next.js 應用程式匯出為靜態網站前執行的指令。

我們很高興地宣布,對於由數千個元件組成的 React 應用 vercel.com,Next.js 5.0 帶來了相當顯著的改進,生產環境建置時間減少了 23.6%

我們的主要應用程式生產建置現在減少了 38 秒完成時間

我們的主要應用程式生產建置現在減少了 38 秒完成時間

改進的動態導入快取

當你使用動態 import() 時,這會向 WebPack 發出信號,表示存在新的程式碼分割入口點。

在建置時,這意味著為對應的模組子樹生成特定 bundle。

在 Next.js 5.0 之前,動態 bundle 會獲得類似以下的 URL:

/_next/1517592683901/webpack/chunks/components_hello1_1345d10fc951cd6717c5676c467579a6.js

現在,我們將動態導入轉換為子樹內容的內容尋址雜湊:

/_next/webpack/chunks/components_hello1_1345d10fc951cd6717c5676c467579a6-b7874680a9e21fb6eb89.js

這意味著跨部署時,你的用戶和客戶無需無謂地重新下載已使用過的程式碼。

片段 (Fragments)

Next.js 建置了一個頂層 <Document> 元件,會與每個頁面一起進行伺服器渲染。覆寫此元件可讓你完全控制標記,實現許多進階用例

初始標記的一部分是 Next.js 需要在客戶端評估的腳本列表。自定義 _document 如下所示:

pages/_document.js
import Document, { Head, Main, NextScript } from 'next/document';
export default class extends Document {
  render() {
    return (
      <html>
        <Head />
        <body>
          <Main />
          <NextScript />
        </body>
      </html>
    );
  }
}

Document 允許你自定義頁面的整個伺服器渲染輸出

直到最近,我們還被迫將腳本包裹在 <div> 中。

在 Next.js 5.0 中,我們現在利用了新的 Fragment 支援,這轉化為更輕量的頁面和對頁面樣式的完全控制,沒有多餘的標記。

更準確的錯誤訊息

Node.js 不支援 source map,伺服器端發生的錯誤會伴隨指向編譯後程式碼的堆疊追蹤。

在 Next 5 中,我們改進了伺服器端的 source map 支援。伺服器渲染時發生的錯誤現在會指向正確的函式和行號。

錯誤現在顯示正確的行號、檔案和函式名稱

錯誤現在顯示正確的行號、檔案和函式名稱

結論

Universal Webpack 鞏固了 Next.js 的基礎並使其更具未來適應性。總的來說,不再有人為區分哪些插件或載入器適用於 Next.js,哪些不適用。

秉持_零配置_精神,我們興奮地推出 Next Plugins,這是社群配方儲存庫,可自動擴展 Next.js 功能,無需調整特定選項。

透過此功能,我們現在支援所有 CSS 解決方案、TypeScript 等編譯到 JS 的語言,以及 Nerve 等 React 替代方案,只需引入額外模組並在 next.config.js 中明確包含即可。簡單而不隱晦。

區域 (Zones) 允許互連不同儲存庫甚至不同伺服器的 Next.js 應用程式。我們認為這是「團隊可擴展性」類別改進的重要里程碑。

Next.js 因此成為由多個團隊維護的大型應用程式的絕佳選擇。他們現在可以並行部署改進,減少錯誤範圍,提高迭代速度,甚至可以在我們核心之外嘗試不同技術,如各種不同方法的狀態管理或資料獲取。

我們想藉此機會感謝 Deep Varma 和 Trulia 工程團隊貢獻的關鍵見解、程式碼和測試,這些促成了此功能的設計。

一如既往,這個版本的發布離不開眾多開源貢獻者和我們優秀的社群。