读书随想——学习力的五项修炼

封面

  • 书籍信息:
  • 反向学习:学习力的五项修炼
  • 作者: 刘澜
  • 出版社: 机械工业出版社

中学开始被各种鸡汤、励志、“读者” 文荼毒已久,这类文章往往逻辑结构混乱,充满口号式的重复,用貌似合理的语言结构胡说八道。 今天看的这本书很不一样,虽然名字听上去有点唬人,但有挺多能引起我共鸣,也有挺多打破我之前认知的内容,所以流水账式的记录下。后面看的过程中,也发掘这本书逻辑上有很多觉得不顺的。

总有各种力学概念不断被发明 —— 认知力、产品力等等,初始是由于没办法用精简的词汇描述抽象的概念,所以发明一个某某力的新词。之后就变成唬人、装杯的做法。继而形成风潮,貌似这样说话显得更高级。其实,除了伪装自己语言简练、生动之外,在书籍中往往是为了方面在这个笼统的概念中安插各种奇怪、勉强的解释。

不过“学习力”这个词还是比较清晰 —— 学习的能力。 现在 LLM 风行,人类获取信息/知识的成本急剧降低,同时碎片化的信息塞满了所有之前可以放空的时间,对于我这个ADHD来说,完全没有办法系统、持续的思考。 我想大部分人和我的困扰是一样的,没有动力学习,不知道怎么学习,然后轻易被人忽悠,脑子里已经学到的东西逐渐过时。

这本书的导论中就给出了几个关键的路径:

  1. 反向学习 —— 将自己已经知道的知识作为基础,通过主动发问、多人沟通、突破标准答案、试错等获得更新、更有质量的知识,这样的学习过程就是学习力的建立过程。这个过程中对于学习习惯的培养,思考能力的拓展,可能必最终学到什么更重要。
  2. “参考答案”的思维方式 —— 这是相对于“标准答案”的思维方式而言的,介绍如何突破片面、唯一的的思维方式。
  3. 聚焦到一个细分领域中学习,而不是碎片化的学习支离破碎的学习。
  4. 模式化学习,中知识中总结共性,提炼模式。
  5. 深层迁移 —— 还没看到这里。

反向学习

