vite + react + ts 手摸手做项目系列二 (实战篇)

传送门

前言

  • 这篇实战篇文章,我改了很多遍,本来加了很多复杂的封装,但是对于初学 react+ts 的同学很不友好,因为不好看懂 所以我删删减减,尽量用写的大家都能看的懂,尽量用简洁的语言表达出我们想做什么功能,先易后难

  • 复杂功能的封装都会再后面的系列文章中详细讲解,一步一步深入学习, 项目中绝大部分的代码都有详细的注释,希望大家看的不懵逼,因为我自己在初学的时候再 github 上找开源项目,很多 项目封装的很好,但是对小白就很懵了,得看好久,所以我这个教程,就会尽力的讲详细,按照文档来,项目能跑通,注释写的详尽,再配合直播,讲明白说清楚。

  • ts 大家不要觉得很难,去官网看一看基础用法,就能干了,碰到不懂的语法直接查就好,用几次就会了。

  • react 同理,切勿眼高手低,要大量代码实践。不懂就问,不会就查,一来二去自然会了。

  • 这一篇主要写的是

    • 假数据 json-server 的使用

    • 请求接口的简单使用

    • 路由的简单使用

    • 一些简单的全局配置数据

    • 国际化的使用

    • 简单公共组件的封装

json-server mock 数据

  • yarn add json-server -D

  • 在终端

    mkdir mock
    cd mock
    touch db.json
    
    
  • 在 package.json 中的 scripts 中添加

    "mock": "json-server mock/db.json --port 3008"
    
    
  • 然后运行命令 yarn mock 就可以在控制台成功访问到我们在 db.json 中配置的接口数据了

请求封装

注意事项:process.env 要替换成 import.meta.env

  • 全局的公共配置文件都会放在根目录下的 config.ts 文件中,目前项目刚开始只有少量配置信息

 
 * 当前环境变量
 */



 * 接口地址
 * @description env 可为主要环境或自定义地址
 */
export const apiAddress = "http://localhost:3008/";


 * 开发代理前缀
 */
export const proxyApi = "/api";


 * 接口前缀
 * 判断环境,是否需要使用前缀
 * 生产环境不需要代理,同时本地配置的代理在生产环境也是不能用的
 */
export const urlPrefix = process.env.NODE_ENV === "development" ? proxyApi : "";


  • 项目中用的 umi-request 这个库,目前我给配置的很少的东西,错误处理,中间件处理等等我的给删减了,刚开始不搞这么复杂

   

 
 * request 网络请求工具
 * 更详细的 api 文档: https://github.com/umijs/umi-request
 */
import umiRequest, { extend } from "umi-request";
import { urlPrefix } from "../config";


export const whyRequest = extend({
  prefix: `${urlPrefix}`,
});

export default umiRequest;

  • 定义接口:要提前和后段沟通好入参数,出参数的格式,结合 ts 的类型提示,在其他地方调用的时候就可以直接看到接口定义的属性了,非常方便

* 登陆请求数据类型
*/
export interface ILogin {
  userName: string;
  pwd: string;
}


* 返回数据类型
* 要提前和后段定义好类型,等接口写完直接替换地址就好了
*
*/
export interface ILoginData {
code: number;
message: string;
token: string;
}


* 登陆接口
* @param params
  */
  export const loginApp = (params: ILogin): Promise<ILoginData> => {
  return whyRequest.get("/login", params);
  };

  • 使用就很简单,直接调用,之后我们会配合,ahooks 中的 useRequest()使用

 loginApp({ userName: "why", pwd: "123" }).then((res) => {
     if (res.code === 200) {
       history.push("/home");
     } else {
       message.error("用户名或密码错误!");
     }
   });

国际化配置

  • yarn add react-intl -D

  • 国际化我们使用 react-intl 同时也要兼容 antd,的之类插件的中英文,我们在切换语言的时候插件库也要直接进行切换到对应的语言,配置起来也很方便,

  • 我们直接上代码

import { createIntl, IntlProvider } from "react-intl";

import antdEnUS from "antd/lib/locale/en_US";
import antdZhCN from "antd/lib/locale/zh_CN";

import enLn from "./components/ln-en";
import zhLn from "./components/ln-zh-cn";
···核心代码

* 包裹了默认 locale 的 Provider
* LocaleProvider 需要在App.tx使用,包装整个项目
* @param props
* @returns
  */
  export const LocaleProvider: React.FC = (props) => {
  return <IntlProvider locale={getLocale()}>{props.children}</IntlProvider>;
  };

 * 获取当前的 intl 对象,可以在 node 中使用
 * @param locale 需要切换的语言类型
 * @param changeIntl 是否不使用 g_intl
 * @returns IntlShape
 */
  const getIntl = (locale?: string, changeIntl?: boolean) => {

  
  if (gIntl && !changeIntl && !locale) {
  return gIntl;
  }
  
  if (locale && localeInfo[locale]) {
  return createIntl(localeInfo[locale]);
  }


if (localeInfo[defaultLanguage])
return createIntl(localeInfo[defaultLanguage]);

if (localeInfo["zh-cn"]) return createIntl(localeInfo["zh-cn"]);
  
if (!locale || !!localeInfo[locale]) {
  throw new Error(
  "The current popular language does not exist, please check the locales folder!"
  );
  }

return createIntl({
locale: "zh-cn",
messages: {},
});
};

