使用next.js完成從開發到部署

next.js 是一個很是棒的輕量級的react同構框架,使用它能夠快速的開發出基於服務端渲染的react應用。在next.js 官網推薦的是使用now來部署應用,可是對於國內用戶或者說是有特殊需求的用戶來講,部署到自定義服務器也許是大多數人但願的。藉着近期公司官網改版,順便分享下本身從開發到部署過程當中所經歷的點點滴滴。javascript

依稀還記得第一次使用next.js 是在去年(2017年),那個時候使用的是next.js 2.x版本,react仍是15版本,一年過去,如今react已經發展到16版本,而next.js 已經發展到6.0版本了,迭代速度瞠目結舌,在使用新版本的過程當中也是遇到很多的坑。css

用到的技術

先說下此次用到了哪些技術,下面列舉了項目中主要用到的技術或工具庫。html

由express原班人馬開發的下一代web框架,用來提供web服務。前端

是一個高性能的HTTP和反向代理服務器,也是一個IMAP/POP3/SMTP服務器(摘自百度百科),由俄羅斯人開發。用來提供靜態文件服務、https證書、代理服務。java

一個javascript ui庫node

一個輕量級的react同構應用框架react

由螞蟻金服開發的基於react的一套中後臺產品組件庫webpack

基於react的動畫解決方案nginx

判斷組件是否在當前可視區的react 組件git

一個帶有負載均衡功能的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,這裏就隨便從我真實項目中抽取部分代碼來做師範。

export default class HomePage extends React.Component {
  static async getInitialProps({ req, pathname }) {
    const data = await fetch(`${ctx}/api/projects/common/list`).then(res => res.json())
    .then(dt => dt)
    .catch(err => {
      return {
        success: false,
        message: err.message
      }
    })
    return { pathname,data };
  }

  render() {
    const { pathname, data } = this.props;
    return (
      <div> <Head> <title>首頁-易科捷(武漢)生態科技有限公司</title> </Head> <div>Welcome to next.js!</div> {/*這裏省略代碼*/} </div>
    );
  }
}

複製代碼

若是你的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啓動入口文件,下面貼出實例代碼:

const Koa = require('koa')
const next = require('next')
const Router = require('koa-router')

const port = parseInt(process.env.PORT, 10) || 3000
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()

app.prepare()
  .then(() => {
    const server = new Koa()
    const router = new Router()
	// 首頁
    router.get('/', async ctx => {
      await app.render(ctx.req, ctx.res, '/', ctx.query)
      ctx.respond = false
    })
	// 關於
    router.get('/about', async ctx => {
      await app.render(ctx.req, ctx.res, '/about', ctx.query)
      ctx.respond = false
    })
	// 產品
    router.get('/products/:id', async ctx => {
      const {id} = ctx.params
      await app.render(ctx.req, ctx.res, `/products/${id}`, ctx.query)
      ctx.respond = false
    })
	// 案例
    router.get('/case', async ctx => {
      await app.render(ctx.req, ctx.res, '/case', ctx.query)
      ctx.respond = false
    })
	// 聯繫咱們
    router.get('/contact', async ctx => {
      await app.render(ctx.req, ctx.res, '/contact', ctx.query)
      ctx.respond = false
    })
	// 詳情
    router.get('/view/:type/:id', async ctx => {
      const {id, type} = ctx.params
      await app.render(ctx.req, ctx.res, `/view`, {id, type})
      ctx.respond = false
    })
    // 若是沒有配置nginx作靜態文件服務,下面代碼請務必開啓
   /* router.get('*', async ctx => { await handle(ctx.req, ctx.res) ctx.respond = false })*/
    // 防止出現控制檯報404錯誤
    server.use(async (ctx, next) => {
      ctx.res.statusCode = 200
      await next()
    })
    server.use(router.routes())
    server.listen(port, () => {
      console.log(`> Ready on http://localhost:${port}`)
    })
  })

複製代碼

通常的靜態文件、gzip壓縮無需交給nodejs來作,我的一直認爲專業的事交給專業的人。這裏將該項任務轉移給nginx,特別注意上面實例代碼中我註釋的部分代碼,若果你沒有使用nginx來作靜態文件服務,請務必開啓,不然像next.js打包出來的jscss、圖片文件等,都將報404

next.js生產打包階段打包出來的js文件請求路徑中帶有版本號,而真實打包出來的文件夾卻沒有實際對應的目錄,也就是打包出來的是虛擬目錄,這裏若是使用nginx就須要特別注意。好在next.js提供配置項來修改build id,如下是個人真實代碼:

// next.config.js
module.exports = {
  generateBuildId: async () => {
    // For example get the latest git commit hash here
    return 'v1'
  }
}
複製代碼

這樣打包出來的虛擬路徑大概是_next/v1/page/xxx.js,若是你使用cdn前綴,這裏有一點區別,可是版本號依然存在。

