使用最新版vite搭一个react 18项目

2023-10-26

使用Vite搭建一个简单的react项目

使用到的技术
Vite @latest、React.js @18.2.0、React-router @6.14.1、Typescript @5.2.0、Ant Design @5.7.0、Tailwind.css @3.3.3、Sass @1.63.6、less @4.1.3、zustand @4.3.9

项目源码地址:react-app
可以根据这个代码来修改,有可能有遗漏的bug我没找到

ps: 暂时还没有使用到状态管理工具,后续再选择

开始吧

项目初始化第一步选择脚手架

打开vite官网

vite官网

按照官网的命令,选择一个文件夹(在此文件夹下初始化你的项目),打开终端

// windows 
cmd 当前文件夹打开终端
npm create vite@latest

// mac
自己设置的快捷键打开终端
npm create vite@latest

然后就按着自己的需求,输入项目名称,选技术、就是ts、react
下载好以后
cd 项目名
pnpm install
pnpm run dev
类似下图

tips:博主使用的是pnpm 看个人习惯吧,没什么区别

配置vite.config.ts

import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
// path 是node的一个模块,和文件路径操作有关
import path from "path";

import { createStyleImportPlugin, AntdResolve } from "vite-plugin-style-import";

// 手写了resolve来拼接路径
const resolve = (url) => {
  // __dirname 在这里可以获取到项目的根路径 不太理解的可以看node教程
  return path.resolve(__dirname, url);
};

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    react(),
    // antd 按需加载 在引入antd的时候会使用这个来进行按需导入 按需导入只会引入一部分使用的组件 在打包的时候体积会减小很多
    createStyleImportPlugin({
      resolves: [AntdResolve()],
    }),
  ],
  // 配置 @ 路径 但是在这里配置了还不够,还需要在tsconfig.json里配置 后续会讲到
  resolve: {
    alias: [
      {
        find: "@",
        replacement: resolve("./src"),
      },
    ],
  },
  css: {
    preprocessorOptions: {
      // 配置全局css文件入口,这里配置了就不用在main.ts里引入了
      scss: {
        additionalData: '@import "@/style/index.scss";',
      },
      // 由于antd里使用了less 这里我们也引入一下,不然会报错
      less: {
        javascriptEnabled: true,
      },
    },
  },
});

引入一些需要的框架

  • 引入 antd

// 博主是在页面内导入使用的,所以没有配置其他的,下好就行了
pnpm install antd --save
  • 引入sass、less
pnpm i sass 
pnpm i less
// 后续引入在vite.config.ts里已经引了
  • 引入 tailwind.css

1、下载

pnpm install -D tailwindcss@latest postcss@latest autoprefixer@latest

2、初始化配置文件

npx tailwindcss init -p

上述命令会生成两个文件,分别是tailwind.config.js和postcss.config.js。
3、tailwind.config.js 配置

/** @type {import('tailwindcss').Config} */
export default {
  content: ["./src/**/*.html", "./src/**/*.{js,ts,vue,jsx,tsx}"],
  theme: {
    extend: {},
  },
  plugins: [require("tailwindcss"), require("autoprefixer")],
};

4、postcss.config.js 配置

export default {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
};

实际上是可以配置很多样式、主题的,有兴趣可以去试试
tailwind.css 官网

5、在src下创建style文件夹,在里面创建tailwind.css

@import "tailwindcss/base";
@import "tailwindcss/components";
@import "tailwindcss/utilities";

6、最后在main.tsx里引入

import ReactDOM from "react-dom/client";
import App from "./App";
import "./style/tailwind.scss";

// 初始化项目的时候使用了React.StrictMode 但是在我使用过程中有渲染两次的bug我就删了
ReactDOM.createRoot(document.getElementById("root")!).render(<App />);

  • 引入 vite-plugin-style-import 实现antd按需导入
pnpm i vite-plugin-style-import -D
pnpm i consola -D

// 在vite.config.ts里配置
import { createStyleImportPlugin, AntdResolve } from "vite-plugin-style-import";


plugins: [
    // antd 按需加载
    createStyleImportPlugin({
      resolves: [AntdResolve()],
    }),
],

引入react-router

  • 下载
// react-router-dom 是针对于pc端的
pnpm i react-touter-dom
  • 在src下创建router文件夹

初始化路由

// 懒加载组件 懒加载可以减小打包体积
import { lazy, Suspense } from "react";
import Layout from "@/components/layout";
import NotFound from "@/pages/404/index";
import { Route } from "@/type/router.type";
// 通过路由生成菜单方法
import { generateMenu } from "@/router/generateMenu";

