|
```html 源码查看 我盯着屏幕上那个报错,源码查已经快二十分钟了。源码查控制台就一行红字:Uncaught TypeError: Cannot read property 'map' of undefined。源码查代码是源码查我自己写的,逻辑也顺过一遍,源码查可它就是源码查不认。这时候我做了唯一正确的源码查事——把目光从自己的代码上挪开,点开了node_modules文件夹。源码查 源码查看这件事,源码查门槛比大多数人想象的源码查要低得多。你不需要什么“阅读源码的源码查十个技巧”,也不需要把整个React仓库克隆下来。源码查打开浏览器开发者工具,源码查在Sources面板里找到那个报错的源码查文件,点一下格式化按钮,源码查压缩过的webpack产物就变成了可读的代码。就这么简单。我第一次这么干是为了搞清楚lodash的debounce为什么在我传了leading: true之后还是延迟执行。翻到源码里那个invokeFunc函数,看了三分钟,发现是我自己把maxWait设成了0。问题不在库里,在我。 很多人把源码当成圣经,觉得里面每一行都经过千锤百炼。实际翻进去你会发现,有些代码写得跟你凌晨三点提交的版本差不多。变量名是a、b、temp,注释写着// TODO: fix this later,然后这个later已经later了六年。我在一个知名状态管理库的源码里见过这样的片段: function shallowEqual(objA, objB) { if (objA === objB) return true; if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) { return false; } // 后面还有二十行 } 这代码没毛病,但也没什么玄机。它就是老老实实比较每个键。你看了之后不会觉得“哇,大神”,只会觉得“哦,原来就是一层层比”。这种祛魅很重要。源码不是拿来膜拜的,是拿来用的。 真正让我养成看源码习惯的,是一次调试axios拦截器的经历。项目里有个请求死活不走我配置的transformRequest,文档翻了三遍没找到原因。我直接打开axios/lib/core/dispatchRequest.js,顺着调用栈往下看,发现transformRequest只在data存在且是特定类型时才会被调用。我的data是undefined,所以整个转换逻辑直接被跳过了。文档没写这个前提,但源码写了。从那以后,遇到库的行为和预期不符,我的第一反应不再是搜issue,而是直接去看那个函数。 看源码也有副作用。你会开始对“魔法”免疫。什么自动绑定、依赖注入、响应式更新,拆开来看无非是Object.defineProperty或者Proxy加一个回调队列。前端框架的diff算法听起来高深,翻进去看就是两个数组比来比去,同层比较,加个key优化。知道这些之后,你写代码时心里更有底,因为你知道底下发生了什么。 当然,不是所有源码都值得花时间。有些库的源码组织得像迷宫,一个函数跳转五次还没到核心逻辑。这时候我会退出来,换个思路——看它的测试用例。__tests__文件夹往往比源码本身更能说明问题,因为测试里直接写了输入和预期输出。我上次想搞明白immer的produce怎么处理嵌套对象,就是先看的测试文件,十分钟就清楚了。 最后说个具体的。上周同事问我,为什么JSON.parse(JSON.stringify(obj))会丢掉undefined和函数。我没直接回答,而是打开Node的REPL,敲了几行代码给他看: > JSON.stringify({ a: undefined, b: function(){ }}) '{ }' > JSON.stringify({ a: null, b: 1}) '{ "a":null,"b":1}' 然后我让他去看JSON.stringify的规范文档,里面明确写了:值为undefined、函数或Symbol的属性在序列化时会被忽略。这不是bug,是设计。源码和规范就在那里,翻一下比猜快得多。 所以下次再遇到“这库怎么不按我想的来”,别急着骂。打开node_modules,找到那个文件,点进去看两眼。大部分时候,答案就在那儿,而且比你想象的直白。 ``` |