6.7K Star 的知名开源项目源码,该怎么看?

090aedfe689b4c84cfe28aa13153d079.gif

作者 | 一只图雀

来源 | 程序员巴士

心理认知要到位

首先要认识到,看源码是一个开始比较枯燥、同时时间跨度相对比较长的一个过程。所以看源码的第一步是找到自己想要了解领域、或者自己所在业务领域高度相关的项目,并且在这个领域比较出名,且维护活跃。

打个比方,皮汤我因为是一名前端,而前端这个领域有很多新兴的内容,如 Unbundled 方案 Vite,新兴框架 Svelte,新汇编语言 WebAssembly,CSS 工程化方案 TailwindCSS,组件库如抖音很火的开源库 Semi Design、或者社区比较火的 Vue3 组件库 NaiveUI 等。

而皮汤我一直对组件库、CSS 方向比较痴迷,且是组内最近负责前端工程化 CSS 方面基建的负责人之一,所以让我去研究一个组件库的源码,如 NaiveUI,那么我是很有兴趣、动力和理由的,而这也是驱动你啃下一个源码的核心驱动力之一。

ad2967eae6c3c66f52edfe27f76894a8.png

理解高潮 MVP

其次我们看源码要有一定的技巧,复杂如 React,可以算作一个简单的操作系统了,如果你上来通过比较简单粗暴的从代码入口开始,一路打断点了解源码,那你再怎么坚持也会想吐的。

249dfb430418a74965e3a9ba15b025a4.png

那这里的技巧是什么呢?就像我们互联网创业一样,如果你有一个大而全的点子,但是你的第一步肯定不是找一个空旷的屋子,备好半年的粮食,准备几台电脑,然后高强度开发几个月,然后祈求一问世就惊艳世人。一个是这种情况非常少见,二个是你也得有坚持几个月的资本和耐心。

2d84353e5a2ded9e1a0fb0789c56a5a8.png

而在创业领域一个比较知名且流传深远的技巧就是 MVP,即最小可行性产品,你需要先做出一个非常小的,刚好能够使用以及能够测试你想法的产品,然后快速推向市场,接收用户反馈,接着跟进用户反馈,不断迭代产品,满足用户需求,直至达到 PMF(产品与市场匹配),这个时候你基本上就可以找投资,进行规模化,然后就是融资、去纳斯达克敲钟。

b71a36869a8c2782bd73ec19995b1f8b.png

所以对应到我们看源码这个领域,第二个需要注意的,就是你需要找到一个开源项目能跑起来的最小 MVP,去除其他繁杂的依赖,最最核心的流程与机制。这个能够帮助你理解项目核心的 MVP,我称之为 高潮 MVP -- 即如果你能够跑通一个项目这样的 MVP,那么你内心会异常幸福,感觉自己成就慢慢,兴奋达到了高潮,而接下来其他内容、分支基本上就是复用这套 MVP,往上面添砖加瓦,补齐一些兼容细节等。

2e9577b396e559bb817d740aa33090e1.png

开始之前

那么对于 NaiveUI 来说,它的高潮 MVP 是什么呢?

我们首先打开它的官网:

1f7c2429d7e7697645e747639394ef1d.png

点开开始使用:

0c8bd8e7a1c20b5260c4a846317cf1cc.png

作为一个组件库来说,它一般需要谈论自己的价值观、设计原则、定制方式,ICON 图标相关的内容,但是这对于你搞懂这份源码其实没有多大帮助,所以需要略去这些干扰项。

让我们再来看看它的源码仓库:

5961de5d5a1edb4fb8fdfd95431fd4c1.png

确保这个库使用何种语言编写,这样你在看源码之前可以先衡量你当前的知识储备是否能够支撑你看懂这份源码,当然如果你没有对应的支持储备,但是又坚持想要看这份源码,那么你首先应该考虑根据它使用的语言,提前进行语言学习储备。

看透本质

让我们回到 NaiveUI 的官网:

aa4a31b74e114d230f66740711b23b5c.png

可以看到,对于一个 “组件库” 来说,实际上最最基础的其实就是 “组件”,而组成 “组件” 的背后则需要一系列更加基础的元素,如颜色、间距、边框、背景、字体等。

那么我们的目标是不是很明确了呢?把一个 “按钮” 组件拿下,理解能够完整使用到这样一个按钮背后所需要的所有必须的流程、知识、细节,那么针对其他的组件,基本上 90 % 的逻辑可以复用,只需要理解剩余 10% 特定功能需求就可以搞懂。

类似下面这张冰山图:

27c7a990912225f8525f4fca99dfb8e6.png