// tips: antd高亮是根据key来的,可以根据目前有的菜单key类似的配置 防止不生效
const routes: Array<Route> = [
  {
    id: "1", // 唯一的id
    name: "layout", // 菜单名称
    path: "/", // 菜单路径
    element: <Layout />,
    children: [
      // 子菜单
      {
        id: "1.1",
        name: "首页",
        path: "/",
        icon: "",
        element: () => import("@/pages/home"),
      },
      {
        id: "1.2",
        name: "测试",
        path: "/test",
        icon: "",
        children: [
          {
            id: "1.2.1",
            name: "测试新的",
            path: "new",
            icon: "",
            children: [
              {
                id: "1.2.1.1",
                name: "文章",
                path: "article",
                icon: "",
                element: () => import("@/pages/article"),
              },
            ],
          },
        ],
      },
      {
        id: "1.3",
        name: "烟花",
        path: "/fireWork",
        icon: "",
        element: () => import("@/pages/firework"),
      },
    ],
  },
  {
    id: "2", // 唯一的id
    name: "登录", // 菜单名称
    path: "/login", // 菜单路径
    element: () => import("@/pages/login"),
  },
  {
    id: "4", // 唯一的id
    name: "404", // 菜单名称
    path: "/*", // 菜单路径
    element: <NotFound />,
  },
];

// 懒加载
function LazyElement(props: any) {
  const { importFunc } = props;
  const LazyComponent = lazy(importFunc);
  return (
    <Suspense fallback={<div>loading...</div>}>
      <LazyComponent />
    </Suspense>
  );
}

// 处理routes 如果element是懒加载,要包裹Suspense
function dealRoutes(routesArr: Array<any>) {
  if (routesArr && Array.isArray(routesArr) && routesArr.length > 0) {
    routesArr.forEach((route) => {
      if (route.element && typeof route.element == "function") {
        const importFunc = route.element;
        route.element = <LazyElement importFunc={importFunc} />;
      }
      if (route.children) {
        dealRoutes(route.children);
      }
    });
  }
}
dealRoutes(routes);
const menu = generateMenu(routes);

export default {
  routes,
  menu,
};


  • 使用路由
    我的主页面是App.tsx, 并且我想让我的layout组件是通过路由的形式组装的,这样我可以实现更复杂的路由
import { ConfigProvider } from "antd";
import { BrowserRouter, useRoutes } from "react-router-dom";
import routes from "./router/index";

// 渲染路由
function RouteElement() {
  const element = useRoutes(routes.routes);
  return element;
}

function App() {
  return (
    <>
      <ConfigProvider>
        {/* react-touter-dom 6是这样包裹的 */}
        <BrowserRouter>
          <RouteElement />
        </BrowserRouter>
      </ConfigProvider>
    </>
  );
}

export default App;
  • layout组件编写

1、头部

import "../index.scss";

import { Menu } from "antd";
import { useEffect, useState } from "react";
import { useNavigate, useLocation } from "react-router-dom";
import routes from "@/router/index";
import { menu } from "@/type/router.type";

function Header() {
  const navigate = useNavigate();
  const [current, setCurrent] = useState<any>("1.1");
  // location 获取当前路由路径
  const location = useLocation();
  // 这个菜单就是我们自己生成的
  const menus = routes.menu;

  // 使用useEffect来获取当前菜单的keyPath,让菜单高亮
  useEffect(() => {
    setCurrent(getMenuKey(menus, location.pathname));
  }, [location.pathname]);

  // 通过路径获取当前高亮的pathKey
  const getMenuKey = (menus: menu[], path: string, keyPath: string[] = []) => {
    menus.forEach((menu: menu) => {
      if (menu.path == path) {
        keyPath.push(menu.key);
      }
      if (menu.children && menu.children.length) {
        getMenuKey(menu.children, path, keyPath);
      }
    });

    return keyPath;
  };

  // 选择菜单
  const handleSelect = (val: any) => {
    // 设置菜单高亮的key
    setCurrent(val.keyPath);
    // 路由跳转
    navigate(val.item.props.path);
  };

  return (
    <>
      <div className="header flex justify-center items-center">
        <div className="flex  justify-center items-center w-[80%] cursor-pointer">
          <Menu onSelect={handleSelect} selectedKeys={current} mode="horizontal" items={menus}></Menu>
        </div>
      </div>
    </>
  );
}

export default Header;

2、身体

import "../index.scss";
// 这里二级路由就是使用Outlet渲染的 vue里路由全是用router-view渲染的
import { Outlet } from "react-router-dom";

