美文网首页
React 脚手架搭建过程记录

React 脚手架搭建过程记录

作者: peaktan | 来源:发表于2023-05-18 15:00 被阅读0次

    最近项目中要使用 React 来开发 web 应用,这里记录一下搭建 React web 脚手架的过程,方便后期自查。

    一、CRA 创建官方脚手架

    npx create-react-app my-app
    

    create-react-app 脚手架中将 webpack + babel 封装成了一个叫做 react-scripts 的库,用来隐藏配置,开发人员可以快速上手。

    但是如果要自定义 webpack 的配置,就必须使用 eject 将配置弹出,会导致我们关注到一些无关的配置,体验并不好,同时无法跟随 react-scripts 的官方更新,所以我们通常会借助 craco 来优化这个问题。

    二、安装 craco 方便自定义 webpack 配置

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

    craco 是为了无 eject 、可配置式的去修改 CRA 默认提供的工程配置,这样既能享受 CRA 带来的便利和后续升级,也能自己去自定义打包配置完成项目需要,一举两得。

    1、安装 @craco/craco
    npm i @craco/craco -D
    
    2、修改 package.json 中的 script 标签。

    start/build/test 三个命令修改为 craco 方式:

    "scripts": {
       -   "start": "react-scripts start",
       -   "build": "react-scripts build",
       -   "test": "react-scripts test",
       +   "start": "craco start",
       +   "build": "craco build",
       +   "test": "craco test",
    }
    
    3、在项目根目录中创建 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 的基础配置已经完成,craco.config.js 配置文件结构,可以在 craco 官方的文档中详细查询:Configuration Overview

    关于 react-app-rewrited 的替代品 craco 及最佳实践 可参考

    三、支持修改环境变量 cross-env

    为了支持添加不同网络域名的环境变量,需要在 node 运行时注入一些自定义的环境变量。

    由于不同平台注入环境变量有所差异,为了兼容 WindowsLinux 等不同平台,可以使用 cross-env 这个跨平台设置和使用环境变量的脚本。

    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 脚手架中已经有了 sass 的配置(并没有配置 less,如果想用 less,需要另外配置),因此只需要安装 sass 的依赖包,就可以直接使用 sass了:

    1、安装 sass
    npm i sass -D
    
    2、css modules 解决样式污染问题

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

    步骤:

    • 改样式文件名。从 xx.scss -> xx.module.scss (React脚手架中的约定,与普通 CSS 作区分)
    • 引入使用:
      • 组件中导入该样式文件(注意语法)
    import styles from './index.module.scss'
    
    • 通过 styles 对象访问对象中的样式名来设置样式
    <div className={styles.css类名}></div>
    

    css类名是 index.module.scss 中定义的类名。

    3、css modules 最佳实践
    • 每个组件的根节点使用 CSSModules 形式的类名( 根元素的类名: root )
    • 其他所有的子节点,都使用普通的 CSS 类名 :global

    component.tsx

    import styles from './index.module.scss'
    const 组件 = () => {
      return (
        {/* (1) 根节点使用 CSSModules 形式的类名( 根元素的类名: `root` )*/}
        <div className={styles.root}>
          {/* (2) 所有子节点,都使用普通的 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 包裹其他子节点的类名。此时,这些类名就不会被处理,在 JSX 中使用时,就可以用字符串形式的类名
      // 如果不加 :global ,所有类名就必须添加 styles.title 才可以
      :global {
        .title {
          .text {
          }
          span {
          }
        }
        .login-form { ... }
      }
    }
    

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

    五、eslint、prettier 配置

    由于我们项目使用的 TS,所以我们需要配置的环境为:react + ts + hooks 的工程配置。

    1、安装 eslint 相关依赖

    通过 CRA 脚手架创建的 TypeScript 项目,会默认安装一个 eslint-config-react-app 的 eslint 配置库,这个库自带了如下依赖:

    • @typescript-eslint/parser:将 TypeScript 转换为 ESTree,使 eslint 可以识别
    • @typescript-eslint/eslint-plugin:TypeScript eslint 内置的规则列表,可以直接继承过来
    • eslint-plugin-react:校验 React
    • eslint-plugin-react-hooks:根据 Hooks API 校验 Hooks 的使用
    • eslint-plugin-jsx-a11y:提供 jsx 元素可访问性校验
    • eslint-plugin-import:此插件主要为了校验 import/export 语法,防止错误拼写文件路径以及导出名称的问题

    基本帮我们把 React 需要的 eslint 相关依赖都预先安装好了,我们只需要再安装 eslint 支持库就可以:

    npm install -save-dev eslint
    

    这里要注意你项目中 eslint-config-react-app 需要依赖的 eslint 版本要求,然后安装指定的版本。

    2、安装 prettier 相关依赖

    我们可以借助 ESlint 来提高我们编码的质量,但是却无法保证统一代码风格。一个统一的代码风格对于团队来说是很有价值的,所以为了达到目的,我们可以选择使用 Prettier 在保存和提交代码的时候,将代码修改成统一的风格。这样做同时也降低了 Code Review 的成本。它不会代替 ESlint,所以需要和 ESlint 搭配使用。

    prettier 主要需要安装以下三个依赖:

    npm install --save-dev prettier eslint-config-prettier eslint-plugin-prettier
    
    • prettier:格式化规则核心库
    • eslint-config-prettier:禁用 ESLint 所有和 Prettier 产生冲突的规则
    • eslint-plugin-prettier:把 Prettier 应用到 ESlint,配合 rules "prettier/prettier": "error" 实现 ESlint 提醒
    3、配置 eslint

    在项目根目录创建 .eslintrc.js 配置文件:

    module.exports = {
        env: {
            node: true,
            browser: true,
            es2021: true,
            jest: true
        },
        extends: [ // 继承已有规则,继承按顺序进行覆盖
            "eslint:recommended", // eslint 推荐的规则
            "plugin:react/recommended", // @eslint-plugin-react 的推荐规则
            "plugin:@typescript-eslint/recommended", // @typescript-eslint/eslint-plugin的推荐规则
            "plugin:prettier/recommended" // eslint-plugin-prettier 的推荐规则
        ],
        parser: "@typescript-eslint/parser", // 指定解析器
        parserOptions: {
            ecmaVersion: "latest", // 允许解析那个版本的特性
            sourceType: "module", // 允许使用 import
            ecmafeatures: {
                jsx: true // 允许对JSX进行解析
            }
        },
        plugins: ["react", "react-hooks", "@typescript-eslint", "prettier"],
        settings: {
            react: {
                version: "detect" // 告诉eslint-plugin-react 自动检测 React的版本
            }
        },
        // 自定义规则
        rules: {
            // ///////////////////////// error
            // 禁止不必要的分号
            // "no-extra-semi": 1,
            // 禁止出现令人困惑的多行表达式
            "no-unexpected-multiline": 2,
            // 禁止在return、throw、continue 和 break语句之后出现不可达代码
            /*
           function foo() {
           return true;
           console.log("done");//错误
           }
           */
            "no-unreachable": 2,
            // 强制 typeof 表达式与有效的字符串进行比较
            // typeof foo === "undefimed" 错误
            "valid-typeof": 2,
            // 启用严格模式
            strict: 2,
            // 不允许改变用const声明的变量
            "no-const-assign": 2,
            // 禁止对全局变量赋值
            "no-global-assign": 2,
            // 禁止重复导入模块
            "no-duplicate-imports": 2,
    
            // ///////////////////////// warning
            // 对于不符合 prettier 规则,eslint只提示警告
            "prettier/prettier": 1,
            // 不允许使用var
            "no-var": 1,
            // 禁止定义没有被使用的变量
            "no-unused-vars": 0,
            "@typescript-eslint/no-unused-vars": 0,
            // 要求或禁止使用分号而不是 ASI(这个才是控制行尾部分号的,)
            semi: [1, "never"],
            // 禁止分号之前出现空格
            "semi-spacing": [1, { before: false, after: true }],
            // 禁止在字符串和注释之外不规则的空白
            "no-irregular-whitespace": 1,
            // 强制使用一致的换行风格
            // "linebreak-style": [1, "unix"],
            // 使用双引号 允许es6的``
            quotes: [1, "double", { allowTemplateLiterals: true }],
            // 强制在代码块中开括号前和闭括号后有空格
            "block-spacing": [1, "always"],
            // 在代码块之前强制使用空格
            "space-before-blocks": 1,
            // 要求操作符周围有空格
            "space-infix-ops": 1,
            // 一元操作符必须要有空格
            "space-unary-ops": 1,
            // 强制在注释中 // 或 /* 使用一致的空格
            "spaced-comment": [1, "always", { exceptions: ["-"] }],
            // 强制关键字周围空格的一致性
            "keyword-spacing": [1, { before: true, after: true }],
            // 强制在箭头函数中 "xxx() => {}"
            "arrow-spacing": [1, { before: true, after: true }],
            // 在冒号后要加上空格
            "key-spacing": [1, { beforeColon: false }],
            // 如果一个变量不会被重新赋值,最好使用const进行声明。
            "prefer-const": 1,
            // 强制类型后面要有一个","
            // "flowtype/delimiter-dangle": [1, "only-multiline"],
            // 在 : 后强制加空格
            // "flowtype/space-after-type-colon": [1, "always"],
            // 在 | & 符号中,强制加空格
            // "flowtype/union-intersection-spacing": [1, "always"],
    
            // ///////////////////////// off
            // 尽可能使用`===`
            eqeqeq: 0,
            // 禁止不必要的布尔转换
            "no-extra-boolean-cast": 0,
            "no-useless-computed-key": 0,
            // 禁止不必要的括号 (a * b) + c;
            "no-extra-parens": 0,
            // 允许使用行内样式
            "react-native/no-inline-styles": 0,
            // 禁止空格和 tab 的混合缩进
            "no-mixed-spaces-and-tabs": 0,
            "react/jsx-filename-extension": 0,
    
            // react配置
            // 强制组件方法顺序
            "react/sort-comp": [2],
            // 结束标签,组件省略闭合标签,html不省略闭合标签
            "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 的规则,不允许在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

    根目录创建 .prettierrc.js 配置文件:

    module.exports = {
        // 字符串是否使用单引号,默认为false,使用双引号
        singleQuote: false,
        // 在jsx中把'>' 是否单独放一行
        jsxBracketSameLine: true,
        // 根据显示样式决定 html 要不要折行
        htmlWhitespaceSensitivity: "css",
        // 换行符使用 crlf/lf/auto
        endOfLine: "auto",
        // 一个tab代表几个空格数,默认为80
        tabWidth: 4,
        // 是否使用tab进行缩进,默认为false,表示用空格进行缩减
        useTabs: false,
        // 换行字符串阈值
        printWidth: 80,
        // 句末加分号,默认为true
        semi: false,
        // 是否使用尾逗号,有三个可选值"<none|es5|all>"
        trailingComma: "none",
        // 对象大括号直接是否有空格,默认为true,效果:{ foo: bar }
        bracketSpacing: true,
        // (x) => {} 是否要有小括号
        arrowParens: "avoid",
        // 是否要注释来决定是否格式化代码
        requirePragma: false,
        // 是否要换行
        proseWrap: "preserve",
      };
    
    5、配置 VS Code 编辑器 (如果需要保存时自动格式化可以配置)
    • 在 VS Code 商店中寻找并安装插件 ESlint,Prettier
    ESlint.png Prettier.png
    • 在项目根目录创建 .vscode 文件夹,然后创建 settings.json 文件,填充如下内容:
    {
        "editor.defaultFormatter": "esbenp.prettier-vscode", // 默认的格式化插件prettier
        "editor.formatOnType": true, // 输完一行后自动格式化
        "editor.formatOnSave": true, // 保存时格式化
        "editor.codeActionsOnSave": {
            "source.fixAll.eslint": true // 保存时使用 eslint fix 命令进行格式化对应文件
        },
        "eslint.run": "onSave",
        "eslint.validate": [
            "javascript",
            "javascriptreact",
            "typescriptreact",
            "html",
            "vue"
        ]
    }
    

    这样当我们在保存文件的时候,就会自动优化文件格式了。

    如果团队合作,VSCode 配置文件可以上传到 git 仓库,这样大家都能共享一份配置,有助于代码格式的统一。

    相关文章

      网友评论

          本文标题:React 脚手架搭建过程记录

          本文链接:https://www.haomeiwen.com/subject/qcyisdtx.html