冰山之下就属于那 90%,我们基于一个看似简单的 “按钮” 组件,来梳理整个组件库的核心流程,就可以帮助我们快速、精准的搞懂整份源码,所以我们的高潮 MVP 就是搞懂一个 “按钮” 组件的全流程。

了解上下文

理解我们的高潮 MVP 目标是什么了之后,接下来就是带着这个目标先详细读一下文档中关于 Button 的所有相关说明,可以看到这个按钮包含如下内容:

ea7681d78afa7bda9ee7cc463e987c58.png

通过右侧的目录,了解到一个按钮首先会有基础内容,包含 defaultprimaryinfosuccesswarningerror 这几类,然后需要处理:

  • 边框相关:虚线

  • 尺寸相关:尺寸、行政

  • 颜色:自定义颜色

  • 状态:文本、禁用、加载中

  • 事件

f354fb77f21fb54f1fa21a9156f598a0.png

上述表明了这个 Button 可以达到的效果,可以完成的操作,了解之后,接着可以了解按钮相关的使用 API,通过 API 以及可以达到的效果,我们大致可以理解这个按钮接收的输入和输出有哪些。

一个一个使用 Button 的例子长什么样子:

<template><n-space><n-button>Default</n-button><n-button type="primary">Primary</n-button><n-button type="info">Info</n-button><n-button type="success">Success</n-button><n-button type="warning">Warning</n-button><n-button type="error">Error</n-button></n-space></template>

了解如何开启项目

通常开源项目比较方便的一点是它会有详细的文档,同时它非常渴望有贡献者加入,所以会有完善的 贡献指南,比如 NaiveUI 的贡献指南如下:

9691edbd0f25afaafb2967d2d75f7d5b.png

通过贡献指南,你能够了解如何安装依赖、处理一些启动项目的问题,能够把项目跑起来进行调试,这通常是你了解整个代码运行过程的初次体验。

理解目标项目的项目结构

通常你到这个步骤时,你应该需要知道如下内容:

  • 你已经理解了你的目标,高潮 MVP 是什么

  • 你理解了你目标内容作为一个功能特性,它的输入和输出是什么

  • 你理解此项目的技术栈是什么,如何把项目跑起来

对应到 NaiveUI 我们的这三点分别如下:

  • 高潮 MVP:跑通一个 Button 并能够使用,保有和现有 Button 一样的特性,接收一样的输入,产生一样的输出

  • Button 包含边框、尺寸、颜色、状态、事件等相关的内容,输入这些参数,产出对应条件下的输出

  • 项目的技术栈是 Vue3、TypeScript,构建工具是 Vite,同时使用了 CSS BEM 框架 CSS Render,同时包管理工具使用 pnpm

理解这三点之后,接下来我们就需要对照着源码来理解一下整份文件目录,了解各个目录之前的依赖关系,见下图。

bdd1e210b5d826b278792cdfad024c24.png

我们可以先了解一下大致每个文件夹是干什么的:

  • src:这个是主要放组件库相关的组件代码,以及导出一些国际化、样式、主题定制相关的内容,一般是一个开源项目的核心开发目录

  • scripts:一些运行代码、构建、发版相关的脚本逻辑

  • theme :则为 NaiveUI 内置的默认主题,类似这种组件库一般都允许用户自定义主题,整个 NaiveUI 各个组件在使用各种 UI 属性时都是遵从这套主题进行设置的,也就是可以修改 theme 里面的内容,或者自己完全自定义一套主题

  • .github.husky 等都是一些配置,无需过多关注,可以直接加入到你的 MVP 模板工程里

  • playground 是用于此时代码在各种环境下运行的支持代码,如 SSR 等

  • demo 则是引入 src 相关内容用于展示组件实际效果的网站例子,实际上对于 NaiveUI 也就是我们之前看到的文档官网

  • 其他的如 builddesign-notes 等是构建产物,或者一些主题设计的笔记等,基本上不属于本次源码需要阅读的部门,看兴趣的同学可以看看

然后就是一些用于各种工程化配置的文件如:

  • .prettierrc :Prettier 相关

  • .gitignore :Git 相关

  • .eslintrc.js :ESLint 相关

  • babel.config.js :Babel 相关

  • jest.config.js :Jest 测试相关

  • postcss.config.js :处理 CSS 相关

  • tsconfig.xx.json 处理 TypeScript 相关

  • vite.config.js :Vite 构建工具相关的配置

以及一些和项目强相关,用于了解整个想法发展上下文的 CHANGELOG.xx.md ,还有我们之前提到的用于跑通代码的 CONTRIBUTING 贡献指南。

