重要!!!
在看以下内容前,请提前知道一下,我刚开始接触React,工作中用的是Vue3,所以以下的很多对 React 的不理解和误解,大概率均是我的问题,多半是我的能力不足,而与React无关。但如果真是React也有不足的地方,那我也没什么可说的。
前言
在写了近3年的 Vue3 后,慢慢地感觉没什么意思了。发现随着原理了解的越多,和工作相关的就越少。目前我学的原理,几乎完全用不到,倒是实用性的技术,这边学,下次可能就可以用到。当然偶尔还是能有些帮助,比如同事遇到一个bug,基本会说:"真是见了鬼了,这bug莫名其妙"。我帮着解决过几次,其实并不莫名其妙,明显的是代码有问题。
上面的问题遇到过:
- 不要用 reactive,reactive不准,用ref,它比较准一些,reactive的值经常莫名其妙的值不对。
- 对 reactive 的值重新赋值
js
let obj = reactive();
... 一些代码
const {data} = await getDetail();
obj = reactive(data)
Vue3的官网看了也有 七八遍了,相关的原理书也看了不少,目前只能说有的时候突然很想知道一个技术为什么是这样,然后想了解其原理,但大多时候看原理,已经提不起来兴趣了。这时候我就想学些新的技术了。
学些什么呢
其实当想到35岁这个坎时,我对学什么技术就提不起来兴趣了,总觉得自己到了那个年纪,找工作也很费劲。但躺平也解决不了问题,反而可能使之后处境更不好。因为在做这个行业之前就想过这个问题,想的是自己做一个产品。又认真想了一段时间,好吧,开始做吧。
思考了一段时间后,决定自己去做一个,我打算用2年时间实现,分别是 一个后台、一个手机端。前端就用 Reactjs 写、后端用 Nestjs 来写。

之所以不用Vue,是因为这个产品我是否会一直做下去还不一定,虽然我现在比较坚定,但没发生的事还是有各种各样的可能。所以如果我借着写自己的产品,还可以学到新的技术如:React、Nestjs,哪怕之后还是工作,求职的路上大概率也会比少会一些要好。
Trae 帮助了我
此次用 React 和 Nestjs 去写后台管理系统,为了加快进展,我就使用了 Trae 。之前一直用 Cursor,但中途也会时不时的用用 Trae,慢慢地发现 Trae 进步也很快。

一个是 Tab 功能已经支持了,这点就很不错了,而且 Tab 好像是可以基于光标学习,因为随着我用的多,它就越来越准了,虽然和 Cursor 还是有些差距。比如 Cursor 的 Tab 支持跨文件的预判(说实话,这点是真牛,但用的不多)。但 Trae 我现在是免费用,这好感度就增加了一些。不过,由于我很大一部分是基于学习的目的写这个项目,所以我只需要 Trae 帮我实现基本功能,剩下的我会看 React 官网 和 Nestjs 官网去学习,和通过优化 Trae 生成的代码去加快自己的学习速度。
不过它在快捷键上和Cursor不一样,也不知为啥,又让我适应了一段时间。而且我明明导入配置时,选择的是从Cursor导入,但是页面主题感觉也不一样,这也让我适应了一段时间。但整体还是好感的,因为第一次使用 Trae 的时候,是不太舒服的。
还有一点是智能体,我也比较喜欢,不过我主要是在 豆包 上创建过多个智能体,感觉这个很不错,但在 Trae 上还没有尝试,所以也不好介绍。
既然文章是讲用 React 的,而且是 有缝连接。那就来讲下这些感觉遇到的问题吧
使用 React 的不适应
路由
路由,如果直接在 组件或函数 外部使用(比如tsx的文件中直接用) const navigate = useNavigate(); 会报下面的错误
js
import { useNavigate } from "react-router-dom";
const navigate = useNavigate();

如果写在函数内部,就没有这个问题了。
不过可以想到,可能是 React 是把声明的 function AA 这个当做是组件,然后 export default AA; 出去,所以即使写在 tsx 文件中,它依然不属于组件内的。而 Vue 中是将 .vue 文件当做一个组件,所以即使写在 script 中也可以。可能主要还是要将 路由的操作 限制在组件内。
但我的 Trae 装了 圈复杂度 的插件,推荐的是尽量不要超过 10,而 React 又以函数作为组件,所以一个组件 轻轻松松的就有 20+ 的 圈复杂度。

