[Next] 04.next自定义内容
Posted mybilibili
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[Next] 04.next自定义内容相关的知识,希望对你有一定的参考价值。
自定义 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.
自定义服务器
之前一直是使用 next start 来开启一个服务器,实际上 next 支持自定义服务器,包括现有的路由和模式.
新建 server.js
const express = require("express");
const next = require("next");
const dev = process.env.NODE_ENV !== "production";
const app = next({ dev });
const handle = app.getRequestHandler();
app
.prepare()
.then(() => {
const server = express();
server.get("/m/:id", (req, res) => {
const actualPage = "/marked/[id]";
const queryParams = { id: req.params.id };
app.render(req, res, actualPage, queryParams);
});
server.get("*", (req, res) => {
return handle(req, res);
});
server.listen(6776, err => {
if (err) throw err;
console.log("> Ready on http://localhost:6776");
});
})
.catch(ex => {
console.error(ex.stack);
process.exit(1);
});
修改 package.json,添加内容
"server:dev": "node server.js",
"server:start": "NODE_ENV=production node server.js",
输入http://localhost:6776,页面正常进行加载渲染.其中我们做了额外的一些操作,正常访问marked/1进入highlight渲染页面,但是通过自定义起别名的形式,我们输入m/1就可以直接指向同一个页面.
其中 app 有 next(opts: object)创建而来
- dev(bool)是否在开发模式下启动 Next.js-默认 false
- dir(string)下一个项目所在的位置-默认‘.‘
- quiet(bool)隐藏服务器信息的错误消息-默认 false
- conf(object)重新配置 next.config.js-默认{}
禁用文件系统路由
next 会将/pages 下的 js 文件匹配一个静态文件存放在服务器上.这样可能导致 /index 和 /index.htm 这样都能访问页面.尤其是使用了自定义路由,则该操作可能导致同一内容从=由多个路径提供,这可能会导致 SEO 和 UX 问题.
// next.config.js
module.exports = {
useFileSystemPublicRoutes: false,
}
useFileSystemPublicRoutes 仅禁用来自 SSR 的文件名路由,客户端路由仍然可以访问这些路径。
客户端路由想要禁止客户端匹配到文件系统,使用 popstate 拦截.
动态 assetPrefix
有时候我们需要动态路由,比如根据基于请求更改 assetPrefix,这时候我们需要使用 app.setAssetPrefix
server.get("*", (req, res) => {
if (req.headers.host === "my-app.com") {
app.setAssetPrefix("http://cdn.com/myapp");
} else {
app.setAssetPrefix("");
}
return handle(req, res);
});
禁用 X-Powered-By
Next.js 将添加 x-powered-by 到请求标头中。这个是由语言解析器或者应用程序框架输出的。这个值的意义用于告知网站是用何种语言或框架编写的。
直接禁用它
// next.config.js
module.exports = {
poweredByHeader: false,
}
自定义 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"
}
]
]
}
Doc
以上是关于[Next] 04.next自定义内容的主要内容,如果未能解决你的问题,请参考以下文章