边学边写nextjs - 4 踩坑(不定时更新)

2,197 阅读4分钟

动态导入

canvas即操作dom节点

在写项目中,总有那么几个需求可能需要操作dom。

import dynamic from 'next/dynamic'
const Loading = dynamic(
  () => import('@/commonComp/Loading'),
  { ssr: false }
)

这就代表了,不需要在服务端渲染,而是在客户端渲染, 就可操作dom了。

有一点差点忘了,这种方式渲染的模块是无法获取到实例的,只能依靠eventBus式,或者回调函数等方来保存和获取。

动态路由(自定义路由无法获取参数)

前面只是大致写了一下,并没有实际操作,然而自己写的时候 ,总算发现一个超级大坑了o(╥﹏╥)o,跟着官网写,根本没发获取路由参数

看看官网怎么写的,以下是官网代码,坑点啊

// server.js
const { createServer } = require('http')
const { parse } = require('url')
const next = require('next')

const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()

app.prepare().then(() => {
  createServer((req, res) => {
    // Be sure to pass `true` as the second argument to `url.parse`.
    // This tells it to parse the query portion of the URL.
    const parsedUrl = parse(req.url, true)
    const { pathname, query } = parsedUrl

    if (pathname === '/a') {
      app.render(req, res, '/a', query)
    } else if (pathname === '/b') {
      app.render(req, res, '/b', query)
    } else {
      handle(req, res, parsedUrl)
    }
  }).listen(3000, (err) => {
    if (err) throw err
    console.log('> Ready on http://localhost:3000')
  })
})

骚微改造一下,成自己测试代码。

// server.js
const express = require('express');
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser')
// const cp = require('child_process');
const next = require('next');
const router = require('./router');

const dev =  process.env.NODE_ENV !== 'production';
const PORT = dev ? 5555 : 80
const nextApp = next({ dev });
const handle = nextApp.getRequestHandler();

nextApp.prepare()
  .then(() => {
    const app = express();
    app.use(cookieParser());
    app.use(bodyParser.json({limit: '2mb'}));
    app.use(bodyParser.urlencoded({limit: '2mb', extended:false}));
    app.use(express.static('./public'));

    // 路由中间件
    app.use('/api', router); // 添加router中间件
    app.get('/abc', (req, res) => {
      return nextApp.render(req, res, '/abc', req.params);
    });
    app.get('/:catalog', (req, res) => {
      return nextApp.render(req, res, '/index', req.params);
    });
    app.get('/', (req, res) => {
      return nextApp.render(req, res, '/index', { catalog: 0 });
    });

    app.get('*', (req, res) => {
      return handle(req, res);
    });

    app.listen(PORT, '0.0.0.0', (err) => {
      if (err) throw err;
      console.log(`> Ready on ${PORT}`);
    });
  });

以下是pages里的index

import React, { Component, Fragment } from 'react'
import { withRouter } from "next/router";
import url from '@/common/url'
import axios from 'axios'
import Link from 'next/Link'

class HomePage extends Component {
  componentDidMount () {
    console.log(this.props.router.query)
  }
  render () {
    return <div>
      <Link href="/abc?abc=1" ><a>to abc</a></Link>
    </div>
  }
}

export default withRouter(HomePage);

结果就是,query里的参数根本没法拿到,一直就是undefined。前前后后测试了近三个小时,把我都心态都搞炸了,找官网吧。功夫不负有心人,在数据获取模块发现了东西(为什么就不能在路由那块也加个提示!!!!!!!)。原文英文,用的google翻译。

画红框的地方非常重要啊,简单点说就是,只要使用了路由,最好都加上这个函数

单页面应用(同一个页面会刷新重新加载)

这个标题不知道怎么取,直接说问题:如果在服务里用/index/:catalog这种写法,进入的是同一个页面,整个页面也会重新加载一遍,也就是刷新,这样作为单页面应用的优势就没了,单页面要的就是无刷新。

举个例子,以下为server.js

// server.js
app.get('/index/:catalog', (req, res) => {
      return nextApp.render(req, res, '/index', req.params);
    });

如果index页面有个跳转,跳转到当前index/1,也就是catalog为1,这时整个页面会重新刷新一遍,所有东西重新加载,唉难受啊。所以改呗,官网支持的那种,

// server.js
app.get('/index', (req, res) => {
    // 解析url,自行配置
      return nextApp.render(req, res, '/index', params);
    });

。。。。。。又改回去了。还有一点服务的params配置是一定要写的,否则就会想下图一样,刷新页面就没了

使用富文本编辑器draft-editor

跟上面一样使用dynamic,动态导入。这个单独说的原因是,这款编辑器需要使用到它自身的函数,而它内部使用的是服务器端没有的window属性,所以一引入就会报错,而且异步导入也会获取不到构造函数

所以,需要动态导入(不需要服务端渲染)的不是编辑器,而是跟编辑器相关的所有模块。同时这也带来了问题,无法使用redux,不过没关系,在需要redux的外层再嵌套一层就行了。如下图

静态资源

可以在根目录创建一个public,那么这个目录下的所有资源都可以引用了,如果需要包含文件夹,可以在confit.next.js里进行如下配置

publicRuntimeConfig: {
    // Will be available on both server and client
    staticFolder: '/other',
  },

部署-linux @代表src路径所产生的问题

在前面的文章写到可以用@代表src路径,可是当部署到linux后,貌似是不可在css里进行该写法(js里还是可以用的,css里不行)。希望有知道的大佬,可以说下 Thanks♪(・ω・)ノ

打包后的文件如何拆分,放到cdn

这个问题主要是打包后的静态资源还是蛮大的,如果一直放到服务器很明显不合理,所以就需要配置。

在next.config.js里加入如下

const path = require('path')
const withSass = require('@zeit/next-sass')
const withCss = require('@zeit/next-css');
const withPlugins = require("next-compose-plugins");
const { override, adjustStyleLoaders, addWebpackAlias } = require("customize-cra");

module.exports = withPlugins([withSass,withCss], {
  // 这里就是配置cnd的地址
  assetPrefix: process.env.NODE_ENV !== 'production' ? 'http://127.0.0.1:4321' : '',
  // 这里是兼容scss和css的,以及@代理地址适配
  webpack: override(addWebpackAlias({
    '@': path.resolve('src'),
  })),
});

然后可以看下打包后的html文件,基本都是这个地址了

接下来需要做的就是把文件放进去就行了。

打包后有很多历史文件夹,找到最新的就是刚打的包。