使用next.js完成从开发到部署

next.js 是一个非常棒的轻量级的react同构框架,使用它可以快速的开发出基于服务端渲染的react应用。在next.js 官网推荐的是使用now来部署应用,但是对于国内用户或者说是有特殊需求的用户来说,部署到自定义服务器也许是大多数人希望的。借着近期公司官网改版,顺便分享下自己从开发到部署过程中所经历的点点滴滴。

依稀还记得第一次使用next.js 是在去年(2017年),那个时候使用的是next.js 2.x版本,react还是15版本,一年过去,现在react已经发展到16版本,而next.js 已经发展到6.0版本了,迭代速度瞠目结舌,在使用新版本的过程中也是遇到不少的坑。

用到的技术

先说下这次用到了哪些技术,下面列举了项目中主要用到的技术或工具库。

由express原班人马开发的下一代web框架,用来提供web服务。

是一个高性能的HTTP和反向代理服务器,也是一个IMAP/POP3/SMTP服务器(摘自百度百科),由俄罗斯人开发。用来提供静态文件服务、https证书、代理服务。

一个javascript ui库

一个轻量级的react同构应用框架

由蚂蚁金服开发的基于react的一套中后台产品组件库

基于react的动画解决方案

判断组件是否在当前可视区的react 组件

一个带有负载均衡功能的Node应用的进程管理器

同构WHATWG Fetch API

开发阶段

讲了这么多,让我们进入开发阶段,第一步构建项目架构,这里分享下自己的项目结构:

? .vscode

vscode 配置文件

? component

react组件

? common

公共部分,我放置的是导航栏信息、全局变量和全局样式等等

? pages

项目所有页面入口,也是next.js 各页面入口文件

? static

静态文件

? styles

各页面样式表

? index.js

node启动文件

? .babelrc

babel配置文件

? .gitignore

git 配置文件

? ecosystem.config.js

pm2配置文件

? next.config.js

next.js 配置文件

? postcss.config.js

postcss 配置文件

? nginx.conf

nginx配置文件

? package.json

npm配置文件

在完成了项目结构配置之后,假设你已经在package.json中保存了我们所需要的所有依赖,让我们尝试着输入yarn来安装依赖。这里假设安装一切顺利,下面继续我们的开发之旅。

首先,在pages文件下新建一个index.js,这里就随便从我真实项目中抽取部分代码来作师范。

  1.  
    export default class HomePage extends React.Component {
  2.  
    static async getInitialProps({ req, pathname }) {
  3.  
    const data = await fetch(`${ctx}/api/projects/common/list`).then(res => res.json())
  4.  
    .then(dt => dt)
  5.  
    .catch(err => {
  6.  
    return {
  7.  
    success: false,
  8.  
    message: err.message
  9.  
    }
  10.  
    })
  11.  
    return { pathname,data };
  12.  
    }
  13.  
     
  14.  
    render() {
  15.  
    const { pathname, data } = this.props;
  16.  
    return (
  17.  
    <div>
  18.  
    <Head>
  19.  
    <title>首页-易科捷(武汉)生态科技有限公司</title>
  20.  
    </Head>
  21.  
    <div>Welcome to next.js!</div>
  22.  
    {/*这里省略代码*/}
  23.  
    </div>
  24.  
    );
  25.  
    }
  26.  
    }
  27.  
     
  28.  
    复制代码

如果你的package.json中没有配置next启动脚本,请访问setup进行配置,下面我们在控制台运行npm run dev,如果一切顺利,打开浏览器,你将会看到Welcome to next.js!

next.js中开发体验和react几乎没有什么区别,但是在webpack配置这块可能需要下点功夫。一些常用的插件像sasscss等, next.js都已经给你提供了,你也可以使用社区开源的插件来完成你的开发之旅。详情请查看next.js官网

部署