ca0917fa2dfe8bbd946f3ecfdfdf3721.png

有点看懵了。🤯

创建你的高潮 MVP 项目

了解了整个 NaiveUI 的项目目录结构之后,我们就可以着手创建我们的高潮 MVP 项目了,但在这之前我们可以再进行一波简化,即我们有些内容可以不要:

  • 针对目录的来说

    • .github.huskyplaygroundscripts 这种我们可以不要,我们只需要测试最基础的环境,以及在开发时可以跑通即可

    • theme 这种只是整个 NaiveUI 遵循的设计体系,在其他部分会遵循这个体系,但是不会直接引用,所以我们也可以不要

    • 这样我们只剩下 demosrc ,而更近一步,我们可以把 demo 做到 src 里面,整个 src 我们将其职责变为高潮 MVP 网站入口,然后原剩下的 src 下面的代码则用于导入到 src 入口文件里面使用

  • 针对配置文件来说:

    • 测试相关的,Jest 等我们并不需要

    • TypeScript 相关的,我们后续可以迭代,不用引入不必要的复杂度以及类型体操

    • ESLint 和 Prettier 等我们也可以不需要,依赖于编辑器默认的格式化就可,当然引入这两个到我们初始的高潮 MVP 项目里也不碍事

经过简化之后,我们的高潮 MVP 项目就只需要如下几个文件了:

  • 构建项目和提供开发服务器的 Vite 相关内容:vite.config.js

  • 用于提供语法转译的 babel.config.js

  • 项目依赖文件 package.json

  • 用于跑通项目的主要代码 src 以及 index.html 入口模板

目录结构如下:

.├── babel.config.js├── index.html├── node_modules├── package.json├── public├── src├── vite.config.js└── yarn.lock

很精简,没有多余繁杂的内容对吧?同时也非常易懂。

cc5a310faa5d5be11971ff1e84544a2a.png

这些剩下要创建的文件内容,从 NaiveUI 的工程目录里面 Copy 过来,然后安装对应的依赖即可。

跑通流程

当我们根据源码库创建了我们的高潮 MVP 项目之后,现在应该可以跑起来了,只不过内容只是一个简单的 Button,因为为了快速跑起来项目,我们的入口文件 src/App.vue 会如下:

<template><t-button>hello tuture</t-button></template><script>import { defineComponent } from "vue";import { TButton } from "./components";export default defineComponent({name: "App",components: {TButton,},});</script>

而对应的 src/components/TButton.vue 如下:

<template><button>{$slots.default}</button></template><script>import { defineComponent } from "vue";import { TButton } from "./components";export default defineComponent({name: "Button"});</script>
4d5ab71a54850cfc3cb3f5ee97d1dc13.png

接下来我们就尝试一遍了解 NaiveUI 的代码,一遍将这些主干代码迁移到我们的高潮 MVP 项目中来,然后确保迁移过程中能够持续跑起来,虽然我们可能会遇到有时候一个依赖需要大量的前置依赖,所以需要迁移一大段代码才能将项目跑起来。

5f298bcdecd2011c1cfafdf53bdfb076.png

找到核心入口

我们要完成一个 Button 的所有前置依赖,只需要去到 NaiveUI 对应的工程目录文件里面,找到 Button 对应的代码,如下:

426137ca3de3bb3c25fe4799175cfd0e.png

其实解析一下组件文件的代码,就是下面几部分:

  • 前置的 import 依赖

  • 定义组件 defineComponent

    • 组件里面处理 props 传入与使用、自身状态的定义与使用

    • 模板代码

  • 导出组件

而上图代码中的所有和 TS 定义相关的内容我们都是不需要的,所以可以删除 ButtonPropsNativeButtonPropsMergedPropsXButton 这些类型定义相关的内容。

而导入部分涉及到类型定义相关的我们也可以删除掉:

import type { ThemeProps } from '../../_mixins'import type { BaseWaveRef } from '../../_internal'import type { ExtractPublicPropTypes, MaybeArray } from '../../_utils'import type { ButtonTheme } from '../styles'import type { Type, Size } from './interface'

删除完这些无关的代码之后,我们的代码还剩下那些内容呢?

导入依赖部分:

import {h,ref,computed,inject,nextTick,defineComponent,PropType,renderSlot,CSSProperties,ButtonHTMLAttributes
} from 'vue'
import { useMemo } from 'vooks'
import { createHoverColor, createPressedColor } from '../../_utils/color/index'
import { useConfig, useFormItem, useTheme } from '../../_mixins'
import {NFadeInExpandTransition,NIconSwitchTransition,NBaseLoading,NBaseWave
} from '../../_internal'
import { call, createKey } from '../../_utils'
import { buttonLight } from '../styles'
import { buttonGroupInjectionKey } from './ButtonGroup'
import style from './styles/button.cssr'
import useRtl from '../../_mixins/use-rtl'

