Vite 2.x + React 项目模板搭建 ,并接入 前端工程规范

1,979 阅读7分钟

Vite 2.x + React 项目模板搭建

最近开始准备毕设了,所以尝试使用 vite +react 创建项目模板,并接入 前端工程规范&记录搭建过程中遇到的问题

  • react-router-dom 使用 v6
  • cssmodules 配置类名规则
  • 集成eslint + prettier + husky 等 进行代码风格和commit 校验

模板

  • 地址:

github

  • 使用模板
npx degit Galileo01/vite-react-template#main project_name

cd my-project

或者 clone 项目

vite

使用vite 初始化项目

# npm create vite project_name -- --template react  
npm create vite vite-react-app -- --template react

注意:两个——

  • 框架 选择react

  • 模板(variant)选择 react-ts

然后进入目录 安装依赖

npm i 

尝试运行

npm run dev

设置路径别名

为了能 require 和 __dirname 能被 ts 正确识别,需要安装 @types/node

npm  i @types/node -D
// vite.config.ts
const { resolve } = require('path');
...
resolve: {
    // 配置路径 别名
    alias: [
      {
        find: '@',
        replacement: resolve(__dirname, 'src'),
      },
    ],
  },

设置之后可以方便的 通过 ‘@/assets/xx’ 导入

Exp:

import logo from '@/logo.svg';

CSS Module 设置类名前缀

css module 默认对 *.moudle.css 的文件开启

  • 安装 less

    Vite 默认提供了对 .scss, .sass, .less, .styl.stylus 文件的内置支持,不需要安装特殊插件,但是需要我们安装预处理器依赖

    npm i less -D
    
  • 设置生成的 类名格式

配置:

css: {
    modules: {
      // 类名 前缀
      generateScopedName: 'vite_demo__[folder]__[local]___[hash:base64:5]',
    },
}

Ps:这里使用[folder]-文件夹名而不是[name]-文件名 作为前缀的原因是:个人更喜欢把组件的文件分别命名为index.module.lessindex.tsx

如果使用[name]-文件名的话 所有组件的类名前缀都会是“index.mudule_xxxx”,使用[folder]-文件夹名 可以很好的达到效果

具体看个人的命名爱好

更具体的配置见postcss-modules

react-router-dom 使用v6

安装 react-router-dom

npm i react-router-dom -S

V6 版 用ts 重写,拥抱react 新特性,变化比较大,API 比之前更好用,但也有部分开发者反应 不好用(对class 组件)

具体的改动见 react-router 官方升级指南 或者阅读 什么,React Router已经到V6了 ??

较大的改动

  • Routes 代替 Switch
  • Route prop : element 代替 component
  • useNavigate 代替 useHistory
  • 新增 useRoutes hooks,可以以新的方式注册和渲染路由,更有利于 阅读和维护

ps: useRoutes 有个 注意点:useRoutes hooks 不能和Router 组件在同一个层级中渲染,至少要高一个层级,否则就会报错

原因是:使用 useRoutes 创建routes 需要在Router 的上下文中

Exp:

// App.tsx 以下是错误的写法,会报错
import * as React from 'react';

import { useRoutes, Routes } from 'react-router-dom';

import Test from './components/test';
import routes from './router';

function App() {
  const element = useRoutes(routes);
  return (
    <div className="App">
      <Test tag="666" />
      <Routes>{element}</Routes>
    </div>
  );
}

export default App;

useRoutes() may be used only in the context of a Router component:

Stackoverflow 链接

正确的上下文结构如下

// file: src/router.ts
import React, { Suspense } from 'react';

import type { RouteObject } from 'react-router-dom';
import Index from './pages/index/index';

// React.lazy 配合 import() 实现懒加载
const About = React.lazy(() => import('./pages/about'));

const routes: RouteObject[] = [
  {
    path: '/',
    element: <Index />,
  },
  {
    path: '/about',
    element: (
      <Suspense fallback={<span>loading component</span>}>
        <About />
      </Suspense>
    ),
  },
];

export default routes;
// file: src/App.tsx
import * as React from 'react';

import { useRoutes } from 'react-router-dom';

import Test from './components/test';
import routes from './router';

function App() {
  const element = useRoutes(routes);
  return (
    <div className="App">
      <Test tag="666" />
      {element}
    </div>
  );
}

export default App;
// file: src/main.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router } from 'react-router-dom';

import './index.css';
import App from './App';

ReactDOM.render(
  <React.StrictMode>
    <Router>
      <App />
    </Router>
  </React.StrictMode>,
  document.getElementById('root')
);
// useRoutes hooks 不能和Router 组件在同一个层级中渲染

使用 eslint 检测代码规范

安装 eslint 并初始化

npm i eslint -D
npx eslint --init 

npx eslint --init 命令会打开 可交互的命令行,根据选择生成**.eslintrc.js**

  • How would you like to use ESLint - To check syntax and find problems (若 选择 enforce code style ,会让eslint 进行代码格式化)

  • What type of modules does your project use? -Javascript modules (import/export )

  • Which framework does your project use? - React

  • Does your project use TypeScript?-yes

  • Where does your code run?-browser

  • What format do you want your config file to be in?-JavaScript

  • Would you like to install them now with npm? › Yes

这样选择后 会安装 eslint-plugin-react、 @typescript-eslint/eslint-plugin 、@typescript-eslint/parser

创建.eslintignore 文件

*.sh 
node_modules 
*.md 
*.woff 
*.ttf 
.vscode 
.idea 
dist 
/public 
/docs 
.husky 
.local 
/bin
.eslintrc.js
*.config.js
vite.config.ts

安装 eslint-plugin-react-hooks 对 hooks 进行校验

npm i eslint-plugin-react-hooks -D

在.eslintrc.js 进行个性化的设置,开启对 hooks 依赖的检测

// file: .eslintrc.js 
{
  "plugins": [
    // ...
    "react-hooks"
  ],
  "rules": {
    // ...
    "react-hooks/rules-of-hooks": "error",
    "react-hooks/exhaustive-deps": "warn"
  }
}

Ghx0Zn.png

使用 airbnb 的规则 对 react 进行校验

安装 eslint-config-airbnb

eslint-config-airbnb 对 eslint-plugin-import、eslint-plugin-react、eslint-plugin-jsx-a11y 有依赖 ,所以一起安装

npm i eslint-config-airbnb eslint-plugin-import eslint-plugin-react eslint-plugin-jsx-a11y  -D

安装 eslint-config-airbnb-typescript

npm i  eslint-config-airbnb-typescript -D

配置

根据 官方文档进行配置:文档链接

// file: .eslintrc.js 
...
extends: [
    // react
    // - 删除  eslint:recommended 
    'plugin:react/recommended',
    // airbnb + airbnb 推荐的 ts 规范
    'airbnb',
    'airbnb-typescript',
    //- 删除  ts推荐配置
    //- 删除 'plugin:@typescript-eslint/recommended',
  ],
  parserOptions: {
    project: './tsconfig.json', // + 新增 parserOptions 配置
  },
  rules: {
    // 覆盖 eslint-config-airbnb里的配置
    //  允许 在ts、tsx 中书写 jsx
    'react/jsx-filename-extension': [
      2,
      { extensions: ['.js', '.jsx', '.ts', '.tsx'] },
    ], 
      // 修改 对于 函数式组件 声明方式(箭头函数 or 函数声明)的 校验
     'react/function-component-definition': [
      'error',
      {
        namedComponents: ['arrow-function', 'function-declaration'],
        unnamedComponents: ['arrow-function'],
      },
    ], 
  },
    ...

使用 prettier 对代码风格进行统一

安装

npm i prettier eslint-config-prettier eslint-plugin-prettier -D

创建 .prettierrc.js文件

// file: .prettierrc.js
module.exports = {
  semi: false,
  tabWidth: 2,
  singleQuote: true,
  trailingComma: 'es5',
}

