1

有时候过于依赖脚手架、各种库,会让我们的能力陷入一个瓶颈。本教程试图从零开始,记录一个项目的形成过程。

本教程涉及什么

涉及技术

  • React
  • Tailwind + SCSS
  • Git(Github)

预备知识

最好能有所了解。

基础工作

创建 Git 仓库

名称:bloggo-admin-frontend

链接:https://github.com/pluveto/bloggo-admin-frontend

创建项目目录

$ cd ~/proj

$ mkdir bloggo-admin-frontend

$ cd bloggo-admin-frontend

初始化项目


$ npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help init` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
package name: (bloggo-admin-frontend) 
version: (1.0.0) 
description: Frontend project for bloggo dashboard
entry point: (index.js) 
test command: 
git repository: https://github.com/pluveto/bloggo-admin-frontend
keywords: bloggo admin frontend tailwind react

license: (ISC) MIT
About to write to /root/proj/bloggo-admin-frontend/package.json:

{
  "name": "bloggo-admin-frontend",
  "version": "1.0.0",
  "description": "Frontend project for bloggo dashboard",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/pluveto/bloggo-admin-frontend.git"
  },
  "keywords": [
    "bloggo",
    "admin",
    "frontend",
    "tailwind",
    "react"
  ],
  "author": "pluveto",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/pluveto/bloggo-admin-frontend/issues"
  },
  "homepage": "https://github.com/pluveto/bloggo-admin-frontend#readme"
}


Is this OK? (yes) yes

安装依赖

react

核心依赖是 react

$ yarn add react
$ yarn add react-dom
$ yarn add react-router-dom
  • react-dom:提供 DOM 元素操作
  • react-router-dom:提供 DOM 的路由绑定

sass

$ yarn add node-sass --global

sass 是一个 css 预处理器,从而能让我们用 sass 或 scss 语法编写更工程化的样式代码。

webpack

$ yarn add webpack webpack-cli --dev
  • webpack:用于提供打包功能
  • webpack-cli:提供打包 CLI 命令

devServer

用于提供热重载

$ yarn add webpack-dev-server --dev

wepack loader

$ yarn add babel-loader html-loader style-loader css-loader sass-loader --dev

webpack plugin

用于生成 HTML5 文件并引入 webpack 包裹。

$ yarn add html-webpack-plugin --dev

babel

$ yarn add @babel/core @babel/preset-react @babel/preset-env --dev
  • babel:提供 ES6 到 ES5 的编译
  • babel-preset-react:提供 react 相关的 babel 插件
  • babel-preset-env:用于配置需要支持的平台和版本(比如 IE>=11,Chrome >64)

配置 babel

.babelrc 进行配置如下:

{
  "presets": [
    "@babel/preset-react"
  ]
}

从而支持 JSX 语法

配置 webpack

webpack.config.js 配置如下:

const path = require("path");
const webpack = require("webpack");
const HTMLWebpackPlugin = require("html-webpack-plugin");

module.exports = {
    mode: "development",
    entry: "./index.js",
    output: {
        filename: "bundle.js",
        path: path.resolve("dist"),
        publicPath: "/",
    },
    module: {
        rules: [
            {
                test: /\.(js|jsx)$/,
                exclude: /node_modules/,
                use: "babel-loader"
            },
            {
                test: /\.html$/,
                use: "html-loader"
            },{
                test: /\.css$/,
                use: ["style-loader", "css-loader"],
            },
            {
                test: /\.scss$/,
                use: [
                    "style-loader",
                    "css-loader",
                    "sass-loader"
                ],
            },
        ],
    },
    plugins: [
        new HTMLWebpackPlugin({
            template: "index.html"
        }),
    ]
}

HTML 首页

index.html

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Hello, React!</title>
</head>
<body>
    <div id="root"></div>
</body>
</html>

入口文件

index.js

import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";

import App from "./App.js";
import "./styles.scss";

const appRouting = (
  <Router>
    <Switch>
      <Route exact path="/" component={App} />
    </Switch>
  </Router>
);
const root = document.querySelector("#root")
ReactDOM.render(appRouting, root);

App.js

import React from "react";

const App = () => {
   return <div className="hello">Hello, World!</div>;
};

export default App;

styles.scss

.hello {
    text-align: center;
}

检查

现在的项目依赖是这样的:

  "dependencies": {
    "react": "^17.0.2",
    "react-dom": "^17.0.2"
  },
  "devDependencies": {
    "@babel/core": "^7.14.8",
    "@babel/preset-env": "^7.14.8",
    "@babel/preset-react": "^7.14.5",
    "babel-loader": "^8.2.2",
    "css-loader": "^6.2.0",
    "html-loader": "^2.1.2",
    "html-webpack-plugin": "^5.3.2",
    "react-router-dom": "^5.2.0",
    "sass-loader": "^12.1.0",
    "style-loader": "^3.2.1",
    "webpack": "^5.47.1",
    "webpack-cli": "^4.7.2",
    "webpack-dev-server": "^3.11.2"
  }

增加常用脚本

package.json 增加:

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
+   "start": "webpack serve"
  },

运行

$ yarn start

效果如下:

image-20210730135201479

推送到 Github

配置忽略规则

.gitignore

# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# production
/build

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

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

推送代码

$ git init
$ git remote add origin https://github.com/pluveto/bloggo-admin-frontend.git
$ git add .
$ git status
On branch master

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)

        new file:   .babelrc
        new file:   .gitignore
        new file:   App.js
        new file:   index.html
        new file:   index.js
        new file:   package.json
        new file:   styles.scss
        new file:   webpack.config.js
$ git commit -m "feat: basic project"
$ git push --set-upstream origin master

2

现在,基本的项目有了。但是项目结构是完全平行的:

image-20210730135931073

因此我们需要设计一下项目结构。

新建分支

我们新建一个基于 master 的分支:

$ git branch v2

切换到新分支:

$ git checkout v2

重设结构

我比较喜欢相同功能的放在一起,而不是分层,因为那样不好快速跳转到对应的文件。

我们增加一个 如下目录:

  • src/:存放项目特有的代码
  • public/:存放静态文件
  • helpers/:存放工具组件

App.jsindex.jsstyles.scss 移动到 src/,并更新相关依赖。

登录页面

我们首先做登录页面

src/pages/login/LoginPage.jsx

import React from "react";

export default class LoginPage extends React.Component {
    render() {
        return (
            <div >
                Login Page
            </div>
        )
    }
}

增加路由

现在我们要登录页到路由表中。

允许 BrowserRouter

为了避免得到下面的响应:

Cannot GET /login

我们需要做如下设置

webpack.config.js

    output: {
        filename: "bundle.js",
        path: path.resolve("dist"),
        publicPath: "/", // HERE
    },
    devServer: {
        historyApiFallback: true // HERE
    },

首页

App.js 改成 HomePage.js,放到 src/pages/home:

import React from "react";
import { Link, Route, Switch } from "react-router-dom";
import LoginPage from "./LoginPage";

const Home = () => {
   return (
      <div>
 <p>Bloggo Admin</p>
         <Link to="/login"> 登录 </Link>
      </div>
   )
};

export default Home;

路由表

编辑 index.js

import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";

import HomePage from "./pages/login/HomePage.js";
import LoginPage from "./pages/login/LoginPage.js";
import "./styles.scss";

const appRouting = (
  <Router>
    <div className="sans-serif">
      <Route exact path="/" component={HomePage} />
      <Route path="/login" component={LoginPage} />
    </div>
  </Router>
);
const root = document.querySelector("#root")
ReactDOM.render(appRouting, root);

运行,并访问 `http://localhost:8080

