重构《node+React实战:从0到1实现记账本》记录(二):前端环境搭建

373 阅读4分钟

前言

这次记录距离上次已经三个多月了。一直想写,但总觉自己缺点什么,现在开始下笔。

好了,废话不多说。根据上篇文章的开发环境推荐,搭建react前端环境。

知识点

  • 构建工具 Vite
  • 配置vsocde 调试配置。
  • 引入 react-router-dom 路由。
  • 引入 Zarm UI 组件库。
  • 配置 CSS 预处理器 Less
  • 移动端项目适配rem
  • 引入 axios 并做简单封装
  • 项目设置别名

初始化 Vite + React 项目

Vite官方提供两种初始化项目的方式,一种是如下所示,可以自由选择需要的前端框架(注意,Vite 官网因为兼容性要求,要 node.js 版本 18+)

npm create vite@latest

另一种是通过附加的命令行选项直接指定项目名称和你想要使用的模板,直接生成项目

# npm 6.x
npm init @vitejs/app account-react-client --template react

# npm 7+, 需要额外的双横线:
npm init @vitejs/app account-react-client -- --template react

命令行中的 account-react-client是项目文件夹名称,你可以修改成任何名字。

选择第二种方式,生成一个 react+ts 的项目。

npm create vite@latest account-react-client -- --template react-ts

初始化完成后,进入项目目录,用VSCode打开并安装依赖,启动项目。

npm i
npm run dev

如下所示,项目启动成功。

image.png

配置 VSCode 调试

VSCode中,我们使用launch.json来配置调试。在项目根目录下,创建一个.vscode文件夹,并在其中创建launch.json文件,写入以下配置:

{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "chrome",  
      "request": "launch",
      "name": "调试记账本前端项目",
      "runtimeExecutable": "canary", // 这里启动的是canary,也可以选择chrome
      "runtimeArgs": ["--auto-open-devtools-for-tabs"], // 启动浏览器后也打开开发者工具
      "userDataDir": false,
      "url": "http://localhost:5173", // 启动的地址,也是你项目的地址
      "webRoot": "${workspaceFolder}"
    }
  ]
}

配置完成后,保存后在VSCode中按F5键,会启动一个打开开发者工具的chrome canary版的浏览器。如下图所示:

image.png

这样做有什么好处呢?我们可以在vscode中打断点,调试代码,而不需要每次都去浏览器中打断点。例如在App.tsx文件中打断点:

image.png

然后在浏览器中点击这里

image.png

点击后,浏览器会自动跳转到VSCode,并暂停在断点处,如下图所示:

image.png

这样就可以在VSCode中调试,查看和修改代码了,非常方便。

引入 react-router-dom 路由

没有路由的项目,那就不是一个完整项目,而是一个页面而已。真实项目都是存在各种模块之间的切换,各个模块的功能组合在一起才能叫做一个项目。

在安装react-router-dom之前,我们先把项目清一下:

  • 删除 src 目录下的 assets文件夹

  • 删除 src 目录下的 index.css

  • 删除 src 目录下的 App.css

  • App.tsx 文件中只保留如下内容:

    function App() {
      return <div >
      </div>
    }
    
    export default App
    
  • main.tsx 文件中只保留如下内容:

    import React from 'react'
    import ReactDOM from 'react-dom/client'
    import App from './App'
    
    const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement)
    root.render(
        <App />
    )
    

    安装react-router-dom

npm i react-router-dom

在项目 src 目录下新增 page 目录用于放置页面组件,再在 page 下新增两个目录分别是 Index 和 About ,添加如下内容:

// Index/index.tsx
export default function Index() {
  return <div>
    Index
  </div>
}

// About/index.tsx
export default function About() {
  return <div>
    About
  </div>
}

新建 src/router/index.ts 配置路由数组,添加如下内容:

// router/index.ts
import Index from '../page/Index'
import About from '../page/About'

const routes = [
  {
    path: "/",
    component: Index
  },
  {
    path: "/about",
    component: About
  }
];

export default routes

在 App.tsx 引入路由配置,实现切换浏览器路径,显示相应的组件:

// App.tsx
import { BrowserRouter, Route, Routes } from 'react-router-dom'
import routes from './router'

