全端網站設計範例:前端專案建立

本篇為「全端網站架構」中的前端範例及細節。

所需工具及準備

  • Node.js,此專案使用 v14.15.0
  • Yarn,或 npm,此專案指令將使用 yarn

建立 React 專案

我們將安裝 React 搭配 TypeScript,並且手動安裝套件(沒有使用如 create-react-app 這類的 toolchains)。這裡的建立過程大多參考一篇 Medium 的文章(參考資料 1)

並且搭配 Babel, Webpack, Prettier, ESLint。

首先,初始化

1
2
3
4
5
6
7
$ mkdir client && cd client # 在根目錄建立 client 資料夾
$ yarn init # 初始化,產生 package.json
$ yarn add react react-dom # 新增 npm 套件
$ yarn add -D typescript @types/react @types/react-dom # 新增 typescript 相關套件,-D 表示安裝在 devDependencies
$ mkdir src && mkdir src/public # 新增 src, src/public 資料夾
$ touch src/index.tsx # 建立程式進入點
$ touch src/public/index.html # 建立網頁進入點

再來建立網頁進入點,剛剛透過指令建立的 src/public/index.html 就是將要丟給瀏覽器的目錄文件,程式碼如下

1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>React App</title>
</head>
<body>
<div id="root"></div>
</body>
</html>

比較需要注意的是 body 下的 div,id 必須為 "root",也就是接下來用 ReactDOM 去渲染時為了取得此 dom 所需要的 id。

然後,建立程式進入點。接下來便要實際把 React 的程式碼加上,編輯 src/index.tsx

1
2
3
4
import React from 'react';
import ReactDOM from 'react-dom';

ReactDOM.render(<div>Hello, World!</div>, document.getElementById('root'));

設定 TypeScript

1
$ touch tsconfig.json # 新增 TypeScript 設定檔

編輯此設定檔

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"compilerOptions": {
"target": "es5",
"module": "esnext",
"moduleResolution": "node",
"lib": ["dom", "esnext"],
"jsx": "react-jsx",
"noEmit": true,
"isolatedModules": true,
"esModuleInterop": true,
"strict": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"allowJs": true,
"checkJs": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "build"]
}

設定 Sass,讓 Styling 更簡潔

安裝 Sass

1
$ yarn add -D sass sass-loader

這裡我們採用 .scss 的格式來撰寫 style,新增 scss 檔案

1
$ touch src/index.scss

編輯其內容

1
2
3
div {
color: red; // 測試樣式是否應用成功,顯示的文字是否為紅色
}

設定 Babel,編譯程式碼

安裝 Babel

1
2
3
4
$ yarn add -D \
@babel/core @babel/cli @babel/preset-env \
@babel/preset-typescript @babel/preset-react \
@babel/runtime @babel/plugin-transform-runtime

其中 @babel/core @babel/cli @babel/preset-env 是 Babel 基本必需,而 @babel/runtime @babel/plugin-transform-runtime 則是使用 async-await 會需要加上的。

再來是設定 Babel,透過 .babelrc.json 這支設定檔

1
$ touch .babelrc.json

編輯其內容

1
2
3
4
{
"presets": ["@babel/preset-env", ["@babel/preset-react", { "runtime": "automatic" }], "@babel/preset-typescript"],
"plugins": [["@babel/plugin-transform-runtime", { "regenerator": true }]]
}

設定 Webpack,打包程式碼

安裝 Webpack

1
2
3
4
$ yarn add -D \
webpack webpack-cli webpack-dev-server \
style-loader css-loader babel-loader \
html-webpack-plugin clean-webpack-plugin

再來是設定 Webpack,新增 webpack.config.js 這支檔案,作為 Webpack 的進入點

1
$ touch webpack.config.js

編輯其內容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

module.exports = {
entry: path.resolve(__dirname, 'src', 'index.tsx'),
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
},
mode: 'development',
module: {
rules: [
{
test: /\.[jt]sx?$/,
use: ['babel-loader'],
exclude: /node_modules/,
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
{
test: /\.scss$/,
use: ['style-loader', 'css-loader', 'sass-loader'],
},
{
test: /\.(?:ico|gif|png|jpg|jpeg)$/i,
type: 'asset/resource',
},
{
test: /\.(woff(2)?|eot|ttf|otf|svg|)$/,
type: 'asset/inline',
},
],
},
resolve: {
extensions: ['.tsx', '.ts', '.js', '.jsx'],
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, './src/public/index.html'),
}),
new CleanWebpackPlugin(),
],
devServer: {
port: 3000,
hot: 'only',
compress: true,
open: true,
},
};

設定 Prettier,美化程式碼

安裝 Prettier

1
$ yarn add -D prettier eslint-config-prettier eslint-plugin-prettier

其中 eslint 開頭的套件是為了讓接下來要安裝的 eslint 能和 prettier 一起使用(不會因為 prettier 的設定而使得 linter conflict)。
再來,新增 prettier 的設定檔。

1
$ touch .prettierrc.json

編輯其內容

1
2
3
4
5
6
7
{
"trailingComma": "es5",
"tabWidth": 2,
"semi": true,
"singleQuote": true,
"printWidth": 120
}

設定 ESLint,檢查程式碼

安裝 ESLint

1
2
3
4
5
6
7
8
$ yarn add -D \
eslint \
eslint-plugin-react \
eslint-plugin-react-hooks \
eslint-plugin-jsx-a11y \
eslint-plugin-import \
@typescript-eslint/parser \
@typescript-eslint/eslint-plugin

再來新增 ESLint 的設定檔

1
$ touch .eslintrc.json

編輯其內容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
{
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 2020,
"sourceType": "module",
"ecmaFeatures": { "jsx": true }
},
"settings": {
"react": { "version": "detect" }
},
"extends": [
"plugin:react/recommended",
"plugin:react-hooks/recommended",
"plugin:@typescript-eslint/recommended",
"plugin:import/errors",
"plugin:import/warnings",
"plugin:import/typescript",
"plugin:jsx-a11y/recommended",
"plugin:prettier/recommended"
],
"rules": {
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": ["error"],
"@typescript-eslint/no-var-requires": "off",
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn",
"react/prop-types": "off",
"react/jsx-uses-react": "off",
"react/react-in-jsx-scope": "off",
"@typescript-eslint/explicit-module-boundary-types": "off"
}
}

新增要執行的 Scripts

回到 package.json 設定檔,新增 scripts,加上執行專案的指令

1
2
3
4
5
6
7
8
{
"scripts": {
"start": "webpack serve --config webpack.config.js --env env=development",
"build": "webpack --config webpack.config.js --env env=production",
"lint": "eslint \"./src/**/*.{js,jsx,ts,tsx,json}\"",
"format": "prettier --write \"./src/**/*.{js,jsx,ts,tsx,json,css,scss,md}\""
}
}

.gitignore

將一般需要忽略的檔案寫入 .gitignore

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# dependencies
/node_modules

# production
/dist

# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local

npm-debug.log*
yarn-debug.log*
yarn-error.log*

執行專案

1
2
$ yarn start # 啟動 dev server,並在 http://localhost:3000 看到成果
$ yarn build # 建置,在 dist 資料夾中看到成果

參考資料

  1. A Complete Guideline to Creating a Modern React App With TypeScript From Scratch

修訂記錄

2022-03-20 - 新增 SCSS 相關設定