纯函数
React 推荐使用纯函数,就是不修改外部变量,这样的好处就是无论这个函数执行了多少次,由于所操作的数据都在自己的作用域内,所以无论重复执行了多少次,其结果是一样的。不会因为每次执行都会得出不一样的结果。
js
// 纯函数
function getVal(val){
return val + 5;
}
// 非纯函数
let val;
function getVal(){
val+=5;
return val;
}
React 官方是建议数据通过参数传递,这样多个地方用到的数据,就需要来回传递。而目前我除了静态数据是直接放在 tsx 文件的根中,其他数据都是在某个函数中定义的,但不少的变量可能在多个地方使用,要么多个函数都写在一个组件中,这样就可以把变量声明在组件中,其中的函数就可以直接访问了。但代码多了后,我还是习惯把一些函数放在与 组件同级的位置,这样就又需要问题传参。
有时还需要传两层,当然了,官网给出了如下的解决方法
js
function Profile(props) {
return (
<div className="card">
<Avatar {...props} />
</div>
);
}
通过 扩展运算符 将数据平铺后传入组件。但在一个组件中,需要用到多个函数,而函数之间不断的传参就变得很频繁了。
父组件调用子组件的方法
首先需要在父组件定义 ref
js
父组件
function BookList(){
在父组件定义 ref
const bookFormModelRef = useRef<typeof BookFormModel>(null);
<BookTable ref={bookFormModelRef} onClickEdit={showEditModal} />
...
}
子组件接收 ref
function BookTable({ ref }){
再使用 useImperativeHandle函数,传入 ref,再传入需要向父组件暴露的数据
useImperativeHandle(ref, () => ({
loadBooks,
}));
...
}
不太舒服的是,明明是父组件要获取子组件的方法,怎么子组件还要先接收父组件的ref。初步猜测是 在 useImperativeHandle 方法中,将 第二个参数的返回值赋值给第一个参数ref,但还没有来得及看原理。
我的代码,看下有问题不
下面是用 ant-design 写的一个表格(react初学者,写的不好请见谅)。一段代码中又有js 又有 html。动不动一个函数就上百行。觉得太多了也可以折成多个函数,然后参数一传就是好几个。
tsx
const ActionButton = ({
record,
showModal,
handleDelete,
}: {
record: Paragraph;
showModal: (record: Paragraph) => void;
handleDelete: (id: number) => void;
}) => {
return (
<Space>
<Button
type="text"
icon={<EditOutlined />}
onClick={() => showModal(record)}
>
编辑
</Button>
<Popconfirm
title="确定要删除这个段落吗?"
onConfirm={() => handleDelete(record.paragraph_id)}
okText="确定"
cancelText="取消"
>
<Button type="text" danger icon={<DeleteOutlined />}>
删除
</Button>
</Popconfirm>
</Space>
);
};
// 渲染表格
const ParagraphTable = ({
paragraphs,
pagination,
total,
handleTableChange,
showModal,
handleDelete,
}: {
paragraphs: Paragraph[];
pagination: Pagination;
total: number;
handleTableChange: (pagination: Pagination) => void;
showModal: (record: Paragraph) => void;
handleDelete: (id: number) => void;
}) => {
// 表格列定义
const columns = [
{
title: "序号",
dataIndex: "index",
key: "index",
width: 80,
render: (_: any, _record: Paragraph, index: number) => index + 1,
},
{
title: "排序",
dataIndex: "paragraph_order",
key: "paragraph_order",
width: 80,
},
{
title: "类型",
dataIndex: "type",
key: "type",
width: 80,
},
{
title: "内容",
dataIndex: "content",
key: "content",
render: (text: string, record: Paragraph) => {
if (record.type === "image") {
// 使用imagePath属性显示图片
return (
<img
src={record.imagePath}
alt="段落图片"
style={{ maxWidth: "100px", maxHeight: "100px" }}
/>
);
}
return text;
},
},
{
title: "操作",
key: "action",
width: 180,
render: (_: any, record: Paragraph) => (
<ActionButton
record={record}
showModal={showModal}
handleDelete={handleDelete}
/>
),
},
];
return (
<Table
dataSource={paragraphs}
columns={columns}
rowKey="paragraph_id"
pagination={{
current: pagination.current,
pageSize: pagination.pageSize,
total,
}}
onChange={handleTableChange}
/>
);
};
export default ParagraphTable;
以上就是一个常规的表格,在我写Vue时,也会将 el-table 进行二次封装。之后和 Ant-Design 比较像,是通过配置项的方式进行传入,如果是遇到要写模板内容的,则会通过插槽实现。如下
js
[
{
prop: 'publishTime',
label: '发布时间',
width: 180
},
{
prop: 'status',
label: '发布标志',
dict: 'BIDDING_STATUS',
width: 100,
fixed: 'right'
},
{
label: '操作',
width: 140,
slot: 'operation'
}
]
而 .vue 页面在模板中则可以通过 插槽的方式注入
js
<Table @register="tableRegister">
<template #operation="{ row }">
这里可以写功能按钮了
</template>
</Table>
这样习惯下来后,感觉还挺清爽的。模板和js分开来写,每个地方的代码量也比较好找。
对 useState 的猜测
这里不是说用 useState 的不适应了。只是想讲下刚接触到 useState 的想法。
ts
import { useState } from 'react';
export default function Gallery() {
const [index, setIndex] = useState(0);
function handleClick() {
setIndex(index + 1);
}
let sculpture = sculptureList[index];
return (
<>
<button onClick={handleClick}>
Next
</button>
<p>
{index}
</p>
</>
);
}
这是 React 使用 useState 的写法,主要是为了让 index 数据在 Gallery 组件多次渲染时,依然记得 index 的值。 就是说 第一次 index 是1,第二次就是 2 了。
但在 js 中,每个函数都有自己的作用域和上下文,虽然函数的作用域是在函数定义的时候就确定的(由其词法环境决定)。但每当函数执行完后,其执行上下文中的相关亦是会被垃圾回收,释放内存,下次再调用时,重新生成其 执行上下文与相关变量。
但闭包可以保存其变量,使其函数执行后,该变量依然保存在 内存中。
而 React 的 useState 给我的感觉就是这个作用,那我直接把 index 放在组件外定义不就好了吗? 当然,肯定还有我不知道的原因,React 官网已经看过一遍了(教程部分,还没有看 api 部分,那部分量应该会比较大),目前还不知道 useState 的特别之处。
结语
就像我一开始说的,上面的内容大多可能是我的问题,而不是 React 的问题,毕竟还有这么多公司在用它,而且还有面试官和我说,会 React 的工资要比 Vue 高一点。所以 React 应该还是很优秀的。那我就多向它学习吧 !