前言bb

前段时间,一同事问我react相关知识,我的大脑突然空白,许多细节都已模糊,回去看看以前的项目,才回想起来点,温故知新(捡起来)

  • vue,react都是很好的框架,两则相互的竞争,相互借鉴,突破对方的弱点,相信前端会越来越好(近年来相对稳定,有好有坏吧,不管怎么说,不管什么行业,尤其靠技术吃饭的,自己不提升将会被时代抛弃)

同样服务端渲染的next框架

  • nuxt搞过,好奇心:next也要学学,先学习该工具的使用

vue与react框架思想一样,那么相对应他们与之配套的服务端渲染nuxt和next是不是大同小异,gogogo

学习一个新语言,新框架,新工具,都请先阅读官方文档:Next英文文档,中文文档有点落后

一、搭建项目

官网脚手架 / 当然你也可以自己搭建,安装所需npm包即可
新版本next需要注意node版本10以上

npm init next-app
# or
yarn create next-app

使用官方cli初始化项目,就只一个pages文件夹,其他都需要你自己丰富

我的demo
目录结构

├── .next  --------------------------- 打包后项目主文件
├── api  ----------------------------- 说明文件
    ├── asyncData  ------------------- node服务端请求接口
    ├── syncData  -------------------- 浏览器请求接口
├── assets  -------------------------- 项目静态资源文件目录(图片、公共样式、字体等)
├── components  ---------------------- 组件
├── layouts  ------------------------- 布局模式
├── pages  --------------------------- 页面(该文件夹下也对应着项目路由路径)
├── server
    └── index.js  -------------------- node服务启动文件
├── static  -------------------------- robot静态文件
├── store  --------------------------- redux状态管理
├── utils  --------------------------- 工具
├── next.config.js ------------------- webpack配置入口
├── package.json  -------------------- 项目配置

老规矩,整理出页面所需要的基础配置与依赖

  • webpack
  • node服务配置
  • 路由
  • 获取数据和组件生命周期
  • rudex状态管理
  • 最后开始写页面或改造已有项目

基本目标确定,开始工作workwork

一、webpack

next并没有对该配置文件做多余的处理,编程者还是自己选择所需的打包吧
webpack使用zeit下提供的三连utils,简易的话就不用配置啦,相对nuxt,他就没有其他多余的配置,大多还是webpack的使用

next.config.js

const withSass = require('@zeit/next-sass')
const withCSS = require('@zeit/next-css')
const withSourceMaps = require('@zeit/next-source-maps')
module.exports = withSass(withCSS(withSourceMaps()))

二、node服务配置

server/index.js

这里可以使用express,也可以使用koa作为node框架服务

const express = require('express')
const next = require('next')

const compression = require('compression')
const dev = process.env.NODE_ENV !== 'production'
const app = next({dev})
const handle = app.getRequestHandler()
let port = dev ? 6666 : 8080
console.log('Waiting ready on http://localhost ' + port + ' ……')

// Pass in the absolute path to your robots.txt file
app.prepare()
  .then(() => {
    const server = express()

    if (!dev) {
      server.use(compression()) //gzip
    }
    // 动态请求处理,自定义路由映射
    server.get('/a/:id', (req, res) => {
      const actualPage = '/detail'
      const queryParams = {id: req.params.id}
      app.render(req, res, actualPage, queryParams)
    })

    server.get('/robots.txt', (req, res) => (
      res.status(200).sendFile('robots.txt', optionsPlain)
    ));
    server.get('/sitemap.html', (req, res) => (
      res.status(200).sendFile('sitemap.html', optionsHtml)
    ));
    server.get('/sitemap.xml', (req, res) => (
      res.status(200).sendFile('sitemap.xml', optionsXml)
    ));
    server.get('*', (req, res) => {
      return handle(req, res)
    })
    server.listen(port, (err) => {
      if (err) throw err
      console.log('> Ready on http://localhost ' + port)
    })

  })
  .catch((ex) => {
    console.error(ex.stack)
    process.exit(1)
  })
  • 其实对比nuxt,启动文件一样的,在该node入口服务文件中,使用express启动服务,对请求进行处理,再使用next模块render页面返回客户端。
  • 这里还添加seo两大文件,robots.txt,sitemap.html,站点链接地图与访问指定可访问页面

三、路由

还是与nuxt一样,让我们不用关心与维护路由文件(app.js),那他的路由路径怎么对应,其实就是对应着pages下面的文件路径,即是页面路径。所以我们在写页面的时候,规范组件抽离至component文件夹,也不要有其他不是单独页面的文件

让我们看看他具体使用,两个页面如何跳转

  • 一、link下必须是一个元素节点
  • 二、想要页面跳转,link包裹的必须是a标签,其他标签href传递不下去,没用的哦,引用官方解释:The default behaviour for the <Link> component is to push a new url into the stack. You can use the replace prop to prevent adding a new entry.
// pages/index.js
import Link from 'next/link';

function Home() {
  return (
    <>
      <ul>
        <li>Home</li>
        <li>
          <Link href="/xiaojuzi">
            <a>hello 小橘子</a>
          </Link>
        </li>
      </ul>

      <h1>首页</h1>
    </>
  );
}

export default Home;
// pages/xiaojuzi.js
import Link from 'next/link';

function About() {
  return (
    <>
      <ul>
        <li>
          <Link href="/">
            <a>首页</a>
          </Link>
        </li>
      </ul>
    </>
  );
}

