主题
React Router 路由(二)
本页对应 React Router 路由,共 7 段视频,约 1 小时 18 分。
学习目标
- 能够说明前端路由的作用,并使用 CRA 创建记账本项目环境、安装必要依赖且完成初始化清理。
- 能够配置
@别名路径解析与 VS Code 路径联想,并通过测试导入验证别名可用。 - 能够使用
json-server创建 Mock 数据、配置启动命令并验证接口数据返回。 - 能够根据页面结构划分一级/二级路由,使用
createBrowserRouter、RouterProvider和Outlet完成路由渲染。 - 能够使用
antd-mobile的 CSS 变量完成全局/局部主题色定制,并验证组件样式变化。 - 能够使用 RTK 创建 slice、配置 store、编写异步 action 管理账目列表,并使用 TabBar 组件实现静态布局与点击切换路由。
知识点
先看清页面结构,再决定路由层级
为什么需要先设计路由?因为这个记账本项目不是所有页面都整页切换:点击“月度收支”和“年度账单”时,底部区域不变,只有中间内容变化;点击“记一笔”时,整个页面和路径都变化。路由层级如果一开始分错,后面布局复用、二级出口和跳转都会乱。
怎么想?打开演示站边点边观察:/ 对应月度收支,/new 对应记一笔,这两个应是一级路由;/ 下面还能切到月度、年度,说明月度收支和年度账单是挂在布局容器 Layout 下的二级路由。转写稿里“流由”“red gun routeuter down”“atlet”“pas”等是口误或同音,实际分别是“路由”“react-router-dom”“Outlet”“path”。
怎么做?在 src/router/index.js 中用 createBrowserRouter 创建路由实例,一级配置 Layout 和 New,在 Layout 的 children 中配置 Month、Year。按转写稿整理代码如下:
jsx
import { createBrowserRouter } from 'react-router-dom'
import Layout from '../pages/Layout'
import New from '../pages/New'
import Month from '../pages/Month'
import Year from '../pages/Year'
const router = createBrowserRouter([
{
path: '/',
element: <Layout />,
children: [
{ path: 'month', element: <Month /> },
{ path: 'year', element: <Year /> }
]
},
{
path: '/new',
element: <New />
}
])
export default router易错点:二级路由必须挂在父路由的 children 里;父组件 Layout 中必须放二级出口 <Outlet />,否则访问 /month 时只会看到 Layout,看不到 Month。转写稿的调试过程正是先测 / 和 /new 能显示,再测二级路径发现只显示 Layout,补上 Outlet 后才显示“我是 Month / 我是 Year”。
把路由实例注入应用入口
为什么需要注入?createBrowserRouter 只是创建了路由表,React 应用还不知道它。怎么想?入口文件要使用 RouterProvider 包裹应用,并把 router 传给 router 属性。怎么做?在入口中导入 RouterProvider 和路由实例,把原来的 App 替换掉:
jsx
import { RouterProvider } from 'react-router-dom'
import router from './router'
root.render(
<RouterProvider router={router} />
)易错点:忘记导入 RouterProvider、实例没有默认导出、路径写错都会导致页面空白;转写稿中特别提醒“把 app 干掉,渲染 RouterProvider”,本质是让路由接管页面渲染。
用 TabBar 的 onChange 完成路由切换
为什么需要?底部 TabBar 点击后不能只改高亮,还要跳到对应路由。怎么想?看组件文档:TabBar 提供 onChange,点击时把当前项的 path 传出来;拿到 path 后调用 React Router v6 的 useNavigate 跳转。转写稿里“us gate”应纠正为 useNavigate,“陈北”指 TabBar。
怎么做?在布局组件中绑定事件:
jsx
import { useNavigate } from 'react-router-dom'
const navigate = useNavigate()
const switchRoute = (path) => {
navigate(path)
}
<TabBar onChange={switchRoute} />数据项里的 key 将来用于路由切换,onChange 回调拿到的就是它。易错点:不要用旧版 useHistory;useNavigate 必须在组件顶层调用;事件参数名要和回调形参对应;路径要和路由表一致,例如 /new、/month、/year,否则点击后匹配不到页面。
调试顺序与常见坑
先启动项目测一级路由:访问 / 看 Layout,访问 /new 看 New;再测二级路由:访问 /month、/year,若只显示父布局,就检查 Outlet。接着在 TabBar 的 onChange 中先 console.log(path),确认点击时能打印出 new、year、month,再调用 navigate(path)。这样能把“事件是否触发”和“路由是否跳转”分开排查。最后注意:项目需安装 react-router-dom v6;二级路径不要重复写父级 /;createBrowserRouter 拼写和导入不能错。转写稿还留了一个思考题:访问 / 时默认显示月度内容,可用索引路由或重定向处理,但本节先保证一级、二级路由和 TabBar 跳转跑通。
先抓住定义:路径与组件的对应关系
本节的核心只有一句话:一个路径 path 对应一个组件 component;当我们在浏览器中访问某个 path 时,这个 path 对应的组件就会在页面中渲染出来。
为什么需要: 在前后端分离的项目里,页面切换不能每次都向后端请求一份新 HTML,否则体验会被打断。前端路由要解决的是:地址变了,页面该显示哪一块内容,由前端自己根据路径来匹配组件。转写稿里的记账本演示很典型:点击“月度收支”和“年度收支”时,底部区域没有整体变化,只是中间内容区在变;点击“记一笔”时,整个页面和路径都变了。这说明路径变化会决定组件的渲染结果,而且组件之间还存在层级关系。
怎么想: 不要一上来就写配置,先观察页面:哪些部分切换时不动,哪些部分跟着路径变。不动的部分通常是父级布局组件,变化的部分是子级路由组件。转写稿分析后得出结论:Layout 是一级路由,它内部承载“月度账单”和“年度账单”两个二级路由;New 是另一个一级路由。这个判断就是前端路由设计的起点。
怎么做: 先建立“路由表”的意识:把 path 和 component 登记成对应关系。访问 / 时渲染 Layout,访问 /new 时渲染 New;在 Layout 的 children 中再登记月度、年度两个二级路径。这样浏览器地址命中哪个 path,就渲染哪个 component。
易错点: 前端路由不是“把组件都写出来再用 CSS 隐藏”,而是按路径匹配渲染;路径和组件必须登记清楚;一级、二级不要凭感觉分,要看页面整体布局是否切换。转写稿里说的“red gun routeuter down”应纠正为 react-router-dom,路由版本是 v6。
二级路由为什么需要出口:Outlet 的作用
为什么需要: 一级路由 Layout 负责整体框架,二级路由只负责框架内部的内容区。如果只在路由表里配了 children,但父组件里没有留渲染位置,二级组件就没有地方显示。
怎么想: 转写稿的调试过程很值得复盘:一级路由能显示,手动输入 new 能显示 New;但输入二级路径时,页面仍只显示 Layout,二级内容不出现。原因不是路由没配,而是 Layout 缺少二级路由出口。
怎么做: 在 Layout 组件中放置 Outlet。转写稿里口述的“atlet”就是 Outlet。父级路由渲染时,Outlet 的位置会渲染当前匹配到的子路由组件。补上后,再访问月度、年度路径,Monthly、Yearly 就能在 Layout 内部显示。
易错点: 二级不显示先查父组件有没有 Outlet;不要把二级路由再配成新的一级布局;Layout、Outlet 这类固定写法不要拼错。转写稿里“le out”应理解为 Layout,“mouse”“页儿”分别对应月度、年度组件。
点击菜单如何触发路由:从事件到跳转
为什么需要: 用户通常不会手输地址,而是点底部 TabBar。点击后要拿到目标路径,再让路由切换。
怎么想: 转写稿的实现思路是:菜单点击时绑定 onChange,回调里先拿到 path 参数;调试时先打印 path,看到点击“记账”打印 new,点击年度、月度分别打印对应路径,确认参数正确后再跳转。这个“先验证参数,再调用跳转”的过程很重要。
怎么做: 用 useNavigate 得到跳转方法,在事件回调中执行 navigate(path)。转写稿里说的“use gate”应纠正为 useNavigate。这样点击哪个菜单,就访问哪个 path,路由匹配后渲染对应组件。
易错点: 不要用 window.location 做跳转,否则容易整页刷新,失去前端路由的意义;事件参数要从菜单数据项的 key/path 来;v6 中不要套用旧版路由的跳转 API。
默认路径与匹配:访问 / 时显示谁
为什么需要: 用户一进来通常访问根路径 /,但根路径下可能需要默认显示“月度账单”,否则内容区为空。
怎么想: 转写稿最后留了思考题:输入 / 时也让月度组件显示。思路是给 Layout 的 children 配一个默认子路由,或把根路径重定向到月度路径。关键是父级 Layout 仍要渲染,二级内容在 Outlet 中出现。
怎么做: 在 children 中配置索引路由或默认重定向到月度路径;保证访问 / 时命中的是 Layout 下的默认子路由,而不是另起一个重复布局。
易错点: 默认子路由不要重复写父级布局;路径斜杠不要多写;匹配不到路径时要有兜底处理。这样,前端路由“路径对应组件渲染”的定义就落到了可运行的页面结构里。
功能演示:先明确三个核心模块
为什么需要:做综合案例前先看成品,避免一上来就写代码却不知道页面之间怎么组织。转写稿中演示了三个核心模块:月度收支、年度收支、新增一笔账单。
怎么想:先按“页面区域是否整体变化”理解需求。月度收支和年度收支主要切换中间内容,新增账单会跳到新的页面,这为后面设计一级、二级路由埋下伏笔。
怎么做:打开演示站点,依次查看:首页月度收支可切换月份;年度账单可切换年份;点击记账进入新增页,选择支出、填写金额、选择餐费并保存后,新记录会回到当月支出列表中。
易错点:不要把“功能演示”当成单纯看效果,要观察路径和布局变化;本节先完成环境准备,不急着实现业务。
创建 CRA 项目并安装依赖
为什么需要:项目要使用路由、状态管理、时间处理、类名处理、移动端组件库和请求库,先把依赖装齐,后续开发才不被环境问题打断。
怎么想:继续使用熟悉的 CRA 创建项目;转写稿明确需要安装:Redux 相关的 @reduxjs/toolkit、react-redux,路由 react-router-dom,时间处理 dayjs,类名处理 classnames,移动端组件库 antd-mobile,请求库 axios。
怎么做:
bash
npx create-react-app react-bill-test
cd react-bill-test
npm i @reduxjs/toolkit react-redux react-router-dom dayjs classnames antd-mobile axios安装后打开 package.json,在 dependencies 中检查这些包是否都在。转写稿中就是这样逐项确认的。
易错点:包名要写准确,例如是 react-router-dom、antd-mobile、classnames;安装完不要只看终端,要回到 package.json 核对。
清理 CRA 默认文件
为什么需要:CRA 自带样式、logo、测试文件等会干扰案例阅读,先清理成最小入口,后面再逐步添加。
怎么想:保留入口 index.js 和根组件 App.js,删除暂时不用的样式、logo、报告文件和测试文件。转写稿还提到暂时去掉 StrictMode,因为它会让部分钩子执行两次,容易让初学者疑惑。
怎么做:清理 App.css、logo.svg、reportWebVitals.js、测试文件等;index.js 中保留根渲染;App.js 保留一个简单结构。随后执行:
bash
npm start转写稿中遇到端口占用,终端提示后输入 Y 替换端口即可。
易错点:不要误删 index.js 入口;去掉 StrictMode 只是本案例为减少干扰,不是所有项目都必须删除;启动成功后再提交代码。
配置 @ 别名:路径解析
为什么需要:深层目录中用相对路径会写成很多 ../../,容易出错。配置 @ 指向 src 后,导入更直观。
怎么想:CRA 底层封装了 webpack,默认配置是黑盒,直接改定制化不方便,所以借助 @craco/craco 扩展配置。注意别名分两部分:webpack 的路径解析,以及 VS Code 的路径联想。
怎么做:先安装开发依赖:
bash
npm i -D @craco/craco在项目根目录新建 craco.config.js:
js
const path = require('path')
module.exports = {
webpack: {
alias: {
'@': path.resolve(__dirname, 'src')
}
}
}再修改 package.json 的启动和打包命令:
json
"scripts": {
"start": "craco start",
"build": "craco build"
}易错点:craco.config.js 必须放在项目根目录;修改 scripts 后仍执行 npm start,但内部已由 craco 接管;别名键 @ 与后面 jsconfig 配置要一致。
配置 @ 别名:VS Code 联想
为什么需要:只配置 webpack 能编译,但编辑器输入 @/ 时没有提示,选择目录仍不方便。
怎么想:这是编辑器层面的配置,不影响打包,只提升开发体验。转写稿明确在根目录新增 jsconfig.json。
怎么做:
json
{
"compilerOptions": {
"baseUrl": "./",
"paths": {
"@/*": ["src/*"]
}
}
}保存后输入 @/,应能看到 src 下目录的联想提示。可新建 src/test.js 写一个求和函数,在 index.js 中用 import sum from '@/test' 测试;能正常启动、无报错,说明别名生效。
易错点:jsconfig.json 也要放根目录;它只负责联想,真正解析仍靠 craco 的 webpack 配置;两边路径规则要对应。
启动验证与提交
为什么需要:环境配置完成后要确认项目能跑、别名能用,再提交一个干净基线。
怎么想:先验证启动,再验证 @ 导入,最后提交“项目初始化”。
怎么做:
bash
npm start
git add .
git commit -m "项目初始化"易错点:若别名导入报错,先检查 craco 配置和 scripts 是否改对;提交前确认依赖已安装、页面能正常打开。
课件图示





代码示例
示例 1:路由配置与注入(一级路由 + 嵌套二级路由)
jsx
// src/router/index.jsx
import { createBrowserRouter } from 'react-router-dom'
import Layout from '../pages/Layout'
import Month from '../pages/Month'
import Year from '../pages/Year'
import New from '../pages/New'
// 一级路由:Layout(布局容器)、New(记一笔)
// 二级路由:Layout 内部的 Month(月度账单)、Year(年度账单)
const router = createBrowserRouter([
{
path: '/',
element: <Layout />,
children: [
{ path: '', element: <Month /> }, // 访问 / 时渲染月度账单
{ path: 'year', element: <Year /> } // 访问 /year 时渲染年度账单
]
},
{
path: '/new',
element: <New />
}
])
export default routerjsx
// src/pages/Layout/index.jsx
import { Outlet } from 'react-router-dom'
const Layout = () => {
return (
<div>
<div className="container">
{/* 二级路由的出口,Month / Year 在这里渲染 */}
<Outlet />
</div>
<div className="bottom">
{/* TabBar 见示例 2 */}
</div>
</div>
)
}
export default Layoutjsx
// src/index.js
import ReactDOM from 'react-dom/client'
import { RouterProvider } from 'react-router-dom'
import router from './router'
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(<RouterProvider router={router} />)要点解释
createBrowserRouter([...])接收一个路由对象数组,path与element一一对应;返回的router实例必须通过<RouterProvider router={router} />注入,组件内部才能使用路由相关 Hook。- 二级路由不是另起一套配置,而是写在父路由对象的
children里;父级path为/,子级写相对路径(''表示 index,'year'表示/year)。 - 易错点:只配了
children但父组件里忘记写<Outlet />,二级路由匹配成功却渲染不出来——现象是访问/year时页面仍只显示“我是 Layout”。 - 易错点:
Outlet要放在希望二级内容出现的位置(这里在底部 TabBar 之上),位置放错会导致布局错乱。 - 易错点:
createBrowserRouter的对象数组里,element必须是 JSX(<Layout />),写成Layout函数名不会渲染。 - 开发阶段如果把
<StrictMode>包在入口,Effect 会执行两次,调试时容易误以为接口被请求了两遍,可暂时去掉。
示例 2:TabBar 标签栏 + 点击切换路由
jsx
// src/pages/Layout/index.jsx
import { Outlet, useNavigate } from 'react-router-dom'
import { TabBar } from 'antd-mobile'
import { AppOutline, CalendarOutline, AddCircleOutline } from 'antd-mobile-icons'
import './index.css'
// key 就是将来要跳转的路由 path,必须和路由表里的 path 对应
const tabs = [
{ key: '/', title: '月度账单', icon: <AppOutline /> },
{ key: '/year', title: '年度账单', icon: <CalendarOutline /> },
{ key: '/new', title: '记一笔', icon: <AddCircleOutline /> }
]
const Layout = () => {
const navigate = useNavigate()
// 点击菜单,拿到当前项的 key(即 path),做路由跳转
const switchRoute = (path) => {
navigate(path)
}
return (
<div className="layout">
<div className="container">
<Outlet />
</div>
<div className="bottom">
<TabBar onChange={switchRoute}>
{tabs.map((item) => (
<TabBar.Item
key={item.key}
icon={item.icon}
title={item.title}
/>
))}
</TabBar>
</div>
</div>
)
}
export default Layoutcss
/* src/pages/Layout/index.css */
html,
body,
#root {
height: 100%;
}
.layout {
height: 100%;
display: flex;
flex-direction: column;
}
/* 二级路由内容区占满剩余空间,超出可滚动 */
.container {
flex: 1;
overflow-y: auto;
}
/* TabBar 固定在底部 */
.bottom {
flex-shrink: 0;
border-top: 1px solid #eee;
}要点解释
TabBar的onChange在点击切换时自动触发,回调参数就是当前TabBar.Item的key,所以tabs里把key直接写成路由 path,拿到后navigate(path)即可跳转。- 易错点:
key写成'year'(少了/)或'month'之类的随意值,navigate会按相对路径跳转,导致地址变成/year/year这类错误路径。 - 易错点:
useNavigate只能在被RouterProvider包裹的组件内使用,Layout 必须是被路由渲染出来的组件(element: <Layout />),否则报“useNavigate 只能在 Router 上下文中使用”。 tabs.map时key必须唯一且不可省略,否则控制台报 warning,切换时也可能渲染错位。- 布局上用
flex让内容区flex: 1自适应、TabBarflex-shrink: 0固定在底部,避免内容把 TabBar 顶出视口。 - 使用 antd-mobile 的样式依赖,需要额外安装
antd-mobile与antd-mobile-icons,并在入口引入全局样式,否则组件样式不生效。
示例 3:@ 别名路径配置(路径解析 + 路径联想)
js
// craco.config.js —— 必须放在项目根目录,文件名固定
const path = require('path')
module.exports = {
webpack: {
alias: {
// 把 @ 解析为 src 目录
'@': path.resolve(__dirname, 'src')
}
}
}json
// jsconfig.json —— 放在项目根目录,供 VS Code 做路径联想
{
"compilerOptions": {
"baseUrl": "./",
"paths": {
"@/*": ["src/*"]
}
}
}json
// package.json 片段:用 craco 接管原来的启动与打包命令
{
"scripts": {
"start": "craco start",
"build": "craco build"
}
}jsx
// src/index.js —— 验证别名是否生效
import { sum } from '@/test'
console.log(sum(1, 2))要点解释
- CRA 把 webpack 配置封装成黑盒,想改
alias必须借助@craco/craco这个扩展配置插件(装在开发依赖-D),配置文件名字必须是craco.config.js。 - 易错点:
craco.config.js和jsconfig.json都必须放在项目根目录,放到src里不会生效。 - 易错点:只建了
craco.config.js却没改package.json里的start/build命令,依旧执行react-scripts start,别名完全不生效。 jsconfig.json只负责 VS Code 的联想提示,craco.config.js才负责真正的编译解析,两者缺一不可:少前者没提示,少后者编译报“找不到模块”。path.resolve(__dirname, 'src')中__dirname是当前配置文件所在目录(即项目根目录),直接写'./src'容易因相对路径基准不同而出错。
常见错误
1. 配完 @ 别名,代码里 import xxx from '@/xxx' 仍然报"找不到模块"
- 现象:编辑器不报红但运行报错,或两边都报错;
npm start直接失败。 - 原因:三个环节任一缺失——
craco.config.js没放在项目根目录(放到src里最常见);package.json的start/build没换成craco start/craco build;改完没重启 dev server(webpack 配置只在启动时读取一次)。 - 修法:确认根目录下有
craco.config.js;核对scripts已被 craco 接管;杀掉进程重新npm start;用一次真实的@/引用跑通再继续写业务。
2. VS Code 里敲 @/ 没有任何路径联想,但项目能跑
- 现象:编译正常,只是没有自动补全,写路径全靠手打。
- 原因:把"路径解析"和"路径联想"当成一件事——前者是 webpack(craco)的事,后者是编辑器读
jsconfig.json的事;或jsconfig.json位置不对、baseUrl/paths写错、通配符*漏写。 - 修法:根目录新建
jsconfig.json,baseUrl设为当前目录,paths里写"@/*": ["src/*"];保存后若仍无提示,重启 VS Code 窗口(编辑器只在启动时加载该配置)。
3. 二级路由能匹配上,但页面只渲染布局容器,子页面内容是空的
- 现象:访问
/month、/year时地址栏对了,Layout里的按钮、底部 TabBar 都在,中间区域一片空白。 - 原因:
Layout组件里漏写<Outlet />;或子路由children的path写成了绝对路径(/month)而非相对路径(month),导致嵌套关系失效。 - 修法:在
Layout的内容区补<Outlet />;子路由path一律写相对值;用"改地址栏 + 点 TabBar"两种方式各验证一次。
4. 启动即报 name is a required option for createSlice
- 现象:页面白屏,控制台报错,整条 Redux 链路全断。
- 原因:
createSlice的配置对象里漏了name字段(写业务时只顾着写initialState和reducers)。 - 修法:补上
name;顺带检查另外两个必填项initialState、reducers是否齐全。改完刷新,先看 Redux DevTools 里有没有对应 slice,再往下写组件。
5. 组件里 dispatch(getBillList()) 报 "is not a function"
- 现象:异步 action 拿不到,或者解构出来是
undefined。 - 原因:
createSlice返回值解构时写错字段名——是actions(带 s),不是action;同理reducer是单数。 - 修法:统一记住
const { actions, reducer } = slice;解构后立即在下面dispatch一次自测,确认函数存在再往下写业务。
6. 组件库主题色改了,但按钮还是默认蓝
- 现象:样式文件写了 CSS 变量,页面无变化。
- 原因:样式文件没在入口
import(写了但没加载);变量名拼错(组件库的变量名是固定契约,不能自造);做局部定制时,外层类名和实际渲染出的 DOM 类名对不上。 - 修法:先在入口文件确认 import 生效(可在浏览器里看该样式是否被加载);变量名从官方文档直接复制;局部定制时用 DevTools 确认外层元素的真实类名。
7. mock 接口 404 或返回的不是预期数组
- 现象:
axios.get拿到 404,或拿到{}导致列表渲染报错。 - 原因:启动命令里的 json 文件路径写错(相对路径是相对执行命令时的当前目录);端口被占用换端口后前端地址没同步改;json 顶层结构被改成了对象而代码按数组取。
- 修法:先在浏览器直接访问接口地址确认返回结构,再回代码里写;把接口地址抽成常量,改端口只改一处。
8. TabBar 点击后高亮和路由对不上,或从记账页返回时回退路径混乱
- 现象:点"记账"再返回,回到的是上上次的页面;或某个 tab 永远不高亮。
- 原因:
onChange回调里拿到的 key 与path没做对应;跳转用了默认的push,导致历史栈里堆了一串中间态。 - 修法:先在回调里
console.log打印 key,确认它就是路由 path;跳转时按需加{ replace: true };高亮态用useLocation().pathname反推,而不是自己维护一份 state。
练习
1. 默认路由兜底 访问 / 时直接展示月度账单内容,且地址栏仍保持 /。要求:用路由配置解决,不要在 Layout 里写 useEffect 手动跳转。(提示:想一想 index 路由和 Navigate 两种做法,说说各自在"地址栏显示"上的差别。)
2. 多级路径下的 TabBar 高亮 把路由改成 /bill/month、/bill/year,再让底部 TabBar 在进入 /bill/new 这类更深层级时也能正确高亮/不高亮。要求:写出你的匹配规则,并说明为什么不能用 pathname === path 直接比较。
3. 串起一条写链路 新增一个 addBill 异步 action:POST 到 mock 服务保存一笔账单,成功后重新拉取列表并让首页刷新。要求:失败时给出提示,不要在组件里直接 axios。
4. 一条命令启动两套服务 把 mock 服务和前端服务合并成 npm start 一条命令,同时保证任意一方启动失败时你能立刻发现。要求:说明你用的方式(&、npm-run-all 或 concurrently)在 Windows / macOS 上的差异,以及日志混在一起时你怎么定位问题。
5. 局部主题定制 在不动全局主题的前提下,只让"记账"页面里的按钮变成另一套配色。要求:给出样式写法,并在 DevTools 里验证该样式的作用范围没有溢出到别的页面。
原视频对照
| # | 视频 | 时长 |
|---|---|---|
| 1 | 记账本-功能演示和环境创建 | 09:12 |
| 2 | 记账本-配置别名路径@ | 12:13 |
| 3 | 记账本-数据Mock实现 | 08:56 |
| 4 | 记账本-整体路由设计 | 10:12 |
| 5 | 记账本-antD主题色定制 | 11:08 |
| 6 | 记账本-Redux管理账目列表 | 16:26 |
| 7 | 记账本-TabBar功能实现 | 10:33 |
小结
@别名是两件事:webpack 侧负责"编译能找到文件",编辑器侧负责"写的时候有提示"。只配一半,项目照样能跑,但开发体验会持续掉血。- 改构建配置的三件套是固定的——配置文件在根目录、启动命令被接管、改完必须重启。踩坑几乎都出在漏掉其中一条。
- 路由层级不要拍脑袋定,看"变化区域":整个页面换 = 一级,只有局部内容换 = 二级;二级一定要有
<Outlet />作为出口,否则匹配成功但渲染为空。 - 组件库的正确姿势是先抄 demo 跑通、再改定制。跑不通时先怀疑依赖没装(如 sass)或样式没引入,而不是怀疑组件本身。
- RTK 的写法高度模板化,
name / initialState / reducers、actions(带 s)、reducer(单数)这些固定项错一个就整条链路断,写完先看 DevTools 再写组件。 - 调试的顺序应该是从外到内:先看 Network 拿到数据没有,再看 Redux 里存进去没有,最后才看组件渲染——跳过前两步直接改 JSX 是最耗时的做法。
- 项目里的重复手工操作(两个窗口、两条命令、两处地址)都值得在当天就合并掉,它省的不是这一次的时间,是后面每一次忘记执行时的排查时间。