是的,“反向学习”也是作者引出的新概念。我的理解是,所谓“反向学习”其实是逐渐消除掉我们错误的学习习惯,包括:

  1. 标准答案思维
    1. 很多事情是没有标准答案的,而且追求非标准但是更合理的标准答案是有价值的。 这个论断不能适用于任何问题,比如当你骑自行车的时候,前方遇到障碍物是否应该停车,这种生活化的、简单的问题是有标准答案的。 经常没有标准答案的是开放结论的,难以获得足够信息但是有必须做出决策的问题, 例如高考报专业应该报哪个专业。这种情况下应该放下焦虑,避免盲从固定的思考路径。
    2. 被动学习。 对应的应当主动寻求想要学习的东西,并且发散思维。 但是主动学习比被动学习难多了,首先第一个问题,如果有充足的时间和资料,你要学什么? 如果没有一个老师或者教练的话,这种没有标准答案的问题,很难通过自己闭门造车想明白。 如果完全遵循兴趣,那我一定选择纯数学或者性高潮研究, 但是显然两者对我来说都没有太大价值,前者搞不懂,后者搞不到。
    3. 单人学习。 不知道是随着年龄变大,还是随着社会升级,我觉得人与人之间的边界越来越清晰,如果不是工作、生活的必要社交,我几乎不会和任何人有交流。所以一个多人学习的环境就非常难得,我也并不期望一个充满讨论的环境。 但是从个人感受来说,和人交流能快速得到不同的想法,从而快速纠正自身。 人都是不自知的,如果没有外部影响的话,进入思想反刍和死胡同是必然的。 我不喜欢多人的环境怎么办呢? 至少要有输出,输出是整理思维链路的有效方式。 要写下来,至少是思维连续地说出来。最好是费曼学习法那样,能教给别人。这里只是我的感受,原因是我极度不喜欢社交,但是输入从何而来呢? 只能是从各种短视频、长视频、书籍中来,这样的效率一定是低的,但是我舒服,我管你呢。
    4. 害怕出错。 这里我觉得作者有点把自己放在家长的位置上了。我可以容忍我孩子出错,为他创造试错的环境,但是换个角度,我的工作环境是否允许我出错? 绩效是无情的,而且可能由于各种原因,绩效会专门针对个人的失误和短板。 那我一定选择最稳妥的路径。 从我个人身上看的话,难以破局。只能尝试更多新鲜的事情,比如周末不喝大酒,出去看茶道,体验不同的环境,接收不同的声音。 不玩儿我闭着眼都能打过的疯狂电脑,尝试下我一直控制不好的FPS游戏,即便20枪都打不中,我可以摔完鼠标,接收这小小的失误。
    5. 重视结果而非过程。 这个和上面一个一样,要看环境和成本,我无法破局。
  2. 弥补短板,这部分有点反传统教育,或者说反初阶教育的论调。 中小学强调的从来就是全面发展,而且要全面优秀。哈哈,想起我们高中几个数学超级好,但是其他可能不好的同学,不知道他们怎么样了。有声音说之后初等教育也要鼓励偏科的天才,不知道真假。
    1. 被误用的木桶理论。 我听过一种观点是,木桶理论适用于团队,而不适用于个人。 觉得有些道理。
    2. 学习上的二八法则,这个小节太短, 没看明白作者想说什么。一个简要的结论是“你应该把你的全部注意力放到你擅长的事情上”,而不是“全部事情”。
    3. 弥补短板思维的两个坏习惯:“满分思维”, “不会规划自己的学习”。
  3. 碎片化学习。 嗯,无需多言。
  4. 学用脱节。 这个的类比,我想的就是学什么东西的时候,至少做做练习。 或者反向来说,有了真实的应用环境再学习,而不是凭空学一些没用的东西。 我知道这么说曲解了作者的意图,但是我的感觉是我学了什么东西未必有非常合适的应用场景,比如前段时间看汇编,我也不知道为什么看,也没什么应用场景,大概率下次真的用到还要重新看。

这章剩下的部分就是可以对照上面的坏习惯,建立一个自查的清单,客服这些换习惯。这里实际执行上的问题是,如果没有一个教练,或者个人有非常强的自律习惯(是习惯而不是能力)的话,人还是会回到坏习惯。我觉得这章的逻辑层次有点乱。

第一章其实是总章,后面的章节是从第一章中的每个小节中细化出来了。第二章是参考答案思维方式,就对应第一章的反对标准答案思维。 第三章中聚焦,对应的就是“弥补短板”。第四章的模式化学习,就对标碎片化学习。第五章深层迁移,就对标学用脱节。

这好像是一种不错的写作方式。我想过,如果让我讲一个话题,我可能一千字就顶天了。那如果把这一千字再拆分乘多个小论点,依次打开解释,嘿嘿,一下子又能水不少。

这本书不算太长,剩下的章节我也看完了,不过不想写笔记了,不然会出现各种重复的内容。

总结下来,只是阅读这样一本书并不会提高我的学习能力。还需要脱离书,动起来。

重读Vue3源码之reactivity

这是一篇流水账,边看代码边写,写到哪儿算哪儿。准备写完第一遍之后再总结重构。

Vue 的源码比较简洁,符合中国人的编码直觉,所以读起来会比较顺畅,比较有学习价值。

Vue 的代码是一个monorepo,大体分为下面几个部分:

  • reactivity 核心中的核心,可以脱离Vue 独立使用,包含了响应式的核心逻辑,比如scopeeffectdeplink 等。
  • compiler-* 这里其实是4个包,包含coredomsfcssr 4个编译器实现,分别对应核心框架、DOM(template部分)处理、单文件组件、服务端渲染。
  • runtime-* 包含三个包,分别是coredomtest
  • server-renderer 服务端渲染的实现。
  • shared 多个仓库共用的一些代码,例如枚举定义等。
  • vue 嗯,这里是入口。
  • vue-compat 这个是兼容Vue2 的代码实现。

