[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

原文地址:https://www.cnblogs.com/mybilibili/p/11725443.html

时间: 2024-08-10 01:41:29

[Next] 04.next自定义内容的相关文章

利用DEDE正则批量查找替换数据库的自定义内容

正则的表达式在MY SQL中也有应用,但是在具体将查找的东西直接进行Replace的时候目前还不能解决,幸好后台中有个dede利用 正则,利用dede正则批量替换数据库中内容,这样我们完全减小了自己的工作量了. 例如网站的一个例子, 是替换一个div中的内容,摘抄实践下,关键还是自己去体会,多用下正则,还是不错的.正则相关的文章我在我的博客中也多次 提到了他的多种运用途径.如果你都掌握好它的话,你的工作效率就会成倍的提高了.下面这个正则的用途是查找数据库的中自 定义的DIV层中运用,一般这种用法

drupal7 如何自定义内容部链接,并预留hook接口(自定义视图字段比较有用)

$links = array(); //获取系统菜单,所有权限都默认进行了验证 $links['case-edit']=menu_get_item('node/'.$row->nid.'/edit'); //自定义链接地址 $links['custom_url']=array( 'title' => t('custom_url'), 'href' => 'custom/url', 'attributes' => array('class' => array_merge(cdi

contentprovider 自定义内容提供者

contentprovider 自定义内容提供者步骤; 1.继承contentprovider 2.清单文件配置,包括主机路径配置:content://包名.类名 3.定义URI匹配器,用于匹配uri,若路径不满足条件返回-1. 4.添加匹配规则 5.复写增删改查方法 注意:结果集系统会自动关闭!!!

dede自定义内容模型下,列表只显示10条的问题及解决方法

<div class="zjtd-content-ld s-content"> {dede:arclist tagid='ld' row='100' pagesize='5' typeid="43" channelid='17' orderby='sortrank' addfields='zjname,zjtitle,zjfield'} <div class="zjtd-yy"> <div class="z

Android学习笔记(二十)——自定义内容提供器

//此系列博文是<第一行Android代码>的学习笔记,如有错漏,欢迎指正! 如果我们想要实现跨程序共享数据的功能,官方推荐的方式就是使用内容提供器,可以通过新建一个类去继承 ContentProvider 的方式来创建一个自己的内容提供器: 一.继承ContentProvider的六个方法: ContentProvider 类中有六个抽象方法,我们需要使用子类去继承它,并重写六个方法,我们先来认识这六个类.新建 MyProvider继承自 ContentProvide,代码如下所示: 1 p

dede自定义内容模型会员投稿显示不了

1.新建一个栏目,内容模型为此模型,且允许投稿 2.在 系统设置->基本参数->会员设置   中  “是否开放会员对自定义模型投稿: ” 选择了 “是” 测试, 注册一个会员,系统模型的栏目都可以投稿, 无法选择到(就是不显示)自定义模型的栏目 解决方法: 先找到你自定义频道的ID,比如是17,在/member/目录下复制一个article_add.php并重命名这个文件为article_add1.php,打开编辑它,找到第7行$channelid = isset($channelid) &a

使用 [email&#160;protected] 在边缘站点自定义内容

[email protected] 是一个 AWS Lambda 扩展,后者是一个计算服务,可用于执行函数以自定义 CloudFront 提供的内容.您可以在一个区域(US-East-1(弗吉尼亚北部))中编写 Node.js 或 Python 函数,然后在与查看器较为接近的全球 AWS 位置执行它们,而无需预置或管理服务器.[email protected] 会自动扩展,从每天几个请求到每秒数千个请求.在与查看器较为接近 AWS 位置 (而不是源服务器) 上处理请求,可显著减少延迟并改善用户体

android100 自定义内容提供者

#ContentProvider,就是来操作数据的,增删改查, * 四大组件之一 * 应用的数据库是不允许其他应用访问的 * 内容提供者的作用就是让别的应用访问到你的数据库 * 内容提供者的作用:把私有数据暴露给其他应用,通常,是把私有数据库的数据暴露给其他应用 *短信联系人都是在数据库里面.mmssms是短信数据库, *短信数据库有短信数据库的内容提供者,联系人数据库有联系人数据库的内容提供者.拿到对应数据库的内容提供者就能够访问对应的数据库数据. ###短信数据库 * sms表 * body

bootstrap-缩略图-自定义内容

说明 在缩略图组件内添加html内容 示例 <!DOCTYPE html> <html lang="zh-CN">    <head>      <meta charset="utf-8">      <meta http-equiv="X-UA-Compatible" content="IE=edge">      <meta name="viewp