react 项目搭建与配置(1)

216 阅读4分钟

react 项目开发文档记录

1 初始化

使用模板安装 npx create-react-app my-app --template typescript

eslint+prettier 配置

npm install --save-dev eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin 
prettier  eslint-config-prettier  eslint-plugin-prettier

.eslintrc.js

module.exports = {
  parser: '@typescript-eslint/parser',
  plugins: ['@typescript-eslint', 'prettier'],
  extends: ['plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended', 'prettier'],
  rules: {
    'prettier/prettier': 'error',
  },
};

.prettierrc

{
  "printWidth": 120,
  "tabWidth": 2,
  "useTabs": false,
  "semi": true,
  "singleQuote": true,
  "trailingComma": "es5",
  "bracketSpacing": true,
  "jsxBracketSameLine": false,
  "arrowParens": "avoid"
}

配置 VS Code

{
  "editor.formatOnSave": true,
  "[javascript]": {
    "editor.formatOnSave": false
  },
  "[typescript]": {
    "editor.formatOnSave": false
  },
  "eslint.alwaysShowStatus": true,
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true
  }
}

2 路由配置

安装依赖 yarn add react-router-dom @types/react-router-dom

在 index.ts 文件中引入 BrowserRouter 并将其包裹在 App 组件周围,以便在整个应用程序中使用路由。

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { BrowserRouter } from 'react-router-dom';

const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
root.render(
  <React.StrictMode>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </React.StrictMode>
);

路由嵌套 多层级路
app.ts 这里面定义了两个页面,登录和首页 ,首页里面有 Menu 和 Head 以及 Content。以及子路由 User实现常用的管理后台布局

    import { Routes, Route } from 'react-router-dom';
    import Login from './views/login';
    import Layout from '@/views/layout';
    import User from './views/users';
    import UserInfo from '@/views/users/iuser-info';
    import 'antd/dist/reset.css';
    import './assets/style/index.scss';
    function App() {
      return (
        <Routes>
          <Route path="" element={<Login />}></Route>
          <Route path="/login" element={<Login />}></Route>
          <Route path="/*" element={<Layout />}>
            <Route path="users" element={<User />}></Route>
          </Route>
        </Routes>
      );
    }
    export default App;

layout中

import LayoutHead from './components/layout-head';
import LayoutMenu from './components/layout-menu';
import { Outlet } from 'react-router-dom';
import './index.scss';
function Layout() {
  return (
    <div className="layout">
      <LayoutHead></LayoutHead>
      <LayoutMenu></LayoutMenu>
      <div className="layout-content">
        <Outlet></Outlet>
        {/* Outlet 作为路由出口 等同vue中的 <reoute-view></reoute-view> 
        渲染我们定义的User ,如果想在 User中配置子级路由,同样的嵌套操作*/} 
      </div>
    </div>
  );
}
export default Layout;

动态路由

3 sass

npm install node-sass sass-loader --save-dev

注意 node 版本兼容,直接新建 .scss 文件,引入即可使用

4 antd

yarn add antd

 //app.tsx 中
 import 'antd/dist/reset.css';
 import { Button } from 'antd';
 
 <div>
     <Button type="primary">Button</Button>
  </div>

5 redux reduxjs/toolkit

yarn add react-redux
yarn add @reduxjs/toolkit 使用 Redux Toolkit 的切片(slice)是一种更简单、更直观的方式来管理 Redux 状态

新建 src/store/reducer/user.ts 创建切片,将切片暴露出去

import { createSlice } from '@reduxjs/toolkit';
export const userSlice = createSlice({
  name: 'user',
  initialState: {
    name: '张三',
    age: 22,
  },
  reducers: {
    setUser(state, action) {
      //state 当前的值 是一个代理对象。不能直接做赋值处理
      Object.assign(state, action.payload);
    },
  },
});
//切片对象会自动生成action
export const { setUser } = userSlice.actions; //更新方法暴露外部直接调用无需设置action.type

src/store/index.ts

import { configureStore } from '@reduxjs/toolkit';
import { userSlice } from './reducer/user';
const store = configureStore({
  reducer: userSlice.reducer,
});
export { store };

src/index.ts

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { BrowserRouter } from 'react-router-dom';
import { Provider } from 'react-redux';
import { store } from './store';
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
root.render(
  <React.StrictMode>
    <BrowserRouter>
      <Provider store={store}>
        <App />
      </Provider>
    </BrowserRouter>
  </React.StrictMode>
);