function Main() {
  return (
    <>
      <div className="main">
        <Outlet />
      </div>
    </>
  );
}

export default Main;

引入状态管理库

找了一些资料,目前比较好用的,和vue比较类似的就是zustand,主要是操作简单一些
1、下载

pnpm i zustand

2、在src下创建store文件夹,增加index.ts文件,middleware.ts文件
index.ts用于定义store
middleware.ts用于持久化store

index.ts

import { create } from "zustand";
import myMiddleware from "./middleware";

export const userStore = create<IUser>(
  // 自定义的中间件
  myMiddleware(
    (set: any) => ({
      id: "1",
      name: "zym",
      age: 23,

      increase: () => {
        set((state: any) => ({ age: state.age + 1 }));
      },
    }),
    "react-app-user"
  )
);

middleware.ts

import { devtools, persist } from "zustand/middleware";

// 自定义持久化中间件
const myMiddleware = (f: any, name: string): any => devtools(persist(f, { name }));

export default myMiddleware;

3、使用

import "../index.scss";

import { userStore } from "@/store/index";
import { Button } from "antd";

function Footer() {
  const { age, increase } = userStore();
  return (
    <>
      <div className="footer">
        {age}
        <Button onClick={increase}>点我</Button>
        底部
      </div>
    </>
  );
}

export default Footer;

自定义字体

1、在一些字体网站上下载字体
我使用的是 自由字体
2、通过在全局css里引入
先将下好的字体放到项目里的assets/font下


@font-face {
  font-family: "Alimama";
  font-weight: 400;
  src: url("../assets/font/SmileySans-Oblique.ttf"), url("../assets/font/SmileySans-Oblique.ttf.woff2") format("woff2");
  font-display: swap;
}

* {
  font-family: "Alimama", serif;
}

tips:字体是比较大的,可能字体还没加载好网站就已经加载了,得使用一些方法去解决(博主还在试,目前知道在线cdn比较可行)

解决打包报错

// 在运行pnpm 打包的时候,会遇到报错json-schema类型找不到的情况

pnpm i @types/json-schema

在tsconfig.json里配置

"compilerOptions" : {
    "baseUrl": "./", // 这个重要
    "paths": {
      // 这里还解决了在vite.config.ts里配置@路径不起作用的问题
      "@/*": ["src/*"]
    },
    // 在这引入类型再打包就没问题了
    "typeRoots": ["node_modules/@types"],
    "types": ["json-schema"]
}

动画

我还引入了动画库 Ant Motion,只有首页用到了,很好用

pnpm i rc-banner-anim rc-queue-anim rc-tween-one

Ant Motion

结束

到这里你就完成了一个react项目的简单初始化,开始你的表演吧!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用最新版vite搭一个react 18项目 的相关文章