组件声明部分:

const Button = defineComponent({name: 'Button',props: buttonProps,setup(props) {// 定义组件状态const selfRef = ref<HTMLElement | null>(null)const waveRef = ref<BaseWaveRef | null>(null)const enterPressedRef = ref(false)// 使用 Props 或注入全局状态const NButtonGroup = inject(buttonGroupInjectionKey, {})const { mergedSizeRef } = useFormItem(...)const mergedFocusableRef = computed(() => {...})// 定义组件事件处理const handleMouseDown = (e: MouseEvent): void => {...}const handleClick = (e: MouseEvent): void => {...}const handleKeyUp = (e: KeyboardEvent): void => {...}const handleKeyDown = (e: KeyboardEvent): void => {...}const handleBlur = (): void => {...}// 处理组件的主题,获取该 Button 组件在整个全局设计系统中的对应样式const { mergedClsPrefixRef, NConfigProvider } = useConfig(props)const themeRef = useTheme(...)const rtlEnabledRef = useRtl(...)// 将自身状态、全局状态相关的主题样式、各个 CSS 属性的值、事件相关的内容处理之后返回给模板使用return {selfRef,waveRef,mergedClsPrefix: mergedClsPrefixRef,mergedFocusable: mergedFocusableRef,mergedSize: mergedSizeRef,showBorder: showBorderRef,enterPressed: enterPressedRef,rtlEnabled: rtlEnabledRef,handleMouseDown,handleKeyDown,handleBlur,handleKeyUp,handleClick,customColorCssVars: computed(() => {...}),cssVars: computed(() => {...})}},render() {// 处理各种组件相关的样式渲染、事件处理相关的内容,这里的样式渲染对应着在文档里提到的 Button 可以呈现的状态和能处理的操作const { $slots, mergedClsPrefix, tag: Component } = thisreturn (<Componentref="selfRef"class={[`${mergedClsPrefix}-button`,`${mergedClsPrefix}-button--${this.type}-type`,{[`${mergedClsPrefix}-button--rtl`]: this.rtlEnabled,[`${mergedClsPrefix}-button--disabled`]: this.disabled,[`${mergedClsPrefix}-button--block`]: this.block,[`${mergedClsPrefix}-button--pressed`]: this.enterPressed,[`${mergedClsPrefix}-button--dashed`]: !this.text && this.dashed,[`${mergedClsPrefix}-button--color`]: this.color,[`${mergedClsPrefix}-button--ghost`]: this.ghost // required for button group border collapse}]}tabindex={this.mergedFocusable ? 0 : -1}type={this.attrType}style={this.cssVars as CSSProperties}disabled={this.disabled}onClick={this.handleClick}onBlur={this.handleBlur}onMousedown={this.handleMouseDown}onKeyup={this.handleKeyUp}onKeydown={this.handleKeyDown}>{$slots.default && this.iconPlacement === 'right' ? (<div class={`${mergedClsPrefix}-button__content`}>{$slots}</div>) : null}<NFadeInExpandTransition></NFadeInExpandTransition>{$slots.default && this.iconPlacement === 'left' ? (<span class={`${mergedClsPrefix}-button__content`}>{$slots}</span>) : null}{!this.text ? (<NBaseWave ref="waveRef" clsPrefix={mergedClsPrefix} />) : null}{this.showBorder ? ( ...)}{this.showBorder ? (...)}</Component>)}
})

进一步简化代码

从上述还剩下的代码,我们可以看到,其实对于理解组件库来说,我们其实绝大部分内容是在做定制主题,然后如果根据各种传入的 props,展示不同的主题的工作,所以你会看到 Button 组件里充斥着大量的 CSS 变量,如 this.colorthis.ghostthis.textthis.cssVars ,所以我们的核心就是理解这些主题是如何定制的,包含哪些变量和依赖,这些变量和依赖是如何影响 Button 可以承载不同样式和功能的。

所以上述代码中,有一些内容其实我们就可以删掉了:

  • 我们只需要看一个独立的 Button 是如何运作的,所以 NButtonGroup 部分,按钮组部分就可以不要了

  • 我们也不需要处理一些独特的适配,如 RTL(从右向左排版)等

所以我们需要近一步删除这些代码:

import { buttonGroupInjectionKey } from './ButtonGroup'import useRtl from '../../_mixins/use-rtl'const NButtonGroup = inject(buttonGroupInjectionKey, {})

以及其他使用到 buttonGroup 相关的内容。

理解输入

通过上一步,我们基本上去除了所有无关的内容,达到了我们最终高潮 MVP 项目里需要的 Button 的所有的、最精简的内容,也就是说我们核心入口代码自身和依赖的部分已经确定了,那么接下来就需要处理全部的输入,以及删除这些输入中相关的依赖与 Button 处理无关的逻辑。

我们可以看到 Button 主要有如下一种输入:

  • 文件顶部的 import 输入

  • 使用钩子 useFormItem 、或全局状态注入 inject(...) 相关的输入

我们可以看到,import 相关的输入主要分为两类:

  • 某些库,如 vue 的导入:这个我们只需要查询对应库的文档就可了解对于 API 的作用

  • 直接依赖于自身项目的其他相对路径导入:这个我们就需要继续探究 NaiveUI 源码库的其他部分

而钩子 useFormItem 、或全局状态注入 inject(...) 相关的输入则也依赖于 import 里自身项目的其他相对路径引入。

我们需要顺着如下的这些依赖,进行依赖分析:

import { createHoverColor, createPressedColor } from '../../_utils/color/index'import { useConfig, useFormItem, useTheme } from '../../_mixins'import {NFadeInExpandTransition,NIconSwitchTransition,NBaseLoading,NBaseWave} from '../../_internal'import { call, createKey } from '../../_utils'import { buttonLight } from '../styles'import { buttonGroupInjectionKey } from './ButtonGroup'import style from './styles/button.cssr'

这些依赖里面有些自己本就是叶子依赖,并无其它依赖,如:

import { createHoverColor, createPressedColor } from "../../_utils/color/index";// 其中某几项import { useFormItem } from "../../_mixins";// 下面的某几项import {NFadeInExpandTransition,NIconSwitchTransition,} from "../../_internal";import { call, createKey, getSlot, flatten } from "../../_utils";

这些叶子依赖可以直接对照着原仓库建立对应的目录结构和文件命名,然后把代码拷贝过来。

对于那些非叶子依赖,我们需要再下一番功夫继续解析其依赖,重复之前的两项操作:

  • 删除 TS 或者其他和 Button 不相干的代码和依赖

  • 寻找其依赖的依赖,继续上面的过程

最后就是对照着源码的目录结构创建一样的结构,将处理完无关内容的代码拷贝过去。

打个比方,对于非叶子依赖 style

import style from "./styles/button.cssr.js";

我们需要去到对应的文件下,查看其依赖:

import { c, cB, cE, cM, cNotM } from '../../../_utils/cssr'import fadeInWidthExpandTransition from '../../../_styles/transitions/fade-in-width-expand.cssr'import iconSwitchTransition from '../../../_styles/transitions/icon-switch.cssr'

发现其依赖了用于进行 BEM 规范定义的 cssr 库(自建)、以及处理动画的一些 fadeInWidthExpandTransitioniconSwitchTransition 依赖,那么接着要继续进入这些依赖,如:

import { c, cB, cE, cM, cNotM } from '../../../_utils/cssr'

它的依赖如下:

/* eslint-disable @typescript-eslint/restrict-template-expressions */import CSSRender, { CNode, CProperties } from 'css-render'import BEMPlugin from '@css-render/plugin-bem'

发现没有其他再需要继续递归寻找的依赖了,都是引入的第三方库,那么就可以去查阅一下对应的第三方库的文档,了解 API 的含义即可。

如此往复进行上述的依赖分析,直至收敛,最后我们会得到一个如下的文件组织图:

.
├── App.vue
├── _internal
│   ├── fade-in-expand-transition
│   │   ├── index.js
│   │   └── src
│   │       └── FadeInExpandTransition.jsx
│   ├── icon
│   │   ├── index.js
│   │   └── src
│   │       ├── Icon.jsx
│   │       └── styles
│   │           └── index.cssr.js
│   ├── icon-switch-transition
│   │   ├── index.js
│   │   └── src
│   │       └── IconSwitchTransition.jsx
│   ├── index.js
│   ├── loading
│   │   ├── index.js
│   │   └── src
│   │       ├── Loading.jsx
│   │       └── styles
│   │           └── index.cssr.js
│   └── wave
│       ├── index.js
│       └── src
│           ├── Wave.jsx
│           └── styles
│               └── index.cssr.js
├── _mixins
│   ├── index.js
│   ├── use-config.js
│   ├── use-form-item.js
│   ├── use-style.js
│   └── use-theme.js
├── _styles
│   ├── common
│   │   ├── _common.js
│   │   ├── index.js
│   │   └── light.js
│   ├── global
│   │   └── index.cssr.js
│   └── transitions
│       ├── fade-in-width-expand.cssr.js
│       └── icon-switch.cssr.js
├── _utils
│   ├── color
│   │   └── index.js
│   ├── cssr
│   │   ├── create-key.js
│   │   └── index.js
│   ├── index.js
│   ├── naive
│   │   ├── index.js
│   │   └── warn.js
│   └── vue
│       ├── call.js
│       ├── flatten.js
│       ├── get-slot.js
│       └── index.js
├── assets
│   └── logo.png
├── button
│   ├── src
│   │   ├── Button.jsx
│   │   └── styles
│   │       └── button.cssr.js
│   └── styles
│       ├── _common.js
│       ├── index.js
│       └── light.js
├── components
│   └── Button.jsx
├── config-provider
│   └── src
│       └── ConfigProvider.js
└── main.js32 directories, 45 files

一个简单的 Button 竟然要包含 45 个文件,32个目录来进行支撑,我们基本上可以确定组件库中 90% 的内容是共通的,只需要理解了一个 Button 需要的所有底层依赖和设计理念,理解这个组件库只需要再努力一步,了解剩下 10 % 的各组件特殊设计,就可以弄懂整个组件库的源码。

上述核心整理的一个 Button 的全部依赖代码可以进入我的 Github 仓库查阅:https://github.com/pftom/naive-app。

2a3b226cf3410b6f6946a5d63aaf7a61.png

抽丝剥茧

当我们能够拿到一个 Button 能够完美运行背后所需要的所有 “必要” 和 “最简” 的依赖之后,我们就可以边运行这个项目,边通过查阅资料,画思维导图理解这份最简必要代码了。

37db8f7c572f46ec660dd32e02887b6d.png

我们首先把代码跑起来,然后逐层理解代码逻辑,如前置的几个钩子函数是干嘛的:

f711437c745e8d944a13efc087b950c9.png

核心的 useTheme 钩子是干嘛的:

b5ca4e95b74bd23d8a4f6284f6d89629.png

用户自定义相关的钩子函数又是干嘛的,它包含哪些 CSS 变量:

ba96c90255d35e6635ea5c7b9d1f7ba9.png

Vue3 组件里面的 setup 返回值有哪些:

7ca587857d10fa870ec92b6cc13c7903.png

最终用于渲染的 render 函数逻辑是干嘛的:

19708ee05729f4efabee0a8bec720427.png

通过查阅 Vue3 文档、梳理整个代码流程,然后了解各个分支是如何运作的,我们就能慢慢理解 Button 组件是如何跑起来的。得益于我们进行了代码的最精简化处理,所以整个看代码的流程虽然会慢一点,但是整体需要理解的内容相比之前我们拿到一整份源码,几百上千个文件来一股脑从入口开始打断点调试会好很多。

6be51c0bfe103fc62b31306b16b7089e.png

写在最后

相信大家在看皮汤的这篇源码阅读文章之前,应该也看过各种大牛的源码解读文章,但是相信每个人都有自己比较独特的看源码技巧,虽然我这里是拿如何看懂 NaiveUI 的源码举例子,但是相信所有看源码的过程都是如此,遵循如下步骤:

  • 树立好的心理认知

  • 理解高潮 MVP,又包含定位源码最小可行性代码需要的内容,在看源码之前先梳理结构,确保 MVP 能够跑起来

  • 然后再在最小、最核心的源码上进行打断点、画思维导图、查阅文档等方式帮助自己啃下源码

这是皮汤在看 Vite、NaiveUI 源码过程中总结出来的经验,相信能够为徘徊在看源码路上却没有方法的同学提供一点指引,你完全可以应用这个技巧去看其他的源码,如 Webpack?qiankun?Ant Design?或者抖音最近发布的 Semi Design。共勉 💪

b73268feb0447e99072abfd67b846a1f.png

参考资料

[1]

尤大都推荐的组件库是如何开发出来的?: https://mp.weixin.qq.com/s?__biz=MzkxMjI3OTA3NQ==&mid=2247485296&idx=1&sn=61b6de490f9d437215cadde9502d75df&chksm=c10e143cf6799d2afe13b5aecebc2b6608bd0fab3e61149b80910ce87757ceb2219651ed9a07&token=586597170&lang=zh_CN#rd

[2]

Semi Design: https://github.com/DouyinFE/semi-design

[3]

React: https://github.com/facebook/react

[4]

贡献指南: https://github.com/TuSimple/naive-ui/blob/main/CONTRIBUTING.md

[5]

CSS Render: https://github.com/07akioni/css-render

往期推荐移动云TeaTalk:这是一场云数据库技术的深度对话!
长跑11年,腾讯开源的变与不变
低代码发展专访系列之一:低代码平台产品的使用者都是谁?
CSDN云原生Meet up深圳站与你不见不散!
点分享点收藏点点赞点在看

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/513179.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网站建设中 模板_网站建设之模板网站的缺点

随着网站建设市场的日益成熟&#xff0c;许多的自助建站平台纷纷涌入建站市场&#xff0c;甚至许多主机供应商也与一些自助建站平台达成了战略合作&#xff0c;买服务器赠送模板网站。那么我们进行网站建设时是选择专业建站公司的定制网站还是模板站呢&#xff1f;看完模板网站…

阿里云李飞飞:什么是云原生数据库

简介&#xff1a; 云原生是一种新型技术体系&#xff0c;是云计算未来的发展方向。今天&#xff0c;阿里云李飞飞将和我们分享何为云原生、云原生如何与分布式有机结合&#xff0c;以及云原生技术如何帮助客户迈入数字原生时代。 作者 | 飞刀 来源 | 阿里技术公众号 云原生是一…

stm32 adc 连续和扫描_技术分享 | STM32多个ADC模块同时采样转换的应用示例

在STM32家族里&#xff0c;多数系列芯片内含2到3个ADC模块&#xff0c;有的甚至更多&#xff0c;比方G4系列可以有5个ADC模块。其中&#xff0c;通道数因不同的系列或型号多少不等&#xff0c;几个到几十个的都有。有时&#xff0c;我们可能需要多个ADC模块同时工作&#xff0c…

云原生/低代码/数据科学/计算等方向内容整理志愿者招募了!

持续招募内容整理志愿者&#xff01;云原生、数据科学、AI、低代码、计算等方向&#xff0c;有意愿的小伙伴&#xff0c;欢迎识别二维码提前报名哦。我们将持续为爱学习、有时间的小伙伴&#xff0c;提供多重福利&#xff01;要求&#xff1a;1. 你需要具备一定学术背景&#x…

python计算运动会某个参赛选手的得分。数据保存在文件中_成绩计算电脑程序的使用说明...

成绩计算电脑程序的使用说明为了计算成绩&#xff0c;特制作这个电脑程序&#xff0c;说明如下&#xff1a;一、开发软件&#xff1a;Python二、使用说明&#xff1a;在电脑D盘根目录下建立两个excel文件&#xff0c;一个是1.xlsx&#xff0c;另一个是2.xlsx其中&#xff0c;1.…

android 创建文件夹_Android 动画小记

精简介绍Android中的动画&#xff0c;主要介绍用法。动画种类包括了&#xff1a;补间动画、逐帧动画、属性动画&#xff0c;前两者统称为视图动画。补间动画包括了平移、旋转、缩放和透明度四种&#xff0c;每种动画支持xml和代码设置。另外还有一种组合动画&#xff0c;就是将…

2021金蝶全球创见者大会成功举办, 500强企业共话EBC数字战斗力

11月27日&#xff0c;由金蝶主办的“2021全球创见者大会”成功举办。大会以“用数字战斗力&#xff0c;向管理要效益”为主题&#xff0c;求索不确定时代&#xff0c;EBC如何帮助500强及中小企业拥抱数字战斗力&#xff0c;构建企业韧性。 据了解&#xff0c;金蝶全球创见者大…

使用管控策略,设定多账号组织全局访问边界

简介&#xff1a; 企业上云多账号架构中&#xff0c;如何做到从上到下管理的同时&#xff0c;处理好员工的权限边界问题&#xff1f; 由多账号上云模式说起 多账号上云模式的产生 我们的企业客户上云&#xff0c;一般都是从尝试部署少量业务开始&#xff0c;然后逐步将更多业…

使用MaxCompute LOAD命令批量导入OSS数据最佳实践—STS方式LOAD开启KMS加密OSS数据

简介&#xff1a; MaxCompute使用load overwrite或load into命令将外部存储的数据&#xff08;如&#xff1a;oss&#xff09;导入到MaxCompute前的授权操作。 MaxCompute使用load overwrite或load into命令将外部存储的数据&#xff08;如&#xff1a;oss&#xff09;导入到M…

pca降维的基本思想_R语言进行PCA分析

点击上方「蓝字」关注我写在前面前面我们知道了降维分析学习了PCoA分析今天学习PCA分析...PCA(Principal Component Analysis)&#xff0c;即主成分分析方法&#xff0c;是一种使用最广泛的数据降维算法。PCA的主要思想是将n维特征映射到k维上&#xff0c;这k维是全新的正交特征…

c语言线性分类回归库 台湾,最全的线性回归算法库总结—— scikit-learn篇

原标题&#xff1a;最全的线性回归算法库总结—— scikit-learn篇本文建议收藏后食用更加美味scikit-learn对于线性回归提供了比较多的类库&#xff0c;这些类库都可以用来做线性回归分析&#xff0c;本文就对这些类库的使用做一个总结&#xff0c;重点讲述这些线性回归算法库的…

吉麦新能源与联想签订战略合作协议,共同拥抱智能网联汽车升维时代

11月25日&#xff0c;吉麦新能源汽车与联想战略合作签约仪式成功举行&#xff0c;双方正式建立战略合作伙伴关系。未来&#xff0c;双方将在车联网、云解决方案及服务、IT基础架构产品服务、智慧工厂服务领域开展深入合作&#xff0c;合力推动新能源造车数智化转型&#xff0c;…

供应链商品域DDD实践

简介&#xff1a; DDD是一套方法论&#xff0c;实践能否成功&#xff0c;不仅仅是个技术问题&#xff0c;更是执行贯彻实施的问题。本文将就DDD的基本概念和DDD的实施进行分享。 作者 | 侧帽 来源 | 阿里技术公众号 前言 供应链商品域DDD实践时间不长&#xff0c;在实践过程也…

20岁,Universal GIS拥抱新生的GIS信创

近20年来&#xff0c;在快速发展的IT新技术和国家应用需求的双轮驱动下&#xff0c;我国GIS技术和产业得到了飞速发展&#xff0c;GIS软件应用范围从几个行业深入到了数字中国建设中的数字经济、数字政务、数字社会、数字生态建设的几乎所有行业和领域。GIS软件已成为国家治理体…

如何用Netty写一个高性能的分布式服务框架?

byte[] -->堆外内存 / 堆外内存--> byte[] -->java对象。 优化&#xff1a;省去 byte[] 环节&#xff0c;直接 读/写 堆外内存&#xff0c;这需要扩展对应的序列化框架。 String 编码/解码优化。 Varint 优化&#xff1a;多次 writeByte 合并为 writeShort/writeInt/w…

C语言rec文件如何打开,REC 文件扩展名: 它是什么以及如何打开它?

REC 疑难解答典型的 REC 开放挑战Squared 5 MPEG Streamclip 不在你尝试加载 REC 文件并收到错误&#xff0c;例如 “%%os%% 无法打开 REC 文件扩展名”。 通常&#xff0c;这是因为你没有安装适用于 %%os%% 的 Squared 5 MPEG Streamclip。 操作系统不知道如何处理你的 REC 文…

Quick BI:降低使用门槛,大东鞋业8000家门店的数据导航

简介&#xff1a; 通过引入MaxCompute和Quick BI&#xff0c;大东解决了以往数据查询即刻导致数据库闪崩的状况&#xff0c;还搭建起完善的报表体系&#xff0c;稳定应对高频、高并发的数据分析。 大东鞋业一季大约有500款的新品。大区下辖的各个分公司要对这500款新品进行订货…

绿盟科技发布安全知识图谱技术白皮书

随着全球数字化和万物互联的加速发展&#xff0c;近年来勒索病毒的攻击手段不断升级&#xff0c;例如今年美国最大燃油管道受攻击导致美国17个州和华盛顿特区进入紧急状态&#xff0c;引起全球关注。当下企业如何提高安全运营知识以面对不断升级的威胁攻击&#xff1f; 近日&a…

「应用管理与交付」为什么会成为云原生新的价值聚焦点?

简介&#xff1a; 为什么“云原生应用管理与交付”会成为 Kubernetes 之上重要的价值聚焦点&#xff1f;CNCF App Delivery SIG 在推动 Kubernetes 之上应用层技术快速演进的过程中将扮演什么角色&#xff1f;这个领域又将发生哪些值得大家期待的创新&#xff1f;让我们一起了解…

android 4.4官方下载,安卓系统电脑版-android x86 4.4 iso下载官方最新版【pc版】-西西软件下载...

Android-X86是由Beyounn和Cwhuang主持设计的。提供了一套完整的可行源代码树&#xff0c;配套文档以及Live CD与Live USB。Android系统主要应用在智能手机以及平板电脑设备上。日前&#xff0c;越来越多使用英特尔和AMD处理器的计算机也开始运行Android系统。如何才 能让Androi…