自定义 head
这是默认的 head

这样的 head 并不能满足我们的需求.next 公开了一个内置组件,用于将元素追加到<head>
标签的.我们可以通过这个自定义 head
新建 components/Head.js
import { Fragment } from "react"; import Head from "next/head"; function MyHead() { return ( <Fragment> <Head> <meta charset="utf-8"></meta> <meta name="referrer" content="origin"></meta> <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1"></meta> <meta name="keywords" content="next.js,react.js"></meta> <meta content="next 简介 next.js作为一款轻量级的应用框架,主要用于构建静态网站和后端渲染网站。 next 特点 默认情况下由服务器呈现 自动代码拆分可加快页面加载速度 简单的客户端路由(基于页面) 基于"></meta> <title>蓝猫</title> </Head> </Fragment> ); } export default MyHead;
为了避免重复的标签,您
<head>
可以使用 key 属性,以确保标签仅呈现一次
在 MyLayout.js 引入
import Head from "./Head"; ...... <Fragment> <Head /> <Header /> <div className={"content"}>{children}</div> <Footer /> </Fragment> ......
添加完之后的 head 标签,但是在使用中<meta charset="utf-8"></meta>
如果再添加的话就会出现两个,去掉 Head 组件中的 charset.

自定义 app

上面是 next 的渲染流程,next 使用 app 组件来进行初始化页面. 可以覆盖 next 自带的 app 来进行初始化
- 在页面更改是保持持久布局
- 当页面切换时保持状态
- 使用自定义错误处理
componentDidCatch
- 页面的数据注入
新建 pages/_app.js
import React from "react"; import App from "next/app"; class MyApp extends App { render() { const { Component, pageProps } = this.props; return ( <div className="my-app"> <Component {...pageProps} /> </div> ); } } export default MyApp;
getInitialProps 在 App 中添加自定义设置会影响自动静态优化
自定义 document
- 自定义
<Document>
通常用于扩充<html>
和<body>
标签 - 在服务端呈现
- 初始化服务端时添加文档标记元素
- 通常实现服务端渲染会使用一些 css-in-js 库,如 styled-components, glamorous 或 emotion。styled-jsx 是 Next.js 自带默认使用的 css-in-js 库
新建 pages/_document.js
import Document, { Html, Head, Main, NextScript } from "next/document"; class MyDocument extends Document { static async getInitialProps(ctx) { const initialProps = await Document.getInitialProps(ctx); return { ...initialProps }; } render() { return ( <Html> <Head /> <body> <div className="document"> <Main /> <div className="inner-document"></div> <NextScript /> </div> </body> </Html> ); } } export default MyDocument;
钩子 getInitialProps 接收到的参数 ctx 对象都是一样的
- 回调函数 renderPage 是会执行 React 渲染逻辑的函数(同步),这种做法有助于此函数支持一些类似于 Aphrodite 的 renderStatic 等一些服务器端渲染容器。
注意:
<Main />
外的 React 组件将不会渲染到浏览器中,所以那添加应用逻辑代码。如果你页面需要公共组件(菜单或工具栏),可以参照上面说的 App 组件代替。

自定义 renderPage
使用 renderPage 的唯一原因是 css-in-js 库需要包裹当前应用并且在服务端渲染下也能正常工作
static async getInitialProps(ctx) { const originalRenderPage = ctx.renderPage ctx.renderPage = () => originalRenderPage({ // useful for wrapping the whole react tree enhanceApp: App => App, // useful for wrapping in a per-page basis enhanceComponent: Component => Component, }) // Run the parent `getInitialProps` using `ctx` that now includes our custom `renderPage` const initialProps = await Document.getInitialProps(ctx) return initialProps }
自定义错误页面
客户端和服务器端的 404 或 500 错误默认由 error.js 组件处理。
新建 pages/_error.js 覆盖
import React from 'react' function Error({ statusCode }) { return ( <p> {statusCode ? `An error ${statusCode} occurred on server` : 'An error occurred on client'} </p> ) } Error.getInitialProps = ({ res, err }) => { const statusCode = res ? res.statusCode : err ? err.statusCode : 404 return { statusCode } }
自定义配置 / 自定义 Webpack 配置 / 自定义 babel 配置
当前的配置./next.config.js
const fetch = require("isomorphic-unfetch"); const withBundleAnalyzer = require("@zeit/next-bundle-analyzer"); const withLess = require("@zeit/next-less"); const FilterWarningsPlugin = require("webpack-filter-warnings-plugin"); if (typeof require !== "undefined") { require.extensions[".less"] = file => {}; } function HACK_removeMinimizeOptionFromCssLoaders(config) { config.module.rules.forEach(rule => { if (Array.isArray(rule.use)) { rule.use.forEach(u => { if (u.loader === "css-loader" && u.options) { delete u.options.minimize; } }); } }); } module.exports = withBundleAnalyzer( withLess({ poweredByHeader: false, analyzeServer: ["server", "both"].includes(process.env.BUNDLE_ANALYZE), analyzeBrowser: ["browser", "both"].includes(process.env.BUNDLE_ANALYZE), bundleAnalyzerConfig: { server: { analyzerMode: "static", reportFilename: "../bundles/server.html" }, browser: { analyzerMode: "static", reportFilename: "../bundles/client.html" } }, exportPathMap: async function() { const paths = { "/": { page: "/" }, "/books": { page: "/books" }, "/article": { page: "/article" }, "/write": { page: "/write" } }; const res = await fetch("https://api.tvmaze.com/search/shows?q=batman"); const data = await res.json(); const shows = data.map(entry => entry.show); shows.forEach(show => { paths[`/book/${show.id}`] = { page: "/book/[id]", query: { id: show.id } }; }); return paths; }, lessLoaderOptions: { javascriptEnabled: true }, webpack(config) { config.plugins.push( new FilterWarningsPlugin({ exclude: /mini-css-extract-plugin[^]*Conflicting order between:/ }) ); HACK_removeMinimizeOptionFromCssLoaders(config); return config; } }) );
当前的.babelrc
{ "presets": ["next/babel"], "plugins": [ [ "import", { "libraryName": "antd", "style": "less" } ] ] }