这样一列就比较明确了,阅读顺序应该是reactivitycompiler-corecompiler-domcompiler-sfc ,后面再说。

响应式核心 —— reactivity

核心概念:

  • reactivity 最最核心的响应式变量封装对象,value必须是个Object对象。
  • ref 可以粗略的认为是reactive的上层封装,有了这层就能让基础数据类型,比如number实现响应式(其实就是包了一层)。
  • dep PubSub 模式中的订阅源。
  • link 关联 depeffect 的双向链表。
  • effect 响应式操作,简单来说就是订阅源变化之后要做什么操作。
  • computedeffect 是并列的,用于根据响应式变量计算出衍生的值。
  • effectScope 响应式生命周期的管理,说白了就是什么时候开始收集依赖,什么时候暂停、结束。

其他的还有watch 等等,按下不表。

响应式变量是如何封装的

开篇先看看,核心的响应式变量封装了个什么 —— Target。 显然是个Object,并且包含了几个特殊标记:

  • SKIP : 跳过响应式监听
  • IS_REACTIVE: 是否是响应式的,比如上面SKIP了的话,IS_REACTIVE 就是INVALID,后面也就不会再参与响应式过程。 IS_READONLY 是否只读、、IS_SHALLOW 是否是浅层响应式,这两个标记功能类似。
  • RAW: 这里就是响应式对象的原始值了,可以看到是个any类型。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
export interface Target {
[ReactiveFlags.SKIP]?: boolean
[ReactiveFlags.IS_REACTIVE]?: boolean
[ReactiveFlags.IS_READONLY]?: boolean
[ReactiveFlags.IS_SHALLOW]?: boolean
[ReactiveFlags.RAW]?: any
}

export const reactiveMap: WeakMap<Target, any> = new WeakMap<Target, any>()
export const shallowReactiveMap: WeakMap<Target, any> = new WeakMap<
Target,
any
>()
export const readonlyMap: WeakMap<Target, any> = new WeakMap<Target, any>()
export const shallowReadonlyMap: WeakMap<Target, any> = new WeakMap<
Target,
any
>()

之后是4个全局变量 reactiveMapshallowReactiveMapreadonlyMapshallowReadonlyMap,分别存储了所有的响应式变量Target 和用于封装这个Target的Proxy的映射关系。

从4个变量的名字可以看出来,一般响应式变量、浅层响应式变量、只读变量、浅层只读变量4个是分开存储的。

对应的也有4个方法来创建不同类型的响应式变量:reactiveshallowreadonlyshallowReadonly

这4个方法都是对createReactiveObject 方法的封装。嘿嘿,主角登场。

核心方法: createReactiveObject

这个方法的主体逻辑非常清晰简洁。

  1. 如果Target是个Proxy,并且想在标记为响应式的Target上创建一个只读的响应式变量,就直接返回原响应式变量。
  2. 获取target的响应式类型,这个类型分为集合、一般变量和无法响应式的变量。这里的无法可能是标记为不需要响应式。这里集合类型包含map和set,一般变量类型包含一般对象和数组。嗯,就这几种,非常直观。 上面已经说过Target必须是Object,所以不用考虑其他情况。
  3. 先在全局map中查看下,是不是已经创建过Proxy,如果创建过则直接返回。
  4. 如果没有的话,根据响应式类型装配不同的handlers
  5. 在全局map中记录这个新创建的proxy,并返回。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
function createReactiveObject(
target: Target,
isReadonly: boolean,
baseHandlers: ProxyHandler<any>,
collectionHandlers: ProxyHandler<any>,
proxyMap: WeakMap<Target, any>,
) {
// DEV 模式下的类型检验,省略。
// target is already a Proxy, return it.
// exception: calling readonly() on a reactive object
if (
target[ReactiveFlags.RAW] &&
!(isReadonly && target[ReactiveFlags.IS_REACTIVE])
) {
return target
}
// only specific value types can be observed.
const targetType = getTargetType(target)
if (targetType === TargetType.INVALID) {
return target
}
// target already has corresponding Proxy
const existingProxy = proxyMap.get(target)
if (existingProxy) {
return existingProxy
}
const proxy = new Proxy(
target,
targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers,
)
proxyMap.set(target, proxy)
return proxy
}