配置

eslint-config-prettier新版本更新之后,只需要写一个 "prettier" 即可,无需多言指令

// file: .eslintrc.js 
...
{
  extends:[
    // 解决 eslint 和 prettier 的冲突 , 此项配置必须在最后
    'prettier',
  ]
}

使用husky +lint-staged+commitlint,通过钩子函数,对代码和commit message 进行校验、格式化操作

安装husky 并初始化

  • 安装
npm i husky -D
  • 在package.json中添加脚本 prepare :

"scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "preview": "vite preview",
    "prepare": "husky install",
  ...
  },
  • 初始化husky
npm run prepare # 初始化husky,将 git hooks 钩子交由,husky执行

使用 lint-staged 对暂存的代码进行规范校验和格式化

lint-staged 可以对暂存的文件进行操作

  • 安装
npm i lint-staged -D
  • 创建 .lintstagedrc.js 文件,配置不同类型的文件需要执行的操作
// file: .lintstagedrc.js
module.exports = {
  '*.{js,jsx,ts,tsx}': ['prettier --write .', 'eslint  --fix'],
  '*.md': ['prettier --write'],
}
  • 添加 pre-commit 钩子:执行 npx lint-staged 命令
npx husky add .husky/pre-commit "npx lint-staged"

使用 commitlint 对提交信息进行校验

  • 安装
npm i commitlint @commitlint/config-conventional -D
  • 创建 commitlint.config.js 文件
// file: commitlint.config.js
module.exports = {
  extends: ['@commitlint/config-conventional'],
}
  • 添加commit-msg 钩子:执行 “npx --no-install commitlint --edit "$1"” 命令
npx husky add .husky/commit-msg 'npx --no-install commitlint --edit "$1"'

@commitlint/config-conventional 这是一个规范配置,标识采用什么规范来执行消息校验, 这个默认是Angular的提交规范

类型描述
build编译相关的修改,例如发布版本、对项目构建或者依赖的改动
chore其他修改, 比如改变构建流程、或者增加依赖库、工具等
ci持续集成修改
docs文档修改
feat新特性、新功能
fix修改bug
perf优化相关,比如提升性能、体验
refactor代码重构
revert回滚到上一个版本
style代码格式修改, 注意不是 css 修改
test测试用例修改

配置成功后,每次 git commit -m "xxx" 都会 进行响应的校验

GhxQQz

其他

antd 按需引入

安装 vite-plugin-imp

npm i vite-plugin-imp -D

vite.config.ts 中配置

//file: vite.config.ts
import vitePluginImp from 'vite-plugin-imp'
...
export default defineConfig({
plugins: [
    react(),
    // 按需 引入 antd
    vitePluginImp({
      libList: [
        {
          libName: 'antd',
          style: (name) => `antd/lib/${name}/style/index.less`,
        },
      ],
    }),
  ],
  ...
	css: {
    preprocessorOptions: {
      less: {
        // 支持内联 JavaScript ,不开启 antd 的按需引入 会报错
        javascriptEnabled: true,
      },
    },
  },
    ...
})
  

解决 Vite 里路径别名引用没有类型等提示

配置了路径别名后,虽然可以让vite 正确的导入资源,但是发现在书写 路径的时候vscode 并不能正确的提示,导入之后对应的ts类型也无法识别

配置tsconfig.json

需要配置 baseUrl,和 path

"compilerOptions":{
  "jsx": "react-jsx",
    "baseUrl": ".",
    "paths": {
      "@/*": [
        "src/*"
      ]
    }
}

修复

正常情况下,这样就行不需要额外的配置。但我这里使用了airbnb的 代码风格,airbnb 对文件扩展有校验,在使用路径别进行导入时 ,遇到不能正确推导文件扩展名的问题,查询一番后 最简单的方法就是关闭 import/extensions 规则,希望后续有待修复(2022.03.04)

rules:{
   // 关闭 对文件扩展名的 校验
    'import/extensions': 'off',
}