前言
搭建react企业级项目框架 react+ts+eslint+react-router+react-redux+sass(包括全局变量自动引入)
工具版本
node v14.18.1
yarn v1.22.15
开始搭建
1.安装脚手架create-react-app
// yarn/npm安装
yarn global add create-react-app
npm install -g create-react-app
2.初始化项目
npx create-react-app my-app --template typescript
cd my-app // 进入新建项目
code . //使用vscode打开项目
// 删除多余文件,项目目录如下所示
3.安装eslint(使用airbnb规范)
3.1安装插件
npx install-peerdeps --dev eslint-config-airbnb
3.2配置 .eslintignore 忽略文件(根目录下创建此文件)
在该文件中配置不需要被 ESLint 检测的文件
/.git//.vscode/node_modules//dist/
4.安装Perttier
4.1安装插件
yarn add --dev prettier
yarn add --dev stylelint-config-prettier
yarn add --dev eslint-config-prettier
yarn add --dev eslint-plugin-prettier
4.2配置规则
根目录下新建.prettierrc.js文件
module.exports = { tabWidth: 4, printWidth: 140, singleQuote: true, endOfLine: 'auto'}
5.eslint+perittier规则文件配置(3-4完成后添加此文件)
根目录下新建.eslintrc.js
module.exports = { extends: ['airbnb', 'airbnb/hooks', 'prettier'], parser: '@typescript-eslint/parser', // ESLint默认使用esprima作为其解析器,也可以在配置文件中指定一个不同的解析器(它必须是一个Node模块,且它必须符合parserinterface) env: { // 要在配置文件里指定环境,使用env关键字指定你想启用的环境,并设置它们为true browser: true, node: true, mocha: true, es6: true, commonjs: true }, globals: { // 要在配置文件中配置全局变量,对于每个全局变量键,将对应的值设置为"writable"以允许重写变量,或"readonly"不允许重写变量 // "Babel":"writable", // "React":"writable" }, plugins: [ // 在配置文件里配置插件时,可以使用plugins关键字来存放插件名字的列表。插件名称可以省略eslint-plugin-前缀。 'react', 'jsx-a11y', 'react-hooks', 'import', 'prettier' ], rules: { indent: ['error', 4], semi: [ 'error', 'always', { omitLastInOneLineBlock: true } ], 'react/jsx-indent': ['error', 4], 'no-use-before-define': 'off', 'linebreak-style': ['off', 'windows'], 'react/jsx-filename-extension': [ 'error', { extensions: ['.js', '.jsx', '.ts', '.tsx'] } ], 'class-methods-use-this': 'off', 'import/extensions': 'off', 'import/no-unresolved': 'off' // 'prettier/prettier': [ // 'error', // { // trailingComma: 'es5', // singleQuote: true, // printWidth: 100, // tabWidth: 4, // semi: true, // endOfLine: 'auto', // }, // ], }};
配置完毕请重启编辑器vscode!!!
6.全局配置sass
6.1安装插件 请安装固定版本sass否则会出现兼容问题
yarn add --dev node-sass@4.14.1 sass-loader@6.0.1
yarn add customize-cra react-app-rewired sass-resources-loader
6.2配置文件
根目录下新建config-overrides.js文件,导入scss文件路径请自行修改
const { override, adjustStyleLoaders, addWebpackAlias } = require('customize-cra');const path = require('path');module.exports = override( // 配置指定文件为sass全局文件,可以不用导入就可以使用 adjustStyleLoaders((rule) => { if (rule.test.toString().includes('scss')) { rule.use.push({ loader: require.resolve('sass-resources-loader'), options: { resources: ['./src/assets/public.scss'] } }); } }), addWebpackAlias({ '@': path.resolve(__dirname, './src') }));
修改package.json文件
"scripts": { "start": "set PORT=8888&&react-app-rewired start", "build": "react-app-rewired build", "test": "react-app-rewired test", "eject": "react-app-rewired eject"},
public.scss文件
$defaultColor: red;
index.scss引用无需导入public.scss文件
.App{ background-color: $defaultColor;}
7.安装router
7.1安装插件
yarn add react-router-dom
yarn add --dev @types/react-router-dom // 解决ts类型报错
7.2创建router文件夹-index.ts文件
import React from 'react';const Home = React.lazy(() => import('../page/login'));const routes = [ { path: '/login', component: Home }];export default routes;
7.3修改App.tsx
import React, { Suspense } from 'react';import { BrowserRouter as Router, Route } from 'react-router-dom';import routes from './router';import './index.scss';function App() { return ( <div className="App"> <Suspense fallback={<div />}> <Router> <div> {routes.map((route) => ( <Route key={route.path} path={route.path} component={route.component} /> ))} </div> </Router> </Suspense> </div> );}export default App;
8.安装redux
8.1安装插件
yarn add redux react-redux
8.2配置
创建store文件夹-index.ts
import { createStore } from 'redux';import reducers from './reducers';import initState from './state';const store = createStore(reducers, initState);export default store;
state.ts文件
const initState = { card: { name: 'Jack' }, dialog: { status: false }};export default initState;
reducers文件夹-index.ts文件
import { combineReducers } from 'redux';import initState from '../state';function card(state: any = initState, action: any) { switch (action.type) { case 'CHANGE_NAME': return { name: action.name // 使用action携带的新name }; default: return state; // 没有匹配的action type,返回原来的state }}function dialog(state: any = initState, action: any) { switch (action.type) { case 'SHOW_DIALOG': return { status: true }; case 'CLOSE_DIALOG': return { status: false }; default: return state; // 没有匹配的action type,返回原来的state }}export default combineReducers({ card, dialog});
修改入口文件index.tsx
import React from 'react';import ReactDOM from 'react-dom';import { Provider } from 'react-redux';import App from './App';import store from './store';ReactDOM.render( <Provider store={store}> <React.StrictMode> <App /> </React.StrictMode> </Provider>, document.getElementById('root'));
login.tsx同目录下创建widthStore.ts
function mapStateToProps(state: any) { return state;}function mapDispatchToProps(dispatch: any) { return { changeName() { dispatch({ type: 'CHANGE_NAME', name: '葬爱' }); }, showDialog() { dispatch({ type: 'SHOW_DIALOG' }); } };}export { mapStateToProps, mapDispatchToProps };
修改login.tsx
import React from 'react';import { connect } from 'react-redux';import { mapStateToProps, mapDispatchToProps } from './widthStore';interface TypePorps { card: any; dialog: any; showDialog: Function;}function Login(props: TypePorps) { const { card, dialog, showDialog } = props; const { name } = card; const { status } = dialog; return ( <div className="login" onClick={() => showDialog()} aria-hidden="true"> login{name} {status ? 'true' : 'false'} </div> );}export default connect(mapStateToProps, mapDispatchToProps)(Login);