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證照、代理服務。
- react 16.3
一個javascript ui庫
- next.js 6.0.1
一個輕量級的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
,這裡就隨便從我真實專案中抽取部分程式碼來作師範。
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
配置這塊可能需要下點功夫。一些常用的外掛像sass
、css
等, 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
打包出來的js
、css
、圖片檔案等,都將報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
打包出來的有三個資料夾:bundles
、dist
、static
,對於不知道原始碼的人來說,根本不知道實際請求檔案在哪一個資料夾。於是我就看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
複製程式碼
在執行完上述命令後,如果一切順利,你就可以輸入域名來訪問你的應用了(假設你已經完成了域名解析工作)。
總結
一入前端深似海