* 语言转换
* @param descriptor
* @param values
  */
  export const formatMessage = (
  descriptor: MessageDescriptor,
  values?: Record<string, any>
  ) => {
  if (!gIntl) {
  setIntl(getLocale());
  }
  return gIntl.formatMessage(descriptor, values);
  };

  • 页面中使用

    1,我们要在对应的 ts 文件中配置中英文对照

    // 在locale 文件下配置中文对照
    export default {
    frontEnd: "Work hard on the front end",
    switchLan: "Chinese-English shift",
    switchToEn: "switch to chinese",
    switchToCh: " switch to english",
    localLan: "The internationalization of this project is   based on",
    };
    // 配置英文对照
    export default {
    frontEnd: "前端要努力",
    switchLan: "中英文切换",
    switchToEn: "切换到中文",
    switchToCh: "切换到英文",
    localLan: "本项目国际化基于",
    };
    
    

    2,在页面中我们直接调用 formatMessage() 这个方法就好了


 * 国际化页面
 * @constructor
 */
const LocalePage: React.FC = () => {
  
  const [value, setValue] = React.useState(
    localStorage.getItem("why__locale") || "zh-cn"
  );
  
  const onChange = (e: RadioChangeEvent) => {
    setValue(e.target.value); 
    setLocale(e.target.value); 
  };
  return (
    <Card title={formatMessage({ id: "switchLan" })} style={{ width: "500px" }}>
      <Radio.Group onChange={onChange} value={value}>
        <Radio value={"zh-cn"}>{formatMessage({ id: "switchToEn" })}</Radio>
        <Radio value={"en"}>{formatMessage({ id: "switchToCh" })}</Radio>
      </Radio.Group>
      <div className={styles.localLan}>
        {formatMessage({ id: "localLan" })}react-intl
      </div>
    </Card>
  );
};

  • 国际化页面

路由

  • react 路由看这个

  • react 路由系统和 vue 大有不同,没有路由导航前钩子,配置登陆鉴权就要自己配置下,结合 token,

  • 我们项目中路由的目的就是支持动态路由,路由权限,配置抽离,目前就是最简单的,裸的

公共组件封装

  • 我们如何封装一个公共组件?

    1, 项目中需要多处使用的组件

    2, 不和业务耦合的组件,业务耦合的公共组件

    3, 所有状态都可以在外部控制,通过传入的props来控制其行为而不是暴露其内部结构。

    封装良好的组件隐藏其内部结构,并提供一组属性来控制其行为。
    
    隐藏内部结构是必要的。其他组件没必要知道或也不依赖组件的内部  结构或实现细节
    
    
  • 我们的项目中统一目录,主要为了看起来舒服

  • 目录:

    • index.tsx为主入口文件

    • index.md为组件使用样例,必要的代码注释,要清楚的告诉别人怎么使用这个公共组件

如何使用iconfont的字体图标

  • 封装 icon,主要配合 antd createFromIconfontCN 直接引入 iconfont 中的字体图标,非常方便

  • 如下图所示直接登陆到iconfont网站生成对应js文件,在项目中直接用就好,很简单

  // 简单来说

  // 这里可以根据各属性动态添加,如果属性值为true则为其添加该类名,

  // 如果值为false,则不添加。这样达到了动态添加class的目的

   <FontIcon
      className={classNames(
        {
          [styles.large]: size === "large", // 返回为true使用css .large,下方同理
          [styles.normal]: size === "normal",
          [styles.small]: size === "small",
          [styles.disabled]: disabled,
        },
        className
      )}
      {...restProps}
    />

  • React.FC<>的使用 1.React.FC 是函数式组件,是在 TypeScript 使用的一个泛型,FC 就是 FunctionComponent 的缩写,事实上 React.FC 可以写成 React.FunctionComponent:

const App: React.FunctionComponent<{ message: string }> = ({ message }) => (
  <div>{message}</div>
);

2.React.FC 包含了 PropsWithChildren 的泛型,不用显式的声明 props.children 的类型。React.FC<> 对于返回类型是显式的,而普通函数版本是隐式的(否则需要附加注释)。

3.React.FC 提供了类型检查和自动完成的静态属性:displayName,propTypes 和 defaultProps(注意:defaultProps 与 React.FC 结合使用会存在一些问题)。

4.我们使用 React.FC 来写 React 组件的时候,是不能用 setState 的,取而代之的是 useState()、useEffect 等 Hook API。

封装icon公共组件

  
  export interface IconType extends React.HTMLAttributes<any> {
  
  
  type: string;
  
  size?: "small" | "normal" | "large" | null; 
  
  disabled?: boolean;
}

const FontIcon = createFromIconfontCN({
  
  scriptUrl: "//at.alicdn.com/t/font_955172_ymhvgyhjk.js",
});

const Icon: React.FC<IconType> = ({
  className,
  size = "normal",
  disabled,
  ...restProps
}) => {
  
  return (
    <FontIcon
      className={classNames(
        {
          [styles.large]: size === "large",
          [styles.normal]: size === "normal",
          [styles.small]: size === "small",
          [styles.disabled]: disabled,
        },
        className
      )}
      {...restProps}
    />
  );
};

export default React.memo(Icon);


  • 使用(截图中有iconSelect公共组件,此篇不做讲解)

结语

如果你现在正在找工作,可以私信“web”或者直接添加小助理进群领取前端面试小册、简历优化修改、大厂内推以及更多阿里、字节大厂面试真题合集,和p8大佬一起交流。

  • 21
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Web面试那些事儿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值