在经历了开发阶段、测试等等一系列流程,现在终于等到了部署阶段。在next.js中生产阶段打包只需要运行npm run build即可,官方推荐不修改打包的文件夹名字(原名称为.next),这里个人推荐修改成build或者dist这些名称。在打包完成之后,需要编写nodejs启动入口文件,下面贴出实例代码:

  1.  
    const Koa = require('koa')
  2.  
    const next = require('next')
  3.  
    const Router = require('koa-router')
  4.  
     
  5.  
    const port = parseInt(process.env.PORT, 10) || 3000
  6.  
    const dev = process.env.NODE_ENV !== 'production'
  7.  
    const app = next({ dev })
  8.  
    const handle = app.getRequestHandler()
  9.  
     
  10.  
    app.prepare()
  11.  
    .then(() => {
  12.  
    const server = new Koa()
  13.  
    const router = new Router()
  14.  
    // 首页
  15.  
    router.get('/', async ctx => {
  16.  
    await app.render(ctx.req, ctx.res, '/', ctx.query)
  17.  
    ctx.respond = false
  18.  
    })
  19.  
    // 关于
  20.  
    router.get('/about', async ctx => {
  21.  
    await app.render(ctx.req, ctx.res, '/about', ctx.query)
  22.  
    ctx.respond = false
  23.  
    })
  24.  
    // 产品
  25.  
    router.get('/products/:id', async ctx => {
  26.  
    const {id} = ctx.params
  27.  
    await app.render(ctx.req, ctx.res, `/products/${id}`, ctx.query)
  28.  
    ctx.respond = false
  29.  
    })
  30.  
    // 案例
  31.  
    router.get('/case', async ctx => {
  32.  
    await app.render(ctx.req, ctx.res, '/case', ctx.query)
  33.  
    ctx.respond = false
  34.  
    })
  35.  
    // 联系我们
  36.  
    router.get('/contact', async ctx => {
  37.  
    await app.render(ctx.req, ctx.res, '/contact', ctx.query)
  38.  
    ctx.respond = false
  39.  
    })
  40.  
    // 详情
  41.  
    router.get('/view/:type/:id', async ctx => {
  42.  
    const {id, type} = ctx.params
  43.  
    await app.render(ctx.req, ctx.res, `/view`, {id, type})
  44.  
    ctx.respond = false
  45.  
    })
  46.  
    // 如果没有配置nginx做静态文件服务,下面代码请务必开启
  47.  
    /* router.get('*', async ctx => {
  48.  
    await handle(ctx.req, ctx.res)
  49.  
    ctx.respond = false
  50.  
    })*/
  51.  
    // 防止出现控制台报404错误
  52.  
    server.use(async (ctx, next) => {
  53.  
    ctx.res.statusCode = 200
  54.  
    await next()
  55.  
    })
  56.  
    server.use(router.routes())
  57.  
    server.listen(port, () => {
  58.  
    console.log(`> Ready on http://localhost:${port}`)
  59.  
    })
  60.  
    })
  61.  
     
  62.  
    复制代码

一般的静态文件、gzip压缩无需交给nodejs来做,个人一直认为专业的事交给专业的人。这里将该项任务转移给nginx,特别注意上面实例代码中我注释的部分代码,若果你没有使用nginx来做静态文件服务,请务必开启,否则像next.js打包出来的jscss、图片文件等,都将报404

next.js生产打包阶段打包出来的js文件请求路径中带有版本号,而真实打包出来的文件夹却没有实际对应的目录,也就是打包出来的是虚拟目录,这里如果使用nginx就需要特别注意。好在next.js提供配置项来修改build id,以下是我的真实代码:

  1.  
    // next.config.js
  2.  
    module.exports = {
  3.  
    generateBuildId: async () => {
  4.  
    // For example get the latest git commit hash here
  5.  
    return 'v1'
  6.  
    }
  7.  
    }
  8.  
    复制代码

这样打包出来的虚拟路径大概是_next/v1/page/xxx.js,如果你使用cdn前缀,这里有一点区别,但是版本号依然存在。

还有一个坑就是next.js打包出来的有三个文件夹:bundlesdiststatic,对于不知道源码的人来说,根本不知道实际请求文件在哪一个文件夹。于是我就看next.js源码,发现其实找的是bundle文件下的page,源码位置:L214

所以在配置nginx就需要使用别名。下面给出一段我的nginx真实配置代码:

  1.  
    # 网站根目录文件
  2.  
    location ~ ^/(robots.txt|humans.txt|favicon.ico|sw.js|baidu_verify_7Kj6tQjI3v.html) {
  3.  
    root /home/website/eco_website_pc/static/;
  4.  
    if ($request_filename ~* sw.js){
  5.  
    expires -1s;
  6.  
    }
  7.  
    expires 10m;
  8.  
     
  9.  
    }
  10.  
    # static下的文件
  11.  
    location ^~ /static/ {
  12.  
    alias /home/website/eco_website_pc/static/;
  13.  
    if ($request_filename ~* sw.js){
  14.  
    expires -1s;
  15.  
    }
  16.  
    expires 10m;
  17.  
     
  18.  
    }
  19.  
    # next pages页面下的脚本
  20.  
    location ~ ^/(/_next/v1/) {
  21.  
    alias /home/website/eco_website_pc/build/bundles/;
  22.  
    if ($request_filename ~* sw.js){
  23.  
    expires -1s;
  24.  
    }
  25.  
    expires 10m;
  26.  
     
  27.  
    }
  28.  
    # next static下的静态文件
  29.  
    location ~ ^/(/_next/static/) {
  30.  
    root /home/website/eco_website_pc/build;
  31.  
    if ($request_filename ~* sw.js){
  32.  
    expires -1s;
  33.  
    }
  34.  
    expires 10m;
  35.  
     
  36.  
    }
  37.  
    复制代码

