node.js更新
這時小黑窗說我node.js的版本不對,不能跑Create Next App
所以先更新node.js
npm cache clean -fsudo npm install -g nsudon [version.number]
當然使用nvm也沒有問題
nvm install <version>nvm use <version>// 查詢已安裝node.js
nvm ls// 查詢可安裝node.js
nvm ls-remote
安裝Next及TypeScript
執行CNA及安裝TypeScript
yarn create next-app 專案名yarn add -D typescript @types/react @types/react-dom @types/node
安裝完之後把檔名是.js
的jsx檔改成.tsx
後執行yarn dev
此時Next會自動幫你建好tsconfig.json
整個就是很自動化
另外在 package.json 中新增 prettier 設定
...
"prettier": {
"singleQuote": true,
"jsxSingleQuote": true,
"semi": false,
"trailingComma": "none"
}
Eslint ( Airbnb )
yarn add -D eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-config-airbnb eslint-plugin-jsx-a11y eslint-plugin-react eslint-plugin-react-hooks eslint-plugin-import
這裡原本想使用eslint-config-prettier / eslint-plugin-prettier
來解決prettier跟eslint打架的問題
然後eslint-config-prettier
會跟eslint-config-airbnb
打架…讓airbnb
某些警告收不到
後來放棄用eslint-prettier
還要在根目錄新增 .eslintrc.js 後 eslint 才會正式上工
// .eslintrc.jsmodule.exports = {
root: true,
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint', 'react'],
parserOptions: {
ecmaFeatures: { jsx: true }
},
env: {
browser: true,
node: true
},
extends: [
'airbnb',
'eslint:recommended',
'plugin:@typescript-eslint/eslint-recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react/recommended',
'plugin:react-hooks/recommended',
'plugin:jsx-a11y/recommended'
],
rules: {
semi: 'off',
'no-unused-vars': 'off',
'react/react-in-jsx-scope': 'off',
'react/jsx-props-no-spreading': 'off',
'react/jsx-one-expression-per-line': 'off',
'@typescript-eslint/ban-ts-comment': 'off',
'function-paren-newline': 'off',
'implicit-arrow-linebreak': 'off',
'jsx-quotes': ['error', 'prefer-single'],
'comma-dangle': ['error', 'never'],
'react/jsx-filename-extension': [
1,
{
extensions: ['.jsx', '.ts', '.tsx']
}
],
'import/extensions': [
'error',
'ignorePackages',
{
js: 'never',
jsx: 'never',
ts: 'never',
tsx: 'never',
mjs: 'never'
}
]
},
settings: {
react: {
version: 'detect'
},
'import/extensions': ['.js', '.jsx', '.ts', '.tsx'],
'import/parsers': {
'@typescript-eslint/parser': ['.ts', '.tsx']
},
'import/resolver': {
node: {
extensions: ['.js', '.jsx', '.ts', '.tsx']
}
}
}
}
設定當然是可以依自己喜好調整
server內使用ES6語法
架設簡易的server可以地使用express
// server.js
const express = require('express')
const path = require('path')const app = express()
const port = process.env.PORT || 3000app.use(express.static(path.join(__dirname, 'dist')))
app.get('/*', (req, res) => {
res.sendFile(path.join(__dirname, 'dist', 'index.html'))
})
app.listen(port, () => {
// do ...
})
然後使用指令
node server.js
然而這種寫法依現在的設定eslint會跳出警告
- eslint 警告:使用 require 而不是 import
- 改成 import 編譯時會問 import 是什麼
- 如果寫成 server.ts時node無法直接執行 .ts檔
這裡改來改去常有顧此失彼的問題,最後是使用 babel 編譯檔案後再執行的方式解決
yarn add -D @babel/cli @babel/core @babel/plugin-proposal-class-properties @babel/plugin-proposal-object-rest-spread @babel/preset-env @babel/preset-typescript @types/express @babel/plugin-proposal-do-expressionsyarn add express nodemon
新增 server/index.ts
import next from 'next'
import express, { Request, Response } from 'express'const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()app.prepare().then(() => {
const server = express()server.get('/apitest/side-menu', (_req: Request, res: Response) =>
res.json({ testArray: [1, 2, 3] })
)server.get('*', (req: Request, res: Response) => handle(req, res))const PORT = process.env.PORT || 3000server.listen(PORT, () => {
console.log(`> Ready on port ${PORT}`)
})
})
這裡因為是讓 next 用的所以有些許調整
根目錄新增 .babelrc
// .babelrc
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"esmodules": true
}
}
],
"@babel/typescript",
"next/babel"
],
"plugins": [
"@babel/plugin-proposal-class-properties",
"@babel/plugin-proposal-object-rest-spread",
"@babel/plugin-proposal-do-expressions"
]
}
根目錄新增 nodemon.json
{
"watch": ["src", "server"],
"ext": "ts",
"ignore": ["src/**/*.spec.ts", "dist", "node_modules"],
"exec": "yarn build && node dist/index.js"
}
更改 package.json 的 script
"scripts": {
... "dev": "nodemon",
"build": "babel server --out-dir dist --extensions \".ts, .tsx\" --source-maps inline",
},
這樣 express 部分也可以用 ES6 + TypeScript 撰寫
當初看到 next.js 可以寫客製化的 server,以及本身 /pages/api 下可以寫 api
我曾經想試著把前後端都寫在 next.js 的專案下,如此一來只要用一個 port 就能跑前後端了,所以我才想把 server 部分也改成 TypeScript,提升後端的嚴謹度
沒想到在build時出現一個問題:使用getStaticProps
方法做預先渲染時,因為 server 還沒啟動所以無法抓取資料 ( 正常程序是要先build完專案才執行專案 ),導致 build 失敗
解決方法有 3 個
1.另外開專案寫api
這樣就是前後端分開來弄,當然完全沒有問題
2.另外開小黑窗
用next dev
先run server,原本的小黑窗再跑build
可以是可以…但感覺…
3.使用getServerSideProps
跟getStaticProps
不同的是getServerSideProps
不會在build時先抓資料,而會在每次請求頁面時才抓取資料,所以可以避開這個問題
不過 getServerSideProps
的目的是抓取可能變動的資料進行 SSR,如果連資料不會變動而可以先 build 的頁面也讓getServerSideProps
處理,那似乎違反了設計的目的了。
3種方法當然還是1最好,不過我也覺得3以測試目的隨便寫東西來用時也算方便的解決方案。
到最後不方便專案寫api給專案用,達成一個自給自足的前提下,server 部分似乎寫個十行左右的程式碼就夠了,在這種前提下,server 還有需要用 TypeScript 嗎?我個人是抱持否定態度的 (殺雞焉用牛刀)。
參考: