React 腳手架搭建過程記錄

最近項(xiàng)目中要使用 React 來開發(fā) web 應(yīng)用,這里記錄一下搭建 React web 腳手架的過程,方便后期自查。

一、CRA 創(chuàng)建官方腳手架

npx create-react-app my-app

create-react-app 腳手架中將 webpack + babel 封裝成了一個(gè)叫做 react-scripts 的庫,用來隱藏配置,開發(fā)人員可以快速上手。

但是如果要自定義 webpack 的配置,就必須使用 eject 將配置彈出,會(huì)導(dǎo)致我們關(guān)注到一些無關(guān)的配置,體驗(yàn)并不好,同時(shí)無法跟隨 react-scripts 的官方更新,所以我們通常會(huì)借助 craco 來優(yōu)化這個(gè)問題。

二、安裝 craco 方便自定義 webpack 配置

craco 全稱 Create React App Configuration Override,取首字母即組成了工具名稱。

craco 是為了無 eject 、可配置式的去修改 CRA 默認(rèn)提供的工程配置,這樣既能享受 CRA 帶來的便利和后續(xù)升級(jí),也能自己去自定義打包配置完成項(xiàng)目需要,一舉兩得。

1、安裝 @craco/craco
npm i @craco/craco -D
2、修改 package.json 中的 script 標(biāo)簽。

start/build/test 三個(gè)命令修改為 craco 方式:

"scripts": {
   -   "start": "react-scripts start",
   -   "build": "react-scripts build",
   -   "test": "react-scripts test",
   +   "start": "craco start",
   +   "build": "craco build",
   +   "test": "craco test",
}
3、在項(xiàng)目根目錄中創(chuàng)建 craco.config.js

下面是支持了 路徑@別名配置、postcss 集成 的配置示例:

const path = require("path")
const resolve = pathUrl => path.join(__dirname, pathUrl)
const pxToViewport = require("postcss-px-to-viewport")
const vw = pxToViewport({
    viewportWidth: 375
})

const WebpackBar = require("webpackbar")

module.exports = {
    webpack: {
        alias: {
            "@": resolve("src/"),
            "@common": resolve("src/common")
        },
        plugins: [new WebpackBar({ profile: true })]
    },

    style: {
        postcss: {
            mode: "extends",
            loaderOptions: {
                postcssOptions: {
                    ident: "postcss",
                    plugins: [vw]
                }
            }
        }
    }
}

craco 的基礎(chǔ)配置已經(jīng)完成,craco.config.js 配置文件結(jié)構(gòu),可以在 craco 官方的文檔中詳細(xì)查詢:Configuration Overview

關(guān)于 react-app-rewrited 的替代品 craco 及最佳實(shí)踐 可參考

三、支持修改環(huán)境變量 cross-env

為了支持添加不同網(wǎng)絡(luò)域名的環(huán)境變量,需要在 node 運(yùn)行時(shí)注入一些自定義的環(huán)境變量。

由于不同平臺(tái)注入環(huán)境變量有所差異,為了兼容 WindowsLinux 等不同平臺(tái),可以使用 cross-env 這個(gè)跨平臺(tái)設(shè)置和使用環(huán)境變量的腳本。

1、安裝 cross-env
npm install --save-dev cross-env
2、注入自定義變量
{
  "scripts": {
    "build": "cross-env REACT_APP_ENV=sit craco start"
  }
}
3、使用自定義變量
process.env.REACT_APP_ENV

四、支持 sass 樣式編寫

React 腳手架中已經(jīng)有了 sass 的配置(并沒有配置 less,如果想用 less,需要另外配置),因此只需要安裝 sass 的依賴包,就可以直接使用 sass了:

1、安裝 sass
npm i sass -D
2、css modules 解決樣式污染問題

React 腳手架已集成 CSS Modules ,可直接使用

步驟:

  • 改樣式文件名。從 xx.scss -> xx.module.scss (React腳手架中的約定,與普通 CSS 作區(qū)分)
  • 引入使用:
    • 組件中導(dǎo)入該樣式文件(注意語法)
import styles from './index.module.scss'
  • 通過 styles 對(duì)象訪問對(duì)象中的樣式名來設(shè)置樣式
<div className={styles.css類名}></div>

css類名是 index.module.scss 中定義的類名。

3、css modules 最佳實(shí)踐
  • 每個(gè)組件的根節(jié)點(diǎn)使用 CSSModules 形式的類名( 根元素的類名: root )
  • 其他所有的子節(jié)點(diǎn),都使用普通的 CSS 類名 :global

component.tsx

import styles from './index.module.scss'
const 組件 = () => {
  return (
    {/* (1) 根節(jié)點(diǎn)使用 CSSModules 形式的類名( 根元素的類名: `root` )*/}
    <div className={styles.root}>
      {/* (2) 所有子節(jié)點(diǎn),都使用普通的 CSS 類名*/}
        <h1 className="title">
        <span className="text">登錄</span>  
        <span>登錄</span>  
      </h1>
            <form className="login-form"></form>
    </div>
  )
}

index.module.scss

.root {
  display: 'block';
  position: 'absolute';
  // 此處,使用 global 包裹其他子節(jié)點(diǎn)的類名。此時(shí),這些類名就不會(huì)被處理,在 JSX 中使用時(shí),就可以用字符串形式的類名
  // 如果不加 :global ,所有類名就必須添加 styles.title 才可以
  :global {
    .title {
      .text {
      }
      span {
      }
    }
    .login-form { ... }
  }
}

參考:https://juejin.cn/post/7031556713329197093

五、eslint、prettier 配置

由于我們項(xiàng)目使用的 TS,所以我們需要配置的環(huán)境為:react + ts + hooks 的工程配置。

1、安裝 eslint 相關(guān)依賴

通過 CRA 腳手架創(chuàng)建的 TypeScript 項(xiàng)目,會(huì)默認(rèn)安裝一個(gè) eslint-config-react-app 的 eslint 配置庫,這個(gè)庫自帶了如下依賴:

  • @typescript-eslint/parser:將 TypeScript 轉(zhuǎn)換為 ESTree,使 eslint 可以識(shí)別
  • @typescript-eslint/eslint-plugin:TypeScript eslint 內(nèi)置的規(guī)則列表,可以直接繼承過來
  • eslint-plugin-react:校驗(yàn) React
  • eslint-plugin-react-hooks:根據(jù) Hooks API 校驗(yàn) Hooks 的使用
  • eslint-plugin-jsx-a11y:提供 jsx 元素可訪問性校驗(yàn)
  • eslint-plugin-import:此插件主要為了校驗(yàn) import/export 語法,防止錯(cuò)誤拼寫文件路徑以及導(dǎo)出名稱的問題

基本幫我們把 React 需要的 eslint 相關(guān)依賴都預(yù)先安裝好了,我們只需要再安裝 eslint 支持庫就可以:

npm install -save-dev eslint

這里要注意你項(xiàng)目中 eslint-config-react-app 需要依賴的 eslint 版本要求,然后安裝指定的版本。

2、安裝 prettier 相關(guān)依賴

我們可以借助 ESlint 來提高我們編碼的質(zhì)量,但是卻無法保證統(tǒng)一代碼風(fēng)格。一個(gè)統(tǒng)一的代碼風(fēng)格對(duì)于團(tuán)隊(duì)來說是很有價(jià)值的,所以為了達(dá)到目的,我們可以選擇使用 Prettier 在保存和提交代碼的時(shí)候,將代碼修改成統(tǒng)一的風(fēng)格。這樣做同時(shí)也降低了 Code Review 的成本。它不會(huì)代替 ESlint,所以需要和 ESlint 搭配使用。

prettier 主要需要安裝以下三個(gè)依賴:

npm install --save-dev prettier eslint-config-prettier eslint-plugin-prettier
  • prettier:格式化規(guī)則核心庫
  • eslint-config-prettier:禁用 ESLint 所有和 Prettier 產(chǎn)生沖突的規(guī)則
  • eslint-plugin-prettier:把 Prettier 應(yīng)用到 ESlint,配合 rules "prettier/prettier": "error" 實(shí)現(xiàn) ESlint 提醒
3、配置 eslint

在項(xiàng)目根目錄創(chuàng)建 .eslintrc.js 配置文件:

module.exports = {
    env: {
        node: true,
        browser: true,
        es2021: true,
        jest: true
    },
    extends: [ // 繼承已有規(guī)則,繼承按順序進(jìn)行覆蓋
        "eslint:recommended", // eslint 推薦的規(guī)則
        "plugin:react/recommended", // @eslint-plugin-react 的推薦規(guī)則
        "plugin:@typescript-eslint/recommended", // @typescript-eslint/eslint-plugin的推薦規(guī)則
        "plugin:prettier/recommended" // eslint-plugin-prettier 的推薦規(guī)則
    ],
    parser: "@typescript-eslint/parser", // 指定解析器
    parserOptions: {
        ecmaVersion: "latest", // 允許解析那個(gè)版本的特性
        sourceType: "module", // 允許使用 import
        ecmafeatures: {
            jsx: true // 允許對(duì)JSX進(jìn)行解析
        }
    },
    plugins: ["react", "react-hooks", "@typescript-eslint", "prettier"],
    settings: {
        react: {
            version: "detect" // 告訴eslint-plugin-react 自動(dòng)檢測 React的版本
        }
    },
    // 自定義規(guī)則
    rules: {
        // ///////////////////////// error
        // 禁止不必要的分號(hào)
        // "no-extra-semi": 1,
        // 禁止出現(xiàn)令人困惑的多行表達(dá)式
        "no-unexpected-multiline": 2,
        // 禁止在return、throw、continue 和 break語句之后出現(xiàn)不可達(dá)代碼
        /*
       function foo() {
       return true;
       console.log("done");//錯(cuò)誤
       }
       */
        "no-unreachable": 2,
        // 強(qiáng)制 typeof 表達(dá)式與有效的字符串進(jìn)行比較
        // typeof foo === "undefimed" 錯(cuò)誤
        "valid-typeof": 2,
        // 啟用嚴(yán)格模式
        strict: 2,
        // 不允許改變用const聲明的變量
        "no-const-assign": 2,
        // 禁止對(duì)全局變量賦值
        "no-global-assign": 2,
        // 禁止重復(fù)導(dǎo)入模塊
        "no-duplicate-imports": 2,

        // ///////////////////////// warning
        // 對(duì)于不符合 prettier 規(guī)則,eslint只提示警告
        "prettier/prettier": 1,
        // 不允許使用var
        "no-var": 1,
        // 禁止定義沒有被使用的變量
        "no-unused-vars": 0,
        "@typescript-eslint/no-unused-vars": 0,
        // 要求或禁止使用分號(hào)而不是 ASI(這個(gè)才是控制行尾部分號(hào)的,)
        semi: [1, "never"],
        // 禁止分號(hào)之前出現(xiàn)空格
        "semi-spacing": [1, { before: false, after: true }],
        // 禁止在字符串和注釋之外不規(guī)則的空白
        "no-irregular-whitespace": 1,
        // 強(qiáng)制使用一致的換行風(fēng)格
        // "linebreak-style": [1, "unix"],
        // 使用雙引號(hào) 允許es6的``
        quotes: [1, "double", { allowTemplateLiterals: true }],
        // 強(qiáng)制在代碼塊中開括號(hào)前和閉括號(hào)后有空格
        "block-spacing": [1, "always"],
        // 在代碼塊之前強(qiáng)制使用空格
        "space-before-blocks": 1,
        // 要求操作符周圍有空格
        "space-infix-ops": 1,
        // 一元操作符必須要有空格
        "space-unary-ops": 1,
        // 強(qiáng)制在注釋中 // 或 /* 使用一致的空格
        "spaced-comment": [1, "always", { exceptions: ["-"] }],
        // 強(qiáng)制關(guān)鍵字周圍空格的一致性
        "keyword-spacing": [1, { before: true, after: true }],
        // 強(qiáng)制在箭頭函數(shù)中 "xxx() => {}"
        "arrow-spacing": [1, { before: true, after: true }],
        // 在冒號(hào)后要加上空格
        "key-spacing": [1, { beforeColon: false }],
        // 如果一個(gè)變量不會(huì)被重新賦值,最好使用const進(jìn)行聲明。
        "prefer-const": 1,
        // 強(qiáng)制類型后面要有一個(gè)","
        // "flowtype/delimiter-dangle": [1, "only-multiline"],
        // 在 : 后強(qiáng)制加空格
        // "flowtype/space-after-type-colon": [1, "always"],
        // 在 | & 符號(hào)中,強(qiáng)制加空格
        // "flowtype/union-intersection-spacing": [1, "always"],

        // ///////////////////////// off
        // 盡可能使用`===`
        eqeqeq: 0,
        // 禁止不必要的布爾轉(zhuǎn)換
        "no-extra-boolean-cast": 0,
        "no-useless-computed-key": 0,
        // 禁止不必要的括號(hào) (a * b) + c;
        "no-extra-parens": 0,
        // 允許使用行內(nèi)樣式
        "react-native/no-inline-styles": 0,
        // 禁止空格和 tab 的混合縮進(jìn)
        "no-mixed-spaces-and-tabs": 0,
        "react/jsx-filename-extension": 0,

        // react配置
        // 強(qiáng)制組件方法順序
        "react/sort-comp": [2],
        // 結(jié)束標(biāo)簽,組件省略閉合標(biāo)簽,html不省略閉合標(biāo)簽
        "react/self-closing-comp": [
            2,
            {
                component: true,
                html: false
            }
        ],
        "@typescript-eslint/ban-ts-comment": "off",

        "@typescript-eslint/no-var-requires": 0,
        "@typescript-eslint/ban-types": [
            "error",
            {
                extendDefaults: true,
                types: {
                    "{}": false
                }
            }
        ],

        // 檢查 Hook 的規(guī)則,不允許在if for里面使用
        "react-hooks/rules-of-hooks": [2],
        // 檢查 effect 的依賴
        "react-hooks/exhaustive-deps": [2],
        // suppress errors for missing 'import React' in files
        "react/react-in-jsx-scope": "off",
        "@typescript-eslint/no-non-null-assertion": "off",
        "no-empty-function": "off",
        "@typescript-eslint/no-explicit-any": "off",
        "@typescript-eslint/no-empty-function": "off",
        "@angular-eslint/no-empty-lifecycle-method": "off"
    }
}
4、配置 prettier

根目錄創(chuàng)建 .prettierrc.js 配置文件:

module.exports = {
    // 字符串是否使用單引號(hào),默認(rèn)為false,使用雙引號(hào)
    singleQuote: false,
    // 在jsx中把'>' 是否單獨(dú)放一行
    jsxBracketSameLine: true,
    // 根據(jù)顯示樣式?jīng)Q定 html 要不要折行
    htmlWhitespaceSensitivity: "css",
    // 換行符使用 crlf/lf/auto
    endOfLine: "auto",
    // 一個(gè)tab代表幾個(gè)空格數(shù),默認(rèn)為80
    tabWidth: 4,
    // 是否使用tab進(jìn)行縮進(jìn),默認(rèn)為false,表示用空格進(jìn)行縮減
    useTabs: false,
    // 換行字符串閾值
    printWidth: 80,
    // 句末加分號(hào),默認(rèn)為true
    semi: false,
    // 是否使用尾逗號(hào),有三個(gè)可選值"<none|es5|all>"
    trailingComma: "none",
    // 對(duì)象大括號(hào)直接是否有空格,默認(rèn)為true,效果:{ foo: bar }
    bracketSpacing: true,
    // (x) => {} 是否要有小括號(hào)
    arrowParens: "avoid",
    // 是否要注釋來決定是否格式化代碼
    requirePragma: false,
    // 是否要換行
    proseWrap: "preserve",
  };
5、配置 VS Code 編輯器 (如果需要保存時(shí)自動(dòng)格式化可以配置)
  • 在 VS Code 商店中尋找并安裝插件 ESlint,Prettier
ESlint.png
Prettier.png
  • 在項(xiàng)目根目錄創(chuàng)建 .vscode 文件夾,然后創(chuàng)建 settings.json 文件,填充如下內(nèi)容:
{
    "editor.defaultFormatter": "esbenp.prettier-vscode", // 默認(rèn)的格式化插件prettier
    "editor.formatOnType": true, // 輸完一行后自動(dòng)格式化
    "editor.formatOnSave": true, // 保存時(shí)格式化
    "editor.codeActionsOnSave": {
        "source.fixAll.eslint": true // 保存時(shí)使用 eslint fix 命令進(jìn)行格式化對(duì)應(yīng)文件
    },
    "eslint.run": "onSave",
    "eslint.validate": [
        "javascript",
        "javascriptreact",
        "typescriptreact",
        "html",
        "vue"
    ]
}

這樣當(dāng)我們?cè)诒4嫖募臅r(shí)候,就會(huì)自動(dòng)優(yōu)化文件格式了。

如果團(tuán)隊(duì)合作,VSCode 配置文件可以上傳到 git 倉庫,這樣大家都能共享一份配置,有助于代碼格式的統(tǒng)一。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容