当前位置: 首页>后端>正文

React+Vite+antd+Typescript 框架搭建-1(含日夜间主题切换)

使用vite脚手架构建项目

npm create vite@latest
创建时,输入项目名称,并选择react和typescript
进入到文件夹,安装依赖,运行

React+Vite+antd+Typescript 框架搭建-1(含日夜间主题切换),第1张

如果npm安装没反应,可以使用代理方式
npm config set registry https://registry.npm.taobao.org

安装第三方插件和依赖

路由 react-router:npm install react-router-dom@6
状态管理react-redux:npm install @reduxjs/toolkit react-redux
UI组件库 antd:npm install antd
MOCK数据 mockjs:npm install mockjs
loading加载nprogress: npm install nprogress
request请求axios:npm install axios

less:npm install -D less
Mock vite:npm install -D vite-plugin-mock
nprogress 加载中类型:npm i -D @types/nprogress
依赖安装完后,package.json是这个样子

{
  "name": "my-antd-vite-project",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "@reduxjs/toolkit": "^1.9.0",
    "antd": "^4.24.2",
    "axios": "^1.1.3",
    "mockjs": "^1.1.0",
    "nprogress": "^0.2.0",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-redux": "^8.0.5",
    "react-router-dom": "^6.4.3"
  },
  "devDependencies": {
    "@types/nprogress": "^0.2.0",
    "@types/react": "^18.0.22",
    "@types/react-dom": "^18.0.7",
    "@vitejs/plugin-react": "^2.2.0",
    "less": "^4.1.3",
    "typescript": "^4.6.4",
    "vite": "^3.2.0",
    "vite-plugin-mock": "^2.9.6"
  }
}

搭建完成后,页面效果(含主页、setup、错误三个页面,都可切换日夜间模式)

React+Vite+antd+Typescript 框架搭建-1(含日夜间主题切换),第2张
录制_2022_11_25_11_07_35_18920221125119361.gif

配置文件修改(vite.config.ts文件修改)

import react from "@vitejs/plugin-react";
import path from "path";
import { defineConfig } from "vite";
import { viteMockServe } from "vite-plugin-mock";

export default defineConfig({
  plugins: [
    react(),
    //mock数据
    viteMockServe({
      mockPath: "mock",
      localEnabled: true,
      prodEnabled: false,
      supportTs: false,
      watchFiles: true,
      injectCode: `
          import { setupProdMockServer } from './mockProdServer';
          setupProdMockServer();
        `,
    }),
  ],
  resolve: {
   //别名
    alias: {
      "~": path.resolve(__dirname, "./"), // 根路径
      "@": path.resolve(__dirname, "src"), // src 路径
    },
  },
  css: {
    preprocessorOptions: {
      //less文件
      less: {
        additionalData: `@import "${path.resolve(
          __dirname,
          "src/styles/variable.less"
        )}";`,
        // 支持内联 JavaScript
        javascriptEnabled: true,
      },
    },
  },
  server: {
    port: 3001,
    host: "0.0.0.0",
    open: false,
    // proxy: {
    //   "/api": {
    //     target: "https://xxx.com/api/v1",
    //     changeOrigin: true,
    //     rewrite: (path) => path.replace(/^\/dev-api/, ""),
    //   },
    // },
  },
});

配置tsconfig.json

//在compilerOptions对象中添加属性
    "baseUrl": "./",
    "paths": {
      "@/*": ["src/*"]
    },

配置router 支持按需加载

路径:src/routers/index.tsx
import { lazy, Suspense, ReactNode } from "react";
import { createBrowserRouter, RouteObject } from "react-router-dom";
import LoadingComponent from "@/compontents/Loading";
import LayoutPage from "@/layout";
const Introduction = lazy(() => import("@/views/introduction"));
const Setup = lazy(() => import("@/views/setup"));
const Error404 = lazy(() => import("@/views/error/404"));
const load = (children: ReactNode): ReactNode => (
  <Suspense fallback={<LoadingComponent />}>{children}</Suspense>
);
//定义路由数据
export const routes: RouteObject[] = [
  {
    path: "/",
    element: <LayoutPage />,
    children: [
      {
        index: true,
        element: load(<Introduction />),
      },
      {
        path: "setup",
        element: load(<Setup />),
      },
      {
        path: "*",
        element: load(<Error404 />),
      },
    ],
  },
];
export default createBrowserRouter(routes);