還有一個坑就是next.js打包出來的有三個文件夾:bundlesdiststatic,對於不知道源碼的人來講,根本不知道實際請求文件在哪個文件夾。因而我就看next.js源碼,發現其實找的是bundle文件下的page,源碼位置:L214

因此在配置nginx就須要使用別名。下面給出一段個人nginx真實配置代碼:

# 網站根目錄文件
        location ~ ^/(robots.txt|humans.txt|favicon.ico|sw.js|baidu_verify_7Kj6tQjI3v.html) {
            root /home/website/eco_website_pc/static/;
            if ($request_filename ~* sw.js){
                    expires -1s;
                }
            expires 10m;

	    }
        # static下的文件
        location ^~ /static/ {
            alias /home/website/eco_website_pc/static/;
            if ($request_filename ~* sw.js){
                    expires -1s;
                }
            expires 10m;

	    }
        # next pages頁面下的腳本
        location ~ ^/(/_next/v1/) {
            alias /home/website/eco_website_pc/build/bundles/;
            if ($request_filename ~* sw.js){
                    expires -1s;
                }
            expires 10m;

	    }
        # next static下的靜態文件
        location ~ ^/(/_next/static/) {
            root /home/website/eco_website_pc/build;
            if ($request_filename ~* sw.js){
                    expires -1s;
                }
            expires 10m;

	    }
複製代碼

靜態文件配置好了就須要配置https證書了,由於咱們此次項目是公司官網,證書我就本身去免費弄了一個,這裏我使用的freessl上面提供的亞洲誠信的證書。在申請完ssl證書以後須要去域名提供商那裏去配置TXT記錄,我這裏使用的是阿里雲,在完成驗證後,freessl將會下載證書,拿到該證書以後須要去配置nginx ssl證書,下面貼出個人完整配置:

server {
        listen       80;
        listen       443 ssl;
        server_name  wh-eco.com;
        charset utf-8;
        ssl_certificate /home/website/ssl/www/full_chain.pem;
        ssl_certificate_key /home/website/ssl/www/private.key;
        fastcgi_param   HTTPS               on;
        fastcgi_param   HTTP_SCHEME         https;

        if ($scheme = http ) {
          return 301 https://$host$request_uri;
        }
        access_log      /var/log/nginx/www.wh-eco.com.access.log;
        error_log       /var/log/nginx/www.wh-eco.com.error.log;
        location / {
            proxy_pass http://127.0.0.1:xxxx; #保密 0.0
            proxy_set_header Host $host;
            #proxy_redirect off;
            proxy_set_header    REMOTE-HOST $remote_addr;
        	# 網站可能後期會使用websocket 特次升級請求協議
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
	        proxy_connect_timeout 60;
            proxy_read_timeout 600;
            proxy_send_timeout 600;
        }
        # 網站根目錄文件
        location ~ ^/(robots.txt|humans.txt|favicon.ico|sw.js|baidu_verify_7Kj6tQjI3v.html) {
            root /home/website/eco_website_pc/static/;
            if ($request_filename ~* sw.js){
                    expires -1s;
                }
            expires 10m;

	    }
        # static下的文件
        location ^~ /static/ {
            alias /home/website/eco_website_pc/static/;
            if ($request_filename ~* sw.js){
                    expires -1s;
                }
            expires 10m;

	    }
        # next pages頁面下的腳本
        location ~ ^/(/_next/v1/) {
            alias /home/website/eco_website_pc/build/bundles/;
            if ($request_filename ~* sw.js){
                    expires -1s;
                }
            expires 10m;

	    }
        # next static下的靜態文件
        location ~ ^/(/_next/static/) {
            root /home/website/eco_website_pc/build;
            if ($request_filename ~* sw.js){
                    expires -1s;
                }
            expires 10m;

	    }
        error_page   500 502 503 504 = /error.html;
        error_page  404 = /notfound.html;
        location = /error.html {
                root   /home;
            }
        location = /notfound.html{
            root  /home;
        }
    }
複製代碼

至於gzip你能夠根據你要求來作配置,貼一個個人示例配置:

gzip  on;
    gzip_comp_level 6;
    gzip_vary on;
    gzip_types
        application/atom+xml
        application/javascript
        application/json
        application/rss+xml
        application/vnd.ms-fontobject
        application/x-font-ttf
        application/x-web-app-manifest+json
        application/xhtml+xml
        application/xml
        font/opentype
        image/svg+xml
        image/x-icon
        image/jpeg 
        image/gif 
        image/png
        text/css
        text/plain
        text/x-component;
複製代碼

在完成nginx配置以後須要作的是以pm2方式啓動整個應用

pm2 start ecosystem.config.js
複製代碼

在運行完上述命令後,若是一切順利,你就能夠輸入域名來訪問你的應用了(假設你已經完成了域名解析工做)。

總結

一入前端深似海


Github | 博客

相關文章
相關標籤/搜索