第四章:技术选型

0 阅读3分钟

凌晨一点,林晨还在公司。

办公室的灯一排一排灭掉,只剩他这一片亮着。屏幕上,Chrome DevTools 的 Performance 面板刚刚跑完一次录制,时间轴被红色和黄色填满。

他盯着那条明显的紫色长条——

Recalculate Style + Layout

“果然。”

章节内容一多,DOM 节点数量失控,哪怕只是改个字号,整棵渲染树都会被牵着跑。

他打开代码,把核心逻辑单独抽了出来。

watch(
  () => readerSettings,
  () => {
    // 更新阅读样式
    applyReaderStyle()
  },
  { deep: true }
)

这段代码本身没错,但deep watch 就像一把钝刀——

方便、好用,但每次都伤筋动骨。

林晨把手放在键盘上,没有立刻改。

他在想的不是“怎么写”,而是——

要不要现在写得这么“重”


第二天早上,站会气氛不太轻松。

“阅读器这块,我们是不是可以直接用现成的虚拟列表库?”

后端同事随口提了一句。

苏雨点头:“我也觉得,用成熟方案更稳。”

林晨却摇了摇头。

“列表库解决的是固定高度或可预估高度的问题,”

他解释得很慢,“但章节是富文本,图片、标题、段落高度都不确定。”

他走到白板前,画了个简单示意。

“如果强行套库,要么牺牲滚动连续性,要么引入大量占位计算,反而更复杂。”

苏雨看着白板,皱眉:“那你打算自己写?”

“写一层轻量的虚拟渲染。”林晨说。

会议室里静了一下。

陈浩抬头:“风险?”

“有。”

林晨没有回避,“但可控,而且能针对我们的场景优化。”


午后,争论升级了。

苏雨翻着原型图:“我担心你们工程这边,一旦开始自研,就会不断往‘技术最优’走,最后体验被牺牲。”

林晨听出来了——

她说的不是代码,是不信任

“我不追求最优。”他说,“我追求可解释。”

“什么意思?”

“当用户卡顿时,我能清楚知道卡在哪。”

“而不是被库封装住,只能祈祷。”

这一次,他的语气比之前更坚定。


傍晚,林晨开始真正落代码。

他没有一开始就“虚拟化一切”,而是选了最保守的切入点:

只虚拟不可见章节

const VISIBLE_BUFFER = 2

function getRenderRange(currentIndex, total) {
  const start = Math.max(0, currentIndex - VISIBLE_BUFFER)
  const end = Math.min(total, currentIndex + VISIBLE_BUFFER)
  return { start, end }
}

不是炫技。

只是把“用户永远只看当前章节附近内容”这个业务事实,变成了代码约束。

真正的渲染逻辑被包在一个计算属性里:

const renderChapters = computed(() => {
  const { start, end } = getRenderRange(currentIndex.value, chapters.length)
  return chapters.slice(start, end)
})

DOM 数量瞬间降了一个数量级。

他刷新页面,滚动。

顺了。


但问题没有结束。

设置面板一动,依然会触发整块内容重算。

林晨盯着 watch 那一行代码,敲下了删除键。

他改成了拆分状态 + 精准依赖

const fontSize = computed(() => readerSettings.fontSize)
const lineHeight = computed(() => readerSettings.lineHeight)

watch(fontSize, updateFontSize)
watch(lineHeight, updateLineHeight)

没有 deep,没有魔法。

只是让变化只影响该影响的部分

这不是技巧,是前端最基础的原则之一:

减少不必要的依赖传播****


晚上十点,苏雨又一次走到他工位前。

“我刚拉了最新代码。”她说,“体验……比我想象中好。”

林晨没有抬头:“但设置反馈比你原型里慢了大概 50ms。”

“普通用户感觉不到。”

“但你能。”他说。

苏雨笑了一下。

“你这个人,真的很前端。”

林晨也笑了。

他知道,这不是夸他写代码。

是认可他在边界里坚持


深夜,PR 被合并。

陈浩在评论区只留了一句话:

“技术方案可解释,风险可控,先这样。”****

林晨关掉电脑,靠在椅子上。

他忽然意识到一件事——

真正的技术选型,从来不是框架、库、实现方式。

而是:

你愿不愿意为结果负责。