静态文件配置好了就需要配置https证书了,因为我们这次项目是公司官网,证书我就自己去免费弄了一个,这里我使用的freessl上面提供的亚洲诚信的证书。在申请完ssl证书之后需要去域名提供商那里去配置TXT记录,我这里使用的是阿里云,在完成验证后,freessl将会下载证书,拿到该证书之后需要去配置nginx ssl证书,下面贴出我的完整配置:

  1.  
    server {
  2.  
    listen 80;
  3.  
    listen 443 ssl;
  4.  
    server_name wh-eco.com;
  5.  
    charset utf-8;
  6.  
    ssl_certificate /home/website/ssl/www/full_chain.pem;
  7.  
    ssl_certificate_key /home/website/ssl/www/private.key;
  8.  
    fastcgi_param HTTPS on;
  9.  
    fastcgi_param HTTP_SCHEME https;
  10.  
     
  11.  
    if ($scheme = http ) {
  12.  
    return 301 https://$host$request_uri;
  13.  
    }
  14.  
    access_log /var/log/nginx/www.wh-eco.com.access.log;
  15.  
    error_log /var/log/nginx/www.wh-eco.com.error.log;
  16.  
    location / {
  17.  
    proxy_pass http://127.0.0.1:xxxx; #保密 0.0
  18.  
    proxy_set_header Host $host;
  19.  
    #proxy_redirect off;
  20.  
    proxy_set_header REMOTE-HOST $remote_addr;
  21.  
    # 网站可能后期会使用websocket 特次升级请求协议
  22.  
    proxy_http_version 1.1;
  23.  
    proxy_set_header Upgrade $http_upgrade;
  24.  
    proxy_set_header Connection "upgrade";
  25.  
    proxy_set_header X-Real-IP $remote_addr;
  26.  
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  27.  
    proxy_connect_timeout 60;
  28.  
    proxy_read_timeout 600;
  29.  
    proxy_send_timeout 600;
  30.  
    }
  31.  
    # 网站根目录文件
  32.  
    location ~ ^/(robots.txt|humans.txt|favicon.ico|sw.js|baidu_verify_7Kj6tQjI3v.html) {
  33.  
    root /home/website/eco_website_pc/static/;
  34.  
    if ($request_filename ~* sw.js){
  35.  
    expires -1s;
  36.  
    }
  37.  
    expires 10m;
  38.  
     
  39.  
    }
  40.  
    # static下的文件
  41.  
    location ^~ /static/ {
  42.  
    alias /home/website/eco_website_pc/static/;
  43.  
    if ($request_filename ~* sw.js){
  44.  
    expires -1s;
  45.  
    }
  46.  
    expires 10m;
  47.  
     
  48.  
    }
  49.  
    # next pages页面下的脚本
  50.  
    location ~ ^/(/_next/v1/) {
  51.  
    alias /home/website/eco_website_pc/build/bundles/;
  52.  
    if ($request_filename ~* sw.js){
  53.  
    expires -1s;
  54.  
    }
  55.  
    expires 10m;
  56.  
     
  57.  
    }
  58.  
    # next static下的静态文件
  59.  
    location ~ ^/(/_next/static/) {
  60.  
    root /home/website/eco_website_pc/build;
  61.  
    if ($request_filename ~* sw.js){
  62.  
    expires -1s;
  63.  
    }
  64.  
    expires 10m;
  65.  
     
  66.  
    }
  67.  
    error_page 500 502 503 504 = /error.html;
  68.  
    error_page 404 = /notfound.html;
  69.  
    location = /error.html {
  70.  
    root /home;
  71.  
    }
  72.  
    location = /notfound.html{
  73.  
    root /home;
  74.  
    }
  75.  
    }
  76.  
    复制代码

至于gzip你可以根据你要求来做配置,贴一个我的示例配置:

  1.  
    gzip on;
  2.  
    gzip_comp_level 6;
  3.  
    gzip_vary on;
  4.  
    gzip_types
  5.  
    application/atom+xml
  6.  
    application/javascript
  7.  
    application/json
  8.  
    application/rss+xml
  9.  
    application/vnd.ms-fontobject
  10.  
    application/x-font-ttf
  11.  
    application/x-web-app-manifest+json
  12.  
    application/xhtml+xml
  13.  
    application/xml
  14.  
    font/opentype
  15.  
    image/svg+xml
  16.  
    image/x-icon
  17.  
    image/jpeg
  18.  
    image/gif
  19.  
    image/png
  20.  
    text/css
  21.  
    text/plain
  22.  
    text/x-component;
  23.  
    复制代码

在完成nginx配置之后需要做的是以pm2方式启动整个应用

  1.  
    pm2 start ecosystem.config.js
  2.  
    复制代码

在运行完上述命令后,如果一切顺利,你就可以输入域名来访问你的应用了(假设你已经完成了域名解析工作)。

总结

一入前端深似海

漫思
原文地址:https://www.cnblogs.com/sexintercourse/p/14421789.html