Proxy封装了个啥: handlers

从上面代码可以看出,创建响应式变量中到底响应了个啥,再深层的逻辑都在handlers中。和上面逻辑对应,只读/可写、集合/一般对象、深层/浅层,叉乘下来貌似需要很多类型的Handler,但是实际上只有几个核心的类。

Handlers的类结构

不同类型变量的封装逻辑

先从最简单的 BaseReactiveHandler 开始。 这个类只封装了一个get方法。 做的事情就是:

  1. 排除掉一些Vue内置的标记位,直接返回不参与响应式;
  2. 如果要访问的property就是Target的原始值呢? 有个边缘Case,就是用户的原始值就是一个Proxy,但是仿照了Vue的响应式Proxy,那直接返回这个用户定义的Proxy。(这里给我看郁闷了,有点绕)
  3. 如果目标对象是个数组,这次get其实是访问数组的方法,比如forEach呢? 这里会从全局arrayInstrumentations取对应的方法,直接返回,原因后面再管。
  4. Reflect获取具体的属性值。** 注意 ** ,直到这里,都还没有收集依赖哦。
  5. 如果当前Proxy是只读的,或者访问的是一些内定的不需要跟踪的属性,嘿嘿,就不用收集依赖了。
  6. 反之,收集依赖。 至于如何收集,后面再慢慢展开。 我粗看上去是一个LinkDep 组成的双向十字链表(不是)的样子。
  7. 按照常规情况,还需要将返回的值封装成reactive对象。 也就是说Vue中多层嵌套响应式对象,不是在创建的时候就遍历属性值创建Proxy,而是在get阶段,get到哪儿创建到哪儿。

还是比较简单的。

MutableReactiveHandler

下面是MutableReactiveHandler,增加了setdeletePropertyhasownKeys方法。

  • set 比较简单,因为get拿到的一定是reactive对象,所以先toRaw之后做了一些防御性编程。通过Reflect设置值之后,收集依赖。
  • deletePropertyhasownKeys 几乎就是单纯调用了Reflect,然后收集依赖。

有了这几个方法之后,Handler就能处理可写的对象了。

处理集合类型(Set/Map, 没有数组)的Handler比较特殊,并不是用类组装而成,而是调用createInstrumentationGetter方法生成的Handler.get,这个方法返回的类型是一个Instrumentation 对象。 乍一看很奇怪,为什么这么做呢?

首先,访问集合中的元素是不能直接按照property的方式访问的,而是调用集合的hasget等方法,所以收集依赖实际上就要劫持这些方法,劫持也是通过Proxy覆盖这些方法来实现的。

其次, createInstrumentationGetter 也只劫持了影响响应式的几个方法,比如forEachclear等。

不过这几个方法封装的都比较简单,大体流程都是先转化成原始值、检测是否有变化、触发依赖收集、返回值/调用具体的方法。略有区别的就是对于是否只读做区分处理。

什么时候开始收集依赖?

EffectScope 管理了什么时候开始、暂停、结束依赖收集。从构造函数看,这个scope类是一个树状结构,允许嵌套的scope。一个scope中存储了下属的若干个子scope。

得益于JavaScript的单线程模型,在全局定义一个activeEffectScope 就可以记录当前在哪个scope内做依赖收集。

这个类中存储了所有的ReactiveEffect,也就是响应式变量变化的时候应该做什么。

这个类也极致简洁,只有200多行。从使用上看,Vue在全局创建了一个detached的scope,在每个组件实例中创建了一个专属的scope。

EffectScope的构造函数可以看出来,在创建组件的scope的时候,这个新创建的scope会自动存储在activeEffectScope.scopes中。这样Scope就天然的组装成了一个树状结构。

EffectScope 包含的方法包括暂停、恢复、运行某个方法(猜测是Effect)、on(成为当前activeEffectScope)、off(恢复之前的activeEffectScope)、彻底结束scope。

嗯嗯,就这些了。

可以看到,真实记录DepEffect 的关联关系,以及触发Effect 运行的逻辑都不在这里,在Scope中只有一个记录ReactiveEffect的数组,貌似有点关联。别急,继续看。

响应式关系具体如何记录的?

这里我遇到了一些困难。Dep、Link、ReactiveEffect 等这些类的关联关系错综复杂。感觉这几个类相互之间都存了一个双向链表。

先从最基础的来,响应式关系本质是一个订阅者模式,订阅的源在Vue里面的数据类型是 Dep 。 订阅者的核心数据结构是 SubscriberSubscriber 分两种: ReactiveEffectComputed

这样的订阅结构使用Link 数据结构来存储,DepSubscriber 是多对多结构,所以Link中存储的数据比较复杂。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/**
* Represents a link between a source (Dep) and a subscriber (Effect or Computed).
* Deps and subs have a many-to-many relationship - each link between a
* dep and a sub is represented by a Link instance.
*
* A Link is also a node in two doubly-linked lists - one for the associated
* sub to track all its deps, and one for the associated dep to track all its
* subs.
*
* @internal
*/
export class Link {
/**
* - Before each effect run, all previous dep links' version are reset to -1
* - During the run, a link's version is synced with the source dep on access
* - After the run, links with version -1 (that were never used) are cleaned
* up
*/
version: number

/**
* Pointers for doubly-linked lists
*/
nextDep?: Link
prevDep?: Link
nextSub?: Link
prevSub?: Link
prevActiveLink?: Link

constructor(
public sub: Subscriber,
public dep: Dep,
) {
this.version = dep.version
this.nextDep =
this.prevDep =
this.nextSub =
this.prevSub =
this.prevActiveLink =
undefined
}

这段代码我必须把注释一起贴过来,可以看到里面存储Dep 用的是双向链表。存储Subscriber也是双向链表。用preActiveLink 来存储当前active的link链表,这里数据结构都飞起来了。 Link 是一个纯存储类,没有任何方法。

然后Dep 中也存了Link的双向链表,用于找到订阅者,再然后,Subscriber 中也存了Link的双向链表,用于找到订阅源。

响应式的大流程

单从数据结构看,会感觉逻辑非常破碎,但是将响应式的流程穿起来,就清晰一些。

  1. 神说,先要有 ReactiveScope ,标记下现在开始收集依赖。
  2. 然后,有凡人创建了 Ref 或者 Reactive 对象.
  3. 这两类对象中,或者在创建阶段,或者在依赖收集阶段会创建Dep .
  4. 有了订阅源,后面凡人要创建 Subscriber , 可能是一个 Effect 对象,也可能是一个 Computed 对象。
  5. 例如 Effect 创建的时候,其内部的方法一定被调用了。
  6. 在这个方法调用的时候,会访问到 Ref 或者 Reactive 对象,这个时候触发了Track流程。
  7. Track 流程中会追加 Link 关系。就是在这一步,开始织网的。

关键是“网” 怎么织:

  1. 有个全局的activeSub,一定指向正在运行的 Subscriber 。 如果没有,说明触发响应式变量的操作不在 Effect 或者 Computed 对象里面,嘿嘿😄 ,忽略就好。
  2. 有这个activeSub,就可以对应创建一个Link,这个Link要放到activeSub的订阅源链表中。
  3. 同样的,Link也要追加到当前 Dep 的 订阅者列表里面。
    • 如果订阅源是一个computed对象的话,那computed本身既是订阅源,也是订阅者,那这个computed对象的deps,也同时是 activeSub的订阅源,activeSub的订阅源列表中要递归加入这个computed对象的订阅源。 这里是一个小逻辑。

自然而然的,在调用Ref或者Reactive对象的写操作的时候,就会触发这些对象对应Dep的trigger 操作。

这里的trigger 分为 notify 阶段 和 Subscriber的trigger阶段。原因是一次effect中,可能在一条指令中修改了多个响应式变量,每当触发变量的trigger,实际上启动了一次 startBatch —— 批操作。但是在endBatch的时候,未必会真正的执行effect,代码内部通过一个计数器的方式,保证要batch的操作都完成了之后,才会真正触发effect的trigger。

notify 阶段,实际上是把Subscriber的flag增加上 NOTIFIED标记,同时加入到 batchSub 链表中,用于之后真正执行响应式动作。

如果Subscriber是一个Computed 的话,流程类似,就是加到 batchedComputed 中。

剩下的一些源码就是computed 如何实现、Watch如何实现。其实到这里的时候,这部分代码已经没有什么引起我好奇的事情了,大体可以猜到这两个部分如何实现的。所以reactivity 的阅读到此告一段落。

我的一点点哲学

这里说哲学其实是给自己镶金边,也就是刷短视频、看序言(嗯,看书也就看看序言的耐心)得来的一点点感想。

我懂了!

我的整个学习历程中经常停留在这个阶段:老师问“懂了吗”,我说 “懂了”,其实不懂。什么是懂了呢? 一个古典词汇很有价值“教学相长”,就是学习和教学其实是相互促进的,这和“费曼学习法” 相呼应。

这里有一个朴素的真理,如果一件事情,我说不能顺畅的教授给另一个人,那一定不是学明白了。 “不成熟 ” 的人类,经常会陷入这种困境。 利用一点脑科学的知识 —— 人的思绪从来就是零散的、如星光般涌现的 , 如果不是主动的整理 、 回顾,那这些想法就没有任何价值,可能只是刹那间在大脑中迸发出的电信号。 从“想到” 到 “懂”,必须经过整理,再思考,输出的过程,不然一切都会随风而逝。映射到职场,每当遇到线上问题的时候,我相信很多人会有“这明显有问题嘛,应该 XXXXX”,但是呢,谁会最终产出价值呢?往往是第一个“新建文档”,拉大家汇总思路,并梳理各方想法,产出“TODO” 的人。 这就是社会化智慧的 “核”,如果没有这个“新建文档”的人, 那所有人的想法,或者变成“抱怨”,如“屁”般消散; 或者化作“流萤”,惊艳但是转瞬即逝; 最终不能转化成有价值的输出。

TBD

【阅后感】AI产品经理课程

在B站搜“吴恩达”老师的课程,第一个推荐系列是《AI产品经理》 的一个课程。本来是当做游戏背景音频来听的 ,但是自打第一集就觉得很受启发,这里记录下。

第一集其实是讲独立开发者的,作为一个中登,最近常觉得有一点自己的副业能缓解年龄焦虑。

有一些点很受启发或者心有戚戚,流水账式记录如下:

  1. 独立开发者在这是时代是有非常大的机会的,不是赚大钱的机会,而是自由的机会。 有大模型帮助,有云服务提供基础服务,有各种代注册公司等的服务帮助建立公司,建立一个一人企业的成本已经是比较低了。 刚好最近也看了一点《一人企业》 的内容。感觉有很多共鸣。
  2. 独立开发者的风险是巨大的,可能前期(甚至持续)没有收入,必须要频繁试错。视频第一集中介绍的几个优秀案例,例如那个印度小哥 ,和 Notion的创始人,起初一两年也没什么收入,过得略窘迫。所以裸辞根本和自杀没什么区别,最好通过副业的方式试手。
  3. 独立开发者的收入很难达到大厂程序员的水平 ,毕竟这是一个考察综合实力的赛道,光靠技术NB几乎没什么成功的机会。这里的成功的标准已经定义的比较低了,就是能养家而已,但这已经不容易了 。
  4. 独立开发者选择赛道千万不能选择天花板高的方向,因为无论国内还是国际的互联网行业中,高频打低频几乎是一个常态,如果我们选择了一个天花板高的赛道,大厂分分钟复制,个人开发者没有任何优势。 参考Github上个人开发者的优秀案例,很多是从很简单的浏览器扩展开始开发的,这个小赛道就是门槛低+天花板也低的方向,几乎不会被大厂抄袭。
  5. 不要怕同质化,因为这几乎是避不开的局面,同质化的内容也可能 获得一点点流量。在试水的初期,“抄袭”往往是一个快速验证的好方式 。但是单纯的抄袭是无法累积优势,无法吸引更多的用户的。所以独立开发者的赛道应该比较垂直、比较窄,但是足够深入。
  6. 作为独立开发者做的是自己喜欢做,或者有欲望做的事情,所以容易有激情。 视频作者提到自己的工作状态,几乎007,我非常羡慕,身在大厂的我早就职业倦怠了。回顾过往,为了一个创业的点子我连续一周熬夜写代码,不是身体不行了 ,是心懒了。
  7. 独立开发者最大的成本是沟通成本,税务、合同、著作权等等的事情,都会付出巨量的沟通成本。
  8. 保证自律的三个方法:
    • 对外承诺自己的目标 ,这样会有外部压力,如果完不成目标就会觉得没面子,有社交压力。有外部压力的情况下更容易坚持。
    • 有惩罚承诺,例如完不成目标就发红包。 这样会有更多人来主动监督自己。
    • 要有仪式感,办公氛围。这样让自己有明确的节点切换状态,保证工作期间和散漫的休息状态彻底切割。这里作者举了一个比较夸张的例子,他的一个朋友故意租了一个比较贵的办公地点,结果这个朋友即便为了赚回房租也会拼命工作。
  9. 关于自律,我是两个极端状态,例如我一年减肥80斤来说,我从不对外承诺目标,但是我持续做下来了。原因减少食量(提高质量)对我来说是非常简单的事情,中间都依赖惯性。 中后段的时候,一周保证4次健身房,其实一方面是有氧运动确实让我感觉有快感,另一方面是运动的时候放空自己,有摸鱼的嫌疑。但是更多的时候,由于没有对外明确的目标,所以中间做废了也没人管,我自己更不管了,这样做事情的中间过程没人监督,目标没人负责,最后不了了之。 哈哈~ 我不会觉得有什么不好意思,相信很多“不成熟”的人也会这样。我这里就列个目标吧——“2025年底之前副业营收一千元”
  10. 思考的时间不要超过 10% 。这个阈值很有意思,我相信很多人对于不确定的事情,在做之前都会 “深思熟虑” , 然后陷入焦虑。 其实先动起来,哪怕先有一个只能打10分的baseline,后续要在哪里改进也会逐渐有想法,逐步建立方法论。而 10% 的思考时间够吗? 其实不在于比例,而在于质量。 如果知识散漫地无目标的空想,那100%的时间都不够,但是,打个比方,每天工作之前花30分钟想清楚这一天自己要做什么,肯定不算难。 何况大部分情况下,根本用不了30分钟时间。

回顾下人生,确实更多时候我是完全没有自律能力,且没有负罪感的。我经常做的事情就是大刀临头再不慌不忙的准备,准备不足也就不足了,一点不担心。 颇有一种“视死如归” 的感觉,哈哈~ 真实的原因是,我一直挺喜欢另一句话“活着挺好,死了也行”。 虽然过去经历过一点坎坷,但是我这辈子太顺利了,几乎不用主动做什么,幸福就会降临——我被幸运眷顾太久了。

视频没看完,先写到这里吧。

源码阅读计划

目的

  • 阅读优秀源码,扎实编码基础
  • 激发想象力,寻找灵感
  • 深入思考: 设计哲学、背景思考、项目不足、提升点
  • 发散思维,寻找类似机会

项目选择原则

  1. 广泛使用的框架代码,熟悉完整生态,了解每个框架的能力边界。
  2. 流行的轻量级项目,尤其个人开发者项目,激发个人思考。
  3. 纯学习类项目 ,例如LLM相关,虽然绝大概率看不懂,但是用来装X也是好的。

阅读笔记提纲

  1. 项目介绍
  2. 设计哲学
  3. 核心技术点
  4. “+1” 思考

计划阅读列表

书籍阅读计划

目的

  • 系统的学点东西,雁过留痕

书籍选择

  • 专业书籍
  • 经济类书籍
  • 个人成长类型的书籍
  • 我爱读的书

笔记提纲

  • 书籍概述:名称、作者、核心内容、书籍影响力评价。
  • 知识脑图
  • 核心启发点
  • 发散

计划列表

  • 还没有计划