image-20210730153324547

image-20210730153335521

两个页面均正常显示。

设计样式

增加 Tailwind 依赖

$ yarn add tailwindcss

// TODO: 集成 PostCSS,以及按需导出

添加 Tailwind 到项目

index.js 增加:

import "tailwindcss/tailwind.css"

这时并不会生效,你如果定位到那个文件,会看到:

tailwindcss/tailwind.css:

@tailwind base;

@tailwind components;

@tailwind utilities;

它用了自己的指令,这些指令由 PostCSS 提供支持

集成 PostCSS

$ yarn add postcss@latest autoprefixer@latest

postcss.config.js:

module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  }
}

tailwind.config.js

module.exports = {
    // 配置清除 (purge) 选项以删除任何未使用类,这样生成的文件尺寸最小
    purge: [
        './src/**/*.html',
        './src/**/*.js',
    ],
    darkMode: false, // or 'media' or 'class'
    theme: {
        extend: {},
    },
    variants: {},
    plugins: [],
}

安装 PostCSS 的 Webpack 插件:

yarn add postcss-loader --dev

webpack.config.js

// ...
module.exports = {
    mode: "development",
    entry: "./src/index.js",
    output: {
        filename: "bundle.js",
        path: path.resolve("dist"),
        publicPath: "/",
    },
    devServer: {
        historyApiFallback: true
    },
    module: {
        rules: [
        	// ...
        	{
                test: /\.css$/,
                use: [
                    "style-loader",
                    "css-loader",
+                   "postcss-loader"
                ],
            },
            {
                test: /\.scss$/,
                use: [
                    "style-loader",
                    "css-loader",
                    "sass-loader",
+                   "postcss-loader"
                ],
// ...

现在,Tailwind 可以正常使用了。

拥抱 jsx:

webpack.config.js

// ...
    resolve: {
      extensions: ['', '.js', '.jsx'],
    }
}