大家好,本菜鸟又开始写博客了!
每一天感觉收获最多的环节其实就是写博客复盘问题。
今天主要完成了三件核心工作:重新规划项目架构、实现主题切换功能、完成 UI 占位布局,同时对技术学习和项目开发有了新的思考.... 本文是开发心路的反思与记录,核心落地的功能是主题切换。
一、重新规划项目架构与技术选型
今天其实我本来的规划是要把编辑器的文件导入功能这个模块进行开发,参考的是 Typora,它可以打开文件夹,支持本地的文件夹目录树形组件,支持点选,然后也支持本地修改。
但是我遇到了个问题,就是我现在因为没有后端服务,它需要是一个怎样的模式。是即开即用,就是打开编辑一下,然后就马上导出吗?还是采用文件夹目录导入的形式,本地或者云端都支持离线修改?
我本来想要做的是文件夹目录导入,然后拥有一个嵌套的树形结构,可以配合递归的组件进行渲染。用户可以点选展开相应的文件夹,点开相应的文件进行编辑器内的导入,然后再次进行编辑保存。 整个数据链路基于 Web,但当前无后端服务,文件内容和目录结构只能存在浏览器的 IndexedDB 里,缺少唯一标识(例如 UUID),文件读取标识不明确。
之前 H5 传统方案只能读不能写回原文件,涉及产品设计问题,后续要做个人 Web 端知识库笔记产品,全部历史记录存在 IndexedDB 里从产品角度并不可行。
架构设计上考虑镜像或链接,BlockNote 底层是 JSON 格式,涉及数据库选型,我对比了 PG、MySQL 和 MongoDB,也反思了之前做后端项目囫囵吞枣的问题,有太多的细节还需要重新学习...... 最终重新选择了架构方案、规划了项目安排。
二、实现主题切换功能
第二个是今天重点做的主题切换功能。
我一开始很困扰,在我的认知里面,主题切换还以为是简单地 “换了个颜色”,但实际上是状态分发和 CSS 变量映射。Context 作为 React 实现状态跨组件分发的底层原理,主题切换正是它的典型应用场景,可以在应用顶层放置 Context Provider 让所有组件获取主题状态。
但我最终选择了工程化的方案,用 next-themes 这个封装好的、基于 Context 的成品库来实现。点击 UI 切换 button 改变状态并通知所有组件的过程中,我也借此更加明确了 Context 的适用场景:处理嵌套较深的组件、多个组件需要全局共享同一状态时使用。
更换样式时不能直接操作 DOM,否则会触发浏览器重排、重绘、重渲染,或导致 SSR 服务端渲染闪烁,底层原理较为复杂。
而 next-themes 帮我规避了这些手动实现的坑,核心要解决的是三套系统的状态同步:
- next-themes 提供的 light/dark 主题状态
- AntD 组件的样式算法
- BlockNote 编辑器的主题配置
编写 ThemeBridge 桥接层组件
import { useTheme } from "next-themes";
import { ConfigProvider, theme } from "antd";
export function ThemeBridge({ children }: { children: React.ReactNode }) {
const { resolvedTheme } = useTheme();
const algorithm =
resolvedTheme === "dark" ? theme.darkAlgorithm : theme.defaultAlgorithm;
return <ConfigProvider theme={{ algorithm }}>{children}</ConfigProvider>;
}
将主题状态 “翻译” 给 AntD 的 ConfigProvider,同时修改硬编码图标颜色、定义状态将主题值传递到 CSS 变量中,既保证了状态同步,又减少了组件重渲染次数,最终完成了主题功能。
import { BlockNoteView } from "@blocknote/mantine";
import "@blocknote/mantine/style.css";
import "@blocknote/core/fonts/inter.css";
import { BlockNoteEditor } from "@blocknote/core";
import { useCallback } from "react";
import { useTheme } from "next-themes";
interface EditorProps {
editor: BlockNoteEditor;
onSave?: (content: any) => void;
noteId?: string;
}
export default function Editor({ editor, onSave, noteId }: EditorProps) {
const {resolvedTheme} = useTheme();
const themeValue = resolvedTheme === 'dark' ? 'dark' : 'light';
const handleSave = useCallback(() => {
if (onSave && noteId) {
onSave({
id: noteId,
content: editor.document,
updatedAt: Date.now(),
});
}
}, [onSave, noteId, editor.document]);
return <BlockNoteView editor={editor} theme={themeValue} onChange={handleSave} />;
}
三、完成 UI 占位布局开发
第三件事是做了 UI 的占位布局。
这一块我觉得没有太大难度,但是我发现 flex 布局用的是比较多的,还有 z-index 避免遮挡。有冲突的样式可以在全局 CSS 里面进行设置。以前觉得很麻烦、需要让 AI 反复修改的 UI,其实也就几个关键参数,比如设置横向纵向居中、使用 fixed 或 sticky 实现固定位置。
开发时要找准 render 的位置,把相关父子组件当作一个整体来看:关注父级是否支持 flex、有没有原生高度、是否会塌陷。
我也发现 UI 其实是最简单的一层,和业务逻辑关联很小,只是项目中很小一部分。
四、学习与成长总结
我感觉自己好像是进步了,但是这也可能是一种错觉,回头看之前处理不来的小问题现在都能解决,也对 React 有了自己的理解。
我理解中 React 的核心是用 JSX 描述 UI,接着拆分 UI 层、逻辑层、数据层,目前感受到的重点还是在于状态管理和组件通信 —— 比如这次主题切换,让我明白手写 Context 容易留下技术债务(边界条件处理不到位),而选择成熟的库是更优的工程化选择,后续也可以试一试手搓,理解原理,再复刻优化。
今天,我边学边拓展,学习了重排、重绘、架构设计、数据存储、UI 渲染等知识,也更加注重组件页面的纯净度和逻辑复用。我感受到快速学习的核心是实践加思考,带着需求和问题去看开源文档效率会更高 —— 比如这次做主题切换,带着问题去查文档,比单纯看概念要清晰得多。
我也反思了之前为做项目而做项目,只追求功能实现的问题,而根本没有关注代码,类似于无代码的技术自嗨,做出来的小项目都是黑盒一般,自己都不能理解,盲目依赖 AI 生成内容。
现在虽然还是会用 AI,但review代码时会更加在意类型安全,以及代码是否符合当前相关上下文,也开始关注产品体验的细节等等。
坚持,加油!