export default xiaojuzi;

如果很少访问某些页面,可以将其手动设置prefetch为false

<Link href="/xiaojuzi" prefetch={false}>
  <a>666</a>
</Link>

我们关心的动态路由它又来了

  • 方法一(服务端方式):动态路由映射,node服务配置文件中,我们对路由重新pushstate,当node服务接受到一个/a/1的请求,这个时候我们通过拦截,将路由映射到/detail的页面,并携带动态id参数过去,最后render页面
 server.get('/a/:id', (req, res) => {
      const actualPage = '/detail'
      const queryParams = {id: req.params.id}
      app.render(req, res, actualPage, queryParams)
    })
  • 方法二:next提供了特殊的文件命名,[id].js,在中括号就是动态的路由路径页面文件,该动态参数你可以在query中取到,来看官方例子与解释

Defining routes by using predefined paths is not always enough for complex applications, in Next.js you can add brackets to a page ([param]) to create a dynamic route (a.k.a. url slugs, pretty urls, et al).
Consider the following page pages/post/[pid].js:

import { useRouter } from 'next/router';

const Post = () => {
  const router = useRouter();
  const { pid } = router.query;

  return <p>Post: {pid}</p>;
};

export default Post;

同理,多动态参数,那文件夹就使用[]来命名包裹

  • 例:pages/post/[pid]/[comment].js将匹配/post/1/a-comment。它的query对象是:{ pid: '1', comment: 'a-comment' }。

四、获取数据和组件生命周期

与nuxt提供的asyncData一样,next也提供了钩子getInitialProps

  • 在函数式组件中
import fetch from 'isomorphic-unfetch';

function Page({ stars }) {
  return <div>Next stars: {stars}</div>;
}

Page.getInitialProps = async ({ req }) => {
  const res = await fetch('https://api.github.com/repos/zeit/next.js');
  const json = await res.json();
  return { stars: json.stargazers_count };
};

export default Page;
  • 在类组件中
import React from 'react';

class HelloUA extends React.Component {
  static async getInitialProps({ req }) {
    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;
    return { userAgent };
  }

  render() {
    return <div>Hello World {this.props.userAgent}</div>;
  }
}

export default HelloUA;

getInitialProps 接收具有以下属性的上下文对象:

  • pathname -URL的路径部分
  • query -URL的查询字符串部分被解析为对象
  • asPath- String实际路径(包括查询)的-在浏览器中显示
  • req -HTTP请求对象(仅服务器)
  • res -HTTP响应对象(仅服务器)
  • err -渲染期间遇到任何错误的错误对象

五、rudex状态管理

依旧是快乐正常使用,在你的app最外层组件挂载,全局嘻嘻
看到一篇还可以的博客,还不会使用rudex的可以看看

六、默认页面

一、自定义app.js。Next.js使用该App组件来初始化页面。您可以覆盖它并控制页面初始化。这使您可以做一些令人惊奇的事情,例如:

  • 页面更改之间的持久布局
  • 导航页面时保持状态
  • 使用自定义错误处理 componentDidCatch
  • 将其他数据注入页面(例如,通过处理GraphQL查询)

要覆盖,请创建./pages/_app.js文件并覆盖App类

import React from 'react';
import App from 'next/app';

class MyApp extends App {
  render() {
    const { Component, pageProps } = this.props;
    return <Component {...pageProps} />;
  }
}

export default MyApp;

六、最后开始写页面或改造已有项目

enenen,细节部分还得多实践实践,最后奉上请求方案

axios.js

import axios from "axios";
import qs from "qs";
 
// 创建axios默认请求
axios.defaults.baseURL = "http://gongpengji.com";
// 配置超时时间
axios.defaults.timeout = 100000;
// 配置请求拦截
axios.interceptors.request.use(
    // xxx
);
// 添加响应拦截器
axios.interceptors.response.use(
  function(response) {
    console.log(response);
    return response;
  },
  function(error) {
    // 对响应错误做点什么
    return Promise.reject(error);
  }
);
/**
 * get请求
 * @method get
 * @param {url, params, loading} 请求地址,请求参数,是否需要加载层
 */
var get = function(url, params, loading) {
  return new Promise((resolve, reject) => {
    // {
    //   params: params
    // }
    axios
      .get(url, params)
      .then(res => {
        resolve(res);
      })
      .catch(err => {
        reject(err);
      });
  });
};
/**
 * post请求
 * @method post
 * @param {url, params} 请求地址,请求参数,是否需要加载层
 */
var post = function(url, data) {
  return new Promise((resolve, reject) => {
    // qs.stringify(data)
    axios
      .post(url, data)
      .then(res => {
        console.log(res);
        resolve(res);
      })
      .catch(err => {
        reject(err);
      });
  });
};
export default { get, post };

思考

  • 在看一边服务端渲染实现思想都是一样,两则使用上的不同,给我又一次不同的体会(英文文档真的难读,但看的越多越容易找到表达的关键点)【谷歌翻译随时点开哈哈哈】,发现在服务端进行请求路径修改并reader指定页面,确实nice。
  • 现在清楚当客户端请求过来,nextjs才开始处理请求,并render,在这之间,我觉得还可以其他事件!
Last modification:May 2nd, 2020 at 08:18 pm
如果觉得我的文章对你有用,请随意赞赏