全端網站設計範例:前端專案建立
本篇為「全端網站架構」中的前端範例及細節。
所需工具及準備
建立 React 專案
我們將安裝 React 搭配 TypeScript,並且手動安裝套件(沒有使用如 create-react-app 這類的 toolchains)。這裡的建立過程大多參考一篇 Medium 的文章(參考資料 1)。
並且搭配 Babel, Webpack, Prettier, ESLint。
首先,初始化
$ 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 就是將要丟給瀏覽器的目錄文件,程式碼如下
<!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
import React from 'react';
import ReactDOM from 'react-dom';
ReactDOM.render(<div>Hello, World!</div>, document.getElementById('root'));
設定 TypeScript
$ touch tsconfig.json # 新增 TypeScript 設定檔
編輯此設定檔
{
"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,
$ yarn add -D sass sass-loader
這裡我們採用 .scss 的格式來撰寫 style,新增 scss 檔案
$ touch src/index.scss
編輯其內容
div {
color: red; // 測試樣式是否應用成功,顯示的文字是否為紅色
}
設定 Babel,編譯程式碼
安裝 Babel
$ 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 這支設定檔
$ touch .babelrc.json
編輯其內容
{
"presets": ["@babel/preset-env", ["@babel/preset-react", { "runtime": "automatic" }], "@babel/preset-typescript"],
"plugins": [["@babel/plugin-transform-runtime", { "regenerator": true }]]
}
設定 Webpack,打包程式碼
安裝 Webpack
$ 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 的進入點
$ touch webpack.config.js
編輯其內容
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
$ yarn add -D prettier eslint-config-prettier eslint-plugin-prettier
其中 eslint 開頭的套件是為了讓接下來要安裝的 eslint 能和 prettier 一起使用(不會因為 prettier 的設定而使得 linter conflict)。 再來,新增 prettier 的設定檔。
$ touch .prettierrc.json
編輯其內容
{
"trailingComma": "es5",
"tabWidth": 2,
"semi": true,
"singleQuote": true,
"printWidth": 120
}
設定 ESLint,檢查程式碼
安裝 ESLint
$ 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 的設定檔
$ touch .eslintrc.json
編輯其內容
{
"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,加上執行專案的指令
{
"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 中
# 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*
執行專案
$ yarn start # 啟動 dev server,並在 http://localhost:3000 看到成果
$ yarn build # 建置,在 dist 資料夾中看到成果
參考資料
修訂記錄
2022-03-20 - 新增 SCSS 相關設定