function App() {
  return (
      <BrowserRouter>
        <Routes>
          {routes.map((route, index) => (
            <Route key={index} path={route.path} element={<route.component />} />
          ))}
        </Routes>
      </BrowserRouter>
  )
}

export default App

启动项目 npm run dev,如下图所示就表示成功了:

jump.gif

引入Zarm UI 组件库

安装Zarm UI:

 npm i zarm --save

修改App.tsx文件,全局引入样式和中文包:

// App.tsx
import { BrowserRouter, Route, Routes } from "react-router-dom";
import routes from "./router";
import { ConfigProvider } from "zarm";
import zhCN from "zarm/lib/config-provider/locale/zh_CN";
import "zarm/dist/zarm.css";

function App() {
  return (
      <BrowserRouter>
        <ConfigProvider locale={zhCN}>
          <Routes>
            {routes.map((route, index) => (
              <Route
                key={index}
                path={route.path}
                element={<route.component />}
              />
            ))}
          </Routes>
        </ConfigProvider>
      </BrowserRouter>
  );
}

export default App;

在 src/page/Index/index.tsx中添加一个按钮组件,代码如下:

// src/page/Index/index.tsx
import { Button } from "zarm"

export default function Index() {
  return <div>Index
    <Button theme="primary" onClick={() => { }}>按钮</Button>
  </div>
}

启动项目出现下图效果表示引入成功了

image.png

但是在控制台中有报错

image.png

直译是:ConfigProvider:对defaultProps的支持将在未来的重要版本中从函数组件中移除。请使用JavaScript的默认参数。

这是Zarm 3.0版本的原因,我查了很多资料也没有解决,如果想解决的话得重写Zarm,这样工程量就大了,所以这里就先这样吧。

小优化

组件已经引入成功了。但是所有的组件样式都被引入了。这样代码就显得臃肿了,我们可以只引入需要的组件样式。

先看看打完包后,静态样式css文件有多大。运行指令npm run build,如下所示:

image.png

打包后的静态样式css文件有164.49kb,这显然是不合理的,因为我们的项目就一个按钮,所以我们可以只引入需要的组件样式。

首先安装运行命令,安装两个插件:

npm i -D consola
npm i vite-plugin-style-import -D

然后在vite.config.ts中配置插件,如下所示:

import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { createStyleImportPlugin } from "vite-plugin-style-import";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    react(),
    createStyleImportPlugin({
      libs: [
        {
          libraryName: "zarm",
          esModule: true,
          resolveStyle: name => `zarm/es/${name}/style/css`,
        },
      ],
    }),
  ],
});

App.tsx 文件中的样式引入import "zarm/dist/zarm.css";删除掉。

再运行npm run build,如下所示:

image.png

打包后的静态样式css文件由164.49KB -> 29.87KB,体积明显缩小。这种方式也是前端性能优化的一种方式。

配置CSS预处理器Less

项目中采用less作为css预处理器,它能设置变量以及一些嵌套罗技,便于项目样式编写。运行下面命令安装:

npm i less -D

vite.config.ts中配置less,如下所示:

import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { createStyleImportPlugin } from "vite-plugin-style-import";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    react(),
    createStyleImportPlugin({
      libs: [
        {
          libraryName: "zarm",
          esModule: true,
          resolveStyle: name => `zarm/es/${name}/style/css`,
        },
      ],
    }),
  ],
  css: {
    preprocessorOptions: {
      less: {
        javascriptEnabled: true,
      },
    },
  }
});

看是否引入成功。新建src/page/Index/index.less文件,代码如下:

// src/page/Index/index.less
.index{
  &>span{
    color: red;
  }
}

src/page/Index/index.tsx中引入样式,代码如下:

// src/page/Index/index.tsx
import { Button } from "zarm"
import './index.less'

export default function Index() {
  return <div className="index">
    <span>Index</span>
    <br />
    <Button theme="primary" onClick={() => { }}>按钮</Button>
  </div>
}

启动项目,如下所示:

image.png

可以看到样式已经生效了,说明less已经引入成功了。

移动端项目适配rem

移动项目,是需要适配不同屏幕的,所以我们需要将px单位转换为rem单位。这里我们使用lib-flexiblepostcss-pxtorem两个插件。