tsx 页面

import { useDispatch, useSelector } from 'react-redux';
import { setUser } from '../../../store/reducer/user';
function LayoutConent() {
  const dispatch = useDispatch();
  const store: any = useSelector(state => state);
  const updateUser = () => {
    dispatch(setUser({ name: '李四' }));
  };
  return (
    <div className="layout-content">
      <p>{store.user.name}</p>
      {/* 在store reducer 中配置了key(user) */}
      <button onClick={updateUser}>更新用户</button>
    </div>
  );
}
export default LayoutConent;

redux 全局状态持久化配置 yarn add redux-persiststore/index.ts 中添加配置

import { configureStore } from '@reduxjs/toolkit';
import { persistStore, persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
import { userSlice } from './reducer/user';
const store = configureStore({
  reducer: {
    user: persistReducer({ key: 'user', storage }, userSlice.reducer),
  },
  //A non-serializable value was detected in an action, in the path: `register` 需要关闭序列化
  middleware: getDefaultMiddleware =>
    getDefaultMiddleware({
      serializableCheck: false,
    }),
});
const persistor = persistStore(store);
export { store, persistor };

src/index.ts

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { BrowserRouter } from 'react-router-dom';
import { Provider } from 'react-redux';
import { store, persistor } from './store';
import { PersistGate } from 'redux-persist/integration/react';
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
root.render(
  <BrowserRouter>
    <Provider store={store}>
      <PersistGate loading={null} persistor={persistor}>
        <App />
      </PersistGate>
    </Provider>
  </BrowserRouter>
);

页面中使用方式不变

6 配置 @ 路径

安装依赖 yarn add @craco/craco 根路径下创建 craco.config.js

/* eslint-disable */  
//eslint 文件禁止以 commonjs 方式引入,这里加下文件忽略
const path = require("path")
module.exports = {
  webpack:{
    alias:{
      "@":path.resolve(__dirname,"src")
    }
  }
}

package.json 文件

    "scripts": {
        "start": "craco start",
        "build": "craco build",
        "test": "craco test",
        "eject": "react-scripts eject"
     },

tsconfig.ts 件添加配置

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

7 动态路由配置

我们定义一组数据模拟接口返回的权限 src/router/index.ts

interface Irouter {
  name: string;
  path: string;
  children?: Array<Irouter> | null;
  component: string;
}
export const routerMap: Array<Irouter> = [
  {
    name: '用户中心',
    path: 'users',
    component: 'users/index',
    children: [
      {
        name: '新增用户',
        path: 'createUser',
        component: 'users/user-info',
        children: [],
      },
    ],
  },
  {
    name: '公司管理',
    path: 'company',
    component: 'company/index',
    children: [],
  },
  {
    name: '账户管理',
    path: 'home',
    component: 'company/index',
    children: [],
  },
];

app.ts 中进行路由懒加载 Suspense 组件的作用是当路由未加载完成时可以做前置处理,(最开始使用时将这个组件放在app 组件的最外层,导致路由跳转出现闪屏问题,原来是这个组件配置出了问题)

import React, { lazy, Suspense } from 'react';
import { Routes, Route } from 'react-router-dom';
import Login from './views/login';
import Layout from '@/views/layout';  //包括左侧 Menu, 头部Head 以及主要路由页面 content
import { routerMap } from '@/router';
import 'antd/dist/reset.css';
import './assets/style/index.scss';

const lazyLoad = (moduleName: string) => {
  const Module = lazy(() => import(`@/views/${moduleName}`));
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <Module />
    </Suspense>
  );
};
function App() {
  return (
    <Routes>
      <Route path="" element={<Login />}></Route>
      <Route path="/login" element={<Login />}></Route>
      <Route path="/*" element={<Layout />}>
        {routerMap.map(item => {
          return <Route key={item.path} path={item.path} element={lazyLoad(item.component)}></Route>;
        })}
      </Route>
    </Routes>
  );
}
export default App;

8 axios 配置 在之前 vue3 项目搭建已经有相应的配置,可以直接拿来使用 juejin.cn/post/721699…

结语

下篇会进行项目的打包配置,以及常用组件的封装等,目前公司所有业务线都已经停掉了,得抽出时间来把之前欠的东西补出来了!