配置store

//文件路径:src/store/index.ts
import { configureStore, ThunkAction, Action } from "@reduxjs/toolkit";
import indexSlice from "./reducers/indexSlice";

//定义store
const store = configureStore({
  reducer: {
    index: indexSlice,
  },
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
export type AppThunk<ReturnType = void> = ThunkAction<
  ReturnType,
  RootState,
  unknown,
  Action<string>
>;
export default store;

//文件路径:src/store/hooks.ts
import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
import type { RootState, AppDispatch } from "./index";
// 在整个应用程序中使用,而不是简单的 `useDispatch` 和 `useSelector`
export const useAppDispatch: () => AppDispatch = useDispatch;
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;

//文件路径:src/store/reducers/indexSlice.ts
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import type { RootState } from "../index";
import { getCatalog } from "@/api/category";
// 为 slice state 定义一个类型
interface IndexState {
  catalogList?: any[];
  status?: "idle" | "loading" | "failed";
}
// 使用该类型定义初始 state
const initialState: IndexState = {
  catalogList: [],
};
//获取首页左侧文档目录
export const getCatalogList = createAsyncThunk("index/getCatalog", async () => {
  const { data } = await getCatalog();
  return data;
});
export const indexSlice = createSlice({
  name: "index",
  initialState,
  reducers: {},
  extraReducers: (builder) => {
    builder.addCase(getCatalogList.fulfilled, (state, action) => {
      const { CatalogList } = action.payload;
      state.catalogList = CatalogList;
    });
  },
});
export const selectCatalogList = (state: RootState) => state.index.catalogList;
export default indexSlice.reducer;

修改入口文件main.tsx和App.tsx

//路径:src/main.tsx
import ReactDOM from "react-dom/client";
import App from "./App";
import "./index.css";
import store from "@/store/index";
import { Provider } from "react-redux";
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
  <Provider store={store}>
    <App />
  </Provider>
);

//路径:src/App.tsx
import { FC, useState } from "react";
import { RouterProvider } from "react-router-dom";
import { GlobalContext } from "@/context";
import router from "./routers/index";
import "./App.less";
interface GlobalContext {
  theme: string;
  setTheme: (theme?: string) => void;
}
const App: FC = () => {
  const [theme, setTheme] = useState<string>("light"); //主题 默认日间模式
  const contextVal = { theme, setTheme } as GlobalContext;
  return (
    <div className="app-container">
      <GlobalContext.Provider value={contextVal}>
        <RouterProvider router={router} />
      </GlobalContext.Provider>
    </div>
  );
};
export default App;

定义Context用来切换主题(日夜模式)

//文件路径:src/context.ts
import { createContext } from "react";
interface GlobalContext {
  theme: string;
  setTheme: (theme?: string) => void;
}
export const GlobalContext = createContext<GlobalContext>({
  theme: "light",
  setTheme: () => {},
});

定义layout

//文件路径src/layout/index.tsx
import React from "react";
import { Outlet } from "react-router-dom";
import { Layout } from "antd";
import { HeaderComponent, AsideComponent, FooterComponent } from "./components";
import "./style.less";
const { Header, Content, Footer } = Layout;
function DefaultLayout() {
  return (
    <Layout className="default-container text-gray-700 bg-white dark:bg-gray-900 dark:text-gray-300">
      <Header
        style={{
          position: "sticky",
          top: 0,
          zIndex: 1,
          width: "100%",
          padding: 0,
          backgroundColor: "transparent",
        }}
      >
        <HeaderComponent />
      </Header>
      <Content className="main-content">
        <div className="content-box">
          <AsideComponent />
          <div className="article-content" style={{ width: "79.17%" }}>
            <Outlet />
          </div>
        </div>
      </Content>
      <Footer style={{ backgroundColor: "transparent", padding: 0 }}>
        <FooterComponent />
      </Footer>
    </Layout>
  );
}
export default React.memo(DefaultLayout);

https://www.xamrdz.com/backend/3pd1940076.html

相关文章: