美文网首页
企业级前端工程化配置指南:vite + react + redu

企业级前端工程化配置指南:vite + react + redu

作者: 习惯水文的前端苏 | 来源:发表于2023-12-08 14:54 被阅读0次

    其实,我只在四年前完整的自己搭建过react项目,后来都基于umijs一键一把梭了。最近闲得慌,再加上最近一两年react项目开发的少,知识点感觉跟不上了,就想着复盘一下,随从项目搭建开始...

    你可以学到什么?

    • 如何使用 vite 搭建项目

    • 如何集成与使用 web-localstorage-plus

    • 如何集成与使用 react-router6

    • 如何集成与使用 redux

    • 如何集成与使用 ant-design

    • 如何封装与使用 umi-request

    • 如何借力 eslint 和 prettier 保证代码质量

    • 如何借力 commitlint 规范git提交信息

    1.按提示创建项目

    • 运行vite
    yarn create vite
    
    • 输入自定义的项目名称
    name: › your-project-name
    
    • 选择你想要的技术框架
    ? Select a framework: › - Use arrow-keys. Return to submit.
        Vanilla
        Vue
    ❯  React
        Preact
        Lit
        Svelte
        Others
    
    • 选择ts模板
    ? Select a variant: › - Use arrow-keys. Return to submit.
        TypeScript
    ❯  TypeScript + SWC
        JavaScript
        JavaScript + SWC
    

    5.按提示安装并运行项目

    Done. Now run:
      cd react-blob
      yarn
      yarn dev
    

    6.优化项目结构

    打开工程可以看到,vite默认了部分页面和配置,大多数其实都是无用的,需要进行下修剪。此处省略过程,有需要的可以(拉取源码)[https://github.com/supanpanCn/svite]查看

    2.配置vite.config.ts

    这里和vite+vue工程化配置是一样的,此处不再赘述,感兴趣的可直接跳转查看

    3.集成web-storage-plus

    对于需要使用到持久缓存的地方,localstorage是优选的方案,不过原生接口比较难用,而该npm包对其进行了二次封装,使其支持了命名空间、过期时间、监听变化、批量操作等特性,笔者的项目里一致都在用,文档看这里:传送门

    • 安装
    yarn add web-localstorage-plus@next
    
    • main.tsx中引入并初始化根存储
    import createStorage from 'web-localstorage-plus'
    
    createStorage({
        // 根命名空间
        rootName:'spp-storage',
        // 是否禁用原生localstorage
        noUseLocalStorage:false
    })
    
    
    • 在xxx.tsx文件中引入并使用
    import { useStorage } from "web-localstorage-plus";
    
    function Storage() {
      const storage = useStorage();
      storage.setItem("name", "spp", "author");
      storage.setItem("age", 29, "author");
      return <>learn storage</>;
    }
    
    export default Storage;
    
    • 存储结果如下
    image.png

    4.集成react-router

    • 安装依赖
    yarn add react-router-dom
    
    • 配置路由表

    在src目录下新建router文件夹,并在router下新建index.ts文件,内容如下

    import { createHashRouter } from "react-router-dom";
    import User from "../pages/user";
    
    const router = createHashRouter([
      {
        path: "/",
      },
      {
        path: "/user",
        Component: User,
      },
    ]);
    
    export default router;
    
    • 路由出口

    找到App.tsx文件,引入并注入路由表

    import Storage from './pages/storage';
    import { RouterProvider } from 'react-router-dom';
    import router from './router'
    
    function App() {
      return (
        <>
          <RouterProvider router={router}/>
          <br />
          <Storage/>
        </>
      )
    }
    
    export default App
    
    • 预览
    Dec-09-2023 14-46-13.gif

    5.集成redux

    • 安装redux
    yarn add @reduxjs/toolkit react-redux
    
    • 创建根store

    在根目录下新建store/index.ts文件

    import { configureStore } from '@reduxjs/toolkit'
    
    export default configureStore({
      reducer: {
      }
    })
    
    
    • 拆分reducer

    store下新建xxx.ts,笔者这里为calculate.ts

    import { createSlice } from '@reduxjs/toolkit'
    
    export const counterSlice = createSlice({
      name: 'calculate',
      initialState: {
        value: 0
      },
      reducers: {
        increment: state => {
          state.value += 1
        },
      }
    })
    
    export const { increment } = counterSlice.actions
    export default counterSlice.reducer
    

    将定义的calculate.ts导入到index.ts中并设置

    import calculateReducer from './calculate'
    
    export default configureStore({
      reducer: {
        calculate:calculateReducer
      }
    })
    export { increment } from './calculate'
    
    • 全局注入

    main.ts中注入store

    import store from "./store";
    import { Provider } from "react-redux";
    
    ReactDOM.createRoot(...).render(
      ...
        <Provider store={store}>
          ...
        </Provider>
      ...
    );
    
    • xxx.tsx组件页面中使用

    可以看到,报了TypeScript相关的类型错误

    image.png

    这需要返回store/index.ts中导出类型

    export type Store = ReturnType<typeof store.getState>
    

    并在xxx.tsx组件内导入类型并作为泛型传入

    import { ...,Store } from "../store";
    ... useSelector<Store>(...);
    
    • 预览
    redux.gif
    • 支持异步

    一般使用第三方中间件redux-sagaredux-thunk,不过笔者从工作开始,就没在状态程序中写过异步,感觉不甚重要,此处就不引入了

    6.集成antd

    • 安装
    yarn add antd
    
    • 使用
    import { DatePicker } from 'antd'
    function Atd() {
      return <>
        <DatePicker/>
      </>;
    }
    export default Atd;
    
    • 预览
    image.png
    • 全局配置

    预览中可以看到,默认语言是英文

    找到app.ts文件,修改如下

    image.png

    再次预览,就变成中文了

    image.png

    7.集成umi-request

    • 安装
    yarn add umi-request
    
    • 配置

    在根目录下新建api文件夹,在api下新建index.ts文件,此处统一进行配置。其实和axios一样,都是设置下拦截器统一处理请求的发起和接收

    一般来说,在请求发起时拦截,是为了统一增加字段,比如token。又或者,改变url前缀,毕竟同一个项目可能需要向不同的后端服务发起请求;在响应接收阶段,则是统一输出,以使业务代码接收统一

    import { extend, type ResponseError } from "umi-request";
    import { stringify } from "qs";
    import { message } from "antd";
    
    const errorHandler = (
      err: ResponseError & {
        description?: string;
      }
    ) => {
      message.destroy();
      message.error(err.description || "接口请求失败,请稍后再试...");
      return {
        code: -1,
      };
    };
    
    // 拦截错误
    const Request = extend({
      timeout: 30000,
      errorHandler,
    });
    
    // 拦截请求
    Request.interceptors.request.use((url) => {
      return {
        url,
      };
    });
    
    // 拦截响应
    
    Request.interceptors.response.use((response) => {
      return new Promise(function (resolve, reject) {
        response.text().then((res) => {
          let resData;
          try {
            resData = JSON.parse(res);
          } catch (err) {
            resData = { code: -1 };
          }
          if (resData.responseCode === 200) {
            resolve(resData);
          } else {
            reject(resData);
          }
        });
      });
    });
    
    export default Request;
    
    // 序列化函数--辅助用
    
    export function stringifyWithTrim(params = {}) {
    
      function encoder(str:unknown, defaultEncoder:(str: unknown, defaultEncoder?: unknown, charset?: string) => string) {
        if (typeof str === "string") {
          return defaultEncoder(str.trim());
        }
        return defaultEncoder(str);
      }
      return stringify(params, { encoder });
    }
    
    
    • 使用

    api文件夹下按模块新建并引入index.ts中的导出模块进行接口请求即可,一般对于get请求还需要对参数进行序列化

    import request,{stringifyWithTrim} from "./index.ts";
    
    export default {
        post(params){
          return request(url, {
            method: "POST",
            params,
          })
        },
        get(query){
          return request(`url?${stringifyWithTrim(query)}`)
        }
    }
    
    

    8.css隔离

    样式方案有很多,你也可以使用lessscss等预处理器。笔者这里以模块化方式举例

    • css模块

    在根目录下创建style文件夹,并新建xxx.module.css

    .wrapper {
      color: red;
    }
    
    
    • 使用

    首先,安装classnames

    yarn add classnames
    

    接着导入并使用

    import classnames from "classnames";
    import CSS from "../style/css.module.css";
    
    function Css() {
      const klass = classnames({
        [CSS.wrapper]: true,
      });
      return <div className={klass}>css</div>;
    }
    
    export default Css;
    
    
    • 预览
    image.png

    9.代码质量与提交规范

    这里的完整步骤可以参考vite+vue工程化配置

    首先,安装相关依赖

    
    yarn add lint-staged husky @commitlint/config-conventional @commitlint/cli -D
    
    

    然后,修改package.json

    
    "scripts": {
    
        "prepare": "husky install",
    
        "check": "lint-staged"
    
    },
    
    "lint-staged": {
    
        "*.{tsx,ts}": [
    
          "npm run lint"
    
        ]
    
    }
    
    

    接着,注册husky相关钩子

    
    npx husky add .husky/pre-commit "npm run check"
    
    git add .husky/pre-commit
    
    npx husky add .husky/commit-msg  'npx --no -- commitlint --edit ${1}'
    
    

    再然后,在根目录下定义commitlint.config.cjs配置文件

    module.exports = {
      extends: ["@commitlint/config-conventional"],
      rules: {
        "type-enum": [
          2,
          "always",
          [
            "feature", // 迭代功能
            "conf", // 修改构建配置
            "fixbug", // 修复bug
            "refactor", // 代码重构
            "optimize", // 代码优化
            "style", // 仅修改样式文件
            "docs", // 文档补充说明
          ],
        ],
        "header-max-length": [0, "always", 72], //限制最长72
      },
    };
    

    最后,测试下。可以成功拦截

    image.png

    源码地址

    传送门

    相关文章

      网友评论

          本文标题:企业级前端工程化配置指南:vite + react + redu

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