随机推荐

  • composer.json和composer.lock到底是什么以及区别?

    composer方文档 https docs phpcomposer com 04 schema html我们在做项目的时候 总是要安装一些依赖 composer给我们提供了很多方便 直接运行composer install 当我们运行co
  • Keras 深度学习之猫狗大战

    项目地址和代码 Project Dogs vs Cats 项目详细报告 Report dogs vs cats pdf keras 版本 2 1 5使用滴滴云AI大师码 0212 消费GPU有9折优惠哦 1 问题定义和数据集获取 项目属于计
  • Android RecyclerView 实现瀑布流

    Android RecyclerView 使用大全 基础使用 item 动画 下拉刷新等 瀑布流也是个常用的显示控件了 但是在使用时经常遇到一些问题 比如滑动回顶部后出现空隙 item在滑动时乱跳等问题 下面就来说说我怎么实现的瀑布流 并且
  • 51单片机_动态数码管(定时器中断实现)

    动态数码管 定时器中断 原理 利用定时器0 定时5ms 中断溢出200次即为1s 1s数码管显示的数字加1 一直加到250后归0 全部代码 include
  • ASP.NET Web Pages基础知识---使用Chart 帮助器

    Chart 帮助器 可以创建不同类型的带有多种格式化选项和标签的图表图像 它可以创建面积图 条形图 柱形图 折线图 饼图等标准图表 也可以创建像股票图表这样的更专业的图表 在图表中显示的数据可以是来自一个数组 一个数据库 或者一个文件中的数
  • win10系统anaconda搭建tensorflow环境运行Mask_RCNN以及ubuntu中conda配置

    0 相关说明及anaconda下载安装 0 1 anaconda版本3 4 3 0 0 2 遇到的问题 PermissionError Errno 13 Permission denied C ProgramData Anaconda3 t
  • LeetCode详细题解-Java版

    个人在leetcode刷题的过程中 也记录了一些解题的过程 不一定是最优的 但是都能正确通过 还有一些是官方给的解答 本文会陆陆续续更新 有一些本人看到的一些好的解题博文 本文直接引用了原文 如涉及侵权或博文失效 请联系博主删除博文链接 L
  • elasticsearch的实现全文检索

    elasticsearch一个准实时的搜索引擎 基于lucene构建 它的主要强项还是在全文检索方面 工作中还是使用到了这部分功能 这里做一个简单的总结 可以使初次使用的人很快的配置和使用 一 全文检索的概念 首先介绍全文检索的概念 就是对
  • HBuilder X 连接苹果手机(IOS)详细教程。Windows: 连接iOS手机调试项目

    手机 苹果11 IOS版本 15 0 1 HBuilder X要是最新版本 2 需要下载iTunes 注意事项 Windows 32位 itunes下载地址 Windows 64位 itunes下载地址 建议从如上地址下载iTunes 如果
  • IDEA git 出现分支游离问题 You are in 'detached HEAD' state,

    Can t update no current branch You are in detached HEAD state which means that you re not on any branch Checkout a branc
  • 高速电路信号完整性分析常用名词

    时域特性与频域特性 从字面理解时域就是时间区域或者说时间范围 频域就是频率区域或者说频率范围 某个信号量随时间变化的特征 就是这个信号量的时域特性 信号的时域特性可以用时间波形显示 时域函数可以转换为频域函数 频域特性则是时域的积分变换 信
  • Postgre 时间间隔类型(Interval)

    业务场景 对时间进行操作 比如获取5分钟之前的时间 是 now 5 minute 但是有时后面的间隔值是动态的 需要动态拼装 这是就需要把拼装好的字符串转变为时间间隔类型interval 如下 SELECT now CASE WHEN co
  • windows 下 openGLES 3.0 配合 vs 环境搭建(一)

    1 OpenGL ES3 0 Programming guide 里的例子代码 下载地址 https codeload github com danginsburg opengles3 book zip master 2 下载mail op
  • 六、C++语言初阶:异常

    6 异常 6 1 为什么需要异常 异常机制的处理原理 程序会出现错误 尤其是不易察觉的错误 需要了解并解决这些错误 通常 程序出现错误 都会强制退出 很难排除错误原因 6 2 C语言如何表示错误 1 函数返回值 通常 成功返回0 返回值 1
  • 2022-2-19 vmware workstation pro安装-15.0.2和15.5.7(含guest OS运行导致蓝屏解决)

    vmware workstation pro安装 1 安装 2 运行 3 导入虚拟机 ovf文件 4 导入虚拟机 vmx文件 含蓝屏问题解决 5 VMX格式转换成OVF 6 升级VM 1 安装 版本VMware workstation fu
  • Linux(CentOS)安装MySQL教程

    1 准备工作 1 1 安装CentOS虚拟机 教程 点击跳转 1 2 将CentOS虚拟机设置为静态IP 否则你每次重启虚拟机后连接数据库都要重新查IP 教程 点击跳转 1 3 如果有安装过MySQL 请先卸载MySQL 教程 点击跳转 1
  • 2019年东南大学网络空间安全学院夏令营经历

    今年报名时间也比较晚 但开营时间又比较早 夏令营活动的时间是在2019 07 09 2019 07 10 只有两天的时间 只提供9号一天的住宿 因为离的远 所以我是8号就去 11号才返程 所以有两天的住宿自理 建议去南门的酒店 东门的那个酒
  • 【Unity】如何制作小地图

    我们为什么要制作小地图呢 原因很简单 导航和定位 小地图可以显示玩家当前位置以及周围环境的概览 这使得玩家能够更好地导航和定位自己在游戏中的位置 找到目标或避开障碍物 场景了解 通过小地图 玩家可以获得对整个游戏场景的全局视角 他们可以看到
  • Spring对于循环依赖是如何解决的

    随着项目的不断扩大 依赖关系的复杂度也在不断增加 在这种情况下 循环依赖问题就显得尤为突出 Spring框架作为一种广泛使用的Java企业级应用开发框架 为开发者提供了一套优雅的解决方案来应对循环依赖 在本篇文章中 我们将探讨Spring是
  • 使用最新版vite搭一个react 18项目

    使用Vite搭建一个简单的react项目 使用到的技术 Vite latest React js 18 2 0 React router 6 14 1 Typescript 5 2 0 Ant Design 5 7 0 Tailwind c