Next.js 7.0正式釋出:重新編譯速度提高42%,支援WebAssembly
在經過26次金絲雀釋出和340萬次下載之後,現在,我們正式推出生產就緒的Next.js 7。
- DX改進:啟動速度提高57%,重新編譯速度提高42%;
- 使用react-error-overlay更好地報告錯誤;
- 編譯管道昇級:Webpack 4和Babel 7;
- 標準化的動態匯入;
- 靜態CDN支援;
- 較小的初始HTML載荷;
- App和Page之間的React Context(伺服器端渲染)。
DX改進
Next.js的主要目標之一是提供最佳的效能和開發者體驗。最新版本為構建和除錯管道帶來了很多重大改進。
得益於Webpack 4和Babel 7,以及我們對程式碼庫做出的很多改進和優化,Next.js現在在開發過程中的啟動速度提高了57%。
我們新增了增量編譯快取,讓變更程式碼的構建速度快了40%。
以下是我們收集的一些示例資料:
因為使用了webpackbar,在開發和構建的同時可以看到更好的實時反饋:
使用react-error-overlay更好地報告錯誤
準確地渲染錯誤對於良好的開發和除錯體驗來說是至關重要的。到目前為止,我們可以渲染錯誤訊息和堆疊跟蹤資訊。我們在此基礎上更進一步,我們使用react-error-overlay來豐富堆疊跟蹤資訊:
- 準確的伺服器端和客戶端錯誤位置;
- 高亮顯示錯誤來源;
- 完整的堆疊跟蹤資訊。
這是之前和之後的錯誤顯示比較:
另外,藉助react-error-overlay,你只需單擊特定程式碼塊就可以輕鬆開啟文字編輯器。
Webpack 4
從釋出第一個版本以來,Next.js一直使用Webpack來打包程式碼和重用豐富的外掛。Next.js現在使用了最新的Webpack 4,其中包含很多改進和bug修復。
- 支援.mjs原始檔;
- 程式碼拆分改進;
- 更好的搖樹優化(刪除未使用的程式碼)支援。
另一個新功能是支援WebAssembly,Next.js甚至可以進行WebAssembly伺服器渲染,這裡有一個ofollow,noindex" target="_blank">例子 。
CSS匯入
因為使用了Webpack 4,我們引入了一種從捆綁包中提取CSS的新方法,這個外掛叫作mini-extract-css-plugin 。
mini-extract-css-plugin提供了@zeit/next-css、@zeit/next-less、@zeit/next-sass和@zeit/next-stylus。
這些Next.js外掛的新版本解決了與CSS匯入相關的20個問題,例如,現在支援import()動態匯入CSS:
// components/my-dynamic-component.js import './my-dynamic-component.css' export default () => <h1>My dynamic component</h1>
// pages/index.js import dynamic from 'next/dynamic' const MyDynamicComponent = dynamic(import('../components/my-dynamic-component')) export default () => <div> <MyDynamicComponent/> </div>
一個重大改進是現在不再需要在pages/_document.js中新增以下內容:
<link rel="stylesheet" href="/_next/static/style.css" />
Next.js會自動注入這個CSS檔案。在生產環境中,Next.js還會自動向CSS URL中新增內容雜湊,當檔案發生變更時,終端使用者就不會得到舊檔案,並且能夠獲得不可變的永久快取。
簡而言之,要在Next.js專案中支援匯入.css檔案,只需要在next.config.js中註冊withCSS外掛:
const withCSS = require('@zeit/next-css') module.exports = withCSS({/* my next config */})
標準化動態匯入
從版本3開始,Next.js就通過next/dynamic來支援動態匯入。
作為這項技術的早期採用者,我們必須自己編寫解決方案來處理import()。
因此,Next.js逐漸缺失Webpack後來引入的一些功能,包括import()。
例如,無法手動命名和捆綁某些檔案:
import(/* webpackChunkName: 'my-chunk' */ '../lib/my-library')
另一個例子是在next/dyanmic模組之外使用import()。
從Next.js 7開始,我們不再直接使用預設的import(),Next.js為我們提供了開箱即用的import()支援。
這個變更也是完全向後相容的。使用動態元件非常簡單:
import dynamic from 'next/dynamic' const MyComponent = dynamic(import('../components/my-component')) export default () => { return <div> <MyComponent /> </div> }
這段程式碼的作用是為my-component建立一個新的JavaScript檔案,並只在渲染<MyComponent/>時載入它。
最重要的是,如果沒有進行渲染,<script>標記就不會出現在初始HTML文件中。
為了進一步簡化我們的程式碼庫並利用優秀的React生態系統,在Next.js 7中,我們使用react-loadable重寫了next/dynamic模組。這為動態元件引入了兩個很棒的新特性:
- 使用next/dynamic的timeout選項設定超時;
- 使用next/dynamic的delay選項設定元件載入延遲。例如,如果匯入非常快,可以通過這個選項讓載入元件在渲染載入狀態之前等待一小段時間。
Babel 7
Next.js 6中就已經引入了Babel 7測試版。後來Babel 7穩定版本釋出,現在,Next.js 7正在使用這個新發布的穩定版Babel 7。
一些主要特性:
- Typescript支援,在Next.js中可以使用@zeit/next-typescript;
- 片段語法<>支援;
- babel.config.js支援;
- 通過overrides屬性將預設/外掛應用於檔案或目錄的子集。
如果你的Next.js專案中沒有自定義Babel配置,那麼就不存在重大變更。
但如果你具有自定義Babel配置,則必須將相應的自定義外掛/預設升級到最新版本。
如果你從Next.js 6以下的版本升級,可以使用babel-upgrade工具。
除了升級到Babel 7之外,當NODE_ENV被設定為test時,Next.js Babel預設(next/babel)現在預設將modules選項設定為commonjs。
這個配置選項通常是在Next.js專案中建立自定義.babelrc的唯一理由:
{ "env": { "development": { "presets": ["next/babel"] }, "production": { "presets": ["next/babel"] }, "test": { "presets": [["next/babel", { "preset-env": { "modules": "commonjs" } }]] } } }
使用Next.js 7,這將變成:
{ "presets": ["next/babel"] }
現在可以刪除.babelrc,因為在沒有Babel配置時,Next.js將自動使用next/babel。
較小的初始HTML載荷
Next.js在預渲染HTML時會將頁面內容放在<html>、<head>、<body>結構中,幷包含頁面所需的JavaScript檔案。
這個初始載荷之前約為1.62kB。在Next.js 7中,我們優化了初始HTML載荷,現在為1.5kB,減少了7.4%,讓頁面變得更加精簡。
我們主要通過以下幾種方式來縮小檔案:
- 移除__next-error div;
- 內聯指令碼被最小化,在未來的版本中,它們將被完全移除;
- 去掉未使用的__NEXT_DATA__屬性,例如nextExport和assetPrefix屬性。
靜態CDN支援
在Next.js 5中,我們引入了assetPrefix支援,讓Next.js可以自動從某個位置(通常是CDN)載入資源。如果你的CDN支援代理,可以使用這種辦法。你可以像這樣請求資源:
https://cdn.example.com/_next/static/<buildid>/pages/index.js
通常,CDN先檢查快取中是否包含這個檔案,否則直接從源中請求檔案。
不過,某些解決方案需要將目錄直接預先上傳到CDN中。這樣做的問題在於Next.js的URL結構與.next資料夾中的資料夾結構不匹配。例如我們之前的例子:
https://cdn.example.com/_next/static/<buildid>/pages/index.js //對映到: .next/page/index.js
在Next.js 7中,我們改變了.next的目錄結構,讓它與URL結構相匹配:
https://cdn.example.com/_next/static/<buildid>/pages/index.js //對映到: .next/static/<buildid>/pages/index.js
儘管我們建議使用代理型別的CDN,但新結構也允許不同型別CDN的使用者將.next目錄上傳到CDN。
styled-jsx 3
我們也引入了styled-jsx 3,Next.js的預設CSS-in-JS解決方案,現在已經為React Suspense做好了準備。
如果一個元件不屬於當前元件作用域的一部分,那麼該如何設定這個子元件的樣式呢?例如,如果你將一個元件包含在父元件中,並只有當它被用在父元件中時才需要特定的樣式:
const ChildComponent = () => <div> <p>some text</p> </div> export default () => <div> <ChildComponent /> <style jsx>{` p { color: black } `}</style> </div>
上面的程式碼試圖選擇p標籤,但其實不起作用,因為styled-jsx樣式被限定在當前元件,並沒有洩漏到子元件中。解決這個問題的一種方法是使用:global方法,將p標記的字首移除。但這樣又引入了一個新問題,即樣式洩露到了整個頁面中。
在styled-jsx 3中,通過引入一個新的API css.resolve解決了這個問題,它將為給定的syled-jsx字串生成className和<style>標籤(styles屬性):
import css from 'styled-jsx/css' const ChildComponent = ({className}) => <div> <p className={className}>some text</p> </div> const { className, styles } = css.resolve`p { color: black }` export default () => <div> <ChildComponent className={className} /> {styles} </div>
這個新API可以將自定義樣式傳給子元件。
由於這是styled-jsx的主要版本,如果你使用了styles-jsx/css,那麼在捆綁包大小方面有一個重大變化。在styled-jsx 2中,我們將生成外部樣式的“scoped”和“global”版本,即使只使用“scoped”版本,我們也會將“global”版本包含在內。
使用styled-jsx 3時,全域性樣式必須使用css.global而不是css,這樣styled-jsx才能對包大小進行優化。
App和Page之間的React Context(伺服器端渲染)
從Next.js 7開始,我們支援pages/_app.js和頁面元件之間的React Context API。
以前,我們無法在伺服器端的頁面之間使用React上下文。原因是Webpack保留了內部快取模組而不是使用require.cache,我們開發了一個自定義Webpack外掛來改變這種行為,以便在頁面之間共享模組例項。
這樣我們不僅可以使用新的React上下文,在頁面之間共享程式碼時還能減少Next.js的記憶體佔用。
社群
從Next.js首次釋出以來,就已獲得相當多的使用者,從財富500強公司到個人部落格。我們非常高興看到Next.js的採用量一直在增長。
- 目前,超過12,500個被公開索引的域名在使用Next.js。
- 我們有超過500名貢獻者,他們至少提交過一次程式碼。
- 在GitHub上,這個專案已經有29,000個star。
- 自首次釋出以來,已提交了大約2200個拉取請求。
Next.js社群在spectrum.chat/next-js上擁有近2000名成員。