lib-flexible 是一个用于移动端网页开发的前端框架,主要功能是帮助开发者实现响应式布局。通过这个插件,开发者可以根据不同设备屏幕尺寸自动调整页面元素的大小和位置,确保网站在各种设备上都能良好显示,从而提升用户体验。

postcss-pxtorem 是一个 PostCSS 插件,用于将 px 单位转换为 rem 单位。通过这个插件,开发者可以将 px 单位转换为 rem 单位,从而实现响应式布局。

运行下面命令安装:

npm i lib-flexible postcss-pxtorem -D

安装完成后,先在main.tsx中引入lib-flexible,如下所示:

// src/main.tsx
import "lib-flexible";

再在根目录下新建一个postcss.config.cjs文件(我的vite版本是5.0,引入的文件必须是.cjs,其他版本待查)。代码如下:

// postcss.config.cjs
module.exports = {
  "plugins": [
    require("postcss-pxtorem")({
      rootValue: 37.5,
      propList: ['*'],
      selectorBlackList: ['.norem'] // 过滤掉.norem-开头的class,不进行rem转换
    })
  ]
}

修改src/page/Index/index.less文件,代码如下:

// src/page/Index/index.less
.index{
  width: 200px;
  height: 200px;
  background: green;
  &>span{
    color: red;
  }
}

启动项目,如下所示:

image.png

可以看到,200px已经转化为5.33rem了,我们设置的 rootValue37.5,你可以换算一下 5.33333 * 37.5 = 200

引入 axios 并做一下简单封装

调用接口,我们使用axios,这里我们做一下简单的封装。

运行下面命令安装:

npm i axios -D

src目录下新建一个utils文件夹,然后新建一个request.ts文件,代码如下:

// src/utils/request.ts
import axios from "axios";
import { Toast } from "zarm";
import { baseUrl } from "../config";

const axiosInstance = axios.create({
  baseURL: baseUrl,
  timeout: 3000,
});

// 添加请求拦截器
axiosInstance.interceptors.request.use(function (config) {
  config.headers['X-Requested-With'] = 'XMLHttpRequest';
  config.headers['Content-Type'] = 'application/json';
  const accessToken = localStorage.getItem("access_token");
  if (accessToken) {
    config.headers.authorization = `Bearer ${accessToken}`;
  }
  return config;
});

// 添加响应拦截器
axiosInstance.interceptors.response.use(
  res => res,
  error => {
    console.log(error);
    Toast.show(error.message);
  }
)
/**下面写接口函数以便于统一管理**/

src目录下新建一个config.ts文件,代码如下:

// src/config.ts
const MODE = import.meta.env.MODE; // 环境变量
export const baseUrl = MODE == "development" ? "http://localhost:3000" : "";

MODE 是一个环境变量,通过 Vite 构建的项目中,环境变量在项目中,可以通过 import.meta.env.MODE 获取,环境变量的作用就是判断当前代码运行在开发环境还是生产环境。

baseURL 是 axios 的配置项,它的作用就是设置请求的基础路径,后续我们会在项目实战中有所体现。配置基础路径的好处就是,当请求地址修改的时候,可以在此统一配置。这里的http://localhost:3000是我们暂时写的,后期可以修改

resolve.alias 别名设置

vite.config.ts中,我们可以通过resolve.alias来设置别名,这样我们在引入文件的时候,就可以使用别名来代替相对路径,这样代码看起来会更加简洁。

// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()],
  resolve: {
    alias: {
      '@': resolve(__dirname, 'src'),
    }
  }
})

tsconfig.app.json 中设置,这样vscode不弹出错误提示,

{
  "compilerOptions": {
    ...
    "noFallthroughCasesInSwitch": true,

    //别名配置
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"]
    }
  },
  "include": ["src"]
}

此时我们便可以修改之前的代码:

router/index.ts

import Index from '@/page/Index'
import About from '@/page/About'

utils/index.ts

import axios from '@/utils/request';

utils/request.ts

import { baseUrl } from "@/config.ts";

App.tsx

import routes from "@/router";

main.tsx

import App from "@/App.tsx";

总结

行文至此,我们的基础开发环境已经搭建完毕,涉及构建工具、前端框架、vscode调试、UI 组件库、CSS 预加载器、、移动端分辨率适配、HTTP 请求库等。这些知识都是一个合格的前端工程师应该具备的,所以请大家加油,将他们都通通拿下。

代码仓库:

前端代码